// Copyright 2012 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/window_sizer/window_sizer_chromeos.h"

#include <memory>
#include <utility>

#include "ash/root_window_controller.h"
#include "ash/shelf/shelf.h"
#include "ash/shell.h"
#include "base/command_line.h"
#include "chrome/browser/ui/browser_init_state.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/browser_window/public/create_browser_window.h"
#include "chrome/browser/ui/browser_window/test/mock_browser_window_interface.h"
#include "chrome/browser/ui/window_sizer/window_sizer_common_unittest.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/test/base/chrome_ash_test_base.h"
#include "chrome/test/base/testing_profile.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/env.h"
#include "ui/aura/test/test_windows.h"
#include "ui/aura/window.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/display/display.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/screen.h"
#include "ui/display/test/display_manager_test_api.h"
#include "ui/wm/public/activation_client.h"

namespace {

class FakeBrowserWindow : public MockBrowserWindowInterface {
 public:
  explicit FakeBrowserWindow(BrowserWindowInterface::Type type =
                                 BrowserWindowInterface::Type::TYPE_NORMAL)
      : type_(type),
        init_state_(BrowserWindowCreateParams(type, &profile_, true),
                    GetUnownedUserDataHost()) {
    ON_CALL(*this, GetProfile()).WillByDefault(testing::Return(&profile_));
  }
  FakeBrowserWindow(const FakeBrowserWindow&) = delete;
  FakeBrowserWindow& operator=(const FakeBrowserWindow&) = delete;
  ~FakeBrowserWindow() override = default;

  BrowserWindowInterface::Type GetType() const override { return type_; }
  ui::BaseWindow* GetWindow() override { return nullptr; }
  const ui::BaseWindow* GetWindow() const override { return nullptr; }

  BrowserInitState* init_state() { return &init_state_; }

 private:
  TestingProfile profile_;
  BrowserWindowInterface::Type type_;
  BrowserInitState init_state_;
};

}  // namespace

class WindowSizerChromeOSTest : public ChromeAshTestBase {
 public:
  WindowSizerChromeOSTest() = default;
  WindowSizerChromeOSTest(const WindowSizerChromeOSTest&) = delete;
  WindowSizerChromeOSTest& operator=(const WindowSizerChromeOSTest&) = delete;
  ~WindowSizerChromeOSTest() override = default;

  // Similar to WindowSizerTestUtil::GetWindowBounds() but takes an existing
  // |display_id| instead of creating a TestScreen and new displays.
  // TODO(mek): Refactor this to use a builder pattern similar to what
  // WindowSizerTestUtil does.
  enum Source { DEFAULT, LAST_ACTIVE, PERSISTED, BOTH };
  void GetWindowBounds(BrowserWindowInterface* browser,
                       const gfx::Rect& passed_in,
                       int64_t display_id,
                       const gfx::Rect& bounds,
                       const gfx::Rect& work_area,
                       Source source,
                       gfx::Rect* out_bounds) {
    auto state_provider = std::make_unique<TestStateProvider>();
    if (source == PERSISTED) {
      state_provider->SetPersistentState(bounds, work_area,
                                         ui::mojom::WindowShowState::kDefault);
    } else {
      DCHECK_EQ(source, DEFAULT);
    }
    display::Screen::Get()->SetDisplayForNewWindows(display_id);

    ui::mojom::WindowShowState ignored;
    WindowSizer::GetBrowserWindowBoundsAndShowState(
        std::move(state_provider), passed_in, browser, out_bounds, &ignored);
  }
  void GetWindowBounds(BrowserWindowInterface* browser,
                       const gfx::Rect& passed_in,
                       int64_t display_id,
                       gfx::Rect* out_bounds = nullptr) {
    GetWindowBounds(browser, passed_in, display_id, gfx::Rect(), gfx::Rect(),
                    PERSISTED, out_bounds);
  }

  // Returns browser window |out_bounds| and |out_show_state| for simulated
  // persisted and last-active window bounds, work area, show state, etc.
  static void GetBrowserWindowBoundsAndShowState(
      const gfx::Rect& bounds,
      const gfx::Rect& work_area,
      ui::mojom::WindowShowState show_state_persisted,
      ui::mojom::WindowShowState show_state_last,
      Source source,
      BrowserWindowInterface* browser,
      const gfx::Rect& passed_in,
      gfx::Rect* out_bounds,
      ui::mojom::WindowShowState* out_show_state) {
    DCHECK(out_show_state);
    auto provider = std::make_unique<TestStateProvider>();
    if (source == PERSISTED || source == BOTH) {
      provider->SetPersistentState(bounds, work_area, show_state_persisted);
    }
    if (source == LAST_ACTIVE || source == BOTH) {
      provider->SetLastActiveState(bounds, show_state_last);
    }

    WindowSizer::GetBrowserWindowBoundsAndShowState(
        std::move(provider), passed_in, browser, out_bounds, out_show_state);
  }

