summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjonross <jonross@chromium.org>2016-03-22 10:40:04 -0700
committerCommit bot <commit-bot@chromium.org>2016-03-22 17:42:27 +0000
commita3e8ee8bca171e70a591d8ef4927cae08e9484cc (patch)
treee47d398719e5c8b0d07c84d5472f6cb1b50d2d19
parent4dc1dc6668a87620574dd43392046c3691914da2 (diff)
downloadchromium_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.gn4
-rw-r--r--components/mus/ws/accelerator.cc124
-rw-r--r--components/mus/ws/accelerator.h71
-rw-r--r--components/mus/ws/event_dispatcher.cc178
-rw-r--r--components/mus/ws/event_dispatcher.h17
-rw-r--r--components/mus/ws/event_dispatcher_delegate.h4
-rw-r--r--components/mus/ws/event_dispatcher_unittest.cc47
-rw-r--r--components/mus/ws/test_utils.cc24
-rw-r--r--components/mus/ws/test_utils.h77
-rw-r--r--components/mus/ws/window_manager_state.cc43
-rw-r--r--components/mus/ws/window_manager_state.h15
-rw-r--r--components/mus/ws/window_manager_state_unittest.cc357
-rw-r--r--components/mus/ws/window_tree.cc2
-rw-r--r--components/mus/ws/window_tree_unittest.cc58
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()));