|  | // 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 <memory> | 
|  |  | 
|  | #include "ash/public/cpp/test/shell_test_api.h" | 
|  | #include "base/memory/scoped_refptr.h" | 
|  | #include "base/task/sequenced_task_runner.h" | 
|  | #include "base/task/task_traits.h" | 
|  | #include "base/task/thread_pool.h" | 
|  | #include "chrome/browser/ash/crosapi/screen_manager_ash.h" | 
|  | #include "chrome/browser/ui/browser.h" | 
|  | #include "chrome/browser/ui/browser_window.h" | 
|  | #include "chrome/test/base/in_process_browser_test.h" | 
|  | #include "chromeos/crosapi/mojom/screen_manager.mojom.h" | 
|  | #include "content/public/test/browser_test.h" | 
|  | #include "mojo/public/cpp/bindings/remote.h" | 
|  | #include "mojo/public/cpp/bindings/sync_call_restrictions.h" | 
|  | #include "ui/aura/window.h" | 
|  | #include "ui/display/test/display_manager_test_api.h" | 
|  |  | 
|  | namespace crosapi { | 
|  | namespace { | 
|  |  | 
|  | // This class tests that the ash-chrome implementation of the screen manager | 
|  | // crosapi works properly. | 
|  | class ScreenManagerAshBrowserTest : public InProcessBrowserTest { | 
|  | protected: | 
|  | using SMRemote = mojo::Remote<mojom::ScreenManager>; | 
|  | using SMPendingRemote = mojo::PendingRemote<mojom::ScreenManager>; | 
|  | using SMPendingReceiver = mojo::PendingReceiver<mojom::ScreenManager>; | 
|  |  | 
|  | ScreenManagerAshBrowserTest() = default; | 
|  |  | 
|  | ScreenManagerAshBrowserTest(const ScreenManagerAshBrowserTest&) = delete; | 
|  | ScreenManagerAshBrowserTest& operator=(const ScreenManagerAshBrowserTest&) = | 
|  | delete; | 
|  |  | 
|  | ~ScreenManagerAshBrowserTest() override { | 
|  | background_sequence_->DeleteSoon(FROM_HERE, | 
|  | std::move(screen_manager_remote_)); | 
|  | } | 
|  |  | 
|  | void SetUpOnMainThread() override { | 
|  | // The implementation of screen manager is affine to this sequence. | 
|  | screen_manager_ = std::make_unique<ScreenManagerAsh>(); | 
|  |  | 
|  | SMPendingRemote pending_remote; | 
|  | SMPendingReceiver pending_receiver = | 
|  | pending_remote.InitWithNewPipeAndPassReceiver(); | 
|  |  | 
|  | // Bind the implementation of ScreenManager to this sequence. | 
|  | screen_manager_->BindReceiver(std::move(pending_receiver)); | 
|  |  | 
|  | // Bind the remote to a background sequence. This is necessary because the | 
|  | // screen manager API is synchronous and blocks the calling sequence. | 
|  | background_sequence_ = base::ThreadPool::CreateSequencedTaskRunner( | 
|  | {base::TaskPriority::USER_BLOCKING, base::MayBlock(), | 
|  | base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}); | 
|  |  | 
|  | // We construct the remote on this sequence for simplicity. All subsequent | 
|  | // invocations, including destruction, are from the background sequence. | 
|  | screen_manager_remote_ = std::make_unique<SMRemote>(); | 
|  | auto bind_background = base::BindOnce( | 
|  | [](SMRemote* remote, SMPendingRemote pending_remote) { | 
|  | remote->Bind(std::move(pending_remote)); | 
|  | }, | 
|  | screen_manager_remote_.get(), std::move(pending_remote)); | 
|  | background_sequence_->PostTask(FROM_HERE, std::move(bind_background)); | 
|  | } | 
|  |  | 
|  | // Affine to main sequence. | 
|  | std::unique_ptr<ScreenManagerAsh> screen_manager_; | 
|  |  | 
|  | // A sequence that is allowed to block. | 
|  | scoped_refptr<base::SequencedTaskRunner> background_sequence_; | 
|  |  | 
|  | // Affine to background sequence. | 
|  | std::unique_ptr<SMRemote> screen_manager_remote_; | 
|  | }; | 
|  |  | 
|  | IN_PROC_BROWSER_TEST_F(ScreenManagerAshBrowserTest, ScreenCapturer) { | 
|  | base::RunLoop run_loop; | 
|  | bool success; | 
|  | SkBitmap snapshot; | 
|  |  | 
|  | // Take a snapshot on a background sequence. The call is blocking, so when it | 
|  | // finishes, we can also unblock the main thread. | 
|  | auto take_snapshot_background = base::BindOnce( | 
|  | [](SMRemote* remote, bool* success, SkBitmap* snapshot) { | 
|  | mojo::Remote<mojom::SnapshotCapturer> capturer; | 
|  | (*remote)->GetScreenCapturer(capturer.BindNewPipeAndPassReceiver()); | 
|  |  | 
|  | { | 
|  | mojo::ScopedAllowSyncCallForTesting allow_sync; | 
|  |  | 
|  | std::vector<mojom::SnapshotSourcePtr> screens; | 
|  | capturer->ListSources(&screens); | 
|  |  | 
|  | // There should be at least one screen! | 
|  | ASSERT_LE(1u, screens.size()); | 
|  |  | 
|  | capturer->TakeSnapshot(screens[0]->id, success, snapshot); | 
|  | } | 
|  | }, | 
|  | screen_manager_remote_.get(), &success, &snapshot); | 
|  | background_sequence_->PostTaskAndReply( | 
|  | FROM_HERE, std::move(take_snapshot_background), run_loop.QuitClosure()); | 
|  | run_loop.Run(); | 
|  |  | 
|  | // Check that the IPC succeeded. | 
|  | ASSERT_TRUE(success); | 
|  |  | 
|  | // Check that the screenshot has the right dimensions. | 
|  | aura::Window* primary_window = | 
|  | browser()->window()->GetNativeWindow()->GetRootWindow(); | 
|  | EXPECT_EQ(int{snapshot.width()}, primary_window->bounds().width()); | 
|  | EXPECT_EQ(int{snapshot.height()}, primary_window->bounds().height()); | 
|  | } | 
|  |  | 
|  | IN_PROC_BROWSER_TEST_F(ScreenManagerAshBrowserTest, | 
|  | ScreenCapturer_MultipleDisplays) { | 
|  | base::RunLoop run_loop; | 
|  | bool success[2]; | 
|  | SkBitmap snapshot[2]; | 
|  |  | 
|  | display::test::DisplayManagerTestApi(ash::ShellTestApi().display_manager()) | 
|  | .UpdateDisplay("400x300,100x500"); | 
|  |  | 
|  | // Take a snapshot on a background sequence. The call is blocking, so when it | 
|  | // finishes, we can also unblock the main thread. | 
|  | auto take_snapshot_background = base::BindOnce( | 
|  | [](SMRemote* remote, bool success[2], SkBitmap snapshot[2]) { | 
|  | mojo::Remote<mojom::SnapshotCapturer> capturer; | 
|  | (*remote)->GetScreenCapturer(capturer.BindNewPipeAndPassReceiver()); | 
|  |  | 
|  | { | 
|  | mojo::ScopedAllowSyncCallForTesting allow_sync; | 
|  |  | 
|  | std::vector<mojom::SnapshotSourcePtr> screens; | 
|  | capturer->ListSources(&screens); | 
|  |  | 
|  | // There should be exactly two screens! | 
|  | ASSERT_EQ(2u, screens.size()); | 
|  |  | 
|  | capturer->TakeSnapshot(screens[0]->id, &success[0], &snapshot[0]); | 
|  | capturer->TakeSnapshot(screens[1]->id, &success[1], &snapshot[1]); | 
|  | } | 
|  | }, | 
|  | screen_manager_remote_.get(), success, snapshot); | 
|  | background_sequence_->PostTaskAndReply( | 
|  | FROM_HERE, std::move(take_snapshot_background), run_loop.QuitClosure()); | 
|  | run_loop.Run(); | 
|  |  | 
|  | // Check that the IPCs succeeded. | 
|  | ASSERT_TRUE(success[0]); | 
|  | ASSERT_TRUE(success[1]); | 
|  |  | 
|  | // Check that the screenshots have the right dimensions. | 
|  | EXPECT_EQ(400, int{snapshot[0].width()}); | 
|  | EXPECT_EQ(300, int{snapshot[0].height()}); | 
|  | EXPECT_EQ(100, int{snapshot[1].width()}); | 
|  | EXPECT_EQ(500, int{snapshot[1].height()}); | 
|  | } | 
|  |  | 
|  | IN_PROC_BROWSER_TEST_F(ScreenManagerAshBrowserTest, WindowCapturer) { | 
|  | base::RunLoop run_loop; | 
|  | bool success; | 
|  | SkBitmap snapshot; | 
|  |  | 
|  | // Take a snapshot on a background sequence. The call is blocking, so when it | 
|  | // finishes, we can also unblock the main thread. | 
|  | auto take_snapshot_background = base::BindOnce( | 
|  | [](SMRemote* remote, bool* success, SkBitmap* snapshot) { | 
|  | mojo::Remote<mojom::SnapshotCapturer> capturer; | 
|  | (*remote)->GetWindowCapturer(capturer.BindNewPipeAndPassReceiver()); | 
|  |  | 
|  | { | 
|  | mojo::ScopedAllowSyncCallForTesting allow_sync; | 
|  |  | 
|  | std::vector<mojom::SnapshotSourcePtr> windows; | 
|  | capturer->ListSources(&windows); | 
|  |  | 
|  | // There should be at least one window! | 
|  | ASSERT_LE(1u, windows.size()); | 
|  |  | 
|  | capturer->TakeSnapshot(windows[0]->id, success, snapshot); | 
|  | } | 
|  | }, | 
|  | screen_manager_remote_.get(), &success, &snapshot); | 
|  | background_sequence_->PostTaskAndReply( | 
|  | FROM_HERE, std::move(take_snapshot_background), run_loop.QuitClosure()); | 
|  | run_loop.Run(); | 
|  |  | 
|  | // Check that the IPC succeeded. | 
|  | ASSERT_TRUE(success); | 
|  |  | 
|  | // Check that the screenshot has the right dimensions. | 
|  | aura::Window* window = browser()->window()->GetNativeWindow(); | 
|  | EXPECT_EQ(int{snapshot.width()}, window->bounds().width()); | 
|  | EXPECT_EQ(int{snapshot.height()}, window->bounds().height()); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace crosapi |