  // Returns browser window show state for simulated persisted and last-active
  // window bounds, work area, show state, etc.
  static ui::mojom::WindowShowState GetBrowserWindowShowState(
      ui::mojom::WindowShowState show_state_persisted,
      ui::mojom::WindowShowState show_state_last,
      Source source,
      BrowserWindowInterface* browser,
      const gfx::Rect& bounds,
      const gfx::Rect& work_area) {
    ui::mojom::WindowShowState out_show_state =
        ui::mojom::WindowShowState::kDefault;
    gfx::Rect ignored;
    GetBrowserWindowBoundsAndShowState(bounds, work_area, show_state_persisted,
                                       show_state_last, source, browser,
                                       gfx::Rect(), &ignored, &out_show_state);
    return out_show_state;
  }
};

namespace {

// Shorten identifiers to improve line wrapping.
const int kDesktopBorderSize = WindowSizerChromeOS::kDesktopBorderSize;
const int kMaximumWindowWidth = WindowSizerChromeOS::kMaximumWindowWidth;
const int kWindowTilePixels = WindowSizer::kWindowTilePixels;

}  // namespace

// WindowSizerChromeOSNoAshTest are the tests that does not require ash
// environment, thus it can use WindowSizerTestUtil that create a TestScreen
// inside.

// Test that the window is sized appropriately for the first run experience
// where the default window bounds calculation is invoked.
TEST(WindowSizerChromeOSNoAshTest, DefaultSizeCase) {
  {
    // 4:3 monitor case, 1024x768, no taskbar.
    gfx::Rect window_bounds =
        WindowSizerTestUtil().WithMonitorBounds(p1024x768).GetWindowBounds();
    EXPECT_EQ(
        gfx::Rect(kDesktopBorderSize, kDesktopBorderSize,
                  1024 - kDesktopBorderSize * 2, 768 - kDesktopBorderSize),
        window_bounds);
  }

  {
    // 4:3 monitor case, 1024x768, taskbar on bottom.
    gfx::Rect window_bounds = WindowSizerTestUtil()
                                  .WithMonitorBounds(p1024x768)
                                  .WithMonitorWorkArea(taskbar_bottom_work_area)
                                  .GetWindowBounds();
    EXPECT_EQ(gfx::Rect(kDesktopBorderSize, kDesktopBorderSize,
                        1024 - kDesktopBorderSize * 2,
                        taskbar_bottom_work_area.height() - kDesktopBorderSize),
              window_bounds);
  }

  {
    // 4:3 monitor case, 1024x768, taskbar on right.
    gfx::Rect window_bounds = WindowSizerTestUtil()
                                  .WithMonitorBounds(p1024x768)
                                  .WithMonitorWorkArea(taskbar_right_work_area)
                                  .GetWindowBounds();
    EXPECT_EQ(
        gfx::Rect(kDesktopBorderSize, kDesktopBorderSize,
                  taskbar_right_work_area.width() - kDesktopBorderSize * 2,
                  768 - kDesktopBorderSize),
        window_bounds);
  }

  {
    // 4:3 monitor case, 1024x768, taskbar on left.
    gfx::Rect window_bounds = WindowSizerTestUtil()
                                  .WithMonitorBounds(p1024x768)
                                  .WithMonitorWorkArea(taskbar_left_work_area)
                                  .GetWindowBounds();
    EXPECT_EQ(gfx::Rect(taskbar_left_work_area.x() + kDesktopBorderSize,
                        kDesktopBorderSize,
                        taskbar_left_work_area.width() - kDesktopBorderSize * 2,
                        taskbar_left_work_area.height() - kDesktopBorderSize),
              window_bounds);
  }

  {
    // 4:3 monitor case, 1024x768, taskbar on top.
    gfx::Rect window_bounds = WindowSizerTestUtil()
                                  .WithMonitorBounds(p1024x768)
                                  .WithMonitorWorkArea(taskbar_top_work_area)
                                  .GetWindowBounds();
    EXPECT_EQ(gfx::Rect(kDesktopBorderSize,
                        taskbar_top_work_area.y() + kDesktopBorderSize,
                        1024 - kDesktopBorderSize * 2,
                        taskbar_top_work_area.height() - kDesktopBorderSize),
              window_bounds);
  }

  {
    // 4:3 monitor case, 1280x1024.
    gfx::Rect window_bounds =
        WindowSizerTestUtil().WithMonitorBounds(p1280x1024).GetWindowBounds();
    EXPECT_EQ(gfx::Rect((1280 - kMaximumWindowWidth) / 2, kDesktopBorderSize,
                        kMaximumWindowWidth, 1024 - kDesktopBorderSize),
              window_bounds);
  }

  {
    // 4:3 monitor case, 1600x1200.
    gfx::Rect window_bounds =
        WindowSizerTestUtil().WithMonitorBounds(p1600x1200).GetWindowBounds();
    EXPECT_EQ(gfx::Rect((1600 - kMaximumWindowWidth) / 2, kDesktopBorderSize,
                        kMaximumWindowWidth, 1200 - kDesktopBorderSize),
              window_bounds);
  }

  {
    // 16:10 monitor case, 1680x1050.
    gfx::Rect window_bounds =
        WindowSizerTestUtil().WithMonitorBounds(p1680x1050).GetWindowBounds();
    EXPECT_EQ(gfx::Rect((1680 - kMaximumWindowWidth) / 2, kDesktopBorderSize,
                        kMaximumWindowWidth, 1050 - kDesktopBorderSize),
              window_bounds);
  }

  {
    // 16:10 monitor case, 1920x1200.
    gfx::Rect window_bounds =
        WindowSizerTestUtil().WithMonitorBounds(p1920x1200).GetWindowBounds();
    EXPECT_EQ(gfx::Rect((1920 - kMaximumWindowWidth) / 2, kDesktopBorderSize,
                        kMaximumWindowWidth, 1200 - kDesktopBorderSize),
              window_bounds);
  }
}

