#ifdef UNSAFE_BUFFERS_BUILD
#pragma allow_unsafe_buffers
#endif
#include "chrome/browser/extensions/api/tabs/tabs_api.h"
#include <memory>
#include <optional>
#include <utility>
#include "base/containers/contains.h"
#include "base/run_loop.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/values_test_util.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/extensions/api/tab_groups/tab_groups_util.h"
#include "chrome/browser/extensions/api/tabs/tabs_constants.h"
#include "chrome/browser/extensions/extension_service_test_base.h"
#include "chrome/browser/extensions/extension_tab_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sessions/session_tab_helper_factory.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/tabs/saved_tab_groups/saved_tab_group_service_factory.h"
#include "chrome/browser/ui/tabs/tab_group.h"
#include "chrome/browser/ui/tabs/tab_group_model.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/ui_features.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/test_browser_window.h"
#include "components/saved_tab_groups/features.h"
#include "components/sessions/content/session_tab_helper.h"
#include "components/tab_groups/tab_group_id.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/test/navigation_simulator.h"
#include "content/public/test/web_contents_tester.h"
#include "extensions/browser/api_test_utils.h"
#include "extensions/common/constants.h"
#include "extensions/common/extension_builder.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ash/test/ash_test_helper.h"
#include "ash/test/test_window_builder.h"
#include "chrome/browser/ui/chromeos/window_pin_util.h"
#endif
#if BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/chromeos/policy/dlp/test/mock_dlp_content_manager.h"
#endif
namespace extensions {
namespace {
base::Value::List RunTabsQueryFunction(content::BrowserContext* browser_context,
const Extension* extension,
const std::string& query_info) { … }
scoped_refptr<const Extension> CreateTabsExtension() { … }
content::WebContents* CreateAndAppendWebContentsWithHistory(
Profile* profile,
TabStripModel* tab_strip_model,
const std::vector<GURL>& urls) { … }
}
class TabsApiUnitTest : public ExtensionServiceTestBase { … };
void TabsApiUnitTest::SetUp() { … }
void TabsApiUnitTest::TearDown() { … }
bool TabsApiUnitTest::CommitPendingLoadForController(
content::NavigationController& controller) { … }
TEST_F(TabsApiUnitTest, IsTabStripEditable) { … }
TEST_F(TabsApiUnitTest, QueryWithoutTabsPermission) { … }
TEST_F(TabsApiUnitTest, QueryWithHostPermission) { … }
TEST_F(TabsApiUnitTest, PDFExtensionNavigation) { … }
TEST_F(TabsApiUnitTest, ExecuteScriptNoTabIsNonFatalError) { … }
TEST_F(TabsApiUnitTest, TabsUpdate) { … }
TEST_F(TabsApiUnitTest, TabsUpdateSavedTabGroupTab) { … }
TEST_F(TabsApiUnitTest, TabsUpdateJavaScriptUrlNotAllowed) { … }
TEST_F(TabsApiUnitTest, TabsMoveWithinWindow) { … }
TEST_F(TabsApiUnitTest, TabsMoveAcrossWindows) { … }
TEST_F(TabsApiUnitTest, TabsMoveSavedTabGroupTabAllowed) { … }
TEST_F(TabsApiUnitTest, TabsGroupWithinWindow) { … }
TEST_F(TabsApiUnitTest, TabsGroupMixedTabIds) { … }
TEST_F(TabsApiUnitTest, TabsGroupParamsError) { … }
TEST_F(TabsApiUnitTest, TabsGroupAcrossWindows) { … }
TEST_F(TabsApiUnitTest, TabsGroupForSavedTabGroupTab) { … }
TEST_F(TabsApiUnitTest, TabsUngroupSingleGroup) { … }
TEST_F(TabsApiUnitTest, TabsUngroupSingleGroupForSavedTabGroup) { … }
TEST_F(TabsApiUnitTest, TabsUngroupFromMultipleGroups) { … }
TEST_F(TabsApiUnitTest, TabsGoForwardNoSelectedTabError) { … }
TEST_F(TabsApiUnitTest, TabsGoForwardAndBack) { … }
TEST_F(TabsApiUnitTest, TabsGoForwardAndBackSavedTabGroupTab) { … }
TEST_F(TabsApiUnitTest, TabsGoForwardAndBackWithoutTabId) { … }
#if BUILDFLAG(IS_CHROMEOS)
TEST_F(TabsApiUnitTest, ScreenshotsRestricted) {
scoped_refptr<const Extension> extension =
ExtensionBuilder("Screenshot")
.AddAPIPermission("tabs")
.AddHostPermission("<all_urls>")
.Build();
auto function = base::MakeRefCounted<TabsCaptureVisibleTabFunction>();
function->set_extension(extension.get());
std::unique_ptr<content::WebContents> web_contents =
content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
content::WebContentsTester* web_contents_tester =
content::WebContentsTester::For(web_contents.get());
const GURL kGoogle("http://www.google.com");
GetTabStripModel()->AppendWebContents(std::move(web_contents),
true);
web_contents_tester->NavigateAndCommit(kGoogle);
policy::MockDlpContentManager mock_dlp_content_manager;
policy::ScopedDlpContentObserverForTesting scoped_dlp_content_observer_(
&mock_dlp_content_manager);
EXPECT_CALL(mock_dlp_content_manager, IsScreenshotApiRestricted(testing::_))
.Times(1)
.WillOnce(testing::Return(true));
std::string error = api_test_utils::RunFunctionAndReturnError(
function.get(), "[{}]", profile(), api_test_utils::FunctionMode::kNone);
EXPECT_EQ(tabs_constants::kScreenshotsDisabledByDlp, error);
}
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
TEST_F(TabsApiUnitTest, DontCreateTabsInLockedFullscreenMode) {
scoped_refptr<const Extension> extension_with_tabs_permission =
CreateTabsExtension();
ash::TestWindowBuilder builder;
std::unique_ptr<aura::Window> window =
builder.SetTestWindowDelegate().AllowAllWindowStates().Build();
browser_window()->SetNativeWindow(window.get());
auto function = base::MakeRefCounted<TabsCreateFunction>();
function->set_extension(extension_with_tabs_permission.get());
PinWindow(browser_window()->GetNativeWindow(), true);
EXPECT_EQ(tabs_constants::kLockedFullscreenModeNewTabError,
api_test_utils::RunFunctionAndReturnError(
function.get(), "[{}]", profile(),
api_test_utils::FunctionMode::kNone));
}
TEST_F(TabsApiUnitTest, ScreenshotDisabledInProfilePreferences) {
scoped_refptr<const Extension> extension =
ExtensionBuilder("Screenshot")
.AddAPIPermission("tabs")
.AddHostPermission("<all_urls>")
.Build();
auto function = base::MakeRefCounted<TabsCaptureVisibleTabFunction>();
function->set_extension(extension.get());
std::unique_ptr<content::WebContents> web_contents =
content::WebContentsTester::CreateTestWebContents(profile(), nullptr);
content::WebContentsTester* web_contents_tester =
content::WebContentsTester::For(web_contents.get());
const GURL kGoogle("http://www.google.com");
GetTabStripModel()->AppendWebContents(std::move(web_contents),
true);
web_contents_tester->NavigateAndCommit(kGoogle);
browser()->profile()->GetPrefs()->SetBoolean(prefs::kDisableScreenshots,
true);
std::string error = api_test_utils::RunFunctionAndReturnError(
function.get(), "[{}]", profile(), api_test_utils::FunctionMode::kNone);
EXPECT_EQ(tabs_constants::kScreenshotsDisabled, error);
}
#endif
TEST_F(TabsApiUnitTest, CannotDuplicatePictureInPictureWindows) { … }
TEST_F(TabsApiUnitTest, TabsDiscard) { … }
TEST_F(TabsApiUnitTest, TabsDiscardSavedTabGroupTabNotAllowed) { … }
#if BUILDFLAG(IS_CHROMEOS)
TEST_F(TabsApiUnitTest,
TabsDiscardSavedTabGroupTabAllowedForLockedFullscreenPermission) {
scoped_refptr<const Extension> extension =
ExtensionBuilder("DiscardTest")
.SetID("pmgljoohajacndjcjlajcopidgnhphcl")
.AddAPIPermission("lockWindowFullscreenPrivate")
.Build();
const GURL kExampleCom("http://example.com");
std::unique_ptr<content::WebContents> contents(
content::WebContentsTester::CreateTestWebContents(profile(), nullptr));
content::WebContents* web_contents = contents.get();
GetTabStripModel()->AppendWebContents(std::move(contents), true);
EXPECT_EQ(GetActiveWebContents(), web_contents);
CreateSessionServiceTabHelper(web_contents);
int index = GetTabStripModel()->GetIndexOfWebContents(web_contents);
int tab_id = sessions::SessionTabHelper::IdForTab(web_contents).id();
content::WebContentsTester* web_contents_tester =
content::WebContentsTester::For(web_contents);
web_contents_tester->NavigateAndCommit(kExampleCom);
EXPECT_EQ(kExampleCom, web_contents->GetLastCommittedURL());
tab_groups::TabGroupId group = GetTabStripModel()->AddToNewGroup(
{GetTabStripModel()->GetIndexOfWebContents(web_contents)});
tab_groups::TabGroupVisualData visual_data(
u"Initial title", tab_groups::TabGroupColorId::kBlue);
browser()
->tab_strip_model()
->group_model()
->GetTabGroup(group)
->SetVisualData(visual_data);
tab_groups::SavedTabGroupKeyedService* saved_service =
tab_groups::SavedTabGroupServiceFactory::GetInstance()->GetForProfile(
browser()->profile());
ASSERT_NE(saved_service, nullptr);
saved_service->SaveGroup(group);
auto function = base::MakeRefCounted<TabsDiscardFunction>();
function->set_extension(extension);
ASSERT_TRUE(api_test_utils::RunFunction(
function.get(), base::StringPrintf("[%d]", tab_id), profile(),
api_test_utils::FunctionMode::kNone));
content::WebContents* new_contents_at_index =
GetTabStripModel()->GetWebContentsAt(index);
EXPECT_TRUE(new_contents_at_index->WasDiscarded());
saved_service->UnsaveGroup(group, tab_groups::ClosingSource::kUnknown);
}
#endif
}