#include "components/zoom/zoom_controller.h"
#include "base/memory/raw_ptr.h"
#include "base/process/kill.h"
#include "base/scoped_observation.h"
#include "base/test/bind.h"
#include "build/build_config.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/tabs/tab_enums.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/webui/signin/login_ui_test_utils.h"
#include "chrome/browser/ui/zoom/chrome_zoom_level_prefs.h"
#include "chrome/common/url_constants.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/prefs/pref_service.h"
#include "components/zoom/test/zoom_test_utils.h"
#include "components/zoom/zoom_observer.h"
#include "content/public/browser/disallow_activation_reason.h"
#include "content/public/browser/host_zoom_map.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/page_type.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/prerender_test_util.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "testing/gmock/include/gmock/gmock.h"
using zoom::ZoomChangedWatcher;
using zoom::ZoomController;
using zoom::ZoomObserver;
class ZoomControllerBrowserTest : public InProcessBrowserTest {
public:
ZoomControllerBrowserTest() = default;
~ZoomControllerBrowserTest() override = default;
void SetUpOnMainThread() override {
InProcessBrowserTest::SetUpOnMainThread();
host_resolver()->AddRule("a.com", "127.0.0.1");
host_resolver()->AddRule("b.com", "127.0.0.1");
}
void TestResetOnNavigation(ZoomController::ZoomMode zoom_mode) {
DCHECK(zoom_mode == ZoomController::ZOOM_MODE_ISOLATED ||
zoom_mode == ZoomController::ZOOM_MODE_MANUAL);
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
browser(), GURL("about:blank"), 1);
ZoomController* zoom_controller =
ZoomController::FromWebContents(web_contents);
double zoom_level = zoom_controller->GetDefaultZoomLevel();
zoom_controller->SetZoomMode(zoom_mode);
ZoomController::ZoomChangedEventData zoom_change_data(
web_contents, web_contents->GetPrimaryMainFrame()->GetFrameTreeNodeId(),
zoom_level, zoom_level, ZoomController::ZOOM_MODE_DEFAULT, false);
ZoomChangedWatcher zoom_change_watcher(web_contents, zoom_change_data);
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), GURL(chrome::kChromeUISettingsURL)));
zoom_change_watcher.Wait();
}
};
#if BUILDFLAG(IS_ANDROID)
#define MAYBE_CrashedTabsDoNotChangeZoom DISABLED_CrashedTabsDoNotChangeZoom
#else
#define MAYBE_CrashedTabsDoNotChangeZoom CrashedTabsDoNotChangeZoom
#endif
IN_PROC_BROWSER_TEST_F(ZoomControllerBrowserTest,
MAYBE_CrashedTabsDoNotChangeZoom) {
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ZoomController* zoom_controller =
ZoomController::FromWebContents(web_contents);
double old_zoom_level = zoom_controller->GetZoomLevel();
double new_zoom_level = old_zoom_level + 0.5;
content::RenderProcessHost* host =
web_contents->GetPrimaryMainFrame()->GetProcess();
{
content::RenderProcessHostWatcher crash_observer(
host, content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
host->Shutdown(0);
crash_observer.Wait();
}
EXPECT_FALSE(web_contents->GetPrimaryMainFrame()->IsRenderFrameLive());
zoom_controller->SetZoomLevel(new_zoom_level);
EXPECT_FLOAT_EQ(old_zoom_level, zoom_controller->GetZoomLevel());
}
IN_PROC_BROWSER_TEST_F(ZoomControllerBrowserTest, OnPreferenceChanged) {
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
double new_default_zoom_level = 1.0;
ZoomController::ZoomChangedEventData zoom_change_data(
web_contents, web_contents->GetPrimaryMainFrame()->GetFrameTreeNodeId(),
new_default_zoom_level, new_default_zoom_level,
ZoomController::ZOOM_MODE_DEFAULT, false);
ZoomChangedWatcher zoom_change_watcher(web_contents, zoom_change_data);
browser()->profile()->GetZoomLevelPrefs()->SetDefaultZoomLevelPref(
new_default_zoom_level);
zoom_change_watcher.Wait();
}
IN_PROC_BROWSER_TEST_F(ZoomControllerBrowserTest, ErrorPagesCanZoom) {
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GURL("http://kjfhkjsdf.com")));
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ZoomController* zoom_controller =
ZoomController::FromWebContents(web_contents);
EXPECT_EQ(
content::PAGE_TYPE_ERROR,
web_contents->GetController().GetLastCommittedEntry()->GetPageType());
EXPECT_EQ(GURL(content::kUnreachableWebDataURL),
content::HostZoomMap::GetURLForWebContents(web_contents));
double old_zoom_level = zoom_controller->GetZoomLevel();
double new_zoom_level = old_zoom_level + 0.5;
zoom_controller->SetZoomLevel(new_zoom_level);
EXPECT_FLOAT_EQ(new_zoom_level, zoom_controller->GetZoomLevel());
}
IN_PROC_BROWSER_TEST_F(ZoomControllerBrowserTest,
ErrorPagesCanZoomAfterTabRestore) {
GURL url("http://kjfhkjsdf.com");
TabStripModel* tab_strip = browser()->tab_strip_model();
ASSERT_TRUE(tab_strip);
ui_test_utils::NavigateToURLWithDisposition(
browser(), url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
{
content::WebContents* web_contents = tab_strip->GetActiveWebContents();
EXPECT_EQ(
content::PAGE_TYPE_ERROR,
web_contents->GetController().GetLastCommittedEntry()->GetPageType());
content::WebContentsDestroyedWatcher destroyed_watcher(web_contents);
tab_strip->CloseWebContentsAt(tab_strip->active_index(),
TabCloseTypes::CLOSE_CREATE_HISTORICAL_TAB);
destroyed_watcher.Wait();
}
EXPECT_EQ(1, tab_strip->count());
content::WebContentsAddedObserver new_web_contents_observer;
chrome::RestoreTab(browser());
content::WebContents* web_contents =
new_web_contents_observer.GetWebContents();
content::WaitForLoadStop(web_contents);
EXPECT_EQ(2, tab_strip->count());
EXPECT_EQ(
content::PAGE_TYPE_ERROR,
web_contents->GetController().GetLastCommittedEntry()->GetPageType());
ZoomController* zoom_controller =
ZoomController::FromWebContents(web_contents);
double old_zoom_level = zoom_controller->GetZoomLevel();
double new_zoom_level = old_zoom_level + 0.5;
zoom_controller->SetZoomLevel(new_zoom_level);
EXPECT_FLOAT_EQ(new_zoom_level, zoom_controller->GetZoomLevel());
}
IN_PROC_BROWSER_TEST_F(ZoomControllerBrowserTest, Observe) {
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
double new_zoom_level = 1.0;
ZoomController::ZoomChangedEventData zoom_change_data(
web_contents, web_contents->GetPrimaryMainFrame()->GetFrameTreeNodeId(),
new_zoom_level, new_zoom_level, ZoomController::ZOOM_MODE_DEFAULT,
false);
ZoomChangedWatcher zoom_change_watcher(web_contents, zoom_change_data);
content::HostZoomMap* host_zoom_map =
content::HostZoomMap::GetDefaultForBrowserContext(
web_contents->GetBrowserContext());
host_zoom_map->SetZoomLevelForHost("about:blank", new_zoom_level);
zoom_change_watcher.Wait();
}
IN_PROC_BROWSER_TEST_F(ZoomControllerBrowserTest, ObserveDisabledModeEvent) {
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ZoomController* zoom_controller =
ZoomController::FromWebContents(web_contents);
double default_zoom_level = zoom_controller->GetDefaultZoomLevel();
double new_zoom_level = default_zoom_level + 1.0;
zoom_controller->SetZoomLevel(new_zoom_level);
ZoomController::ZoomChangedEventData zoom_change_data(
web_contents, web_contents->GetPrimaryMainFrame()->GetFrameTreeNodeId(),
new_zoom_level, default_zoom_level, ZoomController::ZOOM_MODE_DISABLED,
true);
ZoomChangedWatcher zoom_change_watcher(web_contents, zoom_change_data);
zoom_controller->SetZoomMode(ZoomController::ZOOM_MODE_DISABLED);
zoom_change_watcher.Wait();
}
IN_PROC_BROWSER_TEST_F(ZoomControllerBrowserTest, PerTabModeResetSendsEvent) {
TestResetOnNavigation(ZoomController::ZOOM_MODE_ISOLATED);
}
IN_PROC_BROWSER_TEST_F(ZoomControllerBrowserTest, NavigationResetsManualMode) {
TestResetOnNavigation(ZoomController::ZOOM_MODE_MANUAL);
}
#if !BUILDFLAG(IS_MAC)
IN_PROC_BROWSER_TEST_F(ZoomControllerBrowserTest,
RestoredPageScaleFromNavigation) {
ASSERT_TRUE(embedded_test_server()->Start());
const GURL url_a(embedded_test_server()->GetURL("a.com", "/title1.html"));
const GURL url_b(embedded_test_server()->GetURL("b.com", "/title2.html"));
content::RenderFrameHostWrapper rfh_a(
ui_test_utils::NavigateToURL(browser(), url_a));
ASSERT_TRUE(rfh_a);
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
zoom::ZoomController* zoom_controller =
zoom::ZoomController::FromWebContents(web_contents);
EXPECT_TRUE(zoom_controller->PageScaleFactorIsOne());
EXPECT_FALSE(chrome::CanResetZoom(web_contents));
EXPECT_FALSE(chrome::IsCommandEnabled(browser(), IDC_ZOOM_NORMAL));
const gfx::Rect contents_rect = web_contents->GetContainerBounds();
const gfx::PointF anchor(contents_rect.width() / 2,
contents_rect.height() / 2);
const float scale_change = 1.5;
base::RunLoop run_loop;
content::SimulateTouchscreenPinch(web_contents, anchor, scale_change,
run_loop.QuitClosure());
run_loop.Run();
base::RepeatingClosure synchronize_threads =
base::BindLambdaForTesting([web_contents]() {
content::MainThreadFrameObserver observer(
web_contents->GetRenderWidgetHostView()->GetRenderWidgetHost());
observer.Wait();
});
synchronize_threads.Run();
EXPECT_FALSE(zoom_controller->PageScaleFactorIsOne());
EXPECT_TRUE(chrome::CanResetZoom(web_contents));
EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_ZOOM_NORMAL));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url_b));
if (rfh_a) {
ASSERT_TRUE(rfh_a->IsInactiveAndDisallowActivation(
content::DisallowActivationReasonId::kForTesting));
}
ASSERT_TRUE(rfh_a.WaitUntilRenderFrameDeleted());
synchronize_threads.Run();
EXPECT_TRUE(zoom_controller->PageScaleFactorIsOne());
EXPECT_FALSE(chrome::CanResetZoom(web_contents));
EXPECT_FALSE(chrome::IsCommandEnabled(browser(), IDC_ZOOM_NORMAL));
ASSERT_TRUE(web_contents->GetController().CanGoBack());
web_contents->GetController().GoBack();
ASSERT_TRUE(content::WaitForLoadStop(web_contents));
synchronize_threads.Run();
EXPECT_FALSE(zoom_controller->PageScaleFactorIsOne());
EXPECT_TRUE(chrome::CanResetZoom(web_contents));
EXPECT_TRUE(chrome::IsCommandEnabled(browser(), IDC_ZOOM_NORMAL));
}
#endif
#if !BUILDFLAG(IS_CHROMEOS)
IN_PROC_BROWSER_TEST_F(ZoomControllerBrowserTest,
SettingsZoomAfterSigninWorks) {
GURL signin_url(std::string(chrome::kChromeUIChromeSigninURL)
.append("?access_point=0&reason=5"));
ui_test_utils::NavigateToURLWithDisposition(
browser(), signin_url, WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
login_ui_test_utils::WaitUntilUIReady(browser());
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_NE(
content::PAGE_TYPE_ERROR,
web_contents->GetController().GetLastCommittedEntry()->GetPageType());
EXPECT_EQ(signin_url, web_contents->GetLastCommittedURL());
ZoomController* zoom_controller =
ZoomController::FromWebContents(web_contents);
GURL settings_url(chrome::kChromeUISettingsURL);
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), settings_url));
EXPECT_NE(
content::PAGE_TYPE_ERROR,
web_contents->GetController().GetLastCommittedEntry()->GetPageType());
EXPECT_EQ(2, browser()->tab_strip_model()->count());
EXPECT_EQ(web_contents, browser()->tab_strip_model()->GetActiveWebContents());
EXPECT_EQ(settings_url, web_contents->GetLastCommittedURL());
EXPECT_EQ(zoom_controller, ZoomController::FromWebContents(web_contents));
double old_zoom_level = zoom_controller->GetZoomLevel();
double new_zoom_level = old_zoom_level + 0.5;
ZoomController::ZoomChangedEventData zoom_change_data(
web_contents, web_contents->GetPrimaryMainFrame()->GetFrameTreeNodeId(),
old_zoom_level, new_zoom_level, ZoomController::ZOOM_MODE_DEFAULT,
true);
ZoomChangedWatcher zoom_change_watcher(web_contents, zoom_change_data);
zoom_controller->SetZoomLevel(new_zoom_level);
zoom_change_watcher.Wait();
}
#endif
class ZoomControllerForPrerenderingTest : public ZoomControllerBrowserTest,
public zoom::ZoomObserver {
public:
ZoomControllerForPrerenderingTest()
: prerender_helper_(base::BindRepeating(
&ZoomControllerForPrerenderingTest::GetWebContents,
base::Unretained(this))) {}
~ZoomControllerForPrerenderingTest() override = default;
void SetUp() override {
prerender_helper_.RegisterServerRequestMonitor(embedded_test_server());
ZoomControllerBrowserTest::SetUp();
}
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
ASSERT_TRUE(embedded_test_server()->Start());
auto* zoom_controller = ZoomController::FromWebContents(GetWebContents());
zoom_observation_.Observe(zoom_controller);
}
void TearDownOnMainThread() override { zoom_observation_.Reset(); }
content::test::PrerenderTestHelper& prerender_helper() {
return prerender_helper_;
}
content::WebContents* GetWebContents() {
return browser()->tab_strip_model()->GetActiveWebContents();
}
void OnZoomControllerDestroyed(
zoom::ZoomController* zoom_controller) override {
zoom_observation_.Reset();
}
void OnZoomChanged(
const zoom::ZoomController::ZoomChangedEventData& data) override {
is_on_zoom_changed_called_ = true;
}
void reset_is_on_zoom_changed_called() { is_on_zoom_changed_called_ = false; }
bool is_on_zoom_changed_called() { return is_on_zoom_changed_called_; }
private:
bool is_on_zoom_changed_called_ = false;
content::test::PrerenderTestHelper prerender_helper_;
base::ScopedObservation<zoom::ZoomController, zoom::ZoomObserver>
zoom_observation_{this};
};
IN_PROC_BROWSER_TEST_F(ZoomControllerForPrerenderingTest,
DontFireZoomChangedListenerOnPrerender) {
GURL initial_url = embedded_test_server()->GetURL("/empty.html");
GURL prerender_url = embedded_test_server()->GetURL("/title1.html");
ASSERT_NE(ui_test_utils::NavigateToURL(browser(), initial_url), nullptr);
reset_is_on_zoom_changed_called();
content::FrameTreeNodeId host_id =
prerender_helper().AddPrerender(prerender_url);
content::test::PrerenderHostObserver host_observer(*GetWebContents(),
host_id);
EXPECT_FALSE(host_observer.was_activated());
EXPECT_FALSE(is_on_zoom_changed_called());
prerender_helper().NavigatePrimaryPage(prerender_url);
EXPECT_TRUE(host_observer.was_activated());
EXPECT_TRUE(is_on_zoom_changed_called());
}