// Test that the next opened window is positioned appropriately given the
// bounds of an existing window of the same type.
TEST(WindowSizerChromeOS2NoAshTest, LastWindowBoundsCase) {
  {
    // Normal, in the middle of the screen somewhere.
    gfx::Rect window_bounds =
        WindowSizerTestUtil()
            .WithMonitorBounds(p1024x768)
            .WithLastActiveBounds(
                gfx::Rect(kDesktopBorderSize, kDesktopBorderSize, 500, 400))
            .GetWindowBounds();
    EXPECT_EQ(gfx::Rect(kWindowTilePixels + kDesktopBorderSize,
                        kWindowTilePixels + kDesktopBorderSize, 500, 400)
                  .ToString(),
              window_bounds.ToString());
  }

  {
    // Taskbar on top.
    gfx::Rect window_bounds =
        WindowSizerTestUtil()
            .WithMonitorBounds(p1024x768)
            .WithMonitorWorkArea(taskbar_top_work_area)
            .WithLastActiveBounds(
                gfx::Rect(kDesktopBorderSize, kDesktopBorderSize, 500, 400))
            .GetWindowBounds();
    EXPECT_EQ(gfx::Rect(kWindowTilePixels + kDesktopBorderSize,
                        std::max(kWindowTilePixels + kDesktopBorderSize,
                                 34 /* toolbar height */),
                        500, 400)
                  .ToString(),
              window_bounds.ToString());
  }

  {
    // Too small to satisfy the minimum visibility condition.
    gfx::Rect window_bounds =
        WindowSizerTestUtil()
            .WithMonitorBounds(p1024x768)
            .WithLastActiveBounds(
                gfx::Rect(kDesktopBorderSize, kDesktopBorderSize, 29, 29))
            .GetWindowBounds();
    EXPECT_EQ(gfx::Rect(kWindowTilePixels + kDesktopBorderSize,
                        kWindowTilePixels + kDesktopBorderSize, 30 /* not 29 */,
                        30 /* not 29 */)
                  .ToString(),
              window_bounds.ToString());
  }
}

