| // Copyright 2013 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/views/frame/browser_view.h" | 
 |  | 
 | #include <memory> | 
 |  | 
 | #include "base/callback_list.h" | 
 | #include "base/functional/bind.h" | 
 | #include "base/memory/raw_ptr.h" | 
 | #include "base/strings/utf_string_conversions.h" | 
 | #include "base/test/values_test_util.h" | 
 | #include "base/time/time.h" | 
 | #include "build/build_config.h" | 
 | #include "chrome/browser/devtools/devtools_window_testing.h" | 
 | #include "chrome/browser/policy/dm_token_utils.h" | 
 | #include "chrome/browser/profiles/profile.h" | 
 | #include "chrome/browser/safe_browsing/chrome_enterprise_url_lookup_service_factory.h" | 
 | #include "chrome/browser/ui/browser.h" | 
 | #include "chrome/browser/ui/browser_commands_mac.h" | 
 | #include "chrome/browser/ui/browser_navigator.h" | 
 | #include "chrome/browser/ui/browser_tabstrip.h" | 
 | #include "chrome/browser/ui/browser_window/public/browser_window_features.h" | 
 | #include "chrome/browser/ui/tab_modal_confirm_dialog.h" | 
 | #include "chrome/browser/ui/tab_ui_helper.h" | 
 | #include "chrome/browser/ui/tabs/public/tab_features.h" | 
 | #include "chrome/browser/ui/tabs/split_tab_metrics.h" | 
 | #include "chrome/browser/ui/tabs/tab_strip_model.h" | 
 | #include "chrome/browser/ui/tabs/tab_strip_user_gesture_details.h" | 
 | #include "chrome/browser/ui/test/test_browser_ui.h" | 
 | #include "chrome/browser/ui/ui_features.h" | 
 | #include "chrome/browser/ui/views/bookmarks/bookmark_bar_view.h" | 
 | #include "chrome/browser/ui/views/bookmarks/bookmark_bar_view_observer.h" | 
 | #include "chrome/browser/ui/views/frame/browser_view.h" | 
 | #include "chrome/browser/ui/views/frame/contents_container_view.h" | 
 | #include "chrome/browser/ui/views/frame/multi_contents_view.h" | 
 | #include "chrome/browser/ui/views/frame/multi_contents_view_drop_target_controller.h" | 
 | #include "chrome/browser/ui/views/frame/scrim_view.h" | 
 | #include "chrome/browser/ui/views/frame/top_container_view.h" | 
 | #include "chrome/browser/ui/views/side_panel/side_panel.h" | 
 | #include "chrome/browser/ui/views/side_panel/side_panel_coordinator.h" | 
 | #include "chrome/browser/ui/views/side_panel/side_panel_util.h" | 
 | #include "chrome/browser/ui/views/tabs/tab_strip.h" | 
 | #include "chrome/common/chrome_features.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/ui_test_utils.h" | 
 | #include "components/bookmarks/common/bookmark_pref_names.h" | 
 | #include "components/enterprise/connectors/core/common.h" | 
 | #include "components/enterprise/connectors/core/connectors_prefs.h" | 
 | #include "components/enterprise/data_controls/core/browser/test_utils.h" | 
 | #include "components/keyed_service/content/browser_context_dependency_manager.h" | 
 | #include "components/policy/core/common/policy_types.h" | 
 | #include "components/prefs/pref_service.h" | 
 | #include "components/safe_browsing/core/browser/realtime/fake_url_lookup_service.h" | 
 | #include "components/tabs/public/split_tab_collection.h" | 
 | #include "components/tabs/public/split_tab_visual_data.h" | 
 | #include "content/public/browser/invalidate_type.h" | 
 | #include "content/public/browser/web_contents.h" | 
 | #include "content/public/browser/web_contents_observer.h" | 
 | #include "content/public/common/drop_data.h" | 
 | #include "content/public/test/browser_test.h" | 
 | #include "content/public/test/browser_test_utils.h" | 
 | #include "content/public/test/scoped_accessibility_mode_override.h" | 
 | #include "content/public/test/test_navigation_observer.h" | 
 | #include "media/base/media_switches.h" | 
 | #include "net/traffic_annotation/network_traffic_annotation_test_helper.h" | 
 | #include "ui/accessibility/platform/ax_platform_node.h" | 
 | #include "ui/accessibility/platform/ax_platform_node_test_helper.h" | 
 | #include "ui/base/buildflags.h" | 
 | #include "ui/base/l10n/l10n_util.h" | 
 | #include "ui/base/ozone_buildflags.h" | 
 | #include "ui/base/ui_base_features.h" | 
 | #include "ui/gfx/geometry/point.h" | 
 | #include "url/url_constants.h" | 
 |  | 
 | #if defined(USE_AURA) | 
 | #include "ui/aura/client/focus_client.h" | 
 | #include "ui/views/widget/native_widget_aura.h" | 
 | #endif  // USE_AURA | 
 |  | 
 | #if BUILDFLAG(IS_OZONE) | 
 | #include "ui/ozone/public/ozone_platform.h" | 
 | #endif | 
 |  | 
 | class BrowserViewTest : public InProcessBrowserTest { | 
 |  public: | 
 |   BrowserViewTest() : devtools_(nullptr) { | 
 |     scoped_feature_list_.InitWithFeatures({features::kSideBySide}, {}); | 
 |   } | 
 |  | 
 |   BrowserViewTest(const BrowserViewTest&) = delete; | 
 |   BrowserViewTest& operator=(const BrowserViewTest&) = delete; | 
 |  | 
 |  protected: | 
 |   BrowserView* browser_view() { | 
 |     return BrowserView::GetBrowserViewForBrowser(browser()); | 
 |   } | 
 |  | 
 |   views::WebView* devtools_web_view() { | 
 |     return browser_view() | 
 |         ->GetActiveContentsContainerView() | 
 |         ->devtools_web_view(); | 
 |   } | 
 |  | 
 |   views::WebView* contents_web_view() { | 
 |     return browser_view()->contents_web_view(); | 
 |   } | 
 |  | 
 |   content::WebContents* active_web_contents() { | 
 |     return browser()->tab_strip_model()->GetActiveWebContents(); | 
 |   } | 
 |  | 
 |   ScrimView* active_contents_scrim_view() { | 
 |     return browser_view() | 
 |         ->GetActiveContentsContainerView() | 
 |         ->contents_scrim_view(); | 
 |   } | 
 |  | 
 |   ContentsContainerView* active_contents_container_view() { | 
 |     return browser_view() | 
 |         ->multi_contents_view() | 
 |         ->GetActiveContentsContainerView(); | 
 |   } | 
 |  | 
 |   ContentsContainerView* inactive_contents_container_view() { | 
 |     return browser_view() | 
 |         ->multi_contents_view() | 
 |         ->GetInactiveContentsContainerView(); | 
 |   } | 
 |  | 
 |   void OpenDevToolsWindow(bool docked) { | 
 |     devtools_ = | 
 |         DevToolsWindowTesting::OpenDevToolsWindowSync(browser(), docked); | 
 |   } | 
 |  | 
 |   void CloseDevToolsWindow() { | 
 |     DevToolsWindowTesting::CloseDevToolsWindowSync( | 
 |         devtools_.ExtractAsDangling()); | 
 |   } | 
 |  | 
 |   void SetDevToolsBounds(const gfx::Rect& bounds) { | 
 |     DevToolsWindowTesting::Get(devtools_)->SetInspectedPageBounds(bounds); | 
 |   } | 
 |  | 
 |   raw_ptr<DevToolsWindow> devtools_; | 
 |  | 
 |   base::test::ScopedFeatureList scoped_feature_list_; | 
 | }; | 
 |  | 
 | class BrowserViewWithoutSideBySideTest : public BrowserViewTest { | 
 |  public: | 
 |   BrowserViewWithoutSideBySideTest() { | 
 |     scoped_feature_list_.InitWithFeatures({}, {features::kSideBySide}); | 
 |   } | 
 |  | 
 |   SidePanel* side_panel() { | 
 |     return browser_view()->contents_height_side_panel(); | 
 |   } | 
 |  | 
 |   views::View* side_panel_rounded_corner() { | 
 |     return browser_view()->GetSidePanelRoundedCornerForTesting(); | 
 |   } | 
 |  | 
 |  private: | 
 |   base::test::ScopedFeatureList scoped_feature_list_; | 
 | }; | 
 |  | 
 | namespace { | 
 | // Used to simulate scenario in a crash. When WebContentsDestroyed() is | 
 | // invoked updates the navigation state of another tab. | 
 | class TestWebContentsObserver : public content::WebContentsObserver { | 
 |  public: | 
 |   TestWebContentsObserver(content::WebContents* source, | 
 |                           content::WebContents* other) | 
 |       : content::WebContentsObserver(source), other_(other) {} | 
 |  | 
 |   TestWebContentsObserver(const TestWebContentsObserver&) = delete; | 
 |   TestWebContentsObserver& operator=(const TestWebContentsObserver&) = delete; | 
 |  | 
 |   ~TestWebContentsObserver() override = default; | 
 |  | 
 |   void WebContentsDestroyed() override { | 
 |     other_->NotifyNavigationStateChanged(static_cast<content::InvalidateTypes>( | 
 |         content::INVALIDATE_TYPE_URL | content::INVALIDATE_TYPE_LOAD)); | 
 |   } | 
 |  | 
 |  private: | 
 |   raw_ptr<content::WebContents, DanglingUntriaged> other_; | 
 | }; | 
 |  | 
 | // Waits for a different view to claim focus within a widget with the | 
 | // specified name. | 
 | class TestFocusChangeWaiter : public views::FocusChangeListener { | 
 |  public: | 
 |   TestFocusChangeWaiter(views::FocusManager* focus_manager, | 
 |                         const std::string& expected_widget_name) | 
 |       : focus_manager_(focus_manager), | 
 |         expected_widget_name_(expected_widget_name) { | 
 |     if (auto* current_focused_view = focus_manager->GetFocusedView()) { | 
 |       previous_view_id_ = current_focused_view->GetID(); | 
 |     } else { | 
 |       previous_view_id_ = -1; | 
 |     } | 
 |     focus_manager_->AddFocusChangeListener(this); | 
 |   } | 
 |  | 
 |   TestFocusChangeWaiter(const TestFocusChangeWaiter&) = delete; | 
 |   TestFocusChangeWaiter& operator=(const TestFocusChangeWaiter&) = delete; | 
 |   ~TestFocusChangeWaiter() override { | 
 |     focus_manager_->RemoveFocusChangeListener(this); | 
 |   } | 
 |  | 
 |   void Wait() { run_loop_.Run(); } | 
 |  | 
 |  private: | 
 |   // views::FocusChangeListener: | 
 |   void OnDidChangeFocus(views::View* focused_before, | 
 |                         views::View* focused_now) override { | 
 |     if (focused_now && focused_now->GetID() != previous_view_id_) { | 
 |       views::Widget* widget = focused_now->GetWidget(); | 
 |       if (widget && widget->GetName() == expected_widget_name_) { | 
 |         run_loop_.Quit(); | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   raw_ptr<views::FocusManager> focus_manager_; | 
 |   base::RunLoop run_loop_; | 
 |   int previous_view_id_; | 
 |   std::string expected_widget_name_; | 
 |   base::WeakPtrFactory<TestFocusChangeWaiter> weak_factory_{this}; | 
 | }; | 
 |  | 
 | class TestTabModalConfirmDialogDelegate : public TabModalConfirmDialogDelegate { | 
 |  public: | 
 |   explicit TestTabModalConfirmDialogDelegate(content::WebContents* contents) | 
 |       : TabModalConfirmDialogDelegate(contents) {} | 
 |  | 
 |   TestTabModalConfirmDialogDelegate(const TestTabModalConfirmDialogDelegate&) = | 
 |       delete; | 
 |   TestTabModalConfirmDialogDelegate& operator=( | 
 |       const TestTabModalConfirmDialogDelegate&) = delete; | 
 |  | 
 |   std::u16string GetTitle() override { return std::u16string(u"Dialog Title"); } | 
 |   std::u16string GetDialogMessage() override { return std::u16string(); } | 
 | }; | 
 | }  // namespace | 
 |  | 
 | // Verifies don't crash when CloseNow() is invoked with two tabs in a browser. | 
 | // Additionally when one of the tabs is destroyed NotifyNavigationStateChanged() | 
 | // is invoked on the other. | 
 | IN_PROC_BROWSER_TEST_F(BrowserViewTest, CloseWithTabs) { | 
 |   Browser* browser2 = | 
 |       Browser::Create(Browser::CreateParams(browser()->profile(), true)); | 
 |   chrome::AddTabAt(browser2, GURL(), -1, true); | 
 |   chrome::AddTabAt(browser2, GURL(), -1, true); | 
 |   TestWebContentsObserver observer( | 
 |       browser2->tab_strip_model()->GetWebContentsAt(0), | 
 |       browser2->tab_strip_model()->GetWebContentsAt(1)); | 
 |   BrowserView::GetBrowserViewForBrowser(browser2)->GetWidget()->CloseNow(); | 
 | } | 
 |  | 
 | // Same as CloseWithTabs, but activates the first tab, which is the first tab | 
 | // BrowserView will destroy. | 
 | IN_PROC_BROWSER_TEST_F(BrowserViewTest, CloseWithTabsStartWithActive) { | 
 |   Browser* browser2 = | 
 |       Browser::Create(Browser::CreateParams(browser()->profile(), true)); | 
 |   chrome::AddTabAt(browser2, GURL(), -1, true); | 
 |   chrome::AddTabAt(browser2, GURL(), -1, true); | 
 |   browser2->tab_strip_model()->ActivateTabAt( | 
 |       0, TabStripUserGestureDetails( | 
 |              TabStripUserGestureDetails::GestureType::kOther)); | 
 |   TestWebContentsObserver observer( | 
 |       browser2->tab_strip_model()->GetWebContentsAt(0), | 
 |       browser2->tab_strip_model()->GetWebContentsAt(1)); | 
 |   BrowserView::GetBrowserViewForBrowser(browser2)->GetWidget()->CloseNow(); | 
 | } | 
 |  | 
 | #if BUILDFLAG(IS_CHROMEOS) | 
 | IN_PROC_BROWSER_TEST_F(BrowserViewTest, OnTaskLockedBrowserView) { | 
 |   browser()->SetLockedForOnTask(true); | 
 |   EXPECT_FALSE(browser_view()->CanMinimize()); | 
 |   EXPECT_FALSE(browser_view()->ShouldShowCloseButton()); | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(BrowserViewTest, OnTaskUnlockedBrowserView) { | 
 |   browser()->SetLockedForOnTask(false); | 
 |   EXPECT_TRUE(browser_view()->CanMinimize()); | 
 |   EXPECT_TRUE(browser_view()->ShouldShowCloseButton()); | 
 | } | 
 | #endif | 
 |  | 
 | // Verifies that page and devtools WebViews are being correctly laid out | 
 | // when DevTools is opened/closed/updated while docked. | 
 | IN_PROC_BROWSER_TEST_F(BrowserViewTest, DevToolsDockedUpdatesBrowserWindow) { | 
 | #if BUILDFLAG(IS_OZONE) | 
 |   // Ozone/wayland doesn't support getting/setting window position in global | 
 |   // screen coordinates. So this test is not applicable. | 
 |   if (ui::OzonePlatform::GetPlatformNameForTest() == "wayland") { | 
 |     GTEST_SKIP(); | 
 |   } | 
 | #endif | 
 |   gfx::Rect full_bounds = active_contents_container_view()->GetLocalBounds(); | 
 |   gfx::Rect small_bounds(10, 20, 30, 40); | 
 |  | 
 |   browser_view()->UpdateDevTools(active_web_contents()); | 
 |   EXPECT_FALSE(devtools_web_view()->web_contents()); | 
 |   EXPECT_EQ(full_bounds, devtools_web_view()->bounds()); | 
 |   EXPECT_EQ(full_bounds, contents_web_view()->bounds()); | 
 |  | 
 |   // Docked. | 
 |   OpenDevToolsWindow(true); | 
 |   EXPECT_TRUE(devtools_web_view()->web_contents()); | 
 |   EXPECT_EQ(full_bounds, devtools_web_view()->bounds()); | 
 |  | 
 |   SetDevToolsBounds(small_bounds); | 
 |   EXPECT_TRUE(devtools_web_view()->web_contents()); | 
 |   EXPECT_EQ(full_bounds, devtools_web_view()->bounds()); | 
 |   EXPECT_EQ(small_bounds, contents_web_view()->bounds()); | 
 |  | 
 |   browser_view()->UpdateDevTools(active_web_contents()); | 
 |   EXPECT_TRUE(devtools_web_view()->web_contents()); | 
 |   EXPECT_EQ(full_bounds, devtools_web_view()->bounds()); | 
 |   EXPECT_EQ(small_bounds, contents_web_view()->bounds()); | 
 |  | 
 |   CloseDevToolsWindow(); | 
 |   EXPECT_FALSE(devtools_web_view()->web_contents()); | 
 |   EXPECT_EQ(full_bounds, devtools_web_view()->bounds()); | 
 |   EXPECT_EQ(full_bounds, contents_web_view()->bounds()); | 
 |  | 
 |   browser_view()->UpdateDevTools(active_web_contents()); | 
 |   EXPECT_FALSE(devtools_web_view()->web_contents()); | 
 |   EXPECT_EQ(full_bounds, devtools_web_view()->bounds()); | 
 |   EXPECT_EQ(full_bounds, contents_web_view()->bounds()); | 
 | } | 
 |  | 
 | // Verifies that page and devtools WebViews are being correctly laid out | 
 | // when DevTools is opened/closed/updated while undocked. | 
 | IN_PROC_BROWSER_TEST_F(BrowserViewTest, DevToolsUndockedUpdatesBrowserWindow) { | 
 | #if BUILDFLAG(IS_OZONE) | 
 |   // Ozone/wayland doesn't support getting/setting window position in global | 
 |   // screen coordinates. So this test is not applicable. | 
 |   if (ui::OzonePlatform::GetPlatformNameForTest() == "wayland") { | 
 |     GTEST_SKIP(); | 
 |   } | 
 | #endif | 
 |   gfx::Rect full_bounds = active_contents_container_view()->GetLocalBounds(); | 
 |   gfx::Rect small_bounds(10, 20, 30, 40); | 
 |  | 
 |   OpenDevToolsWindow(false); | 
 |   EXPECT_TRUE(devtools_web_view()->web_contents()); | 
 |   EXPECT_EQ(full_bounds, devtools_web_view()->bounds()); | 
 |  | 
 |   SetDevToolsBounds(small_bounds); | 
 |   EXPECT_TRUE(devtools_web_view()->web_contents()); | 
 |   EXPECT_EQ(full_bounds, devtools_web_view()->bounds()); | 
 |   EXPECT_EQ(small_bounds, contents_web_view()->bounds()); | 
 |  | 
 |   browser_view()->UpdateDevTools(active_web_contents()); | 
 |   EXPECT_TRUE(devtools_web_view()->web_contents()); | 
 |   EXPECT_EQ(full_bounds, devtools_web_view()->bounds()); | 
 |   EXPECT_EQ(small_bounds, contents_web_view()->bounds()); | 
 |  | 
 |   CloseDevToolsWindow(); | 
 |   EXPECT_FALSE(devtools_web_view()->web_contents()); | 
 |   EXPECT_EQ(full_bounds, devtools_web_view()->bounds()); | 
 |   EXPECT_EQ(full_bounds, contents_web_view()->bounds()); | 
 |  | 
 |   browser_view()->UpdateDevTools(active_web_contents()); | 
 |   EXPECT_FALSE(devtools_web_view()->web_contents()); | 
 |   EXPECT_EQ(full_bounds, devtools_web_view()->bounds()); | 
 |   EXPECT_EQ(full_bounds, contents_web_view()->bounds()); | 
 | } | 
 |  | 
 | void SetDevToolsWindowSizePrefs(Browser* browser, | 
 |                                 int left, | 
 |                                 int right, | 
 |                                 int top, | 
 |                                 int bottom) { | 
 |   PrefService* prefs = browser->GetProfile()->GetPrefs(); | 
 |   ScopedDictPrefUpdate update(prefs, prefs::kAppWindowPlacement); | 
 |   base::Value::Dict& wp_prefs = update.Get(); | 
 |   base::Value::Dict dev_tools_defaults; | 
 |   dev_tools_defaults.Set("left", left); | 
 |   dev_tools_defaults.Set("right", right); | 
 |   dev_tools_defaults.Set("top", top); | 
 |   dev_tools_defaults.Set("bottom", bottom); | 
 |   dev_tools_defaults.Set("maximized", false); | 
 |   dev_tools_defaults.Set("always_on_top", false); | 
 |   wp_prefs.Set(DevToolsWindow::kDevToolsApp, std::move(dev_tools_defaults)); | 
 | } | 
 |  | 
 | const base::Value::Dict& GetDevToolsWindowSizePrefs(Browser* browser) { | 
 |   PrefService* prefs = browser->GetProfile()->GetPrefs(); | 
 |   return prefs->GetDict(prefs::kAppWindowPlacement) | 
 |       .Find(DevToolsWindow::kDevToolsApp) | 
 |       ->GetDict(); | 
 | } | 
 |  | 
 | auto HasDimensions(int left, int right, int top, int bottom) { | 
 |   return base::test::DictionaryHasValues(base::Value::Dict() | 
 |                                              .Set("left", left) | 
 |                                              .Set("right", right) | 
 |                                              .Set("top", top) | 
 |                                              .Set("bottom", bottom)); | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(BrowserViewTest, DevToolsWindowDefaultSize) { | 
 | #if BUILDFLAG(IS_OZONE) | 
 |   // Ozone/wayland doesn't support getting/setting window position in global | 
 |   // screen coordinates. So this test is not applicable. | 
 |   if (ui::OzonePlatform::GetPlatformNameForTest() == "wayland") { | 
 |     GTEST_SKIP(); | 
 |   } | 
 | #endif | 
 |   // Starting DevTools the first time sets the window size to the default. | 
 |   OpenDevToolsWindow(false); | 
 |   CloseDevToolsWindow(); | 
 |   EXPECT_THAT(GetDevToolsWindowSizePrefs(browser()), | 
 |               HasDimensions(100, 740, 100, 740)); | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(BrowserViewTest, DevToolsWindowKeepsSize) { | 
 | #if BUILDFLAG(IS_OZONE) | 
 |   // Ozone/wayland doesn't support getting/setting window position in global | 
 |   // screen coordinates. So this test is not applicable. | 
 |   if (ui::OzonePlatform::GetPlatformNameForTest() == "wayland") { | 
 |     GTEST_SKIP(); | 
 |   } | 
 | #endif | 
 |   // Setting reasonable size prefs does not change the prefs. | 
 |   SetDevToolsWindowSizePrefs(browser(), 123, 567, 234, 678); | 
 |   EXPECT_THAT(GetDevToolsWindowSizePrefs(browser()), | 
 |               HasDimensions(123, 567, 234, 678)); | 
 |   OpenDevToolsWindow(false); | 
 |   CloseDevToolsWindow(); | 
 |   EXPECT_THAT(GetDevToolsWindowSizePrefs(browser()), | 
 |               HasDimensions(123, 567, 234, 678)); | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(BrowserViewTest, DevToolsWindowResetsSize) { | 
 | #if BUILDFLAG(IS_OZONE) | 
 |   // Ozone/wayland doesn't support getting/setting window position in global | 
 |   // screen coordinates. So this test is not applicable. | 
 |   if (ui::OzonePlatform::GetPlatformNameForTest() == "wayland") { | 
 |     GTEST_SKIP(); | 
 |   } | 
 | #endif | 
 |   // Setting unreasonably small size prefs resets the prefs. | 
 |   SetDevToolsWindowSizePrefs(browser(), 121, 232, 343, 454); | 
 |   EXPECT_THAT(GetDevToolsWindowSizePrefs(browser()), | 
 |               HasDimensions(121, 232, 343, 454)); | 
 |   OpenDevToolsWindow(false); | 
 |   CloseDevToolsWindow(); | 
 |   EXPECT_THAT(GetDevToolsWindowSizePrefs(browser()), | 
 |               HasDimensions(100, 740, 100, 740)); | 
 | } | 
 |  | 
 | // Verifies that the side panel's rounded corner is being correctly layed out. | 
 | IN_PROC_BROWSER_TEST_F(BrowserViewWithoutSideBySideTest, | 
 |                        SidePanelRoundedCornerLayout) { | 
 |   SidePanelCoordinator* coordinator = | 
 |       (browser())->GetFeatures().side_panel_coordinator(); | 
 |   coordinator->SetNoDelaysForTesting(true); | 
 |   coordinator->Show(SidePanelEntry::Id::kBookmarks); | 
 |   EXPECT_EQ(side_panel()->bounds().x(), | 
 |             side_panel_rounded_corner()->bounds().right()); | 
 |   EXPECT_EQ(side_panel()->bounds().y(), | 
 |             side_panel_rounded_corner()->bounds().y()); | 
 | } | 
 |  | 
 | class BookmarkBarViewObserverImpl : public BookmarkBarViewObserver { | 
 |  public: | 
 |   BookmarkBarViewObserverImpl() = default; | 
 |  | 
 |   BookmarkBarViewObserverImpl(const BookmarkBarViewObserverImpl&) = delete; | 
 |   BookmarkBarViewObserverImpl& operator=(const BookmarkBarViewObserverImpl&) = | 
 |       delete; | 
 |  | 
 |   int change_count() const { return change_count_; } | 
 |   void clear_change_count() { change_count_ = 0; } | 
 |  | 
 |   // BookmarkBarViewObserver: | 
 |   void OnBookmarkBarVisibilityChanged() override { change_count_++; } | 
 |  | 
 |  private: | 
 |   int change_count_ = 0; | 
 | }; | 
 |  | 
 | // Verifies we don't unnecessarily change the visibility of the BookmarkBarView. | 
 | IN_PROC_BROWSER_TEST_F(BrowserViewTest, AvoidUnnecessaryVisibilityChanges) { | 
 |   // Create two tabs, the first empty and the second the ntp. Make it so the | 
 |   // BookmarkBarView isn't shown. | 
 |   browser()->profile()->GetPrefs()->SetBoolean( | 
 |       bookmarks::prefs::kShowBookmarkBar, false); | 
 |   GURL new_tab_url(chrome::kChromeUINewTabURL); | 
 |   chrome::AddTabAt(browser(), GURL(), -1, true); | 
 |   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), new_tab_url)); | 
 |  | 
 |   ASSERT_TRUE(browser_view()->bookmark_bar()); | 
 |   BookmarkBarViewObserverImpl observer; | 
 |   BookmarkBarView* bookmark_bar = browser_view()->bookmark_bar(); | 
 |   bookmark_bar->AddObserver(&observer); | 
 |   EXPECT_FALSE(bookmark_bar->GetVisible()); | 
 |  | 
 |   // Go to empty tab. Bookmark bar should hide. | 
 |   browser()->tab_strip_model()->ActivateTabAt( | 
 |       0, TabStripUserGestureDetails( | 
 |              TabStripUserGestureDetails::GestureType::kOther)); | 
 |   EXPECT_FALSE(bookmark_bar->GetVisible()); | 
 |   EXPECT_EQ(0, observer.change_count()); | 
 |   observer.clear_change_count(); | 
 |  | 
 |   // Go to ntp tab. Bookmark bar should not show. | 
 |   browser()->tab_strip_model()->ActivateTabAt( | 
 |       1, TabStripUserGestureDetails( | 
 |              TabStripUserGestureDetails::GestureType::kOther)); | 
 |   EXPECT_FALSE(bookmark_bar->GetVisible()); | 
 |   EXPECT_EQ(0, observer.change_count()); | 
 |   observer.clear_change_count(); | 
 |  | 
 |   // Repeat with the bookmark bar always visible. | 
 |   browser()->profile()->GetPrefs()->SetBoolean( | 
 |       bookmarks::prefs::kShowBookmarkBar, true); | 
 |   browser()->tab_strip_model()->ActivateTabAt( | 
 |       0, TabStripUserGestureDetails( | 
 |              TabStripUserGestureDetails::GestureType::kOther)); | 
 |   EXPECT_TRUE(bookmark_bar->GetVisible()); | 
 |   EXPECT_EQ(1, observer.change_count()); | 
 |   observer.clear_change_count(); | 
 |  | 
 |   browser()->tab_strip_model()->ActivateTabAt( | 
 |       1, TabStripUserGestureDetails( | 
 |              TabStripUserGestureDetails::GestureType::kOther)); | 
 |   EXPECT_TRUE(bookmark_bar->GetVisible()); | 
 |   EXPECT_EQ(0, observer.change_count()); | 
 |   observer.clear_change_count(); | 
 |  | 
 |   browser_view()->bookmark_bar()->RemoveObserver(&observer); | 
 | } | 
 |  | 
 | // Launch the app, navigate to a page with a title, check that the tab title | 
 | // is set before load finishes and the throbber state updates when the title | 
 | // changes. Regression test for crbug.com/752266 | 
 | IN_PROC_BROWSER_TEST_F(BrowserViewTest, TitleAndLoadState) { | 
 |   const std::u16string test_title(u"Title Of Awesomeness"); | 
 |   auto* contents = browser()->tab_strip_model()->GetActiveWebContents(); | 
 |   content::TitleWatcher title_watcher(contents, test_title); | 
 |   content::TestNavigationObserver navigation_watcher( | 
 |       contents, 1, content::MessageLoopRunner::QuitMode::DEFERRED); | 
 |  | 
 |   TabStrip* tab_strip = browser_view()->tabstrip(); | 
 |   // Navigate without blocking. | 
 |   const GURL test_url = ui_test_utils::GetTestUrl( | 
 |       base::FilePath(base::FilePath::kCurrentDirectory), | 
 |       base::FilePath(FILE_PATH_LITERAL("title2.html"))); | 
 |   contents->GetController().LoadURL(test_url, content::Referrer(), | 
 |                                     ui::PAGE_TRANSITION_LINK, std::string()); | 
 |   EXPECT_TRUE(browser()->tab_strip_model()->TabsNeedLoadingUI()); | 
 |   EXPECT_EQ(TabNetworkState::kWaiting, | 
 |             tab_strip->tab_at(0)->data().network_state); | 
 |   EXPECT_EQ(test_title, title_watcher.WaitAndGetTitle()); | 
 |   EXPECT_TRUE(browser()->tab_strip_model()->TabsNeedLoadingUI()); | 
 |   EXPECT_EQ(TabNetworkState::kLoading, | 
 |             tab_strip->tab_at(0)->data().network_state); | 
 |  | 
 |   // Now block for the navigation to complete. | 
 |   navigation_watcher.Wait(); | 
 |   EXPECT_FALSE(browser()->tab_strip_model()->TabsNeedLoadingUI()); | 
 |   EXPECT_EQ(TabNetworkState::kNone, tab_strip->tab_at(0)->data().network_state); | 
 | } | 
 |  | 
 | // Verifies a tab should show its favicon. | 
 | IN_PROC_BROWSER_TEST_F(BrowserViewTest, ShowFaviconInTab) { | 
 |   // Opens "chrome://version/" page, which uses default favicon. | 
 |   const GURL version_url(chrome::kChromeUIVersionURL); | 
 |   ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), version_url)); | 
 |   auto* const tab_features = | 
 |       browser()->tab_strip_model()->GetActiveTab()->GetTabFeatures(); | 
 |   auto* const helper = tab_features->tab_ui_helper(); | 
 |   ASSERT_TRUE(helper); | 
 |  | 
 |   const auto favicon = helper->GetFavicon(); | 
 |   ASSERT_FALSE(favicon.IsEmpty()); | 
 | } | 
 |  | 
 | // On Mac, voiceover treats tab modal dialogs as native windows, so setting an | 
 | // accessible title for tab-modal dialogs is not necessary. | 
 | #if !BUILDFLAG(IS_MAC) | 
 |  | 
 | // Open a tab-modal dialog and check that the accessibility tree only contains | 
 | // the dialog. | 
 | IN_PROC_BROWSER_TEST_F(BrowserViewTest, GetAccessibleTabModalDialogTree) { | 
 |   content::ScopedAccessibilityModeOverride ax_mode_override( | 
 |       ui::kAXModeComplete); | 
 |   ui::AXPlatformNode* ax_node = ui::AXPlatformNode::FromNativeViewAccessible( | 
 |       browser_view()->GetWidget()->GetRootView()->GetNativeViewAccessible()); | 
 | // We expect this conversion to be safe on Windows, but can't guarantee that it | 
 | // is safe on other platforms. | 
 | #if BUILDFLAG(IS_WIN) | 
 |   ASSERT_TRUE(ax_node); | 
 | #else | 
 |   if (!ax_node) { | 
 |     return; | 
 |   } | 
 | #endif | 
 |  | 
 |   // There is no dialog, but the browser UI should be visible. So we expect the | 
 |   // browser's reload button and no "OK" button from a dialog. | 
 |   EXPECT_NE(ui::AXPlatformNodeTestHelper::FindChildByName(ax_node, "Reload"), | 
 |             nullptr); | 
 |   EXPECT_EQ(ui::AXPlatformNodeTestHelper::FindChildByName(ax_node, "OK"), | 
 |             nullptr); | 
 |  | 
 |   content::WebContents* contents = browser_view()->GetActiveWebContents(); | 
 |   auto delegate = std::make_unique<TestTabModalConfirmDialogDelegate>(contents); | 
 |  | 
 |   // `ViewAXPlatformNodeDelegate::GetChildWidgets` expects the following | 
 |   // conditions to be met in order to conclude that a tab modal dialog is | 
 |   // showing: | 
 |   // 1. The dialog is included in `Widget::GetAllOwnedWidgets()`. | 
 |   // 2. The currently-focused view is contained in the dialog. | 
 |   // Waiting for the dialog to be shown should ensure that the first | 
 |   // condition is met. But we also need to wait for the focus to change | 
 |   // or the second condition flakily fails. | 
 |   TestFocusChangeWaiter focus_waiter(browser_view()->GetFocusManager(), | 
 |                                      "MessageBoxView"); | 
 |   TabModalConfirmDialog::Create(std::move(delegate), contents); | 
 |   focus_waiter.Wait(); | 
 |  | 
 |   // The tab modal dialog should be in the accessibility tree; everything else | 
 |   // should be hidden. So we expect an "OK" button and no reload button. | 
 |   EXPECT_EQ(ui::AXPlatformNodeTestHelper::FindChildByName(ax_node, "Reload"), | 
 |             nullptr); | 
 |   EXPECT_NE(ui::AXPlatformNodeTestHelper::FindChildByName(ax_node, "OK"), | 
 |             nullptr); | 
 | } | 
 | #endif  // !BUILDFLAG(IS_MAC) | 
 |  | 
 | // Tests that a content area scrim is shown when a tab modal dialog is active. | 
 | IN_PROC_BROWSER_TEST_F(BrowserViewTest, ScrimForTabModal) { | 
 |   content::WebContents* contents = browser_view()->GetActiveWebContents(); | 
 |   auto delegate = std::make_unique<TestTabModalConfirmDialogDelegate>(contents); | 
 |  | 
 |   // Showing a tab modal dialog will enable the content scrim. | 
 |   TabModalConfirmDialog::Create(std::move(delegate), contents); | 
 |   EXPECT_TRUE(active_contents_scrim_view()->GetVisible()); | 
 |  | 
 |   // Goes to a second tab will disable the content scrim. | 
 |   ASSERT_TRUE( | 
 |       AddTabAtIndex(1, GURL(url::kAboutBlankURL), ui::PAGE_TRANSITION_LINK)); | 
 |   EXPECT_FALSE(active_contents_scrim_view()->GetVisible()); | 
 |  | 
 |   // Switch back to the page that has a modal dialog. | 
 |   browser()->tab_strip_model()->ActivateTabAt( | 
 |       0, TabStripUserGestureDetails( | 
 |              TabStripUserGestureDetails::GestureType::kMouse)); | 
 |   EXPECT_TRUE(active_contents_scrim_view()->GetVisible()); | 
 |  | 
 |   // Closing the tab disables the content scrim. | 
 |   chrome::CloseWebContents(browser(), | 
 |                            browser()->tab_strip_model()->GetActiveWebContents(), | 
 |                            /*add_to_history=*/false); | 
 | } | 
 |  | 
 | // MacOS does not need views window scrim. We use sheet to show window modals | 
 | // (-[NSWindow beginSheet:]), which natively draws a scrim. | 
 | #if !BUILDFLAG(IS_MAC) | 
 | IN_PROC_BROWSER_TEST_F(BrowserViewTest, ScrimForBrowserWindowModal) { | 
 |   auto child_widget_delegate = std::make_unique<views::WidgetDelegate>(); | 
 |   auto child_widget = std::make_unique<views::Widget>(); | 
 |   child_widget_delegate->SetModalType(ui::mojom::ModalType::kWindow); | 
 |   views::Widget::InitParams params( | 
 |       views::Widget::InitParams::CLIENT_OWNS_WIDGET, | 
 |       views::Widget::InitParams::TYPE_WINDOW); | 
 |   params.delegate = child_widget_delegate.get(); | 
 |   params.parent = browser_view()->GetWidget()->GetNativeView(); | 
 |   child_widget->Init(std::move(params)); | 
 |  | 
 |   child_widget->Show(); | 
 |   EXPECT_TRUE(browser_view()->window_scrim_view()->GetVisible()); | 
 |   child_widget->Hide(); | 
 |   EXPECT_FALSE(browser_view()->window_scrim_view()->GetVisible()); | 
 |   child_widget->Show(); | 
 |   EXPECT_TRUE(browser_view()->window_scrim_view()->GetVisible()); | 
 |   // Destroy the child widget, the parent should be notified about child modal | 
 |   // visibility change. | 
 |   child_widget.reset(); | 
 |   EXPECT_FALSE(browser_view()->window_scrim_view()->GetVisible()); | 
 | } | 
 | #endif  // !BUILDFLAG(IS_MAC) | 
 |  | 
 | // Tests that GetInactiveSplitTabIndex returns correctly with two adjacent | 
 | // splits. | 
 | IN_PROC_BROWSER_TEST_F(BrowserViewTest, SplitViewActiveIndexTest) { | 
 |   // Add enough tabs to create two split views. | 
 |   chrome::AddTabAt(browser(), GURL(), -1, true); | 
 |   chrome::AddTabAt(browser(), GURL(), -1, true); | 
 |   chrome::AddTabAt(browser(), GURL(), -1, true); | 
 |   // Add tabs to splits. | 
 |   browser()->tab_strip_model()->ActivateTabAt(0); | 
 |   browser()->tab_strip_model()->AddToNewSplit( | 
 |       {1}, split_tabs::SplitTabVisualData(), | 
 |       split_tabs::SplitTabCreatedSource::kToolbarButton); | 
 |  | 
 |   browser()->tab_strip_model()->ActivateTabAt(2); | 
 |   browser()->tab_strip_model()->AddToNewSplit( | 
 |       {3}, split_tabs::SplitTabVisualData(), | 
 |       split_tabs::SplitTabCreatedSource::kToolbarButton); | 
 |  | 
 |   browser()->tab_strip_model()->ActivateTabAt(0); | 
 |   EXPECT_TRUE(browser_view()->multi_contents_view()); | 
 |   EXPECT_EQ( | 
 |       browser_view()->multi_contents_view()->GetActiveContentsView(), | 
 |       browser_view()->multi_contents_view()->start_contents_view_for_testing()); | 
 |  | 
 |   browser()->tab_strip_model()->ActivateTabAt(2); | 
 |   EXPECT_EQ( | 
 |       browser_view()->multi_contents_view()->GetActiveContentsView(), | 
 |       browser_view()->multi_contents_view()->start_contents_view_for_testing()); | 
 |  | 
 |   browser()->tab_strip_model()->ActivateTabAt(3); | 
 |   EXPECT_EQ( | 
 |       browser_view()->multi_contents_view()->GetActiveContentsView(), | 
 |       browser_view()->multi_contents_view()->end_contents_view_for_testing()); | 
 | } | 
 |  | 
 | // Verifies that page and devtools WebViews are being correctly laid out | 
 | // when DevTools is opened/closed/updated while docked. | 
 | IN_PROC_BROWSER_TEST_F(BrowserViewTest, | 
 |                        DevToolsDockedRemainsOpenInWithFocusInSplit) { | 
 |   // Add enough tabs to create two split views. | 
 |   chrome::AddTabAt(browser(), GURL(), -1, true); | 
 |   chrome::AddTabAt(browser(), GURL(), -1, true); | 
 |   chrome::AddTabAt(browser(), GURL(), -1, true); | 
 |   // Add tabs to splits. | 
 |   browser()->tab_strip_model()->ActivateTabAt(0); | 
 |   browser()->tab_strip_model()->AddToNewSplit( | 
 |       {1}, split_tabs::SplitTabVisualData(), | 
 |       split_tabs::SplitTabCreatedSource::kToolbarButton); | 
 |   browser()->tab_strip_model()->ActivateTabAt(2); | 
 |   browser()->tab_strip_model()->AddToNewSplit( | 
 |       {3}, split_tabs::SplitTabVisualData(), | 
 |       split_tabs::SplitTabCreatedSource::kToolbarButton); | 
 |  | 
 |   browser()->tab_strip_model()->ActivateTabAt(0); | 
 |  | 
 |   // Verify neither devtools is visible. | 
 |   EXPECT_FALSE( | 
 |       active_contents_container_view()->devtools_web_view()->GetVisible()); | 
 |   EXPECT_FALSE( | 
 |       inactive_contents_container_view()->devtools_web_view()->GetVisible()); | 
 |  | 
 |   // Open devtools for the active side of the split and verify it exists only | 
 |   // for the active side. | 
 |   DevToolsWindowTesting::OpenDevToolsWindowSync(browser(), true); | 
 |   EXPECT_TRUE( | 
 |       active_contents_container_view()->devtools_web_view()->GetVisible()); | 
 |   EXPECT_FALSE( | 
 |       inactive_contents_container_view()->devtools_web_view()->GetVisible()); | 
 |  | 
 |   // Activate to the inactive side and verify it stayed open on the appropriate | 
 |   // side of the split. | 
 |   browser()->tab_strip_model()->ActivateTabAt(1); | 
 |   EXPECT_FALSE( | 
 |       active_contents_container_view()->devtools_web_view()->GetVisible()); | 
 |   EXPECT_TRUE( | 
 |       inactive_contents_container_view()->devtools_web_view()->GetVisible()); | 
 |  | 
 |   // Activate to the other split and verify no devtools are seen. | 
 |   browser()->tab_strip_model()->ActivateTabAt(2); | 
 |   EXPECT_FALSE( | 
 |       active_contents_container_view()->devtools_web_view()->GetVisible()); | 
 |   EXPECT_FALSE( | 
 |       inactive_contents_container_view()->devtools_web_view()->GetVisible()); | 
 |  | 
 |   // Switch back to the split where devtools is open and verify is is still | 
 |   // visible. | 
 |   browser()->tab_strip_model()->ActivateTabAt(1); | 
 |   EXPECT_FALSE( | 
 |       active_contents_container_view()->devtools_web_view()->GetVisible()); | 
 |   EXPECT_TRUE( | 
 |       inactive_contents_container_view()->devtools_web_view()->GetVisible()); | 
 |  | 
 |   // Verify two devtools can be seen at once (one for each side of a split). | 
 |   DevToolsWindowTesting::OpenDevToolsWindowSync(browser(), true); | 
 |   EXPECT_TRUE( | 
 |       active_contents_container_view()->devtools_web_view()->GetVisible()); | 
 |   EXPECT_TRUE( | 
 |       inactive_contents_container_view()->devtools_web_view()->GetVisible()); | 
 | } | 
 |  | 
 | // Verifies that page and devtools WebViews are being correctly laid out | 
 | // when DevTools is opened/closed/updated while docked. | 
 | IN_PROC_BROWSER_TEST_F(BrowserViewTest, | 
 |                        DevToolsRemainsCorrectlyDockedAfterSwappingSplit) { | 
 |   // Add enough tabs to create two split views. | 
 |   chrome::AddTabAt(browser(), GURL(), -1, true); | 
 |   // Add tabs to splits. | 
 |   browser()->tab_strip_model()->ActivateTabAt(0); | 
 |   browser()->tab_strip_model()->AddToNewSplit( | 
 |       {1}, split_tabs::SplitTabVisualData(), | 
 |       split_tabs::SplitTabCreatedSource::kToolbarButton); | 
 |  | 
 |   browser()->tab_strip_model()->ActivateTabAt(0); | 
 |  | 
 |   // Open devtools for the active side of the split and verify it exists only | 
 |   // for the active side. | 
 |   DevToolsWindowTesting::OpenDevToolsWindowSync(browser(), true); | 
 |   EXPECT_TRUE( | 
 |       active_contents_container_view()->devtools_web_view()->GetVisible()); | 
 |   EXPECT_FALSE( | 
 |       inactive_contents_container_view()->devtools_web_view()->GetVisible()); | 
 |  | 
 |   // Reverse the split and verify the correct side has devtools. | 
 |   browser_view()->multi_contents_view()->OnSwap(); | 
 |   EXPECT_TRUE( | 
 |       active_contents_container_view()->devtools_web_view()->GetVisible()); | 
 |   EXPECT_FALSE( | 
 |       inactive_contents_container_view()->devtools_web_view()->GetVisible()); | 
 | } | 
 |  | 
 | // TODO(crbug.com/425715421): Re-enable when wayland supports drag and drop | 
 | #if !BUILDFLAG(IS_OZONE_WAYLAND) | 
 | #define MAYBE_DragNotSupportedInFullscreen DragNotSupportedInFullscreen | 
 | #else | 
 | #define MAYBE_DragNotSupportedInFullscreen DISABLED_DragNotSupportedInFullscreen | 
 | #endif | 
 | IN_PROC_BROWSER_TEST_F(BrowserViewTest, MAYBE_DragNotSupportedInFullscreen) { | 
 |   // Add enough tabs to create two split views. | 
 |   chrome::AddTabAt(browser(), GURL(), -1, true); | 
 |   // Add tabs to splits. | 
 |   browser()->tab_strip_model()->ActivateTabAt(0); | 
 |   browser()->tab_strip_model()->AddToNewSplit( | 
 |       {1}, split_tabs::SplitTabVisualData(), | 
 |       split_tabs::SplitTabCreatedSource::kToolbarButton); | 
 |  | 
 |   // Make fullscreen | 
 |   ui_test_utils::ToggleFullscreenModeAndWait(browser()); | 
 |  | 
 |   // Attempt to start a drag | 
 |   content::DropData drop_data; | 
 |   drop_data.url = GURL("https://mail.google.com"); | 
 |   const gfx::Rect bounds = browser_view()->GetBoundsInScreen(); | 
 |   const gfx::PointF point(bounds.left_center().x() + 10, | 
 |                           bounds.left_center().y()); | 
 |   browser_view()->PreHandleDragUpdate(drop_data, point); | 
 |  | 
 |   EXPECT_FALSE(browser_view() | 
 |                    ->multi_contents_view() | 
 |                    ->drop_target_controller() | 
 |                    .IsDropTimerRunningForTesting()); | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(BrowserViewTest, ScrimForTabModalInSplitView) { | 
 |   // Create a split view with two tabs followed by a third that will show the | 
 |   // scrim. | 
 |   chrome::AddTabAt(browser(), GURL(), -1, true); | 
 |   chrome::AddTabAt(browser(), GURL(), -1, true); | 
 |   browser()->tab_strip_model()->ActivateTabAt(0); | 
 |   browser()->tab_strip_model()->AddToNewSplit( | 
 |       {1}, split_tabs::SplitTabVisualData(), | 
 |       split_tabs::SplitTabCreatedSource::kToolbarButton); | 
 |  | 
 |   // Show a tab modal dialog on the third tab (not part of the split). | 
 |   browser()->tab_strip_model()->ActivateTabAt(2); | 
 |   content::WebContents* contents = browser_view()->GetActiveWebContents(); | 
 |   auto delegate = std::make_unique<TestTabModalConfirmDialogDelegate>(contents); | 
 |   TabModalConfirmDialog::Create(std::move(delegate), contents); | 
 |   EXPECT_TRUE( | 
 |       active_contents_container_view()->contents_scrim_view()->GetVisible()); | 
 |  | 
 |   // Activating a tab in the split will cause the scrim to hide. | 
 |   browser()->tab_strip_model()->ActivateTabAt(0); | 
 |   EXPECT_FALSE( | 
 |       active_contents_container_view()->contents_scrim_view()->GetVisible()); | 
 |  | 
 |   // Swapping the tab with the tab modal dialog into the inactive spot in the | 
 |   // split should make that view active and show the scrim. | 
 |   browser()->tab_strip_model()->UpdateTabInSplit( | 
 |       browser()->tab_strip_model()->GetTabAtIndex(1), 2, | 
 |       TabStripModel::SplitUpdateType::kSwap); | 
 |   EXPECT_EQ(1, browser()->tab_strip_model()->active_index()); | 
 |   EXPECT_TRUE( | 
 |       active_contents_container_view()->contents_scrim_view()->GetVisible()); | 
 |   EXPECT_FALSE( | 
 |       inactive_contents_container_view()->contents_scrim_view()->GetVisible()); | 
 | } | 
 |  | 
 | // Tests that GetAccessibleTabLabel correctly labels each tab in a split. | 
 | IN_PROC_BROWSER_TEST_F(BrowserViewTest, AccessibleTabLabel) { | 
 |   // Create a pinned split. | 
 |   chrome::AddTabAt(browser(), GURL(), -1, true); | 
 |   browser()->tab_strip_model()->SetTabPinned(0, true); | 
 |   browser()->tab_strip_model()->SetTabPinned(1, true); | 
 |   browser()->tab_strip_model()->ActivateTabAt(0); | 
 |   browser()->tab_strip_model()->AddToNewSplit( | 
 |       {1}, split_tabs::SplitTabVisualData(), | 
 |       split_tabs::SplitTabCreatedSource::kToolbarButton); | 
 |   EXPECT_EQ(l10n_util::GetStringFUTF16( | 
 |                 IDS_TAB_AX_LABEL_PINNED_FORMAT, | 
 |                 l10n_util::GetStringFUTF16( | 
 |                     IDS_TAB_AX_LABEL_SPLIT_TAB_LEFT_VIEW_FORMAT, | 
 |                     browser()->GetTitleForTab(0))), | 
 |             browser_view()->GetAccessibleTabLabel(0)); | 
 |   EXPECT_EQ(l10n_util::GetStringFUTF16( | 
 |                 IDS_TAB_AX_LABEL_PINNED_FORMAT, | 
 |                 l10n_util::GetStringFUTF16( | 
 |                     IDS_TAB_AX_LABEL_SPLIT_TAB_RIGHT_VIEW_FORMAT, | 
 |                     browser()->GetTitleForTab(1))), | 
 |             browser_view()->GetAccessibleTabLabel(1)); | 
 |  | 
 |   // Create a split. | 
 |   chrome::AddTabAt(browser(), GURL(), -1, true); | 
 |   chrome::AddTabAt(browser(), GURL(), -1, true); | 
 |   browser()->tab_strip_model()->ActivateTabAt(2); | 
 |   browser()->tab_strip_model()->AddToNewSplit( | 
 |       {3}, split_tabs::SplitTabVisualData(), | 
 |       split_tabs::SplitTabCreatedSource::kToolbarButton); | 
 |   EXPECT_EQ( | 
 |       l10n_util::GetStringFUTF16(IDS_TAB_AX_LABEL_SPLIT_TAB_LEFT_VIEW_FORMAT, | 
 |                                  browser()->GetTitleForTab(2)), | 
 |       browser_view()->GetAccessibleTabLabel(2)); | 
 |   EXPECT_EQ( | 
 |       l10n_util::GetStringFUTF16(IDS_TAB_AX_LABEL_SPLIT_TAB_RIGHT_VIEW_FORMAT, | 
 |                                  browser()->GetTitleForTab(3)), | 
 |       browser_view()->GetAccessibleTabLabel(3)); | 
 |  | 
 |   // Create a grouped split. | 
 |   chrome::AddTabAt(browser(), GURL(), -1, true); | 
 |   chrome::AddTabAt(browser(), GURL(), -1, true); | 
 |   browser()->tab_strip_model()->ActivateTabAt(4); | 
 |   browser()->tab_strip_model()->AddToNewSplit( | 
 |       {5}, split_tabs::SplitTabVisualData(), | 
 |       split_tabs::SplitTabCreatedSource::kToolbarButton); | 
 |   browser()->tab_strip_model()->AddToNewGroup({4, 5}); | 
 |   EXPECT_EQ(l10n_util::GetStringFUTF16( | 
 |                 IDS_TAB_AX_LABEL_UNNAMED_GROUP_FORMAT, | 
 |                 l10n_util::GetStringFUTF16( | 
 |                     IDS_TAB_AX_LABEL_SPLIT_TAB_LEFT_VIEW_FORMAT, | 
 |                     browser()->GetTitleForTab(4))), | 
 |             browser_view()->GetAccessibleTabLabel(4)); | 
 |   EXPECT_EQ(l10n_util::GetStringFUTF16( | 
 |                 IDS_TAB_AX_LABEL_UNNAMED_GROUP_FORMAT, | 
 |                 l10n_util::GetStringFUTF16( | 
 |                     IDS_TAB_AX_LABEL_SPLIT_TAB_RIGHT_VIEW_FORMAT, | 
 |                     browser()->GetTitleForTab(5))), | 
 |             browser_view()->GetAccessibleTabLabel(5)); | 
 | } | 
 |  | 
 | #if BUILDFLAG(IS_MAC) | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(BrowserViewTest, SplitViewFullscreenLayout) { | 
 |   // Disable always show toolbar in fullscreen | 
 |   chrome::SetAlwaysShowToolbarInFullscreenForTesting(browser(), false); | 
 |  | 
 |   // Create tabs and add to split | 
 |   chrome::AddTabAt(browser(), GURL(), -1, true); | 
 |   chrome::AddTabAt(browser(), GURL(), -1, true); | 
 |   browser()->tab_strip_model()->ActivateTabAt(0); | 
 |   browser()->tab_strip_model()->AddToNewSplit( | 
 |       {1}, split_tabs::SplitTabVisualData(), | 
 |       split_tabs::SplitTabCreatedSource::kToolbarButton); | 
 |  | 
 |   ASSERT_TRUE(browser()->tab_strip_model()->selection_model().IsSelected(0)); | 
 |   ASSERT_TRUE(browser()->tab_strip_model()->selection_model().IsSelected(1)); | 
 |  | 
 |   TopContainerView* top_container = browser_view()->top_container(); | 
 |   views::View* overlay_view = browser_view()->overlay_view(); | 
 |  | 
 |   // Verify top_container is parented to browser_view before fullscreen | 
 |   EXPECT_EQ(browser_view()->main_container(), top_container->parent()); | 
 |   ui_test_utils::ToggleFullscreenModeAndWait(browser()); | 
 |  | 
 |   // Verify top_container is parented to overlay after entering fullscreen | 
 |   EXPECT_EQ(overlay_view, top_container->parent()); | 
 |  | 
 |   browser_view()->ExitFullscreen(); | 
 |  | 
 |   // Verify top_container is re-parented to browser_view after fullscreen exit | 
 |   EXPECT_EQ(browser_view()->main_container(), top_container->parent()); | 
 | } | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(BrowserViewTest, SplitViewTabRevealFullscreen) { | 
 |   // Disable always show toolbar in fullscreen | 
 |   chrome::SetAlwaysShowToolbarInFullscreenForTesting(browser(), false); | 
 |  | 
 |   // Create tabs and add to split | 
 |   chrome::AddTabAt(browser(), GURL(), -1, true); | 
 |   chrome::AddTabAt(browser(), GURL(), -1, true); | 
 |   chrome::AddTabAt(browser(), GURL(), -1, true); | 
 |   browser()->tab_strip_model()->ActivateTabAt(0); | 
 |   browser()->tab_strip_model()->AddToNewSplit( | 
 |       {1}, split_tabs::SplitTabVisualData(), | 
 |       split_tabs::SplitTabCreatedSource::kToolbarButton); | 
 |  | 
 |   ASSERT_TRUE(browser()->tab_strip_model()->selection_model().IsSelected(0)); | 
 |   ASSERT_TRUE(browser()->tab_strip_model()->selection_model().IsSelected(1)); | 
 |  | 
 |   ui_test_utils::ToggleFullscreenModeAndWait(browser()); | 
 |   ASSERT_FALSE(browser()->window()->IsToolbarShowing()); | 
 |  | 
 |   // Switching between split tabs does not reveal top container. | 
 |   browser()->tab_strip_model()->ActivateTabAt(1); | 
 |   ASSERT_FALSE(browser()->window()->IsToolbarShowing()); | 
 |  | 
 |   // Switching to tab not in split should reveal top container. | 
 |   browser()->tab_strip_model()->ActivateTabAt(2); | 
 |   ASSERT_TRUE(browser()->window()->IsToolbarShowing()); | 
 | } | 
 | #endif | 
 |  | 
 | namespace { | 
 |  | 
 | class FakeRealTimeUrlLookupService | 
 |     : public safe_browsing::testing::FakeRealTimeUrlLookupService { | 
 |  public: | 
 |   FakeRealTimeUrlLookupService() = default; | 
 |  | 
 |   // RealTimeUrlLookupServiceBase: | 
 |   void StartMaybeCachedLookup( | 
 |       const GURL& url, | 
 |       safe_browsing::RTLookupResponseCallback response_callback, | 
 |       scoped_refptr<base::SequencedTaskRunner> callback_task_runner, | 
 |       SessionID session_id, | 
 |       std::optional<safe_browsing::internal::ReferringAppInfo> | 
 |           referring_app_info, | 
 |       bool use_cache) override { | 
 |     auto response = std::make_unique<safe_browsing::RTLookupResponse>(); | 
 |  | 
 |     callback_task_runner->PostTask( | 
 |         FROM_HERE, | 
 |         base::BindOnce(std::move(response_callback), | 
 |                        /*is_rt_lookup_successful=*/true, | 
 |                        /*is_cached_response=*/true, std::move(response))); | 
 |   } | 
 | }; | 
 |  | 
 | class BrowserViewDataProtectionTest : public InProcessBrowserTest { | 
 |  public: | 
 |   BrowserViewDataProtectionTest() { | 
 |     scoped_feature_list_.InitAndEnableFeature(features::kSideBySide); | 
 |   } | 
 |   BrowserViewDataProtectionTest(const BrowserViewDataProtectionTest&) = delete; | 
 |   BrowserViewDataProtectionTest& operator=( | 
 |       const BrowserViewDataProtectionTest&) = delete; | 
 |  | 
 |   void SetUpCommandLine(base::CommandLine* command_line) override { | 
 |     // Set a DM token since the enterprise real-time URL service expects one. | 
 |     policy::SetDMTokenForTesting(policy::DMToken::CreateValidToken("dm_token")); | 
 |  | 
 |     auto create_service_callback = | 
 |         base::BindRepeating([](content::BrowserContext* context) { | 
 |           Profile* profile = Profile::FromBrowserContext(context); | 
 |  | 
 |           // Enable real-time URL checks. | 
 |           profile->GetPrefs()->SetInteger( | 
 |               enterprise_connectors::kEnterpriseRealTimeUrlCheckMode, | 
 |               enterprise_connectors::REAL_TIME_CHECK_FOR_MAINFRAME_ENABLED); | 
 |           profile->GetPrefs()->SetInteger( | 
 |               enterprise_connectors::kEnterpriseRealTimeUrlCheckScope, | 
 |               policy::POLICY_SCOPE_MACHINE); | 
 |  | 
 |           auto testing_factory = | 
 |               base::BindRepeating([](content::BrowserContext* context) | 
 |                                       -> std::unique_ptr<KeyedService> { | 
 |                 return std::make_unique<FakeRealTimeUrlLookupService>(); | 
 |               }); | 
 |           safe_browsing::ChromeEnterpriseRealTimeUrlLookupServiceFactory:: | 
 |               GetInstance() | 
 |                   ->SetTestingFactory(context, testing_factory); | 
 |         }); | 
 |  | 
 |     create_services_subscription_ = | 
 |         BrowserContextDependencyManager::GetInstance() | 
 |             ->RegisterCreateServicesCallbackForTesting(create_service_callback); | 
 |   } | 
 |  | 
 |   content::WebContents* NavigateAsync(const GURL& url) { | 
 |     NavigateParams params(browser(), url, ui::PAGE_TRANSITION_LINK); | 
 |     Navigate(¶ms); | 
 |     return params.navigated_or_inserted_contents; | 
 |   } | 
 |  | 
 |   void NavigateToAndWait(const GURL& url) { | 
 |     content::WaitForLoadStop(NavigateAsync(url)); | 
 |   } | 
 |  | 
 |  private: | 
 |   base::CallbackListSubscription create_services_subscription_; | 
 |   base::test::ScopedFeatureList scoped_feature_list_; | 
 | }; | 
 |  | 
 | }  // namespace | 
 |  | 
 | #if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(BrowserViewDataProtectionTest, DC_Screenshot) { | 
 |   data_controls::SetDataControls(browser()->profile()->GetPrefs(), {R"( | 
 |         { | 
 |           "name":"block", | 
 |           "rule_id":"1234", | 
 |           "sources":{"urls":["noscreenshot.com"]}, | 
 |           "restrictions":[{"class": "SCREENSHOT", "level": "BLOCK"} ] | 
 |         } | 
 |       )"}); | 
 |  | 
 |   auto* widget = BrowserView::GetBrowserViewForBrowser(browser())->GetWidget(); | 
 |   ASSERT_TRUE(widget); | 
 |  | 
 |   NavigateToAndWait(GURL("https://noscreenshot.com")); | 
 |   EXPECT_FALSE(widget->AreScreenshotsAllowed()); | 
 |  | 
 |   NavigateToAndWait(GURL("https://screenshot.com")); | 
 |   EXPECT_TRUE(widget->AreScreenshotsAllowed()); | 
 | } | 
 |  | 
 | #endif  // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_MAC) | 
 |  | 
 | namespace { | 
 |  | 
 | // chrome/test/data/simple.html | 
 | const char kSimplePage[] = "/simple.html"; | 
 |  | 
 | class BrowserViewScrimPixelTest : public UiBrowserTest { | 
 |  public: | 
 |   // UiBrowserTest: | 
 |   void ShowUi(const std::string& name) override { | 
 |     ASSERT_TRUE(embedded_test_server()->Start()); | 
 |     GURL url = embedded_test_server()->GetURL(kSimplePage); | 
 |     ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), url)); | 
 |     browser()->window()->Show(); | 
 |     BrowserView::GetBrowserViewForBrowser(browser()) | 
 |         ->GetActiveContentsContainerView() | 
 |         ->contents_scrim_view() | 
 |         ->SetVisible(true); | 
 |   } | 
 |  | 
 |   bool VerifyUi() override { | 
 |     const auto* const test_info = | 
 |         testing::UnitTest::GetInstance()->current_test_info(); | 
 |     return VerifyPixelUi(BrowserView::GetBrowserViewForBrowser(browser()) | 
 |                              ->contents_container(), | 
 |                          test_info->test_suite_name(), | 
 |                          test_info->name()) != ui::test::ActionResult::kFailed; | 
 |   } | 
 |  | 
 |   void WaitForUserDismissal() override { | 
 |     ui_test_utils::WaitForBrowserToClose(); | 
 |   } | 
 | }; | 
 |  | 
 | }  // namespace | 
 |  | 
 | IN_PROC_BROWSER_TEST_F(BrowserViewScrimPixelTest, InvokeUi_content_scrim) { | 
 |   ShowAndVerifyUi(); | 
 | } |