Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / profiles / host_zoom_map_browsertest.cc
blob2fdf7811d7ce604115f39d52a1d5db0cfa9ac25b
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.
5 #include "content/public/browser/host_zoom_map.h"
7 #include <algorithm>
8 #include <string>
9 #include <vector>
11 #include "base/bind.h"
12 #include "base/command_line.h"
13 #include "base/files/file_path.h"
14 #include "base/files/file_util.h"
15 #include "base/memory/ref_counted.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/path_service.h"
18 #include "base/prefs/pref_service.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/values.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/browser/profiles/profile_impl.h"
23 #include "chrome/browser/ui/browser.h"
24 #include "chrome/browser/ui/tabs/tab_strip_model.h"
25 #include "chrome/browser/ui/zoom/chrome_zoom_level_prefs.h"
26 #include "chrome/common/chrome_constants.h"
27 #include "chrome/common/chrome_paths.h"
28 #include "chrome/common/pref_names.h"
29 #include "chrome/common/url_constants.h"
30 #include "chrome/test/base/in_process_browser_test.h"
31 #include "chrome/test/base/testing_profile.h"
32 #include "chrome/test/base/ui_test_utils.h"
33 #include "components/signin/core/common/profile_management_switches.h"
34 #include "components/signin/core/common/signin_switches.h"
35 #include "components/ui/zoom/page_zoom.h"
36 #include "components/ui/zoom/zoom_event_manager.h"
37 #include "content/public/test/test_utils.h"
38 #include "net/dns/mock_host_resolver.h"
39 #include "net/test/embedded_test_server/embedded_test_server.h"
40 #include "net/test/embedded_test_server/http_response.h"
41 #include "testing/gmock/include/gmock/gmock.h"
42 #include "url/gurl.h"
44 using content::HostZoomMap;
46 namespace {
48 class ZoomLevelChangeObserver {
49 public:
50 explicit ZoomLevelChangeObserver(content::BrowserContext* context)
51 : message_loop_runner_(new content::MessageLoopRunner) {
52 subscription_ = ui_zoom::ZoomEventManager::GetForBrowserContext(context)
53 ->AddZoomLevelChangedCallback(base::Bind(
54 &ZoomLevelChangeObserver::OnZoomLevelChanged,
55 base::Unretained(this)));
58 void BlockUntilZoomLevelForHostHasChanged(const std::string& host) {
59 while (!std::count(changed_hosts_.begin(), changed_hosts_.end(), host)) {
60 message_loop_runner_->Run();
61 message_loop_runner_ = new content::MessageLoopRunner;
63 changed_hosts_.clear();
66 private:
67 void OnZoomLevelChanged(const content::HostZoomMap::ZoomLevelChange& change) {
68 changed_hosts_.push_back(change.host);
69 message_loop_runner_->Quit();
72 scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
73 std::vector<std::string> changed_hosts_;
74 scoped_ptr<content::HostZoomMap::Subscription> subscription_;
76 DISALLOW_COPY_AND_ASSIGN(ZoomLevelChangeObserver);
79 } // namespace
81 class HostZoomMapBrowserTest : public InProcessBrowserTest {
82 public:
83 HostZoomMapBrowserTest() {}
85 protected:
86 void SetDefaultZoomLevel(double level) {
87 browser()->profile()->GetZoomLevelPrefs()->SetDefaultZoomLevelPref(level);
90 double GetZoomLevel(const GURL& url) {
91 content::HostZoomMap* host_zoom_map = static_cast<content::HostZoomMap*>(
92 content::HostZoomMap::GetDefaultForBrowserContext(
93 browser()->profile()));
94 return host_zoom_map->GetZoomLevelForHostAndScheme(url.scheme(),
95 url.host());
98 std::vector<std::string> GetHostsWithZoomLevels() {
99 typedef content::HostZoomMap::ZoomLevelVector ZoomLevelVector;
100 content::HostZoomMap* host_zoom_map = static_cast<content::HostZoomMap*>(
101 content::HostZoomMap::GetDefaultForBrowserContext(
102 browser()->profile()));
103 content::HostZoomMap::ZoomLevelVector zoom_levels =
104 host_zoom_map->GetAllZoomLevels();
105 std::vector<std::string> results;
106 for (ZoomLevelVector::const_iterator it = zoom_levels.begin();
107 it != zoom_levels.end(); ++it)
108 results.push_back(it->host);
109 return results;
112 std::vector<std::string> GetHostsWithZoomLevelsFromPrefs() {
113 PrefService* prefs = browser()->profile()->GetPrefs();
114 const base::DictionaryValue* dictionaries =
115 prefs->GetDictionary(prefs::kPartitionPerHostZoomLevels);
116 const base::DictionaryValue* values = NULL;
117 std::string partition_key =
118 ChromeZoomLevelPrefs::GetHashForTesting(base::FilePath());
119 dictionaries->GetDictionary(partition_key, &values);
120 std::vector<std::string> results;
121 if (values) {
122 for (base::DictionaryValue::Iterator it(*values);
123 !it.IsAtEnd(); it.Advance())
124 results.push_back(it.key());
126 return results;
129 GURL ConstructTestServerURL(const char* url_template) {
130 return GURL(base::StringPrintf(
131 url_template, embedded_test_server()->port()));
134 private:
135 scoped_ptr<net::test_server::HttpResponse> HandleRequest(
136 const net::test_server::HttpRequest& request) {
137 return scoped_ptr<net::test_server::HttpResponse>(
138 new net::test_server::BasicHttpResponse);
141 // BrowserTestBase:
142 void SetUpOnMainThread() override {
143 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
144 embedded_test_server()->RegisterRequestHandler(base::Bind(
145 &HostZoomMapBrowserTest::HandleRequest, base::Unretained(this)));
146 host_resolver()->AddRule("*", "127.0.0.1");
149 DISALLOW_COPY_AND_ASSIGN(HostZoomMapBrowserTest);
152 #define PARTITION_KEY_PLACEHOLDER "NNN"
154 class HostZoomMapBrowserTestWithPrefs : public HostZoomMapBrowserTest {
155 public:
156 explicit HostZoomMapBrowserTestWithPrefs(const std::string& prefs_data)
157 : prefs_data_(prefs_data) {}
159 private:
160 // InProcessBrowserTest:
161 bool SetUpUserDataDirectory() override {
162 std::replace(prefs_data_.begin(), prefs_data_.end(), '\'', '\"');
163 // It seems the hash functions on different platforms can return different
164 // values for the same input, so make sure we test with the hash appropriate
165 // for the platform.
166 std::string hash_string =
167 ChromeZoomLevelPrefs::GetHashForTesting(base::FilePath());
168 std::string partition_key_placeholder(PARTITION_KEY_PLACEHOLDER);
169 size_t start_index;
170 while ((start_index = prefs_data_.find(partition_key_placeholder)) !=
171 std::string::npos) {
172 prefs_data_.replace(
173 start_index, partition_key_placeholder.size(), hash_string);
176 base::FilePath user_data_directory, path_to_prefs;
177 PathService::Get(chrome::DIR_USER_DATA, &user_data_directory);
178 path_to_prefs = user_data_directory
179 .AppendASCII(TestingProfile::kTestUserProfileDir)
180 .Append(chrome::kPreferencesFilename);
181 base::CreateDirectory(path_to_prefs.DirName());
182 base::WriteFile(
183 path_to_prefs, prefs_data_.c_str(), prefs_data_.size());
184 return true;
187 std::string prefs_data_;
189 DISALLOW_COPY_AND_ASSIGN(HostZoomMapBrowserTestWithPrefs);
192 // Zoom-related preferences demonstrating the two problems that
193 // could be caused by the bug. They incorrectly contain a per-host
194 // zoom level for the empty host; and a value for 'host1' that only
195 // differs from the default by epsilon. Neither should have been
196 // persisted.
197 const char kSanitizationTestPrefs[] =
198 "{'partition': {"
199 " 'default_zoom_level': { '" PARTITION_KEY_PLACEHOLDER "': 1.2 },"
200 " 'per_host_zoom_levels': {"
201 " '" PARTITION_KEY_PLACEHOLDER "': {"
202 " '': 1.1, 'host1': 1.20001, 'host2': 1.3 }"
203 " }"
204 "}}";
206 #undef PARTITION_KEY_PLACEHOLDER
208 class HostZoomMapSanitizationBrowserTest
209 : public HostZoomMapBrowserTestWithPrefs {
210 public:
211 HostZoomMapSanitizationBrowserTest()
212 : HostZoomMapBrowserTestWithPrefs(kSanitizationTestPrefs) {}
214 private:
215 DISALLOW_COPY_AND_ASSIGN(HostZoomMapSanitizationBrowserTest);
218 // Regression test for crbug.com/437392
219 IN_PROC_BROWSER_TEST_F(HostZoomMapBrowserTest, ZoomEventsWorkForOffTheRecord) {
220 GURL test_url(url::kAboutBlankURL);
221 std::string test_host(test_url.host());
222 std::string test_scheme(test_url.scheme());
223 Browser* incognito_browser =
224 OpenURLOffTheRecord(browser()->profile(), test_url);
226 content::WebContents* web_contents =
227 incognito_browser->tab_strip_model()->GetActiveWebContents();
229 content::BrowserContext* context = web_contents->GetBrowserContext();
230 EXPECT_TRUE(context->IsOffTheRecord());
231 ZoomLevelChangeObserver observer(context);
232 HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents);
234 double new_zoom_level =
235 host_zoom_map->GetZoomLevelForHostAndScheme(test_scheme, test_host) + 0.5;
236 host_zoom_map->SetZoomLevelForHostAndScheme(test_scheme, test_host,
237 new_zoom_level);
238 observer.BlockUntilZoomLevelForHostHasChanged(test_host);
239 EXPECT_EQ(new_zoom_level, host_zoom_map->GetZoomLevelForHostAndScheme(
240 test_scheme, test_host));
243 IN_PROC_BROWSER_TEST_F(
244 HostZoomMapBrowserTest,
245 WebviewBasedSigninUsesDefaultStoragePartitionForEmbedder) {
246 GURL test_url = ConstructTestServerURL(chrome::kChromeUIChromeSigninURL);
247 std::string test_host(test_url.host());
248 std::string test_scheme(test_url.scheme());
249 ui_test_utils::NavigateToURL(browser(), test_url);
251 content::WebContents* web_contents =
252 browser()->tab_strip_model()->GetActiveWebContents();
254 HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents);
256 // For the webview based sign-in code, the sign in page uses the default host
257 // zoom map.
258 HostZoomMap* default_profile_host_zoom_map =
259 HostZoomMap::GetDefaultForBrowserContext(browser()->profile());
260 // Since ChromeOS still uses IFrame-based signin, we should expect the
261 // storage partition to be different if Webview signin is not enabled.
262 if (switches::IsEnableWebviewBasedSignin())
263 EXPECT_EQ(host_zoom_map, default_profile_host_zoom_map);
264 else
265 EXPECT_NE(host_zoom_map, default_profile_host_zoom_map);
268 class HostZoomMapIframeSigninBrowserTest : public HostZoomMapBrowserTest {
269 public:
270 void SetUpCommandLine(base::CommandLine* command_line) override {
271 command_line->AppendSwitch(switches::kEnableIframeBasedSignin);
275 // Regression test for crbug.com/435017.
276 IN_PROC_BROWSER_TEST_F(HostZoomMapIframeSigninBrowserTest,
277 EventsForNonDefaultStoragePartition) {
278 ZoomLevelChangeObserver observer(browser()->profile());
279 // TODO(wjmaclean): Make this test more general by implementing a way to
280 // force a generic URL to be loaded in a non-default storage partition. This
281 // test currently relies on the signin page being loaded into a non-default
282 // storage partition (and verifies this is the case), but ultimately it would
283 // be better not to rely on what the signin page is doing.
284 GURL test_url = ConstructTestServerURL(chrome::kChromeUIChromeSigninURL);
285 std::string test_host(test_url.host());
286 std::string test_scheme(test_url.scheme());
287 ui_test_utils::NavigateToURL(browser(), test_url);
289 content::WebContents* web_contents =
290 browser()->tab_strip_model()->GetActiveWebContents();
292 // We are forcing non-webview based signin, so we expect the signin page to
293 // be in a different storage partition, and hence a different HostZoomMap.
294 HostZoomMap* host_zoom_map = HostZoomMap::GetForWebContents(web_contents);
296 EXPECT_FALSE(switches::IsEnableWebviewBasedSignin());
297 HostZoomMap* default_profile_host_zoom_map =
298 HostZoomMap::GetDefaultForBrowserContext(browser()->profile());
299 EXPECT_NE(host_zoom_map, default_profile_host_zoom_map);
301 double new_zoom_level =
302 host_zoom_map->GetZoomLevelForHostAndScheme(test_scheme, test_host) + 0.5;
303 host_zoom_map->SetZoomLevelForHostAndScheme(test_scheme, test_host,
304 new_zoom_level);
305 observer.BlockUntilZoomLevelForHostHasChanged(test_host);
306 EXPECT_EQ(new_zoom_level, host_zoom_map->GetZoomLevelForHostAndScheme(
307 test_scheme, test_host));
310 // Regression test for crbug.com/364399.
311 IN_PROC_BROWSER_TEST_F(HostZoomMapBrowserTest, ToggleDefaultZoomLevel) {
312 const double default_zoom_level = content::ZoomFactorToZoomLevel(1.5);
314 const char kTestURLTemplate1[] = "http://host1:%u/";
315 const char kTestURLTemplate2[] = "http://host2:%u/";
317 ZoomLevelChangeObserver observer(browser()->profile());
319 GURL test_url1 = ConstructTestServerURL(kTestURLTemplate1);
320 ui_test_utils::NavigateToURL(browser(), test_url1);
322 SetDefaultZoomLevel(default_zoom_level);
323 observer.BlockUntilZoomLevelForHostHasChanged(test_url1.host());
324 EXPECT_TRUE(
325 content::ZoomValuesEqual(default_zoom_level, GetZoomLevel(test_url1)));
327 GURL test_url2 = ConstructTestServerURL(kTestURLTemplate2);
328 ui_test_utils::NavigateToURLWithDisposition(
329 browser(), test_url2, NEW_FOREGROUND_TAB,
330 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION);
331 EXPECT_TRUE(
332 content::ZoomValuesEqual(default_zoom_level, GetZoomLevel(test_url2)));
334 content::WebContents* web_contents =
335 browser()->tab_strip_model()->GetActiveWebContents();
336 ui_zoom::PageZoom::Zoom(web_contents, content::PAGE_ZOOM_OUT);
337 observer.BlockUntilZoomLevelForHostHasChanged(test_url2.host());
338 EXPECT_FALSE(
339 content::ZoomValuesEqual(default_zoom_level, GetZoomLevel(test_url2)));
341 ui_zoom::PageZoom::Zoom(web_contents, content::PAGE_ZOOM_IN);
342 observer.BlockUntilZoomLevelForHostHasChanged(test_url2.host());
343 EXPECT_TRUE(
344 content::ZoomValuesEqual(default_zoom_level, GetZoomLevel(test_url2)));
346 // Now both tabs should be at the default zoom level, so there should not be
347 // any per-host values saved either to Pref, or internally in HostZoomMap.
348 EXPECT_TRUE(GetHostsWithZoomLevels().empty());
349 EXPECT_TRUE(GetHostsWithZoomLevelsFromPrefs().empty());
352 // Test that garbage data from crbug.com/364399 is cleared up on startup.
353 IN_PROC_BROWSER_TEST_F(HostZoomMapSanitizationBrowserTest, ClearOnStartup) {
354 EXPECT_THAT(GetHostsWithZoomLevels(), testing::ElementsAre("host2"));
355 EXPECT_THAT(GetHostsWithZoomLevelsFromPrefs(), testing::ElementsAre("host2"));
358 // Test four things:
359 // 1. Host zoom maps of parent profile and child profile are different.
360 // 2. Child host zoom map inherits zoom level at construction.
361 // 3. Change of zoom level doesn't propagate from child to parent.
362 // 4. Change of zoom level propagates from parent to child.
363 IN_PROC_BROWSER_TEST_F(HostZoomMapBrowserTest,
364 OffTheRecordProfileHostZoomMap) {
365 // Constants for test case.
366 const std::string host("example.com");
367 const double zoom_level_25 = 2.5;
368 const double zoom_level_30 = 3.0;
369 const double zoom_level_40 = 4.0;
371 Profile* parent_profile = browser()->profile();
372 Profile* child_profile =
373 static_cast<ProfileImpl*>(parent_profile)->GetOffTheRecordProfile();
374 HostZoomMap* parent_zoom_map =
375 HostZoomMap::GetDefaultForBrowserContext(parent_profile);
376 ASSERT_TRUE(parent_zoom_map);
378 parent_zoom_map->SetZoomLevelForHost(host, zoom_level_25);
379 ASSERT_EQ(parent_zoom_map->GetZoomLevelForHostAndScheme("http", host),
380 zoom_level_25);
382 // Prepare child host zoom map.
383 HostZoomMap* child_zoom_map =
384 HostZoomMap::GetDefaultForBrowserContext(child_profile);
385 ASSERT_TRUE(child_zoom_map);
387 // Verify.
388 EXPECT_NE(parent_zoom_map, child_zoom_map);
390 EXPECT_EQ(parent_zoom_map->GetZoomLevelForHostAndScheme("http", host),
391 child_zoom_map->GetZoomLevelForHostAndScheme("http", host)) <<
392 "Child must inherit from parent.";
394 child_zoom_map->SetZoomLevelForHost(host, zoom_level_30);
395 ASSERT_EQ(
396 child_zoom_map->GetZoomLevelForHostAndScheme("http", host),
397 zoom_level_30);
399 EXPECT_NE(parent_zoom_map->GetZoomLevelForHostAndScheme("http", host),
400 child_zoom_map->GetZoomLevelForHostAndScheme("http", host)) <<
401 "Child change must not propagate to parent.";
403 parent_zoom_map->SetZoomLevelForHost(host, zoom_level_40);
404 ASSERT_EQ(
405 parent_zoom_map->GetZoomLevelForHostAndScheme("http", host),
406 zoom_level_40);
408 EXPECT_EQ(parent_zoom_map->GetZoomLevelForHostAndScheme("http", host),
409 child_zoom_map->GetZoomLevelForHostAndScheme("http", host)) <<
410 "Parent change should propagate to child.";
411 base::RunLoop().RunUntilIdle();
414 IN_PROC_BROWSER_TEST_F(HostZoomMapBrowserTest,
415 ParentDefaultZoomPropagatesToIncognitoChild) {
416 Profile* parent_profile = browser()->profile();
417 Profile* child_profile =
418 static_cast<ProfileImpl*>(parent_profile)->GetOffTheRecordProfile();
420 double new_default_zoom_level =
421 parent_profile->GetZoomLevelPrefs()->GetDefaultZoomLevelPref() + 1.f;
422 HostZoomMap* parent_host_zoom_map =
423 HostZoomMap::GetDefaultForBrowserContext(parent_profile);
424 HostZoomMap* child_host_zoom_map =
425 HostZoomMap::GetDefaultForBrowserContext(child_profile);
426 ASSERT_TRUE(parent_host_zoom_map);
427 ASSERT_TRUE(child_host_zoom_map);
428 EXPECT_NE(parent_host_zoom_map, child_host_zoom_map);
429 EXPECT_NE(new_default_zoom_level, child_host_zoom_map->GetDefaultZoomLevel());
431 parent_profile->GetZoomLevelPrefs()->SetDefaultZoomLevelPref(
432 new_default_zoom_level);
433 EXPECT_EQ(new_default_zoom_level, child_host_zoom_map->GetDefaultZoomLevel());