TEST(WindowSizerChromeOSNoAshTest,
     LastWindowOffscreenWithNonAggressiveRepositioning) {
  {
    // Taskbar on left.
    gfx::Rect window_bounds =
        WindowSizerTestUtil()
            .WithMonitorBounds(p1024x768)
            .WithMonitorWorkArea(taskbar_left_work_area)
            .WithLastActiveBounds(
                gfx::Rect(kDesktopBorderSize, kDesktopBorderSize, 500, 400))
            .GetWindowBounds();
    EXPECT_EQ(gfx::Rect(kWindowTilePixels + kDesktopBorderSize,
                        kWindowTilePixels + kDesktopBorderSize, 500, 400)
                  .ToString(),
              window_bounds.ToString());
  }

  {
    // Offset would put the new window offscreen at the bottom but the minimum
    // visibility condition is barely satisfied without relocation.
    gfx::Rect window_bounds =
        WindowSizerTestUtil()
            .WithMonitorBounds(p1024x768)
            .WithLastActiveBounds(gfx::Rect(10, 638, 500, 400))
            .GetWindowBounds();
    // The y position is the display height (768) minus the minimum visible
    // height (0.3f * 400).
    EXPECT_EQ(gfx::Rect(10 + kWindowTilePixels, 648, 500, 400).ToString(),
              window_bounds.ToString());
  }

  {
    // Offset would put the new window offscreen at the bottom and the minimum
    // visibility condition is satisfied by relocation.
    gfx::Rect window_bounds =
        WindowSizerTestUtil()
            .WithMonitorBounds(p1024x768)
            .WithLastActiveBounds(gfx::Rect(20, 639, 500, 400))
            .GetWindowBounds();
    EXPECT_EQ(gfx::Rect(20 + kWindowTilePixels, 648, 500, 400).ToString(),
              window_bounds.ToString());
  }

  {
    // Offset would put the new window offscreen at the right but the minimum
    // visibility condition is barely satisfied without relocation.
    gfx::Rect window_bounds =
        WindowSizerTestUtil()
            .WithMonitorBounds(p1024x768)
            .WithLastActiveBounds(gfx::Rect(864, 10, 500, 400))
            .GetWindowBounds();
    // The x position is the display width (1024) minus the minimum visible
    // width (0.3f * 500).
    EXPECT_EQ(gfx::Rect(874, 10 + kWindowTilePixels, 500, 400).ToString(),
              window_bounds.ToString());
  }

  {
    // Offset would put the new window offscreen at the right and the minimum
    // visibility condition is satisfied by relocation.
    gfx::Rect window_bounds =
        WindowSizerTestUtil()
            .WithMonitorBounds(p1024x768)
            .WithLastActiveBounds(gfx::Rect(865, 20, 500, 400))
            .GetWindowBounds();
    EXPECT_EQ(gfx::Rect(874, 20 + kWindowTilePixels, 500, 400).ToString(),
              window_bounds.ToString());
  }

  {
    // Offset would put the new window offscreen at the bottom right and the
    // minimum visibility condition is satisfied by relocation.
    gfx::Rect window_bounds =
        WindowSizerTestUtil()
            .WithMonitorBounds(p1024x768)
            .WithLastActiveBounds(gfx::Rect(865, 639, 500, 400))
            .GetWindowBounds();
    EXPECT_EQ(gfx::Rect(874, 648, 500, 400).ToString(),
              window_bounds.ToString());
  }
}

// Test the placement of newly created windows.
// TODO(crbug.com/445541616): Reenable the test.
TEST_F(WindowSizerChromeOSTest, DISABLED_PlaceNewWindows) {
  UpdateDisplay("1600x1200");
  auto* shelf = ash::Shell::GetPrimaryRootWindowController()->shelf();
  shelf->SetAutoHideBehavior(ash::ShelfAutoHideBehavior::kAlways);

  int64_t display_id = display::Screen::Get()->GetPrimaryDisplay().id();

  // Create a browser to pass into the WindowSizerTestUtil::GetWindowBounds
  // function.
  FakeBrowserWindow browser(BrowserWindowInterface::TYPE_NORMAL);

  // Creating a popup handler here to make sure it does not interfere with the
  // existing windows.
  std::unique_ptr<aura::Window> window(
      CreateTestWindowInShell({.bounds = {16, 32, 640, 320}, .window_id = 0}));

  // Creating a popup to make sure it does not interfere with the positioning.
  std::unique_ptr<aura::Window> popup(
      CreateTestWindowInShell({.bounds = {16, 32, 128, 256}, .window_id = 1}));

  window->Show();

  // Make sure that popups do not get changed.
  {
    FakeBrowserWindow new_popup(BrowserWindowInterface::TYPE_POPUP);
    gfx::Rect window_bounds;
    GetWindowBounds(&new_popup, gfx::Rect(), display_id,
                    gfx::Rect(50, 100, 300, 150), bottom_s1600x1200, PERSISTED,
                    &window_bounds);
    EXPECT_EQ("50,100 300x150", window_bounds.ToString());
  }

  window->Hide();

  {
    // If a window is there but not shown the persisted default should be used.
    gfx::Rect window_bounds;
    GetWindowBounds(&browser, gfx::Rect(), display_id,
                    gfx::Rect(50, 100, 300, 150), bottom_s1600x1200, PERSISTED,
                    &window_bounds);
    EXPECT_EQ("50,100 300x150", window_bounds.ToString());
  }

  {
    // If a window is there but not shown the default should be returned.
    gfx::Rect window_bounds;
    GetWindowBounds(&browser, gfx::Rect(), display_id, gfx::Rect(),
                    bottom_s1600x1200, DEFAULT, &window_bounds);
    // Note: We need to also take the defaults maximum width into account here
    // since that might get used if the resolution is too big.
    EXPECT_EQ(
        gfx::Rect(
            std::max(kDesktopBorderSize, (1600 - kMaximumWindowWidth) / 2),
            kDesktopBorderSize,
            std::min(kMaximumWindowWidth, 1600 - 2 * kDesktopBorderSize),
            1200 - kDesktopBorderSize)
            .ToString(),
        window_bounds.ToString());
  }
}

