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

chromeos: make KeyEvents go to the embedder if the embedder intercepts events

This is needed as KeyEvents are sent directly to the focused window, where as
LocatedEvents consider the WindowTargeter of each window.

BUG=837692
TEST=covered by tests

Change-Id: I935aaa1aaea6a8ea0339e99cc076ee7d459d9034
Reviewed-on: https://chromium-review.googlesource.com/1070857
Commit-Queue: Scott Violet <sky@chromium.org>
Reviewed-by: default avatarJames Cook <jamescook@chromium.org>
Cr-Commit-Position: refs/heads/master@{#561521}
parent 24290222
......@@ -66,9 +66,9 @@ class ClientWindowTargeter : public aura::WindowTargeter {
ui::Event* event) override {
aura::Window* window = static_cast<aura::Window*>(event_target);
DCHECK_EQ(window, client_window_->window());
if (client_window_->embedded_window_service_client() &&
client_window_->owning_window_service_client() &&
client_window_->owning_window_service_client()->intercepts_events()) {
if (client_window_->DoesOwnerInterceptEvents()) {
// If the owner intercepts events, then don't recurse (otherwise events
// would go to a descendant).
return event_target->CanAcceptEvent(*event) ? window : nullptr;
}
return aura::WindowTargeter::FindTargetForEvent(event_target, event);
......@@ -105,6 +105,9 @@ class ClientWindowEventHandler : public ui::EventHandler {
return;
}
if (HandleInterceptedKeyEvent(event))
return;
if (ShouldIgnoreEvent(*event))
return;
......@@ -156,6 +159,24 @@ class ClientWindowEventHandler : public ui::EventHandler {
}
private:
// If |event| is a KeyEvent that needs to be handled because a client
// intercepts events, then true is returned and the event is handled.
// Otherwise returns false.
bool HandleInterceptedKeyEvent(Event* event) {
if (!event->IsKeyEvent())
return false;
// KeyEvents do not go through through ClientWindowTargeter. As a result
// ClientWindowEventHandler has to check for a client intercepting events.
if (client_window_->DoesOwnerInterceptEvents()) {
client_window_->owning_window_service_client()->SendEventToClient(
window(), *event);
event->StopPropagation();
return true;
}
return false;
}
ClientWindow* const client_window_;
DISALLOW_COPY_AND_ASSIGN(ClientWindowEventHandler);
......@@ -345,6 +366,11 @@ bool ClientWindow::HasNonClientArea() const {
(!client_area_.IsEmpty() || !additional_client_areas_.empty());
}
bool ClientWindow::DoesOwnerInterceptEvents() const {
return HasEmbedding() && owning_window_service_client_ &&
owning_window_service_client_->intercepts_events();
}
bool ClientWindow::IsTopLevel() const {
return owning_window_service_client_ &&
owning_window_service_client_->IsTopLevel(window_);
......
......@@ -89,6 +89,15 @@ class COMPONENT_EXPORT(WINDOW_SERVICE) ClientWindow {
void set_focus_owner(WindowServiceClient* owner) { focus_owner_ = owner; }
WindowServiceClient* focus_owner() const { return focus_owner_; }
// Returns true if the window has an embedding, and the owning client
// intercepts events that would normally target descendants.
bool DoesOwnerInterceptEvents() const;
// Returns true if this window has a client embedded in it.
bool HasEmbedding() const {
return embedded_window_service_client_ != nullptr;
}
// Returns true if the window is a top-level window and there is at least some
// non-client area.
bool HasNonClientArea() const;
......
......@@ -9,6 +9,7 @@
#include <memory>
#include <queue>
#include "services/ui/public/interfaces/window_tree_constants.mojom.h"
#include "services/ui/ws2/event_test_utils.h"
#include "services/ui/ws2/window_service.h"
#include "services/ui/ws2/window_service_client_test_helper.h"
......@@ -147,6 +148,46 @@ TEST(FocusHandlerTest, FocusChangeFromEmbedded) {
EXPECT_TRUE(embedding->changes()->empty());
}
TEST(FocusHandlerTest, EmbedderGetsInterceptedKeyEvents) {
aura::test::TestWindowDelegate test_window_delegate;
const bool intercepts_events = true;
WindowServiceTestSetup setup(intercepts_events);
test_window_delegate.set_can_focus(true);
setup.delegate()->set_delegate_for_next_top_level(&test_window_delegate);
aura::Window* top_level = setup.client_test_helper()->NewTopLevelWindow(1);
ASSERT_TRUE(top_level);
top_level->Show();
aura::Window* embed_window = setup.client_test_helper()->NewWindow(3);
ASSERT_TRUE(embed_window);
top_level->AddChild(embed_window);
embed_window->Show();
std::unique_ptr<Embedding> embedding = setup.CreateEmbedding(
embed_window, mojom::kEmbedFlagEmbedderInterceptsEvents);
ASSERT_TRUE(embedding);
aura::Window* embed_child_window =
embedding->client_test_helper->NewWindow(4);
ASSERT_TRUE(embed_child_window);
embed_child_window->Show();
embed_window->AddChild(embed_child_window);
setup.changes()->clear();
embedding->changes()->clear();
// Set focus from the embedded client.
EXPECT_TRUE(embedding->client_test_helper->SetFocus(embed_child_window));
EXPECT_TRUE(embed_child_window->HasFocus());
// Generate a key-press. Even though focus is on a window in the embedded
// client, the event goes to the embedder because it intercepts events.
test::EventGenerator event_generator(setup.root());
event_generator.PressKey(VKEY_A, EF_NONE);
EXPECT_TRUE(embedding->changes()->empty());
EXPECT_TRUE(embedding->window_tree_client.input_events().empty());
EXPECT_EQ("KEY_PRESSED",
EventToEventType(
setup.window_tree_client()->PopInputEvent().event.get()));
}
} // namespace
} // namespace ws2
} // namespace ui
......@@ -92,7 +92,7 @@ void WindowServiceClientTestHelper::SetWindowProperty(
change_id, TransportIdForWindow(window), name, value);
}
WindowServiceClient* WindowServiceClientTestHelper::Embed(
WindowServiceClientBinding* WindowServiceClientTestHelper::Embed(
aura::Window* window,
mojom::WindowTreeClientPtr client_ptr,
mojom::WindowTreeClient* client,
......@@ -103,8 +103,7 @@ WindowServiceClient* WindowServiceClientTestHelper::Embed(
std::move(client_ptr), client, embed_flags)) {
return nullptr;
}
return window_service_client_->embedded_client_bindings_.back()
->window_service_client();
return window_service_client_->embedded_client_bindings_.back().get();
}
void WindowServiceClientTestHelper::SetEventTargetingPolicy(
......@@ -130,6 +129,12 @@ void WindowServiceClientTestHelper::SetCanFocus(aura::Window* window,
window_service_client_->TransportIdForWindow(window), can_focus);
}
void WindowServiceClientTestHelper::DestroyBinding(
WindowServiceClientBinding* binding) {
// Triggers WindowServiceClient to delete WindowServiceClientBinding.
window_service_client_->OnChildBindingConnectionLost(binding);
}
ClientWindowId WindowServiceClientTestHelper::ClientWindowIdForWindow(
aura::Window* window) {
return window_service_client_->MakeClientWindowId(
......
......@@ -35,6 +35,7 @@ enum class EventTargetingPolicy;
namespace ws2 {
class WindowServiceClient;
class WindowServiceClientBinding;
// Used for accessing private members of WindowServiceClient in tests.
class WindowServiceClientTestHelper {
......@@ -72,13 +73,13 @@ class WindowServiceClientTestHelper {
const std::vector<uint8_t>& value,
uint32_t change_id = 1);
// Creates a new embedding. On success the new WindowServiceClient is
// returned. The returned WindowServiceClient is owned by the
// Creates a new embedding. On success the new WindowServiceClientBinding is
// returned. The returned WindowServiceClientBinding is owned by the
// WindowServiceClient this was created with (|window_service_client_|).
WindowServiceClient* Embed(aura::Window* window,
mojom::WindowTreeClientPtr client_ptr,
mojom::WindowTreeClient* client,
uint32_t embed_flags = 0);
WindowServiceClientBinding* Embed(aura::Window* window,
mojom::WindowTreeClientPtr client_ptr,
mojom::WindowTreeClient* client,
uint32_t embed_flags = 0);
void SetEventTargetingPolicy(aura::Window* window,
mojom::EventTargetingPolicy policy);
bool SetFocus(aura::Window* window);
......@@ -86,6 +87,8 @@ class WindowServiceClientTestHelper {
Id TransportIdForWindow(aura::Window* window);
void DestroyBinding(WindowServiceClientBinding* binding);
private:
ClientWindowId ClientWindowIdForWindow(aura::Window* window);
......
......@@ -7,6 +7,7 @@
#include "services/ui/ws2/gpu_support.h"
#include "services/ui/ws2/window_service.h"
#include "services/ui/ws2/window_service_client.h"
#include "services/ui/ws2/window_service_client_binding.h"
#include "ui/gl/test/gl_surface_test_support.h"
#include "ui/wm/core/base_focus_rules.h"
#include "ui/wm/core/capture_controller.h"
......@@ -31,7 +32,7 @@ class TestFocusRules : public wm::BaseFocusRules {
} // namespace
WindowServiceTestSetup::WindowServiceTestSetup()
WindowServiceTestSetup::WindowServiceTestSetup(bool intercepts_events)
// FocusController takes ownership of TestFocusRules.
: focus_controller_(new TestFocusRules()) {
if (gl::GetGLImplementation() == gl::kGLImplementationNone)
......@@ -50,7 +51,6 @@ WindowServiceTestSetup::WindowServiceTestSetup()
aura::client::SetFocusClient(root(), focus_controller());
delegate_.set_top_level_parent(aura_test_helper_.root_window());
const bool intercepts_events = false;
window_service_client_ = service_->CreateWindowServiceClient(
&window_tree_client_, intercepts_events);
window_service_client_->InitFromFactory();
......@@ -72,19 +72,28 @@ std::unique_ptr<Embedding> WindowServiceTestSetup::CreateEmbedding(
aura::Window* embed_root,
uint32_t flags) {
std::unique_ptr<Embedding> embedding = std::make_unique<Embedding>();
embedding->window_service_client = client_test_helper_->Embed(
embedding->binding = client_test_helper_->Embed(
embed_root, nullptr, &embedding->window_tree_client, flags);
if (!embedding->window_service_client)
if (!embedding->binding)
return nullptr;
embedding->window_service_client =
embedding->binding->window_service_client();
embedding->client_test_helper =
std::make_unique<WindowServiceClientTestHelper>(
embedding->window_service_client);
embedding->parent_window_service_client = window_service_client_.get();
return embedding;
}
Embedding::Embedding() = default;
Embedding::~Embedding() = default;
Embedding::~Embedding() {
if (!binding)
return;
WindowServiceClientTestHelper(parent_window_service_client)
.DestroyBinding(binding);
}
} // namespace ws2
} // namespace ui
......@@ -25,6 +25,7 @@ namespace ws2 {
class WindowService;
class WindowServiceClient;
class WindowServiceClientBinding;
class WindowServiceClientTestHelper;
struct Embedding;
......@@ -32,7 +33,9 @@ struct Embedding;
// Helper to setup state needed for WindowService tests.
class WindowServiceTestSetup {
public:
WindowServiceTestSetup();
// See |WindowServiceClient::intercepts_events| for details on
// |intercepts_events|.
explicit WindowServiceTestSetup(bool intercepts_events = false);
~WindowServiceTestSetup();
// |flags| mirrors that from mojom::WindowTree::Embed(), see it for details.
......@@ -68,8 +71,6 @@ class WindowServiceTestSetup {
// Embedding contains the object necessary for an embedding. This is created
// by way of WindowServiceTestSetup::CreateEmbededing().
//
// NOTE: destroying this object does not destroy the embedding.
struct Embedding {
Embedding();
~Embedding();
......@@ -78,9 +79,16 @@ struct Embedding {
return window_tree_client.tracker()->changes();
}
// This is created by WindowServiceClient::Embed(). It owns
// |window_service_client_|.
WindowServiceClientBinding* binding = nullptr;
TestWindowTreeClient window_tree_client;
// NOTE: this is owned by the WindowServiceClient that Embed() was called on.
// The client Embed() was called on.
WindowServiceClient* parent_window_service_client = nullptr;
// NOTE: this is owned by |parent_window_service_client|.
WindowServiceClient* window_service_client = nullptr;
std::unique_ptr<WindowServiceClientTestHelper> client_test_helper;
......
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