blob: 5bb98549fe6e851688280dc0bc1d68d9c5b3fa3f [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/web_applications/app_browser_controller.h"
#include <memory>
#include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/installable/installable_metrics.h"
#include "chrome/browser/themes/custom_theme_supplier.h"
#include "chrome/browser/themes/theme_properties.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/extensions/application_launch.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
#include "chrome/browser/ui/web_applications/system_web_app_ui_utils.h"
#include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
#include "chrome/browser/web_applications/components/install_manager.h"
#include "chrome/browser/web_applications/components/web_app_constants.h"
#include "chrome/browser/web_applications/system_web_app_manager.h"
#include "chrome/browser/web_applications/test/test_system_web_app_installation.h"
#include "chrome/browser/web_applications/web_app_provider.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/web_application_info.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/test/test_utils.h"
#include "content/public/test/theme_change_waiter.h"
#include "extensions/browser/extension_registry.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
namespace {
SkColor GetTabColor(Browser* browser) {
CustomThemeSupplier* theme = browser->app_controller()->GetThemeSupplier();
SkColor result;
EXPECT_TRUE(theme->GetColor(ThemeProperties::COLOR_TOOLBAR, &result));
return result;
}
} // namespace
namespace web_app {
class LoadFinishedWaiter : public TabStripModelObserver,
public content::WebContentsObserver {
public:
explicit LoadFinishedWaiter(Browser* browser) : browser_(browser) {
browser_->tab_strip_model()->AddObserver(this);
}
~LoadFinishedWaiter() override {
browser_->tab_strip_model()->RemoveObserver(this);
}
void Wait() { run_loop_.Run(); }
SkColor GetColorAtNavigation() { return color_at_navigation_; }
// TabStripModelObserver:
void OnTabStripModelChanged(
TabStripModel* tab_strip_model,
const TabStripModelChange& change,
const TabStripSelectionChange& selection) override {
if (selection.active_tab_changed())
content::WebContentsObserver::Observe(selection.new_contents);
}
// content::WebContentsObserver:
void DidFinishNavigation(content::NavigationHandle* handle) override {
color_at_navigation_ = GetTabColor(browser_);
}
void DidFinishLoad(content::RenderFrameHost* render_frame_host,
const GURL& validated_url) override {
run_loop_.Quit();
}
private:
Browser* browser_;
SkColor color_at_navigation_;
base::RunLoop run_loop_;
};
class AppBrowserControllerBrowserTest : public InProcessBrowserTest {
public:
AppBrowserControllerBrowserTest()
: test_system_web_app_installation_(
TestSystemWebAppInstallation::SetUpTabbedMultiWindowApp()) {}
protected:
void InstallAndLaunchMockApp() {
test_system_web_app_installation_->WaitForAppInstall();
app_browser_ = web_app::LaunchWebAppBrowser(
browser()->profile(), test_system_web_app_installation_->GetAppId());
tabbed_app_url_ = test_system_web_app_installation_->GetAppUrl();
ASSERT_TRUE(content::NavigateToURL(
app_browser_->tab_strip_model()->GetActiveWebContents(),
tabbed_app_url_));
}
void InstallAndLaunchMockPopup() {
test_system_web_app_installation_->WaitForAppInstall();
auto params = web_app::CreateSystemWebAppLaunchParams(
browser()->profile(), test_system_web_app_installation_->GetType());
params->disposition = WindowOpenDisposition::NEW_POPUP;
app_browser_ = web_app::LaunchSystemWebApp(
browser()->profile(), test_system_web_app_installation_->GetType(),
test_system_web_app_installation_->GetAppUrl(), *params);
}
GURL GetActiveTabURL() {
return app_browser_->tab_strip_model()
->GetActiveWebContents()
->GetVisibleURL();
}
Browser* app_browser_ = nullptr;
GURL tabbed_app_url_;
private:
std::unique_ptr<TestSystemWebAppInstallation>
test_system_web_app_installation_;
DISALLOW_COPY_AND_ASSIGN(AppBrowserControllerBrowserTest);
};
IN_PROC_BROWSER_TEST_F(AppBrowserControllerBrowserTest, TabsTest) {
InstallAndLaunchMockApp();
EXPECT_TRUE(app_browser_->SupportsWindowFeature(Browser::FEATURE_TABSTRIP));
// No favicons shown for system apps.
EXPECT_FALSE(
app_browser_->tab_strip_model()->delegate()->ShouldDisplayFavicon(
app_browser_->tab_strip_model()->GetActiveWebContents()));
// Tabbed PWAs only open URLs within the scope of the app. The manifest is
// another URL besides |tabbed_app_url_| in scope.
GURL manifest("chrome://test-system-app/manifest.json");
// Check URL of tab1.
EXPECT_EQ(GetActiveTabURL(), tabbed_app_url_);
// Create tab2 with another URL from app, check URL, number of tabs.
chrome::AddTabAt(app_browser_, manifest, -1, true);
EXPECT_EQ(app_browser_->tab_strip_model()->count(), 2);
EXPECT_EQ(GetActiveTabURL(), manifest);
// Create tab3 with default URL, check URL, number of tabs.
chrome::NewTab(app_browser_);
EXPECT_EQ(app_browser_->tab_strip_model()->count(), 3);
EXPECT_EQ(GetActiveTabURL(), tabbed_app_url_);
// Switch to tab1, check URL.
chrome::SelectNextTab(app_browser_);
EXPECT_EQ(app_browser_->tab_strip_model()->count(), 3);
EXPECT_EQ(GetActiveTabURL(), tabbed_app_url_);
// Switch to tab2, check URL.
chrome::SelectNextTab(app_browser_);
EXPECT_EQ(app_browser_->tab_strip_model()->count(), 3);
EXPECT_EQ(GetActiveTabURL(), manifest);
// Switch to tab3, check URL.
chrome::SelectNextTab(app_browser_);
EXPECT_EQ(app_browser_->tab_strip_model()->count(), 3);
EXPECT_EQ(GetActiveTabURL(), tabbed_app_url_);
// Close tab3, check number of tabs.
chrome::CloseTab(app_browser_);
EXPECT_EQ(app_browser_->tab_strip_model()->count(), 2);
EXPECT_EQ(GetActiveTabURL(), manifest);
// Close tab2, check number of tabs.
chrome::CloseTab(app_browser_);
EXPECT_EQ(app_browser_->tab_strip_model()->count(), 1);
EXPECT_EQ(GetActiveTabURL(), tabbed_app_url_);
}
IN_PROC_BROWSER_TEST_F(AppBrowserControllerBrowserTest, NonAppUrl) {
InstallAndLaunchMockApp();
// Check we have 2 browsers: |browser()| and |app_browser_|.
EXPECT_EQ(BrowserList::GetInstance()->size(), 2u);
EXPECT_NE(browser(), app_browser_);
EXPECT_TRUE(browser()->is_type_normal());
EXPECT_EQ(browser()->tab_strip_model()->count(), 1);
EXPECT_EQ(
browser()->tab_strip_model()->GetActiveWebContents()->GetVisibleURL(),
"about:blank");
EXPECT_TRUE(app_browser_->is_type_app());
EXPECT_EQ(app_browser_->tab_strip_model()->count(), 1);
EXPECT_EQ(GetActiveTabURL(), tabbed_app_url_);
// Create tab2 with URL not from app, it will open in the NORMAL browser.
chrome::AddTabAt(app_browser_, GURL(chrome::kChromeUINewTabURL), -1, true);
EXPECT_EQ(BrowserList::GetInstance()->size(), 2u);
EXPECT_NE(browser(), app_browser_);
EXPECT_TRUE(browser()->is_type_normal());
EXPECT_EQ(browser()->tab_strip_model()->count(), 2);
EXPECT_EQ(
browser()->tab_strip_model()->GetActiveWebContents()->GetVisibleURL(),
"chrome://newtab/");
EXPECT_TRUE(app_browser_->is_type_app());
EXPECT_EQ(app_browser_->tab_strip_model()->count(), 1);
EXPECT_EQ(GetActiveTabURL(), tabbed_app_url_);
}
IN_PROC_BROWSER_TEST_F(AppBrowserControllerBrowserTest, TabLoadNoThemeChange) {
InstallAndLaunchMockApp();
EXPECT_EQ(app_browser_->tab_strip_model()->count(), 1);
// First tab gets manifest theme immediately.
EXPECT_EQ(GetTabColor(app_browser_), SK_ColorGREEN);
// Dynamically change color.
content::WebContents* web_contents =
app_browser_->tab_strip_model()->GetActiveWebContents();
content::ThemeChangeWaiter theme_waiter(web_contents);
EXPECT_TRUE(content::ExecJs(web_contents, R"(
document.documentElement.innerHTML =
'<meta name="theme-color" content="yellow">';
)",
content::EXECUTE_SCRIPT_DEFAULT_OPTIONS,
/*world_id=*/1));
theme_waiter.Wait();
EXPECT_EQ(GetTabColor(app_browser_), SK_ColorYELLOW);
// Second tab keeps dynamic theme until loaded.
LoadFinishedWaiter load_waiter(app_browser_);
chrome::NewTab(app_browser_);
load_waiter.Wait();
EXPECT_EQ(app_browser_->tab_strip_model()->count(), 2);
EXPECT_EQ(load_waiter.GetColorAtNavigation(), SK_ColorYELLOW);
EXPECT_EQ(GetTabColor(app_browser_), SK_ColorGREEN);
// Switching tabs updates themes immediately.
chrome::SelectNextTab(app_browser_);
EXPECT_EQ(GetTabColor(app_browser_), SK_ColorYELLOW);
chrome::SelectNextTab(app_browser_);
EXPECT_EQ(GetTabColor(app_browser_), SK_ColorGREEN);
}
// App Popups are only used on Chrome OS. See https://crbug.com/1060917.
#if defined(OS_CHROMEOS)
IN_PROC_BROWSER_TEST_F(AppBrowserControllerBrowserTest,
WhiteThemeForSystemAppPopup) {
InstallAndLaunchMockPopup();
EXPECT_FALSE(app_browser_->app_controller()->GetThemeColor().has_value());
}
IN_PROC_BROWSER_TEST_F(AppBrowserControllerBrowserTest,
ReuseBrowserForSystemAppPopup) {
InstallAndLaunchMockPopup();
// We should have the original browser for this BrowserTest, plus new popup.
EXPECT_EQ(BrowserList::GetInstance()->size(), 2u);
InstallAndLaunchMockPopup();
EXPECT_EQ(BrowserList::GetInstance()->size(), 2u);
}
IN_PROC_BROWSER_TEST_F(AppBrowserControllerBrowserTest,
NoExtensionsContainerExists) {
InstallAndLaunchMockPopup();
EXPECT_EQ(app_browser_->window()->GetExtensionsContainer(), nullptr);
}
#endif
#if defined(OS_CHROMEOS)
IN_PROC_BROWSER_TEST_F(AppBrowserControllerBrowserTest, InitialBounds) {
InstallAndLaunchMockApp();
EXPECT_EQ(app_browser_->window()->GetBounds(), gfx::Rect(64, 64, 652, 484));
InstallAndLaunchMockPopup();
gfx::Rect work_area =
display::Screen::GetScreen()->GetDisplayForNewWindows().work_area();
int x = (work_area.width() - 768) / 2;
int y = (work_area.height() - 512) / 2;
EXPECT_EQ(app_browser_->window()->GetBounds(), gfx::Rect(x, y, 768, 512));
}
#endif
class AppBrowserControllerChromeUntrustedBrowserTest
: public InProcessBrowserTest {
public:
AppBrowserControllerChromeUntrustedBrowserTest()
: test_system_web_app_installation_(
TestSystemWebAppInstallation::SetUpChromeUntrustedApp()) {}
protected:
Browser* InstallAndLaunchMockApp() {
test_system_web_app_installation_->WaitForAppInstall();
Browser* app_browser = web_app::LaunchWebAppBrowser(
browser()->profile(), test_system_web_app_installation_->GetAppId());
CHECK(content::NavigateToURL(
app_browser->tab_strip_model()->GetActiveWebContents(),
test_system_web_app_installation_->GetAppUrl()));
return app_browser;
}
private:
std::unique_ptr<TestSystemWebAppInstallation>
test_system_web_app_installation_;
};
IN_PROC_BROWSER_TEST_F(AppBrowserControllerChromeUntrustedBrowserTest,
DoesNotShowToolbar) {
Browser* app_browser = InstallAndLaunchMockApp();
EXPECT_FALSE(app_browser->app_controller()->ShouldShowCustomTabBar());
}
} // namespace web_app