// Test the placement of newly created windows on an empty desktop.
// This test supplements "PlaceNewWindows" by testing the creation of a newly
// created browser window on an empty desktop.
// TODO(crbug.com/445541616): Reenable the test.
TEST_F(WindowSizerChromeOSTest, DISABLED_PlaceNewBrowserWindowOnEmptyDesktop) {
  FakeBrowserWindow browser(BrowserWindowInterface::TYPE_NORMAL);

  // A common screen size for Chrome OS devices where forced-maximized
  // windows are desirable.
  UpdateDisplay("1366x768");
  const gfx::Rect p1366x768(0, 0, 1366, 768);

  // If there is no previous state the window should get maximized if the
  // screen is less than or equal to our limit (1366 pixels width).
  gfx::Rect window_bounds;
  ui::mojom::WindowShowState out_show_state1 =
      ui::mojom::WindowShowState::kDefault;
  GetBrowserWindowBoundsAndShowState(
      gfx::Rect(),                           // The (persisted) bounds.
      p1366x768,                             // The overall work area.
      ui::mojom::WindowShowState::kNormal,   // The persisted show state.
      ui::mojom::WindowShowState::kDefault,  // The last show state.
      DEFAULT,                               // No persisted values.
      &browser,                              // Use this browser.
      gfx::Rect(),                           // Don't request valid bounds.
      &window_bounds, &out_show_state1);
  EXPECT_EQ(ui::mojom::WindowShowState::kMaximized, out_show_state1);

  // If there is a stored coordinate however, that should be taken instead.
  ui::mojom::WindowShowState out_show_state2 =
      ui::mojom::WindowShowState::kDefault;
  GetBrowserWindowBoundsAndShowState(
      gfx::Rect(50, 100, 300, 150),          // The (persisted) bounds.
      p1366x768,                             // The overall work area.
      ui::mojom::WindowShowState::kNormal,   // The persisted show state.
      ui::mojom::WindowShowState::kDefault,  // The last show state.
      PERSISTED,                             // Set the persisted values.
      &browser,                              // Use this browser.
      gfx::Rect(),                           // Don't request valid bounds.
      &window_bounds, &out_show_state2);
  EXPECT_EQ(ui::mojom::WindowShowState::kNormal, out_show_state2);
  EXPECT_EQ("50,100 300x150", window_bounds.ToString());
}

// TODO(crbug.com/445541616): Reenable the test.
TEST_F(WindowSizerChromeOSTest, DISABLED_PlaceNewBrowserWindowOnLargeDesktop) {
  FakeBrowserWindow browser(BrowserWindowInterface::TYPE_NORMAL);

  // A larger monitor should not trigger auto-maximize.
  UpdateDisplay("1600x1200");
  gfx::Rect window_bounds;
  ui::mojom::WindowShowState out_show_state =
      ui::mojom::WindowShowState::kDefault;
  GetBrowserWindowBoundsAndShowState(
      gfx::Rect(),                           // The (persisted) bounds.
      p1600x1200,                            // The overall work area.
      ui::mojom::WindowShowState::kNormal,   // The persisted show state.
      ui::mojom::WindowShowState::kDefault,  // The last show state.
      DEFAULT,                               // No persisted values.
      &browser,                              // Use this browser.
      gfx::Rect(),                           // Don't request valid bounds.
      &window_bounds, &out_show_state);
  EXPECT_EQ(ui::mojom::WindowShowState::kDefault, out_show_state);
}

