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

chromeos: expose child windows to client at embed point

When a client embeds another client it can choose to intercept events
by specifying kEmbedFlagEmbedderInterceptsEvents. When this happens
events targetted at windows created by the embedded client go to the
embedder. When this happens the embedder needs to know about the
windows created by the embedded client, otherwise it has no way to
know the real target.

This patch makes it so that if kEmbedFlagEmbedderInterceptsEvents was
specified (and the client was not created by way of Embed()) then the
embedder client sees all windows created by any embedded client. The
embedder can still not operate on the windows, but it sees the
windows.

In terms of chrome this makes it so that browser sees the windows
created by renderers. Renderers can not see any windows created by
other renderers (even ones embedded in renderers).

The long term goal is to get rid of
kEmbedFlagEmbedderInterceptsEvents, at which point this will be removed.

BUG=781391
TEST=covered by test

Change-Id: I110d3205cd3b9c3bfa0e78760dd58b517fa43856
Reviewed-on: https://chromium-review.googlesource.com/760797
Commit-Queue: Scott Violet <sky@chromium.org>
Reviewed-by: default avatarElliot Glaysher <erg@chromium.org>
Reviewed-by: default avatarTom Sepez <tsepez@chromium.org>
Cr-Commit-Position: refs/heads/master@{#516050}
parent c57e7dd4
...@@ -34,6 +34,10 @@ class AccessPolicyDelegate { ...@@ -34,6 +34,10 @@ class AccessPolicyDelegate {
virtual bool IsWindowCreatedByWindowManager( virtual bool IsWindowCreatedByWindowManager(
const ServerWindow* window) const = 0; const ServerWindow* window) const = 0;
// Returns true if the tree intercepts events targetted at |window|.
virtual bool ShouldInterceptEventsForAccessPolicy(
const ServerWindow* window) const = 0;
protected: protected:
virtual ~AccessPolicyDelegate() {} virtual ~AccessPolicyDelegate() {}
}; };
......
...@@ -82,14 +82,16 @@ bool DefaultAccessPolicy::CanDeleteWindow(const ServerWindow* window) const { ...@@ -82,14 +82,16 @@ bool DefaultAccessPolicy::CanDeleteWindow(const ServerWindow* window) const {
bool DefaultAccessPolicy::CanGetWindowTree(const ServerWindow* window) const { bool DefaultAccessPolicy::CanGetWindowTree(const ServerWindow* window) const {
return WasCreatedByThisClient(window) || return WasCreatedByThisClient(window) ||
delegate_->HasRootForAccessPolicy(window); delegate_->HasRootForAccessPolicy(window) ||
delegate_->ShouldInterceptEventsForAccessPolicy(window);
} }
bool DefaultAccessPolicy::CanDescendIntoWindowForWindowTree( bool DefaultAccessPolicy::CanDescendIntoWindowForWindowTree(
const ServerWindow* window) const { const ServerWindow* window) const {
return (WasCreatedByThisClient(window) && return (WasCreatedByThisClient(window) &&
!delegate_->IsWindowRootOfAnotherTreeForAccessPolicy(window)) || !delegate_->IsWindowRootOfAnotherTreeForAccessPolicy(window)) ||
delegate_->HasRootForAccessPolicy(window); delegate_->HasRootForAccessPolicy(window) ||
delegate_->ShouldInterceptEventsForAccessPolicy(window);
} }
bool DefaultAccessPolicy::CanEmbed(const ServerWindow* window) const { bool DefaultAccessPolicy::CanEmbed(const ServerWindow* window) const {
...@@ -210,8 +212,33 @@ bool DefaultAccessPolicy::ShouldNotifyOnHierarchyChange( ...@@ -210,8 +212,33 @@ bool DefaultAccessPolicy::ShouldNotifyOnHierarchyChange(
const ServerWindow* window, const ServerWindow* window,
const ServerWindow** new_parent, const ServerWindow** new_parent,
const ServerWindow** old_parent) const { const ServerWindow** old_parent) const {
if (!WasCreatedByThisClient(window)) if (!WasCreatedByThisClient(window)) {
// The window may have been exposed to the client because of
// ShouldInterceptEventsForAccessPolicy(). Notify the parent in this case,
// but the |old_parent| and/or |new_parent| may need to be updated.
if (delegate_->IsWindowKnownForAccessPolicy(window)) {
if (*old_parent && !delegate_->IsWindowKnownForAccessPolicy(*old_parent))
*old_parent = nullptr;
if (*new_parent &&
(!delegate_->IsWindowKnownForAccessPolicy(*new_parent) &&
delegate_->ShouldInterceptEventsForAccessPolicy(*new_parent))) {
*new_parent = nullptr;
}
return true;
}
// Let the client know about |window| if the client intercepts events for
// |new_parent|.
if (*new_parent &&
delegate_->ShouldInterceptEventsForAccessPolicy(*new_parent)) {
if (*old_parent &&
!delegate_->ShouldInterceptEventsForAccessPolicy(*old_parent)) {
*old_parent = nullptr;
}
return true;
}
return false; return false;
}
if (*new_parent && !WasCreatedByThisClient(*new_parent) && if (*new_parent && !WasCreatedByThisClient(*new_parent) &&
!delegate_->HasRootForAccessPolicy((*new_parent))) { !delegate_->HasRootForAccessPolicy((*new_parent))) {
......
...@@ -515,6 +515,20 @@ TestWindowServerDelegate::TestWindowServerDelegate() ...@@ -515,6 +515,20 @@ TestWindowServerDelegate::TestWindowServerDelegate()
base::MakeUnique<TestThreadedImageCursorsFactory>()) {} base::MakeUnique<TestThreadedImageCursorsFactory>()) {}
TestWindowServerDelegate::~TestWindowServerDelegate() {} TestWindowServerDelegate::~TestWindowServerDelegate() {}
TestWindowTreeBinding* TestWindowServerDelegate::Embed(WindowTree* tree,
ServerWindow* window,
int flags) {
mojom::WindowTreeClientPtr client;
mojom::WindowTreeClientRequest client_request = mojo::MakeRequest(&client);
ClientWindowId client_window_id;
if (!tree->IsWindowKnown(window, &client_window_id))
return nullptr;
tree->Embed(client_window_id, std::move(client), flags);
last_client()->Bind(std::move(client_request));
return last_binding();
}
void TestWindowServerDelegate::StartDisplayInit() {} void TestWindowServerDelegate::StartDisplayInit() {}
void TestWindowServerDelegate::OnNoMoreDisplays() { void TestWindowServerDelegate::OnNoMoreDisplays() {
...@@ -802,7 +816,8 @@ ServerWindow* NewWindowInTree(WindowTree* tree, ClientWindowId* client_id) { ...@@ -802,7 +816,8 @@ ServerWindow* NewWindowInTree(WindowTree* tree, ClientWindowId* client_id) {
ServerWindow* NewWindowInTreeWithParent(WindowTree* tree, ServerWindow* NewWindowInTreeWithParent(WindowTree* tree,
ServerWindow* parent, ServerWindow* parent,
ClientWindowId* client_id) { ClientWindowId* client_id,
const gfx::Rect& bounds) {
if (!parent) if (!parent)
return nullptr; return nullptr;
ClientWindowId parent_client_id; ClientWindowId parent_client_id;
...@@ -815,9 +830,11 @@ ServerWindow* NewWindowInTreeWithParent(WindowTree* tree, ...@@ -815,9 +830,11 @@ ServerWindow* NewWindowInTreeWithParent(WindowTree* tree,
return nullptr; return nullptr;
if (!tree->AddWindow(parent_client_id, client_window_id)) if (!tree->AddWindow(parent_client_id, client_window_id))
return nullptr; return nullptr;
ServerWindow* window = tree->GetWindowByClientId(client_window_id);
window->SetBounds(bounds);
if (client_id) if (client_id)
*client_id = client_window_id; *client_id = client_window_id;
return tree->GetWindowByClientId(client_window_id); return window;
} }
gfx::Point Atomic32ToPoint(base::subtle::Atomic32 atomic) { gfx::Point Atomic32ToPoint(base::subtle::Atomic32 atomic) {
......
...@@ -643,6 +643,12 @@ class TestWindowServerDelegate : public WindowServerDelegate { ...@@ -643,6 +643,12 @@ class TestWindowServerDelegate : public WindowServerDelegate {
bool got_on_no_more_displays() const { return got_on_no_more_displays_; } bool got_on_no_more_displays() const { return got_on_no_more_displays_; }
// Does an Embed() in |tree| at |window| returning the TestWindowTreeBinding
// that resulred (null on failure).
TestWindowTreeBinding* Embed(WindowTree* tree,
ServerWindow* window,
int flags = 0);
// WindowServerDelegate: // WindowServerDelegate:
void StartDisplayInit() override; void StartDisplayInit() override;
void OnNoMoreDisplays() override; void OnNoMoreDisplays() override;
...@@ -734,6 +740,10 @@ class WindowEventTargetingHelper { ...@@ -734,6 +740,10 @@ class WindowEventTargetingHelper {
TestWindowTreeClient* wm_client() { return wm_client_; } TestWindowTreeClient* wm_client() { return wm_client_; }
WindowServer* window_server() { return ws_test_helper_.window_server(); } WindowServer* window_server() { return ws_test_helper_.window_server(); }
TestWindowServerDelegate* test_window_server_delegate() {
return ws_test_helper_.window_server_delegate();
}
private: private:
WindowServerTestHelper ws_test_helper_; WindowServerTestHelper ws_test_helper_;
// TestWindowTreeClient that is used for the WM client. Owned by // TestWindowTreeClient that is used for the WM client. Owned by
...@@ -872,7 +882,8 @@ ServerWindow* NewWindowInTree(WindowTree* tree, ...@@ -872,7 +882,8 @@ ServerWindow* NewWindowInTree(WindowTree* tree,
ClientWindowId* client_id = nullptr); ClientWindowId* client_id = nullptr);
ServerWindow* NewWindowInTreeWithParent(WindowTree* tree, ServerWindow* NewWindowInTreeWithParent(WindowTree* tree,
ServerWindow* parent, ServerWindow* parent,
ClientWindowId* client_id = nullptr); ClientWindowId* client_id = nullptr,
const gfx::Rect& bounds = gfx::Rect());
// Converts an atomic 32 to a point. The cursor location is represented as an // Converts an atomic 32 to a point. The cursor location is represented as an
// atomic 32. // atomic 32.
......
...@@ -1224,6 +1224,32 @@ bool WindowTree::CanReorderWindow(const ServerWindow* window, ...@@ -1224,6 +1224,32 @@ bool WindowTree::CanReorderWindow(const ServerWindow* window,
return true; return true;
} }
bool WindowTree::RemoveWindowFromParent(
const ClientWindowId& client_window_id) {
ServerWindow* window = GetWindowByClientId(client_window_id);
DVLOG(3) << "removing window from parent client=" << id_
<< " client window_id= " << client_window_id
<< " global window_id=" << DebugWindowId(window);
if (!window) {
DVLOG(1) << "RemoveWindowFromParent failed (invalid window id="
<< client_window_id.ToString() << ")";
return false;
}
if (!window->parent()) {
DVLOG(1) << "RemoveWindowFromParent failed (no parent id="
<< client_window_id.ToString() << ")";
return false;
}
if (!access_policy_->CanRemoveWindowFromParent(window)) {
DVLOG(1) << "RemoveWindowFromParent failed (access policy disallowed id="
<< client_window_id.ToString() << ")";
return false;
}
Operation op(this, window_server_, OperationType::REMOVE_WINDOW_FROM_PARENT);
window->parent()->Remove(window);
return true;
}
bool WindowTree::DeleteWindowImpl(WindowTree* source, ServerWindow* window) { bool WindowTree::DeleteWindowImpl(WindowTree* source, ServerWindow* window) {
DCHECK(window); DCHECK(window);
DCHECK_EQ(window->id().client_id, id_); DCHECK_EQ(window->id().client_id, id_);
...@@ -1588,27 +1614,8 @@ void WindowTree::AddWindow(uint32_t change_id, Id parent_id, Id child_id) { ...@@ -1588,27 +1614,8 @@ void WindowTree::AddWindow(uint32_t change_id, Id parent_id, Id child_id) {
} }
void WindowTree::RemoveWindowFromParent(uint32_t change_id, Id window_id) { void WindowTree::RemoveWindowFromParent(uint32_t change_id, Id window_id) {
bool success = false; client()->OnChangeCompleted(
ServerWindow* window = GetWindowByClientId(MakeClientWindowId(window_id)); change_id, RemoveWindowFromParent(MakeClientWindowId(window_id)));
DVLOG(3) << "removing window from parent client=" << id_
<< " client window_id= " << window_id
<< " global window_id=" << DebugWindowId(window);
if (!window) {
DVLOG(1) << "RemoveWindowFromParent failed (invalid window id=" << change_id
<< ")";
} else if (!window->parent()) {
DVLOG(1) << "RemoveWindowFromParent failed (no parent id=" << change_id
<< ")";
} else if (!access_policy_->CanRemoveWindowFromParent(window)) {
DVLOG(1) << "RemoveWindowFromParent failed (access policy disallowed id="
<< change_id << ")";
} else {
success = true;
Operation op(this, window_server_,
OperationType::REMOVE_WINDOW_FROM_PARENT);
window->parent()->Remove(window);
}
client()->OnChangeCompleted(change_id, success);
} }
void WindowTree::AddTransientWindow(uint32_t change_id, void WindowTree::AddTransientWindow(uint32_t change_id,
...@@ -2682,6 +2689,30 @@ bool WindowTree::IsWindowCreatedByWindowManager( ...@@ -2682,6 +2689,30 @@ bool WindowTree::IsWindowCreatedByWindowManager(
window->id().client_id; window->id().client_id;
} }
bool WindowTree::ShouldInterceptEventsForAccessPolicy(
const ServerWindow* window) const {
// Indicates the tree was created as the result of an Embed().
if (user_id_.empty())
return false;
while (window) {
// Find the first window created by this client. If there is an embedding,
// it'll be here.
if (window->id().client_id == id_) {
// embedded_tree->embedder_intercepts_events() indicates Embed() was
// called with kEmbedFlagEmbedderInterceptsEvents. In this case the
// embedder needs to see the window so that it knows the event is
// targetted at it.
WindowTree* embedded_tree = window_server_->GetTreeWithRoot(window);
if (embedded_tree && embedded_tree->embedder_intercepts_events())
return true;
return false;
}
window = window->parent();
}
return false;
}
void WindowTree::OnDragMoved(const gfx::Point& location) { void WindowTree::OnDragMoved(const gfx::Point& location) {
DCHECK(window_server_->in_drag_loop()); DCHECK(window_server_->in_drag_loop());
DCHECK_EQ(this, window_server_->GetCurrentDragLoopInitiator()); DCHECK_EQ(this, window_server_->GetCurrentDragLoopInitiator());
......
...@@ -188,6 +188,7 @@ class WindowTree : public mojom::WindowTree, ...@@ -188,6 +188,7 @@ class WindowTree : public mojom::WindowTree,
const ClientWindowId& child_id); const ClientWindowId& child_id);
bool AddTransientWindow(const ClientWindowId& window_id, bool AddTransientWindow(const ClientWindowId& window_id,
const ClientWindowId& transient_window_id); const ClientWindowId& transient_window_id);
bool RemoveWindowFromParent(const ClientWindowId& window_id);
bool DeleteWindow(const ClientWindowId& window_id); bool DeleteWindow(const ClientWindowId& window_id);
bool SetModalType(const ClientWindowId& window_id, ModalType modal_type); bool SetModalType(const ClientWindowId& window_id, ModalType modal_type);
bool SetChildModalParent(const ClientWindowId& window_id, bool SetChildModalParent(const ClientWindowId& window_id,
...@@ -625,6 +626,8 @@ class WindowTree : public mojom::WindowTree, ...@@ -625,6 +626,8 @@ class WindowTree : public mojom::WindowTree,
const ServerWindow* window) const override; const ServerWindow* window) const override;
bool IsWindowCreatedByWindowManager( bool IsWindowCreatedByWindowManager(
const ServerWindow* window) const override; const ServerWindow* window) const override;
bool ShouldInterceptEventsForAccessPolicy(
const ServerWindow* window) const override;
// DragSource: // DragSource:
void OnDragMoved(const gfx::Point& location) override; void OnDragMoved(const gfx::Point& location) override;
......
...@@ -204,6 +204,10 @@ class WindowTreeTest : public testing::Test { ...@@ -204,6 +204,10 @@ class WindowTreeTest : public testing::Test {
return CreateTreeViaFactory(window_server(), user_id, binding); return CreateTreeViaFactory(window_server(), user_id, binding);
} }
TestWindowServerDelegate* test_window_server_delegate() {
return window_event_targeting_helper_.test_window_server_delegate();
}
protected: protected:
WindowEventTargetingHelper window_event_targeting_helper_; WindowEventTargetingHelper window_event_targeting_helper_;
...@@ -2093,6 +2097,81 @@ TEST_F(WindowTreeTest, PerformWmAction) { ...@@ -2093,6 +2097,81 @@ TEST_F(WindowTreeTest, PerformWmAction) {
EXPECT_EQ("test-action", wm_internal.last_wm_action()); EXPECT_EQ("test-action", wm_internal.last_wm_action());
} }
TEST_F(WindowTreeTest, EmbedderInterceptsEventsSeesWindowsInEmbeddedClients) {
// Make the root visible and give it bounds.
ServerWindow* wm_root = FirstRoot(wm_tree());
ASSERT_TRUE(wm_root);
const gfx::Rect bounds(0, 0, 20, 20);
wm_root->SetBounds(bounds);
wm_root->SetVisible(true);
// Create window for embedded (|w1|).
ClientWindowId w1_id;
ServerWindow* w1 =
NewWindowInTreeWithParent(wm_tree(), wm_root, &w1_id, bounds);
ASSERT_TRUE(w1);
// Embed a new client in |w1|.
TestWindowTreeBinding* embed_binding1 =
test_window_server_delegate()->Embed(wm_tree(), w1);
// Set the user-id to a non-empty string, this way
// kEmbedFlagEmbedderInterceptsEvents is honored.
WindowTreeTestApi(embed_binding1->tree()).set_user_id("x");
ASSERT_TRUE(embed_binding1);
// Create |w2| (in the embedded tree).
ClientWindowId w2_id;
ServerWindow* w2 =
NewWindowInTreeWithParent(embed_binding1->tree(), w1, &w2_id, bounds);
ASSERT_TRUE(w2);
// Embed a new client in |w2|.
TestWindowTreeBinding* embed_binding2 = test_window_server_delegate()->Embed(
embed_binding1->tree(), w2, mojom::kEmbedFlagEmbedderInterceptsEvents);
ASSERT_TRUE(embed_binding2);
// Create |w3| as a child of |w2|.
ClientWindowId w3_id;
ServerWindow* w3 =
NewWindowInTreeWithParent(embed_binding2->tree(), w2, &w3_id, bounds);
ASSERT_TRUE(w3);
// Embed a new client in |w3|.
TestWindowTreeBinding* embed_binding3 =
test_window_server_delegate()->Embed(embed_binding2->tree(), w3);
ASSERT_TRUE(embed_binding3);
// Create |w4| as a child of |w3|.
ClientWindowId w4_id;
ServerWindow* w4 =
NewWindowInTreeWithParent(embed_binding3->tree(), w3, &w4_id, bounds);
ASSERT_TRUE(w4);
// |w4| and |w3| should be known to embed_binding1->tree() because of
// kEmbedFlagEmbedderInterceptsEvents. |w3| should not be known to
// embed_binding2->tree(), because it has an invalid user id.
EXPECT_TRUE(embed_binding1->tree()->IsWindowKnown(w3, nullptr));
ClientWindowId w4_in_tree1_id;
EXPECT_TRUE(embed_binding1->tree()->IsWindowKnown(w4, &w4_in_tree1_id));
EXPECT_FALSE(embed_binding2->tree()->IsWindowKnown(w4, nullptr));
// Verify an event targetting |w4| goes to embed_binding1->tree().
embed_binding1->client()->tracker()->changes()->clear();
AckPreviousEvent();
DispatchEventWithoutAck(CreatePointerDownEvent(5, 5));
WindowManagerStateTestApi wm_state_test_api(
wm_tree()->window_manager_state());
EXPECT_EQ(embed_binding1->tree(),
wm_state_test_api.tree_awaiting_input_ack());
// Event targets |w4|, but goes to embed_binding1->tree() (because of
// kEmbedFlagEmbedderInterceptsEvents).
EXPECT_EQ(1u, embed_binding1->client()->tracker()->changes()->size());
EXPECT_EQ("InputEvent window=" + ClientWindowIdToString(w4_in_tree1_id) +
" event_action=16",
SingleChangeToDescription(
*embed_binding1->client()->tracker()->changes()));
}
} // namespace test } // namespace test
} // namespace ws } // namespace ws
} // namespace ui } // namespace ui
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