blob: fa632723e04eb8987ff2bdc134468fe87371a721 [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 "chrome/browser/ui/webui/webui_embedding_context.h"
#include "base/test/mock_callback.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/resource_coordinator/tab_lifecycle_unit_external.h"
#include "chrome/browser/resource_coordinator/tab_lifecycle_unit_source.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/test/browser_test.h"
using WebUIEmbeddingContextTest = InProcessBrowserTest;
namespace webui {
IN_PROC_BROWSER_TEST_F(
WebUIEmbeddingContextTest,
InitEmbeddingContext_MovingTabsAcrossWindowsUpdatesContext) {
// Create a browser with 2 tabs.
content::WebContents* tab_contents =
chrome::AddAndReturnTabAt(browser(), GURL(url::kAboutBlankURL), 1, true);
tabs::TabInterface* tab_interface =
browser()->tab_strip_model()->GetTabAtIndex(1);
EXPECT_EQ(2, browser()->tab_strip_model()->count());
EXPECT_EQ(tab_interface, GetTabInterface(tab_contents));
EXPECT_EQ(browser(), GetBrowserWindowInterface(tab_contents));
base::MockCallback<base::RepeatingClosure> tab_changed_callback;
base::MockCallback<base::RepeatingClosure> browser_changed_callback;
base::CallbackListSubscription tab_subscription =
RegisterTabInterfaceChanged(tab_contents, tab_changed_callback.Get());
base::CallbackListSubscription browser_subscription =
RegisterBrowserWindowInterfaceChanged(tab_contents,
browser_changed_callback.Get());
// Move the tab into a new browser window.
EXPECT_CALL(tab_changed_callback, Run).Times(0);
EXPECT_CALL(browser_changed_callback, Run).Times(1);
ui_test_utils::BrowserChangeObserver new_browser_observer(
nullptr, ui_test_utils::BrowserChangeObserver::ChangeType::kAdded);
chrome::MoveTabsToNewWindow(browser(), {1});
Browser* new_browser = new_browser_observer.Wait();
ASSERT_TRUE(new_browser);
EXPECT_EQ(1, browser()->tab_strip_model()->count());
EXPECT_EQ(1, new_browser->tab_strip_model()->count());
EXPECT_EQ(tab_interface, GetTabInterface(tab_contents));
EXPECT_EQ(new_browser, GetBrowserWindowInterface(tab_contents));
testing::Mock::VerifyAndClearExpectations(&tab_changed_callback);
testing::Mock::VerifyAndClearExpectations(&browser_changed_callback);
// Move the tab back into its original browser window. This results in the
// closing and destruction of the previously created browser. Only one
// browser-changed notification is expected as the tab is moved to the new
// browser before the old browser is destroyed.
EXPECT_CALL(tab_changed_callback, Run).Times(0);
EXPECT_CALL(browser_changed_callback, Run).Times(1);
chrome::MoveTabsToExistingWindow(new_browser, browser(), {0});
EXPECT_EQ(2, browser()->tab_strip_model()->count());
EXPECT_EQ(tab_interface, GetTabInterface(tab_contents));
EXPECT_EQ(browser(), GetBrowserWindowInterface(tab_contents));
testing::Mock::VerifyAndClearExpectations(&browser_changed_callback);
}
IN_PROC_BROWSER_TEST_F(WebUIEmbeddingContextTest,
InitEmbeddingContext_SetCorrectlyOnTabDiscard) {
// Create a browser with 2 tabs.
content::WebContents* tab_contents =
chrome::AddAndReturnTabAt(browser(), GURL(url::kAboutBlankURL), 1, false);
tabs::TabInterface* tab_interface =
browser()->tab_strip_model()->GetTabAtIndex(1);
EXPECT_EQ(2, browser()->tab_strip_model()->count());
EXPECT_EQ(tab_interface, GetTabInterface(tab_contents));
EXPECT_EQ(browser(), GetBrowserWindowInterface(tab_contents));
// Discard the tab.
EXPECT_NE(browser()->tab_strip_model()->GetActiveTab(), tab_interface);
auto* lifecycle_unit =
resource_coordinator::TabLifecycleUnitSource::GetTabLifecycleUnitExternal(
tab_contents);
lifecycle_unit->DiscardTab(mojom::LifecycleUnitDiscardReason::URGENT);
EXPECT_EQ(mojom::LifecycleUnitState::DISCARDED,
lifecycle_unit->GetTabState());
tab_contents = browser()->tab_strip_model()->GetTabAtIndex(1)->GetContents();
// The tab and browser interfaces should remain associated with the tab
// contents after discard.
EXPECT_EQ(tab_interface, GetTabInterface(tab_contents));
EXPECT_EQ(browser(), GetBrowserWindowInterface(tab_contents));
}
IN_PROC_BROWSER_TEST_F(WebUIEmbeddingContextTest,
SetTabInterface_TracksEmbedderStateCorrectly) {
// Create a new WebContents, the tab and browser should start empty.
std::unique_ptr<content::WebContents> host_contents =
content::WebContents::Create(
content::WebContents::CreateParams(browser()->profile()));
EXPECT_FALSE(GetTabInterface(host_contents.get()));
EXPECT_FALSE(GetBrowserWindowInterface(host_contents.get()));
base::MockCallback<base::RepeatingClosure> tab_changed_callback;
base::MockCallback<base::RepeatingClosure> browser_changed_callback;
base::CallbackListSubscription tab_subscription = RegisterTabInterfaceChanged(
host_contents.get(), tab_changed_callback.Get());
base::CallbackListSubscription browser_subscription =
RegisterBrowserWindowInterfaceChanged(host_contents.get(),
browser_changed_callback.Get());
// Set the tab interface, the tracked state should update.
EXPECT_CALL(tab_changed_callback, Run).Times(1);
EXPECT_CALL(browser_changed_callback, Run).Times(1);
tabs::TabInterface* tab_interface =
browser()->tab_strip_model()->GetActiveTab();
SetTabInterface(host_contents.get(), tab_interface);
EXPECT_EQ(tab_interface, GetTabInterface(host_contents.get()));
EXPECT_EQ(browser(), GetBrowserWindowInterface(host_contents.get()));
testing::Mock::VerifyAndClearExpectations(&tab_changed_callback);
testing::Mock::VerifyAndClearExpectations(&browser_changed_callback);
// Reset the tab interface, this should be reflected in the tracked state.
EXPECT_CALL(tab_changed_callback, Run).Times(1);
EXPECT_CALL(browser_changed_callback, Run).Times(1);
SetTabInterface(host_contents.get(), nullptr);
EXPECT_FALSE(GetTabInterface(host_contents.get()));
EXPECT_FALSE(GetBrowserWindowInterface(host_contents.get()));
}
IN_PROC_BROWSER_TEST_F(WebUIEmbeddingContextTest,
SetTabInterface_NotifiesBrowserChanges) {
// Create a new WebContents and set the emebdding tab interface.
std::unique_ptr<content::WebContents> host_contents =
content::WebContents::Create(
content::WebContents::CreateParams(browser()->profile()));
chrome::AddTabAt(browser(), GURL(url::kAboutBlankURL), 1, true);
tabs::TabInterface* tab_interface =
browser()->tab_strip_model()->GetTabAtIndex(1);
SetTabInterface(host_contents.get(), tab_interface);
EXPECT_EQ(tab_interface, GetTabInterface(host_contents.get()));
EXPECT_EQ(browser(), GetBrowserWindowInterface(host_contents.get()));
base::MockCallback<base::RepeatingClosure> tab_changed_callback;
base::MockCallback<base::RepeatingClosure> browser_changed_callback;
base::CallbackListSubscription tab_subscription = RegisterTabInterfaceChanged(
host_contents.get(), tab_changed_callback.Get());
base::CallbackListSubscription browser_subscription =
RegisterBrowserWindowInterfaceChanged(host_contents.get(),
browser_changed_callback.Get());
// Create a new browser and move the tab over, the appropriate browser changed
// notification should be fired.
EXPECT_CALL(tab_changed_callback, Run).Times(0);
EXPECT_CALL(browser_changed_callback, Run).Times(1);
Browser* dst_browser = CreateBrowser(browser()->profile());
std::unique_ptr<tabs::TabModel> detached_tab =
browser()->tab_strip_model()->DetachTabAtForInsertion(1);
EXPECT_EQ(tab_interface, detached_tab.get());
dst_browser->tab_strip_model()->InsertDetachedTabAt(
1, std::move(detached_tab), AddTabTypes::ADD_NONE);
EXPECT_EQ(tab_interface, GetTabInterface(host_contents.get()));
EXPECT_EQ(dst_browser, GetBrowserWindowInterface(host_contents.get()));
}
IN_PROC_BROWSER_TEST_F(WebUIEmbeddingContextTest,
SetTabInterface_TrackedStateResetOnEmbedderDestruction) {
// Create a new WebContents and set the emebdding tab interface.
std::unique_ptr<content::WebContents> host_contents =
content::WebContents::Create(
content::WebContents::CreateParams(browser()->profile()));
chrome::AddTabAt(browser(), GURL(url::kAboutBlankURL), 1, true);
tabs::TabInterface* tab_interface =
browser()->tab_strip_model()->GetTabAtIndex(1);
SetTabInterface(host_contents.get(), tab_interface);
EXPECT_EQ(tab_interface, GetTabInterface(host_contents.get()));
EXPECT_EQ(browser(), GetBrowserWindowInterface(host_contents.get()));
base::MockCallback<base::RepeatingClosure> tab_changed_callback;
base::MockCallback<base::RepeatingClosure> browser_changed_callback;
base::CallbackListSubscription tab_subscription = RegisterTabInterfaceChanged(
host_contents.get(), tab_changed_callback.Get());
base::CallbackListSubscription browser_subscription =
RegisterBrowserWindowInterfaceChanged(host_contents.get(),
browser_changed_callback.Get());
// Destroy the embedding tab, this should be reflected in tracked state.
EXPECT_CALL(tab_changed_callback, Run).Times(1);
EXPECT_CALL(browser_changed_callback, Run).Times(1);
browser()->tab_strip_model()->DetachAndDeleteWebContentsAt(1);
EXPECT_FALSE(GetTabInterface(host_contents.get()));
EXPECT_FALSE(GetBrowserWindowInterface(host_contents.get()));
}
} // namespace webui