// Test the placement of newly created windows on multiple dislays.
// TODO(crbug.com/445541616): Reenable the test.
TEST_F(WindowSizerChromeOSTest, DISABLED_PlaceNewWindowsOnMultipleDisplays) {
  UpdateDisplay("1600x1200,1600x1200");
  display::Display primary_display =
      display::Screen::Get()->GetPrimaryDisplay();
  display::Display second_display =
      display::test::DisplayManagerTestApi(display_manager())
          .GetSecondaryDisplay();
  gfx::Rect primary_bounds = primary_display.bounds();
  gfx::Rect secondary_bounds = second_display.bounds();

  // Create windows that are used as reference.
  std::unique_ptr<aura::Window> window(
      CreateTestWindowInShell({.bounds = {10, 10, 200, 200}}));
  window->Show();
  EXPECT_EQ(window->GetRootWindow(), ash::Shell::GetRootWindowForNewWindows());

  std::unique_ptr<aura::Window> another_window(
      CreateTestWindowInShell({.bounds = {400, 10, 300, 300}}));
  another_window->Show();

  // Creating a new window to verify the new placement.
  FakeBrowserWindow new_browser(BrowserWindowInterface::TYPE_NORMAL);

  // Make sure the primary root is active.
  ASSERT_EQ(ash::Shell::GetPrimaryRootWindow(),
            ash::Shell::GetRootWindowForNewWindows());

  // First new window should be in the primary.
  {
    gfx::Rect window_bounds;
    GetWindowBounds(&new_browser, gfx::Rect(), primary_display.id(),
                    &window_bounds);
    // TODO(oshima): Use exact bounds when the window sizer includes the result
    // from RearrangeVisibleWindowOnShow.
    EXPECT_TRUE(primary_bounds.Contains(window_bounds));
  }

  // Move the window to the right side of the secondary display and create a new
  // window. It should be opened then on the secondary display.
  {
    window->SetBoundsInScreen(
        gfx::Rect(secondary_bounds.CenterPoint().x() - 100, 10, 200, 200),
        second_display);
    wm::GetActivationClient(window->GetRootWindow())
        ->ActivateWindow(window.get());
    EXPECT_NE(ash::Shell::GetPrimaryRootWindow(),
              ash::Shell::GetRootWindowForNewWindows());
    gfx::Rect window_bounds;
    GetWindowBounds(&new_browser, gfx::Rect(), second_display.id(),
                    &window_bounds);
    // TODO(oshima): Use exact bounds when the window sizer includes the result
    // from RearrangeVisibleWindowOnShow.
    EXPECT_TRUE(secondary_bounds.Contains(window_bounds));
  }

  // Activate another window in the primary display and create a new window.
  // It should be created in the primary display.
  {
    wm::GetActivationClient(another_window->GetRootWindow())
        ->ActivateWindow(another_window.get());
    EXPECT_EQ(ash::Shell::GetPrimaryRootWindow(),
              ash::Shell::GetRootWindowForNewWindows());

    gfx::Rect window_bounds;
    GetWindowBounds(&new_browser, gfx::Rect(), primary_display.id(),
                    &window_bounds);
    // TODO(oshima): Use exact bounds when the window sizer includes the result
    // from RearrangeVisibleWindowOnShow.
    EXPECT_TRUE(primary_bounds.Contains(window_bounds));
  }
}

