chromium/chrome/browser/lacros/screen_manager_lacros_browsertest.cc

// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <string>
#include <utility>
#include <vector>

#include "base/functional/bind.h"
#include "base/rand_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "chrome/browser/lacros/browser_test_util.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/grit/branded_strings.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "chromeos/crosapi/mojom/screen_manager.mojom.h"
#include "chromeos/lacros/lacros_service.h"
#include "content/public/test/browser_test.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/sync_call_restrictions.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "url/gurl.h"

namespace {
constexpr char kLacrosPageTitleFormat[] = "Title Of Lacros Browser Test %lu";
constexpr char kLacrosPageTitleHTMLFormat[] =
    "<html><head><title>%s</title></head>"
    "<body>This page has a title.</body></html>";

mojo::Remote<crosapi::mojom::SnapshotCapturer> GetWindowCapturer() {
  auto* lacros_chrome_service = chromeos::LacrosService::Get();
  mojo::Remote<crosapi::mojom::SnapshotCapturer> capturer;
  lacros_chrome_service->GetRemote<crosapi::mojom::ScreenManager>()
      ->GetWindowCapturer(capturer.BindNewPipeAndPassReceiver());

  return capturer;
}

// Used to find the window corresponding to the test page.
uint64_t WaitForWindow(std::string title) {
  mojo::Remote<crosapi::mojom::SnapshotCapturer> capturer = GetWindowCapturer();

  base::RunLoop run_loop;
  uint64_t window_id;
  auto look_for_window = base::BindRepeating(
      [](mojo::Remote<crosapi::mojom::SnapshotCapturer>* capturer,
         base::RunLoop* run_loop, uint64_t* window_id, std::string tab_title) {
        std::vector<crosapi::mojom::SnapshotSourcePtr> windows;
        {
          mojo::ScopedAllowSyncCallForTesting allow_sync_call;
          (*capturer)->ListSources(&windows);
        }
        for (auto& window : windows) {
          if (window->title == tab_title) {
            if (window_id)
              (*window_id) = window->id;
            run_loop->Quit();
            break;
          }
        }
      },
      &capturer, &run_loop, &window_id, std::move(title));

  // When the browser test start, there is no guarantee that the window is
  // open from ash's perspective.
  base::RepeatingTimer timer;
  timer.Start(FROM_HERE, base::Milliseconds(1), std::move(look_for_window));
  run_loop.Run();

  return window_id;
}

// |browser| is a browser instance that will be navigated to a fixed URL.
// Returns the window ID from the snapshot crosapi.
uint64_t WaitForLacrosToBeAvailableInAsh(Browser* browser) {
  // Generate a random window title so that multiple lacros_chrome_browsertests
  // can run at the same time without confusing windows.
  std::string title =
      base::StringPrintf(kLacrosPageTitleFormat, base::RandUint64());
  std::string html =
      base::StringPrintf(kLacrosPageTitleHTMLFormat, title.c_str());
  GURL url(std::string("data:text/html,") + html);
  EXPECT_TRUE(ui_test_utils::NavigateToURL(browser, url));

  return WaitForWindow(std::move(title));
}

}  // namespace

class ScreenManagerLacrosBrowserTest : public InProcessBrowserTest {
 protected:
  ScreenManagerLacrosBrowserTest() = default;

  ScreenManagerLacrosBrowserTest(const ScreenManagerLacrosBrowserTest&) =
      delete;
  ScreenManagerLacrosBrowserTest& operator=(
      const ScreenManagerLacrosBrowserTest&) = delete;

  ~ScreenManagerLacrosBrowserTest() override = default;
};

// Tests that taking a screen snapshot via crosapi works.
IN_PROC_BROWSER_TEST_F(ScreenManagerLacrosBrowserTest, ScreenCapturer) {
  mojo::Remote<crosapi::mojom::SnapshotCapturer> capturer;
  auto* lacros_service = chromeos::LacrosService::Get();
  lacros_service->GetRemote<crosapi::mojom::ScreenManager>()->GetScreenCapturer(
      capturer.BindNewPipeAndPassReceiver());

  std::vector<crosapi::mojom::SnapshotSourcePtr> screens;
  {
    mojo::ScopedAllowSyncCallForTesting allow_sync_call;
    capturer->ListSources(&screens);
  }
  ASSERT_LE(1u, screens.size());

  bool success = false;
  SkBitmap snapshot;
  {
    mojo::ScopedAllowSyncCallForTesting allow_sync_call;
    capturer->TakeSnapshot(screens[0]->id, &success, &snapshot);
  }
  EXPECT_TRUE(success);
  // Verify the snapshot is non-empty.
  EXPECT_GT(snapshot.height(), 0);
  EXPECT_GT(snapshot.width(), 0);
}

// Tests that taking a window snapshot via crosapi works.
// This test makes the browser load a page with specific title, and then scans
// through a list of windows to look for the window with the expected title.
// This test cannot simply asserts exactly 1 window is present because currently
// in lacros_chrome_browsertests, different browser tests share the same
// ash-chrome, so a window could come from any one of them.
IN_PROC_BROWSER_TEST_F(ScreenManagerLacrosBrowserTest, WindowCapturer) {
  mojo::Remote<crosapi::mojom::SnapshotCapturer> capturer;
  auto* lacros_service = chromeos::LacrosService::Get();
  lacros_service->GetRemote<crosapi::mojom::ScreenManager>()->GetWindowCapturer(
      capturer.BindNewPipeAndPassReceiver());

  uint64_t window_id = WaitForLacrosToBeAvailableInAsh(browser());

  bool success = false;
  SkBitmap snapshot;
  {
    mojo::ScopedAllowSyncCallForTesting allow_sync_call;
    capturer->TakeSnapshot(window_id, &success, &snapshot);
  }
  ASSERT_TRUE(success);
  // Verify the snapshot is non-empty.
  EXPECT_GT(snapshot.height(), 0);
  EXPECT_GT(snapshot.width(), 0);
}

IN_PROC_BROWSER_TEST_F(ScreenManagerLacrosBrowserTest, AppWindowCapturer) {
  mojo::Remote<crosapi::mojom::SnapshotCapturer> capturer;
  auto* lacros_service = chromeos::LacrosService::Get();
  lacros_service->GetRemote<crosapi::mojom::ScreenManager>()->GetWindowCapturer(
      capturer.BindNewPipeAndPassReceiver());

  Browser* app_browser =
      CreateBrowserForApp("test_app_name", browser()->profile());
  uint64_t window_id = WaitForLacrosToBeAvailableInAsh(app_browser);

  bool success = false;
  SkBitmap snapshot;
  {
    mojo::ScopedAllowSyncCallForTesting allow_sync_call;
    capturer->TakeSnapshot(window_id, &success, &snapshot);
  }
  ASSERT_TRUE(success);
  // Verify the snapshot is non-empty.
  EXPECT_GT(snapshot.height(), 0);
  EXPECT_GT(snapshot.width(), 0);
}