blob: 5143c1a119b8eed824f9514d7a5d3ea15e4eb95e [file] [log] [blame]
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <map>
#include <memory>
#include <string>
#include "base/test/scoped_feature_list.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/web_applications/test/web_app_browsertest_util.h"
#include "chrome/browser/ui/web_applications/web_app_browsertest_base.h"
#include "chrome/browser/web_applications/navigation_capturing_information_forwarder.h"
#include "chrome/browser/web_applications/navigation_capturing_navigation_handle_user_data.h"
#include "chrome/browser/web_applications/test/web_app_install_test_utils.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/webapps/common/web_app_id.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/navigation_handle_user_data.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/content_features.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "ui/base/window_open_disposition.h"
namespace web_app {
namespace {
constexpr char kStartPageScopeA[] =
"/banners/link_capturing/scope_a/start.html";
constexpr char kDestinationPageScopeB[] =
"/banners/link_capturing/scope_b/destination.html";
constexpr char kToSiteATargetBlankWithOpener[] = "id-LINK-A_TO_A-BLANK-OPENER";
constexpr char kToSiteBTargetBlankNoopener[] = "id-LINK-A_TO_B-BLANK-NO_OPENER";
constexpr char kToSiteBTargetBlankWithOpener[] = "id-LINK-A_TO_B-BLANK-OPENER";
class NavigationCompletionAwaiter : public content::WebContentsObserver,
public ui_test_utils::AllTabsObserver {
public:
explicit NavigationCompletionAwaiter(content::WebContents* web_contents)
: content::WebContentsObserver(web_contents) {
AddAllBrowsers();
}
~NavigationCompletionAwaiter() override = default;
void DidFinishNavigation(content::NavigationHandle* handle) override {
auto* navigation_handle_data =
web_app::NavigationCapturingNavigationHandleUserData::
GetForNavigationHandle(*handle);
disposition_in_handle_ = navigation_handle_data
? navigation_handle_data->disposition()
: WindowOpenDisposition::UNKNOWN;
ConditionMet();
}
void AwaitNavigationCompletion() { Wait(); }
WindowOpenDisposition GetDispositionForNavigation() {
CHECK(disposition_in_handle_.has_value());
return disposition_in_handle_.value();
}
// AllTabsObserver override:
std::unique_ptr<base::CheckedObserver> ProcessOneContents(
content::WebContents* web_contents) override {
// Stop observing the current `WebContents` if a new one has been created,
// so that we can process the `WebContents` navigation completed in.
if (content::WebContentsObserver::IsInObserverList()) {
Observe(nullptr);
}
Observe(web_contents);
return nullptr;
}
private:
std::optional<WindowOpenDisposition> disposition_in_handle_;
};
class NavigationCapturingDataTransferBrowserTest
: public WebAppBrowserTestBase {
public:
NavigationCapturingDataTransferBrowserTest() {
std::map<std::string, std::string> parameters;
parameters["link_capturing_state"] = "reimpl_default_on";
feature_list_.InitAndEnableFeatureWithParameters(
features::kPwaNavigationCapturing, parameters);
}
~NavigationCapturingDataTransferBrowserTest() override = default;
void SetUpOnMainThread() override {
WebAppBrowserTestBase::SetUpOnMainThread();
ASSERT_TRUE(embedded_test_server()->Start());
}
protected:
GURL GetStartUrl() {
return embedded_test_server()->GetURL(kStartPageScopeA);
}
GURL GetDestinationUrl() {
return embedded_test_server()->GetURL(kDestinationPageScopeB);
}
webapps::AppId InstallTestWebApp(const GURL& start_url) {
auto web_app_info =
web_app::WebAppInstallInfo::CreateWithStartUrlForTesting(start_url);
web_app_info->user_display_mode =
web_app::mojom::UserDisplayMode::kStandalone;
web_app_info->launch_handler = blink::Manifest::LaunchHandler(
blink::mojom::ManifestLaunchHandler_ClientMode::kAuto);
web_app_info->scope = start_url.GetWithoutFilename();
web_app_info->display_mode = blink::mojom::DisplayMode::kStandalone;
const webapps::AppId app_id = web_app::test::InstallWebApp(
browser()->profile(), std::move(web_app_info));
return app_id;
}
Browser* TriggerNavigationCapturingNewAppWindow(
content::WebContents* contents,
test::ClickMethod click,
const std::string& elementId) {
ui_test_utils::BrowserChangeObserver browser_added_waiter(
nullptr, ui_test_utils::BrowserChangeObserver::ChangeType::kAdded);
test::SimulateClickOnElement(contents, elementId, click);
Browser* app_browser = browser_added_waiter.Wait();
EXPECT_NE(browser(), app_browser);
return app_browser;
}
content::WebContents* OpenStartPageInApp(const webapps::AppId& app_id) {
content::DOMMessageQueue message_queue;
Browser* app_browser =
web_app::LaunchWebAppBrowser(browser()->profile(), app_id);
content::WebContents* contents =
app_browser->tab_strip_model()->GetActiveWebContents();
std::string message;
EXPECT_TRUE(message_queue.WaitForMessage(&message));
EXPECT_EQ("\"ReadyForLinkCaptureTesting\"", message);
return contents;
}
content::WebContents* OpenStartPageInTab() {
content::DOMMessageQueue message_queue;
EXPECT_TRUE(ui_test_utils::NavigateToURL(
browser(), embedded_test_server()->GetURL(kStartPageScopeA)));
std::string message;
EXPECT_TRUE(message_queue.WaitForMessage(&message));
EXPECT_EQ("\"ReadyForLinkCaptureTesting\"", message);
return browser()->tab_strip_model()->GetActiveWebContents();
}
NavigationCapturingInformationForwarder* GetForwarderForWebContents(
content::WebContents* contents) {
return NavigationCapturingInformationForwarder::FromWebContents(contents);
}
private:
base::test::ScopedFeatureList feature_list_;
};
IN_PROC_BROWSER_TEST_F(NavigationCapturingDataTransferBrowserTest,
LeftClickNewWebContentsGetsCorrectDisposition) {
const webapps::AppId app_id = InstallTestWebApp(GetDestinationUrl());
content::WebContents* contents = OpenStartPageInTab();
ASSERT_NE(nullptr, contents);
NavigationCompletionAwaiter nav_awaiter(contents);
Browser* app_browser = TriggerNavigationCapturingNewAppWindow(
contents, test::ClickMethod::kLeftClick, kToSiteBTargetBlankNoopener);
nav_awaiter.AwaitNavigationCompletion();
ASSERT_NE(nullptr, app_browser);
EXPECT_EQ(nav_awaiter.GetDispositionForNavigation(),
WindowOpenDisposition::NEW_FOREGROUND_TAB);
// Post navigation, the WebContentsUserData instances should be cleaned up.
EXPECT_THAT(GetForwarderForWebContents(
app_browser->tab_strip_model()->GetActiveWebContents()),
testing::IsNull());
}
IN_PROC_BROWSER_TEST_F(NavigationCapturingDataTransferBrowserTest,
LeftClickSameWebContentsGetsCorrectDisposition) {
content::WebContents* contents = OpenStartPageInTab();
ASSERT_NE(nullptr, contents);
NavigationCompletionAwaiter nav_awaiter(contents);
test::SimulateClickOnElement(contents, kToSiteATargetBlankWithOpener,
test::ClickMethod::kLeftClick);
nav_awaiter.AwaitNavigationCompletion();
EXPECT_EQ(nav_awaiter.GetDispositionForNavigation(),
WindowOpenDisposition::NEW_FOREGROUND_TAB);
// Post navigation, the WebContentsUserData instances should be cleaned up.
EXPECT_THAT(GetForwarderForWebContents(contents), testing::IsNull());
}
IN_PROC_BROWSER_TEST_F(NavigationCapturingDataTransferBrowserTest,
ShiftClickNewWebContentsGetsCorrectDisposition) {
const webapps::AppId app_id_a = InstallTestWebApp(GetStartUrl());
const webapps::AppId app_id_b = InstallTestWebApp(GetDestinationUrl());
content::WebContents* contents = OpenStartPageInApp(app_id_a);
ASSERT_NE(nullptr, contents);
NavigationCompletionAwaiter nav_awaiter(contents);
Browser* app_browser = TriggerNavigationCapturingNewAppWindow(
contents, test::ClickMethod::kShiftClick, kToSiteBTargetBlankWithOpener);
nav_awaiter.AwaitNavigationCompletion();
ASSERT_NE(nullptr, app_browser);
EXPECT_EQ(nav_awaiter.GetDispositionForNavigation(),
WindowOpenDisposition::NEW_WINDOW);
EXPECT_THAT(GetForwarderForWebContents(
app_browser->tab_strip_model()->GetActiveWebContents()),
testing::IsNull());
}
IN_PROC_BROWSER_TEST_F(NavigationCapturingDataTransferBrowserTest,
MiddleClickNewWebContentsGetsCorrectDisposition) {
const webapps::AppId app_id = InstallTestWebApp(GetStartUrl());
content::WebContents* contents = OpenStartPageInApp(app_id);
ASSERT_NE(nullptr, contents);
NavigationCompletionAwaiter nav_awaiter(contents);
Browser* app_browser = TriggerNavigationCapturingNewAppWindow(
contents, test::ClickMethod::kMiddleClick, kToSiteATargetBlankWithOpener);
nav_awaiter.AwaitNavigationCompletion();
ASSERT_NE(nullptr, app_browser);
EXPECT_EQ(nav_awaiter.GetDispositionForNavigation(),
WindowOpenDisposition::NEW_BACKGROUND_TAB);
EXPECT_THAT(GetForwarderForWebContents(
app_browser->tab_strip_model()->GetActiveWebContents()),
testing::IsNull());
}
} // namespace
} // namespace web_app