// Test that the show state is properly returned for non default cases.
// TODO(crbug.com/445541616): Reenable the test.
TEST_F(WindowSizerChromeOSTest, DISABLED_TestShowState) {
  UpdateDisplay("1600x1200");

  // Creating a browser & window to play with.
  FakeBrowserWindow browser(BrowserWindowInterface::TYPE_NORMAL);

  // Create also a popup browser since that behaves different.
  FakeBrowserWindow browser_popup(BrowserWindowInterface::TYPE_POPUP);

  // Tabbed windows should retrieve the saved window state - since there is a
  // top window.
  EXPECT_EQ(
      ui::mojom::WindowShowState::kMaximized,
      GetBrowserWindowShowState(ui::mojom::WindowShowState::kMaximized,
                                ui::mojom::WindowShowState::kNormal, PERSISTED,
                                &browser, p1600x1200, p1600x1200));
  // A window that is smaller than the whole work area is set to default state.
  EXPECT_EQ(
      ui::mojom::WindowShowState::kDefault,
      GetBrowserWindowShowState(ui::mojom::WindowShowState::kDefault,
                                ui::mojom::WindowShowState::kNormal, PERSISTED,
                                &browser, p1280x1024, p1600x1200));
  // A window that is sized to occupy the whole work area is maximized.
  EXPECT_EQ(
      ui::mojom::WindowShowState::kMaximized,
      GetBrowserWindowShowState(ui::mojom::WindowShowState::kDefault,
                                ui::mojom::WindowShowState::kNormal, PERSISTED,
                                &browser, p1600x1200, p1600x1200));
  // Non tabbed windows should always follow the window saved visibility state.
  EXPECT_EQ(ui::mojom::WindowShowState::kMaximized,
            GetBrowserWindowShowState(ui::mojom::WindowShowState::kMaximized,
                                      ui::mojom::WindowShowState::kNormal, BOTH,
                                      &browser_popup, p1600x1200, p1600x1200));
  // The non tabbed window will take the status of the last active of its kind.
  EXPECT_EQ(ui::mojom::WindowShowState::kNormal,
            GetBrowserWindowShowState(ui::mojom::WindowShowState::kDefault,
                                      ui::mojom::WindowShowState::kNormal, BOTH,
                                      &browser_popup, p1600x1200, p1600x1200));

  // A tabbed window should now take the top level window state.
  EXPECT_EQ(ui::mojom::WindowShowState::kNormal,
            GetBrowserWindowShowState(ui::mojom::WindowShowState::kMaximized,
                                      ui::mojom::WindowShowState::kNormal, BOTH,
                                      &browser, p1600x1200, p1600x1200));
  // Non tabbed windows should always follow the window saved visibility state.
  EXPECT_EQ(
      ui::mojom::WindowShowState::kMaximized,
      GetBrowserWindowShowState(ui::mojom::WindowShowState::kMaximized,
                                ui::mojom::WindowShowState::kMinimized, BOTH,
                                &browser_popup, p1600x1200, p1600x1200));
}

// TODO(crbug.com/445541616): Reenable the test.
TEST_F(WindowSizerChromeOSTest, DISABLED_TestShowStateOnTinyScreen) {
  FakeBrowserWindow browser(BrowserWindowInterface::TYPE_NORMAL);

  // In smaller screen resolutions we default to maximized if there is no other
  // window visible.
  UpdateDisplay("640x480");
  const gfx::Rect tiny_screen(0, 0, 640, 480);
  EXPECT_EQ(
      ui::mojom::WindowShowState::kMaximized,
      GetBrowserWindowShowState(ui::mojom::WindowShowState::kMaximized,
                                ui::mojom::WindowShowState::kDefault, BOTH,
                                &browser, tiny_screen, tiny_screen));
}

// Test that the default show state override behavior is properly handled.
// TODO(crbug.com/445541616): Reenable the test.
TEST_F(WindowSizerChromeOSTest, DISABLED_TestShowStateDefaults) {
  UpdateDisplay("1600x1200");
  // Creating a browser & window to play with.

  FakeBrowserWindow browser(BrowserWindowInterface::TYPE_NORMAL);

  // Create also a popup browser since that behaves slightly different for
  // defaults.
  FakeBrowserWindow browser_popup(BrowserWindowInterface::TYPE_POPUP);

  // Check that a browser creation state always get used if not given as
  // SHOW_STATE_DEFAULT.
  ui::mojom::WindowShowState window_show_state =
      GetBrowserWindowShowState(ui::mojom::WindowShowState::kMaximized,
                                ui::mojom::WindowShowState::kMaximized, DEFAULT,
                                &browser, p1600x1200, p1600x1200);
  EXPECT_EQ(window_show_state, ui::mojom::WindowShowState::kDefault);

  browser.init_state()->set_initial_show_state(
      ui::mojom::WindowShowState::kMinimized);
  EXPECT_EQ(GetBrowserWindowShowState(ui::mojom::WindowShowState::kMaximized,
                                      ui::mojom::WindowShowState::kMaximized,
                                      BOTH, &browser, p1600x1200, p1600x1200),
            ui::mojom::WindowShowState::kMinimized);
  browser.init_state()->set_initial_show_state(
      ui::mojom::WindowShowState::kNormal);
  EXPECT_EQ(GetBrowserWindowShowState(ui::mojom::WindowShowState::kMaximized,
                                      ui::mojom::WindowShowState::kMaximized,
                                      BOTH, &browser, p1600x1200, p1600x1200),
            ui::mojom::WindowShowState::kNormal);
  browser.init_state()->set_initial_show_state(
      ui::mojom::WindowShowState::kMaximized);
  EXPECT_EQ(GetBrowserWindowShowState(ui::mojom::WindowShowState::kNormal,
                                      ui::mojom::WindowShowState::kNormal, BOTH,
                                      &browser, p1600x1200, p1600x1200),
            ui::mojom::WindowShowState::kMaximized);

  // Check that setting the maximized command line option is forcing the
  // maximized state.
  base::CommandLine::ForCurrentProcess()->AppendSwitch(
      switches::kStartMaximized);

  browser.init_state()->set_initial_show_state(
      ui::mojom::WindowShowState::kNormal);
  EXPECT_EQ(GetBrowserWindowShowState(ui::mojom::WindowShowState::kNormal,
                                      ui::mojom::WindowShowState::kNormal, BOTH,
                                      &browser, p1600x1200, p1600x1200),
            ui::mojom::WindowShowState::kMaximized);

  // The popup should favor the initial show state over the command line.
  EXPECT_EQ(GetBrowserWindowShowState(ui::mojom::WindowShowState::kNormal,
                                      ui::mojom::WindowShowState::kNormal, BOTH,
                                      &browser_popup, p1600x1200, p1600x1200),
            ui::mojom::WindowShowState::kNormal);
}

