Commit f8595f77 authored by Scott Violet's avatar Scott Violet Committed by Commit Bot

chromeos: wires up various embed functions for ws2

Specifically ScheduleEmbed(), ScheduleEmbedForExistingClient() and
EmbedUsingToken().

BUG=837702
TEST=covered by test
TBR=tsepez@chromium.org

Change-Id: I5a7922ec0c14cf3f0c3b6632794eb2a6f6a9963a
Reviewed-on: https://chromium-review.googlesource.com/1096662Reviewed-by: default avatarScott Violet <sky@chromium.org>
Reviewed-by: default avatarTom Sepez <tsepez@chromium.org>
Reviewed-by: default avatarMichael Wasserman <msw@chromium.org>
Commit-Queue: Scott Violet <sky@chromium.org>
Cr-Commit-Position: refs/heads/master@{#566658}
parent ab566bb1
......@@ -275,6 +275,8 @@ interface WindowTree {
// 4. Client A receives OnEmbedFromToken() with the token from step 1.
//
// |window_id| is the id used for the window once EmbedUsingToken() is called.
// More specifically, when OnEmbedFromToken() is called |window_id| is the id
// of the window identified in the WindowData.
ScheduleEmbedForExistingClient(uint32 window_id) => (
mojo_base.mojom.UnguessableToken token);
......
......@@ -9,6 +9,7 @@
#include "base/bind.h"
#include "services/ui/ws2/window_service.h"
#include "services/ui/ws2/window_tree.h"
#include "services/ui/ws2/window_tree_binding.h"
#include "ui/aura/window.h"
namespace ui {
......@@ -24,15 +25,24 @@ Embedding::Embedding(WindowTree* embedding_tree,
DCHECK(window_);
} // namespace ws2
Embedding::~Embedding() = default;
Embedding::~Embedding() {
if (!binding_)
embedded_tree_->OnEmbeddingDestroyed(this);
}
void Embedding::Init(WindowService* window_service,
mojom::WindowTreeClientPtr window_tree_client_ptr,
mojom::WindowTreeClient* window_tree_client,
base::OnceClosure connection_lost_callback) {
binding_.InitForEmbed(window_service, std::move(window_tree_client_ptr),
window_tree_client, window_,
std::move(connection_lost_callback));
binding_ = std::make_unique<WindowTreeBinding>();
binding_->InitForEmbed(window_service, std::move(window_tree_client_ptr),
window_tree_client, window_,
std::move(connection_lost_callback));
embedded_tree_ = binding_->window_tree();
}
void Embedding::InitForEmbedInExistingTree(WindowTree* embedded_tree) {
embedded_tree_ = embedded_tree;
}
} // namespace ws2
......
......@@ -11,7 +11,6 @@
#include "base/component_export.h"
#include "base/macros.h"
#include "services/ui/public/interfaces/window_tree.mojom.h"
#include "services/ui/ws2/window_tree_binding.h"
namespace aura {
class Window;
......@@ -22,11 +21,16 @@ namespace ws2 {
class WindowService;
class WindowTree;
// Embedding is created any time a client calls Embed() (Embedding is not
// created for top-levels). Embedding owns the embedded WindowTree (by way of
// owning WindowTreeBinding). Embedding is owned by the Window associated with
// the embedding.
class WindowTreeBinding;
// Embedding is created any time a client calls Embed() or EmbedUsingToken()
// (Embedding is not created for top-levels). Embedding has two distinct
// configurations:
// . The Embedding does not own the embedded WindowTree. This happens if
// ScheduleEmbedForExistingClient() was used.
// . In all other cases Embedding owns the embedded WindowTree.
//
// Embedding is owned by the Window associated with the embedding.
class COMPONENT_EXPORT(WINDOW_SERVICE) Embedding {
public:
Embedding(WindowTree* embedding_tree,
......@@ -39,13 +43,17 @@ class COMPONENT_EXPORT(WINDOW_SERVICE) Embedding {
mojom::WindowTreeClient* window_tree_client,
base::OnceClosure connection_lost_callback);
// Initializes the Embedding as the result of
// ScheduleEmbedForExistingClient().
void InitForEmbedInExistingTree(WindowTree* embedded_tree);
WindowTree* embedding_tree() { return embedding_tree_; }
bool embedding_tree_intercepts_events() const {
return embedding_tree_intercepts_events_;
}
WindowTree* embedded_tree() { return binding_.window_tree(); }
WindowTree* embedded_tree() { return embedded_tree_; }
aura::Window* window() { return window_; }
......@@ -64,7 +72,13 @@ class COMPONENT_EXPORT(WINDOW_SERVICE) Embedding {
// Embedded trees can always observe pointer events, regardless of this value.
const bool embedding_tree_intercepts_events_;
WindowTreeBinding binding_;
// |binding_| is created if the Embedding owns the embedded WindowTree.
std::unique_ptr<WindowTreeBinding> binding_;
// The embedded WindowTree. If |binding_| is non-null, this comes from the
// WindowTreeBinding. If |binding_| is null, this is the value supplied to
// InitForEmbedInExistingTree().
WindowTree* embedded_tree_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(Embedding);
};
......
......@@ -40,6 +40,9 @@ std::string ChangeToDescription(const Change& change,
return base::StringPrintf("OnEmbed drawn=%s",
change.bool_value ? "true" : "false");
case CHANGE_TYPE_EMBED_FROM_TOKEN:
return base::StringPrintf("OnEmbedFromToken");
case CHANGE_TYPE_EMBEDDED_APP_DISCONNECTED:
return base::StringPrintf("OnEmbeddedAppDisconnected window=%s",
WindowIdToString(change.window_id).c_str());
......@@ -266,6 +269,17 @@ void TestChangeTracker::OnEmbed(mojom::WindowDataPtr root, bool drawn) {
AddChange(change);
}
void TestChangeTracker::OnEmbedFromToken(
mojom::WindowDataPtr root,
int64_t display_id,
const base::Optional<viz::LocalSurfaceId>& local_surface_id) {
Change change;
change.type = CHANGE_TYPE_EMBED_FROM_TOKEN;
change.display_id = display_id;
change.windows.push_back(WindowDataToTestWindow(root));
AddChange(change);
}
void TestChangeTracker::OnEmbeddedAppDisconnected(Id window_id) {
Change change;
change.type = CHANGE_TYPE_EMBEDDED_APP_DISCONNECTED;
......
......@@ -25,6 +25,7 @@ enum ChangeType {
CHANGE_TYPE_CAPTURE_CHANGED,
CHANGE_TYPE_FRAME_SINK_ID_ALLOCATED,
CHANGE_TYPE_EMBED,
CHANGE_TYPE_EMBED_FROM_TOKEN,
CHANGE_TYPE_EMBEDDED_APP_DISCONNECTED,
CHANGE_TYPE_UNEMBED,
// TODO(sky): nuke NODE.
......@@ -164,6 +165,10 @@ class TestChangeTracker {
// Each of these functions generate a Change. There is one per
// WindowTreeClient function.
void OnEmbed(mojom::WindowDataPtr root, bool drawn);
void OnEmbedFromToken(
mojom::WindowDataPtr root,
int64_t display_id,
const base::Optional<viz::LocalSurfaceId>& local_surface_id);
void OnEmbeddedAppDisconnected(Id window_id);
void OnUnembed(Id window_id);
void OnCaptureChanged(Id new_capture_window_id, Id old_capture_window_id);
......
......@@ -84,7 +84,9 @@ void TestWindowTreeClient::OnEmbedFromToken(
const base::UnguessableToken& token,
mojom::WindowDataPtr root,
int64_t display_id,
const base::Optional<viz::LocalSurfaceId>& local_surface_id) {}
const base::Optional<viz::LocalSurfaceId>& local_surface_id) {
tracker_.OnEmbedFromToken(std::move(root), display_id, local_surface_id);
}
void TestWindowTreeClient::OnEmbeddedAppDisconnected(Id window_id) {
tracker_.OnEmbeddedAppDisconnected(window_id);
......
......@@ -34,7 +34,9 @@ WindowService::WindowService(WindowServiceDelegate* delegate,
input_device_server_.RegisterAsObserver();
}
WindowService::~WindowService() = default;
WindowService::~WindowService() {
DCHECK(window_trees_.empty());
}
ServerWindow* WindowService::GetServerWindowForWindowCreateIfNecessary(
aura::Window* window) {
......@@ -53,7 +55,10 @@ std::unique_ptr<WindowTree> WindowService::CreateWindowTree(
mojom::WindowTreeClient* window_tree_client) {
const ClientSpecificId client_id = next_client_id_++;
CHECK_NE(0u, next_client_id_);
return std::make_unique<WindowTree>(this, client_id, window_tree_client);
auto window_tree =
std::make_unique<WindowTree>(this, client_id, window_tree_client);
window_trees_.insert(window_tree.get());
return window_tree;
}
void WindowService::SetFrameDecorationValues(
......@@ -68,6 +73,10 @@ bool WindowService::HasRemoteClient(aura::Window* window) {
return ServerWindow::GetMayBeNull(window);
}
void WindowService::OnWillDestroyWindowTree(WindowTree* tree) {
window_trees_.erase(tree);
}
void WindowService::RequestClose(aura::Window* window) {
ServerWindow* server_window = ServerWindow::GetMayBeNull(window);
DCHECK(window && server_window->IsTopLevel());
......
......@@ -6,6 +6,7 @@
#define SERVICES_UI_WS2_WINDOW_SERVICE_H_
#include <memory>
#include <set>
#include "base/component_export.h"
#include "base/macros.h"
......@@ -81,6 +82,11 @@ class COMPONENT_EXPORT(WINDOW_SERVICE) WindowService
aura::client::FocusClient* focus_client() { return focus_client_; }
const std::set<WindowTree*>& window_trees() const { return window_trees_; }
// Called when a WindowServiceClient is about to be destroyed.
void OnWillDestroyWindowTree(WindowTree* tree);
// Asks the client that created |window| to close |window|. |window| must be
// a top-level window.
void RequestClose(aura::Window* window);
......@@ -130,6 +136,9 @@ class COMPONENT_EXPORT(WINDOW_SERVICE) WindowService
IMERegistrarImpl ime_registrar_;
IMEDriverBridge ime_driver_;
// All WindowTrees created by the WindowService.
std::set<WindowTree*> window_trees_;
DISALLOW_COPY_AND_ASSIGN(WindowService);
};
......
......@@ -53,6 +53,8 @@ class WindowServiceTestSetup {
WindowTree* window_tree() { return window_tree_.get(); }
WindowService* service() { return service_.get(); }
private:
base::test::ScopedTaskEnvironment task_environment_{
base::test::ScopedTaskEnvironment::MainThreadType::UI};
......
This diff is collapsed.
......@@ -87,6 +87,11 @@ class COMPONENT_EXPORT(WINDOW_SERVICE) WindowTree
// Asks the client to close |window|. |window| must be a top-level window.
void RequestClose(ServerWindow* window);
// Called when an Embedding is destroyed. This is only called for Embeddings
// that do not own the WindowTree (see Embedding for more details on when this
// happens).
void OnEmbeddingDestroyed(Embedding* embedding);
WindowService* window_service() { return window_service_; }
private:
......@@ -128,9 +133,7 @@ class COMPONENT_EXPORT(WINDOW_SERVICE) WindowTree
// Creates a new ClientRoot. The returned ClientRoot is owned by this.
// |is_top_level| is true if this is called from
// WindowTree::NewTopLevelWindow().
ClientRoot* CreateClientRoot(aura::Window* window,
bool is_top_level,
mojom::WindowTreePtr window_tree);
ClientRoot* CreateClientRoot(aura::Window* window, bool is_top_level);
void DeleteClientRoot(ClientRoot* client_root, DeleteClientRootReason reason);
void DeleteClientRootWithRoot(aura::Window* window);
......@@ -205,14 +208,23 @@ class COMPONENT_EXPORT(WINDOW_SERVICE) WindowTree
const std::vector<aura::Window*>& windows);
mojom::WindowDataPtr WindowToWindowData(aura::Window* window);
// Called before |window| becomes the root Window of a ClientRoot. This
// destroys the existing ClientRoot if there is one (because a Window can
// only be the root Window of a single ClientRoot).
void OnWillBecomeClientRootWindow(aura::Window* window);
// Methods with the name Impl() mirror those of mojom::WindowTree. The return
// value indicates whether they succeeded or not. Generally failure means the
// operation was not allowed.
// Call to complete a previous call to ScheduleEmbedForExistingClient().
void CompleteScheduleEmbedForExistingClient(
aura::Window* window,
const ClientWindowId& id,
const base::UnguessableToken& token);
// Returns the WindowTreeClient previously scheduled for an embed with the
// given |token| from ScheduleEmbed(). If this client is the result of an
// Embed() and ScheduleEmbed() was not called on this client, then this
// recurses to the parent WindowTree. Recursing enables an ancestor to call
// ScheduleEmbed() and the ancestor to communicate the token with the client.
mojom::WindowTreeClientPtr GetAndRemoveScheduledEmbedWindowTreeClient(
const base::UnguessableToken& token);
// Methods with the name Impl() mirror those of mojom::WindowTree. The
// return value indicates whether they succeeded or not. Generally failure
// means the operation was not allowed.
bool NewWindowImpl(
const ClientWindowId& client_window_id,
const std::map<std::string, std::vector<uint8_t>>& properties);
......@@ -331,7 +343,7 @@ class COMPONENT_EXPORT(WINDOW_SERVICE) WindowTree
void ScheduleEmbedForExistingClient(
uint32_t window_id,
ScheduleEmbedForExistingClientCallback callback) override;
void EmbedUsingToken(Id window_id,
void EmbedUsingToken(Id transport_window_id,
const base::UnguessableToken& token,
uint32_t embed_flags,
EmbedUsingTokenCallback callback) override;
......@@ -412,6 +424,20 @@ class COMPONENT_EXPORT(WINDOW_SERVICE) WindowTree
FocusHandler focus_handler_{this};
// Holds WindowTreeClients passed to ScheduleEmbed(). Entries are removed
// when EmbedUsingToken() is called.
using ScheduledEmbeds =
base::flat_map<base::UnguessableToken, mojom::WindowTreeClientPtr>;
ScheduledEmbeds scheduled_embeds_;
// Calls to ScheduleEmbedForExistingClient() add an entry here. The value is
// the value supplied to ScheduleEmbedForExistingClient(). When a matching
// EmbedUsingToken() is received, the value in the map is used as the id for
// the window in this client (specifically |ClientWindowId.sink_id|).
using ScheduledEmbedsForExistingClient =
base::flat_map<base::UnguessableToken, ClientSpecificId>;
ScheduledEmbedsForExistingClient scheduled_embeds_for_existing_client_;
// Events that an ack is expected from the client are added here.
std::queue<std::unique_ptr<InFlightKeyEvent>> in_flight_key_events_;
......
......@@ -10,6 +10,7 @@
#include <memory>
#include <queue>
#include "base/run_loop.h"
#include "base/unguessable_token.h"
#include "services/ui/public/cpp/property_type_converters.h"
#include "services/ui/public/interfaces/window_manager.mojom.h"
......@@ -33,6 +34,10 @@ namespace ui {
namespace ws2 {
namespace {
// Passed to Embed() to give the default behavior (see kEmbedFlag* in mojom for
// details).
constexpr uint32_t kDefaultEmbedFlags = 0;
class TestLayoutManager : public aura::LayoutManager {
public:
TestLayoutManager() = default;
......@@ -63,6 +68,20 @@ class TestLayoutManager : public aura::LayoutManager {
DISALLOW_COPY_AND_ASSIGN(TestLayoutManager);
};
// Used as callback from ScheduleEmbed().
void ScheduleEmbedCallback(base::UnguessableToken* result_token,
const base::UnguessableToken& actual_token) {
*result_token = actual_token;
}
// Used as callback to EmbedUsingToken().
void EmbedUsingTokenCallback(bool* was_called,
bool* result_value,
bool actual_result) {
*was_called = true;
*result_value = actual_result;
}
TEST(WindowTreeTest2, NewWindow) {
WindowServiceTestSetup setup;
EXPECT_TRUE(setup.changes()->empty());
......@@ -988,6 +1007,177 @@ TEST(WindowTreeTest2, Embed) {
EXPECT_EQ(CHANGE_TYPE_FRAME_SINK_ID_ALLOCATED, (*setup.changes())[0].type);
}
// Base class for ScheduleEmbed() related tests. This creates a Window and
// prepares a secondary client (|embed_client_|) that is intended to be embedded
// at some point.
class WindowTreeScheduleEmbedTest : public testing::Test {
public:
WindowTreeScheduleEmbedTest() = default;
~WindowTreeScheduleEmbedTest() override = default;
// testing::Test:
void SetUp() override {
testing::Test::SetUp();
setup_ = std::make_unique<WindowServiceTestSetup>();
embed_binding_.Bind(mojo::MakeRequest(&embed_client_ptr_));
window_ = setup_->window_tree_test_helper()->NewWindow();
ASSERT_TRUE(window_);
}
void TearDown() override {
window_ = nullptr;
embed_binding_.Close();
setup_.reset();
testing::Test::TearDown();
}
protected:
std::unique_ptr<WindowServiceTestSetup> setup_;
TestWindowTreeClient embed_client_;
mojom::WindowTreeClientPtr embed_client_ptr_;
aura::Window* window_ = nullptr;
private:
mojo::Binding<mojom::WindowTreeClient> embed_binding_{&embed_client_};
DISALLOW_COPY_AND_ASSIGN(WindowTreeScheduleEmbedTest);
};
TEST_F(WindowTreeScheduleEmbedTest, ScheduleEmbedWithUnregisteredToken) {
bool embed_result = false;
bool embed_callback_called = false;
setup_->window_tree_test_helper()->window_tree()->EmbedUsingToken(
setup_->window_tree_test_helper()->TransportIdForWindow(window_),
base::UnguessableToken::Create(), kDefaultEmbedFlags,
base::BindOnce(&EmbedUsingTokenCallback, &embed_callback_called,
&embed_result));
EXPECT_TRUE(embed_callback_called);
// ScheduleEmbed() with an invalid token should fail.
EXPECT_FALSE(embed_result);
}
TEST_F(WindowTreeScheduleEmbedTest, ScheduleEmbedRegisteredTokenInvalidWindow) {
// Register a token for embedding.
base::UnguessableToken token;
setup_->window_tree_test_helper()->window_tree()->ScheduleEmbed(
std::move(embed_client_ptr_),
base::BindOnce(&ScheduleEmbedCallback, &token));
EXPECT_FALSE(token.is_empty());
bool embed_result = false;
bool embed_callback_called = false;
setup_->window_tree_test_helper()->window_tree()->EmbedUsingToken(
kInvalidTransportId, token, kDefaultEmbedFlags,
base::BindOnce(&EmbedUsingTokenCallback, &embed_callback_called,
&embed_result));
EXPECT_TRUE(embed_callback_called);
// ScheduleEmbed() with a valid token, but invalid window should fail.
EXPECT_FALSE(embed_result);
}
TEST_F(WindowTreeScheduleEmbedTest, ScheduleEmbed) {
base::UnguessableToken token;
// ScheduleEmbed() with a valid token and valid window.
setup_->window_tree_test_helper()->window_tree()->ScheduleEmbed(
std::move(embed_client_ptr_),
base::BindOnce(&ScheduleEmbedCallback, &token));
EXPECT_FALSE(token.is_empty());
bool embed_result = false;
bool embed_callback_called = false;
setup_->window_tree_test_helper()->window_tree()->EmbedUsingToken(
setup_->window_tree_test_helper()->TransportIdForWindow(window_), token,
kDefaultEmbedFlags,
base::BindOnce(&EmbedUsingTokenCallback, &embed_callback_called,
&embed_result));
EXPECT_TRUE(embed_callback_called);
EXPECT_TRUE(embed_result);
base::RunLoop().RunUntilIdle();
// The embedded client should get OnEmbed().
EXPECT_EQ("OnEmbed",
SingleChangeToDescription(*embed_client_.tracker()->changes()));
}
TEST(WindowTreeTest2, ScheduleEmbedForExistingClient) {
WindowServiceTestSetup setup;
// Schedule an embed in the tree created by |setup|.
base::UnguessableToken token;
const uint32_t window_id_in_child = 149;
setup.window_tree_test_helper()
->window_tree()
->ScheduleEmbedForExistingClient(
window_id_in_child, base::BindOnce(&ScheduleEmbedCallback, &token));
EXPECT_FALSE(token.is_empty());
// Create another client and a window.
TestWindowTreeClient client2;
std::unique_ptr<WindowTree> tree2 =
setup.service()->CreateWindowTree(&client2);
ASSERT_TRUE(tree2);
WindowTreeTestHelper tree2_test_helper(tree2.get());
aura::Window* window_in_parent = tree2_test_helper.NewWindow();
ASSERT_TRUE(window_in_parent);
// Call EmbedUsingToken() from tree2, which should result in the tree from
// |setup| getting OnEmbedFromToken().
bool embed_result = false;
bool embed_callback_called = false;
WindowTreeTestHelper(tree2.get())
.window_tree()
->EmbedUsingToken(
tree2_test_helper.TransportIdForWindow(window_in_parent), token,
kDefaultEmbedFlags,
base::BindOnce(&EmbedUsingTokenCallback, &embed_callback_called,
&embed_result));
EXPECT_TRUE(embed_callback_called);
EXPECT_TRUE(embed_result);
EXPECT_EQ("OnEmbedFromToken", SingleChangeToDescription(*setup.changes()));
EXPECT_EQ(
static_cast<Id>(window_id_in_child),
setup.window_tree_test_helper()->TransportIdForWindow(window_in_parent));
}
TEST(WindowTreeTest2, DeleteRootOfEmbeddingFromScheduleEmbedForExistingClient) {
WindowServiceTestSetup setup;
aura::Window* window_in_parent = setup.window_tree_test_helper()->NewWindow();
ASSERT_TRUE(window_in_parent);
// Create another client.
TestWindowTreeClient client2;
std::unique_ptr<WindowTree> tree2 =
setup.service()->CreateWindowTree(&client2);
WindowTreeTestHelper tree2_test_helper(tree2.get());
base::UnguessableToken token;
tree2_test_helper.window_tree()->ScheduleEmbedForExistingClient(
11, base::BindOnce(&ScheduleEmbedCallback, &token));
EXPECT_FALSE(token.is_empty());
// Call EmbedUsingToken() from setup.window_tree(), which should result in
// |tree2| getting OnEmbedFromToken().
bool embed_result = false;
bool embed_callback_called = false;
setup.window_tree_test_helper()->window_tree()->EmbedUsingToken(
setup.window_tree_test_helper()->TransportIdForWindow(window_in_parent),
token, kDefaultEmbedFlags,
base::BindOnce(&EmbedUsingTokenCallback, &embed_callback_called,
&embed_result));
EXPECT_TRUE(embed_callback_called);
EXPECT_TRUE(embed_result);
EXPECT_EQ("OnEmbedFromToken",
SingleChangeToDescription(*client2.tracker()->changes()));
client2.tracker()->changes()->clear();
// Delete |window_in_parent|, which should trigger notifying tree2.
setup.window_tree_test_helper()->DeleteWindow(window_in_parent);
window_in_parent = nullptr;
// 11 is the same value supplied to ScheduleEmbedForExistingClient().
EXPECT_EQ("WindowDeleted window=0,11",
SingleChangeToDescription(*client2.tracker()->changes()));
}
TEST(WindowTreeTest2, StackAtTop) {
WindowServiceTestSetup setup;
aura::Window* top_level1 =
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment