blob: 5023e56cd568248ff936bf090630fed8b26805fe [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/browser.h"
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <string>
#include "base/command_line.h"
#include "base/compiler_specific.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/location.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/ranges/algorithm.h"
#include "base/run_loop.h"
#include "base/scoped_observation.h"
#include "base/strings/string_split.h"
#include "base/strings/utf_string_conversions.h"
#include "base/system/sys_info.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/metrics/user_action_tester.h"
#include "base/test/test_future.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "chrome/app/chrome_command_ids.h"
#include "chrome/browser/apps/app_service/app_launch_params.h"
#include "chrome/browser/apps/app_service/app_registry_cache_waiter.h"
#include "chrome/browser/apps/app_service/app_service_proxy.h"
#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
#include "chrome/browser/apps/app_service/browser_app_launcher.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_content_browser_client.h"
#include "chrome/browser/command_updater.h"
#include "chrome/browser/defaults.h"
#include "chrome/browser/devtools/devtools_window_testing.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/tab_helper.h"
#include "chrome/browser/first_run/first_run.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/prefs/incognito_mode_prefs.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/resource_coordinator/lifecycle_unit.h"
#include "chrome/browser/resource_coordinator/tab_manager.h"
#include "chrome/browser/search/search.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/sessions/session_service_factory.h"
#include "chrome/browser/translate/chrome_translate_client.h"
#include "chrome/browser/ui/browser_command_controller.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/browser_ui_prefs.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/exclusive_access/exclusive_access_context.h"
#include "chrome/browser/ui/exclusive_access/exclusive_access_manager.h"
#include "chrome/browser/ui/search/search_tab_helper.h"
#include "chrome/browser/ui/startup/launch_mode_recorder.h"
#include "chrome/browser/ui/startup/startup_browser_creator.h"
#include "chrome/browser/ui/startup/startup_browser_creator_impl.h"
#include "chrome/browser/ui/startup/startup_types.h"
#include "chrome/browser/ui/startup/web_app_startup_utils.h"
#include "chrome/browser/ui/tabs/pinned_tab_codec.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
#include "chrome/browser/web_applications/policy/web_app_policy_constants.h"
#include "chrome/browser/web_applications/policy/web_app_policy_manager.h"
#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
#include "chrome/browser/web_applications/web_app_id_constants.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/common/buildflags.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/manifest_handlers/app_launch_info.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "chrome/grit/branded_strings.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/search_test_utils.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/captive_portal/core/buildflags.h"
#include "components/content_settings/core/browser/host_content_settings_map.h"
#include "components/embedder_support/switches.h"
#include "components/javascript_dialogs/app_modal_dialog_controller.h"
#include "components/javascript_dialogs/app_modal_dialog_queue.h"
#include "components/javascript_dialogs/app_modal_dialog_view.h"
#include "components/javascript_dialogs/tab_modal_dialog_manager.h"
#include "components/omnibox/common/omnibox_focus_state.h"
#include "components/policy/core/common/policy_pref_names.h"
#include "components/prefs/pref_service.h"
#include "components/search_engines/template_url_service.h"
#include "components/services/app_service/public/cpp/app_launch_util.h"
#include "components/sessions/core/command_storage_manager_test_helper.h"
#include "components/strings/grit/components_strings.h"
#include "components/translate/core/browser/language_state.h"
#include "components/translate/core/common/language_detection_details.h"
#include "components/webapps/common/web_app_id.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/favicon_status.h"
#include "content/public/browser/host_zoom_map.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/reload_type.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/browser/resource_context.h"
#include "content/public/browser/ssl_status.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/url_constants.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/download_test_observer.h"
#include "content/public/test/no_renderer_crashes_assertion.h"
#include "content/public/test/slow_http_response.h"
#include "content/public/test/test_navigation_observer.h"
#include "extensions/browser/extension_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/uninstall_reason.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension.h"
#include "extensions/common/extension_set.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "net/test/embedded_test_server/request_handler_util.h"
#include "net/test/spawned_test_server/spawned_test_server.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "third_party/abseil-cpp/absl/cleanup/cleanup.h"
#include "third_party/blink/public/mojom/frame/fullscreen.mojom.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/page_transition_types.h"
#include "ui/base/ui_base_features.h"
#if BUILDFLAG(IS_MAC)
#include "base/apple/scoped_nsautorelease_pool.h"
#include "chrome/browser/ui/cocoa/test/run_loop_testing.h"
#include "ui/accelerated_widget_mac/ca_transaction_observer.h"
#include "ui/base/test/scoped_fake_nswindow_fullscreen.h"
#endif
#if BUILDFLAG(IS_WIN)
#include "base/i18n/rtl.h"
#include "base/test/file_path_reparse_point_win.h"
#endif
using base::ASCIIToUTF16;
using content::HostZoomMap;
using content::NavigationController;
using content::NavigationEntry;
using content::OpenURLParams;
using content::Referrer;
using content::WebContents;
using content::WebContentsObserver;
using extensions::Extension;
using javascript_dialogs::AppModalDialogController;
using javascript_dialogs::AppModalDialogQueue;
namespace {
const char* kBeforeUnloadHTML =
"<html><head><title>beforeunload</title></head><body>"
"<script>window.onbeforeunload=function(e){return 'foo'}</script>"
"</body></html>";
const char16_t* kOpenNewBeforeUnloadPage =
u"w=window.open(); w.onbeforeunload=function(e){return 'foo'};";
const base::FilePath::CharType* kTitle1File = FILE_PATH_LITERAL("title1.html");
const base::FilePath::CharType* kTitle2File = FILE_PATH_LITERAL("title2.html");
// Given a page title, returns the expected window caption string.
std::u16string WindowCaptionFromPageTitle(const std::u16string& page_title) {
#if BUILDFLAG(IS_MAC)
// On Mac, we don't want to suffix the page title with the application name.
if (page_title.empty())
return l10n_util::GetStringUTF16(IDS_BROWSER_WINDOW_MAC_TAB_UNTITLED);
return page_title;
#elif BUILDFLAG(IS_CHROMEOS_LACROS)
// On Lacros, we don't want to suffix the page title with the application
// name. Note that the default title for empty title is different from Mac.
if (page_title.empty()) {
return l10n_util::GetStringUTF16(IDS_DEFAULT_TAB_TITLE);
}
return page_title;
#else
if (page_title.empty())
return l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
return l10n_util::GetStringFUTF16(IDS_BROWSER_WINDOW_TITLE_FORMAT,
page_title);
#endif
}
// Returns the number of active RenderProcessHosts.
int CountRenderProcessHosts() {
int result = 0;
for (content::RenderProcessHost::iterator i(
content::RenderProcessHost::AllHostsIterator());
!i.IsAtEnd(); i.Advance())
++result;
return result;
}
class TabClosingObserver : public TabStripModelObserver {
public:
TabClosingObserver() = default;
TabClosingObserver(const TabClosingObserver&) = delete;
TabClosingObserver& operator=(const TabClosingObserver&) = delete;
void OnTabStripModelChanged(
TabStripModel* tab_strip_model,
const TabStripModelChange& change,
const TabStripSelectionChange& selection) override {
if (change.type() != TabStripModelChange::kRemoved)
return;
auto* remove = change.GetRemove();
for (const auto& contents : remove->contents) {
if (contents.remove_reason == TabStripModelChange::RemoveReason::kDeleted)
closing_count_ += 1;
}
}
int closing_count() const { return closing_count_; }
private:
int closing_count_ = 0;
};
// Used by CloseWithAppMenuOpen. Posts a CloseWindowCallback and shows the app
// menu.
void RunCloseWithAppMenuCallback(Browser* browser) {
// ShowAppMenu is modal under views. Schedule a task that closes the window.
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&chrome::CloseWindow, browser));
chrome::ShowAppMenu(browser);
}
class RenderViewSizeObserver : public content::WebContentsObserver {
public:
RenderViewSizeObserver(content::WebContents* web_contents,
BrowserWindow* browser_window)
: WebContentsObserver(web_contents), browser_window_(browser_window) {}
RenderViewSizeObserver(const RenderViewSizeObserver&) = delete;
RenderViewSizeObserver& operator=(const RenderViewSizeObserver&) = delete;
void GetSizeForRenderViewHost(content::RenderViewHost* render_view_host,
gfx::Size* rwhv_create_size,
gfx::Size* rwhv_commit_size,
gfx::Size* wcv_commit_size) {
RenderViewSizes::const_iterator result = render_view_sizes_.end();
result = render_view_sizes_.find(render_view_host);
if (result != render_view_sizes_.end()) {
*rwhv_create_size = result->second.rwhv_create_size;
*rwhv_commit_size = result->second.rwhv_commit_size;
*wcv_commit_size = result->second.wcv_commit_size;
}
}
void set_wcv_resize_insets(const gfx::Size& wcv_resize_insets) {
wcv_resize_insets_ = wcv_resize_insets;
}
// Cache the size when RenderViewHost's main frame is first created.
void RenderFrameCreated(
content::RenderFrameHost* render_frame_host) override {
if (!render_frame_host->GetParent()) {
content::RenderViewHost* render_view_host =
render_frame_host->GetRenderViewHost();
render_view_sizes_[render_view_host].rwhv_create_size =
render_view_host->GetWidget()->GetView()->GetViewBounds().size();
}
}
// Enlarge WebContentsView by |wcv_resize_insets_| while the navigation entry
// is pending.
void DidStartNavigation(
content::NavigationHandle* navigation_handle) override {
Resize();
}
void Resize() {
if (wcv_resize_insets_.IsEmpty())
return;
// Resizing the main browser window by |wcv_resize_insets_| will
// automatically resize the WebContentsView by the same amount.
// Just resizing WebContentsView directly doesn't work on Linux, because the
// next automatic layout of the browser window will resize WebContentsView
// back to the previous size. To make it consistent, resize main browser
// window on all platforms.
gfx::Rect bounds(browser_window_->GetBounds());
gfx::Size size(bounds.size());
size.Enlarge(wcv_resize_insets_.width(), wcv_resize_insets_.height());
bounds.set_size(size);
browser_window_->SetBounds(bounds);
// Let the message loop run so that resize actually takes effect.
content::RunAllPendingInMessageLoop();
}
// Cache the sizes of RenderWidgetHostView and WebContentsView when the
// navigation entry is committed, which is before
// WebContentsObserver::DidFinishNavigation is called.
void NavigationEntryCommitted(
const content::LoadCommittedDetails& details) override {
content::RenderViewHost* rvh =
web_contents()->GetPrimaryMainFrame()->GetRenderViewHost();
render_view_sizes_[rvh].rwhv_commit_size =
web_contents()->GetRenderWidgetHostView()->GetViewBounds().size();
render_view_sizes_[rvh].wcv_commit_size =
web_contents()->GetContainerBounds().size();
}
private:
struct Sizes {
gfx::Size rwhv_create_size; // Size of RenderWidgetHostView when created.
gfx::Size rwhv_commit_size; // Size of RenderWidgetHostView when committed.
gfx::Size wcv_commit_size; // Size of WebContentsView when committed.
};
typedef std::map<content::RenderViewHost*, Sizes> RenderViewSizes;
RenderViewSizes render_view_sizes_;
// Enlarge WebContentsView by this size insets in
// DidStartNavigation.
gfx::Size wcv_resize_insets_;
raw_ptr<BrowserWindow> browser_window_; // Weak ptr.
};
} // namespace
class BrowserTest : public extensions::ExtensionBrowserTest,
public BrowserListObserver {
protected:
void SetUpOnMainThread() override {
extensions::ExtensionBrowserTest::SetUpOnMainThread();
host_resolver()->AddRule("*", "127.0.0.1");
}
// In RTL locales wrap the page title with RTL embedding characters so that it
// matches the value returned by GetWindowTitle().
std::u16string LocaleWindowCaptionFromPageTitle(
const std::u16string& expected_title) {
std::u16string page_title = WindowCaptionFromPageTitle(expected_title);
#if BUILDFLAG(IS_WIN)
std::string locale = g_browser_process->GetApplicationLocale();
if (base::i18n::GetTextDirectionForLocale(locale.c_str()) ==
base::i18n::RIGHT_TO_LEFT) {
base::i18n::WrapStringWithLTRFormatting(&page_title);
}
return page_title;
#else
// Do we need to use the above code on POSIX as well?
return page_title;
#endif
}
void OpenURLFromTab(WebContents* source, OpenURLParams params) {
browser()->OpenURLFromTab(source, params,
/*navigation_handle_callback=*/{});
}
// Returns the app extension aptly named "App Test".
const Extension* GetExtension() {
extensions::ExtensionRegistry* registry =
extensions::ExtensionRegistry::Get(browser()->profile());
for (const scoped_refptr<const extensions::Extension>& extension :
registry->enabled_extensions()) {
if (extension->name() == "App Test")
return extension.get();
}
NOTREACHED();
return nullptr;
}
// BrowserListObserver:
MOCK_METHOD(void,
OnBrowserCloseCancelled,
(Browser * browser, BrowserClosingStatus reason),
(override));
};
// Launch the app on a page with no title, check that the app title was set
// correctly.
IN_PROC_BROWSER_TEST_F(BrowserTest, NoTitle) {
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(kTitle1File))));
EXPECT_EQ(
LocaleWindowCaptionFromPageTitle(u"title1.html"),
browser()->GetWindowTitleForCurrentTab(true /* include_app_name */));
std::u16string tab_title;
ASSERT_TRUE(ui_test_utils::GetCurrentTabTitle(browser(), &tab_title));
EXPECT_EQ(u"title1.html", tab_title);
}
// Check that a file:// URL displays the filename, but no path, with any ref or
// query parameters following it if the content does not have a <title> tag.
// Specifically verify the cases where the ref or query parameters have a '/'
// character in them. This is a regression test for
// https://crbug.com/503003.
IN_PROC_BROWSER_TEST_F(BrowserTest, NoTitleFileUrl) {
// Note that the host names used and the order of these cases are by design.
// There must be unique query parameters and references per case (i.e. the
// indexed foo*.com hosts) because if the same query parameter is repeated in
// a row, then the navigation may not actually happen, as it will only appear
// as a reference change. Additionally, cases with references first must
// appear after a query parameter case since otherwise it will not be a
// navigation.
struct {
std::string suffix;
std::string message;
} cases[]{
{"#https://foo1.com", "file:/// URL with slash in ref"},
{"?x=https://foo2.com", "file:/// URL with slash in query parameter"},
{"?x=https://foo3.com#https://foo3.com",
"file:/// URL with slashes in query parameter and ref"},
{"#https://foo4.com?x=https://foo4.com",
"file:/// URL with slashes in ref and query parameter"},
{"?x=https://foo6.com?x=https://foo6.com",
"file:/// URL with slashes in multiple query parameter"},
{"#https://foo5.com#https://foo5.com",
"file:/// URL with slashes in multiple refs"}};
GURL prefix_url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(kTitle1File));
std::u16string tab_title;
std::u16string test_title;
for (const auto& c : cases) {
SCOPED_TRACE(c.message);
GURL url(prefix_url.spec() + c.suffix);
test_title = u"title1.html" + ASCIIToUTF16(c.suffix);
content::TitleWatcher title_watcher(
browser()->tab_strip_model()->GetActiveWebContents(), test_title);
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
EXPECT_EQ(test_title, title_watcher.WaitAndGetTitle());
}
}
// Launch the app, navigate to a page with a title, check that the app title
// was set correctly.
IN_PROC_BROWSER_TEST_F(BrowserTest, Title) {
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(kTitle2File))));
const std::u16string test_title(u"Title Of Awesomeness");
EXPECT_EQ(
LocaleWindowCaptionFromPageTitle(test_title),
browser()->GetWindowTitleForCurrentTab(true /* include_app_name */));
std::u16string tab_title;
ASSERT_TRUE(ui_test_utils::GetCurrentTabTitle(browser(), &tab_title));
EXPECT_EQ(test_title, tab_title);
}
#if BUILDFLAG(ENABLE_CAPTIVE_PORTAL_DETECTION)
// Check that the title is different when a page is opened in a captive portal
// window.
IN_PROC_BROWSER_TEST_F(BrowserTest, CaptivePortalWindowTitle) {
const GURL url = ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(kTitle2File));
NavigateParams captive_portal_params(browser(), url,
ui::PAGE_TRANSITION_TYPED);
captive_portal_params.disposition = WindowOpenDisposition::NEW_POPUP;
captive_portal_params.is_captive_portal_popup = true;
ui_test_utils::NavigateToURL(&captive_portal_params);
std::u16string captive_portal_window_title =
chrome::FindBrowserWithTab(
captive_portal_params.navigated_or_inserted_contents)
->GetWindowTitleForCurrentTab(true /* include_app_name */);
NavigateParams normal_params(browser(), url, ui::PAGE_TRANSITION_TYPED);
normal_params.disposition = WindowOpenDisposition::NEW_POPUP;
ui_test_utils::NavigateToURL(&normal_params);
std::u16string normal_window_title =
chrome::FindBrowserWithTab(normal_params.navigated_or_inserted_contents)
->GetWindowTitleForCurrentTab(true /* include_app_name */);
ASSERT_NE(captive_portal_window_title, normal_window_title);
}
#endif
IN_PROC_BROWSER_TEST_F(BrowserTest, NoJavaScriptDialogsActivateTab) {
// Set up two tabs, with the tab at index 0 active.
GURL url(ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(kTitle1File)));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
ASSERT_TRUE(AddTabAtIndex(0, url, ui::PAGE_TRANSITION_TYPED));
EXPECT_EQ(2, browser()->tab_strip_model()->count());
EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
WebContents* second_tab = browser()->tab_strip_model()->GetWebContentsAt(1);
ASSERT_TRUE(second_tab);
// Show a confirm() dialog from the tab at index 1. The active index shouldn't
// budge.
{
content::WebContentsConsoleObserver confirm_observer(second_tab);
confirm_observer.SetPattern("*confirm*suppressed*");
second_tab->GetPrimaryMainFrame()->ExecuteJavaScriptForTests(
u"confirm('Activate!');", base::NullCallback());
ASSERT_TRUE(confirm_observer.Wait());
}
EXPECT_EQ(2, browser()->tab_strip_model()->count());
EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
// Show a prompt() dialog from the tab at index 1. The active index shouldn't
// budge.
{
content::WebContentsConsoleObserver prompt_observer(second_tab);
prompt_observer.SetPattern("*prompt*suppressed*");
second_tab->GetPrimaryMainFrame()->ExecuteJavaScriptForTests(
u"prompt('Activate!');", base::NullCallback());
ASSERT_TRUE(prompt_observer.Wait());
}
EXPECT_EQ(2, browser()->tab_strip_model()->count());
EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
// Show an alert() dialog from the tab at index 1. The active index shouldn't
// budge.
auto* js_dialog_manager =
javascript_dialogs::TabModalDialogManager::FromWebContents(second_tab);
base::RunLoop alert_wait;
js_dialog_manager->SetDialogShownCallbackForTesting(alert_wait.QuitClosure());
second_tab->GetPrimaryMainFrame()->ExecuteJavaScriptForTests(
u"alert('Activate!');", base::NullCallback());
alert_wait.Run();
EXPECT_EQ(2, browser()->tab_strip_model()->count());
EXPECT_EQ(0, browser()->tab_strip_model()->active_index());
}
// Create 34 tabs and verify that a lot of processes have been created. The
// exact number of processes depends on the amount of memory. Previously we
// had a hard limit of 31 processes and this test is mainly directed at
// verifying that we don't crash when we pass this limit.
// Warning: this test can take >30 seconds when running on a slow (low
// memory?) Mac builder.
// Test is flaky on Win, Linux, Mac: https://crbug.com/1099186.
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC)
#define MAYBE_ThirtyFourTabs DISABLED_ThirtyFourTabs
#else
#define MAYBE_ThirtyFourTabs ThirtyFourTabs
#endif
IN_PROC_BROWSER_TEST_F(BrowserTest, MAYBE_ThirtyFourTabs) {
GURL url(ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(kTitle2File)));
// There is one initial tab.
const int kTabCount = 34;
for (int ix = 0; ix != (kTabCount - 1); ++ix) {
chrome::AddSelectedTabWithURL(browser(), url, ui::PAGE_TRANSITION_TYPED);
}
EXPECT_EQ(kTabCount, browser()->tab_strip_model()->count());
// See GetMaxRendererProcessCount() in
// content/browser/renderer_host/render_process_host_impl.cc
// for the algorithm to decide how many processes to create.
const int kExpectedProcessCount =
#if defined(ARCH_CPU_64_BITS)
12;
#else
17;
#endif
if (base::SysInfo::AmountOfPhysicalMemoryMB() >= 2048) {
EXPECT_GE(CountRenderProcessHosts(), kExpectedProcessCount);
} else {
EXPECT_LT(CountRenderProcessHosts(), kExpectedProcessCount);
}
}
// Test that a browser-initiated navigation to an aborted URL load leaves around
// a pending entry if we start from the NTP but not from a normal page.
// See http://crbug.com/355537.
IN_PROC_BROWSER_TEST_F(BrowserTest, ClearPendingOnFailUnlessNTP) {
ASSERT_TRUE(embedded_test_server()->Start());
WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(),
GURL(chrome::kChromeUINewTabURL)));
// Navigate to a 204 URL (aborts with no content) on the NTP and make sure it
// sticks around so that the user can edit it.
GURL abort_url(embedded_test_server()->GetURL("/nocontent"));
{
content::LoadStopObserver stop_observer(web_contents);
browser()->OpenURL(
OpenURLParams(abort_url, Referrer(), WindowOpenDisposition::CURRENT_TAB,
ui::PAGE_TRANSITION_TYPED, false),
/*navigation_handle_callback=*/{});
stop_observer.Wait();
EXPECT_TRUE(web_contents->GetController().GetPendingEntry());
EXPECT_EQ(abort_url, web_contents->GetVisibleURL());
}
// Navigate to a real URL.
GURL real_url(embedded_test_server()->GetURL("/title1.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), real_url));
EXPECT_EQ(real_url, web_contents->GetVisibleURL());
// Now navigating to a 204 URL should clear the pending entry.
{
content::LoadStopObserver stop_observer(web_contents);
browser()->OpenURL(
OpenURLParams(abort_url, Referrer(), WindowOpenDisposition::CURRENT_TAB,
ui::PAGE_TRANSITION_TYPED, false),
/*navigation_handle_callback=*/{});
stop_observer.Wait();
EXPECT_FALSE(web_contents->GetController().GetPendingEntry());
EXPECT_EQ(real_url, web_contents->GetVisibleURL());
}
}
// Test for crbug.com/1232447. Ensure that a non-user-initiated navigation
// doesn't commit while a JS dialog is showing.
IN_PROC_BROWSER_TEST_F(BrowserTest, DialogDefersNavigationCommit) {
WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(embedded_test_server()->Start());
const GURL kEmptyUrl(embedded_test_server()->GetURL("/empty.html"));
const GURL kSecondUrl(embedded_test_server()->GetURL("/title1.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), kEmptyUrl));
content::TestNavigationManager manager(contents, kSecondUrl);
auto* js_dialog_manager =
javascript_dialogs::TabModalDialogManager::FromWebContents(contents);
// Start a non-user-gesture navigation to the second page but block after the
// request is started.
{
auto script = content::JsReplace("window.location = $1;", kSecondUrl);
ASSERT_TRUE(content::ExecJs(contents->GetPrimaryMainFrame(), script,
content::EXECUTE_SCRIPT_NO_USER_GESTURE));
ASSERT_TRUE(manager.WaitForRequestStart());
}
// Show a modal JavaScript dialog.
{
base::RunLoop run_loop;
js_dialog_manager->SetDialogShownCallbackForTesting(run_loop.QuitClosure());
contents->GetPrimaryMainFrame()->ExecuteJavaScriptForTests(
u"alert('one'); ", base::NullCallback());
run_loop.Run();
ASSERT_TRUE(js_dialog_manager->IsShowingDialogForTesting());
}
// Continue the navigation through the response and on to commit. Since a
// dialog is showing, this should cause the navigation to be deferred before
// commit and the dialog should remain showing.
{
ASSERT_TRUE(manager.WaitForResponse());
manager.ResumeNavigation();
content::NavigationHandle* handle = manager.GetNavigationHandle();
EXPECT_FALSE(handle->IsWaitingToCommit());
EXPECT_TRUE(handle->IsCommitDeferringConditionDeferredForTesting());
EXPECT_TRUE(js_dialog_manager->IsShowingDialogForTesting());
}
// Dismiss the dialog. This should resume the navigation.
{
js_dialog_manager->ClickDialogButtonForTesting(true, std::u16string());
ASSERT_FALSE(js_dialog_manager->IsShowingDialogForTesting());
content::NavigationHandle* handle = manager.GetNavigationHandle();
EXPECT_FALSE(handle->IsCommitDeferringConditionDeferredForTesting());
EXPECT_TRUE(handle->IsWaitingToCommit());
}
ASSERT_TRUE(manager.WaitForNavigationFinished());
}
// Test for crbug.com/297289. Ensure that modal dialogs are closed when a
// cross-process navigation is ready to commit.
IN_PROC_BROWSER_TEST_F(BrowserTest, CrossProcessNavCancelsDialogs) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("/empty.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
// Test this with multiple alert dialogs to ensure that we can navigate away
// even if the renderer tries to synchronously create more.
// See http://crbug.com/312490.
WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
auto* js_dialog_manager =
javascript_dialogs::TabModalDialogManager::FromWebContents(contents);
base::RunLoop dialog_wait;
js_dialog_manager->SetDialogShownCallbackForTesting(
dialog_wait.QuitClosure());
contents->GetPrimaryMainFrame()->ExecuteJavaScriptForTests(
u"alert('one'); alert('two');", base::NullCallback());
dialog_wait.Run();
EXPECT_TRUE(js_dialog_manager->IsShowingDialogForTesting());
// A cross-site navigation should force the dialog to close.
GURL url2("http://www.example.com/empty.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url2));
EXPECT_FALSE(js_dialog_manager->IsShowingDialogForTesting());
// Make sure input events still work in the renderer process.
EXPECT_FALSE(contents->GetPrimaryMainFrame()->GetProcess()->IsBlocked());
}
// Similar to CrossProcessNavCancelsDialogs, with a renderer-initiated main
// frame navigation with user gesture.
IN_PROC_BROWSER_TEST_F(BrowserTest, RendererCrossProcessNavCancelsDialogs) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("/empty.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
// A cross-site renderer-initiated navigation with user gesture (started
// before the dialog is shown) should force the dialog to close. (ExecJS sends
// a user gesture by default.)
GURL url2("http://www.example.com/empty.html");
content::TestNavigationManager manager(contents, url2);
EXPECT_TRUE(content::ExecJs(contents, "location = '" + url2.spec() + "';"));
EXPECT_TRUE(manager.WaitForRequestStart());
auto* js_dialog_manager =
javascript_dialogs::TabModalDialogManager::FromWebContents(contents);
base::RunLoop dialog_wait;
js_dialog_manager->SetDialogShownCallbackForTesting(
dialog_wait.QuitClosure());
content::ExecuteScriptAsync(contents, "alert('dialog')");
dialog_wait.Run();
EXPECT_TRUE(js_dialog_manager->IsShowingDialogForTesting());
// Let the navigation to url2 finish and dismiss the dialog.
ASSERT_TRUE(manager.WaitForNavigationFinished());
EXPECT_FALSE(js_dialog_manager->IsShowingDialogForTesting());
// Make sure input events still work in the renderer process.
EXPECT_FALSE(contents->GetPrimaryMainFrame()->GetProcess()->IsBlocked());
}
// Ensures that a download can complete while a dialog is showing, because it
// poses no risk of dismissing the dialog.
IN_PROC_BROWSER_TEST_F(BrowserTest, DownloadDoesntDismissDialog) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("/empty.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
// A renderer-initiated navigation without a user gesture would normally be
// deferred until the dialog is dismissed. If the navigation turns out to be
// a download at response time (e.g., because download-test3.gif has a
// Content-Disposition: attachment response header), then the download should
// not be deferred or dismiss the dialog.
std::unique_ptr<content::DownloadTestObserver> download_waiter(
new content::DownloadTestObserverTerminal(
browser()->profile()->GetDownloadManager(), 1,
content::DownloadTestObserver::ON_DANGEROUS_DOWNLOAD_FAIL));
GURL url2(embedded_test_server()->GetURL("/download-test3.gif"));
content::TestNavigationManager manager(contents, url2);
EXPECT_TRUE(content::ExecJs(contents, "location = '" + url2.spec() + "';",
content::EXECUTE_SCRIPT_NO_USER_GESTURE));
EXPECT_TRUE(manager.WaitForRequestStart());
// Show a dialog while we're waiting for the url2 response.
auto* js_dialog_manager =
javascript_dialogs::TabModalDialogManager::FromWebContents(contents);
base::RunLoop dialog_wait;
js_dialog_manager->SetDialogShownCallbackForTesting(
dialog_wait.QuitClosure());
content::ExecuteScriptAsync(contents, "alert('dialog')");
dialog_wait.Run();
EXPECT_TRUE(js_dialog_manager->IsShowingDialogForTesting());
// Let the url2 response finish and become a download, without dismissing the
// dialog.
ASSERT_TRUE(manager.WaitForNavigationFinished());
EXPECT_TRUE(js_dialog_manager->IsShowingDialogForTesting());
download_waiter->WaitForFinished();
// Close the dialog after the download finishes, to clean up.
js_dialog_manager->ClickDialogButtonForTesting(true, std::u16string());
EXPECT_FALSE(js_dialog_manager->IsShowingDialogForTesting());
// Make sure input events still work in the renderer process.
EXPECT_FALSE(contents->GetPrimaryMainFrame()->GetProcess()->IsBlocked());
}
#if BUILDFLAG(IS_MAC)
// Flaky on Mac 10.11 CI builder. See https://crbug.com/1251684.
#define MAYBE_SadTabCancelsDialogs DISABLED_SadTabCancelsDialogs
#else
#define MAYBE_SadTabCancelsDialogs SadTabCancelsDialogs
#endif
// Make sure that dialogs are closed after a renderer process dies, and that
// subsequent navigations work. See http://crbug/com/343265.
IN_PROC_BROWSER_TEST_F(BrowserTest, MAYBE_SadTabCancelsDialogs) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL beforeunload_url(embedded_test_server()->GetURL("/beforeunload.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), beforeunload_url));
WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
content::PrepContentsForBeforeUnloadTest(contents);
// Start a navigation to trigger the beforeunload dialog.
contents->GetPrimaryMainFrame()->ExecuteJavaScriptForTests(
u"window.location.href = 'about:blank'", base::NullCallback());
AppModalDialogController* alert = ui_test_utils::WaitForAppModalDialog();
EXPECT_TRUE(alert->IsValid());
AppModalDialogQueue* dialog_queue = AppModalDialogQueue::GetInstance();
EXPECT_TRUE(dialog_queue->HasActiveDialog());
// Crash the renderer process and ensure the dialog is gone.
content::RenderProcessHost* child_process =
contents->GetPrimaryMainFrame()->GetProcess();
content::RenderProcessHostWatcher crash_observer(
child_process, content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
child_process->Shutdown(0);
crash_observer.Wait();
EXPECT_FALSE(dialog_queue->HasActiveDialog());
// Make sure subsequent navigations work.
GURL url2("http://www.example.com/empty.html");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url2));
}
// Make sure that dialogs opened by subframes are closed when the process dies.
// See http://crbug.com/366510.
IN_PROC_BROWSER_TEST_F(BrowserTest, SadTabCancelsSubframeDialogs) {
WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), GURL("data:text/html, <html><body></body></html>")));
// Create an iframe that opens an alert dialog.
auto* js_dialog_manager =
javascript_dialogs::TabModalDialogManager::FromWebContents(contents);
base::RunLoop dialog_wait;
js_dialog_manager->SetDialogShownCallbackForTesting(
dialog_wait.QuitClosure());
contents->GetPrimaryMainFrame()->ExecuteJavaScriptForTests(
u"f = document.createElement('iframe');"
u"f.srcdoc = '<script>alert(1)</script>';"
u"document.body.appendChild(f);",
base::NullCallback());
dialog_wait.Run();
EXPECT_TRUE(js_dialog_manager->IsShowingDialogForTesting());
// Crash the renderer process and ensure the dialog is gone.
content::RenderProcessHost* child_process =
contents->GetPrimaryMainFrame()->GetProcess();
content::RenderProcessHostWatcher crash_observer(
child_process, content::RenderProcessHostWatcher::WATCH_FOR_PROCESS_EXIT);
child_process->Shutdown(0);
crash_observer.Wait();
EXPECT_FALSE(js_dialog_manager->IsShowingDialogForTesting());
// Make sure subsequent navigations work.
GURL url2("data:text/html,foo");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url2));
}
// Test for crbug.com/22004. Reloading a page with a before unload handler and
// then canceling the dialog should not leave the throbber spinning.
// https://crbug.com/898370: Test is flakily timing out
IN_PROC_BROWSER_TEST_F(BrowserTest, DISABLED_ReloadThenCancelBeforeUnload) {
GURL url(std::string("data:text/html,") + kBeforeUnloadHTML);
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
content::PrepContentsForBeforeUnloadTest(contents);
// Navigate to another page, but click cancel in the dialog. Make sure that
// the throbber stops spinning.
chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
AppModalDialogController* alert = ui_test_utils::WaitForAppModalDialog();
alert->CloseModalDialog();
EXPECT_FALSE(contents->IsLoading());
// Clear the beforeunload handler so the test can easily exit.
contents->GetPrimaryMainFrame()->ExecuteJavaScriptForTests(
u"onbeforeunload=null;", base::NullCallback());
}
// Test for crbug.com/11647. A page closed with window.close() should not have
// two beforeunload dialogs shown.
// http://crbug.com/410891
IN_PROC_BROWSER_TEST_F(BrowserTest,
DISABLED_SingleBeforeUnloadAfterWindowClose) {
browser()
->tab_strip_model()
->GetActiveWebContents()
->GetPrimaryMainFrame()
->ExecuteJavaScriptWithUserGestureForTests(kOpenNewBeforeUnloadPage,
base::NullCallback());
// Close the new window with JavaScript, which should show a single
// beforeunload dialog. Then show another alert, to make it easy to verify
// that a second beforeunload dialog isn't shown.
browser()
->tab_strip_model()
->GetWebContentsAt(0)
->GetPrimaryMainFrame()
->ExecuteJavaScriptWithUserGestureForTests(u"w.close(); alert('bar');",
base::NullCallback());
AppModalDialogController* alert = ui_test_utils::WaitForAppModalDialog();
alert->view()->AcceptAppModalDialog();
alert = ui_test_utils::WaitForAppModalDialog();
EXPECT_FALSE(alert->is_before_unload_dialog());
alert->view()->AcceptAppModalDialog();
}
// Test that when a page has an onbeforeunload handler, reloading a page shows a
// different dialog than navigating to a different page.
IN_PROC_BROWSER_TEST_F(BrowserTest, BeforeUnloadVsBeforeReload) {
GURL url(std::string("data:text/html,") + kBeforeUnloadHTML);
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
content::PrepContentsForBeforeUnloadTest(contents);
// Reload the page, and check that we get a "before reload" dialog.
chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
AppModalDialogController* alert = ui_test_utils::WaitForAppModalDialog();
EXPECT_TRUE(alert->is_reload());
// Proceed with the reload.
alert->view()->AcceptAppModalDialog();
EXPECT_TRUE(content::WaitForLoadStop(contents));
content::PrepContentsForBeforeUnloadTest(contents);
// Navigate to another url, and check that we get a "before unload" dialog.
GURL url2(url::kAboutBlankURL);
browser()->OpenURL(
OpenURLParams(url2, Referrer(), WindowOpenDisposition::CURRENT_TAB,
ui::PAGE_TRANSITION_TYPED, false),
/*navigation_handle_callback=*/{});
alert = ui_test_utils::WaitForAppModalDialog();
EXPECT_FALSE(alert->is_reload());
// Accept the navigation so we end up on a page without a beforeunload hook.
alert->view()->AcceptAppModalDialog();
}
// TODO(crbug.com/40641945): Test this with implicitly-created links.
IN_PROC_BROWSER_TEST_F(BrowserTest, TargetBlankLinkOpensInGroup) {
ASSERT_TRUE(browser()->tab_strip_model()->SupportsTabGroups());
ASSERT_TRUE(embedded_test_server()->Start());
// Add a grouped tab.
TabStripModel* const model = browser()->tab_strip_model();
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(
"/frame_tree/anchor_to_same_site_location.html")));
const tab_groups::TabGroupId group_id = model->AddToNewGroup({0});
// Click a target=_blank link.
WebContents* const contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(
ExecJs(contents, "simulateClick(\"test-anchor-with-blank-target\", {})"));
// The new tab should have inherited the tab group from the first tab.
EXPECT_EQ(group_id, browser()->tab_strip_model()->GetTabGroupForTab(1));
}
IN_PROC_BROWSER_TEST_F(BrowserTest, NewTabFromLinkInGroupedTabOpensInGroup) {
ASSERT_TRUE(browser()->tab_strip_model()->SupportsTabGroups());
ASSERT_TRUE(embedded_test_server()->Start());
// Add a grouped tab.
TabStripModel* const model = browser()->tab_strip_model();
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("/empty.html")));
const tab_groups::TabGroupId group_id = model->AddToNewGroup({0});
// Open a new background tab.
WebContents* const contents =
browser()->tab_strip_model()->GetActiveWebContents();
OpenURLFromTab(
contents,
OpenURLParams(embedded_test_server()->GetURL("/empty.html"), Referrer(),
WindowOpenDisposition::NEW_BACKGROUND_TAB,
ui::PAGE_TRANSITION_TYPED, false));
// It should have inherited the tab group from the first tab.
EXPECT_EQ(group_id, model->GetTabGroupForTab(1));
}
// Tests that other popup navigations that do not follow the steps at
// http://www.google.com/chrome/intl/en/webmasters-faq.html#newtab will not
// fork a new renderer process.
IN_PROC_BROWSER_TEST_F(BrowserTest, OtherRedirectsDontForkProcess) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
embedder_support::kDisablePopupBlocking);
// Create http and https servers for a cross-site transition.
ASSERT_TRUE(embedded_test_server()->Start());
net::EmbeddedTestServer https_test_server(
net::EmbeddedTestServer::TYPE_HTTPS);
https_test_server.ServeFilesFromSourceDirectory(GetChromeTestDataDir());
ASSERT_TRUE(https_test_server.Start());
GURL http_url(embedded_test_server()->GetURL("/title1.html"));
GURL https_url(https_test_server.GetURL("/title2.html"));
// Start with an http URL.
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), http_url));
WebContents* oldtab = browser()->tab_strip_model()->GetActiveWebContents();
content::RenderProcessHost* process =
oldtab->GetPrimaryMainFrame()->GetProcess();
// Now open a tab to a blank page and redirect it cross-site.
std::string dont_fork_popup = "w=window.open();";
dont_fork_popup += "w.document.location=\"";
dont_fork_popup += https_url.spec();
dont_fork_popup += "\";";
ui_test_utils::TabAddedWaiter tab_add(browser());
EXPECT_TRUE(content::ExecJs(oldtab->GetPrimaryMainFrame(), dont_fork_popup));
// The tab should be created by the time the script finished running.
ASSERT_EQ(2, browser()->tab_strip_model()->count());
WebContents* newtab = browser()->tab_strip_model()->GetActiveWebContents();
EXPECT_TRUE(newtab);
EXPECT_NE(oldtab, newtab);
// New tab should be in the middle of document.location navigation.
EXPECT_TRUE(newtab->IsLoading());
content::WaitForLoadStop(newtab);
ASSERT_TRUE(newtab->GetController().GetLastCommittedEntry());
EXPECT_EQ(https_url.spec(),
newtab->GetController().GetLastCommittedEntry()->GetURL().spec());
// Process of the (cross-site) popup window depends on whether
// site-per-process mode is enabled or not.
content::RenderProcessHost* popup_process =
newtab->GetPrimaryMainFrame()->GetProcess();
if (content::AreAllSitesIsolatedForTesting())
EXPECT_NE(process, popup_process);
else
EXPECT_EQ(process, popup_process);
// Same thing if the current tab tries to navigate itself.
std::string navigate_str = "document.location=\"";
navigate_str += https_url.spec();
navigate_str += "\";";
EXPECT_TRUE(content::ExecJs(oldtab->GetPrimaryMainFrame(), navigate_str));
// The old tab should be in the middle of document.location navigation.
EXPECT_TRUE(oldtab->IsLoading());
content::WaitForLoadStop(oldtab);
ASSERT_TRUE(oldtab->GetController().GetLastCommittedEntry());
EXPECT_EQ(https_url.spec(),
oldtab->GetController().GetLastCommittedEntry()->GetURL().spec());
// Whether original stays in the original process (when navigating to a
// cross-site url) depends on whether site-per-process mode is enabled or not.
content::RenderProcessHost* new_process =
newtab->GetPrimaryMainFrame()->GetProcess();
if (content::AreAllSitesIsolatedForTesting()) {
EXPECT_NE(process, new_process);
// site-per-process should reuse the process for the https site.
EXPECT_EQ(popup_process, new_process);
} else {
EXPECT_EQ(process, new_process);
}
}
// Test RenderView correctly send back favicon url for web page that redirects
// to an anchor in javascript body.onload handler.
IN_PROC_BROWSER_TEST_F(BrowserTest,
DISABLED_FaviconOfOnloadRedirectToAnchorPage) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("/onload_redirect_to_anchor.html"));
GURL expected_favicon_url(embedded_test_server()->GetURL("/test.png"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
NavigationEntry* entry = browser()
->tab_strip_model()
->GetActiveWebContents()
->GetController()
.GetLastCommittedEntry();
EXPECT_EQ(expected_favicon_url.spec(), entry->GetFavicon().url.spec());
}
// Makes sure TabClosing is sent when uninstalling an extension that is an app
// tab.
IN_PROC_BROWSER_TEST_F(BrowserTest, TabClosingWhenRemovingExtension) {
ASSERT_TRUE(embedded_test_server()->Start());
GURL url(embedded_test_server()->GetURL("/empty.html"));
TabStripModel* model = browser()->tab_strip_model();
ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app/")));
const Extension* extension_app = GetExtension();
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
std::unique_ptr<WebContents> app_contents =
WebContents::Create(WebContents::CreateParams(browser()->profile()));
extensions::TabHelper::CreateForWebContents(app_contents.get());
extensions::TabHelper* extensions_tab_helper =
extensions::TabHelper::FromWebContents(app_contents.get());
extensions_tab_helper->SetExtensionApp(extension_app);
model->AddWebContents(std::move(app_contents), 0,
ui::PageTransitionFromInt(0), AddTabTypes::ADD_NONE);
model->SetTabPinned(0, true);
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
TabClosingObserver observer;
model->AddObserver(&observer);
// Uninstall the extension and make sure TabClosing is sent.
extensions::ExtensionService* service =
extensions::ExtensionSystem::Get(browser()->profile())
->extension_service();
service->UninstallExtension(
GetExtension()->id(), extensions::UNINSTALL_REASON_FOR_TESTING, nullptr);
EXPECT_EQ(1, observer.closing_count());
model->RemoveObserver(&observer);
// There should only be one tab now.
ASSERT_EQ(1, browser()->tab_strip_model()->count());
}
// Open with --app-id=<id>, and see that an application window opens by default.
#if !BUILDFLAG(IS_CHROMEOS_ASH)
IN_PROC_BROWSER_TEST_F(BrowserTest, AppIdSwitch) {
base::HistogramTester tester;
ASSERT_TRUE(embedded_test_server()->Start());
// There should be one browser and one tab to start with.
EXPECT_EQ(1u, chrome::GetBrowserCount(browser()->profile()));
ASSERT_EQ(1, browser()->tab_strip_model()->count());
// Load an app.
webapps::AppId app_id = web_app::test::InstallDummyWebApp(
browser()->profile(), "testapp", GURL("https://testapp.com"));
base::CommandLine command_line(base::CommandLine::NO_PROGRAM);
command_line.AppendSwitchASCII(switches::kAppId, app_id);
ui_test_utils::BrowserChangeObserver browser_change(
nullptr, ui_test_utils::BrowserChangeObserver::ChangeType::kAdded);
base::test::TestFuture<void> launch_done;
web_app::startup::SetStartupDoneCallbackForTesting(launch_done.GetCallback());
EXPECT_TRUE(StartupBrowserCreator().ProcessCmdLineImpl(
command_line, base::FilePath(), chrome::startup::IsProcessStartup::kNo,
{browser()->profile(), StartupProfileMode::kBrowserWindow}, {}));
ASSERT_TRUE(launch_done.Wait());
Browser* app_browser = browser_change.Wait();
EXPECT_TRUE(app_browser->is_type_app());
{
// From launch_mode_recorder.cc:
constexpr char kLaunchModesHistogram[] = "Launch.Modes";
const base::HistogramBase::Sample LM_AS_WEBAPP_IN_WINDOW_BY_APP_ID = 24;
tester.ExpectUniqueSample(kLaunchModesHistogram,
LM_AS_WEBAPP_IN_WINDOW_BY_APP_ID, 1);
}
// Check that the number of browsers and tabs is correct.
EXPECT_EQ(2u, chrome::GetBrowserCount(browser()->profile()));
EXPECT_EQ(1, browser()->tab_strip_model()->count());
}
#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
// Overscroll is only enabled on Aura platforms currently, and even then only
// when a specific feature (OverscrollHistoryNavigation) is enabled.
#if defined(USE_AURA)
IN_PROC_BROWSER_TEST_F(BrowserTest, OverscrollEnabledInRegularWindows) {
ASSERT_TRUE(browser()->is_type_normal());
EXPECT_TRUE(browser()->CanOverscrollContent());
}
IN_PROC_BROWSER_TEST_F(BrowserTest, OverscrollEnabledInPopups) {
Browser* popup_browser = Browser::Create(
Browser::CreateParams(Browser::TYPE_POPUP, browser()->profile(), true));
ASSERT_TRUE(popup_browser->is_type_popup());
EXPECT_TRUE(popup_browser->CanOverscrollContent());
}
IN_PROC_BROWSER_TEST_F(BrowserTest, OverscrollDisabledInDevToolsWindows) {
DevToolsWindowTesting::OpenDevToolsWindowSync(browser(), false);
Browser* dev_tools_browser = chrome::FindLastActive();
ASSERT_EQ(dev_tools_browser->app_name(), DevToolsWindow::kDevToolsApp);
EXPECT_FALSE(dev_tools_browser->CanOverscrollContent());
}
#endif
// Open an app window and the dev tools window and ensure that the location
// bar settings are correct.
IN_PROC_BROWSER_TEST_F(BrowserTest, ShouldShowLocationBar) {
ASSERT_TRUE(embedded_test_server()->Start());
// Load an app.
ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app/")));
const Extension* extension_app = GetExtension();
// Launch it in a window, as AppLauncherHandler::HandleLaunchApp() would.
WebContents* app_window =
apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
->BrowserAppLauncher()
->LaunchAppWithParamsForTesting(apps::AppLaunchParams(
extension_app->id(),
apps::LaunchContainer::kLaunchContainerWindow,
WindowOpenDisposition::NEW_WINDOW,
apps::LaunchSource::kFromTest));
ASSERT_TRUE(app_window);
DevToolsWindow* devtools_window =
DevToolsWindowTesting::OpenDevToolsWindowSync(browser(), false);
// The launch should have created a new app browser and a dev tools browser.
ASSERT_EQ(3u, chrome::GetBrowserCount(browser()->profile()));
// Find the new browsers.
Browser* app_browser = nullptr;
Browser* dev_tools_browser = nullptr;
for (Browser* b : *BrowserList::GetInstance()) {
if (b == browser()) {
continue;
} else if (b->app_name() == DevToolsWindow::kDevToolsApp) {
dev_tools_browser = b;
} else {
app_browser = b;
}
}
ASSERT_TRUE(dev_tools_browser);
ASSERT_TRUE(app_browser);
ASSERT_TRUE(app_browser != browser());
EXPECT_FALSE(
dev_tools_browser->SupportsWindowFeature(Browser::FEATURE_LOCATIONBAR));
// App windows can show location bars, for example when they navigate away
// from their starting origin.
EXPECT_TRUE(app_browser->SupportsWindowFeature(Browser::FEATURE_LOCATIONBAR));
DevToolsWindowTesting::CloseDevToolsWindowSync(devtools_window);
}
// Regression test for crbug.com/702505.
IN_PROC_BROWSER_TEST_F(BrowserTest, ReattachDevToolsWindow) {
ASSERT_TRUE(embedded_test_server()->Start());
net::EmbeddedTestServer https_test_server(
net::EmbeddedTestServer::TYPE_HTTPS);
https_test_server.ServeFilesFromSourceDirectory(GetChromeTestDataDir());
ASSERT_TRUE(https_test_server.Start());
GURL http_url(embedded_test_server()->GetURL("/title1.html"));
WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), http_url));
// Open a devtools window.
DevToolsWindow* devtools_window =
DevToolsWindowTesting::OpenDevToolsWindowSync(browser(),
/*is_docked=*/true);
ASSERT_EQ(1u, chrome::GetBrowserCount(browser()->profile()));
// Grab its main web contents.
content::WebContents* devtools_main_web_contents =
DevToolsWindow::GetInTabWebContents(
devtools_window->GetInspectedWebContents(), nullptr);
ASSERT_NE(web_contents, devtools_main_web_contents);
// Detach the devtools window.
DevToolsUIBindings::Delegate* devtools_delegate =
static_cast<DevToolsUIBindings::Delegate*>(devtools_window);
devtools_delegate->SetIsDocked(false);
// This should have created a new dev tools browser.
ASSERT_EQ(2u, chrome::GetBrowserCount(browser()->profile()));
// Re-attach the dev tools window. This resets its Browser*.
devtools_delegate->SetIsDocked(true);
// Wait until the browser actually gets closed.
ui_test_utils::WaitForBrowserToClose();
ASSERT_EQ(1u, chrome::GetBrowserCount(browser()->profile()));
// Do something that will make SearchTabHelper access its OmniboxView. This
// should not crash, even though the Browser association and thus the
// OmniboxView* has changed, and the old OmniboxView has been deleted.
SearchTabHelper* search_tab_helper =
SearchTabHelper::FromWebContents(devtools_main_web_contents);
SearchIPCRouter::Delegate* search_ipc_router_delegate =
static_cast<SearchIPCRouter::Delegate*>(search_tab_helper);
search_ipc_router_delegate->FocusOmnibox(OMNIBOX_FOCUS_INVISIBLE);
DevToolsWindowTesting::CloseDevToolsWindowSync(devtools_window);
}
// Chromeos defaults to restoring the last session, so this test isn't
// applicable.
#if !BUILDFLAG(IS_CHROMEOS)
// Makes sure pinned tabs are restored correctly on start.
IN_PROC_BROWSER_TEST_F(BrowserTest, RestorePinnedTabs) {
ASSERT_TRUE(embedded_test_server()->Start());
// Add a pinned tab.
GURL url(embedded_test_server()->GetURL("/empty.html"));
TabStripModel* model = browser()->tab_strip_model();
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
model->SetTabPinned(0, true);
// Add a non pinned tab.
chrome::NewTab(browser());
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
// Add another pinned tab.
chrome::NewTab(browser());
ASSERT_TRUE(
ui_test_utils::NavigateToURL(browser(), GURL(url::kAboutBlankURL)));
model->SetTabPinned(2, true);
// Write out the pinned tabs.
PinnedTabCodec::WritePinnedTabs(browser()->profile());
// Set last What's New version to the current version so there is no What's
// New tab shown on launch (for the non-first-run case).
g_browser_process->local_state()->SetInteger(prefs::kLastWhatsNewVersion,
CHROME_VERSION_MAJOR);
// Close the browser window.
browser()->window()->Close();
// Launch again with the same profile.
base::CommandLine dummy(base::CommandLine::NO_PROGRAM);
chrome::startup::IsFirstRun first_run =
first_run::IsChromeFirstRun() ? chrome::startup::IsFirstRun::kYes
: chrome::startup::IsFirstRun::kNo;
StartupBrowserCreatorImpl launch(base::FilePath(), dummy, first_run);
launch.Launch(browser()->profile(), chrome::startup::IsProcessStartup::kNo,
nullptr, /*restore_tabbed_browser=*/true);
// The launch should have created a new browser.
ASSERT_EQ(2u, chrome::GetBrowserCount(browser()->profile()));
// Find the new browser.
BrowserList* browsers = BrowserList::GetInstance();
auto new_browser_iter = base::ranges::find_if_not(
*browsers, [this](Browser* b) { return b == browser(); });
ASSERT_NE(browsers->end(), new_browser_iter);
Browser* new_browser = *new_browser_iter;
// We should get back an additional tab for the app, and another for the
// default home page.
ASSERT_EQ(3, new_browser->tab_strip_model()->count());
// Make sure the state matches.
TabStripModel* new_model = new_browser->tab_strip_model();
EXPECT_TRUE(new_model->IsTabPinned(0));
EXPECT_TRUE(new_model->IsTabPinned(1));
EXPECT_FALSE(new_model->IsTabPinned(2));
}
#endif // !BUILDFLAG(IS_CHROMEOS)
// TODO(crbug.com/40148102): fix the way how exo creates accelerated widgets. At
// the moment, they are created only after the client attaches a buffer to a
// surface, which is incorrect and results in the "[destroyed object]: error 1:
// popup parent not constructed" error.
#if BUILDFLAG(IS_CHROMEOS_LACROS)
#define MAYBE_CloseWithAppMenuOpen DISABLED_CloseWithAppMenuOpen
#else
#define MAYBE_CloseWithAppMenuOpen CloseWithAppMenuOpen
#endif
// This test verifies we don't crash when closing the last window and the app
// menu is showing.
IN_PROC_BROWSER_TEST_F(BrowserTest, MAYBE_CloseWithAppMenuOpen) {
if (browser_defaults::kBrowserAliveWithNoWindows)
return;
// We need a message loop running for menus on windows.
base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(&RunCloseWithAppMenuCallback, browser()));
}
#if !BUILDFLAG(IS_MAC)
IN_PROC_BROWSER_TEST_F(BrowserTest, OpenAppWindowLikeNtp) {
ASSERT_TRUE(embedded_test_server()->Start());
// Load an app
ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app/")));
const Extension* extension_app = GetExtension();
ASSERT_TRUE(extension_app);
// Launch it in a window, as AppLauncherHandler::HandleLaunchApp() would.
WebContents* app_window =
apps::AppServiceProxyFactory::GetForProfile(browser()->profile())
->BrowserAppLauncher()
->LaunchAppWithParamsForTesting(apps::AppLaunchParams(
extension_app->id(),
apps::LaunchContainer::kLaunchContainerWindow,
WindowOpenDisposition::NEW_WINDOW,
apps::LaunchSource::kFromTest));
ASSERT_TRUE(app_window);
// Apps launched in a window from the NTP have an extensions tab helper with
// extension_app set.
ASSERT_TRUE(extensions::TabHelper::FromWebContents(app_window));
EXPECT_TRUE(extensions::TabHelper::FromWebContents(app_window)->is_app());
EXPECT_EQ(extensions::AppLaunchInfo::GetFullLaunchURL(extension_app),
app_window->GetURL());
// The launch should have created a new browser.
ASSERT_EQ(2u, chrome::GetBrowserCount(browser()->profile()));
// Find the new browser.
Browser* new_browser = nullptr;
for (Browser* b : *BrowserList::GetInstance()) {
if (b != browser())
new_browser = b;
}
ASSERT_TRUE(new_browser);
ASSERT_TRUE(new_browser != browser());
EXPECT_TRUE(new_browser->is_type_app());
// The browser's app name should include the extension's id.
std::string app_name = new_browser->app_name_;
EXPECT_NE(app_name.find(extension_app->id()), std::string::npos)
<< "Name " << app_name << " should contain id " << extension_app->id();
}
#endif // !BUILDFLAG(IS_MAC)
// Makes sure the browser doesn't crash when
// set_show_state(ui::SHOW_STATE_MAXIMIZED) has been invoked.
IN_PROC_BROWSER_TEST_F(BrowserTest, StartMaximized) {
Browser::CreateParams params[] = {
Browser::CreateParams(Browser::TYPE_NORMAL, browser()->profile(), true),
Browser::CreateParams(Browser::TYPE_POPUP, browser()->profile(), true),
Browser::CreateParams::CreateForApp("app_name", true, gfx::Rect(),
browser()->profile(), true),
Browser::CreateParams::CreateForDevTools(browser()->profile()),
Browser::CreateParams::CreateForAppPopup("app_name", true, gfx::Rect(),
browser()->profile(), true),
Browser::CreateParams(Browser::TYPE_PICTURE_IN_PICTURE,
browser()->profile(), true),
};
for (size_t i = 0; i < std::size(params); ++i) {
params[i].initial_show_state = ui::SHOW_STATE_MAXIMIZED;
AddBlankTabAndShow(Browser::Create(params[i]));
}
}
// TODO(crbug.com/40248487) This test is flaky on asan lacros and may crash ash.
#if BUILDFLAG(IS_CHROMEOS_LACROS) && defined(ADDRESS_SANITIZER)
#define MAYBE_StartMinimized DISABLED_StartMinimized
#else
#define MAYBE_StartMinimized StartMinimized
#endif
// Makes sure the browser doesn't crash when
// set_show_state(ui::SHOW_STATE_MINIMIZED) has been invoked.
IN_PROC_BROWSER_TEST_F(BrowserTest, MAYBE_StartMinimized) {
Browser::CreateParams params[] = {
Browser::CreateParams(Browser::TYPE_NORMAL, browser()->profile(), true),
Browser::CreateParams(Browser::TYPE_POPUP, browser()->profile(), true),
Browser::CreateParams::CreateForApp("app_name", true, gfx::Rect(),
browser()->profile(), true),
Browser::CreateParams::CreateForDevTools(browser()->profile()),
Browser::CreateParams::CreateForAppPopup("app_name", true, gfx::Rect(),
browser()->profile(), true),
Browser::CreateParams(Browser::TYPE_PICTURE_IN_PICTURE,
browser()->profile(), true),
};
for (size_t i = 0; i < std::size(params); ++i) {
params[i].initial_show_state = ui::SHOW_STATE_MINIMIZED;
AddBlankTabAndShow(Browser::Create(params[i]));
}
}
// Makes sure the forward button is disabled immediately when navigating
// forward to a slow-to-commit page.
IN_PROC_BROWSER_TEST_F(BrowserTest, ForwardDisabledOnForward) {
GURL blank_url(url::kAboutBlankURL);
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), blank_url));
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(kTitle1File))));
content::LoadStopObserver back_nav_load_observer(
browser()->tab_strip_model()->GetActiveWebContents());
chrome::GoBack(browser(), WindowOpenDisposition::CURRENT_TAB);
back_nav_load_observer.Wait();
CommandUpdater* command_updater = browser()->command_controller();
EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_FORWARD));
content::LoadStopObserver forward_nav_load_observer(
browser()->tab_strip_model()->GetActiveWebContents());
chrome::GoForward(browser(), WindowOpenDisposition::CURRENT_TAB);
// This check will happen before the navigation completes, since the browser
// won't process the renderer's response until the Wait() call below.
EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_FORWARD));
forward_nav_load_observer.Wait();
}
// Makes sure certain commands are disabled when Incognito mode is forced.
IN_PROC_BROWSER_TEST_F(BrowserTest, DisableMenuItemsWhenIncognitoIsForced) {
CommandUpdater* command_updater = browser()->command_controller();
// At the beginning, all commands are enabled.
EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_NEW_WINDOW));
EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_NEW_INCOGNITO_WINDOW));
EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_SHOW_BOOKMARK_MANAGER));
EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_IMPORT_SETTINGS));
EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_MANAGE_EXTENSIONS));
EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_OPTIONS));
// Set Incognito to FORCED.
IncognitoModePrefs::SetAvailability(
browser()->profile()->GetPrefs(),
policy::IncognitoModeAvailability::kForced);
// Bookmarks & Settings commands should get disabled.
EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_NEW_WINDOW));
EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_SHOW_BOOKMARK_MANAGER));
EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_IMPORT_SETTINGS));
EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_MANAGE_EXTENSIONS));
EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_OPTIONS));
// New Incognito Window command, however, should be enabled.
EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_NEW_INCOGNITO_WINDOW));
// Create a new browser.
Browser* new_browser = Browser::Create(Browser::CreateParams(
browser()->profile()->GetPrimaryOTRProfile(/*create_if_needed=*/true),
true));
CommandUpdater* new_command_updater = new_browser->command_controller();
// It should have Bookmarks & Settings commands disabled by default.
EXPECT_FALSE(new_command_updater->IsCommandEnabled(IDC_NEW_WINDOW));
EXPECT_FALSE(
new_command_updater->IsCommandEnabled(IDC_SHOW_BOOKMARK_MANAGER));
EXPECT_FALSE(new_command_updater->IsCommandEnabled(IDC_IMPORT_SETTINGS));
EXPECT_FALSE(new_command_updater->IsCommandEnabled(IDC_MANAGE_EXTENSIONS));
EXPECT_FALSE(new_command_updater->IsCommandEnabled(IDC_OPTIONS));
EXPECT_TRUE(new_command_updater->IsCommandEnabled(IDC_NEW_INCOGNITO_WINDOW));
}
#if BUILDFLAG(IS_CHROMEOS_ASH)
IN_PROC_BROWSER_TEST_F(BrowserTest, ArcBrowserWindowFeaturesSetCorrectly) {
Browser* new_browser = Browser::Create(
Browser::CreateParams(Browser::TYPE_CUSTOM_TAB, browser()->profile(),
/* user_gesture= */ true));
ASSERT_TRUE(new_browser);
EXPECT_FALSE(new_browser->SupportsWindowFeature(
Browser::WindowFeature::FEATURE_LOCATIONBAR));
EXPECT_FALSE(new_browser->SupportsWindowFeature(
Browser::WindowFeature::FEATURE_TITLEBAR));
EXPECT_FALSE(new_browser->SupportsWindowFeature(
Browser::WindowFeature::FEATURE_TABSTRIP));
EXPECT_FALSE(new_browser->SupportsWindowFeature(
Browser::WindowFeature::FEATURE_BOOKMARKBAR));
EXPECT_FALSE(
new_browser->SupportsWindowFeature(Browser::WindowFeature::FEATURE_NONE));
EXPECT_TRUE(new_browser->SupportsWindowFeature(
Browser::WindowFeature::FEATURE_TOOLBAR));
}
#endif
// Makes sure New Incognito Window command is disabled when Incognito mode is
// not available.
IN_PROC_BROWSER_TEST_F(BrowserTest,
NoNewIncognitoWindowWhenIncognitoIsDisabled) {
CommandUpdater* command_updater = browser()->command_controller();
// Set Incognito to DISABLED.
IncognitoModePrefs::SetAvailability(
browser()->profile()->GetPrefs(),
policy::IncognitoModeAvailability::kDisabled);
// Make sure New Incognito Window command is disabled. All remaining commands
// should be enabled.
EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_NEW_INCOGNITO_WINDOW));
EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_NEW_WINDOW));
EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_SHOW_BOOKMARK_MANAGER));
EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_IMPORT_SETTINGS));
EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_MANAGE_EXTENSIONS));
EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_OPTIONS));
// Create a new browser.
Browser* new_browser =
Browser::Create(Browser::CreateParams(browser()->profile(), true));
CommandUpdater* new_command_updater = new_browser->command_controller();
EXPECT_FALSE(new_command_updater->IsCommandEnabled(IDC_NEW_INCOGNITO_WINDOW));
EXPECT_TRUE(new_command_updater->IsCommandEnabled(IDC_NEW_WINDOW));
EXPECT_TRUE(new_command_updater->IsCommandEnabled(IDC_SHOW_BOOKMARK_MANAGER));
EXPECT_TRUE(new_command_updater->IsCommandEnabled(IDC_IMPORT_SETTINGS));
EXPECT_TRUE(new_command_updater->IsCommandEnabled(IDC_MANAGE_EXTENSIONS));
EXPECT_TRUE(new_command_updater->IsCommandEnabled(IDC_OPTIONS));
}
class BrowserTestWithExtensionsDisabled : public BrowserTest {
protected:
BrowserTestWithExtensionsDisabled() = default;
public:
BrowserTestWithExtensionsDisabled(const BrowserTestWithExtensionsDisabled&) =
delete;
BrowserTestWithExtensionsDisabled& operator=(
const BrowserTestWithExtensionsDisabled&) = delete;
protected:
~BrowserTestWithExtensionsDisabled() override = default;
void SetUpCommandLine(base::CommandLine* command_line) override {
BrowserTest::SetUpCommandLine(command_line);
command_line->AppendSwitch(switches::kDisableExtensions);
}
};
// Makes sure Extensions and Settings commands are disabled in certain
// circumstances even though normally they should stay enabled.
IN_PROC_BROWSER_TEST_F(BrowserTestWithExtensionsDisabled,
DisableExtensionsAndSettingsWhenIncognitoIsDisabled) {
CommandUpdater* command_updater = browser()->command_controller();
// Set Incognito to DISABLED.
IncognitoModePrefs::SetAvailability(
browser()->profile()->GetPrefs(),
policy::IncognitoModeAvailability::kDisabled);
// Make sure Manage Extensions command is disabled.
EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_MANAGE_EXTENSIONS));
EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_NEW_WINDOW));
EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_SHOW_BOOKMARK_MANAGER));
EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_IMPORT_SETTINGS));
EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_OPTIONS));
// Create a popup (non-main-UI-type) browser. Settings command as well
// as Extensions should be disabled.
Browser* popup_browser = Browser::Create(
Browser::CreateParams(Browser::TYPE_POPUP, browser()->profile(), true));
CommandUpdater* popup_command_updater = popup_browser->command_controller();
EXPECT_FALSE(popup_command_updater->IsCommandEnabled(IDC_MANAGE_EXTENSIONS));
EXPECT_FALSE(popup_command_updater->IsCommandEnabled(IDC_OPTIONS));
EXPECT_TRUE(
popup_command_updater->IsCommandEnabled(IDC_SHOW_BOOKMARK_MANAGER));
EXPECT_FALSE(popup_command_updater->IsCommandEnabled(IDC_IMPORT_SETTINGS));
}
// Makes sure Extensions and Settings commands are disabled in certain
// circumstances even though normally they should stay enabled.
IN_PROC_BROWSER_TEST_F(BrowserTest,
DisableOptionsAndImportMenuItemsConsistently) {
// Create a popup browser.
Browser* popup_browser = Browser::Create(
Browser::CreateParams(Browser::TYPE_POPUP, browser()->profile(), true));
CommandUpdater* command_updater = popup_browser->command_controller();
// OPTIONS and IMPORT_SETTINGS are disabled for a non-normal UI.
EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_OPTIONS));
EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_IMPORT_SETTINGS));
// Set Incognito to FORCED.
IncognitoModePrefs::SetAvailability(
popup_browser->profile()->GetPrefs(),
policy::IncognitoModeAvailability::kForced);
// OPTIONS and IMPORT_SETTINGS are disabled when Incognito is forced.
EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_OPTIONS));
EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_IMPORT_SETTINGS));
// Set Incognito to AVAILABLE.
IncognitoModePrefs::SetAvailability(
popup_browser->profile()->GetPrefs(),
policy::IncognitoModeAvailability::kEnabled);
// OPTIONS and IMPORT_SETTINGS are still disabled since it is a non-normal UI.
EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_OPTIONS));
EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_IMPORT_SETTINGS));
}
namespace {
void OnZoomLevelChanged(base::OnceClosure* callback,
const HostZoomMap::ZoomLevelChange& host) {
std::move(*callback).Run();
}
int GetZoomPercent(content::WebContents* contents,
bool* enable_plus,
bool* enable_minus) {
int percent =
zoom::ZoomController::FromWebContents(contents)->GetZoomPercent();
*enable_plus = percent < contents->GetMaximumZoomPercent();
*enable_minus = percent > contents->GetMinimumZoomPercent();
return percent;
}
} // namespace
IN_PROC_BROWSER_TEST_F(BrowserTest, PageZoom) {
WebContents* contents = browser()->tab_strip_model()->GetActiveWebContents();
bool enable_plus, enable_minus;
{
scoped_refptr<content::MessageLoopRunner> loop_runner(
new content::MessageLoopRunner);
base::OnceClosure quit_closure = loop_runner->QuitClosure();
content::HostZoomMap::ZoomLevelChangedCallback callback =
base::BindRepeating(&OnZoomLevelChanged, &quit_closure);
base::CallbackListSubscription subscription =
content::HostZoomMap::GetDefaultForBrowserContext(browser()->profile())
->AddZoomLevelChangedCallback(std::move(callback));
chrome::Zoom(browser(), content::PAGE_ZOOM_IN);
loop_runner->Run();
subscription = {};
EXPECT_EQ(GetZoomPercent(contents, &enable_plus, &enable_minus), 110);
EXPECT_TRUE(enable_plus);
EXPECT_TRUE(enable_minus);
}
{
scoped_refptr<content::MessageLoopRunner> loop_runner(
new content::MessageLoopRunner);
base::OnceClosure quit_closure = loop_runner->QuitClosure();
content::HostZoomMap::ZoomLevelChangedCallback callback =
base::BindRepeating(&OnZoomLevelChanged, &quit_closure);
base::CallbackListSubscription subscription =
content::HostZoomMap::GetDefaultForBrowserContext(browser()->profile())
->AddZoomLevelChangedCallback(std::move(callback));
chrome::Zoom(browser(), content::PAGE_ZOOM_RESET);
loop_runner->Run();
subscription = {};
EXPECT_EQ(GetZoomPercent(contents, &enable_plus, &enable_minus), 100);
EXPECT_TRUE(enable_plus);
EXPECT_TRUE(enable_minus);
}
{
scoped_refptr<content::MessageLoopRunner> loop_runner(
new content::MessageLoopRunner);
base::OnceClosure quit_closure = loop_runner->QuitClosure();
content::HostZoomMap::ZoomLevelChangedCallback callback =
base::BindRepeating(&OnZoomLevelChanged, &quit_closure);
base::CallbackListSubscription subscription =
content::HostZoomMap::GetDefaultForBrowserContext(browser()->profile())
->AddZoomLevelChangedCallback(std::move(callback));
chrome::Zoom(browser(), content::PAGE_ZOOM_OUT);
loop_runner->Run();
subscription = {};
EXPECT_EQ(GetZoomPercent(contents, &enable_plus, &enable_minus), 90);
EXPECT_TRUE(enable_plus);
EXPECT_TRUE(enable_minus);
}
chrome::Zoom(browser(), content::PAGE_ZOOM_RESET);
}
// TODO(ben): this test was never enabled. It has bit-rotted since being added.
// It originally lived in browser_unittest.cc, but has been moved here to make
// room for real browser unit tests.
#if 0
class BrowserTest2 : public InProcessBrowserTest {
public:
BrowserTest2() {
host_resolver_proc_ = new net::RuleBasedHostResolverProc(NULL);
// Avoid making external DNS lookups. In this test we don't need this
// to succeed.
host_resolver_proc_->AddSimulatedFailure("*.google.com");
scoped_host_resolver_proc_.Init(host_resolver_proc_.get());
}
private:
scoped_refptr<net::RuleBasedHostResolverProc> host_resolver_proc_;
net::ScopedDefaultHostResolverProc scoped_host_resolver_proc_;
};
IN_PROC_BROWSER_TEST_F(BrowserTest2, NoTabsInPopups) {
chrome::RegisterAppPrefs(L"Test");
// We start with a normal browser with one tab.
EXPECT_EQ(1, browser()->tab_strip_model()->count());
// Open a popup browser with a single blank foreground tab.
Browser* popup_browser = Browser::Create(
Browser::CreateParams(Browser::TYPE_POPUP, browser()->profile()));
chrome::AddTabAt(popup_browser, GURL(), -1, true);
EXPECT_EQ(1, popup_browser->tab_strip_model()->count());
// Now try opening another tab in the popup browser.
AddTabWithURLParams params1(url, ui::PAGE_TRANSITION_TYPED);
popup_browser->AddTabWithURL(&params1);
EXPECT_EQ(popup_browser, params1.target);
// The popup should still only have one tab.
EXPECT_EQ(1, popup_browser->tab_strip_model()->count());
// The normal browser should now have two.
EXPECT_EQ(2, browser()->tab_strip_model()->count());
// Open an app frame browser with a single blank foreground tab.
Browser* app_browser = Browser::Create(Browser::CreateParams::CreateForApp(
L"Test", browser()->profile(), false));
chrome::AddTabAt(app_browser, GURL(), -1, true);
EXPECT_EQ(1, app_browser->tab_strip_model()->count());
// Now try opening another tab in the app browser.
AddTabWithURLParams params2(GURL(url::kAboutBlankURL),
ui::PAGE_TRANSITION_TYPED);
app_browser->AddTabWithURL(&params2);
EXPECT_EQ(app_browser, params2.target);
// The popup should still only have one tab.
EXPECT_EQ(1, app_browser->tab_strip_model()->count());
// The normal browser should now have three.
EXPECT_EQ(3, browser()->tab_strip_model()->count());
// Open an app frame popup browser with a single blank foreground tab.
Browser* app_popup_browser = Browser::Create(
Browser::CreateParams::CreateForApp(
L"Test", browser()->profile(), false));
chrome::AddTabAt(app_popup_browser, GURL(), -1, true);
EXPECT_EQ(1, app_popup_browser->tab_strip_model()->count());
// Now try opening another tab in the app popup browser.
AddTabWithURLParams params3(GURL(url::kAboutBlankURL),
ui::PAGE_TRANSITION_TYPED);
app_popup_browser->AddTabWithURL(&params3);
EXPECT_EQ(app_popup_browser, params3.target);
// The popup should still only have one tab.
EXPECT_EQ(1, app_popup_browser->tab_strip_model()->count());
// The normal browser should now have four.
EXPECT_EQ(4, browser()->tab_strip_model()->count());
// Close the additional browsers.
popup_browser->tab_strip_model()->CloseAllTabs();
app_browser->tab_strip_model()->CloseAllTabs();
app_popup_browser->tab_strip_model()->CloseAllTabs();
}
#endif
IN_PROC_BROWSER_TEST_F(BrowserTest, WindowOpenClose1) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
embedder_support::kDisablePopupBlocking);
ASSERT_TRUE(embedded_test_server()->Start());
GURL url = embedded_test_server()->GetURL("/window.close.html");
GURL::Replacements add_query;
std::string query("test1");
add_query.SetQueryStr(query);
url = url.ReplaceComponents(add_query);
std::u16string title = u"Title Of Awesomeness";
content::TitleWatcher title_watcher(
browser()->tab_strip_model()->GetActiveWebContents(), title);
ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url, 2);
EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
}
IN_PROC_BROWSER_TEST_F(BrowserTest, WindowOpenClose2) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
embedder_support::kDisablePopupBlocking);
ASSERT_TRUE(embedded_test_server()->Start());
GURL url = embedded_test_server()->GetURL("/window.close.html");
GURL::Replacements add_query;
std::string query("test2");
add_query.SetQueryStr(query);
url = url.ReplaceComponents(add_query);
std::u16string title = u"Title Of Awesomeness";
content::TitleWatcher title_watcher(
browser()->tab_strip_model()->GetActiveWebContents(), title);
ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url, 2);
EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
}
// Disabled because of timeouts in several builders.
// https://crbug.com/1129313
IN_PROC_BROWSER_TEST_F(BrowserTest, DISABLED_WindowOpenClose3) {
#if BUILDFLAG(IS_MAC)
// Ensure that tests don't wait for frames that will never come.
ui::CATransactionCoordinator::Get().DisableForTesting();
#endif
base::CommandLine::ForCurrentProcess()->AppendSwitch(
embedder_support::kDisablePopupBlocking);
ASSERT_TRUE(embedded_test_server()->Start());
GURL url = embedded_test_server()->GetURL("/window.close.html");
GURL::Replacements add_query;
std::string query("test3");
add_query.SetQueryStr(query);
url = url.ReplaceComponents(add_query);
std::u16string title = u"Title Of Awesomeness";
content::TitleWatcher title_watcher(
browser()->tab_strip_model()->GetActiveWebContents(), title);
ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), url, 2);
EXPECT_EQ(title, title_watcher.WaitAndGetTitle());
}
// TODO(linux_aura) http://crbug.com/163931
// TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
// of lacros-chrome is complete.
#if !(BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS))
IN_PROC_BROWSER_TEST_F(BrowserTest, FullscreenBookmarkBar) {
#if BUILDFLAG(IS_MAC)
ui::test::ScopedFakeNSWindowFullscreen fake_fullscreen;
#endif
chrome::ToggleBookmarkBar(browser());
EXPECT_EQ(BookmarkBar::SHOW, browser()->bookmark_bar_state());
chrome::ToggleFullscreenMode(browser());
EXPECT_TRUE(browser()->window()->IsFullscreen());
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_CHROMEOS_ASH)
// Mac and Chrome OS both have an "immersive style" fullscreen where the
// bookmark bar is visible when the top views slide down.
EXPECT_EQ(BookmarkBar::SHOW, browser()->bookmark_bar_state());
#else
EXPECT_EQ(BookmarkBar::HIDDEN, browser()->bookmark_bar_state());
#endif
}
#endif
IN_PROC_BROWSER_TEST_F(BrowserTest, DisallowFileUrlUniversalAccessTest) {
GURL url = ui_test_utils::GetTestUrl(
base::FilePath(),
base::FilePath().AppendASCII("fileurl_universalaccess.html"));
std::u16string expected_title(u"Disallowed");
content::TitleWatcher title_watcher(
browser()->tab_strip_model()->GetActiveWebContents(), expected_title);
title_watcher.AlsoWaitForTitle(u"Allowed");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
ASSERT_EQ(expected_title, title_watcher.WaitAndGetTitle());
}
class KioskModeTest : public BrowserTest {
public:
KioskModeTest() = default;
void SetUpOnMainThread() override {
BrowserTest::SetUpOnMainThread();
browser()->window()->SetForceFullscreen(true);
}
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitch(switches::kKioskMode);
}
};
// TODO(crbug.com/1052397): Revisit the macro expression once build flag switch
// of lacros-chrome is complete.
#if BUILDFLAG(IS_MAC) || (BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_LACROS))
// Mac: http://crbug.com/103912
// Linux: http://crbug.com/163931
#define MAYBE_EnableKioskModeTest DISABLED_EnableKioskModeTest
#else
#define MAYBE_EnableKioskModeTest EnableKioskModeTest
#endif
IN_PROC_BROWSER_TEST_F(KioskModeTest, MAYBE_EnableKioskModeTest) {
// Check if browser is in fullscreen mode.
ASSERT_TRUE(browser()->window()->IsFullscreen());
ASSERT_FALSE(browser()->window()->IsFullscreenBubbleVisible());
}
#if BUILDFLAG(IS_CHROMEOS)
IN_PROC_BROWSER_TEST_F(KioskModeTest, DoNotExitFullscreen) {
browser()->window()->GetExclusiveAccessContext()->ExitFullscreen();
ASSERT_TRUE(browser()->window()->IsFullscreen());
}
IN_PROC_BROWSER_TEST_F(KioskModeTest, DoNotChangeBounds) {
gfx::Rect old_bounds = browser()->window()->GetBounds();
browser()->window()->SetBounds(gfx::Rect(10, 10, 10, 10));
gfx::Rect new_bounds = browser()->window()->GetBounds();
ASSERT_TRUE(browser()->window()->IsFullscreen());
ASSERT_EQ(old_bounds, new_bounds);
}
#endif // BUILDFLAG(IS_CHROMEOS)
#if BUILDFLAG(IS_WIN)
// This test verifies that Chrome can be launched with a user-data-dir path
// which contains non ASCII characters.
class LaunchBrowserWithNonAsciiUserDatadir : public BrowserTest {
public:
LaunchBrowserWithNonAsciiUserDatadir() {}
void SetUpCommandLine(base::CommandLine* command_line) override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
base::FilePath tmp_profile = temp_dir_.GetPath().AppendASCII("tmp_profile");
tmp_profile = tmp_profile.Append(L"Test Chrome G\u00E9raldine");
ASSERT_TRUE(base::CreateDirectory(tmp_profile));
command_line->AppendSwitchPath(switches::kUserDataDir, tmp_profile);
}
base::ScopedTempDir temp_dir_;
};
IN_PROC_BROWSER_TEST_F(LaunchBrowserWithNonAsciiUserDatadir,
TestNonAsciiUserDataDir) {
// Verify that the window is present.
ASSERT_TRUE(browser());
ASSERT_TRUE(browser()->profile());
// Verify that the profile has been added correctly to the
// ProfileAttributesStorage.
ASSERT_EQ(1u, g_browser_process->profile_manager()
->GetProfileAttributesStorage()
.GetNumberOfProfiles());
}
// This test verifies that Chrome can be launched with a user-data-dir path
// which contains a reparse point. This is important because sandbox
// policy validates that paths passed to policy rules do not contain
// reparse points. New code in Chrome that adjusts the sandbox can
// accidentally pass paths with reparse points to the sandbox and cause
// Chrome not to start anymore.
class LaunchBrowserWithReparsePointUserDatadir : public BrowserTest {
public:
LaunchBrowserWithReparsePointUserDatadir() {}
void SetUpCommandLine(base::CommandLine* command_line) override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
base::FilePath tmp_profile = temp_dir_.GetPath().AppendASCII("profile");
ASSERT_TRUE(base::CreateDirectory(tmp_profile));
base::FilePath reparse_profile =
temp_dir_.GetPath().AppendASCII("profile_reparse");
ASSERT_TRUE(base::CreateDirectory(reparse_profile));
auto reparse_point =
base::test::FilePathReparsePoint::Create(reparse_profile, tmp_profile);
ASSERT_TRUE(reparse_point.has_value());
reparse_point_.emplace(std::move(reparse_point.value()));
command_line->AppendSwitchPath(switches::kUserDataDir, reparse_profile);
}
base::ScopedTempDir temp_dir_;
std::optional<base::test::FilePathReparsePoint> reparse_point_;
};
IN_PROC_BROWSER_TEST_F(LaunchBrowserWithReparsePointUserDatadir,
TestReparsePointUserDataDir) {
// Verify that the window is present.
ASSERT_TRUE(browser());
ASSERT_TRUE(browser()->profile());
// Verify that the profile has been added correctly to the
// ProfileAttributesStorage.
ASSERT_EQ(1u, g_browser_process->profile_manager()
->GetProfileAttributesStorage()
.GetNumberOfProfiles());
}
#endif // BUILDFLAG(IS_WIN)
#if BUILDFLAG(IS_WIN)
// This test verifies that Chrome can be launched with a user-data-dir path
// which trailing slashes.
class LaunchBrowserWithTrailingSlashDatadir : public BrowserTest {
public:
LaunchBrowserWithTrailingSlashDatadir() {}
void SetUpCommandLine(base::CommandLine* command_line) override {
ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
base::FilePath tmp_profile = temp_dir_.GetPath().AppendASCII("tmp_profile");
tmp_profile = tmp_profile.Append(L"Test Chrome\\");
ASSERT_TRUE(base::CreateDirectory(tmp_profile));
command_line->AppendSwitchPath(switches::kUserDataDir, tmp_profile);
}
base::ScopedTempDir temp_dir_;
};
IN_PROC_BROWSER_TEST_F(LaunchBrowserWithTrailingSlashDatadir,
TestTrailingSlashUserDataDir) {
// Verify that the window is present.
ASSERT_TRUE(browser());
ASSERT_TRUE(browser()->profile());
// Verify that the profile has been added correctly to the
// ProfileAttributesStorage.
ASSERT_EQ(1u, g_browser_process->profile_manager()
->GetProfileAttributesStorage()
.GetNumberOfProfiles());
}
#endif // BUILDFLAG(IS_WIN)
#if BUILDFLAG(ENABLE_BACKGROUND_MODE)
// Tests to ensure that the browser continues running in the background after
// the last window closes.
class RunInBackgroundTest : public BrowserTest {
public:
RunInBackgroundTest() {}
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitch(switches::kKeepAliveForTest);
}
};
IN_PROC_BROWSER_TEST_F(RunInBackgroundTest, RunInBackgroundBasicTest) {
// Close the browser window, then open a new one - the browser should keep
// running.
Profile* profile = browser()->profile();
EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
CloseBrowserSynchronously(browser());
EXPECT_EQ(0u, chrome::GetTotalBrowserCount());
chrome::NewEmptyWindow(profile);
EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
}
#endif // BUILDFLAG(ENABLE_BACKGROUND_MODE)
// Tests to ensure that the browser continues running in the background after
// the last window closes.
class NoStartupWindowTest : public BrowserTest {
public:
NoStartupWindowTest() {}
void SetUpCommandLine(base::CommandLine* command_line) override {
command_line->AppendSwitch(switches::kNoStartupWindow);
command_line->AppendSwitch(switches::kKeepAliveForTest);
}
// Returns true if any commands were processed.
bool ProcessedAnyCommands(
sessions::CommandStorageManager* command_storage_manager) {
sessions::CommandStorageManagerTestHelper test_helper(
command_storage_manager);
return test_helper.ProcessedAnyCommands();
}
};
IN_PROC_BROWSER_TEST_F(NoStartupWindowTest, NoStartupWindowBasicTest) {
// No browser window should be started by default.
EXPECT_EQ(0u, chrome::GetTotalBrowserCount());
// Starting a browser window should work just fine.
CreateBrowser(ProfileManager::GetLastUsedProfile());
EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
}
// Chromeos needs to track app windows because it considers them to be part of
// session state.
#if !BUILDFLAG(IS_CHROMEOS_ASH)
IN_PROC_BROWSER_TEST_F(NoStartupWindowTest, DontInitSessionServiceForApps) {
Profile* profile = ProfileManager::GetLastUsedProfileIfLoaded();
SessionService* session_service =
SessionServiceFactory::GetForProfile(profile);
sessions::CommandStorageManager* command_storage_manager =
session_service->GetCommandStorageManagerForTest();
ASSERT_FALSE(ProcessedAnyCommands(command_storage_manager));
CreateBrowserForApp("blah", profile);
ASSERT_FALSE(ProcessedAnyCommands(command_storage_manager));
}
#endif // !BUILDFLAG(IS_CHROMEOS_ASH)
// This test needs to be placed outside the anonymous namespace because we
// need to access private type of Browser.
class AppModeTest : public BrowserTest {
public:
AppModeTest() {}
void SetUpCommandLine(base::CommandLine* command_line) override {
GURL url = ui_test_utils::GetTestUrl(
base::FilePath(), base::FilePath().AppendASCII("title1.html"));
command_line->AppendSwitchASCII(switches::kApp, url.spec());
}
};
IN_PROC_BROWSER_TEST_F(AppModeTest, EnableAppModeTest) {
// Test that an application browser window loads correctly.
// Verify the browser is in application mode.
EXPECT_TRUE(browser()->is_type_app());
}
// Confirm chrome://version contains some expected content.
IN_PROC_BROWSER_TEST_F(BrowserTest, AboutVersion) {
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(),
GURL(chrome::kChromeUIVersionURL)));
WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
ASSERT_GT(
ui_test_utils::FindInPage(tab, u"WebKit", true, true, nullptr, nullptr),
0);
ASSERT_GT(ui_test_utils::FindInPage(tab, u"OS", true, true, nullptr, nullptr),
0);
ASSERT_GT(ui_test_utils::FindInPage(tab, u"JavaScript", true, true, nullptr,
nullptr),
0);
}
static const base::FilePath::CharType* kTestDir =
FILE_PATH_LITERAL("click_modifier");
static const char16_t kFirstPageTitle[] = u"First window";
static const char16_t kSecondPageTitle[] = u"New window!";
class ClickModifierTest : public InProcessBrowserTest {
public:
ClickModifierTest() = default;
ClickModifierTest(const ClickModifierTest&) = delete;
ClickModifierTest& operator=(const ClickModifierTest&) = delete;
// Returns a url that opens a new window or tab when clicked, via javascript.
GURL GetWindowOpenURL() const {
return ui_test_utils::GetTestUrl(
base::FilePath(kTestDir),
base::FilePath(FILE_PATH_LITERAL("window_open.html")));
}
// Returns a url that follows a simple link when clicked, unless affected by
// modifiers.
GURL GetHrefURL() const {
return ui_test_utils::GetTestUrl(
base::FilePath(kTestDir),
base::FilePath(FILE_PATH_LITERAL("href.html")));
}
std::u16string GetFirstPageTitle() const { return kFirstPageTitle; }
std::u16string GetSecondPageTitle() const { return kSecondPageTitle; }
// Loads our test page and simulates a single click using the supplied button
// and modifiers. The click will cause either a navigation or the creation of
// a new window or foreground or background tab. We verify that the expected
// disposition occurs.
void RunTest(Browser* browser,
const GURL& url,
int modifiers,
blink::WebMouseEvent::Button button,
WindowOpenDisposition disposition) {
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser, url));
EXPECT_EQ(1u, chrome::GetBrowserCount(browser->profile()));
EXPECT_EQ(1, browser->tab_strip_model()->count());
content::WebContents* web_contents =
browser->tab_strip_model()->GetActiveWebContents();
EXPECT_EQ(url, web_contents->GetURL());
if (disposition == WindowOpenDisposition::CURRENT_TAB) {
content::TestNavigationObserver same_tab_observer(web_contents);
SimulateMouseClick(web_contents, modifiers, button);
same_tab_observer.Wait();
EXPECT_EQ(1u, chrome::GetBrowserCount(browser->profile()));
EXPECT_EQ(1, browser->tab_strip_model()->count());
EXPECT_EQ(GetSecondPageTitle(), web_contents->GetTitle());
return;
}
content::TestNavigationObserver new_tab_observer(nullptr);
new_tab_observer.StartWatchingNewWebContents();
SimulateMouseClick(web_contents, modifiers, button);
new_tab_observer.Wait();
if (disposition == WindowOpenDisposition::NEW_WINDOW) {
EXPECT_EQ(2u, chrome::GetBrowserCount(browser->profile()));
return;
}
EXPECT_EQ(1u, chrome::GetBrowserCount(browser->profile()));
EXPECT_EQ(2, browser->tab_strip_model()->count());
web_contents = browser->tab_strip_model()->GetActiveWebContents();
if (disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB) {
EXPECT_EQ(GetSecondPageTitle(), web_contents->GetTitle());
} else {
ASSERT_EQ(WindowOpenDisposition::NEW_BACKGROUND_TAB, disposition);
EXPECT_EQ(GetFirstPageTitle(), web_contents->GetTitle());
}
}
};
// Tests for clicking on elements with handlers that run window.open.
IN_PROC_BROWSER_TEST_F(ClickModifierTest, WindowOpenBasicClickTest) {
int modifiers = 0;
blink::WebMouseEvent::Button button = blink::WebMouseEvent::Button::kLeft;
WindowOpenDisposition disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
RunTest(browser(), GetWindowOpenURL(), modifiers, button, disposition);
}
// TODO(ericu): Alt-click behavior on window.open is platform-dependent and not
// well defined. Should we add tests so we know if it changes?
// Shift-clicks open in a new window.
IN_PROC_BROWSER_TEST_F(ClickModifierTest, WindowOpenShiftClickTest) {
int modifiers = blink::WebInputEvent::kShiftKey;
blink::WebMouseEvent::Button button = blink::WebMouseEvent::Button::kLeft;
WindowOpenDisposition disposition = WindowOpenDisposition::NEW_WINDOW;
RunTest(browser(), GetWindowOpenURL(), modifiers, button, disposition);
}
// Control-clicks open in a background tab.
// On OSX meta [the command key] takes the place of control.
IN_PROC_BROWSER_TEST_F(ClickModifierTest, WindowOpenControlClickTest) {
#if BUILDFLAG(IS_MAC)
int modifiers = blink::WebInputEvent::kMetaKey;
#else
int modifiers = blink::WebInputEvent::kControlKey;
#endif
blink::WebMouseEvent::Button button = blink::WebMouseEvent::Button::kLeft;
WindowOpenDisposition disposition = WindowOpenDisposition::NEW_BACKGROUND_TAB;
RunTest(browser(), GetWindowOpenURL(), modifiers, button, disposition);
}
// Control-shift-clicks open in a foreground tab.
// On OSX meta [the command key] takes the place of control.
IN_PROC_BROWSER_TEST_F(ClickModifierTest, WindowOpenControlShiftClickTest) {
#if BUILDFLAG(IS_MAC)
int modifiers = blink::WebInputEvent::kMetaKey;
#else
int modifiers = blink::WebInputEvent::kControlKey;
#endif
modifiers |= blink::WebInputEvent::kShiftKey;
blink::WebMouseEvent::Button button = blink::WebMouseEvent::Button::kLeft;
WindowOpenDisposition disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
RunTest(browser(), GetWindowOpenURL(), modifiers, button, disposition);
}
// Tests for clicking on normal links.
IN_PROC_BROWSER_TEST_F(ClickModifierTest, HrefBasicClickTest) {
int modifiers = 0;
blink::WebMouseEvent::Button button = blink::WebMouseEvent::Button::kLeft;
WindowOpenDisposition disposition = WindowOpenDisposition::CURRENT_TAB;
RunTest(browser(), GetHrefURL(), modifiers, button, disposition);
}
// TODO(ericu): Alt-click behavior on links is platform-dependent and not well
// defined. Should we add tests so we know if it changes?
// Shift-clicks open in a new window.
IN_PROC_BROWSER_TEST_F(ClickModifierTest, HrefShiftClickTest) {
int modifiers = blink::WebInputEvent::kShiftKey;
blink::WebMouseEvent::Button button = blink::WebMouseEvent::Button::kLeft;
WindowOpenDisposition disposition = WindowOpenDisposition::NEW_WINDOW;
RunTest(browser(), GetHrefURL(), modifiers, button, disposition);
}
// Control-clicks open in a background tab.
// On OSX meta [the command key] takes the place of control.
IN_PROC_BROWSER_TEST_F(ClickModifierTest, HrefControlClickTest) {
#if BUILDFLAG(IS_MAC)
int modifiers = blink::WebInputEvent::kMetaKey;
#else
int modifiers = blink::WebInputEvent::kControlKey;
#endif
blink::WebMouseEvent::Button button = blink::WebMouseEvent::Button::kLeft;
WindowOpenDisposition disposition = WindowOpenDisposition::NEW_BACKGROUND_TAB;
RunTest(browser(), GetHrefURL(), modifiers, button, disposition);
}
// Control-shift-clicks open in a foreground tab.
// On OSX meta [the command key] takes the place of control.
IN_PROC_BROWSER_TEST_F(ClickModifierTest, HrefControlShiftClickTest) {
#if BUILDFLAG(IS_MAC)
int modifiers = blink::WebInputEvent::kMetaKey;
#else
int modifiers = blink::WebInputEvent::kControlKey;
#endif
modifiers |= blink::WebInputEvent::kShiftKey;
blink::WebMouseEvent::Button button = blink::WebMouseEvent::Button::kLeft;
WindowOpenDisposition disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
RunTest(browser(), GetHrefURL(), modifiers, button, disposition);
}
// Middle-clicks open in a background tab.
IN_PROC_BROWSER_TEST_F(ClickModifierTest, HrefMiddleClickTest) {
int modifiers = 0;
blink::WebMouseEvent::Button button = blink::WebMouseEvent::Button::kMiddle;
WindowOpenDisposition disposition = WindowOpenDisposition::NEW_BACKGROUND_TAB;
RunTest(browser(), GetHrefURL(), modifiers, button, disposition);
}
// Shift-middle-clicks open in a foreground tab.
IN_PROC_BROWSER_TEST_F(ClickModifierTest, HrefShiftMiddleClickTest) {
int modifiers = blink::WebInputEvent::kShiftKey;
blink::WebMouseEvent::Button button = blink::WebMouseEvent::Button::kMiddle;
WindowOpenDisposition disposition = WindowOpenDisposition::NEW_FOREGROUND_TAB;
RunTest(browser(), GetHrefURL(), modifiers, button, disposition);
}
IN_PROC_BROWSER_TEST_F(BrowserTest, GetSizeForNewRenderView) {
// Force an initial resize. This works around a test-only problem on Chrome OS
// where the shelf may not be created before the initial test browser window
// opens, which leads to sizing issues in WebContents resize.
browser()->window()->SetBounds(gfx::Rect(10, 20, 600, 400));
// Let the message loop run so that resize actually takes effect.
content::RunAllPendingInMessageLoop();
// The instant extended NTP has javascript that does not work with
// ui_test_utils::NavigateToURL. The NTP rvh reloads when the browser tries
// to navigate away from the page, which causes the WebContents to end up in
// an inconsistent state. (is_loaded = true, last_commited_url=ntp,
// visible_url=title1.html)
browser()->profile()->GetPrefs()->SetBoolean(prefs::kWebKitJavascriptEnabled,
false);
ASSERT_TRUE(embedded_test_server()->Start());
// Create an HTTPS server for cross-site transition.
net::EmbeddedTestServer https_test_server(
net::EmbeddedTestServer::TYPE_HTTPS);
https_test_server.ServeFilesFromSourceDirectory(GetChromeTestDataDir());
ASSERT_TRUE(https_test_server.Start());
// Start with NTP.
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("chrome://newtab")));
ASSERT_EQ(BookmarkBar::HIDDEN, browser()->bookmark_bar_state());
WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
content::RenderViewHost* prev_rvh =
web_contents->GetPrimaryMainFrame()->GetRenderViewHost();
const gfx::Size initial_wcv_size = web_contents->GetContainerBounds().size();
RenderViewSizeObserver observer(web_contents, browser()->window());
// Navigate to a non-NTP page, without resizing WebContentsView.
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("/title1.html")));
ASSERT_EQ(BookmarkBar::HIDDEN, browser()->bookmark_bar_state());
// A new RenderViewHost should be created.
EXPECT_NE(prev_rvh, web_contents->GetPrimaryMainFrame()->GetRenderViewHost());
prev_rvh = web_contents->GetPrimaryMainFrame()->GetRenderViewHost();
gfx::Size rwhv_create_size0, rwhv_commit_size0, wcv_commit_size0;
observer.GetSizeForRenderViewHost(
web_contents->GetPrimaryMainFrame()->GetRenderViewHost(),
&rwhv_create_size0, &rwhv_commit_size0, &wcv_commit_size0);
EXPECT_EQ(gfx::Size(initial_wcv_size.width(), initial_wcv_size.height()),
rwhv_create_size0);
// When a navigation entry is committed, the size of RenderWidgetHostView
// should be the same as when it was first created.
EXPECT_EQ(rwhv_create_size0, rwhv_commit_size0);
// Sizes of the current RenderWidgetHostView and WebContentsView should not
// change before and after WebContentsObserver::DidFinishNavigation
// (implemented by Browser); we obtain the sizes before PostCommit via
// WebContentsObserver::NavigationEntryCommitted (implemented by
// RenderViewSizeObserver).
EXPECT_EQ(rwhv_commit_size0,
web_contents->GetRenderWidgetHostView()->GetViewBounds().size());
// The behavior differs between OSX and views.
// In OSX, the wcv does not change size until after the commit, when the
// bookmark bar disappears (correct).
// In views, the wcv changes size at commit time.
#if BUILDFLAG(IS_MAC)
EXPECT_EQ(gfx::Size(wcv_commit_size0.width(), wcv_commit_size0.height()),
web_contents->GetContainerBounds().size());
#else
EXPECT_EQ(wcv_commit_size0, web_contents->GetContainerBounds().size());
#endif
// Navigate to another non-NTP page, without resizing WebContentsView.
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), https_test_server.GetURL("/title2.html")));
ASSERT_EQ(BookmarkBar::HIDDEN, browser()->bookmark_bar_state());
// A new RenderVieHost should be created.
EXPECT_NE(prev_rvh, web_contents->GetPrimaryMainFrame()->GetRenderViewHost());
gfx::Size rwhv_create_size1, rwhv_commit_size1, wcv_commit_size1;
observer.GetSizeForRenderViewHost(
web_contents->GetPrimaryMainFrame()->GetRenderViewHost(),
&rwhv_create_size1, &rwhv_commit_size1, &wcv_commit_size1);
EXPECT_EQ(rwhv_create_size1, rwhv_commit_size1);
EXPECT_EQ(rwhv_commit_size1,
web_contents->GetRenderWidgetHostView()->GetViewBounds().size());
EXPECT_EQ(wcv_commit_size1, web_contents->GetContainerBounds().size());
// Navigate from NTP to a non-NTP page, resizing WebContentsView while
// navigation entry is pending.
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("chrome://newtab")));
gfx::Size wcv_resize_insets(1, 1);
observer.set_wcv_resize_insets(wcv_resize_insets);
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("/title2.html")));
ASSERT_EQ(BookmarkBar::HIDDEN, browser()->bookmark_bar_state());
gfx::Size rwhv_create_size2, rwhv_commit_size2, wcv_commit_size2;
observer.GetSizeForRenderViewHost(
web_contents->GetPrimaryMainFrame()->GetRenderViewHost(),
&rwhv_create_size2, &rwhv_commit_size2, &wcv_commit_size2);
// The behavior on OSX and Views is incorrect in this edge case, but they are
// differently incorrect.
// The behavior should be:
// initial wcv size: (100,100) (to choose random numbers)
// initial rwhv size: (100,140)
// commit wcv size: (101, 101)
// commit rwhv size: (101, 141)
// final wcv size: (101, 141)
// final rwhv size: (101, 141)
//
// On OSX, the commit rwhv size is (101, 101)
// On views, the commit wcv size is (101, 141)
// All other sizes are correct.
EXPECT_EQ(gfx::Size(initial_wcv_size.width(), initial_wcv_size.height()),
rwhv_create_size2);
gfx::Size exp_commit_size(initial_wcv_size);
#if BUILDFLAG(IS_MAC)
exp_commit_size.Enlarge(wcv_resize_insets.width(),
wcv_resize_insets.height());
#else
exp_commit_size.Enlarge(wcv_resize_insets.width(),
wcv_resize_insets.height());
#endif
EXPECT_EQ(exp_commit_size, rwhv_commit_size2);
EXPECT_EQ(exp_commit_size, wcv_commit_size2);
gfx::Size exp_final_size(initial_wcv_size);
exp_final_size.Enlarge(wcv_resize_insets.width(), wcv_resize_insets.height());
EXPECT_EQ(exp_final_size,
web_contents->GetRenderWidgetHostView()->GetViewBounds().size());
EXPECT_EQ(exp_final_size, web_contents->GetContainerBounds().size());
}
IN_PROC_BROWSER_TEST_F(BrowserTest, CanDuplicateTab) {
GURL url(ui_test_utils::GetTestUrl(
base::FilePath(base::FilePath::kCurrentDirectory),
base::FilePath(kTitle1File)));
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
ASSERT_TRUE(AddTabAtIndex(0, url, ui::PAGE_TRANSITION_TYPED));
int active_index = browser()->tab_strip_model()->active_index();
EXPECT_EQ(0, active_index);
EXPECT_TRUE(chrome::CanDuplicateTab(browser()));
EXPECT_TRUE(chrome::CanDuplicateTabAt(browser(), 0));
EXPECT_TRUE(chrome::CanDuplicateTabAt(browser(), 1));
}
namespace {
void CheckDisplayModeMQ(const std::u16string& display_mode,
content::WebContents* web_contents) {
std::u16string function =
u"(function() {return window.matchMedia('(display-mode: " + display_mode +
u")').matches;})();";
bool js_result = false;
base::RunLoop run_loop;
web_contents->GetPrimaryMainFrame()->ExecuteJavaScriptForTests(
function, base::BindLambdaForTesting([&](base::Value value) {
DCHECK(value.is_bool());
js_result = value.GetBool();
run_loop.Quit();
}));
run_loop.Run();
EXPECT_TRUE(js_result);
}
} // namespace
// flaky new test: http://crbug.com/471703
IN_PROC_BROWSER_TEST_F(BrowserTest, DISABLED_ChangeDisplayMode) {
CheckDisplayModeMQ(u"browser",
browser()->tab_strip_model()->GetActiveWebContents());
Profile* profile = browser()->profile();
Browser* app_browser = CreateBrowserForApp("blah", profile);
auto* app_contents = app_browser->tab_strip_model()->GetActiveWebContents();
CheckDisplayModeMQ(u"standalone", app_contents);
app_browser->exclusive_access_manager()->context()->EnterFullscreen(
GURL(), EXCLUSIVE_ACCESS_BUBBLE_TYPE_BROWSER_FULLSCREEN_EXIT_INSTRUCTION,
display::kInvalidDisplayId);
// Sync navigation just to make sure IPC has passed (updated
// display mode is delivered to RP).
content::TestNavigationObserver observer(app_contents, 1);
ASSERT_TRUE(
ui_test_utils::NavigateToURL(app_browser, GURL(url::kAboutBlankURL)));
observer.Wait();
CheckDisplayModeMQ(u"fullscreen", app_contents);
}
// Test to ensure the bounds of popup, devtool, and app windows are properly
// restored.
IN_PROC_BROWSER_TEST_F(BrowserTest, TestPopupBounds) {
// TODO(tdanderson|pkasting): Change this to verify that the contents bounds
// set by params.initial_bounds are the same as the contents bounds in the
// initialized window. See crbug.com/585856.
{
// Minimum height a popup window should have added to the supplied content
// bounds when drawn. This accommodates the browser toolbar.
const int minimum_popup_padding = 26;
// Creates an untrusted popup window and asserts that the eventual height is
// padded with the toolbar and title bar height (initial height is content
// height).
Browser::CreateParams params(Browser::TYPE_POPUP, browser()->profile(),
true);
params.initial_bounds = gfx::Rect(0, 0, 100, 122);
Browser* browser = Browser::Create(params);
gfx::Rect bounds = browser->window()->GetBounds();
// Should be EXPECT_EQ, but this width is inconsistent across platforms.
// See https://crbug.com/567925.
EXPECT_GE(bounds.width(), 100);
// EXPECT_GE as Mac will have a larger height with the additional title bar.
EXPECT_GE(bounds.height(), 122 + minimum_popup_padding);
browser->window()->Close();
}
{
// Creates a trusted popup window and asserts that the eventual height
// doesn't change (initial height is window height).
Browser::CreateParams params(Browser::TYPE_POPUP, browser()->profile(),
true);
params.initial_bounds = gfx::Rect(0, 0, 100, 122);
params.trusted_source = true;
Browser* browser = Browser::Create(params);
gfx::Rect bounds = browser->window()->GetBounds();
// Should be EXPECT_EQ, but this width is inconsistent across platforms.
// See https://crbug.com/567925.
EXPECT_GE(bounds.width(), 100);
EXPECT_EQ(122, bounds.height());
browser->window()->Close();
}
{
// Creates an untrusted app window and asserts that the eventual height
// doesn't change.
Browser::CreateParams params = Browser::CreateParams::CreateForApp(
"app-name", false, gfx::Rect(0, 0, 100, 122), browser()->profile(),
true);
Browser* browser = Browser::Create(params);
gfx::Rect bounds = browser->window()->GetBounds();
// Should be EXPECT_EQ, but this width is inconsistent across platforms.
// See https://crbug.com/567925.
EXPECT_GE(bounds.width(), 100);
EXPECT_EQ(122, bounds.height());
browser->window()->Close();
}
{
// Creates a trusted app window and asserts that the eventual height
// doesn't change.
Browser::CreateParams params = Browser::CreateParams::CreateForApp(
"app-name", true, gfx::Rect(0, 0, 100, 122), browser()->profile(),
true);
Browser* browser = Browser::Create(params);
gfx::Rect bounds = browser->window()->GetBounds();
// Should be EXPECT_EQ, but this width is inconsistent across platforms.
// See https://crbug.com/567925.
EXPECT_GE(bounds.width(), 100);
EXPECT_EQ(122, bounds.height());
browser->window()->Close();
}
{
// Creates a devtools window and asserts that the eventual height
// doesn't change.
Browser::CreateParams params =
Browser::CreateParams::CreateForDevTools(browser()->profile());
params.initial_bounds = gfx::Rect(0, 0, 100, 122);
Browser* browser = Browser::Create(params);
gfx::Rect bounds = browser->window()->GetBounds();
// Should be EXPECT_EQ, but this width is inconsistent across platforms.
// See https://crbug.com/567925.
EXPECT_GE(bounds.width(), 100);
EXPECT_EQ(122, bounds.height());
browser->window()->Close();
}
}
IN_PROC_BROWSER_TEST_F(BrowserTest, IsOffTheRecordBrowserInUse) {
EXPECT_FALSE(BrowserList::IsOffTheRecordBrowserInUse(browser()->profile()));
Browser* incognito_browser = CreateIncognitoBrowser(browser()->profile());
EXPECT_TRUE(BrowserList::IsOffTheRecordBrowserInUse(browser()->profile()));
CloseBrowserSynchronously(incognito_browser);
EXPECT_FALSE(BrowserList::IsOffTheRecordBrowserInUse(browser()->profile()));
}
IN_PROC_BROWSER_TEST_F(BrowserTest, TestActiveTabChangedUserAction) {
base::UserActionTester user_action_tester;
chrome::NewTab(browser());
EXPECT_EQ(user_action_tester.GetActionCount("ActiveTabChanged"), 1);
}
IN_PROC_BROWSER_TEST_F(BrowserTest, TestNavEntryCommittedUserAction) {
base::UserActionTester user_action_tester;
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), GURL("chrome://newtab")));
EXPECT_EQ(user_action_tester.GetActionCount("NavEntryCommitted"), 1);
EXPECT_EQ(user_action_tester.GetActionCount("NavEntryCommitted.SRP"), 0);
}
namespace {
void SetTestDefaultSearchProvider(TemplateURLService* service,
const GURL& search_template) {
ASSERT_TRUE(service);
search_test_utils::WaitForTemplateURLServiceToLoad(service);
ASSERT_TRUE(service->loaded());
TemplateURLData data;
data.SetShortName(u"test");
data.SetKeyword(data.short_name());
data.SetURL(search_template.spec());
TemplateURL* template_url = service->Add(std::make_unique<TemplateURL>(data));
ASSERT_TRUE(template_url);
service->SetUserSelectedDefaultSearchProvider(template_url);
}
} // namespace
IN_PROC_BROWSER_TEST_F(BrowserTest, TestNavEntryCommittedSRPUserAction) {
ASSERT_TRUE(embedded_test_server()->Start());
TemplateURLService* service =
TemplateURLServiceFactory::GetForProfile(browser()->profile());
SetTestDefaultSearchProvider(
service,
embedded_test_server()->GetURL("a.test", "/title1.html?q={searchTerms}"));
const GURL srp_url =
service->GenerateSearchURLForDefaultSearchProvider(u"testing");
base::UserActionTester user_action_tester;
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), srp_url));
EXPECT_EQ(user_action_tester.GetActionCount("NavEntryCommitted"), 1);
EXPECT_EQ(user_action_tester.GetActionCount("NavEntryCommitted.SRP"), 1);
}
IN_PROC_BROWSER_TEST_F(BrowserTest,
TestNavEntryCommittedUserActionOnlyRecordedForTabs) {
ASSERT_TRUE(embedded_test_server()->Start());
TemplateURLService* service =
TemplateURLServiceFactory::GetForProfile(browser()->profile());
SetTestDefaultSearchProvider(
service,
embedded_test_server()->GetURL("a.test", "/title1.html?q={searchTerms}"));
const GURL srp_url =
service->GenerateSearchURLForDefaultSearchProvider(u"testing");
base::UserActionTester user_action_tester;
// Create and navigate a WebContents that is not a tab. The NavEntryCommitted
// actions should not be recorded for this WebContents.
std::unique_ptr<content::WebContents> non_tab_web_contents =
content::WebContents::Create(
content::WebContents::CreateParams(browser()->profile()));
content::TestNavigationObserver observer(non_tab_web_contents.get());
non_tab_web_contents->GetController().LoadURL(
srp_url, content::Referrer(), ::ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
std::string());
observer.Wait();
EXPECT_EQ(user_action_tester.GetActionCount("NavEntryCommitted"), 0);
EXPECT_EQ(user_action_tester.GetActionCount("NavEntryCommitted.SRP"), 0);
}
IN_PROC_BROWSER_TEST_F(BrowserTest, TestTabCountMetrics) {
ASSERT_TRUE(embedded_test_server()->Start());
// This test assumes there's only one browser with one tab at the start of the
// test.
EXPECT_EQ(1u, chrome::GetTotalBrowserCount());
ASSERT_EQ(1, browser()->tab_strip_model()->count());
// Create an additional browser with two tabs.
Browser* browser2 = CreateBrowser(browser()->profile());
chrome::NewTab(browser2);
ASSERT_TRUE(content::WaitForLoadStop(
browser2->tab_strip_model()->GetActiveWebContents()));
EXPECT_EQ(2u, chrome::GetTotalBrowserCount());
ASSERT_EQ(1, browser()->tab_strip_model()->count());
ASSERT_EQ(2, browser2->tab_strip_model()->count());
base::HistogramTester histogram_tester;
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser2, embedded_test_server()->GetURL("/title1.html")));
histogram_tester.ExpectBucketCount("Tabs.TabCountPerWindow", 1, 1);
histogram_tester.ExpectBucketCount("Tabs.TabCountPerWindow", 2, 1);
histogram_tester.ExpectUniqueSample("Tabs.TabCountPerLoad", 3, 1);
// Skipping "Tabs.TabCountActiveWindow" due to flakiness risk w.r.t. focus.
// Also check that tab group count metrics are recorded. In this case, there
// aren't any groups.
histogram_tester.ExpectUniqueSample("TabGroups.UserGroupCountPerLoad", 0, 1);
histogram_tester.ExpectUniqueSample("TabGroups.UserPinnedTabCountPerLoad", 0,
1);
histogram_tester.ExpectUniqueSample("Tabs.TabCountInGroupPerLoad", 0, 1);
histogram_tester.ExpectUniqueSample(
"TabGroups.UserCustomizedGroupCountPerLoad", 0, 1);
histogram_tester.ExpectUniqueSample("TabGroups.CollapsedGroupCountPerLoad", 0,
1);
}
IN_PROC_BROWSER_TEST_F(BrowserTest,
TestTabCountMetricsOnlyRecordedWhenTabLoads) {
ASSERT_TRUE(embedded_test_server()->Start());
base::HistogramTester histogram_tester;
// Create and navigate a WebContents that is not a tab. Tab count metrics
// should not be recorded when this WebContents loads.
std::unique_ptr<content::WebContents> non_tab_web_contents =
content::WebContents::Create(
content::WebContents::CreateParams(browser()->profile()));
content::TestNavigationObserver observer(non_tab_web_contents.get());
non_tab_web_contents->GetController().LoadURL(
embedded_test_server()->GetURL("/title1.html"), content::Referrer(),
::ui::PAGE_TRANSITION_AUTO_TOPLEVEL, std::string());
observer.Wait();
histogram_tester.ExpectTotalCount("Tabs.TabCountPerLoad", 0);
}
IN_PROC_BROWSER_TEST_F(BrowserTest,
TestTabCountMetricsNotRecordedWhenIframeLoads) {
ASSERT_TRUE(embedded_test_server()->Start());
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("/iframe.html")));
base::HistogramTester histogram_tester;
// Tab count metrics should not be recorded when the iframe navigates.
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
const GURL iframe_url = embedded_test_server()->GetURL("/title2.html");
ASSERT_TRUE(content::NavigateIframeToURL(web_contents, "test", iframe_url));
histogram_tester.ExpectTotalCount("Tabs.TabCountPerLoad", 0);
}
IN_PROC_BROWSER_TEST_F(
BrowserTest,
TestTabCountMetricsNotRecordedForSameDocumentNavigation) {
ASSERT_TRUE(embedded_test_server()->Start());
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("/title1.html")));
base::HistogramTester histogram_tester;
// Tab count metrics should not be recorded for same document navigations.
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
content::TestNavigationObserver nav_observer(web_contents);
ASSERT_TRUE(content::ExecJs(web_contents, "location.href = '#test'"));
nav_observer.Wait();
histogram_tester.ExpectTotalCount("Tabs.TabCountPerLoad", 0);
}
IN_PROC_BROWSER_TEST_F(BrowserTest, TestTabCountMetricsRecordedOnReload) {
ASSERT_TRUE(embedded_test_server()->Start());
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("/title1.html")));
base::HistogramTester histogram_tester;
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
content::TestNavigationObserver nav_observer(web_contents);
chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
nav_observer.Wait();
histogram_tester.ExpectTotalCount("Tabs.TabCountPerLoad", 1);
}
IN_PROC_BROWSER_TEST_F(BrowserTest,
TestTabCountMetricsRecordedOnBackNavigation) {
ASSERT_TRUE(embedded_test_server()->Start());
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("/title1.html")));
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL("/title2.html")));
base::HistogramTester histogram_tester;
content::WebContents* web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
content::TestNavigationObserver nav_observer(web_contents);
chrome::GoBack(browser(), WindowOpenDisposition::CURRENT_TAB);
nav_observer.Wait();
histogram_tester.ExpectTotalCount("Tabs.TabCountPerLoad", 1);
}
IN_PROC_BROWSER_TEST_F(BrowserTest, TestActiveBrowserChangedUserAction) {
base::UserActionTester user_action_tester;
BrowserList::SetLastActive(browser());
EXPECT_EQ(user_action_tester.GetActionCount("ActiveBrowserChanged"), 1);
}
// DISABLED for flakiness. See https://crbug.com/1184168
IN_PROC_BROWSER_TEST_F(
BrowserTest,
DISABLED_SameDocumentNavigationWithNothingCommittedAfterCrash) {
// The test sets this closure before each navigation to /sometimes-slow in
// order to control the response for that navigation.
content::SlowHttpResponse::GotRequestCallback got_slow_request;
embedded_test_server()->RegisterRequestHandler(base::BindLambdaForTesting(
[&](const net::test_server::HttpRequest& request)
-> std::unique_ptr<net::test_server::HttpResponse> {
if (request.relative_url != "/sometimes-slow")
return nullptr;
DCHECK(got_slow_request)
<< "Set `got_slow_request` before each navigation request.";
return std::make_unique<content::SlowHttpResponse>(
std::move(got_slow_request));
}));
ASSERT_TRUE(embedded_test_server()->Start());
GURL url1 = embedded_test_server()->GetURL("/sometimes-slow");
GURL url2 = embedded_test_server()->GetURL("/sometimes-slow#foo");
WebContents* wc = browser()->tab_strip_model()->GetActiveWebContents();
// Successfully navigate to `url1`.
got_slow_request = content::SlowHttpResponse::FinishResponseImmediately();
EXPECT_TRUE(NavigateToURL(wc, url1));
// Kill the renderer for the tab.
{
content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
content::RenderFrameDeletedObserver crash_observer(
wc->GetPrimaryMainFrame());
wc->GetPrimaryMainFrame()->GetProcess()->Shutdown(1);
crash_observer.WaitUntilDeleted();
}
// Bring the process back to life for the current RenderFrameHost, though with
// a speculative RenderFrameHost navigating back to `url1`.
{
content::NavigationController::LoadURLParams params(url1);
params.transition_type = ui::PageTransitionFromInt(
ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);
base::RunLoop loop;
got_slow_request =
base::BindLambdaForTesting([&](base::OnceClosure start_response,
base::OnceClosure finish_response) {
// Never starts the response, but informs the test the request has
// been received.
loop.Quit();
});
wc->GetController().LoadURLWithParams(params);
loop.Run();
}
// The navigation has not completed, but the renderer has come alive.
EXPECT_TRUE(wc->GetPrimaryMainFrame()->IsRenderFrameLive());
EXPECT_EQ(wc->GetPrimaryMainFrame()->GetLastCommittedURL().spec(), "");
// Now try to navigate to `url2`. We're currently trying to load `url1` since
// the above navigation will be delayed. Going to `url2` should be a
// same-document navigation according to the urls alone. But it can't be since
// the current frame host does not actually have a document loaded.
content::NavigationHandleCommitObserver nav_observer(wc, url2);
{
content::NavigationController::LoadURLParams params(url2);
params.transition_type = ui::PageTransitionFromInt(
ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);
got_slow_request = content::SlowHttpResponse::FinishResponseImmediately();
wc->GetController().LoadURLWithParams(params);
}
EXPECT_TRUE(WaitForLoadStop(wc));
EXPECT_TRUE(nav_observer.has_committed());
EXPECT_FALSE(nav_observer.was_same_document());
}
// DISABLED for flakiness. See https://crbug.com/1184168
IN_PROC_BROWSER_TEST_F(
BrowserTest,
DISABLED_SameDocumentHistoryNavigationWithNothingCommittedAfterCrash) {
content::SlowHttpResponse::GotRequestCallback got_slow_request;
embedded_test_server()->RegisterRequestHandler(base::BindLambdaForTesting(
[&](const net::test_server::HttpRequest& request)
-> std::unique_ptr<net::test_server::HttpResponse> {
if (request.relative_url != "/sometimes-slow")
return nullptr;
DCHECK(got_slow_request)
<< "Set `got_slow_request` before each navigation request.";
return std::make_unique<content::SlowHttpResponse>(
std::move(got_slow_request));
}));
ASSERT_TRUE(embedded_test_server()->Start());
GURL url1 = embedded_test_server()->GetURL("/sometimes-slow");
GURL url2 = embedded_test_server()->GetURL("/sometimes-slow#foo");
content::WebContents* wc =
browser()->tab_strip_model()->GetActiveWebContents();
// Successfully navigate to `url1`, then do a same-document navigation to
// `url2`.
got_slow_request = content::SlowHttpResponse::FinishResponseImmediately();
EXPECT_TRUE(NavigateToURL(wc, url1));
EXPECT_TRUE(NavigateToURL(wc, url2));
// Kill the renderer for the tab.
{
content::ScopedAllowRendererCrashes scoped_allow_renderer_crashes;
content::RenderFrameDeletedObserver crash_observer(
wc->GetPrimaryMainFrame());
wc->GetPrimaryMainFrame()->GetProcess()->Shutdown(1);
crash_observer.WaitUntilDeleted();
}
// Bring the process back to life for the current RenderFrameHost, though with
// a speculative RenderFrameHost navigating back to `url1`.
{
content::NavigationController::LoadURLParams params(url1);
params.transition_type = ui::PageTransitionFromInt(
ui::PAGE_TRANSITION_TYPED | ui::PAGE_TRANSITION_FROM_ADDRESS_BAR);
base::RunLoop loop;
got_slow_request =
base::BindLambdaForTesting([&](base::OnceClosure start_response,
base::OnceClosure finish_response) {
// Never starts the response, but informs the test the request has
// been received.
loop.Quit();
});
wc->GetController().LoadURLWithParams(params);
loop.Run();
}
// The navigation has not completed, but the renderer has come alive.
EXPECT_TRUE(wc->GetPrimaryMainFrame()->IsRenderFrameLive());
EXPECT_EQ(wc->GetPrimaryMainFrame()->GetLastCommittedURL().spec(), "");
content::NavigationHandleCommitObserver back_observer(wc, url1);
// Now try to go back. We're currently at `url2` since the above navigation
// will be blocked. Going back to `url1` should be a same-document history
// navigation according to the NavigationEntry. But it can't be since the
// current frame host does not actually have a document loaded.
got_slow_request = content::SlowHttpResponse::FinishResponseImmediately();
wc->GetController().GoBack();
EXPECT_TRUE(WaitForLoadStop(wc));
EXPECT_TRUE(back_observer.has_committed());
EXPECT_FALSE(back_observer.was_same_document());
}
#if !BUILDFLAG(IS_CHROMEOS_LACROS)
IN_PROC_BROWSER_TEST_F(BrowserTest, CreatePictureInPicture) {
Browser* popup_browser = Browser::Create(Browser::CreateParams(
Browser::TYPE_PICTURE_IN_PICTURE, browser()->profile(), true));
ASSERT_TRUE(popup_browser->is_type_picture_in_picture());
}
#endif // !IS_CHROMEOS_LACROS
#if BUILDFLAG(IS_CHROMEOS_ASH)
IN_PROC_BROWSER_TEST_F(BrowserTest, PreventCloseYieldsCancelledEvent) {
base::ScopedObservation<BrowserList, BrowserListObserver> observer(this);
observer.Observe(BrowserList::GetInstance());
base::test::TestFuture<void> policy_refresh_sync_future;
web_app::WebAppProvider::GetForWebApps(profile())
->policy_manager()
.SetRefreshPolicySettingsCompletedCallbackForTesting(
policy_refresh_sync_future.GetCallback());
const absl::Cleanup policy_cleanup = [this]() {
// Clear policy values, otherwise we won't be able to gracefully close the
// browser test.
profile()->GetPrefs()->SetList(prefs::kWebAppSettings, base::Value::List());
};
// Set up policy values.
static constexpr char kCalculatorAppUrl[] = "https://calculator.apps.chrome/";
profile()->GetPrefs()->SetList(
prefs::kWebAppSettings,
base::Value::List().Append(
base::Value::Dict()
.Set(web_app::kManifestId, kCalculatorAppUrl)
.Set(web_app::kRunOnOsLogin, web_app::kRunWindowed)
.Set(web_app::kPreventClose, true)));
profile()->GetPrefs()->SetList(
prefs::kWebAppInstallForceList,
base::Value::List().Append(
base::Value::Dict()
.Set(web_app::kUrlKey, kCalculatorAppUrl)
.Set(web_app::kDefaultLaunchContainerKey,
web_app::kDefaultLaunchContainerWindowValue)));
ASSERT_TRUE(policy_refresh_sync_future.Wait());
apps::AppUpdateWaiter waiter(
profile(), web_app::kCalculatorAppId,
base::BindRepeating([](const apps::AppUpdate& update) {
return update.AllowClose().has_value() && !update.AllowClose().value();
}));
waiter.Await();
Browser* const browser =
web_app::LaunchWebAppBrowser(profile(), web_app::kCalculatorAppId);
ASSERT_TRUE(browser);
EXPECT_EQ(BrowserClosingStatus::kDeniedByPolicy,
browser->HandleBeforeClose());
EXPECT_CALL(*this, OnBrowserCloseCancelled(
browser, BrowserClosingStatus::kDeniedByPolicy))
.Times(1);
browser->OnWindowClosing();
}
#endif // BUILDFLAG(IS_CHROMEOS_ASH)