// TODO(crbug.com/445541616): Reenable the test.
TEST_F(WindowSizerChromeOSTest, DISABLED_DefaultStateBecomesMaximized) {
  // Create a browser to pass into the WindowSizerTestUtil::GetWindowBounds
  // function.
  FakeBrowserWindow browser(BrowserWindowInterface::TYPE_NORMAL);

  gfx::Rect display_bounds =
      display::Screen::Get()->GetPrimaryDisplay().bounds();
  gfx::Rect specified_bounds = display_bounds;

  // Make a window bigger than the display work area.
  specified_bounds.Inset(-20);
  ui::mojom::WindowShowState show_state = ui::mojom::WindowShowState::kDefault;
  gfx::Rect bounds;
  WindowSizer::GetBrowserWindowBoundsAndShowState(specified_bounds, &browser,
                                                  &bounds, &show_state);
  // The window should start maximized with its restore bounds shrunken.
  EXPECT_EQ(ui::mojom::WindowShowState::kMaximized, show_state);
  EXPECT_NE(display_bounds.ToString(), bounds.ToString());
  EXPECT_TRUE(display_bounds.Contains(bounds));

  // Make a window smaller than the display work area.
  specified_bounds.Inset(100);
  show_state = ui::mojom::WindowShowState::kDefault;
  WindowSizer::GetBrowserWindowBoundsAndShowState(specified_bounds, &browser,
                                                  &bounds, &show_state);
  // The window should start in default state.
  EXPECT_EQ(ui::mojom::WindowShowState::kDefault, show_state);
  EXPECT_EQ(specified_bounds.ToString(), bounds.ToString());
}

// Test that the target root window is used as the destination of
// the non browser window. This differ from PersistedBoundsCase
// in that this uses real ash shell implementations + StateProvider
// rather than mocks.
TEST_F(WindowSizerChromeOSTest, DefaultBoundsInTargetDisplay) {
  UpdateDisplay("500x400,600x500");

  {
    // By default windows are placed on the primary display.
    aura::Window* first_root = ash::Shell::GetAllRootWindows()[0];
    EXPECT_EQ(first_root, ash::Shell::GetRootWindowForNewWindows());
    gfx::Rect bounds;
    ui::mojom::WindowShowState show_state;
    WindowSizer::GetBrowserWindowBoundsAndShowState(gfx::Rect(), nullptr,
                                                    &bounds, &show_state);
    EXPECT_TRUE(first_root->GetBoundsInScreen().Contains(bounds));
  }

  {
    // When the second display is active new windows are placed there.
    aura::Window* second_root = ash::Shell::GetAllRootWindows()[1];
    int64_t second_display_id =
        display::test::DisplayManagerTestApi(display_manager())
            .GetSecondaryDisplay()
            .id();
    display::Screen::Get()->SetDisplayForNewWindows(second_display_id);
    gfx::Rect bounds;
    ui::mojom::WindowShowState show_state;
    WindowSizer::GetBrowserWindowBoundsAndShowState(gfx::Rect(), nullptr,
                                                    &bounds, &show_state);
    EXPECT_TRUE(second_root->GetBoundsInScreen().Contains(bounds));
  }
}
