diff options
author | jonross <jonross@chromium.org> | 2016-03-22 10:40:04 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-03-22 17:42:27 +0000 |
commit | a3e8ee8bca171e70a591d8ef4927cae08e9484cc (patch) | |
tree | e47d398719e5c8b0d07c84d5472f6cb1b50d2d19 | |
parent | 4dc1dc6668a87620574dd43392046c3691914da2 (diff) | |
download | chromium_src-a3e8ee8bca171e70a591d8ef4927cae08e9484cc.zip chromium_src-a3e8ee8bca171e70a591d8ef4927cae08e9484cc.tar.gz chromium_src-a3e8ee8bca171e70a591d8ef4927cae08e9484cc.tar.bz2 |
Mus Window Server Post Target Accelerators
Implement post-target accelerators in the mus window server.
EventDispatcher's EventMatcher class has been refactored out into Accelerator. This provides a WeakPtr, as accelerators can be deleted by the client while the window server is awaiting an event ack. We don't want to notify a now deleted accelerator.
Updated EventDispatcher to send post-targer accelerators to its delegate.
Updated WindowManagerState to track accelerators awaiting event acks. On unhandled event acks the accelerator is sent.
TEST=EventDispatcherTest, WindowManagerStateTest, manual testing of a post-target accelerator
BUG=560478
Review URL: https://codereview.chromium.org/1806413003
Cr-Commit-Position: refs/heads/master@{#382600}
-rw-r--r-- | components/mus/ws/BUILD.gn | 4 | ||||
-rw-r--r-- | components/mus/ws/accelerator.cc | 124 | ||||
-rw-r--r-- | components/mus/ws/accelerator.h | 71 | ||||
-rw-r--r-- | components/mus/ws/event_dispatcher.cc | 178 | ||||
-rw-r--r-- | components/mus/ws/event_dispatcher.h | 17 | ||||
-rw-r--r-- | components/mus/ws/event_dispatcher_delegate.h | 4 | ||||
-rw-r--r-- | components/mus/ws/event_dispatcher_unittest.cc | 47 | ||||
-rw-r--r-- | components/mus/ws/test_utils.cc | 24 | ||||
-rw-r--r-- | components/mus/ws/test_utils.h | 77 | ||||
-rw-r--r-- | components/mus/ws/window_manager_state.cc | 43 | ||||
-rw-r--r-- | components/mus/ws/window_manager_state.h | 15 | ||||
-rw-r--r-- | components/mus/ws/window_manager_state_unittest.cc | 357 | ||||
-rw-r--r-- | components/mus/ws/window_tree.cc | 2 | ||||
-rw-r--r-- | components/mus/ws/window_tree_unittest.cc | 58 |
14 files changed, 778 insertions, 243 deletions
diff --git a/components/mus/ws/BUILD.gn b/components/mus/ws/BUILD.gn index da53872..b2687b7 100644 --- a/components/mus/ws/BUILD.gn +++ b/components/mus/ws/BUILD.gn @@ -9,6 +9,8 @@ import("//mojo/public/mojo_application_manifest.gni") source_set("lib") { sources = [ + "accelerator.cc", + "accelerator.h", "access_policy.h", "access_policy_delegate.h", "default_access_policy.cc", @@ -161,6 +163,7 @@ test("mus_ws_unittests") { "window_coordinate_conversions_unittest.cc", "window_finder_unittest.cc", "window_manager_client_unittest.cc", + "window_manager_state_unittest.cc", "window_tree_client_unittest.cc", "window_tree_unittest.cc", ] @@ -170,6 +173,7 @@ test("mus_ws_unittests") { ":test_support", "//base", "//base/test:test_config", + "//base/test:test_support", "//cc:cc", "//components/mus/common:mus_common", "//components/mus/public/cpp", diff --git a/components/mus/ws/accelerator.cc b/components/mus/ws/accelerator.cc new file mode 100644 index 0000000..b27ad28 --- /dev/null +++ b/components/mus/ws/accelerator.cc @@ -0,0 +1,124 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/mus/ws/accelerator.h" + +#include "mojo/converters/geometry/geometry_type_converters.h" +#include "mojo/converters/input_events/input_events_type_converters.h" + +namespace mus { +namespace ws { + +Accelerator::Accelerator(uint32_t id, const mojom::EventMatcher& matcher) + : id_(id), + fields_to_match_(NONE), + accelerator_phase_(matcher.accelerator_phase), + event_type_(ui::ET_UNKNOWN), + event_flags_(ui::EF_NONE), + ignore_event_flags_(ui::EF_NONE), + keyboard_code_(ui::VKEY_UNKNOWN), + pointer_type_(ui::EventPointerType::POINTER_TYPE_UNKNOWN), + weak_factory_(this) { + if (matcher.type_matcher) { + fields_to_match_ |= TYPE; + switch (matcher.type_matcher->type) { + case mus::mojom::EventType::POINTER_DOWN: + event_type_ = ui::ET_POINTER_DOWN; + break; + case mus::mojom::EventType::POINTER_MOVE: + event_type_ = ui::ET_POINTER_MOVED; + break; + case mus::mojom::EventType::MOUSE_EXIT: + event_type_ = ui::ET_POINTER_EXITED; + break; + case mus::mojom::EventType::POINTER_UP: + event_type_ = ui::ET_POINTER_UP; + break; + case mus::mojom::EventType::POINTER_CANCEL: + event_type_ = ui::ET_POINTER_CANCELLED; + break; + case mus::mojom::EventType::KEY_PRESSED: + event_type_ = ui::ET_KEY_PRESSED; + break; + case mus::mojom::EventType::KEY_RELEASED: + event_type_ = ui::ET_KEY_RELEASED; + break; + default: + NOTREACHED(); + } + } + if (matcher.flags_matcher) { + fields_to_match_ |= FLAGS; + event_flags_ = matcher.flags_matcher->flags; + if (matcher.ignore_flags_matcher) + ignore_event_flags_ = matcher.ignore_flags_matcher->flags; + } + if (matcher.key_matcher) { + fields_to_match_ |= KEYBOARD_CODE; + keyboard_code_ = static_cast<uint16_t>(matcher.key_matcher->keyboard_code); + } + if (matcher.pointer_kind_matcher) { + fields_to_match_ |= POINTER_KIND; + switch (matcher.pointer_kind_matcher->pointer_kind) { + case mojom::PointerKind::MOUSE: + pointer_type_ = ui::EventPointerType::POINTER_TYPE_MOUSE; + break; + case mojom::PointerKind::TOUCH: + pointer_type_ = ui::EventPointerType::POINTER_TYPE_TOUCH; + break; + default: + NOTREACHED(); + } + } + if (matcher.pointer_location_matcher) { + fields_to_match_ |= POINTER_LOCATION; + pointer_region_ = matcher.pointer_location_matcher->region.To<gfx::RectF>(); + } +} + +Accelerator::~Accelerator() {} + +bool Accelerator::MatchesEvent(const ui::Event& event, + const mojom::AcceleratorPhase phase) const { + if (accelerator_phase_ != phase) + return false; + if ((fields_to_match_ & TYPE) && event.type() != event_type_) + return false; + int flags = event.flags() & ~ignore_event_flags_; + if ((fields_to_match_ & FLAGS) && flags != event_flags_) + return false; + if (fields_to_match_ & KEYBOARD_CODE) { + if (!event.IsKeyEvent()) + return false; + if (keyboard_code_ != event.AsKeyEvent()->GetConflatedWindowsKeyCode()) + return false; + } + if (fields_to_match_ & POINTER_KIND) { + if (!event.IsPointerEvent() || + pointer_type_ != event.AsPointerEvent()->pointer_details().pointer_type) + return false; + } + if (fields_to_match_ & POINTER_LOCATION) { + // TODO(sad): The tricky part here is to make sure the same coord-space is + // used for the location-region and the event-location. + NOTIMPLEMENTED(); + return false; + } + + return true; +} + +bool Accelerator::EqualEventMatcher(const Accelerator* other) const { + return fields_to_match_ == other->fields_to_match_ && + accelerator_phase_ == other->accelerator_phase_ && + event_type_ == other->event_type_ && + event_flags_ == other->event_flags_ && + ignore_event_flags_ == other->ignore_event_flags_ && + keyboard_code_ == other->keyboard_code_ && + pointer_type_ == other->pointer_type_ && + pointer_region_ == other->pointer_region_; +} + +} // namespace ws +} // namespace mus diff --git a/components/mus/ws/accelerator.h b/components/mus/ws/accelerator.h new file mode 100644 index 0000000..b114058 --- /dev/null +++ b/components/mus/ws/accelerator.h @@ -0,0 +1,71 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_MUS_WS_ACCELERATOR_H_ +#define COMPONENTS_MUS_WS_ACCELERATOR_H_ + +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "components/mus/public/interfaces/input_event_matcher.mojom.h" +#include "ui/events/event.h" +#include "ui/gfx/geometry/rect_f.h" + +namespace mus { +namespace ws { + +// An Accelerator encompasses an id defined by the client, along with a unique +// mojom::EventMatcher. See WindowManagerClient. +// +// This provides a WeakPtr, as the client might delete the accelerator between +// an event having been matched and the dispatch of the accelerator to the +// client. +class Accelerator { + public: + Accelerator(uint32_t id, const mojom::EventMatcher& matcher); + ~Accelerator(); + + // Returns true if |event| and |phase | matches the definition in the + // mojom::EventMatcher used for initialization. + bool MatchesEvent(const ui::Event& event, + const mojom::AcceleratorPhase phase) const; + + // Returns true if |other| was created with an identical mojom::EventMatcher. + bool EqualEventMatcher(const Accelerator* other) const; + + base::WeakPtr<Accelerator> GetWeakPtr() { return weak_factory_.GetWeakPtr(); } + + uint32_t id() const { return id_; } + + private: + enum MatchFields { + NONE = 0, + TYPE = 1 << 0, + FLAGS = 1 << 1, + KEYBOARD_CODE = 1 << 2, + POINTER_KIND = 1 << 3, + POINTER_LOCATION = 1 << 4, + }; + + uint32_t id_; + uint32_t fields_to_match_; + mojom::AcceleratorPhase accelerator_phase_; + ui::EventType event_type_; + // Bitfields of kEventFlag* and kMouseEventFlag* values in + // input_event_constants.mojom. + int event_flags_; + int ignore_event_flags_; + uint16_t keyboard_code_; + ui::EventPointerType pointer_type_; + gfx::RectF pointer_region_; + + base::WeakPtrFactory<Accelerator> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(Accelerator); +}; + +} // namespace ws +} // namespace mus + +#endif // COMPONENTS_MUS_WS_ACCELERATOR_H_ diff --git a/components/mus/ws/event_dispatcher.cc b/components/mus/ws/event_dispatcher.cc index 57efcc1..32b3831 100644 --- a/components/mus/ws/event_dispatcher.cc +++ b/components/mus/ws/event_dispatcher.cc @@ -9,6 +9,7 @@ #include "base/time/time.h" #include "cc/surfaces/surface_hittest.h" #include "components/mus/surfaces/surfaces_state.h" +#include "components/mus/ws/accelerator.h" #include "components/mus/ws/display.h" #include "components/mus/ws/event_dispatcher_delegate.h" #include "components/mus/ws/server_window.h" @@ -54,142 +55,6 @@ bool IsLocationInNonclientArea(const ServerWindow* target, } // namespace -class EventMatcher { - public: - explicit EventMatcher(const mojom::EventMatcher& matcher) - : fields_to_match_(NONE), - accelerator_phase_(mojom::AcceleratorPhase::PRE_TARGET), - event_type_(ui::ET_UNKNOWN), - event_flags_(ui::EF_NONE), - ignore_event_flags_(ui::EF_NONE), - keyboard_code_(ui::VKEY_UNKNOWN), - pointer_type_(ui::EventPointerType::POINTER_TYPE_UNKNOWN) { - accelerator_phase_ = matcher.accelerator_phase; - if (matcher.type_matcher) { - fields_to_match_ |= TYPE; - switch (matcher.type_matcher->type) { - case mus::mojom::EventType::POINTER_DOWN: - event_type_ = ui::ET_POINTER_DOWN; - break; - case mus::mojom::EventType::POINTER_MOVE: - event_type_ = ui::ET_POINTER_MOVED; - break; - case mus::mojom::EventType::MOUSE_EXIT: - event_type_ = ui::ET_POINTER_EXITED; - break; - case mus::mojom::EventType::POINTER_UP: - event_type_ = ui::ET_POINTER_UP; - break; - case mus::mojom::EventType::POINTER_CANCEL: - event_type_ = ui::ET_POINTER_CANCELLED; - break; - case mus::mojom::EventType::KEY_PRESSED: - event_type_ = ui::ET_KEY_PRESSED; - break; - case mus::mojom::EventType::KEY_RELEASED: - event_type_ = ui::ET_KEY_RELEASED; - break; - default: - NOTREACHED(); - } - } - if (matcher.flags_matcher) { - fields_to_match_ |= FLAGS; - event_flags_ = matcher.flags_matcher->flags; - if (matcher.ignore_flags_matcher) - ignore_event_flags_ = matcher.ignore_flags_matcher->flags; - } - if (matcher.key_matcher) { - fields_to_match_ |= KEYBOARD_CODE; - keyboard_code_ = - static_cast<uint16_t>(matcher.key_matcher->keyboard_code); - } - if (matcher.pointer_kind_matcher) { - fields_to_match_ |= POINTER_KIND; - switch (matcher.pointer_kind_matcher->pointer_kind) { - case mojom::PointerKind::MOUSE: - pointer_type_ = ui::EventPointerType::POINTER_TYPE_MOUSE; - break; - case mojom::PointerKind::TOUCH: - pointer_type_ = ui::EventPointerType::POINTER_TYPE_TOUCH; - break; - default: - NOTREACHED(); - } - } - if (matcher.pointer_location_matcher) { - fields_to_match_ |= POINTER_LOCATION; - pointer_region_ = - matcher.pointer_location_matcher->region.To<gfx::RectF>(); - } - } - - ~EventMatcher() {} - - bool MatchesEvent(const ui::Event& event, - const mojom::AcceleratorPhase phase) const { - if (accelerator_phase_ != phase) - return false; - if ((fields_to_match_ & TYPE) && event.type() != event_type_) - return false; - int flags = event.flags() & ~ignore_event_flags_; - if ((fields_to_match_ & FLAGS) && flags != event_flags_) - return false; - if (fields_to_match_ & KEYBOARD_CODE) { - if (!event.IsKeyEvent()) - return false; - if (keyboard_code_ != event.AsKeyEvent()->GetConflatedWindowsKeyCode()) - return false; - } - if (fields_to_match_ & POINTER_KIND) { - if (!event.IsPointerEvent() || - pointer_type_ != - event.AsPointerEvent()->pointer_details().pointer_type) - return false; - } - if (fields_to_match_ & POINTER_LOCATION) { - // TODO(sad): The tricky part here is to make sure the same coord-space is - // used for the location-region and the event-location. - NOTIMPLEMENTED(); - return false; - } - - return true; - } - - bool Equals(const EventMatcher& matcher) const { - return fields_to_match_ == matcher.fields_to_match_ && - accelerator_phase_ == matcher.accelerator_phase_ && - event_type_ == matcher.event_type_ && - event_flags_ == matcher.event_flags_ && - ignore_event_flags_ == matcher.ignore_event_flags_ && - keyboard_code_ == matcher.keyboard_code_ && - pointer_type_ == matcher.pointer_type_ && - pointer_region_ == matcher.pointer_region_; - } - - private: - enum MatchFields { - NONE = 0, - TYPE = 1 << 0, - FLAGS = 1 << 1, - KEYBOARD_CODE = 1 << 2, - POINTER_KIND = 1 << 3, - POINTER_LOCATION = 1 << 4, - }; - - uint32_t fields_to_match_; - mojom::AcceleratorPhase accelerator_phase_; - ui::EventType event_type_; - // Bitfields of kEventFlag* and kMouseEventFlag* values in - // input_event_constants.mojom. - int event_flags_; - int ignore_event_flags_; - uint16_t keyboard_code_; - ui::EventPointerType pointer_type_; - gfx::RectF pointer_region_; -}; - //////////////////////////////////////////////////////////////////////////////// EventDispatcher::EventDispatcher(EventDispatcherDelegate* delegate) @@ -303,13 +168,13 @@ void EventDispatcher::UpdateCursorProviderByLastKnownLocation() { bool EventDispatcher::AddAccelerator(uint32_t id, mojom::EventMatcherPtr event_matcher) { - EventMatcher matcher(*event_matcher); + scoped_ptr<Accelerator> accelerator(new Accelerator(id, *event_matcher)); // If an accelerator with the same id or matcher already exists, then abort. for (const auto& pair : accelerators_) { - if (pair.first == id || matcher.Equals(pair.second)) + if (pair.first == id || accelerator->EqualEventMatcher(pair.second.get())) return false; } - accelerators_.insert(Entry(id, matcher)); + accelerators_.insert(Entry(id, std::move(accelerator))); return true; } @@ -326,10 +191,10 @@ void EventDispatcher::ProcessEvent(const ui::Event& event) { if (event.IsKeyEvent()) { const ui::KeyEvent* key_event = event.AsKeyEvent(); if (event.type() == ui::ET_KEY_PRESSED && !key_event->is_char()) { - uint32_t accelerator = 0u; - if (FindAccelerator(*key_event, mojom::AcceleratorPhase::PRE_TARGET, - &accelerator)) { - delegate_->OnAccelerator(accelerator, event); + Accelerator* pre_target = + FindAccelerator(*key_event, mojom::AcceleratorPhase::PRE_TARGET); + if (pre_target) { + delegate_->OnAccelerator(pre_target->id(), event); return; } } @@ -346,10 +211,16 @@ void EventDispatcher::ProcessEvent(const ui::Event& event) { } void EventDispatcher::ProcessKeyEvent(const ui::KeyEvent& event) { + Accelerator* post_target = + FindAccelerator(event, mojom::AcceleratorPhase::POST_TARGET); ServerWindow* focused_window = delegate_->GetFocusedWindowForEventDispatcher(); - if (focused_window) - delegate_->DispatchInputEventToWindow(focused_window, false, event); + if (focused_window) { + delegate_->DispatchInputEventToWindow(focused_window, false, event, + post_target); + } else if (post_target) { + delegate_->OnAccelerator(post_target->id(), event); + } } void EventDispatcher::ProcessPointerEvent(const ui::PointerEvent& event) { @@ -504,8 +375,10 @@ void EventDispatcher::DispatchToPointerTarget(const PointerTarget& target, transform.TransformPoint(&location); scoped_ptr<ui::Event> clone = ui::Event::Clone(event); clone->AsPointerEvent()->set_location(location); + // TODO(jonross): add post-target accelerator support once accelerators + // support pointer events. delegate_->DispatchInputEventToWindow(target.window, target.in_nonclient_area, - *clone); + *clone, nullptr); } void EventDispatcher::CancelPointerEventsToTarget(ServerWindow* window) { @@ -537,16 +410,15 @@ bool EventDispatcher::IsObservingWindow(ServerWindow* window) { return false; } -bool EventDispatcher::FindAccelerator(const ui::KeyEvent& event, - const mojom::AcceleratorPhase phase, - uint32_t* accelerator_id) { +Accelerator* EventDispatcher::FindAccelerator( + const ui::KeyEvent& event, + const mojom::AcceleratorPhase phase) { for (const auto& pair : accelerators_) { - if (pair.second.MatchesEvent(event, phase)) { - *accelerator_id = pair.first; - return true; + if (pair.second->MatchesEvent(event, phase)) { + return pair.second.get(); } } - return false; + return nullptr; } void EventDispatcher::OnWillChangeWindowHierarchy(ServerWindow* window, diff --git a/components/mus/ws/event_dispatcher.h b/components/mus/ws/event_dispatcher.h index 30868e8..0a89e66 100644 --- a/components/mus/ws/event_dispatcher.h +++ b/components/mus/ws/event_dispatcher.h @@ -26,8 +26,8 @@ class PointerEvent; namespace mus { namespace ws { +class Accelerator; class EventDispatcherDelegate; -class EventMatcher; class ServerWindow; namespace test { @@ -152,13 +152,10 @@ class EventDispatcher : public ServerWindowObserver { // observer for a window if any pointer events are targeting it. bool IsObservingWindow(ServerWindow* window); - // Looks to see if there is an accelerator bound to the specified code/flags, - // and of the matching |phase|. If there is one, sets |accelerator_id| to the - // id of the accelerator invoked and returns true. If there is none, returns - // false so normal key event processing can continue. - bool FindAccelerator(const ui::KeyEvent& event, - const mojom::AcceleratorPhase phase, - uint32_t* accelerator_id); + // Returns an Accelerator bound to the specified code/flags, and of the + // matching |phase|. Otherwise returns null. + Accelerator* FindAccelerator(const ui::KeyEvent& event, + const mojom::AcceleratorPhase phase); // ServerWindowObserver: void OnWillChangeWindowHierarchy(ServerWindow* window, @@ -181,8 +178,8 @@ class EventDispatcher : public ServerWindowObserver { cc::SurfaceId surface_id_; - using Entry = std::pair<uint32_t, EventMatcher>; - std::map<uint32_t, EventMatcher> accelerators_; + using Entry = std::pair<uint32_t, scoped_ptr<Accelerator>>; + std::map<uint32_t, scoped_ptr<Accelerator>> accelerators_; using PointerIdToTargetMap = std::map<int32_t, PointerTarget>; // |pointer_targets_| contains the active pointers. For a mouse based pointer diff --git a/components/mus/ws/event_dispatcher_delegate.h b/components/mus/ws/event_dispatcher_delegate.h index 9ecc7a2..a762df5 100644 --- a/components/mus/ws/event_dispatcher_delegate.h +++ b/components/mus/ws/event_dispatcher_delegate.h @@ -14,6 +14,7 @@ class Event; namespace mus { namespace ws { +class Accelerator; class ServerWindow; // Used by EventDispatcher for mocking in tests. @@ -37,7 +38,8 @@ class EventDispatcherDelegate { // |in_nonclient_area| is true if the event occurred in the non-client area. virtual void DispatchInputEventToWindow(ServerWindow* target, bool in_nonclient_area, - const ui::Event& event) = 0; + const ui::Event& event, + Accelerator* accelerator) = 0; protected: virtual ~EventDispatcherDelegate() {} diff --git a/components/mus/ws/event_dispatcher_unittest.cc b/components/mus/ws/event_dispatcher_unittest.cc index 3273c3c..199629e 100644 --- a/components/mus/ws/event_dispatcher_unittest.cc +++ b/components/mus/ws/event_dispatcher_unittest.cc @@ -10,7 +10,9 @@ #include <queue> #include "base/macros.h" +#include "base/memory/weak_ptr.h" #include "components/mus/public/cpp/event_matcher.h" +#include "components/mus/ws/accelerator.h" #include "components/mus/ws/event_dispatcher_delegate.h" #include "components/mus/ws/server_window.h" #include "components/mus/ws/server_window_surface_manager_test_api.h" @@ -26,11 +28,13 @@ namespace { // Identifies a generated event. struct DispatchedEventDetails { - DispatchedEventDetails() : window(nullptr), in_nonclient_area(false) {} + DispatchedEventDetails() + : window(nullptr), in_nonclient_area(false), accelerator(nullptr) {} ServerWindow* window; bool in_nonclient_area; scoped_ptr<ui::Event> event; + Accelerator* accelerator; }; class TestEventDispatcherDelegate : public EventDispatcherDelegate { @@ -68,15 +72,17 @@ class TestEventDispatcherDelegate : public EventDispatcherDelegate { bool has_queued_events() const { return !dispatched_event_queue_.empty(); } ServerWindow* lost_capture_window() { return lost_capture_window_; } + // EventDispatcherDelegate: + void SetFocusedWindowFromEventDispatcher(ServerWindow* window) override { + focused_window_ = window; + } + private: // EventDispatcherDelegate: void OnAccelerator(uint32_t accelerator, const ui::Event& event) override { EXPECT_EQ(0u, last_accelerator_); last_accelerator_ = accelerator; } - void SetFocusedWindowFromEventDispatcher(ServerWindow* window) override { - focused_window_ = window; - } ServerWindow* GetFocusedWindowForEventDispatcher() override { return focused_window_; } @@ -87,11 +93,13 @@ class TestEventDispatcherDelegate : public EventDispatcherDelegate { } void DispatchInputEventToWindow(ServerWindow* target, bool in_nonclient_area, - const ui::Event& event) override { + const ui::Event& event, + Accelerator* accelerator) override { scoped_ptr<DispatchedEventDetails> details(new DispatchedEventDetails); details->window = target; details->in_nonclient_area = in_nonclient_area; details->event = ui::Event::Clone(event); + details->accelerator = accelerator; dispatched_event_queue_.push(std::move(details)); } @@ -376,11 +384,36 @@ TEST_F(EventDispatcherTest, PostTargetAccelerator) { dispatcher->AddAccelerator(accelerator_1, std::move(matcher)); ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN); + // The post-target accelerator should be fired if there is no focused window. + dispatcher->ProcessEvent(key); + EXPECT_EQ(accelerator_1, + event_dispatcher_delegate->GetAndClearLastAccelerator()); + scoped_ptr<DispatchedEventDetails> details = + event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails(); + EXPECT_FALSE(details); + + // Set focused window for EventDispatcher dispatches key events. + scoped_ptr<ServerWindow> child = CreateChildWindow(WindowId(1, 3)); + event_dispatcher_delegate->SetFocusedWindowFromEventDispatcher(child.get()); + + // With a focused window the event should be dispatched. dispatcher->ProcessEvent(key); EXPECT_EQ(0u, event_dispatcher_delegate->GetAndClearLastAccelerator()); + details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails(); + EXPECT_TRUE(details); + EXPECT_TRUE(details->accelerator); + + base::WeakPtr<Accelerator> accelerator_weak_ptr = + details->accelerator->GetWeakPtr(); + dispatcher->RemoveAccelerator(accelerator_1); + EXPECT_FALSE(accelerator_weak_ptr); - // TODO(jonross): Update this test to include actual invokation of PostTarget - // acceleratos once events acking includes consuming. + // Post deletion there should be no accelerator + dispatcher->ProcessEvent(key); + EXPECT_EQ(0u, event_dispatcher_delegate->GetAndClearLastAccelerator()); + details = event_dispatcher_delegate->GetAndAdvanceDispatchedEventDetails(); + EXPECT_TRUE(details); + EXPECT_FALSE(details->accelerator); } TEST_F(EventDispatcherTest, Capture) { diff --git a/components/mus/ws/test_utils.cc b/components/mus/ws/test_utils.cc index 87b517c..4201f94 100644 --- a/components/mus/ws/test_utils.cc +++ b/components/mus/ws/test_utils.cc @@ -7,8 +7,10 @@ #include "cc/output/copy_output_request.h" #include "components/mus/surfaces/surfaces_state.h" #include "components/mus/ws/display_binding.h" +#include "components/mus/ws/window_manager_access_policy.h" #include "components/mus/ws/window_manager_factory_service.h" #include "mojo/converters/geometry/geometry_type_converters.h" +#include "mojo/shell/public/interfaces/connector.mojom.h" namespace mus { namespace ws { @@ -112,6 +114,28 @@ int EventDispatcherTestApi::NumberPointerTargetsForWindow( return count; } +// TestDisplayBinding --------------------------------------------------------- + +WindowTree* TestDisplayBinding::CreateWindowTree(ServerWindow* root) { + return window_server_->EmbedAtWindow( + root, mojo::shell::mojom::kRootUserID, mus::mojom::WindowTreeClientPtr(), + make_scoped_ptr(new WindowManagerAccessPolicy)); +} + +// TestWindowManager ---------------------------------------------------------- + +void TestWindowManager::WmCreateTopLevelWindow( + uint32_t change_id, + mojo::Map<mojo::String, mojo::Array<uint8_t>> properties) { + got_create_top_level_window_ = true; + change_id_ = change_id; +} + +void TestWindowManager::OnAccelerator(uint32_t id, mojom::EventPtr event) { + on_accelerator_called_ = true; + on_accelerator_id_ = id; +} + // TestWindowTreeClient ------------------------------------------------------- TestWindowTreeClient::TestWindowTreeClient() diff --git a/components/mus/ws/test_utils.h b/components/mus/ws/test_utils.h index 096caaf..6366ce4 100644 --- a/components/mus/ws/test_utils.h +++ b/components/mus/ws/test_utils.h @@ -10,6 +10,7 @@ #include "components/mus/public/interfaces/display.mojom.h" #include "components/mus/public/interfaces/window_tree.mojom.h" #include "components/mus/ws/display.h" +#include "components/mus/ws/display_binding.h" #include "components/mus/ws/event_dispatcher.h" #include "components/mus/ws/platform_display.h" #include "components/mus/ws/platform_display_factory.h" @@ -70,6 +71,7 @@ class WindowTreeTestApi { tree_->window_manager_internal_ = wm_internal; } + void ClearAck() { tree_->event_ack_id_ = 0; } void EnableCapture() { tree_->event_ack_id_ = 1u; } private: @@ -120,6 +122,16 @@ class WindowManagerStateTestApi { explicit WindowManagerStateTestApi(WindowManagerState* wms) : wms_(wms) {} ~WindowManagerStateTestApi() {} + void DispatchInputEventToWindow(ServerWindow* target, + bool in_nonclient_area, + const ui::Event& event, + Accelerator* accelerator) { + wms_->DispatchInputEventToWindow(target, in_nonclient_area, event, + accelerator); + } + + void OnEventAckTimeout() { wms_->OnEventAckTimeout(); } + mojom::WindowTree* tree_awaiting_input_ack() { return wms_->tree_awaiting_input_ack_; } @@ -132,6 +144,25 @@ class WindowManagerStateTestApi { // ----------------------------------------------------------------------------- +// Factory that always embeds the new WindowTree as the root user id. +class TestDisplayBinding : public DisplayBinding { + public: + TestDisplayBinding(Display* display, WindowServer* window_server) + : display_(display), window_server_(window_server) {} + ~TestDisplayBinding() override {} + + private: + // DisplayBinding: + WindowTree* CreateWindowTree(ServerWindow* root) override; + + Display* display_; + WindowServer* window_server_; + + DISALLOW_COPY_AND_ASSIGN(TestDisplayBinding); +}; + +// ----------------------------------------------------------------------------- + // Factory that dispenses TestPlatformDisplays. class TestPlatformDisplayFactory : public PlatformDisplayFactory { public: @@ -149,6 +180,52 @@ class TestPlatformDisplayFactory : public PlatformDisplayFactory { // ----------------------------------------------------------------------------- +class TestWindowManager : public mojom::WindowManager { + public: + TestWindowManager() + : got_create_top_level_window_(false), + change_id_(0u), + on_accelerator_called_(false), + on_accelerator_id_(0u) {} + ~TestWindowManager() override {} + + bool did_call_create_top_level_window(uint32_t* change_id) { + if (!got_create_top_level_window_) + return false; + + got_create_top_level_window_ = false; + *change_id = change_id_; + return true; + } + + bool on_accelerator_called() { return on_accelerator_called_; } + uint32_t on_accelerator_id() { return on_accelerator_id_; } + + private: + // WindowManager: + void WmSetBounds(uint32_t change_id, + uint32_t window_id, + mojo::RectPtr bounds) override {} + void WmSetProperty(uint32_t change_id, + uint32_t window_id, + const mojo::String& name, + mojo::Array<uint8_t> value) override {} + void WmCreateTopLevelWindow( + uint32_t change_id, + mojo::Map<mojo::String, mojo::Array<uint8_t>> properties) override; + void OnAccelerator(uint32_t id, mojom::EventPtr event) override; + + bool got_create_top_level_window_; + uint32_t change_id_; + + bool on_accelerator_called_; + uint32_t on_accelerator_id_; + + DISALLOW_COPY_AND_ASSIGN(TestWindowManager); +}; + +// ----------------------------------------------------------------------------- + // WindowTreeClient implementation that logs all calls to a TestChangeTracker. class TestWindowTreeClient : public mus::mojom::WindowTreeClient { public: diff --git a/components/mus/ws/window_manager_state.cc b/components/mus/ws/window_manager_state.cc index 4b9454e..4dcef3d 100644 --- a/components/mus/ws/window_manager_state.cc +++ b/components/mus/ws/window_manager_state.cc @@ -4,6 +4,8 @@ #include "components/mus/ws/window_manager_state.h" +#include "base/memory/weak_ptr.h" +#include "components/mus/ws/accelerator.h" #include "components/mus/ws/display_manager.h" #include "components/mus/ws/platform_display.h" #include "components/mus/ws/server_window.h" @@ -50,9 +52,13 @@ scoped_ptr<ui::Event> CoalesceEvents(scoped_ptr<ui::Event> first, class WindowManagerState::ProcessedEventTarget { public: - ProcessedEventTarget(ServerWindow* window, bool in_nonclient_area) + ProcessedEventTarget(ServerWindow* window, + bool in_nonclient_area, + Accelerator* accelerator) : in_nonclient_area_(in_nonclient_area) { tracker_.Add(window); + if (accelerator) + accelerator_ = accelerator->GetWeakPtr(); } ~ProcessedEventTarget() {} @@ -68,9 +74,12 @@ class WindowManagerState::ProcessedEventTarget { bool in_nonclient_area() const { return in_nonclient_area_; } + base::WeakPtr<Accelerator> accelerator() { return accelerator_; } + private: ServerWindowTracker tracker_; const bool in_nonclient_area_; + base::WeakPtr<Accelerator> accelerator_; DISALLOW_COPY_AND_ASSIGN(ProcessedEventTarget); }; @@ -149,7 +158,9 @@ void WindowManagerState::OnWillDestroyTree(WindowTree* tree) { if (tree_awaiting_input_ack_ != tree) return; // The WindowTree is dying. So it's not going to ack the event. - OnEventAck(tree_awaiting_input_ack_); + // If the dying tree matches the root |tree_| marked as handled so we don't + // notify it of accelerators. + OnEventAck(tree_awaiting_input_ack_, tree == tree_); } WindowManagerState::WindowManagerState(Display* display, @@ -216,7 +227,7 @@ void WindowManagerState::ProcessEvent(const ui::Event& event) { event_dispatcher_.ProcessEvent(event); } -void WindowManagerState::OnEventAck(mojom::WindowTree* tree) { +void WindowManagerState::OnEventAck(mojom::WindowTree* tree, bool handled) { if (tree_awaiting_input_ack_ != tree) { // TODO(sad): The ack must have arrived after the timeout. We should do // something here, and in OnEventAckTimeout(). @@ -224,6 +235,10 @@ void WindowManagerState::OnEventAck(mojom::WindowTree* tree) { } tree_awaiting_input_ack_ = nullptr; event_ack_timer_.Stop(); + + if (!handled && post_target_accelerator_) + OnAccelerator(post_target_accelerator_->id(), *event_awaiting_input_ack_); + ProcessNextEventFromQueue(); } @@ -234,7 +249,7 @@ WindowServer* WindowManagerState::window_server() { void WindowManagerState::OnEventAckTimeout() { // TODO(sad): Figure out what we should do. NOTIMPLEMENTED() << "Event ACK timed out."; - OnEventAck(tree_awaiting_input_ack_); + OnEventAck(tree_awaiting_input_ack_, false); } void WindowManagerState::QueueEvent( @@ -260,7 +275,7 @@ void WindowManagerState::ProcessNextEventFromQueue() { DispatchInputEventToWindowImpl( queued_event->processed_target->window(), queued_event->processed_target->in_nonclient_area(), - *queued_event->event); + *queued_event->event, queued_event->processed_target->accelerator()); return; } } @@ -269,7 +284,8 @@ void WindowManagerState::ProcessNextEventFromQueue() { void WindowManagerState::DispatchInputEventToWindowImpl( ServerWindow* target, bool in_nonclient_area, - const ui::Event& event) { + const ui::Event& event, + base::WeakPtr<Accelerator> accelerator) { if (target == root_->parent()) target = root_.get(); @@ -306,6 +322,10 @@ void WindowManagerState::DispatchInputEventToWindowImpl( &WindowManagerState::OnEventAckTimeout); tree_awaiting_input_ack_ = tree; + if (accelerator) { + event_awaiting_input_ack_ = ui::Event::Clone(event); + post_target_accelerator_ = accelerator; + } tree->DispatchInputEvent(target, event); } @@ -341,18 +361,23 @@ void WindowManagerState::OnServerWindowCaptureLost(ServerWindow* window) { void WindowManagerState::DispatchInputEventToWindow(ServerWindow* target, bool in_nonclient_area, - const ui::Event& event) { + const ui::Event& event, + Accelerator* accelerator) { DCHECK(IsActive()); // TODO(sky): this needs to see if another wms has capture and if so forward // to it. if (event_ack_timer_.IsRunning()) { scoped_ptr<ProcessedEventTarget> processed_event_target( - new ProcessedEventTarget(target, in_nonclient_area)); + new ProcessedEventTarget(target, in_nonclient_area, accelerator)); QueueEvent(event, std::move(processed_event_target)); return; } - DispatchInputEventToWindowImpl(target, in_nonclient_area, event); + base::WeakPtr<Accelerator> weak_accelerator; + if (accelerator) + weak_accelerator = accelerator->GetWeakPtr(); + DispatchInputEventToWindowImpl(target, in_nonclient_area, event, + weak_accelerator); } } // namespace ws diff --git a/components/mus/ws/window_manager_state.h b/components/mus/ws/window_manager_state.h index 18195d2..b53885e 100644 --- a/components/mus/ws/window_manager_state.h +++ b/components/mus/ws/window_manager_state.h @@ -8,6 +8,7 @@ #include <stdint.h> #include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" #include "base/timer/timer.h" #include "components/mus/public/interfaces/display.mojom.h" #include "components/mus/ws/event_dispatcher.h" @@ -22,6 +23,7 @@ struct SurfaceId; namespace mus { namespace ws { +class Accelerator; class Display; class PlatformDisplay; class ServerWindow; @@ -93,9 +95,10 @@ class WindowManagerState : public EventDispatcherDelegate { // Processes an event from PlatformDisplay. void ProcessEvent(const ui::Event& event); - // Called when the ack from an event dispatched to a WindowTree is received. + // Called when the ack from an event dispatched to WindowTree |tree| is + // received. When |handled| is true, the client consumed the event. // TODO(sky): make this private and use a callback. - void OnEventAck(mojom::WindowTree* tree); + void OnEventAck(mojom::WindowTree* tree, bool handled); // Returns a mojom::Display for the specified display. WindowManager specific // values are not set. @@ -146,7 +149,8 @@ class WindowManagerState : public EventDispatcherDelegate { // Dispatches the event to the appropriate client and starts the ack timer. void DispatchInputEventToWindowImpl(ServerWindow* target, bool in_nonclient_area, - const ui::Event& event); + const ui::Event& event, + base::WeakPtr<Accelerator> accelerator); // EventDispatcherDelegate: void OnAccelerator(uint32_t accelerator_id, const ui::Event& event) override; @@ -157,7 +161,8 @@ class WindowManagerState : public EventDispatcherDelegate { void OnServerWindowCaptureLost(ServerWindow* window) override; void DispatchInputEventToWindow(ServerWindow* target, bool in_nonclient_area, - const ui::Event& event) override; + const ui::Event& event, + Accelerator* accelerator) override; Display* display_; PlatformDisplay* platform_display_; @@ -176,6 +181,8 @@ class WindowManagerState : public EventDispatcherDelegate { mojom::FrameDecorationValuesPtr frame_decoration_values_; mojom::WindowTree* tree_awaiting_input_ack_ = nullptr; + scoped_ptr<ui::Event> event_awaiting_input_ack_; + base::WeakPtr<Accelerator> post_target_accelerator_; std::queue<scoped_ptr<QueuedEvent>> event_queue_; base::OneShotTimer event_ack_timer_; diff --git a/components/mus/ws/window_manager_state_unittest.cc b/components/mus/ws/window_manager_state_unittest.cc new file mode 100644 index 0000000..d43085e --- /dev/null +++ b/components/mus/ws/window_manager_state_unittest.cc @@ -0,0 +1,357 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/mus/ws/window_manager_state.h" + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/test/test_simple_task_runner.h" +#include "base/thread_task_runner_handle.h" +#include "components/mus/public/cpp/event_matcher.h" +#include "components/mus/surfaces/surfaces_state.h" +#include "components/mus/ws/accelerator.h" +#include "components/mus/ws/display_binding.h" +#include "components/mus/ws/platform_display.h" +#include "components/mus/ws/platform_display_init_params.h" +#include "components/mus/ws/server_window_surface_manager_test_api.h" +#include "components/mus/ws/test_change_tracker.h" +#include "components/mus/ws/test_server_window_delegate.h" +#include "components/mus/ws/test_utils.h" +#include "components/mus/ws/window_manager_access_policy.h" +#include "components/mus/ws/window_manager_state.h" +#include "components/mus/ws/window_server.h" +#include "components/mus/ws/window_tree.h" +#include "mojo/shell/public/interfaces/connector.mojom.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/events/event.h" + +namespace mus { +namespace ws { +namespace test { + +class WindowManagerStateTest : public testing::Test { + public: + WindowManagerStateTest(); + ~WindowManagerStateTest() override {} + + scoped_ptr<Accelerator> CreateAccelerator(); + + // Creates a child |server_window| with associataed |window_tree| and + // |test_client|. The window is setup for processing input. + void CreateSecondaryTree(TestWindowTreeClient** test_client, + WindowTree** window_tree, + ServerWindow** server_window); + + void DispatchInputEventToWindow(ServerWindow* target, + bool in_nonclient_area, + const ui::Event& event, + Accelerator* accelerator); + void OnEventAckTimeout(); + + WindowTree* tree() { return tree_; } + ServerWindow* window() { return window_; } + TestWindowManager* window_manager() { return &window_manager_; } + TestWindowTreeClient* wm_client() { return wm_client_; } + WindowManagerState* window_manager_state() { return window_manager_state_; } + + // testing::Test: + void SetUp() override; + + private: + int32_t cursor_id_; + + // Owned by WindowServer's DisplayManager. + Display* display_; + // Owned by Display. + WindowManagerState* window_manager_state_; + // Owned by WindowServer. + TestDisplayBinding* display_binding_; + // Destroyed in TearDown. + WindowTree* tree_; + // Owned by Display. + WindowTree* original_tree_; + // TestWindowTreeClient that is used for the WM connection. Owned by + // |window_server_delegate_| + TestWindowTreeClient* wm_client_; + + TestWindowServerDelegate window_server_delegate_; + scoped_ptr<WindowServer> window_server_; + // Handles WindowStateManager ack timeouts. + scoped_refptr<base::TestSimpleTaskRunner> task_runner_; + TestPlatformDisplayFactory platform_display_factory_; + TestWindowManager window_manager_; + ServerWindow* window_; + // Needed to Bind to |wm_client_| + base::MessageLoop message_loop_; + + DISALLOW_COPY_AND_ASSIGN(WindowManagerStateTest); +}; + +WindowManagerStateTest::WindowManagerStateTest() + : cursor_id_(0), + task_runner_(new base::TestSimpleTaskRunner), + platform_display_factory_(&cursor_id_) {} + +scoped_ptr<Accelerator> WindowManagerStateTest::CreateAccelerator() { + mojom::EventMatcherPtr matcher = mus::CreateKeyMatcher( + mus::mojom::KeyboardCode::W, mus::mojom::kEventFlagControlDown); + matcher->accelerator_phase = mojom::AcceleratorPhase::POST_TARGET; + uint32_t accelerator_id = 1; + scoped_ptr<Accelerator> accelerator( + new Accelerator(accelerator_id, *matcher)); + return accelerator; +} + +void WindowManagerStateTest::CreateSecondaryTree( + TestWindowTreeClient** test_client, + WindowTree** window_tree, + ServerWindow** server_window) { + WindowTree* tree1 = window_server_->GetTreeWithRoot(window_); + ASSERT_TRUE(tree1 != nullptr); + ASSERT_NE(tree1, tree()); + + const ClientWindowId child1_id( + WindowIdToTransportId(WindowId(tree1->id(), 1))); + EXPECT_TRUE(tree1->NewWindow(child1_id, ServerWindow::Properties())); + ServerWindow* child1 = tree1->GetWindowByClientId(child1_id); + ASSERT_TRUE(child1); + ClientWindowId window_id; + tree1->IsWindowKnown(window_, &window_id); + EXPECT_TRUE(tree1->AddWindow(window_id, child1_id)); + tree1->GetDisplay(window_)->AddActivationParent(window_); + + child1->SetVisible(true); + child1->SetBounds(gfx::Rect(20, 20, 20, 20)); + + TestWindowTreeClient* embed_connection = + window_server_delegate_.last_client(); + embed_connection->tracker()->changes()->clear(); + wm_client()->tracker()->changes()->clear(); + + *test_client = embed_connection; + *window_tree = tree1; + *server_window = child1; +} + +void WindowManagerStateTest::DispatchInputEventToWindow( + ServerWindow* target, + bool in_nonclient_area, + const ui::Event& event, + Accelerator* accelerator) { + WindowManagerStateTestApi test_api(window_manager_state_); + test_api.DispatchInputEventToWindow(target, in_nonclient_area, event, + accelerator); +} + +void WindowManagerStateTest::OnEventAckTimeout() { + WindowManagerStateTestApi test_api(window_manager_state_); + test_api.OnEventAckTimeout(); +} + +void WindowManagerStateTest::SetUp() { + message_loop_.SetTaskRunner(task_runner_); + + PlatformDisplay::set_factory_for_testing(&platform_display_factory_); + window_server_.reset(new WindowServer(&window_server_delegate_, + scoped_refptr<SurfacesState>())); + + PlatformDisplayInitParams display_init_params; + display_ = new Display(window_server_.get(), display_init_params); + display_binding_ = new TestDisplayBinding(display_, window_server_.get()); + display_->Init(make_scoped_ptr(display_binding_)); + + wm_client_ = window_server_delegate_.last_client(); + window_manager_state_ = display_->GetActiveWindowManagerState(); + + tree_ = window_server_->GetTreeWithId(1); + const ClientWindowId embed_window_id(WindowIdToTransportId(WindowId(1, 1))); + EXPECT_TRUE(tree_->NewWindow(embed_window_id, ServerWindow::Properties())); + ClientWindowId root_id; + if (tree_->roots().size() == 1u) { + // If window isn't known we'll return 0, which should then error out. + tree_->IsWindowKnown(*(tree_->roots().begin()), &root_id); + } + + EXPECT_TRUE(tree_->SetWindowVisibility(embed_window_id, true)); + EXPECT_TRUE(tree_->AddWindow(root_id, embed_window_id)); + display_->root_window()->SetBounds(gfx::Rect(0, 0, 100, 100)); + mojom::WindowTreeClientPtr client; + mojom::WindowTreeClientRequest client_request = GetProxy(&client); + wm_client_->Bind(std::move(client_request)); + tree_->Embed(embed_window_id, std::move(client)); + window_ = tree_->GetWindowByClientId(embed_window_id); + window_->SetBounds(gfx::Rect(0, 0, 50, 50)); + + WindowTreeTestApi(tree_).set_window_manager_internal(&window_manager_); + wm_client_->tracker()->changes()->clear(); +} + +// Tests that when an event is dispatched with no accelerator, that post target +// accelerator is not triggered. +TEST_F(WindowManagerStateTest, NullAccelerator) { + WindowManagerState* state = window_manager_state(); + EXPECT_TRUE(state); + + ServerWindow* target = window(); + ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN); + DispatchInputEventToWindow(target, true, key, nullptr); + WindowTree* target_tree = tree(); + TestChangeTracker* tracker = wm_client()->tracker(); + EXPECT_EQ(1u, tracker->changes()->size()); + EXPECT_EQ("InputEvent window=1,1 event_action=1", + ChangesToDescription1(*tracker->changes())[0]); + + state->OnEventAck(target_tree, false); + EXPECT_FALSE(window_manager()->on_accelerator_called()); +} + +// Tests that when a post target accelerator is provided on an event, that it is +// called on ack. +TEST_F(WindowManagerStateTest, PostTargetAccelerator) { + ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN); + scoped_ptr<Accelerator> accelerator = CreateAccelerator(); + + ServerWindow* target = window(); + DispatchInputEventToWindow(target, true, key, accelerator.get()); + TestChangeTracker* tracker = wm_client()->tracker(); + EXPECT_EQ(1u, tracker->changes()->size()); + EXPECT_EQ("InputEvent window=1,1 event_action=1", + ChangesToDescription1(*tracker->changes())[0]); + + window_manager_state()->OnEventAck(tree(), false); + EXPECT_TRUE(window_manager()->on_accelerator_called()); + EXPECT_EQ(accelerator->id(), window_manager()->on_accelerator_id()); +} + +// Tests that when a client handles an event that post target accelerators are +// not called. +TEST_F(WindowManagerStateTest, ClientHandlesEvent) { + ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN); + scoped_ptr<Accelerator> accelerator = CreateAccelerator(); + + ServerWindow* target = window(); + DispatchInputEventToWindow(target, true, key, accelerator.get()); + TestChangeTracker* tracker = wm_client()->tracker(); + EXPECT_EQ(1u, tracker->changes()->size()); + EXPECT_EQ("InputEvent window=1,1 event_action=1", + ChangesToDescription1(*tracker->changes())[0]); + + window_manager_state()->OnEventAck(tree(), true); + EXPECT_FALSE(window_manager()->on_accelerator_called()); +} + +// Tests that when an accelerator is deleted before an ack, that it is not +// called. +TEST_F(WindowManagerStateTest, AcceleratorDeleted) { + ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN); + scoped_ptr<Accelerator> accelerator(CreateAccelerator()); + + ServerWindow* target = window(); + DispatchInputEventToWindow(target, true, key, accelerator.get()); + TestChangeTracker* tracker = wm_client()->tracker(); + EXPECT_EQ(1u, tracker->changes()->size()); + EXPECT_EQ("InputEvent window=1,1 event_action=1", + ChangesToDescription1(*tracker->changes())[0]); + + accelerator.reset(); + window_manager_state()->OnEventAck(tree(), false); + EXPECT_FALSE(window_manager()->on_accelerator_called()); +} + +// Tests that a events arriving before an ack don't notify the tree until the +// ack arrives, and that the correct accelerator is called. +TEST_F(WindowManagerStateTest, EnqueuedAccelerators) { + WindowManagerState* state = window_manager_state(); + + ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN); + scoped_ptr<Accelerator> accelerator(CreateAccelerator()); + + ServerWindow* target = window(); + DispatchInputEventToWindow(target, true, key, accelerator.get()); + TestChangeTracker* tracker = wm_client()->tracker(); + EXPECT_EQ(1u, tracker->changes()->size()); + EXPECT_EQ("InputEvent window=1,1 event_action=1", + ChangesToDescription1(*tracker->changes())[0]); + + tracker->changes()->clear(); + ui::KeyEvent key2(ui::ET_KEY_PRESSED, ui::VKEY_Y, ui::EF_CONTROL_DOWN); + mojom::EventMatcherPtr matcher = mus::CreateKeyMatcher( + mus::mojom::KeyboardCode::Y, mus::mojom::kEventFlagControlDown); + matcher->accelerator_phase = mojom::AcceleratorPhase::POST_TARGET; + uint32_t accelerator_id = 2; + scoped_ptr<Accelerator> accelerator2( + new Accelerator(accelerator_id, *matcher)); + DispatchInputEventToWindow(target, true, key2, accelerator2.get()); + EXPECT_TRUE(tracker->changes()->empty()); + + WindowTree* target_tree = tree(); + WindowTreeTestApi(target_tree).ClearAck(); + state->OnEventAck(target_tree, false); + EXPECT_EQ(1u, tracker->changes()->size()); + EXPECT_EQ("InputEvent window=1,1 event_action=1", + ChangesToDescription1(*tracker->changes())[0]); + EXPECT_TRUE(window_manager()->on_accelerator_called()); + EXPECT_EQ(accelerator->id(), window_manager()->on_accelerator_id()); +} + +// Tests that the accelerator is not sent when the tree is dying. +TEST_F(WindowManagerStateTest, DeleteTree) { + ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN); + scoped_ptr<Accelerator> accelerator = CreateAccelerator(); + + ServerWindow* target = window(); + DispatchInputEventToWindow(target, true, key, accelerator.get()); + TestChangeTracker* tracker = wm_client()->tracker(); + EXPECT_EQ(1u, tracker->changes()->size()); + EXPECT_EQ("InputEvent window=1,1 event_action=1", + ChangesToDescription1(*tracker->changes())[0]); + + window_manager_state()->OnWillDestroyTree(tree()); + EXPECT_FALSE(window_manager()->on_accelerator_called()); +} + +// Tests that if a tree is destroyed before acking, that the accelerator is +// still sent if it is not the root tree. +TEST_F(WindowManagerStateTest, DeleteNonRootTree) { + TestWindowTreeClient* embed_connection = nullptr; + WindowTree* target_tree = nullptr; + ServerWindow* target = nullptr; + CreateSecondaryTree(&embed_connection, &target_tree, &target); + TestWindowManager target_window_manager; + WindowTreeTestApi(target_tree) + .set_window_manager_internal(&target_window_manager); + + ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN); + scoped_ptr<Accelerator> accelerator = CreateAccelerator(); + DispatchInputEventToWindow(target, true, key, accelerator.get()); + TestChangeTracker* tracker = embed_connection->tracker(); + EXPECT_EQ(1u, tracker->changes()->size()); + EXPECT_EQ("InputEvent window=2,1 event_action=1", + ChangesToDescription1(*tracker->changes())[0]); + EXPECT_TRUE(wm_client()->tracker()->changes()->empty()); + + window_manager_state()->OnWillDestroyTree(target_tree); + EXPECT_FALSE(target_window_manager.on_accelerator_called()); + EXPECT_TRUE(window_manager()->on_accelerator_called()); +} + +// Tests that when an ack times out that the accelerator is notified. +TEST_F(WindowManagerStateTest, AckTimeout) { + ui::KeyEvent key(ui::ET_KEY_PRESSED, ui::VKEY_W, ui::EF_CONTROL_DOWN); + scoped_ptr<Accelerator> accelerator = CreateAccelerator(); + DispatchInputEventToWindow(window(), true, key, accelerator.get()); + TestChangeTracker* tracker = wm_client()->tracker(); + EXPECT_EQ(1u, tracker->changes()->size()); + EXPECT_EQ("InputEvent window=1,1 event_action=1", + ChangesToDescription1(*tracker->changes())[0]); + + OnEventAckTimeout(); + EXPECT_TRUE(window_manager()->on_accelerator_called()); + EXPECT_EQ(accelerator->id(), window_manager()->on_accelerator_id()); +} + +} // namespace test +} // namespace ws +} // namespace mus diff --git a/components/mus/ws/window_tree.cc b/components/mus/ws/window_tree.cc index d4cfae6..5de526c 100644 --- a/components/mus/ws/window_tree.cc +++ b/components/mus/ws/window_tree.cc @@ -1164,7 +1164,7 @@ void WindowTree::OnWindowInputEventAck(uint32_t event_id, bool handled) { WindowManagerState* event_source_wms = event_source_wms_; event_source_wms_ = nullptr; if (event_source_wms) - event_source_wms->OnEventAck(this); + event_source_wms->OnEventAck(this, handled); if (!event_queue_.empty()) { DCHECK(!event_ack_id_); diff --git a/components/mus/ws/window_tree_unittest.cc b/components/mus/ws/window_tree_unittest.cc index a56e23e..72d9406 100644 --- a/components/mus/ws/window_tree_unittest.cc +++ b/components/mus/ws/window_tree_unittest.cc @@ -59,66 +59,8 @@ ClientWindowId ClientWindowIdForWindow(WindowTree* tree, return client_window_id; } -class TestWindowManager : public mojom::WindowManager { - public: - TestWindowManager() : got_create_top_level_window_(false), change_id_(0u) {} - ~TestWindowManager() override {} - - bool did_call_create_top_level_window(uint32_t* change_id) { - if (!got_create_top_level_window_) - return false; - - got_create_top_level_window_ = false; - *change_id = change_id_; - return true; - } - - private: - // WindowManager: - void WmSetBounds(uint32_t change_id, - uint32_t window_id, - mojo::RectPtr bounds) override {} - void WmSetProperty(uint32_t change_id, - uint32_t window_id, - const mojo::String& name, - mojo::Array<uint8_t> value) override {} - void WmCreateTopLevelWindow( - uint32_t change_id, - mojo::Map<mojo::String, mojo::Array<uint8_t>> properties) override { - got_create_top_level_window_ = true; - change_id_ = change_id; - } - void OnAccelerator(uint32_t id, mojom::EventPtr event) override {} - - bool got_create_top_level_window_; - uint32_t change_id_; - - DISALLOW_COPY_AND_ASSIGN(TestWindowManager); -}; - // ----------------------------------------------------------------------------- -class TestDisplayBinding : public DisplayBinding { - public: - TestDisplayBinding(Display* display, WindowServer* window_server) - : display_(display), window_server_(window_server) {} - ~TestDisplayBinding() override {} - - private: - // DisplayBinding: - WindowTree* CreateWindowTree(ServerWindow* root) override { - return window_server_->EmbedAtWindow( - root, mojo::shell::mojom::kRootUserID, - mus::mojom::WindowTreeClientPtr(), - make_scoped_ptr(new WindowManagerAccessPolicy)); - } - - Display* display_; - WindowServer* window_server_; - - DISALLOW_COPY_AND_ASSIGN(TestDisplayBinding); -}; - ui::PointerEvent CreatePointerDownEvent(int x, int y) { return ui::PointerEvent(ui::TouchEvent(ui::ET_TOUCH_PRESSED, gfx::Point(x, y), 1, ui::EventTimeForNow())); |