summaryrefslogtreecommitdiffstats
path: root/mojo/services/window_manager
diff options
context:
space:
mode:
Diffstat (limited to 'mojo/services/window_manager')
-rw-r--r--mojo/services/window_manager/BUILD.gn129
-rw-r--r--mojo/services/window_manager/DEPS6
-rw-r--r--mojo/services/window_manager/basic_focus_rules.cc171
-rw-r--r--mojo/services/window_manager/basic_focus_rules.h49
-rw-r--r--mojo/services/window_manager/capture_controller.cc103
-rw-r--r--mojo/services/window_manager/capture_controller.h49
-rw-r--r--mojo/services/window_manager/capture_controller_observer.h20
-rw-r--r--mojo/services/window_manager/focus_controller.cc317
-rw-r--r--mojo/services/window_manager/focus_controller.h102
-rw-r--r--mojo/services/window_manager/focus_controller_observer.h35
-rw-r--r--mojo/services/window_manager/focus_controller_unittest.cc1197
-rw-r--r--mojo/services/window_manager/focus_rules.h65
-rw-r--r--mojo/services/window_manager/hit_test.h45
-rw-r--r--mojo/services/window_manager/main.cc92
-rw-r--r--mojo/services/window_manager/native_viewport_event_dispatcher_impl.cc36
-rw-r--r--mojo/services/window_manager/native_viewport_event_dispatcher_impl.h42
-rw-r--r--mojo/services/window_manager/run_all_unittests.cc43
-rw-r--r--mojo/services/window_manager/view_event_dispatcher.cc55
-rw-r--r--mojo/services/window_manager/view_event_dispatcher.h47
-rw-r--r--mojo/services/window_manager/view_target.cc192
-rw-r--r--mojo/services/window_manager/view_target.h100
-rw-r--r--mojo/services/window_manager/view_target_unittest.cc81
-rw-r--r--mojo/services/window_manager/view_targeter.cc109
-rw-r--r--mojo/services/window_manager/view_targeter.h44
-rw-r--r--mojo/services/window_manager/view_targeter_unittest.cc114
-rw-r--r--mojo/services/window_manager/window_manager_api_unittest.cc260
-rw-r--r--mojo/services/window_manager/window_manager_app.cc417
-rw-r--r--mojo/services/window_manager/window_manager_app.h213
-rw-r--r--mojo/services/window_manager/window_manager_app_android.cc20
-rw-r--r--mojo/services/window_manager/window_manager_app_linux.cc14
-rw-r--r--mojo/services/window_manager/window_manager_app_win.cc14
-rw-r--r--mojo/services/window_manager/window_manager_apptest.cc212
-rw-r--r--mojo/services/window_manager/window_manager_delegate.h26
-rw-r--r--mojo/services/window_manager/window_manager_impl.cc98
-rw-r--r--mojo/services/window_manager/window_manager_impl.h68
-rw-r--r--mojo/services/window_manager/window_manager_test_util.cc39
-rw-r--r--mojo/services/window_manager/window_manager_test_util.h43
37 files changed, 4667 insertions, 0 deletions
diff --git a/mojo/services/window_manager/BUILD.gn b/mojo/services/window_manager/BUILD.gn
new file mode 100644
index 0000000..c34ebe1
--- /dev/null
+++ b/mojo/services/window_manager/BUILD.gn
@@ -0,0 +1,129 @@
+# Copyright 2014 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.
+
+import("//build/config/ui.gni")
+import("//third_party/mojo/src/mojo/public/mojo_application.gni")
+import("//testing/test.gni")
+
+mojo_native_application("window_manager") {
+ sources = [
+ "main.cc",
+ ]
+
+ public_deps = [
+ ":lib",
+ ]
+
+ deps = [
+ "//base",
+ "//mojo/application",
+ "//mojo/common:tracing_impl",
+ "//third_party/mojo_services/src/view_manager/public/cpp",
+ ]
+}
+
+source_set("lib") {
+ sources = [
+ "basic_focus_rules.cc",
+ "basic_focus_rules.h",
+ "capture_controller.cc",
+ "capture_controller.h",
+ "capture_controller_observer.h",
+ "focus_controller.cc",
+ "focus_controller.h",
+ "focus_controller_observer.h",
+ "focus_rules.h",
+ "native_viewport_event_dispatcher_impl.cc",
+ "native_viewport_event_dispatcher_impl.h",
+ "view_event_dispatcher.cc",
+ "view_event_dispatcher.h",
+ "view_target.cc",
+ "view_target.h",
+ "view_targeter.cc",
+ "view_targeter.h",
+ "window_manager_app.cc",
+ "window_manager_app.h",
+ "window_manager_app_android.cc",
+ "window_manager_app_linux.cc",
+ "window_manager_app_win.cc",
+ "window_manager_delegate.h",
+ "window_manager_impl.cc",
+ "window_manager_impl.h",
+ ]
+
+ deps = [
+ "//base",
+ "//ui/base",
+ "//ui/events",
+ "//ui/gfx",
+ "//ui/gfx/geometry",
+ "//mojo/application",
+ "//mojo/common",
+ "//mojo/converters/geometry",
+ "//mojo/converters/input_events",
+ "//third_party/mojo/src/mojo/public/cpp/bindings:bindings",
+ "//third_party/mojo/src/mojo/public/interfaces/application",
+ "//third_party/mojo_services/src/native_viewport/public/interfaces",
+ "//third_party/mojo_services/src/view_manager/public/cpp",
+ "//third_party/mojo_services/src/window_manager/public/interfaces",
+ ]
+}
+
+test("window_manager_unittests") {
+ sources = [
+ "focus_controller_unittest.cc",
+ "run_all_unittests.cc",
+ "view_target_unittest.cc",
+ "view_targeter_unittest.cc",
+ "window_manager_api_unittest.cc",
+ "window_manager_test_util.cc",
+ "window_manager_test_util.h",
+ ]
+
+ public_deps = [
+ ":lib",
+ ]
+
+ deps = [
+ "//base/test:test_support",
+ "//mojo/converters/geometry",
+ "//third_party/mojo/src/mojo/edk/system",
+ "//mojo/environment:chromium",
+ "//third_party/mojo/src/mojo/public/cpp/application",
+ "//third_party/mojo_services/src/view_manager/public/cpp",
+ "//third_party/mojo_services/src/view_manager/public/interfaces",
+ "//third_party/mojo_services/src/window_manager/public/interfaces",
+ "//mojo/shell/application_manager",
+ "//mojo/shell:test_support",
+ "//testing/gtest",
+ "//ui/events:test_support",
+ "//ui/gfx",
+ "//ui/gfx:test_support",
+ "//ui/gl",
+ ]
+
+ if (use_x11) {
+ deps += [ "//ui/gfx/x" ]
+ }
+}
+
+mojo_native_application("window_manager_apptests") {
+ testonly = true
+
+ sources = [
+ "window_manager_apptest.cc",
+ ]
+
+ deps = [
+ "//base",
+ "//mojo/application",
+ "//mojo/application:test_support",
+ "//mojo/environment:chromium",
+ "//third_party/mojo/src/mojo/public/cpp/system:system",
+ "//third_party/mojo_services/src/view_manager/public/cpp",
+ "//third_party/mojo_services/src/window_manager/public/interfaces",
+ ]
+
+ data_deps = [ ":window_manager($default_toolchain)" ]
+}
diff --git a/mojo/services/window_manager/DEPS b/mojo/services/window_manager/DEPS
new file mode 100644
index 0000000..4a637c6
--- /dev/null
+++ b/mojo/services/window_manager/DEPS
@@ -0,0 +1,6 @@
+include_rules = [
+ "+mojo/application",
+ "+mojo/converters",
+ "+third_party/mojo_services",
+ "+ui",
+]
diff --git a/mojo/services/window_manager/basic_focus_rules.cc b/mojo/services/window_manager/basic_focus_rules.cc
new file mode 100644
index 0000000..5ba49af
--- /dev/null
+++ b/mojo/services/window_manager/basic_focus_rules.cc
@@ -0,0 +1,171 @@
+// Copyright 2014 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 "mojo/services/window_manager/basic_focus_rules.h"
+
+#include "base/macros.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view.h"
+
+using mojo::View;
+
+namespace window_manager {
+
+BasicFocusRules::BasicFocusRules(View* window_container)
+ : window_container_(window_container) {
+}
+
+BasicFocusRules::~BasicFocusRules() {}
+
+bool BasicFocusRules::SupportsChildActivation(View* view) const {
+ return true;
+}
+
+bool BasicFocusRules::IsToplevelView(View* view) const {
+ if (!IsViewParentedToWindowContainer(view))
+ return false;
+
+ // The window must exist within a container that supports activation.
+ // The window cannot be blocked by a modal transient.
+ return SupportsChildActivation(view->parent());
+}
+
+bool BasicFocusRules::CanActivateView(View* view) const {
+ if (!view)
+ return true;
+
+ // Only toplevel windows can be activated
+ if (!IsToplevelView(view))
+ return false;
+
+ // The view must be visible.
+ if (!view->visible())
+ return false;
+
+ // TODO(erg): The aura version of this class asks the aura::Window's
+ // ActivationDelegate whether the window is activatable.
+
+ // A window must be focusable to be activatable. We don't call
+ // CanFocusWindow() from here because it will call back to us via
+ // GetActivatableWindow().
+ if (!CanFocusViewImpl(view))
+ return false;
+
+ // TODO(erg): In the aura version, we also check whether the window is
+ // blocked by a modal transient window.
+
+ return true;
+}
+
+bool BasicFocusRules::CanFocusView(View* view) const {
+ // It is possible to focus a NULL window, it is equivalent to clearing focus.
+ if (!view)
+ return true;
+
+ // The focused view is always inside the active view, so views that aren't
+ // activatable can't contain the focused view.
+ View* activatable = GetActivatableView(view);
+ if (!activatable || !activatable->Contains(view))
+ return false;
+ return CanFocusViewImpl(view);
+}
+
+View* BasicFocusRules::GetToplevelView(View* view) const {
+ View* parent = view->parent();
+ View* child = view;
+ while (parent) {
+ if (IsToplevelView(child))
+ return child;
+
+ parent = parent->parent();
+ child = child->parent();
+ }
+
+ return nullptr;
+}
+
+View* BasicFocusRules::GetActivatableView(View* view) const {
+ View* parent = view->parent();
+ View* child = view;
+ while (parent) {
+ if (CanActivateView(child))
+ return child;
+
+ // TODO(erg): In the aura version of this class, we have a whole bunch of
+ // checks to support modal transient windows, and transient parents.
+
+ parent = parent->parent();
+ child = child->parent();
+ }
+
+ return nullptr;
+}
+
+View* BasicFocusRules::GetFocusableView(View* view) const {
+ if (CanFocusView(view))
+ return view;
+
+ // |view| may be in a hierarchy that is non-activatable, in which case we
+ // need to cut over to the activatable hierarchy.
+ View* activatable = GetActivatableView(view);
+ if (!activatable) {
+ // There may not be a related activatable hierarchy to cut over to, in which
+ // case we try an unrelated one.
+ View* toplevel = GetToplevelView(view);
+ if (toplevel)
+ activatable = GetNextActivatableView(toplevel);
+ if (!activatable)
+ return nullptr;
+ }
+
+ if (!activatable->Contains(view)) {
+ // If there's already a child window focused in the activatable hierarchy,
+ // just use that (i.e. don't shift focus), otherwise we need to at least cut
+ // over to the activatable hierarchy.
+ View* focused = GetFocusableView(activatable);
+ return activatable->Contains(focused) ? focused : activatable;
+ }
+
+ while (view && !CanFocusView(view))
+ view = view->parent();
+ return view;
+}
+
+View* BasicFocusRules::GetNextActivatableView(View* activatable) const {
+ DCHECK(activatable);
+
+ // In the basic scenarios handled by BasicFocusRules, the pool of activatable
+ // windows is limited to the |ignore|'s siblings.
+ const View::Children& siblings = activatable->parent()->children();
+ DCHECK(!siblings.empty());
+
+ for (auto rit = siblings.rbegin(); rit != siblings.rend(); ++rit) {
+ View* cur = *rit;
+ if (cur == activatable)
+ continue;
+ if (CanActivateView(cur))
+ return cur;
+ }
+ return nullptr;
+}
+
+// TODO(erg): aura::Window::CanFocus() exists. View::CanFocus() does
+// not. This is a hack that does everything that Window::CanFocus() currently
+// does that doesn't require a delegate or an EventClient.
+bool BasicFocusRules::CanFocusViewImpl(View* view) const {
+ // TODO(erg): In unit tests, views will never be drawn, so we can't rely on
+ // IsDrawn() here.
+ if (IsViewParentedToWindowContainer(view))
+ return view->visible();
+
+ // TODO(erg): Add the intermediary delegate and event client checks once we
+ // have those.
+
+ return CanFocusViewImpl(view->parent());
+}
+
+bool BasicFocusRules::IsViewParentedToWindowContainer(View* view) const {
+ return view->parent() == window_container_;
+}
+
+} // namespace mojo
diff --git a/mojo/services/window_manager/basic_focus_rules.h b/mojo/services/window_manager/basic_focus_rules.h
new file mode 100644
index 0000000..d14a9b0d
--- /dev/null
+++ b/mojo/services/window_manager/basic_focus_rules.h
@@ -0,0 +1,49 @@
+// Copyright 2014 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 SERVICES_WINDOW_MANAGER_BASIC_FOCUS_RULES_H_
+#define SERVICES_WINDOW_MANAGER_BASIC_FOCUS_RULES_H_
+
+#include "mojo/services/window_manager/focus_rules.h"
+
+namespace mojo {
+class View;
+}
+
+namespace window_manager {
+
+// The focusing rules used inside a window manager.
+//
+// This is intended to be a user supplyable, subclassable component passed to
+// WindowManagerApp, allowing for the creation of other window managers.
+class BasicFocusRules : public FocusRules {
+ public:
+ BasicFocusRules(mojo::View* window_container);
+ ~BasicFocusRules() override;
+
+ protected:
+ // Overridden from mojo::FocusRules:
+ bool SupportsChildActivation(mojo::View* view) const override;
+ bool IsToplevelView(mojo::View* view) const override;
+ bool CanActivateView(mojo::View* view) const override;
+ bool CanFocusView(mojo::View* view) const override;
+ mojo::View* GetToplevelView(mojo::View* view) const override;
+ mojo::View* GetActivatableView(mojo::View* view) const override;
+ mojo::View* GetFocusableView(mojo::View* view) const override;
+ mojo::View* GetNextActivatableView(mojo::View* activatable) const override;
+
+ private:
+ bool CanFocusViewImpl(mojo::View* view) const;
+
+ // Tests to see if |view| is in |window_container_|.
+ bool IsViewParentedToWindowContainer(mojo::View* view) const;
+
+ mojo::View* window_container_;
+
+ DISALLOW_COPY_AND_ASSIGN(BasicFocusRules);
+};
+
+} // namespace window_manager
+
+#endif // SERVICES_WINDOW_MANAGER_BASIC_FOCUS_RULES_H_
diff --git a/mojo/services/window_manager/capture_controller.cc b/mojo/services/window_manager/capture_controller.cc
new file mode 100644
index 0000000..ad6891c
--- /dev/null
+++ b/mojo/services/window_manager/capture_controller.cc
@@ -0,0 +1,103 @@
+// Copyright 2014 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 "mojo/services/window_manager/capture_controller.h"
+
+#include "mojo/services/window_manager/capture_controller_observer.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view_property.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view_tracker.h"
+
+DECLARE_VIEW_PROPERTY_TYPE(window_manager::CaptureController*);
+
+namespace window_manager {
+
+namespace {
+DEFINE_VIEW_PROPERTY_KEY(CaptureController*,
+ kRootViewCaptureController,
+ nullptr);
+} // namespace
+
+CaptureController::CaptureController()
+ : capture_view_(nullptr) {}
+
+CaptureController::~CaptureController() {}
+
+void CaptureController::AddObserver(CaptureControllerObserver* observer) {
+ capture_controller_observers_.AddObserver(observer);
+}
+
+void CaptureController::RemoveObserver(CaptureControllerObserver* observer) {
+ capture_controller_observers_.RemoveObserver(observer);
+}
+
+void CaptureController::SetCapture(mojo::View* view) {
+ if (capture_view_ == view)
+ return;
+
+ if (capture_view_)
+ capture_view_->RemoveObserver(this);
+
+ mojo::View* old_capture_view = capture_view_;
+ capture_view_ = view;
+
+ if (capture_view_)
+ capture_view_->AddObserver(this);
+
+ NotifyCaptureChange(capture_view_, old_capture_view);
+}
+
+void CaptureController::ReleaseCapture(mojo::View* view) {
+ if (capture_view_ != view)
+ return;
+ SetCapture(nullptr);
+}
+
+mojo::View* CaptureController::GetCapture() {
+ return capture_view_;
+}
+
+void CaptureController::NotifyCaptureChange(mojo::View* new_capture,
+ mojo::View* old_capture) {
+ mojo::ViewTracker view_tracker;
+ if (new_capture)
+ view_tracker.Add(new_capture);
+ if (old_capture)
+ view_tracker.Add(old_capture);
+
+ FOR_EACH_OBSERVER(
+ CaptureControllerObserver, capture_controller_observers_,
+ OnCaptureChanged(view_tracker.Contains(new_capture) ? new_capture
+ : nullptr));
+}
+
+void CaptureController::OnViewDestroying(mojo::View* view) {
+ if (view == capture_view_) {
+ view->RemoveObserver(this);
+ NotifyCaptureChange(nullptr, view);
+ capture_view_ = nullptr;
+ }
+}
+
+void SetCaptureController(mojo::View* root_view,
+ CaptureController* capture_controller) {
+ DCHECK_EQ(root_view->GetRoot(), root_view);
+ root_view->SetLocalProperty(kRootViewCaptureController, capture_controller);
+}
+
+CaptureController* GetCaptureController(mojo::View* root_view) {
+ if (root_view)
+ DCHECK_EQ(root_view->GetRoot(), root_view);
+ return root_view ?
+ root_view->GetLocalProperty(kRootViewCaptureController) : nullptr;
+}
+
+mojo::View* GetCaptureView(mojo::View* view) {
+ mojo::View* root = view->GetRoot();
+ if (!root)
+ return nullptr;
+ CaptureController* controller = GetCaptureController(root);
+ return controller ? controller->GetCapture() : nullptr;
+}
+
+} // namespace window_manager
diff --git a/mojo/services/window_manager/capture_controller.h b/mojo/services/window_manager/capture_controller.h
new file mode 100644
index 0000000..64a3339
--- /dev/null
+++ b/mojo/services/window_manager/capture_controller.h
@@ -0,0 +1,49 @@
+// Copyright 2014 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 SERVICES_WINDOW_MANAGER_CAPTURE_CONTROLLER_H_
+#define SERVICES_WINDOW_MANAGER_CAPTURE_CONTROLLER_H_
+
+#include "base/observer_list.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view_observer.h"
+
+namespace window_manager {
+
+class CaptureControllerObserver;
+
+// Manages input capture. A view which has capture will take all input events.
+class CaptureController : public mojo::ViewObserver {
+ public:
+ CaptureController();
+ ~CaptureController() override;
+
+ void AddObserver(CaptureControllerObserver* observer);
+ void RemoveObserver(CaptureControllerObserver* observer);
+
+ void SetCapture(mojo::View* view);
+ void ReleaseCapture(mojo::View* view);
+ mojo::View* GetCapture();
+
+ private:
+ void NotifyCaptureChange(mojo::View* new_capture, mojo::View* old_capture);
+
+ // Overridden from ViewObserver:
+ void OnViewDestroying(mojo::View* view) override;
+
+ // The current capture view. Null if there is no capture view.
+ mojo::View* capture_view_;
+
+ ObserverList<CaptureControllerObserver> capture_controller_observers_;
+
+ DISALLOW_COPY_AND_ASSIGN(CaptureController);
+};
+
+void SetCaptureController(mojo::View* view,
+ CaptureController* capture_controller);
+CaptureController* GetCaptureController(mojo::View* view);
+mojo::View* GetCaptureView(mojo::View* view);
+
+} // namespace window_manager
+
+#endif // SERVICES_WINDOW_MANAGER_CAPTURE_CONTROLLER_H_
diff --git a/mojo/services/window_manager/capture_controller_observer.h b/mojo/services/window_manager/capture_controller_observer.h
new file mode 100644
index 0000000..fc62d60
--- /dev/null
+++ b/mojo/services/window_manager/capture_controller_observer.h
@@ -0,0 +1,20 @@
+// Copyright 2014 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 SERVICES_WINDOW_MANAGER_CAPTURE_CONTROLLER_OBSERVER_H_
+#define SERVICES_WINDOW_MANAGER_CAPTURE_CONTROLLER_OBSERVER_H_
+
+namespace window_manager {
+
+class CaptureControllerObserver {
+ public:
+ virtual void OnCaptureChanged(mojo::View* gained_capture) = 0;
+
+ protected:
+ virtual ~CaptureControllerObserver() {}
+};
+
+} // namespace window_manager
+
+#endif // SERVICES_WINDOW_MANAGER_CAPTURE_CONTROLLER_OBSERVER_H_
diff --git a/mojo/services/window_manager/focus_controller.cc b/mojo/services/window_manager/focus_controller.cc
new file mode 100644
index 0000000..25d60d6
--- /dev/null
+++ b/mojo/services/window_manager/focus_controller.cc
@@ -0,0 +1,317 @@
+// Copyright 2014 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 "mojo/services/window_manager/focus_controller.h"
+
+#include "base/auto_reset.h"
+#include "mojo/services/window_manager/focus_controller_observer.h"
+#include "mojo/services/window_manager/focus_rules.h"
+#include "mojo/services/window_manager/view_target.h"
+#include "mojo/services/window_manager/window_manager_app.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view_property.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view_tracker.h"
+#include "ui/events/event.h"
+
+DECLARE_VIEW_PROPERTY_TYPE(window_manager::FocusController*);
+
+using mojo::View;
+
+namespace window_manager {
+
+namespace {
+DEFINE_VIEW_PROPERTY_KEY(FocusController*, kRootViewFocusController, nullptr);
+} // namespace
+
+FocusController::FocusController(scoped_ptr<FocusRules> rules)
+ : active_view_(nullptr),
+ focused_view_(nullptr),
+ updating_focus_(false),
+ updating_activation_(false),
+ rules_(rules.Pass()),
+ observer_manager_(this) {
+ DCHECK(rules_);
+}
+
+FocusController::~FocusController() {}
+
+void FocusController::AddObserver(FocusControllerObserver* observer) {
+ focus_controller_observers_.AddObserver(observer);
+}
+
+void FocusController::RemoveObserver(FocusControllerObserver* observer) {
+ focus_controller_observers_.RemoveObserver(observer);
+}
+
+void FocusController::ActivateView(View* view) {
+ FocusView(view);
+}
+
+void FocusController::DeactivateView(View* view) {
+ if (view)
+ FocusView(rules_->GetNextActivatableView(view));
+}
+
+View* FocusController::GetActiveView() {
+ return active_view_;
+}
+
+View* FocusController::GetActivatableView(View* view) {
+ return rules_->GetActivatableView(view);
+}
+
+View* FocusController::GetToplevelView(View* view) {
+ return rules_->GetToplevelView(view);
+}
+
+bool FocusController::CanActivateView(View* view) const {
+ return rules_->CanActivateView(view);
+}
+
+void FocusController::FocusView(View* view) {
+ if (view &&
+ (view->Contains(focused_view_) || view->Contains(active_view_))) {
+ return;
+ }
+
+ // Focusing a window also activates its containing activatable window. Note
+ // that the rules could redirect activation activation and/or focus.
+ View* focusable = rules_->GetFocusableView(view);
+ View* activatable =
+ focusable ? rules_->GetActivatableView(focusable) : nullptr;
+
+ // We need valid focusable/activatable windows in the event we're not clearing
+ // focus. "Clearing focus" is inferred by whether or not |window| passed to
+ // this function is non-null.
+ if (view && (!focusable || !activatable))
+ return;
+ DCHECK((focusable && activatable) || !view);
+
+ // Activation change observers may change the focused window. If this happens
+ // we must not adjust the focus below since this will clobber that change.
+ View* last_focused_view = focused_view_;
+ if (!updating_activation_)
+ SetActiveView(view, activatable);
+
+ // If the window's ActivationChangeObserver shifted focus to a valid window,
+ // we don't want to focus the window we thought would be focused by default.
+ bool activation_changed_focus = last_focused_view != focused_view_;
+ if (!updating_focus_ && (!activation_changed_focus || !focused_view_)) {
+ if (active_view_ && focusable)
+ DCHECK(active_view_->Contains(focusable));
+ SetFocusedView(focusable);
+ }
+}
+
+void FocusController::ResetFocusWithinActiveView(View* view) {
+ DCHECK(view);
+ if (!active_view_)
+ return;
+ if (!active_view_->Contains(view))
+ return;
+ SetFocusedView(view);
+}
+
+View* FocusController::GetFocusedView() {
+ return focused_view_;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// FocusController, ui::EventHandler implementation:
+
+void FocusController::OnKeyEvent(ui::KeyEvent* event) {
+}
+
+void FocusController::OnMouseEvent(ui::MouseEvent* event) {
+ if (event->type() == ui::ET_MOUSE_PRESSED && !event->handled()) {
+ View* view = static_cast<ViewTarget*>(event->target())->view();
+ ViewFocusedFromInputEvent(view);
+ }
+}
+
+void FocusController::OnScrollEvent(ui::ScrollEvent* event) {
+}
+
+void FocusController::OnTouchEvent(ui::TouchEvent* event) {
+ if (event->type() == ui::ET_TOUCH_PRESSED && !event->handled()) {
+ View* view = static_cast<ViewTarget*>(event->target())->view();
+ ViewFocusedFromInputEvent(view);
+ }
+}
+
+void FocusController::OnGestureEvent(ui::GestureEvent* event) {
+ if (event->type() == ui::ET_GESTURE_BEGIN &&
+ event->details().touch_points() == 1 &&
+ !event->handled()) {
+ View* view = static_cast<ViewTarget*>(event->target())->view();
+ ViewFocusedFromInputEvent(view);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// FocusController, mojo::ViewObserver implementation:
+
+void FocusController::OnViewVisibilityChanged(View* view) {
+ bool visible = view->visible();
+ if (!visible)
+ ViewLostFocusFromDispositionChange(view, view->parent());
+}
+
+void FocusController::OnViewDestroying(View* view) {
+ ViewLostFocusFromDispositionChange(view, view->parent());
+}
+
+void FocusController::OnTreeChanging(const TreeChangeParams& params) {
+ // TODO(erg): In the aura version, you could get into a situation where you
+ // have different focus clients, so you had to check for that. Does that
+ // happen here? Could we get away with not checking if it does?
+ if (params.receiver == active_view_ &&
+ params.target->Contains(params.receiver) &&
+ (!params.new_parent ||
+ /* different_focus_clients */ false)) {
+ ViewLostFocusFromDispositionChange(params.receiver, params.old_parent);
+ }
+}
+
+void FocusController::OnTreeChanged(const TreeChangeParams& params) {
+ // TODO(erg): Same as Changing version.
+ if (params.receiver == focused_view_ &&
+ params.target->Contains(params.receiver) &&
+ (!params.new_parent ||
+ /* different_focus_clients */ false)) {
+ ViewLostFocusFromDispositionChange(params.receiver, params.old_parent);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// FocusController, private:
+
+void FocusController::SetFocusedView(View* view) {
+ if (updating_focus_ || view == focused_view_)
+ return;
+ DCHECK(rules_->CanFocusView(view));
+ if (view)
+ DCHECK_EQ(view, rules_->GetFocusableView(view));
+
+ base::AutoReset<bool> updating_focus(&updating_focus_, true);
+ View* lost_focus = focused_view_;
+
+ // TODO(erg): In the aura version, we reset the text input client here. Do
+ // that if we bring in something like the TextInputClient.
+
+ // Allow for the window losing focus to be deleted during dispatch. If it is
+ // deleted pass null to observers instead of a deleted window.
+ mojo::ViewTracker view_tracker;
+ if (lost_focus)
+ view_tracker.Add(lost_focus);
+ if (focused_view_ && observer_manager_.IsObserving(focused_view_) &&
+ focused_view_ != active_view_) {
+ observer_manager_.Remove(focused_view_);
+ }
+ focused_view_ = view;
+ if (focused_view_ && !observer_manager_.IsObserving(focused_view_))
+ observer_manager_.Add(focused_view_);
+
+ FOR_EACH_OBSERVER(FocusControllerObserver, focus_controller_observers_,
+ OnFocused(focused_view_));
+
+ // TODO(erg): In aura, there's a concept of a single FocusChangeObserver that
+ // is attached to an aura::Window. We don't currently have this in
+ // mojo::View, but if we add it later, we should make something analogous
+ // here.
+
+ // TODO(erg): In the aura version, we reset the TextInputClient here, too.
+}
+
+void FocusController::SetActiveView(View* requested_view, View* view) {
+ if (updating_activation_)
+ return;
+
+ if (view == active_view_) {
+ if (requested_view) {
+ FOR_EACH_OBSERVER(FocusControllerObserver,
+ focus_controller_observers_,
+ OnAttemptToReactivateView(requested_view,
+ active_view_));
+ }
+ return;
+ }
+
+ DCHECK(rules_->CanActivateView(view));
+ if (view)
+ DCHECK_EQ(view, rules_->GetActivatableView(view));
+
+ base::AutoReset<bool> updating_activation(&updating_activation_, true);
+ View* lost_activation = active_view_;
+ // Allow for the window losing activation to be deleted during dispatch. If
+ // it is deleted pass null to observers instead of a deleted window.
+ mojo::ViewTracker view_tracker;
+ if (lost_activation)
+ view_tracker.Add(lost_activation);
+ if (active_view_ && observer_manager_.IsObserving(active_view_) &&
+ focused_view_ != active_view_) {
+ observer_manager_.Remove(active_view_);
+ }
+ active_view_ = view;
+ if (active_view_ && !observer_manager_.IsObserving(active_view_))
+ observer_manager_.Add(active_view_);
+
+ if (active_view_) {
+ // TODO(erg): Reenable this when we have modal windows.
+ // StackTransientParentsBelowModalWindow(active_view_);
+
+ active_view_->MoveToFront();
+ }
+
+ // TODO(erg): Individual windows can have a single ActivationChangeObserver
+ // set on them. In the aura version of this code, it sends an
+ // OnWindowActivated message to both the window that lost activation, and the
+ // window that gained it.
+
+ FOR_EACH_OBSERVER(FocusControllerObserver, focus_controller_observers_,
+ OnActivated(active_view_));
+}
+
+void FocusController::ViewLostFocusFromDispositionChange(
+ View* view,
+ View* next) {
+ // TODO(erg): We clear the modality state here in the aura::Window version of
+ // this class, and should probably do the same once we have modal windows.
+
+ // TODO(beng): See if this function can be replaced by a call to
+ // FocusWindow().
+ // Activation adjustments are handled first in the event of a disposition
+ // changed. If an activation change is necessary, focus is reset as part of
+ // that process so there's no point in updating focus independently.
+ if (view == active_view_) {
+ View* next_activatable = rules_->GetNextActivatableView(view);
+ SetActiveView(nullptr, next_activatable);
+ if (!(active_view_ && active_view_->Contains(focused_view_)))
+ SetFocusedView(next_activatable);
+ } else if (view->Contains(focused_view_)) {
+ // Active window isn't changing, but focused window might be.
+ SetFocusedView(rules_->GetFocusableView(next));
+ }
+}
+
+void FocusController::ViewFocusedFromInputEvent(View* view) {
+ // Only focus |window| if it or any of its parents can be focused. Otherwise
+ // FocusWindow() will focus the topmost window, which may not be the
+ // currently focused one.
+ if (rules_->CanFocusView(GetToplevelView(view)))
+ FocusView(view);
+}
+
+void SetFocusController(View* root_view, FocusController* focus_controller) {
+ DCHECK_EQ(root_view->GetRoot(), root_view);
+ root_view->SetLocalProperty(kRootViewFocusController, focus_controller);
+}
+
+FocusController* GetFocusController(View* root_view) {
+ if (root_view)
+ DCHECK_EQ(root_view->GetRoot(), root_view);
+ return root_view ?
+ root_view->GetLocalProperty(kRootViewFocusController) : nullptr;
+}
+
+} // namespace window_manager
diff --git a/mojo/services/window_manager/focus_controller.h b/mojo/services/window_manager/focus_controller.h
new file mode 100644
index 0000000..8a9e8ef
--- /dev/null
+++ b/mojo/services/window_manager/focus_controller.h
@@ -0,0 +1,102 @@
+// Copyright 2014 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 SERVICES_WINDOW_MANAGER_FOCUS_CONTROLLER_H_
+#define SERVICES_WINDOW_MANAGER_FOCUS_CONTROLLER_H_
+
+#include "base/compiler_specific.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/observer_list.h"
+#include "base/scoped_observer.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view_observer.h"
+#include "ui/events/event_handler.h"
+
+namespace window_manager {
+
+class FocusControllerObserver;
+class FocusRules;
+
+// FocusController handles focus and activation changes in a mojo window
+// manager. Within the window manager, there can only be one focused and one
+// active window at a time. When focus or activation changes, notifications are
+// sent using the FocusControllerObserver interface.
+class FocusController : public ui::EventHandler, public mojo::ViewObserver {
+ public:
+ // |rules| cannot be null.
+ explicit FocusController(scoped_ptr<FocusRules> rules);
+ ~FocusController() override;
+
+ void AddObserver(FocusControllerObserver* observer);
+ void RemoveObserver(FocusControllerObserver* observer);
+
+ void ActivateView(mojo::View* view);
+ void DeactivateView(mojo::View* view);
+ mojo::View* GetActiveView();
+ mojo::View* GetActivatableView(mojo::View* view);
+ mojo::View* GetToplevelView(mojo::View* view);
+ bool CanActivateView(mojo::View* view) const;
+
+ void FocusView(mojo::View* view);
+
+ void ResetFocusWithinActiveView(mojo::View* view);
+ mojo::View* GetFocusedView();
+
+ // Overridden from ui::EventHandler:
+ void OnKeyEvent(ui::KeyEvent* event) override;
+ void OnMouseEvent(ui::MouseEvent* event) override;
+ void OnScrollEvent(ui::ScrollEvent* event) override;
+ void OnTouchEvent(ui::TouchEvent* event) override;
+ void OnGestureEvent(ui::GestureEvent* event) override;
+
+ // Overridden from ViewObserver:
+ void OnTreeChanging(const TreeChangeParams& params) override;
+ void OnTreeChanged(const TreeChangeParams& params) override;
+ void OnViewVisibilityChanged(mojo::View* view) override;
+ void OnViewDestroying(mojo::View* view) override;
+
+ private:
+ // Internal implementation that sets the focused view, fires events etc.
+ // This function must be called with a valid focusable view.
+ void SetFocusedView(mojo::View* view);
+
+ // Internal implementation that sets the active window, fires events etc.
+ // This function must be called with a valid |activatable_window|.
+ // |requested window| refers to the window that was passed in to an external
+ // request (e.g. FocusWindow or ActivateWindow). It may be null, e.g. if
+ // SetActiveWindow was not called by an external request. |activatable_window|
+ // refers to the actual window to be activated, which may be different.
+ void SetActiveView(mojo::View* requested_view, mojo::View* activatable_view);
+
+ // Called when a window's disposition changed such that it and its hierarchy
+ // are no longer focusable/activatable. |next| is a valid window that is used
+ // as a starting point for finding a window to focus next based on rules.
+ void ViewLostFocusFromDispositionChange(mojo::View* view, mojo::View* next);
+
+ // Called when an attempt is made to focus or activate a window via an input
+ // event targeted at that window. Rules determine the best focusable window
+ // for the input window.
+ void ViewFocusedFromInputEvent(mojo::View* view);
+
+ mojo::View* active_view_;
+ mojo::View* focused_view_;
+
+ bool updating_focus_;
+ bool updating_activation_;
+
+ scoped_ptr<FocusRules> rules_;
+
+ ObserverList<FocusControllerObserver> focus_controller_observers_;
+
+ ScopedObserver<mojo::View, ViewObserver> observer_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(FocusController);
+};
+
+// Sets/Gets the focus controller for a view.
+void SetFocusController(mojo::View* view, FocusController* focus_controller);
+FocusController* GetFocusController(mojo::View* view);
+
+} // namespace window_manager
+
+#endif // SERVICES_WINDOW_MANAGER_FOCUS_CONTROLLER_H_
diff --git a/mojo/services/window_manager/focus_controller_observer.h b/mojo/services/window_manager/focus_controller_observer.h
new file mode 100644
index 0000000..c5960b0
--- /dev/null
+++ b/mojo/services/window_manager/focus_controller_observer.h
@@ -0,0 +1,35 @@
+// Copyright 2014 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 SERVICES_WINDOW_MANAGER_FOCUS_CONTROLLER_OBSERVER_H_
+#define SERVICES_WINDOW_MANAGER_FOCUS_CONTROLLER_OBSERVER_H_
+
+namespace mojo {
+class View;
+}
+
+namespace window_manager {
+
+class FocusControllerObserver {
+ public:
+ // Called when |active| gains focus, or there is no active view
+ // (|active| is null in this case.).
+ virtual void OnActivated(mojo::View* gained_active) = 0;
+
+ // Called when focus moves to |gained_focus|.
+ virtual void OnFocused(mojo::View* gained_focus) = 0;
+
+ // Called when during view activation the currently active view is
+ // selected for activation. This can happen when a view requested for
+ // activation cannot be activated because a system modal view is active.
+ virtual void OnAttemptToReactivateView(mojo::View* request_active,
+ mojo::View* actual_active) {}
+
+ protected:
+ virtual ~FocusControllerObserver() {}
+};
+
+} // namespace window_manager
+
+#endif // SERVICES_WINDOW_MANAGER_FOCUS_CONTROLLER_OBSERVER_H_
diff --git a/mojo/services/window_manager/focus_controller_unittest.cc b/mojo/services/window_manager/focus_controller_unittest.cc
new file mode 100644
index 0000000..ea60ac7
--- /dev/null
+++ b/mojo/services/window_manager/focus_controller_unittest.cc
@@ -0,0 +1,1197 @@
+// Copyright (c) 2012 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 "mojo/services/window_manager/focus_controller.h"
+
+#include "mojo/converters/geometry/geometry_type_converters.h"
+#include "mojo/services/window_manager/basic_focus_rules.h"
+#include "mojo/services/window_manager/capture_controller.h"
+#include "mojo/services/window_manager/focus_controller_observer.h"
+#include "mojo/services/window_manager/view_event_dispatcher.h"
+#include "mojo/services/window_manager/view_targeter.h"
+#include "mojo/services/window_manager/window_manager_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/event_utils.h"
+#include "ui/gfx/geometry/rect.h"
+
+using mojo::View;
+
+namespace window_manager {
+
+// Counts the number of events that occur.
+class FocusNotificationObserver : public FocusControllerObserver {
+ public:
+ FocusNotificationObserver()
+ : activation_changed_count_(0),
+ focus_changed_count_(0),
+ reactivation_count_(0),
+ reactivation_requested_view_(NULL),
+ reactivation_actual_view_(NULL) {}
+ ~FocusNotificationObserver() override {}
+
+ void ExpectCounts(int activation_changed_count, int focus_changed_count) {
+ EXPECT_EQ(activation_changed_count, activation_changed_count_);
+ EXPECT_EQ(focus_changed_count, focus_changed_count_);
+ }
+ int reactivation_count() const { return reactivation_count_; }
+ View* reactivation_requested_view() const {
+ return reactivation_requested_view_;
+ }
+ View* reactivation_actual_view() const {
+ return reactivation_actual_view_;
+ }
+
+ protected:
+ // Overridden from FocusControllerObserver:
+ void OnActivated(View* gained_active) override {
+ ++activation_changed_count_;
+ }
+
+ void OnFocused(View* gained_focus) override { ++focus_changed_count_; }
+
+ void OnAttemptToReactivateView(View* request_active,
+ View* actual_active) override {
+ ++reactivation_count_;
+ reactivation_requested_view_ = request_active;
+ reactivation_actual_view_ = actual_active;
+ }
+
+ private:
+ int activation_changed_count_;
+ int focus_changed_count_;
+ int reactivation_count_;
+ View* reactivation_requested_view_;
+ View* reactivation_actual_view_;
+
+ DISALLOW_COPY_AND_ASSIGN(FocusNotificationObserver);
+};
+
+class ViewDestroyer {
+ public:
+ virtual View* GetDestroyedView() = 0;
+
+ protected:
+ virtual ~ViewDestroyer() {}
+};
+
+// FocusNotificationObserver that keeps track of whether it was notified about
+// activation changes or focus changes with a destroyed view.
+class RecordingFocusNotificationObserver : public FocusNotificationObserver {
+ public:
+ RecordingFocusNotificationObserver(FocusController* focus_controller,
+ ViewDestroyer* destroyer)
+ : focus_controller_(focus_controller),
+ destroyer_(destroyer),
+ active_(nullptr),
+ focus_(nullptr),
+ was_notified_with_destroyed_view_(false) {
+ focus_controller_->AddObserver(this);
+ }
+ ~RecordingFocusNotificationObserver() override {
+ focus_controller_->RemoveObserver(this);
+ }
+
+ bool was_notified_with_destroyed_view() const {
+ return was_notified_with_destroyed_view_;
+ }
+
+ // Overridden from FocusNotificationObserver:
+ void OnActivated(View* gained_active) override {
+ if (active_ && active_ == destroyer_->GetDestroyedView())
+ was_notified_with_destroyed_view_ = true;
+ active_ = gained_active;
+ }
+
+ void OnFocused(View* gained_focus) override {
+ if (focus_ && focus_ == destroyer_->GetDestroyedView())
+ was_notified_with_destroyed_view_ = true;
+ focus_ = gained_focus;
+ }
+
+ private:
+ FocusController* focus_controller_;
+
+ // Not owned.
+ ViewDestroyer* destroyer_;
+ View* active_;
+ View* focus_;
+
+ // Whether the observer was notified about the loss of activation or the
+ // loss of focus with a view already destroyed by |destroyer_| as the
+ // |lost_active| or |lost_focus| parameter.
+ bool was_notified_with_destroyed_view_;
+
+ DISALLOW_COPY_AND_ASSIGN(RecordingFocusNotificationObserver);
+};
+
+class DestroyOnLoseActivationFocusNotificationObserver
+ : public FocusNotificationObserver,
+ public ViewDestroyer {
+ public:
+ DestroyOnLoseActivationFocusNotificationObserver(
+ FocusController* focus_controller,
+ View* view_to_destroy,
+ View* initial_active)
+ : focus_controller_(focus_controller),
+ view_to_destroy_(view_to_destroy),
+ active_(initial_active),
+ did_destroy_(false) {
+ focus_controller_->AddObserver(this);
+ }
+ ~DestroyOnLoseActivationFocusNotificationObserver() override {
+ focus_controller_->RemoveObserver(this);
+ }
+
+ // Overridden from FocusNotificationObserver:
+ void OnActivated(View* gained_active) override {
+ if (view_to_destroy_ && active_ == view_to_destroy_) {
+ view_to_destroy_->Destroy();
+ did_destroy_ = true;
+ }
+ active_ = gained_active;
+ }
+
+ // Overridden from ViewDestroyer:
+ View* GetDestroyedView() override {
+ return did_destroy_ ? view_to_destroy_ : nullptr;
+ }
+
+ private:
+ FocusController* focus_controller_;
+ View* view_to_destroy_;
+ View* active_;
+ bool did_destroy_;
+
+ DISALLOW_COPY_AND_ASSIGN(DestroyOnLoseActivationFocusNotificationObserver);
+};
+
+class ScopedFocusNotificationObserver : public FocusNotificationObserver {
+ public:
+ ScopedFocusNotificationObserver(FocusController* focus_controller)
+ : focus_controller_(focus_controller) {
+ focus_controller_->AddObserver(this);
+ }
+ ~ScopedFocusNotificationObserver() override {
+ focus_controller_->RemoveObserver(this);
+ }
+
+ private:
+ FocusController* focus_controller_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedFocusNotificationObserver);
+};
+
+// Only responds to events if a message contains |target| as a parameter.
+class ScopedFilteringFocusNotificationObserver
+ : public FocusNotificationObserver {
+ public:
+ ScopedFilteringFocusNotificationObserver(FocusController* focus_controller,
+ View* target,
+ View* initial_active,
+ View* initial_focus)
+ : focus_controller_(focus_controller),
+ target_(target),
+ active_(initial_active),
+ focus_(initial_focus) {
+ focus_controller_->AddObserver(this);
+ }
+ ~ScopedFilteringFocusNotificationObserver() override {
+ focus_controller_->RemoveObserver(this);
+ }
+
+ private:
+ // Overridden from FocusControllerObserver:
+ void OnActivated(View* gained_active) override {
+ if (gained_active == target_ || active_ == target_)
+ FocusNotificationObserver::OnActivated(gained_active);
+ active_ = gained_active;
+ }
+
+ void OnFocused(View* gained_focus) override {
+ if (gained_focus == target_ || focus_ == target_)
+ FocusNotificationObserver::OnFocused(gained_focus);
+ focus_ = gained_focus;
+ }
+
+ void OnAttemptToReactivateView(View* request_active,
+ View* actual_active) override {
+ if (request_active == target_ || actual_active == target_) {
+ FocusNotificationObserver::OnAttemptToReactivateView(request_active,
+ actual_active);
+ }
+ }
+
+ FocusController* focus_controller_;
+ View* target_;
+ View* active_;
+ View* focus_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedFilteringFocusNotificationObserver);
+};
+
+// Used to fake the handling of events in the pre-target phase.
+class SimpleEventHandler : public ui::EventHandler {
+ public:
+ SimpleEventHandler() {}
+ ~SimpleEventHandler() override {}
+
+ // Overridden from ui::EventHandler:
+ void OnMouseEvent(ui::MouseEvent* event) override {
+ event->SetHandled();
+ }
+ void OnGestureEvent(ui::GestureEvent* event) override {
+ event->SetHandled();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SimpleEventHandler);
+};
+
+class FocusShiftingActivationObserver
+ : public FocusControllerObserver {
+ public:
+ explicit FocusShiftingActivationObserver(FocusController* focus_controller,
+ View* activated_view)
+ : focus_controller_(focus_controller),
+ activated_view_(activated_view),
+ shift_focus_to_(NULL) {}
+ ~FocusShiftingActivationObserver() override {}
+
+ void set_shift_focus_to(View* shift_focus_to) {
+ shift_focus_to_ = shift_focus_to;
+ }
+
+ private:
+ // Overridden from FocusControllerObserver:
+ void OnActivated(View* gained_active) override {
+ // Shift focus to a child. This should prevent the default focusing from
+ // occurring in FocusController::FocusView().
+ if (gained_active == activated_view_)
+ focus_controller_->FocusView(shift_focus_to_);
+ }
+
+ void OnFocused(View* gained_focus) override {}
+
+ FocusController* focus_controller_;
+ View* activated_view_;
+ View* shift_focus_to_;
+
+ DISALLOW_COPY_AND_ASSIGN(FocusShiftingActivationObserver);
+};
+
+// BasicFocusRules subclass that allows basic overrides of focus/activation to
+// be tested. This is intended more as a test that the override system works at
+// all, rather than as an exhaustive set of use cases, those should be covered
+// in tests for those FocusRules implementations.
+class TestFocusRules : public BasicFocusRules {
+ public:
+ TestFocusRules(View* root)
+ : BasicFocusRules(root), focus_restriction_(NULL) {}
+
+ // Restricts focus and activation to this view and its child hierarchy.
+ void set_focus_restriction(View* focus_restriction) {
+ focus_restriction_ = focus_restriction;
+ }
+
+ // Overridden from BasicFocusRules:
+ bool SupportsChildActivation(View* view) const override {
+ // In FocusControllerTests, only the Root has activatable children.
+ return view->GetRoot() == view;
+ }
+ bool CanActivateView(View* view) const override {
+ // Restricting focus to a non-activatable child view means the activatable
+ // parent outside the focus restriction is activatable.
+ bool can_activate =
+ CanFocusOrActivate(view) || view->Contains(focus_restriction_);
+ return can_activate ? BasicFocusRules::CanActivateView(view) : false;
+ }
+ bool CanFocusView(View* view) const override {
+ return CanFocusOrActivate(view) ? BasicFocusRules::CanFocusView(view)
+ : false;
+ }
+ View* GetActivatableView(View* view) const override {
+ return BasicFocusRules::GetActivatableView(
+ CanFocusOrActivate(view) ? view : focus_restriction_);
+ }
+ View* GetFocusableView(View* view) const override {
+ return BasicFocusRules::GetFocusableView(
+ CanFocusOrActivate(view) ? view : focus_restriction_);
+ }
+ View* GetNextActivatableView(View* ignore) const override {
+ View* next_activatable = BasicFocusRules::GetNextActivatableView(ignore);
+ return CanFocusOrActivate(next_activatable)
+ ? next_activatable
+ : GetActivatableView(focus_restriction_);
+ }
+
+ private:
+ bool CanFocusOrActivate(View* view) const {
+ return !focus_restriction_ || focus_restriction_->Contains(view);
+ }
+
+ View* focus_restriction_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestFocusRules);
+};
+
+// Common infrastructure shared by all FocusController test types.
+class FocusControllerTestBase : public testing::Test {
+ protected:
+ // Hierarchy used by all tests:
+ // root_view
+ // +-- w1
+ // | +-- w11
+ // | +-- w12
+ // +-- w2
+ // | +-- w21
+ // | +-- w211
+ // +-- w3
+ FocusControllerTestBase()
+ : root_view_(TestView::Build(0, gfx::Rect(0, 0, 800, 600))),
+ v1(TestView::Build(1, gfx::Rect(0, 0, 50, 50), root_view())),
+ v11(TestView::Build(11, gfx::Rect(5, 5, 10, 10), v1)),
+ v12(TestView::Build(12, gfx::Rect(15, 15, 10, 10), v1)),
+ v2(TestView::Build(2, gfx::Rect(75, 75, 50, 50), root_view())),
+ v21(TestView::Build(21, gfx::Rect(5, 5, 10, 10), v2)),
+ v211(TestView::Build(211, gfx::Rect(1, 1, 5, 5), v21)),
+ v3(TestView::Build(3, gfx::Rect(125, 125, 50, 50), root_view())) {}
+
+ // Overridden from testing::Test:
+ void SetUp() override {
+ testing::Test::SetUp();
+
+ test_focus_rules_ = new TestFocusRules(root_view());
+ focus_controller_.reset(
+ new FocusController(scoped_ptr<FocusRules>(test_focus_rules_)));
+ SetFocusController(root_view(), focus_controller_.get());
+
+ capture_controller_.reset(new CaptureController);
+ SetCaptureController(root_view(), capture_controller_.get());
+
+ ViewTarget* root_target = root_view_->target();
+ root_target->SetEventTargeter(scoped_ptr<ViewTargeter>(new ViewTargeter()));
+ view_event_dispatcher_.reset(new ViewEventDispatcher());
+ view_event_dispatcher_->SetRootViewTarget(root_target);
+
+ GetRootViewTarget()->AddPreTargetHandler(focus_controller_.get());
+ }
+
+ void TearDown() override {
+ GetRootViewTarget()->RemovePreTargetHandler(focus_controller_.get());
+ view_event_dispatcher_.reset();
+
+ root_view_->Destroy();
+
+ capture_controller_.reset();
+ test_focus_rules_ = nullptr; // Owned by FocusController.
+ focus_controller_.reset();
+
+ testing::Test::TearDown();
+ }
+
+ void FocusView(View* view) { focus_controller_->FocusView(view); }
+ View* GetFocusedView() { return focus_controller_->GetFocusedView(); }
+ int GetFocusedViewId() {
+ View* focused_view = GetFocusedView();
+ return focused_view ? focused_view->id() : -1;
+ }
+ void ActivateView(View* view) { focus_controller_->ActivateView(view); }
+ void DeactivateView(View* view) { focus_controller_->DeactivateView(view); }
+ View* GetActiveView() { return focus_controller_->GetActiveView(); }
+ int GetActiveViewId() {
+ View* active_view = GetActiveView();
+ return active_view ? active_view->id() : -1;
+ }
+
+ View* GetViewById(int id) { return root_view_->GetChildById(id); }
+
+ void ClickLeftButton(View* view) {
+ // Get the center bounds of |target| in |root_view_| coordinate space.
+ gfx::Point center =
+ gfx::Rect(view->bounds().To<gfx::Rect>().size()).CenterPoint();
+ ViewTarget::ConvertPointToTarget(ViewTarget::TargetFromView(view),
+ root_view_->target(), &center);
+
+ ui::MouseEvent button_down(ui::ET_MOUSE_PRESSED, center, center,
+ ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+ ui::EF_NONE);
+ ui::EventDispatchDetails details =
+ view_event_dispatcher_->OnEventFromSource(&button_down);
+ CHECK(!details.dispatcher_destroyed);
+
+ ui::MouseEvent button_up(ui::ET_MOUSE_RELEASED, center, center,
+ ui::EventTimeForNow(), ui::EF_LEFT_MOUSE_BUTTON,
+ ui::EF_NONE);
+ details = view_event_dispatcher_->OnEventFromSource(&button_up);
+ CHECK(!details.dispatcher_destroyed);
+ }
+
+ ViewTarget* GetRootViewTarget() {
+ return ViewTarget::TargetFromView(root_view());
+ }
+
+ View* root_view() { return root_view_; }
+ TestFocusRules* test_focus_rules() { return test_focus_rules_; }
+ FocusController* focus_controller() { return focus_controller_.get(); }
+ CaptureController* capture_controller() { return capture_controller_.get(); }
+
+ // Test functions.
+ virtual void BasicFocus() = 0;
+ virtual void BasicActivation() = 0;
+ virtual void FocusEvents() = 0;
+ virtual void DuplicateFocusEvents() {}
+ virtual void ActivationEvents() = 0;
+ virtual void ReactivationEvents() {}
+ virtual void DuplicateActivationEvents() {}
+ virtual void ShiftFocusWithinActiveView() {}
+ virtual void ShiftFocusToChildOfInactiveView() {}
+ virtual void ShiftFocusToParentOfFocusedView() {}
+ virtual void FocusRulesOverride() = 0;
+ virtual void ActivationRulesOverride() = 0;
+ virtual void ShiftFocusOnActivation() {}
+ virtual void ShiftFocusOnActivationDueToHide() {}
+ virtual void NoShiftActiveOnActivation() {}
+ virtual void ChangeFocusWhenNothingFocusedAndCaptured() {}
+ virtual void DontPassDestroyedView() {}
+ // TODO(erg): Also, void FocusedTextInputClient() once we build the IME.
+
+ private:
+ TestView* root_view_;
+ scoped_ptr<FocusController> focus_controller_;
+ TestFocusRules* test_focus_rules_;
+ scoped_ptr<CaptureController> capture_controller_;
+ // TODO(erg): The aura version of this class also keeps track of WMState. Do
+ // we need something analogous here?
+
+ scoped_ptr<ViewEventDispatcher> view_event_dispatcher_;
+
+ TestView* v1;
+ TestView* v11;
+ TestView* v12;
+ TestView* v2;
+ TestView* v21;
+ TestView* v211;
+ TestView* v3;
+
+ DISALLOW_COPY_AND_ASSIGN(FocusControllerTestBase);
+};
+
+// Test base for tests where focus is directly set to a target view.
+class FocusControllerDirectTestBase : public FocusControllerTestBase {
+ protected:
+ FocusControllerDirectTestBase() {}
+
+ // Different test types shift focus in different ways.
+ virtual void FocusViewDirect(View* view) = 0;
+ virtual void ActivateViewDirect(View* view) = 0;
+ virtual void DeactivateViewDirect(View* view) = 0;
+
+ // Input events do not change focus if the view can not be focused.
+ virtual bool IsInputEvent() = 0;
+
+ void FocusViewById(int id) {
+ View* view = root_view()->GetChildById(id);
+ DCHECK(view);
+ FocusViewDirect(view);
+ }
+ void ActivateViewById(int id) {
+ View* view = root_view()->GetChildById(id);
+ DCHECK(view);
+ ActivateViewDirect(view);
+ }
+
+ // Overridden from FocusControllerTestBase:
+ void BasicFocus() override {
+ EXPECT_EQ(nullptr, GetFocusedView());
+ FocusViewById(1);
+ EXPECT_EQ(1, GetFocusedViewId());
+ FocusViewById(2);
+ EXPECT_EQ(2, GetFocusedViewId());
+ }
+ void BasicActivation() override {
+ EXPECT_EQ(nullptr, GetActiveView());
+ ActivateViewById(1);
+ EXPECT_EQ(1, GetActiveViewId());
+ ActivateViewById(2);
+ EXPECT_EQ(2, GetActiveViewId());
+ // Verify that attempting to deactivate NULL does not crash and does not
+ // change activation.
+ DeactivateView(nullptr);
+ EXPECT_EQ(2, GetActiveViewId());
+ DeactivateView(GetActiveView());
+ EXPECT_EQ(1, GetActiveViewId());
+ }
+ void FocusEvents() override {
+ ScopedFocusNotificationObserver root_observer(focus_controller());
+ ScopedFilteringFocusNotificationObserver observer1(
+ focus_controller(), GetViewById(1), GetActiveView(), GetFocusedView());
+ ScopedFilteringFocusNotificationObserver observer2(
+ focus_controller(), GetViewById(2), GetActiveView(), GetFocusedView());
+
+ {
+ SCOPED_TRACE("initial state");
+ root_observer.ExpectCounts(0, 0);
+ observer1.ExpectCounts(0, 0);
+ observer2.ExpectCounts(0, 0);
+ }
+
+ FocusViewById(1);
+ {
+ SCOPED_TRACE("FocusViewById(1)");
+ root_observer.ExpectCounts(1, 1);
+ observer1.ExpectCounts(1, 1);
+ observer2.ExpectCounts(0, 0);
+ }
+
+ FocusViewById(2);
+ {
+ SCOPED_TRACE("FocusViewById(2)");
+ root_observer.ExpectCounts(2, 2);
+ observer1.ExpectCounts(2, 2);
+ observer2.ExpectCounts(1, 1);
+ }
+ }
+ void DuplicateFocusEvents() override {
+ // Focusing an existing focused view should not resend focus events.
+ ScopedFocusNotificationObserver root_observer(focus_controller());
+ ScopedFilteringFocusNotificationObserver observer1(
+ focus_controller(), GetViewById(1), GetActiveView(), GetFocusedView());
+
+ root_observer.ExpectCounts(0, 0);
+ observer1.ExpectCounts(0, 0);
+
+ FocusViewById(1);
+ root_observer.ExpectCounts(1, 1);
+ observer1.ExpectCounts(1, 1);
+
+ FocusViewById(1);
+ root_observer.ExpectCounts(1, 1);
+ observer1.ExpectCounts(1, 1);
+ }
+ void ActivationEvents() override {
+ ActivateViewById(1);
+
+ ScopedFocusNotificationObserver root_observer(focus_controller());
+ ScopedFilteringFocusNotificationObserver observer1(
+ focus_controller(), GetViewById(1), GetActiveView(), GetFocusedView());
+ ScopedFilteringFocusNotificationObserver observer2(
+ focus_controller(), GetViewById(2), GetActiveView(), GetFocusedView());
+
+ root_observer.ExpectCounts(0, 0);
+ observer1.ExpectCounts(0, 0);
+ observer2.ExpectCounts(0, 0);
+
+ ActivateViewById(2);
+ root_observer.ExpectCounts(1, 1);
+ observer1.ExpectCounts(1, 1);
+ observer2.ExpectCounts(1, 1);
+ }
+ void ReactivationEvents() override {
+ ActivateViewById(1);
+ ScopedFocusNotificationObserver root_observer(focus_controller());
+ EXPECT_EQ(0, root_observer.reactivation_count());
+ GetViewById(2)->SetVisible(false);
+ // When we attempt to activate "2", which cannot be activated because it
+ // is not visible, "1" will be reactivated.
+ ActivateViewById(2);
+ EXPECT_EQ(1, root_observer.reactivation_count());
+ EXPECT_EQ(GetViewById(2),
+ root_observer.reactivation_requested_view());
+ EXPECT_EQ(GetViewById(1),
+ root_observer.reactivation_actual_view());
+ }
+ void DuplicateActivationEvents() override {
+ ActivateViewById(1);
+
+ ScopedFocusNotificationObserver root_observer(focus_controller());
+ ScopedFilteringFocusNotificationObserver observer1(
+ focus_controller(), GetViewById(1), GetActiveView(), GetFocusedView());
+ ScopedFilteringFocusNotificationObserver observer2(
+ focus_controller(), GetViewById(2), GetActiveView(), GetFocusedView());
+
+ root_observer.ExpectCounts(0, 0);
+ observer1.ExpectCounts(0, 0);
+ observer2.ExpectCounts(0, 0);
+
+ ActivateViewById(2);
+ root_observer.ExpectCounts(1, 1);
+ observer1.ExpectCounts(1, 1);
+ observer2.ExpectCounts(1, 1);
+
+ // Activating an existing active view should not resend activation events.
+ ActivateViewById(2);
+ root_observer.ExpectCounts(1, 1);
+ observer1.ExpectCounts(1, 1);
+ observer2.ExpectCounts(1, 1);
+ }
+ void ShiftFocusWithinActiveView() override {
+ ActivateViewById(1);
+ EXPECT_EQ(1, GetActiveViewId());
+ EXPECT_EQ(1, GetFocusedViewId());
+ FocusViewById(11);
+ EXPECT_EQ(11, GetFocusedViewId());
+ FocusViewById(12);
+ EXPECT_EQ(12, GetFocusedViewId());
+ }
+ void ShiftFocusToChildOfInactiveView() override {
+ ActivateViewById(2);
+ EXPECT_EQ(2, GetActiveViewId());
+ EXPECT_EQ(2, GetFocusedViewId());
+ FocusViewById(11);
+ EXPECT_EQ(1, GetActiveViewId());
+ EXPECT_EQ(11, GetFocusedViewId());
+ }
+ void ShiftFocusToParentOfFocusedView() override {
+ ActivateViewById(1);
+ EXPECT_EQ(1, GetFocusedViewId());
+ FocusViewById(11);
+ EXPECT_EQ(11, GetFocusedViewId());
+ FocusViewById(1);
+ // Focus should _not_ shift to the parent of the already-focused view.
+ EXPECT_EQ(11, GetFocusedViewId());
+ }
+ void FocusRulesOverride() override {
+ EXPECT_EQ(NULL, GetFocusedView());
+ FocusViewById(11);
+ EXPECT_EQ(11, GetFocusedViewId());
+
+ test_focus_rules()->set_focus_restriction(GetViewById(211));
+ FocusViewById(12);
+ // Input events leave focus unchanged; direct API calls will change focus
+ // to the restricted view.
+ int focused_view = IsInputEvent() ? 11 : 211;
+ EXPECT_EQ(focused_view, GetFocusedViewId());
+
+ test_focus_rules()->set_focus_restriction(NULL);
+ FocusViewById(12);
+ EXPECT_EQ(12, GetFocusedViewId());
+ }
+ void ActivationRulesOverride() override {
+ ActivateViewById(1);
+ EXPECT_EQ(1, GetActiveViewId());
+ EXPECT_EQ(1, GetFocusedViewId());
+
+ View* v3 = GetViewById(3);
+ test_focus_rules()->set_focus_restriction(v3);
+
+ ActivateViewById(2);
+ // Input events leave activation unchanged; direct API calls will activate
+ // the restricted view.
+ int active_view = IsInputEvent() ? 1 : 3;
+ EXPECT_EQ(active_view, GetActiveViewId());
+ EXPECT_EQ(active_view, GetFocusedViewId());
+
+ test_focus_rules()->set_focus_restriction(NULL);
+ ActivateViewById(2);
+ EXPECT_EQ(2, GetActiveViewId());
+ EXPECT_EQ(2, GetFocusedViewId());
+ }
+ void ShiftFocusOnActivation() override {
+ // When a view is activated, by default that view is also focused.
+ // An ActivationChangeObserver may shift focus to another view within the
+ // same activatable view.
+ ActivateViewById(2);
+ EXPECT_EQ(2, GetFocusedViewId());
+ ActivateViewById(1);
+ EXPECT_EQ(1, GetFocusedViewId());
+
+ ActivateViewById(2);
+
+ View* target = GetViewById(1);
+
+ scoped_ptr<FocusShiftingActivationObserver> observer(
+ new FocusShiftingActivationObserver(focus_controller(), target));
+ observer->set_shift_focus_to(target->GetChildById(11));
+ focus_controller()->AddObserver(observer.get());
+
+ ActivateViewById(1);
+
+ // w1's ActivationChangeObserver shifted focus to this child, pre-empting
+ // FocusController's default setting.
+ EXPECT_EQ(11, GetFocusedViewId());
+
+ ActivateViewById(2);
+ EXPECT_EQ(2, GetFocusedViewId());
+
+ // Simulate a focus reset by the ActivationChangeObserver. This should
+ // trigger the default setting in FocusController.
+ observer->set_shift_focus_to(nullptr);
+ ActivateViewById(1);
+ EXPECT_EQ(1, GetFocusedViewId());
+
+ focus_controller()->RemoveObserver(observer.get());
+
+ ActivateViewById(2);
+ EXPECT_EQ(2, GetFocusedViewId());
+ ActivateViewById(1);
+ EXPECT_EQ(1, GetFocusedViewId());
+ }
+ void ShiftFocusOnActivationDueToHide() override {
+ // Similar to ShiftFocusOnActivation except the activation change is
+ // triggered by hiding the active view.
+ ActivateViewById(1);
+ EXPECT_EQ(1, GetFocusedViewId());
+
+ // Removes view 3 as candidate for next activatable view.
+ root_view()->GetChildById(3)->SetVisible(false);
+ EXPECT_EQ(1, GetFocusedViewId());
+
+ View* target = root_view()->GetChildById(2);
+
+ scoped_ptr<FocusShiftingActivationObserver> observer(
+ new FocusShiftingActivationObserver(focus_controller(), target));
+ observer->set_shift_focus_to(target->GetChildById(21));
+ focus_controller()->AddObserver(observer.get());
+
+ // Hide the active view.
+ root_view()->GetChildById(1)->SetVisible(false);
+
+ EXPECT_EQ(21, GetFocusedViewId());
+
+ focus_controller()->RemoveObserver(observer.get());
+ }
+ void NoShiftActiveOnActivation() override {
+ // When a view is activated, we need to prevent any change to activation
+ // from being made in response to an activation change notification.
+ }
+
+ // Verifies focus change is honored while capture held.
+ void ChangeFocusWhenNothingFocusedAndCaptured() override {
+ View* v1 = root_view()->GetChildById(1);
+ capture_controller()->SetCapture(v1);
+
+ EXPECT_EQ(-1, GetActiveViewId());
+ EXPECT_EQ(-1, GetFocusedViewId());
+
+ FocusViewById(1);
+
+ EXPECT_EQ(1, GetActiveViewId());
+ EXPECT_EQ(1, GetFocusedViewId());
+
+ capture_controller()->ReleaseCapture(v1);
+ }
+
+ // Verifies if a view that loses activation or focus is destroyed during
+ // observer notification we don't pass the destroyed view to other observers.
+ void DontPassDestroyedView() override {
+ FocusViewById(1);
+
+ EXPECT_EQ(1, GetActiveViewId());
+ EXPECT_EQ(1, GetFocusedViewId());
+
+ {
+ View* to_destroy = root_view()->GetChildById(1);
+ DestroyOnLoseActivationFocusNotificationObserver observer1(
+ focus_controller(), to_destroy, GetActiveView());
+ RecordingFocusNotificationObserver observer2(focus_controller(),
+ &observer1);
+
+ FocusViewById(2);
+
+ EXPECT_EQ(2, GetActiveViewId());
+ EXPECT_EQ(2, GetFocusedViewId());
+
+ EXPECT_EQ(to_destroy, observer1.GetDestroyedView());
+ EXPECT_FALSE(observer2.was_notified_with_destroyed_view());
+ }
+
+ {
+ View* to_destroy = root_view()->GetChildById(2);
+ DestroyOnLoseActivationFocusNotificationObserver observer1(
+ focus_controller(), to_destroy, GetActiveView());
+ RecordingFocusNotificationObserver observer2(focus_controller(),
+ &observer1);
+
+ FocusViewById(3);
+
+ EXPECT_EQ(3, GetActiveViewId());
+ EXPECT_EQ(3, GetFocusedViewId());
+
+ EXPECT_EQ(to_destroy, observer1.GetDestroyedView());
+ EXPECT_FALSE(observer2.was_notified_with_destroyed_view());
+ }
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FocusControllerDirectTestBase);
+};
+
+// Focus and Activation changes via the FocusController API.
+class FocusControllerApiTest : public FocusControllerDirectTestBase {
+ public:
+ FocusControllerApiTest() {}
+
+ private:
+ // Overridden from FocusControllerTestBase:
+ void FocusViewDirect(View* view) override { FocusView(view); }
+ void ActivateViewDirect(View* view) override { ActivateView(view); }
+ void DeactivateViewDirect(View* view) override { DeactivateView(view); }
+ bool IsInputEvent() override { return false; }
+
+ DISALLOW_COPY_AND_ASSIGN(FocusControllerApiTest);
+};
+
+// Focus and Activation changes via input events.
+class FocusControllerMouseEventTest : public FocusControllerDirectTestBase {
+ public:
+ FocusControllerMouseEventTest() {}
+
+ // Tests that a handled mouse event does not trigger a view activation.
+ void IgnoreHandledEvent() {
+ EXPECT_EQ(NULL, GetActiveView());
+ View* v1 = root_view()->GetChildById(1);
+ SimpleEventHandler handler;
+ GetRootViewTarget()->PrependPreTargetHandler(&handler);
+ ClickLeftButton(v1);
+ EXPECT_EQ(NULL, GetActiveView());
+ // TODO(erg): Add gesture testing when we get gestures working.
+ GetRootViewTarget()->RemovePreTargetHandler(&handler);
+ ClickLeftButton(v1);
+ EXPECT_EQ(1, GetActiveViewId());
+ }
+
+ private:
+ // Overridden from FocusControllerTestBase:
+ void FocusViewDirect(View* view) override { ClickLeftButton(view); }
+ void ActivateViewDirect(View* view) override { ClickLeftButton(view); }
+ void DeactivateViewDirect(View* view) override {
+ View* next_activatable = test_focus_rules()->GetNextActivatableView(view);
+ ClickLeftButton(next_activatable);
+ }
+ bool IsInputEvent() override { return true; }
+
+ DISALLOW_COPY_AND_ASSIGN(FocusControllerMouseEventTest);
+};
+
+// TODO(erg): Add a FocusControllerGestureEventTest once we have working
+// gesture forwarding and handling.
+
+// Test base for tests where focus is implicitly set to a window as the result
+// of a disposition change to the focused window or the hierarchy that contains
+// it.
+class FocusControllerImplicitTestBase : public FocusControllerTestBase {
+ protected:
+ explicit FocusControllerImplicitTestBase(bool parent) : parent_(parent) {}
+
+ View* GetDispositionView(View* view) {
+ return parent_ ? view->parent() : view;
+ }
+
+ // Change the disposition of |view| in such a way as it will lose focus.
+ virtual void ChangeViewDisposition(View* view) = 0;
+
+ // Allow each disposition change test to add additional post-disposition
+ // change expectations.
+ virtual void PostDispostionChangeExpectations() {}
+
+ // Overridden from FocusControllerTestBase:
+ void BasicFocus() override {
+ EXPECT_EQ(NULL, GetFocusedView());
+
+ View* w211 = root_view()->GetChildById(211);
+ FocusView(w211);
+ EXPECT_EQ(211, GetFocusedViewId());
+
+ ChangeViewDisposition(w211);
+ // BasicFocusRules passes focus to the parent.
+ EXPECT_EQ(parent_ ? 2 : 21, GetFocusedViewId());
+ }
+
+ void BasicActivation() override {
+ DCHECK(!parent_) << "Activation tests don't support parent changes.";
+
+ EXPECT_EQ(NULL, GetActiveView());
+
+ View* w2 = root_view()->GetChildById(2);
+ ActivateView(w2);
+ EXPECT_EQ(2, GetActiveViewId());
+
+ ChangeViewDisposition(w2);
+ EXPECT_EQ(3, GetActiveViewId());
+ PostDispostionChangeExpectations();
+ }
+
+ void FocusEvents() override {
+ View* w211 = root_view()->GetChildById(211);
+ FocusView(w211);
+
+ ScopedFocusNotificationObserver root_observer(focus_controller());
+ ScopedFilteringFocusNotificationObserver observer211(
+ focus_controller(), GetViewById(211), GetActiveView(),
+ GetFocusedView());
+
+ {
+ SCOPED_TRACE("first");
+ root_observer.ExpectCounts(0, 0);
+ observer211.ExpectCounts(0, 0);
+ }
+
+ ChangeViewDisposition(w211);
+ {
+ SCOPED_TRACE("second");
+ {
+ SCOPED_TRACE("root_observer");
+ root_observer.ExpectCounts(0, 1);
+ }
+ {
+ SCOPED_TRACE("observer211");
+ observer211.ExpectCounts(0, 1);
+ }
+ }
+ }
+
+ void ActivationEvents() override {
+ DCHECK(!parent_) << "Activation tests don't support parent changes.";
+
+ View* w2 = root_view()->GetChildById(2);
+ ActivateView(w2);
+
+ ScopedFocusNotificationObserver root_observer(focus_controller());
+ ScopedFilteringFocusNotificationObserver observer2(
+ focus_controller(), GetViewById(2), GetActiveView(), GetFocusedView());
+ ScopedFilteringFocusNotificationObserver observer3(
+ focus_controller(), GetViewById(3), GetActiveView(), GetFocusedView());
+ root_observer.ExpectCounts(0, 0);
+ observer2.ExpectCounts(0, 0);
+ observer3.ExpectCounts(0, 0);
+
+ ChangeViewDisposition(w2);
+ root_observer.ExpectCounts(1, 1);
+ observer2.ExpectCounts(1, 1);
+ observer3.ExpectCounts(1, 1);
+ }
+
+ void FocusRulesOverride() override {
+ EXPECT_EQ(NULL, GetFocusedView());
+ View* w211 = root_view()->GetChildById(211);
+ FocusView(w211);
+ EXPECT_EQ(211, GetFocusedViewId());
+
+ test_focus_rules()->set_focus_restriction(root_view()->GetChildById(11));
+ ChangeViewDisposition(w211);
+ // Normally, focus would shift to the parent (w21) but the override shifts
+ // it to 11.
+ EXPECT_EQ(11, GetFocusedViewId());
+
+ test_focus_rules()->set_focus_restriction(NULL);
+ }
+
+ void ActivationRulesOverride() override {
+ DCHECK(!parent_) << "Activation tests don't support parent changes.";
+
+ View* w1 = root_view()->GetChildById(1);
+ ActivateView(w1);
+
+ EXPECT_EQ(1, GetActiveViewId());
+ EXPECT_EQ(1, GetFocusedViewId());
+
+ View* w3 = root_view()->GetChildById(3);
+ test_focus_rules()->set_focus_restriction(w3);
+
+ // Normally, activation/focus would move to w2, but since we have a focus
+ // restriction, it should move to w3 instead.
+ ChangeViewDisposition(w1);
+ EXPECT_EQ(3, GetActiveViewId());
+ EXPECT_EQ(3, GetFocusedViewId());
+
+ test_focus_rules()->set_focus_restriction(NULL);
+ ActivateView(root_view()->GetChildById(2));
+ EXPECT_EQ(2, GetActiveViewId());
+ EXPECT_EQ(2, GetFocusedViewId());
+ }
+
+ private:
+ // When true, the disposition change occurs to the parent of the window
+ // instead of to the window. This verifies that changes occurring in the
+ // hierarchy that contains the window affect the window's focus.
+ bool parent_;
+
+ DISALLOW_COPY_AND_ASSIGN(FocusControllerImplicitTestBase);
+};
+
+// Focus and Activation changes in response to window visibility changes.
+class FocusControllerHideTest : public FocusControllerImplicitTestBase {
+ public:
+ FocusControllerHideTest() : FocusControllerImplicitTestBase(false) {}
+
+ protected:
+ FocusControllerHideTest(bool parent)
+ : FocusControllerImplicitTestBase(parent) {}
+
+ // Overridden from FocusControllerImplicitTestBase:
+ void ChangeViewDisposition(View* view) override {
+ GetDispositionView(view)->SetVisible(false);
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FocusControllerHideTest);
+};
+
+// Focus and Activation changes in response to window parent visibility
+// changes.
+class FocusControllerParentHideTest : public FocusControllerHideTest {
+ public:
+ FocusControllerParentHideTest() : FocusControllerHideTest(true) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FocusControllerParentHideTest);
+};
+
+// Focus and Activation changes in response to window destruction.
+class FocusControllerDestructionTest : public FocusControllerImplicitTestBase {
+ public:
+ FocusControllerDestructionTest() : FocusControllerImplicitTestBase(false) {}
+
+ protected:
+ FocusControllerDestructionTest(bool parent)
+ : FocusControllerImplicitTestBase(parent) {}
+
+ // Overridden from FocusControllerImplicitTestBase:
+ void ChangeViewDisposition(View* view) override {
+ GetDispositionView(view)->Destroy();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FocusControllerDestructionTest);
+};
+
+// Focus and Activation changes in response to window removal.
+class FocusControllerRemovalTest : public FocusControllerImplicitTestBase {
+ public:
+ FocusControllerRemovalTest()
+ : FocusControllerImplicitTestBase(false),
+ window_to_destroy_(nullptr) {}
+
+ protected:
+ FocusControllerRemovalTest(bool parent)
+ : FocusControllerImplicitTestBase(parent),
+ window_to_destroy_(nullptr) {}
+
+ // Overridden from FocusControllerImplicitTestBase:
+ void ChangeViewDisposition(View* view) override {
+ View* disposition_view = GetDispositionView(view);
+ disposition_view->parent()->RemoveChild(disposition_view);
+ window_to_destroy_ = disposition_view;
+ }
+ void TearDown() override {
+ if (window_to_destroy_)
+ window_to_destroy_->Destroy();
+
+ FocusControllerImplicitTestBase::TearDown();
+ }
+
+ private:
+ View* window_to_destroy_;
+
+ DISALLOW_COPY_AND_ASSIGN(FocusControllerRemovalTest);
+};
+
+// Focus and Activation changes in response to window parent removal.
+class FocusControllerParentRemovalTest : public FocusControllerRemovalTest {
+ public:
+ FocusControllerParentRemovalTest() : FocusControllerRemovalTest(true) {}
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FocusControllerParentRemovalTest);
+};
+
+#define FOCUS_CONTROLLER_TEST(TESTCLASS, TESTNAME) \
+ TEST_F(TESTCLASS, TESTNAME) { TESTNAME(); }
+
+// Runs direct focus change tests (input events and API calls).
+//
+// TODO(erg): Enable gesture events in the future.
+#define DIRECT_FOCUS_CHANGE_TESTS(TESTNAME) \
+ FOCUS_CONTROLLER_TEST(FocusControllerApiTest, TESTNAME) \
+ FOCUS_CONTROLLER_TEST(FocusControllerMouseEventTest, TESTNAME)
+
+// Runs implicit focus change tests for disposition changes to target.
+#define IMPLICIT_FOCUS_CHANGE_TARGET_TESTS(TESTNAME) \
+ FOCUS_CONTROLLER_TEST(FocusControllerHideTest, TESTNAME) \
+ FOCUS_CONTROLLER_TEST(FocusControllerDestructionTest, TESTNAME) \
+ FOCUS_CONTROLLER_TEST(FocusControllerRemovalTest, TESTNAME)
+
+// Runs implicit focus change tests for disposition changes to target's parent
+// hierarchy.
+#define IMPLICIT_FOCUS_CHANGE_PARENT_TESTS(TESTNAME) \
+ FOCUS_CONTROLLER_TEST(FocusControllerParentHideTest, TESTNAME) \
+ FOCUS_CONTROLLER_TEST(FocusControllerParentRemovalTest, TESTNAME)
+// TODO(erg): FocusControllerParentDestructionTest were commented out in the
+// aura version of this file, and don't work when I tried porting things over.
+
+// Runs all implicit focus change tests (changes to the target and target's
+// parent hierarchy)
+#define IMPLICIT_FOCUS_CHANGE_TESTS(TESTNAME) \
+ IMPLICIT_FOCUS_CHANGE_TARGET_TESTS(TESTNAME) \
+ IMPLICIT_FOCUS_CHANGE_PARENT_TESTS(TESTNAME)
+
+// Runs all possible focus change tests.
+#define ALL_FOCUS_TESTS(TESTNAME) \
+ DIRECT_FOCUS_CHANGE_TESTS(TESTNAME) \
+ IMPLICIT_FOCUS_CHANGE_TESTS(TESTNAME)
+
+// Runs focus change tests that apply only to the target. For example,
+// implicit activation changes caused by window disposition changes do not
+// occur when changes to the containing hierarchy happen.
+#define TARGET_FOCUS_TESTS(TESTNAME) \
+ DIRECT_FOCUS_CHANGE_TESTS(TESTNAME) \
+ IMPLICIT_FOCUS_CHANGE_TARGET_TESTS(TESTNAME)
+
+// - Focuses a window, verifies that focus changed.
+ALL_FOCUS_TESTS(BasicFocus);
+
+// - Activates a window, verifies that activation changed.
+TARGET_FOCUS_TESTS(BasicActivation);
+
+// - Focuses a window, verifies that focus events were dispatched.
+ALL_FOCUS_TESTS(FocusEvents);
+
+// - Focuses or activates a window multiple times, verifies that events are only
+// dispatched when focus/activation actually changes.
+DIRECT_FOCUS_CHANGE_TESTS(DuplicateFocusEvents);
+DIRECT_FOCUS_CHANGE_TESTS(DuplicateActivationEvents);
+
+// - Activates a window, verifies that activation events were dispatched.
+TARGET_FOCUS_TESTS(ActivationEvents);
+
+// - Attempts to active a hidden view, verifies that current view is
+// attempted to be reactivated and the appropriate event dispatched.
+FOCUS_CONTROLLER_TEST(FocusControllerApiTest, ReactivationEvents);
+
+// - Input events/API calls shift focus between focusable views within the
+// active view.
+DIRECT_FOCUS_CHANGE_TESTS(ShiftFocusWithinActiveView);
+
+// - Input events/API calls to a child view of an inactive view shifts
+// activation to the activatable parent and focuses the child.
+DIRECT_FOCUS_CHANGE_TESTS(ShiftFocusToChildOfInactiveView);
+
+// - Input events/API calls to focus the parent of the focused view do not
+// shift focus away from the child.
+DIRECT_FOCUS_CHANGE_TESTS(ShiftFocusToParentOfFocusedView);
+
+// - Verifies that FocusRules determine what can be focused.
+ALL_FOCUS_TESTS(FocusRulesOverride);
+
+// - Verifies that FocusRules determine what can be activated.
+TARGET_FOCUS_TESTS(ActivationRulesOverride);
+
+// - Verifies that attempts to change focus or activation from a focus or
+// activation change observer are ignored.
+DIRECT_FOCUS_CHANGE_TESTS(ShiftFocusOnActivation);
+DIRECT_FOCUS_CHANGE_TESTS(ShiftFocusOnActivationDueToHide);
+DIRECT_FOCUS_CHANGE_TESTS(NoShiftActiveOnActivation);
+
+FOCUS_CONTROLLER_TEST(FocusControllerApiTest,
+ ChangeFocusWhenNothingFocusedAndCaptured);
+
+// See description above DontPassDestroyedView() for details.
+FOCUS_CONTROLLER_TEST(FocusControllerApiTest, DontPassDestroyedView);
+
+// TODO(erg): Add the TextInputClient tests here.
+
+// If a mouse event was handled, it should not activate a view.
+FOCUS_CONTROLLER_TEST(FocusControllerMouseEventTest, IgnoreHandledEvent);
+
+} // namespace window_manager
diff --git a/mojo/services/window_manager/focus_rules.h b/mojo/services/window_manager/focus_rules.h
new file mode 100644
index 0000000..becd114
--- /dev/null
+++ b/mojo/services/window_manager/focus_rules.h
@@ -0,0 +1,65 @@
+// Copyright 2014 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 SERVICES_WINDOW_MANAGER_FOCUS_RULES_H_
+#define SERVICES_WINDOW_MANAGER_FOCUS_RULES_H_
+
+#include "third_party/mojo_services/src/view_manager/public/cpp/types.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view.h"
+
+namespace window_manager {
+
+// Implemented by an object that establishes the rules about what can be
+// focused or activated.
+class FocusRules {
+ public:
+ virtual ~FocusRules() {}
+
+ // Returns true if the children of |window| can be activated.
+ virtual bool SupportsChildActivation(mojo::View* window) const = 0;
+
+ // Returns true if |view| is a toplevel view. Whether or not a view
+ // is considered toplevel is determined by a similar set of rules that
+ // govern activation and focus. Not all toplevel views are activatable,
+ // call CanActivateView() to determine if a view can be activated.
+ virtual bool IsToplevelView(mojo::View* view) const = 0;
+ // Returns true if |view| can be activated or focused.
+ virtual bool CanActivateView(mojo::View* view) const = 0;
+ // For CanFocusView(), null is supported, because null is a valid focusable
+ // view (in the case of clearing focus).
+ virtual bool CanFocusView(mojo::View* view) const = 0;
+
+ // Returns the toplevel view containing |view|. Not all toplevel views
+ // are activatable, call GetActivatableView() instead to return the
+ // activatable view, which might be in a different hierarchy.
+ // Will return null if |view| is not contained by a view considered to be
+ // a toplevel view.
+ virtual mojo::View* GetToplevelView(mojo::View* view) const = 0;
+ // Returns the activatable or focusable view given an attempt to activate or
+ // focus |view|. Some possible scenarios (not intended to be exhaustive):
+ // - |view| is a child of a non-focusable view and so focus must be set
+ // according to rules defined by the delegate, e.g. to a parent.
+ // - |view| is an activatable view that is the transient parent of a modal
+ // view, so attempts to activate |view| should result in the modal
+ // transient being activated instead.
+ // These methods may return null if they are unable to find an activatable
+ // or focusable view given |view|.
+ virtual mojo::View* GetActivatableView(mojo::View* view) const = 0;
+ virtual mojo::View* GetFocusableView(mojo::View* view) const = 0;
+
+ // Returns the next view to activate in the event that |ignore| is no longer
+ // activatable. This function is called when something is happening to
+ // |ignore| that means it can no longer have focus or activation, including
+ // but not limited to:
+ // - it or its parent hierarchy is being hidden, or removed from the
+ // RootView.
+ // - it is being destroyed.
+ // - it is being explicitly deactivated.
+ // |ignore| cannot be null.
+ virtual mojo::View* GetNextActivatableView(mojo::View* ignore) const = 0;
+};
+
+} // namespace window_manager
+
+#endif // SERVICES_WINDOW_MANAGER_FOCUS_RULES_H_
diff --git a/mojo/services/window_manager/hit_test.h b/mojo/services/window_manager/hit_test.h
new file mode 100644
index 0000000..747dd01
--- /dev/null
+++ b/mojo/services/window_manager/hit_test.h
@@ -0,0 +1,45 @@
+// Copyright 2015 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 SERVICES_WINDOW_MANAGER_HIT_TEST_H_
+#define SERVICES_WINDOW_MANAGER_HIT_TEST_H_
+
+#if !defined(OS_WIN)
+
+// Defines the same symbolic names used by the WM_NCHITTEST Notification under
+// win32 (the integer values are not guaranteed to be equivalent). We do this
+// because we have a whole bunch of code that deals with window resizing and
+// such that requires these values.
+enum HitTestCompat {
+ HTNOWHERE = 0,
+ HTBORDER,
+ HTBOTTOM,
+ HTBOTTOMLEFT,
+ HTBOTTOMRIGHT,
+ HTCAPTION,
+ HTCLIENT,
+ HTCLOSE,
+ HTERROR,
+ HTGROWBOX,
+ HTHELP,
+ HTHSCROLL,
+ HTLEFT,
+ HTMENU,
+ HTMAXBUTTON,
+ HTMINBUTTON,
+ HTREDUCE,
+ HTRIGHT,
+ HTSIZE,
+ HTSYSMENU,
+ HTTOP,
+ HTTOPLEFT,
+ HTTOPRIGHT,
+ HTTRANSPARENT,
+ HTVSCROLL,
+ HTZOOM
+};
+
+#endif // !defined(OS_WIN)
+
+#endif // SERVICES_WINDOW_MANAGER_HIT_TEST_H_
diff --git a/mojo/services/window_manager/main.cc b/mojo/services/window_manager/main.cc
new file mode 100644
index 0000000..701a249
--- /dev/null
+++ b/mojo/services/window_manager/main.cc
@@ -0,0 +1,92 @@
+// Copyright 2014 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 "base/memory/scoped_ptr.h"
+#include "mojo/application/application_runner_chromium.h"
+#include "mojo/common/tracing_impl.h"
+#include "mojo/public/c/system/main.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/service_provider_impl.h"
+#include "mojo/services/window_manager/basic_focus_rules.h"
+#include "mojo/services/window_manager/window_manager_app.h"
+#include "mojo/services/window_manager/window_manager_delegate.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view_manager.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view_manager_delegate.h"
+
+// ApplicationDelegate implementation file for WindowManager users (e.g.
+// core window manager tests) that do not want to provide their own
+// ApplicationDelegate::Create().
+
+using mojo::View;
+using mojo::ViewManager;
+
+namespace window_manager {
+
+class DefaultWindowManager : public mojo::ApplicationDelegate,
+ public mojo::ViewManagerDelegate,
+ public WindowManagerDelegate {
+ public:
+ DefaultWindowManager()
+ : window_manager_app_(new WindowManagerApp(this, this)),
+ root_(nullptr),
+ window_offset_(10) {
+ }
+ ~DefaultWindowManager() override {}
+
+ private:
+ // Overridden from mojo::ApplicationDelegate:
+ void Initialize(mojo::ApplicationImpl* impl) override {
+ window_manager_app_->Initialize(impl);
+ tracing_.Initialize(impl);
+ }
+ bool ConfigureIncomingConnection(
+ mojo::ApplicationConnection* connection) override {
+ window_manager_app_->ConfigureIncomingConnection(connection);
+ return true;
+ }
+
+ // Overridden from ViewManagerDelegate:
+ void OnEmbed(View* root,
+ mojo::InterfaceRequest<mojo::ServiceProvider> services,
+ mojo::ServiceProviderPtr exposed_services) override {
+ root_ = root;
+ window_manager_app_->InitFocus(
+ make_scoped_ptr(new window_manager::BasicFocusRules(root_)));
+ }
+ void OnViewManagerDisconnected(ViewManager* view_manager) override {}
+
+ // Overridden from WindowManagerDelegate:
+ void Embed(const mojo::String& url,
+ mojo::InterfaceRequest<mojo::ServiceProvider> services,
+ mojo::ServiceProviderPtr exposed_services) override {
+ DCHECK(root_);
+ View* view = root_->view_manager()->CreateView();
+ root_->AddChild(view);
+
+ mojo::Rect rect;
+ rect.x = rect.y = window_offset_;
+ rect.width = rect.height = 100;
+ view->SetBounds(rect);
+ window_offset_ += 10;
+
+ view->SetVisible(true);
+ view->Embed(url, services.Pass(), exposed_services.Pass());
+ }
+
+ scoped_ptr<WindowManagerApp> window_manager_app_;
+
+ View* root_;
+ int window_offset_;
+ mojo::TracingImpl tracing_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(DefaultWindowManager);
+};
+
+} // namespace window_manager
+
+MojoResult MojoMain(MojoHandle shell_handle) {
+ mojo::ApplicationRunnerChromium runner(
+ new window_manager::DefaultWindowManager);
+ return runner.Run(shell_handle);
+}
diff --git a/mojo/services/window_manager/native_viewport_event_dispatcher_impl.cc b/mojo/services/window_manager/native_viewport_event_dispatcher_impl.cc
new file mode 100644
index 0000000..4bd0b80
--- /dev/null
+++ b/mojo/services/window_manager/native_viewport_event_dispatcher_impl.cc
@@ -0,0 +1,36 @@
+// Copyright 2014 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 "mojo/services/window_manager/native_viewport_event_dispatcher_impl.h"
+
+#include "mojo/converters/input_events/input_events_type_converters.h"
+#include "mojo/services/window_manager/view_event_dispatcher.h"
+#include "mojo/services/window_manager/window_manager_app.h"
+
+namespace window_manager {
+
+NativeViewportEventDispatcherImpl::NativeViewportEventDispatcherImpl(
+ WindowManagerApp* app,
+ mojo::InterfaceRequest<mojo::NativeViewportEventDispatcher> request)
+ : app_(app), binding_(this, request.Pass()) {
+}
+NativeViewportEventDispatcherImpl::~NativeViewportEventDispatcherImpl() {
+}
+
+ui::EventProcessor* NativeViewportEventDispatcherImpl::GetEventProcessor() {
+ return app_->event_dispatcher();
+}
+
+void NativeViewportEventDispatcherImpl::OnEvent(
+ mojo::EventPtr event,
+ const mojo::Callback<void()>& callback) {
+ scoped_ptr<ui::Event> ui_event = event.To<scoped_ptr<ui::Event>>();
+
+ if (ui_event)
+ SendEventToProcessor(ui_event.get());
+
+ callback.Run();
+}
+
+} // namespace window_manager
diff --git a/mojo/services/window_manager/native_viewport_event_dispatcher_impl.h b/mojo/services/window_manager/native_viewport_event_dispatcher_impl.h
new file mode 100644
index 0000000..6624c93
--- /dev/null
+++ b/mojo/services/window_manager/native_viewport_event_dispatcher_impl.h
@@ -0,0 +1,42 @@
+// Copyright 2014 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 SERVICES_WINDOW_MANAGER_NATIVE_VIEWPORT_EVENT_DISPATCHER_IMPL_H_
+#define SERVICES_WINDOW_MANAGER_NATIVE_VIEWPORT_EVENT_DISPATCHER_IMPL_H_
+
+#include "base/basictypes.h"
+#include "mojo/public/cpp/bindings/strong_binding.h"
+#include "third_party/mojo_services/src/native_viewport/public/interfaces/native_viewport.mojom.h"
+#include "ui/events/event_source.h"
+
+namespace window_manager {
+
+class WindowManagerApp;
+
+class NativeViewportEventDispatcherImpl
+ : public ui::EventSource,
+ public mojo::NativeViewportEventDispatcher {
+ public:
+ NativeViewportEventDispatcherImpl(
+ WindowManagerApp* app,
+ mojo::InterfaceRequest<mojo::NativeViewportEventDispatcher> request);
+ ~NativeViewportEventDispatcherImpl() override;
+
+ private:
+ // ui::EventSource:
+ ui::EventProcessor* GetEventProcessor() override;
+
+ // NativeViewportEventDispatcher:
+ void OnEvent(mojo::EventPtr event,
+ const mojo::Callback<void()>& callback) override;
+
+ WindowManagerApp* app_;
+
+ mojo::StrongBinding<mojo::NativeViewportEventDispatcher> binding_;
+ DISALLOW_COPY_AND_ASSIGN(NativeViewportEventDispatcherImpl);
+};
+
+} // namespace window_manager
+
+#endif // SERVICES_WINDOW_MANAGER_NATIVE_VIEWPORT_EVENT_DISPATCHER_IMPL_H_
diff --git a/mojo/services/window_manager/run_all_unittests.cc b/mojo/services/window_manager/run_all_unittests.cc
new file mode 100644
index 0000000..51fd967
--- /dev/null
+++ b/mojo/services/window_manager/run_all_unittests.cc
@@ -0,0 +1,43 @@
+// Copyright 2014 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 "base/bind.h"
+#include "base/test/launcher/unit_test_launcher.h"
+#include "base/test/test_suite.h"
+#include "ui/gl/gl_surface.h"
+
+#if defined(USE_X11)
+#include "ui/gfx/x/x11_connection.h"
+#endif
+
+namespace window_manager {
+
+class WindowManagerTestSuite : public base::TestSuite {
+ public:
+ WindowManagerTestSuite(int argc, char** argv) : TestSuite(argc, argv) {}
+ ~WindowManagerTestSuite() override {}
+
+ protected:
+ void Initialize() override {
+#if defined(USE_X11)
+ // Each test ends up creating a new thread for the native viewport service.
+ // In other words we'll use X on different threads, so tell it that.
+ gfx::InitializeThreadedX11();
+#endif
+ base::TestSuite::Initialize();
+ gfx::GLSurface::InitializeOneOffForTests();
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(WindowManagerTestSuite);
+};
+
+} // namespace window_manager
+
+int main(int argc, char** argv) {
+ window_manager::WindowManagerTestSuite test_suite(argc, argv);
+
+ return base::LaunchUnitTests(
+ argc, argv, base::Bind(&TestSuite::Run, base::Unretained(&test_suite)));
+}
diff --git a/mojo/services/window_manager/view_event_dispatcher.cc b/mojo/services/window_manager/view_event_dispatcher.cc
new file mode 100644
index 0000000..657cf88
--- /dev/null
+++ b/mojo/services/window_manager/view_event_dispatcher.cc
@@ -0,0 +1,55 @@
+// Copyright 2014 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 "mojo/services/window_manager/view_event_dispatcher.h"
+
+#include "mojo/services/window_manager/view_target.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view.h"
+
+namespace window_manager {
+
+ViewEventDispatcher::ViewEventDispatcher()
+ : event_dispatch_target_(nullptr),
+ old_dispatch_target_(nullptr) {
+}
+
+ViewEventDispatcher::~ViewEventDispatcher() {}
+
+void ViewEventDispatcher::SetRootViewTarget(ViewTarget* root_view_target) {
+ root_view_target_ = root_view_target;
+}
+
+ui::EventTarget* ViewEventDispatcher::GetRootTarget() {
+ return root_view_target_;
+}
+
+void ViewEventDispatcher::OnEventProcessingStarted(ui::Event* event) {
+}
+
+bool ViewEventDispatcher::CanDispatchToTarget(ui::EventTarget* target) {
+ return event_dispatch_target_ == target;
+}
+
+ui::EventDispatchDetails ViewEventDispatcher::PreDispatchEvent(
+ ui::EventTarget* target,
+ ui::Event* event) {
+ // TODO(erg): PreDispatch in aura::WindowEventDispatcher does many, many
+ // things. It, and the functions split off for different event types, are
+ // most of the file.
+ old_dispatch_target_ = event_dispatch_target_;
+ event_dispatch_target_ = static_cast<ViewTarget*>(target);
+ return ui::EventDispatchDetails();
+}
+
+ui::EventDispatchDetails ViewEventDispatcher::PostDispatchEvent(
+ ui::EventTarget* target,
+ const ui::Event& event) {
+ // TODO(erg): Not at all as long as PreDispatchEvent, but still missing core
+ // details.
+ event_dispatch_target_ = old_dispatch_target_;
+ old_dispatch_target_ = nullptr;
+ return ui::EventDispatchDetails();
+}
+
+} // namespace window_manager
diff --git a/mojo/services/window_manager/view_event_dispatcher.h b/mojo/services/window_manager/view_event_dispatcher.h
new file mode 100644
index 0000000..399939e
--- /dev/null
+++ b/mojo/services/window_manager/view_event_dispatcher.h
@@ -0,0 +1,47 @@
+// Copyright 2014 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 SERVICES_WINDOW_MANAGER_VIEW_EVENT_DISPATCHER_H_
+#define SERVICES_WINDOW_MANAGER_VIEW_EVENT_DISPATCHER_H_
+
+#include "base/memory/scoped_ptr.h"
+#include "ui/events/event_processor.h"
+#include "ui/events/event_target.h"
+
+namespace window_manager {
+
+class ViewTarget;
+
+class ViewEventDispatcher : public ui::EventProcessor {
+ public:
+ ViewEventDispatcher();
+ ~ViewEventDispatcher() override;
+
+ void SetRootViewTarget(ViewTarget* root_view_target);
+
+ private:
+ // Overridden from ui::EventProcessor:
+ ui::EventTarget* GetRootTarget() override;
+ void OnEventProcessingStarted(ui::Event* event) override;
+
+ // Overridden from ui::EventDispatcherDelegate.
+ bool CanDispatchToTarget(ui::EventTarget* target) override;
+ ui::EventDispatchDetails PreDispatchEvent(ui::EventTarget* target,
+ ui::Event* event) override;
+ ui::EventDispatchDetails PostDispatchEvent(
+ ui::EventTarget* target, const ui::Event& event) override;
+
+ // We keep a weak reference to ViewTarget*, which corresponds to the root of
+ // the mojo::View tree.
+ ViewTarget* root_view_target_;
+
+ ViewTarget* event_dispatch_target_;
+ ViewTarget* old_dispatch_target_;
+
+ DISALLOW_COPY_AND_ASSIGN(ViewEventDispatcher);
+};
+
+} // namespace window_manager
+
+#endif // SERVICES_WINDOW_MANAGER_VIEW_EVENT_DISPATCHER_H_
diff --git a/mojo/services/window_manager/view_target.cc b/mojo/services/window_manager/view_target.cc
new file mode 100644
index 0000000..adb3ec2
--- /dev/null
+++ b/mojo/services/window_manager/view_target.cc
@@ -0,0 +1,192 @@
+// Copyright 2014 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 "mojo/services/window_manager/view_target.h"
+
+#include "mojo/converters/geometry/geometry_type_converters.h"
+#include "mojo/services/window_manager/view_targeter.h"
+#include "mojo/services/window_manager/window_manager_app.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view_property.h"
+#include "ui/events/event.h"
+#include "ui/events/event_target_iterator.h"
+#include "ui/events/event_targeter.h"
+#include "ui/gfx/geometry/point3_f.h"
+#include "ui/gfx/geometry/rect.h"
+#include "ui/gfx/transform.h"
+
+namespace window_manager {
+
+namespace {
+
+DEFINE_OWNED_VIEW_PROPERTY_KEY(ViewTarget, kViewTargetKey, nullptr);
+
+// Provides a version which keeps a copy of the data (for when it has to be
+// derived instead of pointed at).
+template <typename T>
+class CopyingEventTargetIteratorImpl : public ui::EventTargetIterator {
+ public:
+ explicit CopyingEventTargetIteratorImpl(const std::vector<T*>& children)
+ : children_(children),
+ begin_(children_.rbegin()),
+ end_(children_.rend()) {}
+ ~CopyingEventTargetIteratorImpl() override {}
+
+ ui::EventTarget* GetNextTarget() override {
+ if (begin_ == end_)
+ return nullptr;
+ ui::EventTarget* target = *(begin_);
+ ++begin_;
+ return target;
+ }
+
+ private:
+ typename std::vector<T*> children_;
+ typename std::vector<T*>::const_reverse_iterator begin_;
+ typename std::vector<T*>::const_reverse_iterator end_;
+};
+
+} // namespace
+
+ViewTarget::~ViewTarget() {
+}
+
+// static
+ViewTarget* ViewTarget::TargetFromView(mojo::View* view) {
+ if (!view)
+ return nullptr;
+
+ ViewTarget* target = view->GetLocalProperty(kViewTargetKey);
+ if (target)
+ return target;
+
+ return new ViewTarget(view);
+}
+
+void ViewTarget::ConvertPointToTarget(const ViewTarget* source,
+ const ViewTarget* target,
+ gfx::Point* point) {
+ // TODO(erg): Do we need to deal with |source| and |target| being in
+ // different trees?
+ DCHECK_EQ(source->GetRoot(), target->GetRoot());
+ if (source == target)
+ return;
+
+ const ViewTarget* root_target = source->GetRoot();
+ CHECK_EQ(root_target, target->GetRoot());
+
+ if (source != root_target)
+ source->ConvertPointForAncestor(root_target, point);
+ if (target != root_target)
+ target->ConvertPointFromAncestor(root_target, point);
+}
+
+std::vector<ViewTarget*> ViewTarget::GetChildren() const {
+ std::vector<ViewTarget*> targets;
+ for (mojo::View* child : view_->children())
+ targets.push_back(TargetFromView(child));
+ return targets;
+}
+
+const ViewTarget* ViewTarget::GetParent() const {
+ return TargetFromView(view_->parent());
+}
+
+gfx::Rect ViewTarget::GetBounds() const {
+ return view_->bounds().To<gfx::Rect>();
+}
+
+bool ViewTarget::HasParent() const {
+ return !!view_->parent();
+}
+
+bool ViewTarget::IsVisible() const {
+ return view_->visible();
+}
+
+const ViewTarget* ViewTarget::GetRoot() const {
+ const ViewTarget* root = this;
+ for (const ViewTarget* parent = this; parent; parent = parent->GetParent())
+ root = parent;
+ return root;
+}
+
+scoped_ptr<ViewTargeter> ViewTarget::SetEventTargeter(
+ scoped_ptr<ViewTargeter> targeter) {
+ scoped_ptr<ViewTargeter> old_targeter = targeter_.Pass();
+ targeter_ = targeter.Pass();
+ return old_targeter.Pass();
+}
+
+bool ViewTarget::CanAcceptEvent(const ui::Event& event) {
+ // We need to make sure that a touch cancel event and any gesture events it
+ // creates can always reach the window. This ensures that we receive a valid
+ // touch / gesture stream.
+ if (event.IsEndingEvent())
+ return true;
+
+ if (!view_->visible())
+ return false;
+
+ // The top-most window can always process an event.
+ if (!view_->parent())
+ return true;
+
+ // In aura, we only accept events if this is a key event or if the user
+ // supplied a TargetHandler, usually the aura::WindowDelegate. Here, we're
+ // just forwarding events to other Views which may be in other processes, so
+ // always accept.
+ return true;
+}
+
+ui::EventTarget* ViewTarget::GetParentTarget() {
+ return TargetFromView(view_->parent());
+}
+
+scoped_ptr<ui::EventTargetIterator> ViewTarget::GetChildIterator() const {
+ return scoped_ptr<ui::EventTargetIterator>(
+ new CopyingEventTargetIteratorImpl<ViewTarget>(GetChildren()));
+}
+
+ui::EventTargeter* ViewTarget::GetEventTargeter() {
+ return targeter_.get();
+}
+
+void ViewTarget::ConvertEventToTarget(ui::EventTarget* target,
+ ui::LocatedEvent* event) {
+ event->ConvertLocationToTarget(this, static_cast<ViewTarget*>(target));
+}
+
+ViewTarget::ViewTarget(mojo::View* view_to_wrap) : view_(view_to_wrap) {
+ DCHECK(view_->GetLocalProperty(kViewTargetKey) == nullptr);
+ view_->SetLocalProperty(kViewTargetKey, this);
+}
+
+bool ViewTarget::ConvertPointForAncestor(const ViewTarget* ancestor,
+ gfx::Point* point) const {
+ gfx::Vector2d offset;
+ bool result = GetTargetOffsetRelativeTo(ancestor, &offset);
+ *point += offset;
+ return result;
+}
+
+bool ViewTarget::ConvertPointFromAncestor(const ViewTarget* ancestor,
+ gfx::Point* point) const {
+ gfx::Vector2d offset;
+ bool result = GetTargetOffsetRelativeTo(ancestor, &offset);
+ *point -= offset;
+ return result;
+}
+
+bool ViewTarget::GetTargetOffsetRelativeTo(const ViewTarget* ancestor,
+ gfx::Vector2d* offset) const {
+ const ViewTarget* v = this;
+ for (; v && v != ancestor; v = v->GetParent()) {
+ gfx::Rect bounds = v->GetBounds();
+ *offset += gfx::Vector2d(bounds.x(), bounds.y());
+ }
+ return v == ancestor;
+}
+
+} // namespace window_manager
diff --git a/mojo/services/window_manager/view_target.h b/mojo/services/window_manager/view_target.h
new file mode 100644
index 0000000..1119e1f
--- /dev/null
+++ b/mojo/services/window_manager/view_target.h
@@ -0,0 +1,100 @@
+// Copyright 2014 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 SERVICES_WINDOW_MANAGER_VIEW_TARGET_H_
+#define SERVICES_WINDOW_MANAGER_VIEW_TARGET_H_
+
+#include "ui/events/event_target.h"
+
+namespace gfx {
+class Point;
+class Rect;
+class Vector2d;
+}
+
+namespace ui {
+class EventTargeter;
+}
+
+namespace mojo {
+class View;
+}
+
+namespace window_manager {
+
+class TestView;
+class ViewTargeter;
+class WindowManagerApp;
+
+// A wrapper class around mojo::View; we can't subclass View to implement the
+// event targeting interfaces, so we create a separate object which observes
+// the View and ties its lifetime to it.
+//
+// We set ourselves as a property of the view passed in, and we are owned by
+// said View.
+class ViewTarget : public ui::EventTarget {
+ public:
+ ~ViewTarget() override;
+
+ // Returns the ViewTarget for a View. ViewTargets are owned by the |view|
+ // passed in, and are created on demand.
+ static ViewTarget* TargetFromView(mojo::View* view);
+
+ // Converts |point| from |source|'s coordinates to |target|'s. If |source| is
+ // NULL, the function returns without modifying |point|. |target| cannot be
+ // NULL.
+ static void ConvertPointToTarget(const ViewTarget* source,
+ const ViewTarget* target,
+ gfx::Point* point);
+
+ mojo::View* view() { return view_; }
+
+ // TODO(erg): Make this const once we've removed aura from the tree and it's
+ // feasible to change all callers of the EventTargeter interface to pass and
+ // accept const objects. (When that gets done, re-const the
+ // EventTargetIterator::GetNextTarget and EventTarget::GetChildIterator
+ // interfaces.)
+ std::vector<ViewTarget*> GetChildren() const;
+
+ const ViewTarget* GetParent() const;
+ gfx::Rect GetBounds() const;
+ bool HasParent() const;
+ bool IsVisible() const;
+
+ const ViewTarget* GetRoot() const;
+
+ // Sets a new ViewTargeter for the view, and returns the previous
+ // ViewTargeter.
+ scoped_ptr<ViewTargeter> SetEventTargeter(scoped_ptr<ViewTargeter> targeter);
+
+ // Overridden from ui::EventTarget:
+ bool CanAcceptEvent(const ui::Event& event) override;
+ EventTarget* GetParentTarget() override;
+ scoped_ptr<ui::EventTargetIterator> GetChildIterator() const override;
+ ui::EventTargeter* GetEventTargeter() override;
+ void ConvertEventToTarget(ui::EventTarget* target,
+ ui::LocatedEvent* event) override;
+
+ private:
+ friend class TestView;
+ explicit ViewTarget(mojo::View* view_to_wrap);
+
+ bool ConvertPointForAncestor(const ViewTarget* ancestor,
+ gfx::Point* point) const;
+ bool ConvertPointFromAncestor(const ViewTarget* ancestor,
+ gfx::Point* point) const;
+ bool GetTargetOffsetRelativeTo(const ViewTarget* ancestor,
+ gfx::Vector2d* offset) const;
+
+ // The mojo::View that we dispatch to.
+ mojo::View* view_;
+
+ scoped_ptr<ViewTargeter> targeter_;
+
+ DISALLOW_COPY_AND_ASSIGN(ViewTarget);
+};
+
+} // namespace window_manager
+
+#endif // SERVICES_WINDOW_MANAGER_VIEW_TARGET_H_
diff --git a/mojo/services/window_manager/view_target_unittest.cc b/mojo/services/window_manager/view_target_unittest.cc
new file mode 100644
index 0000000..bec5c37
--- /dev/null
+++ b/mojo/services/window_manager/view_target_unittest.cc
@@ -0,0 +1,81 @@
+// Copyright 2014 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 "mojo/services/window_manager/view_target.h"
+
+#include <set>
+
+#include "mojo/services/window_manager/window_manager_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace window_manager {
+
+using ViewTargetTest = testing::Test;
+
+// V1
+// +-- V2
+// +-- V3
+TEST_F(ViewTargetTest, GetRoot) {
+ TestView v1(1, gfx::Rect(20, 20, 400, 400));
+ TestView v2(2, gfx::Rect(10, 10, 350, 350));
+ TestView v3(3, gfx::Rect(10, 10, 100, 100));
+ v1.AddChild(&v2);
+ v2.AddChild(&v3);
+
+ EXPECT_EQ(ViewTarget::TargetFromView(&v1),
+ ViewTarget::TargetFromView(&v1)->GetRoot());
+ EXPECT_EQ(ViewTarget::TargetFromView(&v1),
+ ViewTarget::TargetFromView(&v2)->GetRoot());
+ EXPECT_EQ(ViewTarget::TargetFromView(&v1),
+ ViewTarget::TargetFromView(&v3)->GetRoot());
+}
+
+// V1
+// +-- V2
+TEST_F(ViewTargetTest, ConvertPointToTarget_Simple) {
+ TestView v1(1, gfx::Rect(20, 20, 400, 400));
+ TestView v2(2, gfx::Rect(10, 10, 350, 350));
+ v1.AddChild(&v2);
+
+ ViewTarget* t1 = v1.target();
+ ViewTarget* t2 = v2.target();
+
+ gfx::Point point1_in_t2_coords(5, 5);
+ ViewTarget::ConvertPointToTarget(t2, t1, &point1_in_t2_coords);
+ gfx::Point point1_in_t1_coords(15, 15);
+ EXPECT_EQ(point1_in_t1_coords, point1_in_t2_coords);
+
+ gfx::Point point2_in_t1_coords(5, 5);
+ ViewTarget::ConvertPointToTarget(t1, t2, &point2_in_t1_coords);
+ gfx::Point point2_in_t2_coords(-5, -5);
+ EXPECT_EQ(point2_in_t2_coords, point2_in_t1_coords);
+}
+
+// V1
+// +-- V2
+// +-- V3
+TEST_F(ViewTargetTest, ConvertPointToTarget_Medium) {
+ TestView v1(1, gfx::Rect(20, 20, 400, 400));
+ TestView v2(2, gfx::Rect(10, 10, 350, 350));
+ TestView v3(3, gfx::Rect(10, 10, 100, 100));
+ v1.AddChild(&v2);
+ v2.AddChild(&v3);
+
+ ViewTarget* t1 = v1.target();
+ ViewTarget* t3 = v3.target();
+
+ gfx::Point point1_in_t3_coords(5, 5);
+ ViewTarget::ConvertPointToTarget(t3, t1, &point1_in_t3_coords);
+ gfx::Point point1_in_t1_coords(25, 25);
+ EXPECT_EQ(point1_in_t1_coords, point1_in_t3_coords);
+
+ gfx::Point point2_in_t1_coords(5, 5);
+ ViewTarget::ConvertPointToTarget(t1, t3, &point2_in_t1_coords);
+ gfx::Point point2_in_t3_coords(-15, -15);
+ EXPECT_EQ(point2_in_t3_coords, point2_in_t1_coords);
+}
+
+} // namespace window_manager
diff --git a/mojo/services/window_manager/view_targeter.cc b/mojo/services/window_manager/view_targeter.cc
new file mode 100644
index 0000000..58fb370
--- /dev/null
+++ b/mojo/services/window_manager/view_targeter.cc
@@ -0,0 +1,109 @@
+// Copyright 2014 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 "mojo/services/window_manager/view_targeter.h"
+
+#include "mojo/services/window_manager/capture_controller.h"
+#include "mojo/services/window_manager/focus_controller.h"
+#include "mojo/services/window_manager/view_target.h"
+
+namespace window_manager {
+
+ViewTargeter::ViewTargeter() {}
+
+ViewTargeter::~ViewTargeter() {}
+
+ui::EventTarget* ViewTargeter::FindTargetForEvent(ui::EventTarget* root,
+ ui::Event* event) {
+ ViewTarget* view = static_cast<ViewTarget*>(root);
+ ViewTarget* target =
+ event->IsKeyEvent()
+ ? FindTargetForKeyEvent(view, *static_cast<ui::KeyEvent*>(event))
+ : static_cast<ViewTarget*>(
+ EventTargeter::FindTargetForEvent(root, event));
+
+ // TODO(erg): The aura version of this method does a lot of work to handle
+ // dispatching to a target that isn't a child of |view|. For now, punt on
+ // this.
+ DCHECK_EQ(view->GetRoot(), target->GetRoot());
+
+ return target;
+}
+
+ui::EventTarget* ViewTargeter::FindTargetForLocatedEvent(
+ ui::EventTarget* root,
+ ui::LocatedEvent* event) {
+ ViewTarget* view = static_cast<ViewTarget*>(root);
+ if (!view->HasParent()) {
+ ViewTarget* target = FindTargetInRootView(view, *event);
+ if (target) {
+ view->ConvertEventToTarget(target, event);
+ return target;
+ }
+ }
+ return EventTargeter::FindTargetForLocatedEvent(view, event);
+}
+
+bool ViewTargeter::SubtreeCanAcceptEvent(ui::EventTarget* target,
+ const ui::LocatedEvent& event) const {
+ ViewTarget* view = static_cast<ViewTarget*>(target);
+
+ if (!view->IsVisible())
+ return false;
+
+ // TODO(erg): We may need to keep track of the parent on ViewTarget, because
+ // we have a check here about
+ // WindowDelegate::ShouldDescendIntoChildForEventHandling().
+
+ // TODO(sky): decide if we really want this. If we do, it should be a public
+ // constant and documented.
+ if (view->view()->shared_properties().count("deliver-events-to-parent"))
+ return false;
+
+ return true;
+}
+
+bool ViewTargeter::EventLocationInsideBounds(
+ ui::EventTarget* target,
+ const ui::LocatedEvent& event) const {
+ ViewTarget* view = static_cast<ViewTarget*>(target);
+ gfx::Point point = event.location();
+ const ViewTarget* parent = view->GetParent();
+ if (parent)
+ ViewTarget::ConvertPointToTarget(parent, view, &point);
+ return gfx::Rect(view->GetBounds().size()).Contains(point);
+}
+
+ViewTarget* ViewTargeter::FindTargetForKeyEvent(ViewTarget* view_target,
+ const ui::KeyEvent& key) {
+ FocusController* focus_controller = GetFocusController(view_target->view());
+ if (focus_controller) {
+ mojo::View* focused_view = focus_controller->GetFocusedView();
+ if (focused_view)
+ return ViewTarget::TargetFromView(focused_view);
+ }
+ return view_target;
+}
+
+ViewTarget* ViewTargeter::FindTargetInRootView(ViewTarget* root_view,
+ const ui::LocatedEvent& event) {
+ // TODO(erg): This here is important because it resolves
+ // mouse_pressed_handler() in the aura version. This is what makes sure
+ // that a view gets both the mouse down and up.
+
+ CaptureController* capture_controller =
+ GetCaptureController(root_view->view());
+ if (capture_controller) {
+ mojo::View* capture_view = capture_controller->GetCapture();
+ if (capture_view)
+ return ViewTarget::TargetFromView(capture_view);
+ }
+
+ // TODO(erg): There's a whole bunch of junk about handling touch events
+ // here. Handle later.
+
+ return nullptr;
+}
+
+} // namespace window_manager
diff --git a/mojo/services/window_manager/view_targeter.h b/mojo/services/window_manager/view_targeter.h
new file mode 100644
index 0000000..3a01e57
--- /dev/null
+++ b/mojo/services/window_manager/view_targeter.h
@@ -0,0 +1,44 @@
+// Copyright 2014 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 SERVICES_WINDOW_MANAGER_VIEW_TARGETER_H_
+#define SERVICES_WINDOW_MANAGER_VIEW_TARGETER_H_
+
+#include "ui/events/event_targeter.h"
+
+namespace window_manager {
+
+class ViewTarget;
+
+class ViewTargeter : public ui::EventTargeter {
+ public:
+ ViewTargeter();
+ ~ViewTargeter() override;
+
+ protected:
+ // ui::EventTargeter:
+ ui::EventTarget* FindTargetForEvent(ui::EventTarget* root,
+ ui::Event* event) override;
+ ui::EventTarget* FindTargetForLocatedEvent(ui::EventTarget* root,
+ ui::LocatedEvent* event) override;
+ bool SubtreeCanAcceptEvent(ui::EventTarget* target,
+ const ui::LocatedEvent& event) const override;
+ bool EventLocationInsideBounds(ui::EventTarget* target,
+ const ui::LocatedEvent& event) const override;
+
+ private:
+ // Targets either the root View or the currently focused view.
+ ViewTarget* FindTargetForKeyEvent(ViewTarget* view, const ui::KeyEvent& key);
+
+ // Deals with cases where the |root_view| needs to change how things are
+ // dispatched. (For example, in the case of capture.)
+ ViewTarget* FindTargetInRootView(ViewTarget* root_view,
+ const ui::LocatedEvent& event);
+
+ DISALLOW_COPY_AND_ASSIGN(ViewTargeter);
+};
+
+} // namespace window_manager
+
+#endif // SERVICES_WINDOW_MANAGER_VIEW_TARGETER_H_
diff --git a/mojo/services/window_manager/view_targeter_unittest.cc b/mojo/services/window_manager/view_targeter_unittest.cc
new file mode 100644
index 0000000..0b3281d
--- /dev/null
+++ b/mojo/services/window_manager/view_targeter_unittest.cc
@@ -0,0 +1,114 @@
+// Copyright (c) 2013 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 "mojo/services/window_manager/view_targeter.h"
+
+#include "mojo/services/window_manager/basic_focus_rules.h"
+#include "mojo/services/window_manager/capture_controller.h"
+#include "mojo/services/window_manager/focus_controller.h"
+#include "mojo/services/window_manager/view_event_dispatcher.h"
+#include "mojo/services/window_manager/window_manager_test_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/events/event_utils.h"
+#include "ui/events/test/test_event_handler.h"
+
+namespace window_manager {
+
+class ViewTargeterTest : public testing::Test {
+ public:
+ ViewTargeterTest() {}
+ ~ViewTargeterTest() override {}
+
+ void SetUp() override {
+ view_event_dispatcher_.reset(new ViewEventDispatcher());
+ }
+
+ void TearDown() override {
+ view_event_dispatcher_.reset();
+ testing::Test::TearDown();
+ }
+
+ protected:
+ scoped_ptr<ViewEventDispatcher> view_event_dispatcher_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ViewTargeterTest);
+};
+
+TEST_F(ViewTargeterTest, Basic) {
+ // The dispatcher will take ownership of the tree root.
+ TestView root(1, gfx::Rect(0, 0, 100, 100));
+ ViewTarget* root_target = root.target();
+ root_target->SetEventTargeter(scoped_ptr<ViewTargeter>(new ViewTargeter()));
+ view_event_dispatcher_->SetRootViewTarget(root_target);
+
+ CaptureController capture_controller;
+ SetCaptureController(&root, &capture_controller);
+
+ TestView one(2, gfx::Rect(0, 0, 500, 100));
+ TestView two(3, gfx::Rect(501, 0, 500, 1000));
+
+ root.AddChild(&one);
+ root.AddChild(&two);
+
+ ui::test::TestEventHandler handler;
+ one.target()->AddPreTargetHandler(&handler);
+
+ ui::MouseEvent press(ui::ET_MOUSE_PRESSED, gfx::Point(20, 20),
+ gfx::Point(20, 20), ui::EventTimeForNow(), ui::EF_NONE,
+ ui::EF_NONE);
+ ui::EventDispatchDetails details =
+ view_event_dispatcher_->OnEventFromSource(&press);
+ ASSERT_FALSE(details.dispatcher_destroyed);
+
+ EXPECT_EQ(1, handler.num_mouse_events());
+
+ one.target()->RemovePreTargetHandler(&handler);
+}
+
+TEST_F(ViewTargeterTest, KeyTest) {
+ // The dispatcher will take ownership of the tree root.
+ TestView root(1, gfx::Rect(0, 0, 100, 100));
+ ViewTarget* root_target = root.target();
+ root_target->SetEventTargeter(scoped_ptr<ViewTargeter>(new ViewTargeter()));
+ view_event_dispatcher_->SetRootViewTarget(root_target);
+
+ CaptureController capture_controller;
+ SetCaptureController(&root, &capture_controller);
+
+ TestView one(2, gfx::Rect(0, 0, 500, 100));
+ TestView two(3, gfx::Rect(501, 0, 500, 1000));
+
+ root.AddChild(&one);
+ root.AddChild(&two);
+
+ ui::test::TestEventHandler one_handler;
+ one.target()->AddPreTargetHandler(&one_handler);
+
+ ui::test::TestEventHandler two_handler;
+ two.target()->AddPreTargetHandler(&two_handler);
+
+ FocusController focus_controller(make_scoped_ptr(new BasicFocusRules(&root)));
+ SetFocusController(&root, &focus_controller);
+
+ // Focus |one|. Then test that it receives a key event.
+ focus_controller.FocusView(&one);
+ ui::KeyEvent key_event_one(ui::ET_KEY_PRESSED, ui::VKEY_A, 0);
+ ui::EventDispatchDetails details =
+ view_event_dispatcher_->OnEventFromSource(&key_event_one);
+ ASSERT_FALSE(details.dispatcher_destroyed);
+ EXPECT_EQ(1, one_handler.num_key_events());
+
+ // Focus |two|. Then test that it receives a key event.
+ focus_controller.FocusView(&two);
+ ui::KeyEvent key_event_two(ui::ET_KEY_PRESSED, ui::VKEY_A, 0);
+ details = view_event_dispatcher_->OnEventFromSource(&key_event_two);
+ ASSERT_FALSE(details.dispatcher_destroyed);
+ EXPECT_EQ(1, two_handler.num_key_events());
+
+ two.target()->RemovePreTargetHandler(&two_handler);
+ one.target()->RemovePreTargetHandler(&one_handler);
+}
+
+} // namespace window_manager
diff --git a/mojo/services/window_manager/window_manager_api_unittest.cc b/mojo/services/window_manager/window_manager_api_unittest.cc
new file mode 100644
index 0000000..7d5dccf
--- /dev/null
+++ b/mojo/services/window_manager/window_manager_api_unittest.cc
@@ -0,0 +1,260 @@
+// Copyright 2014 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 "base/bind.h"
+#include "base/command_line.h"
+#include "base/memory/scoped_vector.h"
+#include "base/run_loop.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/service_provider_impl.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+#include "mojo/shell/application_manager/application_manager.h"
+#include "mojo/shell/shell_test_helper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/types.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view_manager.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view_manager_client_factory.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view_manager_delegate.h"
+#include "third_party/mojo_services/src/view_manager/public/interfaces/view_manager.mojom.h"
+#include "third_party/mojo_services/src/window_manager/public/interfaces/window_manager.mojom.h"
+
+using mojo::ApplicationImpl;
+using mojo::Id;
+using mojo::View;
+
+namespace window_manager {
+namespace {
+
+const char kTestServiceURL[] = "mojo:test_url";
+
+void EmptyResultCallback(bool result) {}
+
+class TestWindowManagerObserver : public mojo::WindowManagerObserver {
+ public:
+ using NodeIdCallback = base::Callback<void(Id)>;
+
+ explicit TestWindowManagerObserver(
+ mojo::InterfaceRequest<mojo::WindowManagerObserver> observer_request)
+ : binding_(this, observer_request.Pass()) {}
+ ~TestWindowManagerObserver() override {}
+
+ void set_focus_changed_callback(const NodeIdCallback& callback) {
+ focus_changed_callback_ = callback;
+ }
+ void set_active_window_changed_callback(const NodeIdCallback& callback) {
+ active_window_changed_callback_ = callback;
+ }
+
+ private:
+ // Overridden from mojo::WindowManagerObserver:
+ void OnCaptureChanged(Id new_capture_node_id) override {}
+ void OnFocusChanged(Id focused_node_id) override {
+ if (!focus_changed_callback_.is_null())
+ focus_changed_callback_.Run(focused_node_id);
+ }
+ void OnActiveWindowChanged(Id active_window) override {
+ if (!active_window_changed_callback_.is_null())
+ active_window_changed_callback_.Run(active_window);
+ }
+
+ NodeIdCallback focus_changed_callback_;
+ NodeIdCallback active_window_changed_callback_;
+ mojo::Binding<WindowManagerObserver> binding_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestWindowManagerObserver);
+};
+
+class TestApplicationLoader : public mojo::shell::ApplicationLoader,
+ public mojo::ApplicationDelegate,
+ public mojo::ViewManagerDelegate {
+ public:
+ typedef base::Callback<void(View*)> RootAddedCallback;
+
+ explicit TestApplicationLoader(const RootAddedCallback& root_added_callback)
+ : root_added_callback_(root_added_callback) {}
+ ~TestApplicationLoader() override {}
+
+ private:
+ // Overridden from mojo::shell::ApplicationLoader:
+ void Load(
+ const GURL& url,
+ mojo::InterfaceRequest<mojo::Application> application_request) override {
+ ASSERT_TRUE(application_request.is_pending());
+ scoped_ptr<ApplicationImpl> app(
+ new ApplicationImpl(this, application_request.Pass()));
+ apps_.push_back(app.release());
+ }
+
+ // Overridden from mojo::ApplicationDelegate:
+ void Initialize(ApplicationImpl* app) override {
+ view_manager_client_factory_.reset(
+ new mojo::ViewManagerClientFactory(app->shell(), this));
+ }
+
+ bool ConfigureIncomingConnection(
+ mojo::ApplicationConnection* connection) override {
+ connection->AddService(view_manager_client_factory_.get());
+ return true;
+ }
+
+ // Overridden from mojo::ViewManagerDelegate:
+ void OnEmbed(View* root,
+ mojo::InterfaceRequest<mojo::ServiceProvider> services,
+ mojo::ServiceProviderPtr exposed_services) override {
+ root_added_callback_.Run(root);
+ }
+ void OnViewManagerDisconnected(mojo::ViewManager* view_manager) override {}
+
+ RootAddedCallback root_added_callback_;
+
+ ScopedVector<ApplicationImpl> apps_;
+ scoped_ptr<mojo::ViewManagerClientFactory> view_manager_client_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestApplicationLoader);
+};
+
+} // namespace
+
+class WindowManagerApiTest : public testing::Test {
+ public:
+ WindowManagerApiTest() {}
+ ~WindowManagerApiTest() override {}
+
+ protected:
+ Id WaitForEmbed() {
+ Id id;
+ base::RunLoop run_loop;
+ root_added_callback_ = base::Bind(&WindowManagerApiTest::OnEmbed,
+ base::Unretained(this), &id, &run_loop);
+ run_loop.Run();
+ return id;
+ }
+
+ Id WaitForFocusChange() {
+ Id new_focused;
+ base::RunLoop run_loop;
+ window_manager_observer()->set_focus_changed_callback(
+ base::Bind(&WindowManagerApiTest::OnFocusChanged,
+ base::Unretained(this), &new_focused, &run_loop));
+ run_loop.Run();
+ return new_focused;
+ }
+
+ Id WaitForActiveWindowChange() {
+ Id new_active;
+ base::RunLoop run_loop;
+ window_manager_observer()->set_active_window_changed_callback(
+ base::Bind(&WindowManagerApiTest::OnActiveWindowChanged,
+ base::Unretained(this), &new_active, &run_loop));
+ run_loop.Run();
+ return new_active;
+ }
+
+ Id OpenWindow() {
+ return OpenWindowWithURL(kTestServiceURL);
+ }
+
+ Id OpenWindowWithURL(const std::string& url) {
+ base::RunLoop run_loop;
+ window_manager_->Embed(url, nullptr, nullptr);
+ run_loop.Run();
+ return WaitForEmbed();
+ }
+
+ TestWindowManagerObserver* window_manager_observer() {
+ return window_manager_observer_.get();
+ }
+
+ mojo::WindowManagerPtr window_manager_;
+
+ private:
+ // Overridden from testing::Test:
+ void SetUp() override {
+ test_helper_.reset(new mojo::shell::ShellTestHelper);
+ test_helper_->Init();
+ test_helper_->AddURLMapping(GURL("mojo:window_manager"),
+ GURL("mojo:core_window_manager"));
+ test_helper_->SetLoaderForURL(
+ scoped_ptr<mojo::shell::ApplicationLoader>(
+ new TestApplicationLoader(base::Bind(
+ &WindowManagerApiTest::OnRootAdded, base::Unretained(this)))),
+ GURL(kTestServiceURL));
+ ConnectToWindowManager2();
+ }
+ void TearDown() override {}
+
+ void ConnectToWindowManager2() {
+ test_helper_->application_manager()->ConnectToService(
+ GURL("mojo:window_manager"), &window_manager_);
+ base::RunLoop connect_loop;
+ mojo::WindowManagerObserverPtr observer;
+ window_manager_observer_.reset(
+ new TestWindowManagerObserver(GetProxy(&observer)));
+
+ window_manager_->GetFocusedAndActiveViews(
+ observer.Pass(),
+ base::Bind(&WindowManagerApiTest::GotFocusedAndActiveViews,
+ base::Unretained(this)));
+ connect_loop.Run();
+
+ // The RunLoop above ensures the connection to the window manager completes.
+ // Without this the ApplicationManager would load the window manager twice.
+ test_helper_->application_manager()->ConnectToService(
+ GURL("mojo:core_window_manager"), &window_manager_);
+ }
+
+ void GotFocusedAndActiveViews(uint32_t, uint32_t, uint32_t) {}
+
+ void OnRootAdded(View* root) {
+ if (!root_added_callback_.is_null())
+ root_added_callback_.Run(root);
+ }
+
+ void OnEmbed(Id* root_id,
+ base::RunLoop* loop,
+ View* root) {
+ *root_id = root->id();
+ loop->Quit();
+ }
+
+ void OnFocusChanged(Id* new_focused,
+ base::RunLoop* run_loop,
+ Id focused_node_id) {
+ *new_focused = focused_node_id;
+ run_loop->Quit();
+ }
+
+ void OnActiveWindowChanged(Id* new_active,
+ base::RunLoop* run_loop,
+ Id active_node_id) {
+ *new_active = active_node_id;
+ run_loop->Quit();
+ }
+
+ scoped_ptr<mojo::shell::ShellTestHelper> test_helper_;
+ scoped_ptr<TestWindowManagerObserver> window_manager_observer_;
+ TestApplicationLoader::RootAddedCallback root_added_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowManagerApiTest);
+};
+
+// TODO(sky): resolve this. Temporarily disabled as ApplicationManager ends up
+// loading windowmanager twice because of the mapping of window_manager to
+// core_window_manager.
+TEST_F(WindowManagerApiTest, DISABLED_FocusAndActivateWindow) {
+ Id first_window = OpenWindow();
+ window_manager_->FocusWindow(first_window, base::Bind(&EmptyResultCallback));
+ Id id = WaitForFocusChange();
+ EXPECT_EQ(id, first_window);
+
+ Id second_window = OpenWindow();
+ window_manager_->ActivateWindow(second_window,
+ base::Bind(&EmptyResultCallback));
+ id = WaitForActiveWindowChange();
+ EXPECT_EQ(id, second_window);
+}
+
+} // namespace window_manager
diff --git a/mojo/services/window_manager/window_manager_app.cc b/mojo/services/window_manager/window_manager_app.cc
new file mode 100644
index 0000000..99d2b35
--- /dev/null
+++ b/mojo/services/window_manager/window_manager_app.cc
@@ -0,0 +1,417 @@
+// Copyright 2014 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 "mojo/services/window_manager/window_manager_app.h"
+
+#include "base/message_loop/message_loop.h"
+#include "base/stl_util.h"
+#include "mojo/converters/geometry/geometry_type_converters.h"
+#include "mojo/converters/input_events/input_events_type_converters.h"
+#include "mojo/public/cpp/application/application_connection.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/interfaces/application/shell.mojom.h"
+#include "mojo/services/window_manager/capture_controller.h"
+#include "mojo/services/window_manager/focus_controller.h"
+#include "mojo/services/window_manager/focus_rules.h"
+#include "mojo/services/window_manager/hit_test.h"
+#include "mojo/services/window_manager/view_event_dispatcher.h"
+#include "mojo/services/window_manager/view_target.h"
+#include "mojo/services/window_manager/view_targeter.h"
+#include "mojo/services/window_manager/window_manager_delegate.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view_manager.h"
+
+using mojo::ApplicationConnection;
+using mojo::Id;
+using mojo::ServiceProvider;
+using mojo::View;
+using mojo::WindowManager;
+
+namespace window_manager {
+
+namespace {
+
+Id GetIdForView(View* view) {
+ return view ? view->id() : 0;
+}
+
+} // namespace
+
+// Used for calls to Embed() that occur before we've connected to the
+// ViewManager.
+struct WindowManagerApp::PendingEmbed {
+ mojo::String url;
+ mojo::InterfaceRequest<ServiceProvider> services;
+ mojo::ServiceProviderPtr exposed_services;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerApp, public:
+
+WindowManagerApp::WindowManagerApp(
+ ViewManagerDelegate* view_manager_delegate,
+ WindowManagerDelegate* window_manager_delegate)
+ : shell_(nullptr),
+ wrapped_view_manager_delegate_(view_manager_delegate),
+ window_manager_delegate_(window_manager_delegate),
+ root_(nullptr) {
+}
+
+WindowManagerApp::~WindowManagerApp() {
+ // TODO(msw|sky): Should this destructor explicitly delete the ViewManager?
+ mojo::ViewManager* cached_view_manager = view_manager();
+ for (RegisteredViewIdSet::const_iterator it = registered_view_id_set_.begin();
+ cached_view_manager && it != registered_view_id_set_.end(); ++it) {
+ View* view = cached_view_manager->GetViewById(*it);
+ if (view && view == root_)
+ root_ = nullptr;
+ if (view)
+ view->RemoveObserver(this);
+ }
+ registered_view_id_set_.clear();
+ DCHECK(!root_);
+
+ STLDeleteElements(&connections_);
+}
+
+void WindowManagerApp::AddConnection(WindowManagerImpl* connection) {
+ DCHECK(connections_.find(connection) == connections_.end());
+ connections_.insert(connection);
+}
+
+void WindowManagerApp::RemoveConnection(WindowManagerImpl* connection) {
+ DCHECK(connections_.find(connection) != connections_.end());
+ connections_.erase(connection);
+}
+
+bool WindowManagerApp::SetCapture(Id view_id) {
+ View* view = view_manager()->GetViewById(view_id);
+ return view && SetCaptureImpl(view);
+}
+
+bool WindowManagerApp::FocusWindow(Id view_id) {
+ View* view = view_manager()->GetViewById(view_id);
+ return view && FocusWindowImpl(view);
+}
+
+bool WindowManagerApp::ActivateWindow(Id view_id) {
+ View* view = view_manager()->GetViewById(view_id);
+ return view && ActivateWindowImpl(view);
+}
+
+bool WindowManagerApp::IsReady() const {
+ return !!root_;
+}
+
+void WindowManagerApp::InitFocus(scoped_ptr<FocusRules> rules) {
+ DCHECK(root_);
+
+ focus_controller_.reset(new FocusController(rules.Pass()));
+ focus_controller_->AddObserver(this);
+ SetFocusController(root_, focus_controller_.get());
+
+ capture_controller_.reset(new CaptureController);
+ capture_controller_->AddObserver(this);
+ SetCaptureController(root_, capture_controller_.get());
+}
+
+void WindowManagerApp::Embed(
+ const mojo::String& url,
+ mojo::InterfaceRequest<mojo::ServiceProvider> services,
+ mojo::ServiceProviderPtr exposed_services) {
+ if (view_manager()) {
+ window_manager_delegate_->Embed(url, services.Pass(),
+ exposed_services.Pass());
+ return;
+ }
+ scoped_ptr<PendingEmbed> pending_embed(new PendingEmbed);
+ pending_embed->url = url;
+ pending_embed->services = services.Pass();
+ pending_embed->exposed_services = exposed_services.Pass();
+ pending_embeds_.push_back(pending_embed.release());
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerApp, ApplicationDelegate implementation:
+
+void WindowManagerApp::Initialize(mojo::ApplicationImpl* impl) {
+ shell_ = impl->shell();
+ LaunchViewManager(impl);
+}
+
+bool WindowManagerApp::ConfigureIncomingConnection(
+ ApplicationConnection* connection) {
+ connection->AddService<WindowManager>(this);
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerApp, ViewManagerDelegate implementation:
+
+void WindowManagerApp::OnEmbed(
+ View* root,
+ mojo::InterfaceRequest<mojo::ServiceProvider> services,
+ mojo::ServiceProviderPtr exposed_services) {
+ DCHECK(!root_);
+ root_ = root;
+
+ view_event_dispatcher_.reset(new ViewEventDispatcher);
+
+ RegisterSubtree(root_);
+
+ if (wrapped_view_manager_delegate_) {
+ wrapped_view_manager_delegate_->OnEmbed(root, services.Pass(),
+ exposed_services.Pass());
+ }
+
+ for (PendingEmbed* pending_embed : pending_embeds_) {
+ Embed(pending_embed->url, pending_embed->services.Pass(),
+ pending_embed->exposed_services.Pass());
+ }
+ pending_embeds_.clear();
+}
+
+void WindowManagerApp::OnViewManagerDisconnected(
+ mojo::ViewManager* view_manager) {
+ if (wrapped_view_manager_delegate_)
+ wrapped_view_manager_delegate_->OnViewManagerDisconnected(view_manager);
+
+ base::MessageLoop* message_loop = base::MessageLoop::current();
+ if (message_loop && message_loop->is_running())
+ message_loop->Quit();
+}
+
+bool WindowManagerApp::OnPerformAction(mojo::View* view,
+ const std::string& action) {
+ if (!view)
+ return false;
+ if (action == "capture")
+ return SetCaptureImpl(view);
+ if (action == "focus")
+ return FocusWindowImpl(view);
+ else if (action == "activate")
+ return ActivateWindowImpl(view);
+ return false;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerApp, ViewObserver implementation:
+
+void WindowManagerApp::OnTreeChanged(
+ const ViewObserver::TreeChangeParams& params) {
+ if (params.receiver != root_)
+ return;
+ DCHECK(params.old_parent || params.new_parent);
+ if (!params.target)
+ return;
+
+ if (params.new_parent) {
+ if (registered_view_id_set_.find(params.target->id()) ==
+ registered_view_id_set_.end()) {
+ RegisteredViewIdSet::const_iterator it =
+ registered_view_id_set_.find(params.new_parent->id());
+ DCHECK(it != registered_view_id_set_.end());
+ RegisterSubtree(params.target);
+ }
+ } else if (params.old_parent) {
+ UnregisterSubtree(params.target);
+ }
+}
+
+void WindowManagerApp::OnViewDestroying(View* view) {
+ Unregister(view);
+ if (view == root_) {
+ root_ = nullptr;
+ if (focus_controller_)
+ focus_controller_->RemoveObserver(this);
+ if (capture_controller_)
+ capture_controller_->RemoveObserver(this);
+ }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerApp, ui::EventHandler implementation:
+
+void WindowManagerApp::OnEvent(ui::Event* event) {
+ if (!window_manager_client_)
+ return;
+
+ View* view = static_cast<ViewTarget*>(event->target())->view();
+ if (!view)
+ return;
+
+ if (event->IsKeyEvent()) {
+ const ui::KeyEvent* key_event = static_cast<const ui::KeyEvent*>(event);
+ if (key_event->type() == ui::ET_KEY_PRESSED) {
+ ui::Accelerator accelerator = ConvertEventToAccelerator(key_event);
+ if (accelerator_manager_.Process(accelerator))
+ return;
+ }
+ }
+
+ if (focus_controller_)
+ focus_controller_->OnEvent(event);
+
+ window_manager_client_->DispatchInputEventToView(view->id(),
+ mojo::Event::From(*event));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerApp, mojo::FocusControllerObserver implementation:
+
+void WindowManagerApp::OnFocused(View* gained_focus) {
+ for (Connections::const_iterator it = connections_.begin();
+ it != connections_.end(); ++it) {
+ (*it)->NotifyViewFocused(GetIdForView(gained_focus));
+ }
+}
+
+void WindowManagerApp::OnActivated(View* gained_active) {
+ for (Connections::const_iterator it = connections_.begin();
+ it != connections_.end(); ++it) {
+ (*it)->NotifyWindowActivated(GetIdForView(gained_active));
+ }
+ if (gained_active)
+ gained_active->MoveToFront();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerApp, mojo::CaptureControllerObserver implementation:
+
+void WindowManagerApp::OnCaptureChanged(View* gained_capture) {
+ for (Connections::const_iterator it = connections_.begin();
+ it != connections_.end(); ++it) {
+ (*it)->NotifyCaptureChanged(GetIdForView(gained_capture));
+ }
+ if (gained_capture)
+ gained_capture->MoveToFront();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// WindowManagerApp, private:
+
+bool WindowManagerApp::SetCaptureImpl(View* view) {
+ CHECK(view);
+ capture_controller_->SetCapture(view);
+ return capture_controller_->GetCapture() == view;
+}
+
+bool WindowManagerApp::FocusWindowImpl(View* view) {
+ CHECK(view);
+ focus_controller_->FocusView(view);
+ return focus_controller_->GetFocusedView() == view;
+}
+
+bool WindowManagerApp::ActivateWindowImpl(View* view) {
+ CHECK(view);
+ focus_controller_->ActivateView(view);
+ return focus_controller_->GetActiveView() == view;
+}
+
+void WindowManagerApp::RegisterSubtree(View* view) {
+ view->AddObserver(this);
+ DCHECK(registered_view_id_set_.find(view->id()) ==
+ registered_view_id_set_.end());
+ // All events pass through the root during dispatch, so we only need a handler
+ // installed there.
+ if (view == root_) {
+ ViewTarget* target = ViewTarget::TargetFromView(view);
+ target->SetEventTargeter(scoped_ptr<ViewTargeter>(new ViewTargeter()));
+ target->AddPreTargetHandler(this);
+ view_event_dispatcher_->SetRootViewTarget(target);
+ }
+ registered_view_id_set_.insert(view->id());
+ View::Children::const_iterator it = view->children().begin();
+ for (; it != view->children().end(); ++it)
+ RegisterSubtree(*it);
+}
+
+void WindowManagerApp::UnregisterSubtree(View* view) {
+ for (View* child : view->children())
+ UnregisterSubtree(child);
+ Unregister(view);
+}
+
+void WindowManagerApp::Unregister(View* view) {
+ RegisteredViewIdSet::iterator it = registered_view_id_set_.find(view->id());
+ if (it == registered_view_id_set_.end()) {
+ // Because we unregister in OnViewDestroying() we can still get a subsequent
+ // OnTreeChanged for the same view. Ignore this one.
+ return;
+ }
+ view->RemoveObserver(this);
+ DCHECK(it != registered_view_id_set_.end());
+ registered_view_id_set_.erase(it);
+}
+
+void WindowManagerApp::DispatchInputEventToView(View* view,
+ mojo::EventPtr event) {
+ window_manager_client_->DispatchInputEventToView(view->id(), event.Pass());
+}
+
+void WindowManagerApp::SetViewportSize(const gfx::Size& size) {
+ window_manager_client_->SetViewportSize(mojo::Size::From(size));
+}
+
+void WindowManagerApp::LaunchViewManager(mojo::ApplicationImpl* app) {
+ // TODO(sky): figure out logic if this connection goes away.
+ view_manager_client_factory_.reset(
+ new mojo::ViewManagerClientFactory(shell_, this));
+
+ ApplicationConnection* view_manager_app =
+ app->ConnectToApplication("mojo:view_manager");
+ view_manager_app->ConnectToService(&view_manager_service_);
+
+ view_manager_app->AddService<WindowManagerInternal>(this);
+ view_manager_app->AddService<mojo::NativeViewportEventDispatcher>(this);
+
+ view_manager_app->ConnectToService(&window_manager_client_);
+}
+
+void WindowManagerApp::Create(
+ ApplicationConnection* connection,
+ mojo::InterfaceRequest<WindowManagerInternal> request) {
+ if (wm_internal_binding_.get()) {
+ VLOG(1) <<
+ "WindowManager allows only one WindowManagerInternal connection.";
+ return;
+ }
+ wm_internal_binding_.reset(
+ new mojo::Binding<WindowManagerInternal>(this, request.Pass()));
+}
+
+void WindowManagerApp::Create(ApplicationConnection* connection,
+ mojo::InterfaceRequest<WindowManager> request) {
+ WindowManagerImpl* wm = new WindowManagerImpl(this, false);
+ wm->Bind(request.PassMessagePipe());
+ // WindowManagerImpl is deleted when the connection has an error, or from our
+ // destructor.
+}
+
+void WindowManagerApp::Create(
+ mojo::ApplicationConnection* connection,
+ mojo::InterfaceRequest<mojo::NativeViewportEventDispatcher> request) {
+ new NativeViewportEventDispatcherImpl(this, request.Pass());
+}
+
+void WindowManagerApp::CreateWindowManagerForViewManagerClient(
+ uint16_t connection_id,
+ mojo::ScopedMessagePipeHandle window_manager_pipe) {
+ // TODO(sky): pass in |connection_id| for validation.
+ WindowManagerImpl* wm = new WindowManagerImpl(this, true);
+ wm->Bind(window_manager_pipe.Pass());
+ // WindowManagerImpl is deleted when the connection has an error, or from our
+ // destructor.
+}
+
+void WindowManagerApp::SetViewManagerClient(
+ mojo::ScopedMessagePipeHandle view_manager_client_request) {
+ view_manager_client_.reset(
+ mojo::ViewManagerClientFactory::WeakBindViewManagerToPipe(
+ mojo::MakeRequest<mojo::ViewManagerClient>(
+ view_manager_client_request.Pass()),
+ view_manager_service_.Pass(), shell_, this));
+}
+
+} // namespace window_manager
diff --git a/mojo/services/window_manager/window_manager_app.h b/mojo/services/window_manager/window_manager_app.h
new file mode 100644
index 0000000..f91be46
--- /dev/null
+++ b/mojo/services/window_manager/window_manager_app.h
@@ -0,0 +1,213 @@
+// Copyright 2014 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 SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_APP_H_
+#define SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_APP_H_
+
+#include <set>
+
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/scoped_vector.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/interface_factory_impl.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/string.h"
+#include "mojo/services/window_manager/capture_controller_observer.h"
+#include "mojo/services/window_manager/focus_controller_observer.h"
+#include "mojo/services/window_manager/native_viewport_event_dispatcher_impl.h"
+#include "mojo/services/window_manager/view_target.h"
+#include "mojo/services/window_manager/window_manager_impl.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/types.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view_manager_client_factory.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view_manager_delegate.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view_observer.h"
+#include "third_party/mojo_services/src/window_manager/public/interfaces/window_manager_internal.mojom.h"
+#include "ui/base/accelerators/accelerator_manager.h"
+#include "ui/events/event_handler.h"
+
+namespace gfx {
+class Size;
+}
+
+namespace window_manager {
+
+class CaptureController;
+class FocusController;
+class FocusRules;
+class ViewEventDispatcher;
+class WindowManagerDelegate;
+class WindowManagerImpl;
+
+// Implements core window manager functionality that could conceivably be shared
+// across multiple window managers implementing superficially different user
+// experiences. Establishes communication with the view manager.
+// A window manager wishing to use this core should create and own an instance
+// of this object. They may implement the associated ViewManager/WindowManager
+// delegate interfaces exposed by the view manager, this object provides the
+// canonical implementation of said interfaces but will call out to the wrapped
+// instances.
+class WindowManagerApp
+ : public mojo::ApplicationDelegate,
+ public mojo::ViewManagerDelegate,
+ public mojo::ViewObserver,
+ public ui::EventHandler,
+ public FocusControllerObserver,
+ public CaptureControllerObserver,
+ public mojo::InterfaceFactory<mojo::WindowManager>,
+ public mojo::InterfaceFactory<mojo::WindowManagerInternal>,
+ public mojo::InterfaceFactory<mojo::NativeViewportEventDispatcher>,
+ public mojo::WindowManagerInternal {
+ public:
+ WindowManagerApp(ViewManagerDelegate* view_manager_delegate,
+ WindowManagerDelegate* window_manager_delegate);
+ ~WindowManagerApp() override;
+
+ ViewEventDispatcher* event_dispatcher() {
+ return view_event_dispatcher_.get();
+ }
+
+ // Register/deregister new connections to the window manager service.
+ void AddConnection(WindowManagerImpl* connection);
+ void RemoveConnection(WindowManagerImpl* connection);
+
+ // These are canonical implementations of the window manager API methods.
+ bool SetCapture(mojo::Id view);
+ bool FocusWindow(mojo::Id view);
+ bool ActivateWindow(mojo::Id view);
+
+ void DispatchInputEventToView(mojo::View* view, mojo::EventPtr event);
+ void SetViewportSize(const gfx::Size& size);
+
+ bool IsReady() const;
+
+ FocusController* focus_controller() { return focus_controller_.get(); }
+ CaptureController* capture_controller() { return capture_controller_.get(); }
+
+ void InitFocus(scoped_ptr<FocusRules> rules);
+
+ ui::AcceleratorManager* accelerator_manager() {
+ return &accelerator_manager_;
+ }
+
+ // WindowManagerImpl::Embed() forwards to this. If connected to ViewManager
+ // then forwards to delegate, otherwise waits for connection to establish then
+ // forwards.
+ void Embed(const mojo::String& url,
+ mojo::InterfaceRequest<mojo::ServiceProvider> services,
+ mojo::ServiceProviderPtr exposed_services);
+
+ // Overridden from ApplicationDelegate:
+ void Initialize(mojo::ApplicationImpl* impl) override;
+ bool ConfigureIncomingConnection(
+ mojo::ApplicationConnection* connection) override;
+
+ private:
+ // TODO(sky): rename this. Connections is ambiguous.
+ typedef std::set<WindowManagerImpl*> Connections;
+ typedef std::set<mojo::Id> RegisteredViewIdSet;
+
+ struct PendingEmbed;
+ class WindowManagerInternalImpl;
+
+ mojo::ViewManager* view_manager() {
+ return root_ ? root_->view_manager() : nullptr;
+ }
+
+ bool SetCaptureImpl(mojo::View* view);
+ bool FocusWindowImpl(mojo::View* view);
+ bool ActivateWindowImpl(mojo::View* view);
+
+ ui::Accelerator ConvertEventToAccelerator(const ui::KeyEvent* event);
+
+ // Creates an ViewTarget for every view in the hierarchy beneath |view|,
+ // and adds to the registry so that it can be retrieved later via
+ // GetViewTargetForViewId().
+ // TODO(beng): perhaps View should have a property bag.
+ void RegisterSubtree(mojo::View* view);
+
+ // Recursively invokes Unregister() for |view| and all its descendants.
+ void UnregisterSubtree(mojo::View* view);
+
+ // Deletes the ViewTarget associated with the hierarchy beneath |id|,
+ // and removes from the registry.
+ void Unregister(mojo::View* view);
+
+ // Overridden from ViewManagerDelegate:
+ void OnEmbed(mojo::View* root,
+ mojo::InterfaceRequest<mojo::ServiceProvider> services,
+ mojo::ServiceProviderPtr exposed_services) override;
+ void OnViewManagerDisconnected(mojo::ViewManager* view_manager) override;
+ bool OnPerformAction(mojo::View* view, const std::string& action) override;
+
+ // Overridden from ViewObserver:
+ void OnTreeChanged(const ViewObserver::TreeChangeParams& params) override;
+ void OnViewDestroying(mojo::View* view) override;
+
+ // Overridden from ui::EventHandler:
+ void OnEvent(ui::Event* event) override;
+
+ // Overridden from mojo::FocusControllerObserver:
+ void OnFocused(mojo::View* gained_focus) override;
+ void OnActivated(mojo::View* gained_active) override;
+
+ // Overridden from mojo::CaptureControllerObserver:
+ void OnCaptureChanged(mojo::View* gained_capture) override;
+
+ // Creates the connection to the ViewManager.
+ void LaunchViewManager(mojo::ApplicationImpl* app);
+
+ // InterfaceFactory<WindowManagerInternal>:
+ void Create(
+ mojo::ApplicationConnection* connection,
+ mojo::InterfaceRequest<mojo::WindowManagerInternal> request) override;
+
+ // InterfaceFactory<WindowManager>:
+ void Create(mojo::ApplicationConnection* connection,
+ mojo::InterfaceRequest<mojo::WindowManager> request) override;
+
+ // InterfaceFactory<NativeViewportEventDispatcher>:
+ void Create(mojo::ApplicationConnection* connection,
+ mojo::InterfaceRequest<mojo::NativeViewportEventDispatcher>
+ request) override;
+
+ // WindowManagerInternal:
+ void CreateWindowManagerForViewManagerClient(
+ uint16_t connection_id,
+ mojo::ScopedMessagePipeHandle window_manager_pipe) override;
+ void SetViewManagerClient(
+ mojo::ScopedMessagePipeHandle view_manager_client_request) override;
+
+ mojo::Shell* shell_;
+
+ ViewManagerDelegate* wrapped_view_manager_delegate_;
+ WindowManagerDelegate* window_manager_delegate_;
+
+ mojo::ViewManagerServicePtr view_manager_service_;
+ scoped_ptr<mojo::ViewManagerClientFactory> view_manager_client_factory_;
+ mojo::View* root_;
+
+ scoped_ptr<FocusController> focus_controller_;
+ scoped_ptr<CaptureController> capture_controller_;
+
+ ui::AcceleratorManager accelerator_manager_;
+
+ Connections connections_;
+ RegisteredViewIdSet registered_view_id_set_;
+
+ mojo::WindowManagerInternalClientPtr window_manager_client_;
+
+ ScopedVector<PendingEmbed> pending_embeds_;
+
+ scoped_ptr<mojo::ViewManagerClient> view_manager_client_;
+
+ scoped_ptr<ViewEventDispatcher> view_event_dispatcher_;
+
+ scoped_ptr<mojo::Binding<WindowManagerInternal>> wm_internal_binding_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowManagerApp);
+};
+
+} // namespace window_manager
+
+#endif // SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_APP_H_
diff --git a/mojo/services/window_manager/window_manager_app_android.cc b/mojo/services/window_manager/window_manager_app_android.cc
new file mode 100644
index 0000000..84feff1
--- /dev/null
+++ b/mojo/services/window_manager/window_manager_app_android.cc
@@ -0,0 +1,20 @@
+// Copyright 2015 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 "mojo/services/window_manager/window_manager_app.h"
+
+#include <android/keycodes.h>
+
+#include "ui/events/keycodes/keyboard_codes_posix.h"
+
+namespace window_manager {
+
+ui::Accelerator WindowManagerApp::ConvertEventToAccelerator(
+ const ui::KeyEvent* event) {
+ if (event->platform_keycode() == AKEYCODE_BACK)
+ return ui::Accelerator(ui::VKEY_BROWSER_BACK, 0);
+ return ui::Accelerator(event->key_code(), event->flags());
+}
+
+} // namespace window_manager
diff --git a/mojo/services/window_manager/window_manager_app_linux.cc b/mojo/services/window_manager/window_manager_app_linux.cc
new file mode 100644
index 0000000..11d43dc
--- /dev/null
+++ b/mojo/services/window_manager/window_manager_app_linux.cc
@@ -0,0 +1,14 @@
+// Copyright 2015 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 "mojo/services/window_manager/window_manager_app.h"
+
+namespace window_manager {
+
+ui::Accelerator WindowManagerApp::ConvertEventToAccelerator(
+ const ui::KeyEvent* event) {
+ return ui::Accelerator(event->key_code(), event->flags());
+}
+
+} // namespace window_manager
diff --git a/mojo/services/window_manager/window_manager_app_win.cc b/mojo/services/window_manager/window_manager_app_win.cc
new file mode 100644
index 0000000..11d43dc
--- /dev/null
+++ b/mojo/services/window_manager/window_manager_app_win.cc
@@ -0,0 +1,14 @@
+// Copyright 2015 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 "mojo/services/window_manager/window_manager_app.h"
+
+namespace window_manager {
+
+ui::Accelerator WindowManagerApp::ConvertEventToAccelerator(
+ const ui::KeyEvent* event) {
+ return ui::Accelerator(event->key_code(), event->flags());
+}
+
+} // namespace window_manager
diff --git a/mojo/services/window_manager/window_manager_apptest.cc b/mojo/services/window_manager/window_manager_apptest.cc
new file mode 100644
index 0000000..12a9aca
--- /dev/null
+++ b/mojo/services/window_manager/window_manager_apptest.cc
@@ -0,0 +1,212 @@
+// Copyright 2014 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 "base/bind.h"
+#include "base/run_loop.h"
+#include "mojo/public/cpp/application/application_delegate.h"
+#include "mojo/public/cpp/application/application_impl.h"
+#include "mojo/public/cpp/application/application_test_base.h"
+#include "mojo/public/cpp/application/service_provider_impl.h"
+#include "mojo/public/cpp/system/macros.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view_manager_client_factory.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view_manager_delegate.h"
+#include "third_party/mojo_services/src/window_manager/public/interfaces/window_manager.mojom.h"
+
+namespace mojo {
+namespace {
+
+// TestApplication's view is embedded by the window manager.
+class TestApplication : public ApplicationDelegate, public ViewManagerDelegate {
+ public:
+ TestApplication() : root_(nullptr) {}
+ ~TestApplication() override {}
+
+ View* root() const { return root_; }
+
+ void set_embed_callback(const base::Closure& callback) {
+ embed_callback_ = callback;
+ }
+
+ private:
+ // ApplicationDelegate:
+ void Initialize(ApplicationImpl* app) override {
+ view_manager_client_factory_.reset(
+ new ViewManagerClientFactory(app->shell(), this));
+ }
+
+ bool ConfigureIncomingConnection(ApplicationConnection* connection) override {
+ connection->AddService(view_manager_client_factory_.get());
+ return true;
+ }
+
+ // ViewManagerDelegate:
+ void OnEmbed(View* root,
+ InterfaceRequest<ServiceProvider> services,
+ ServiceProviderPtr exposed_services) override {
+ root_ = root;
+ embed_callback_.Run();
+ }
+ void OnViewManagerDisconnected(ViewManager* view_manager) override {}
+
+ View* root_;
+ base::Closure embed_callback_;
+ scoped_ptr<ViewManagerClientFactory> view_manager_client_factory_;
+
+ MOJO_DISALLOW_COPY_AND_ASSIGN(TestApplication);
+};
+
+class TestWindowManagerObserver : public WindowManagerObserver {
+ public:
+ explicit TestWindowManagerObserver(
+ InterfaceRequest<WindowManagerObserver> observer_request)
+ : binding_(this, observer_request.Pass()) {}
+ ~TestWindowManagerObserver() override {}
+
+ private:
+ // Overridden from WindowManagerClient:
+ void OnCaptureChanged(Id new_capture_node_id) override {}
+ void OnFocusChanged(Id focused_node_id) override {}
+ void OnActiveWindowChanged(Id active_window) override {}
+
+ Binding<WindowManagerObserver> binding_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestWindowManagerObserver);
+};
+
+class WindowManagerApplicationTest : public test::ApplicationTestBase {
+ public:
+ WindowManagerApplicationTest() {}
+ ~WindowManagerApplicationTest() override {}
+
+ protected:
+ // ApplicationTestBase:
+ void SetUp() override {
+ ApplicationTestBase::SetUp();
+ application_impl()->ConnectToService("mojo:window_manager",
+ &window_manager_);
+ }
+ ApplicationDelegate* GetApplicationDelegate() override {
+ return &test_application_;
+ }
+
+ void EmbedApplicationWithURL(const std::string& url) {
+ window_manager_->Embed(url, nullptr, nullptr);
+
+ base::RunLoop run_loop;
+ test_application_.set_embed_callback(run_loop.QuitClosure());
+ run_loop.Run();
+ }
+
+ WindowManagerPtr window_manager_;
+ TestApplication test_application_;
+
+ private:
+ MOJO_DISALLOW_COPY_AND_ASSIGN(WindowManagerApplicationTest);
+};
+
+TEST_F(WindowManagerApplicationTest, Embed) {
+ EXPECT_EQ(nullptr, test_application_.root());
+ EmbedApplicationWithURL(application_impl()->url());
+ EXPECT_NE(nullptr, test_application_.root());
+}
+
+struct BoolCallback {
+ BoolCallback(bool* bool_value, base::RunLoop* run_loop)
+ : bool_value(bool_value), run_loop(run_loop) {}
+
+ void Run(bool value) const {
+ *bool_value = value;
+ run_loop->Quit();
+ }
+
+ bool* bool_value;
+ base::RunLoop* run_loop;
+};
+
+TEST_F(WindowManagerApplicationTest, SetCaptureFailsFromNonVM) {
+ EmbedApplicationWithURL(application_impl()->url());
+ bool callback_value = true;
+ base::RunLoop run_loop;
+ window_manager_->SetCapture(test_application_.root()->id(),
+ BoolCallback(&callback_value, &run_loop));
+ run_loop.Run();
+ // This call only succeeds for WindowManager connections from the ViewManager.
+ EXPECT_FALSE(callback_value);
+}
+
+TEST_F(WindowManagerApplicationTest, FocusWindowFailsFromNonVM) {
+ EmbedApplicationWithURL(application_impl()->url());
+ bool callback_value = true;
+ base::RunLoop run_loop;
+ window_manager_->FocusWindow(test_application_.root()->id(),
+ BoolCallback(&callback_value, &run_loop));
+ run_loop.Run();
+ // This call only succeeds for WindowManager connections from the ViewManager.
+ EXPECT_FALSE(callback_value);
+}
+
+TEST_F(WindowManagerApplicationTest, ActivateWindowFailsFromNonVM) {
+ EmbedApplicationWithURL(application_impl()->url());
+ bool callback_value = true;
+ base::RunLoop run_loop;
+ window_manager_->ActivateWindow(test_application_.root()->id(),
+ BoolCallback(&callback_value, &run_loop));
+ run_loop.Run();
+ // This call only succeeds for WindowManager connections from the ViewManager.
+ EXPECT_FALSE(callback_value);
+}
+
+struct FocusedAndActiveViewsCallback {
+ FocusedAndActiveViewsCallback(uint32* capture_view_id,
+ uint32* focused_view_id,
+ uint32* active_view_id,
+ base::RunLoop* run_loop)
+ : capture_view_id(capture_view_id),
+ focused_view_id(focused_view_id),
+ active_view_id(active_view_id),
+ run_loop(run_loop) {
+ }
+
+ void Run(uint32 capture, uint32 focused, uint32 active) const {
+ *capture_view_id = capture;
+ *focused_view_id = focused;
+ *active_view_id = active;
+ run_loop->Quit();
+ }
+
+ uint32* capture_view_id;
+ uint32* focused_view_id;
+ uint32* active_view_id;
+ base::RunLoop* run_loop;
+};
+
+TEST_F(WindowManagerApplicationTest, GetFocusedAndActiveViewsFailsWithoutFC) {
+ EmbedApplicationWithURL(application_impl()->url());
+ uint32 capture_view_id = -1;
+ uint32 focused_view_id = -1;
+ uint32 active_view_id = -1;
+ base::RunLoop run_loop;
+
+ WindowManagerObserverPtr observer;
+ scoped_ptr<TestWindowManagerObserver> window_manager_observer(
+ new TestWindowManagerObserver(GetProxy(&observer)));
+
+ window_manager_->GetFocusedAndActiveViews(
+ observer.Pass(),
+ FocusedAndActiveViewsCallback(&capture_view_id,
+ &focused_view_id,
+ &active_view_id,
+ &run_loop));
+ run_loop.Run();
+ // This call fails if the WindowManager does not have a FocusController.
+ EXPECT_EQ(0u, capture_view_id);
+ EXPECT_EQ(0u, focused_view_id);
+ EXPECT_EQ(0u, active_view_id);
+}
+
+// TODO(msw): Write tests exercising other WindowManager functionality.
+
+} // namespace
+} // namespace mojo
diff --git a/mojo/services/window_manager/window_manager_delegate.h b/mojo/services/window_manager/window_manager_delegate.h
new file mode 100644
index 0000000..277e3a8
--- /dev/null
+++ b/mojo/services/window_manager/window_manager_delegate.h
@@ -0,0 +1,26 @@
+// Copyright 2014 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 SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_DELEGATE_H_
+#define SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_DELEGATE_H_
+
+#include "mojo/public/cpp/bindings/string.h"
+#include "mojo/public/interfaces/application/service_provider.mojom.h"
+
+namespace window_manager {
+
+class WindowManagerDelegate {
+ public:
+ // See WindowManager::Embed() for details.
+ virtual void Embed(const mojo::String& url,
+ mojo::InterfaceRequest<mojo::ServiceProvider> services,
+ mojo::ServiceProviderPtr exposed_services) = 0;
+
+ protected:
+ virtual ~WindowManagerDelegate() {}
+};
+
+} // namespace mojo
+
+#endif // SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_DELEGATE_H_
diff --git a/mojo/services/window_manager/window_manager_impl.cc b/mojo/services/window_manager/window_manager_impl.cc
new file mode 100644
index 0000000..99b3e2f
--- /dev/null
+++ b/mojo/services/window_manager/window_manager_impl.cc
@@ -0,0 +1,98 @@
+// Copyright 2014 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 "mojo/services/window_manager/window_manager_impl.h"
+
+#include "mojo/services/window_manager/capture_controller.h"
+#include "mojo/services/window_manager/focus_controller.h"
+#include "mojo/services/window_manager/window_manager_app.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view.h"
+
+using mojo::Callback;
+using mojo::Id;
+
+namespace window_manager {
+
+WindowManagerImpl::WindowManagerImpl(WindowManagerApp* window_manager,
+ bool from_vm)
+ : window_manager_(window_manager), from_vm_(from_vm), binding_(this) {
+ window_manager_->AddConnection(this);
+ binding_.set_error_handler(this);
+}
+
+WindowManagerImpl::~WindowManagerImpl() {
+ window_manager_->RemoveConnection(this);
+}
+
+void WindowManagerImpl::Bind(
+ mojo::ScopedMessagePipeHandle window_manager_pipe) {
+ binding_.Bind(window_manager_pipe.Pass());
+}
+
+void WindowManagerImpl::NotifyViewFocused(Id focused_id) {
+ if (from_vm_ && observer_)
+ observer_->OnFocusChanged(focused_id);
+}
+
+void WindowManagerImpl::NotifyWindowActivated(Id active_id) {
+ if (from_vm_ && observer_)
+ observer_->OnActiveWindowChanged(active_id);
+}
+
+void WindowManagerImpl::NotifyCaptureChanged(Id capture_id) {
+ if (from_vm_ && observer_)
+ observer_->OnCaptureChanged(capture_id);
+}
+
+void WindowManagerImpl::Embed(
+ const mojo::String& url,
+ mojo::InterfaceRequest<mojo::ServiceProvider> services,
+ mojo::ServiceProviderPtr exposed_services) {
+ window_manager_->Embed(url, services.Pass(), exposed_services.Pass());
+}
+
+void WindowManagerImpl::SetCapture(Id view,
+ const Callback<void(bool)>& callback) {
+ callback.Run(from_vm_ && window_manager_->IsReady() &&
+ window_manager_->SetCapture(view));
+}
+
+void WindowManagerImpl::FocusWindow(Id view,
+ const Callback<void(bool)>& callback) {
+ callback.Run(from_vm_ && window_manager_->IsReady() &&
+ window_manager_->FocusWindow(view));
+}
+
+void WindowManagerImpl::ActivateWindow(Id view,
+ const Callback<void(bool)>& callback) {
+ callback.Run(from_vm_ && window_manager_->IsReady() &&
+ window_manager_->ActivateWindow(view));
+}
+
+void WindowManagerImpl::GetFocusedAndActiveViews(
+ mojo::WindowManagerObserverPtr observer,
+ const mojo::WindowManager::GetFocusedAndActiveViewsCallback& callback) {
+ observer_ = observer.Pass();
+ if (!window_manager_->focus_controller()) {
+ // TODO(sky): add typedef for 0.
+ callback.Run(0, 0, 0);
+ return;
+ }
+ mojo::View* capture_view =
+ window_manager_->capture_controller()->GetCapture();
+ mojo::View* active_view =
+ window_manager_->focus_controller()->GetActiveView();
+ mojo::View* focused_view =
+ window_manager_->focus_controller()->GetFocusedView();
+ // TODO(sky): sanitize ids for client.
+ callback.Run(capture_view ? capture_view->id() : 0,
+ focused_view ? focused_view->id() : 0,
+ active_view ? active_view->id() : 0);
+}
+
+void WindowManagerImpl::OnConnectionError() {
+ delete this;
+}
+
+} // namespace window_manager
diff --git a/mojo/services/window_manager/window_manager_impl.h b/mojo/services/window_manager/window_manager_impl.h
new file mode 100644
index 0000000..f51941f
--- /dev/null
+++ b/mojo/services/window_manager/window_manager_impl.h
@@ -0,0 +1,68 @@
+// Copyright 2014 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 SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_IMPL_H_
+#define SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_IMPL_H_
+
+#include "base/basictypes.h"
+#include "base/logging.h"
+#include "mojo/public/cpp/bindings/binding.h"
+#include "mojo/public/cpp/bindings/error_handler.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/types.h"
+#include "third_party/mojo_services/src/window_manager/public/interfaces/window_manager.mojom.h"
+
+namespace window_manager {
+
+class WindowManagerApp;
+
+class WindowManagerImpl : public mojo::WindowManager,
+ public mojo::ErrorHandler {
+ public:
+ // See description above |from_vm_| for details on |from_vm|.
+ // WindowManagerImpl deletes itself on connection errors. WindowManagerApp
+ // also deletes WindowManagerImpl in its destructor.
+ WindowManagerImpl(WindowManagerApp* window_manager, bool from_vm);
+ ~WindowManagerImpl() override;
+
+ void Bind(mojo::ScopedMessagePipeHandle window_manager_pipe);
+
+ void NotifyViewFocused(mojo::Id focused_id);
+ void NotifyWindowActivated(mojo::Id active_id);
+ void NotifyCaptureChanged(mojo::Id capture_id);
+
+ private:
+ // mojo::WindowManager:
+ void Embed(const mojo::String& url,
+ mojo::InterfaceRequest<mojo::ServiceProvider> services,
+ mojo::ServiceProviderPtr exposed_services) override;
+ void SetCapture(uint32_t view_id,
+ const mojo::Callback<void(bool)>& callback) override;
+ void FocusWindow(uint32_t view_id,
+ const mojo::Callback<void(bool)>& callback) override;
+ void ActivateWindow(uint32_t view_id,
+ const mojo::Callback<void(bool)>& callback) override;
+ void GetFocusedAndActiveViews(
+ mojo::WindowManagerObserverPtr observer,
+ const mojo::WindowManager::GetFocusedAndActiveViewsCallback& callback)
+ override;
+
+ // mojo::ErrorHandler:
+ void OnConnectionError() override;
+
+ WindowManagerApp* window_manager_;
+
+ // Whether this connection originated from the ViewManager. Connections that
+ // originate from the view manager are expected to have clients. Connections
+ // that don't originate from the view manager do not have clients.
+ const bool from_vm_;
+
+ mojo::Binding<mojo::WindowManager> binding_;
+ mojo::WindowManagerObserverPtr observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(WindowManagerImpl);
+};
+
+} // namespace window_manager
+
+#endif // SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_IMPL_H_
diff --git a/mojo/services/window_manager/window_manager_test_util.cc b/mojo/services/window_manager/window_manager_test_util.cc
new file mode 100644
index 0000000..7c3eb21
--- /dev/null
+++ b/mojo/services/window_manager/window_manager_test_util.cc
@@ -0,0 +1,39 @@
+// Copyright 2014 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 "mojo/services/window_manager/window_manager_test_util.h"
+
+#include "base/stl_util.h"
+#include "mojo/converters/geometry/geometry_type_converters.h"
+#include "ui/gfx/geometry/rect.h"
+
+namespace window_manager {
+
+TestView::TestView(int id, const gfx::Rect& rect)
+ : target_(new ViewTarget(this)) {
+ mojo::ViewPrivate(this).set_id(id);
+
+ mojo::Rect mojo_rect = *mojo::Rect::From(rect);
+ SetBounds(mojo_rect);
+}
+
+TestView::TestView(int id, const gfx::Rect& rect, View* parent)
+ : TestView(id, rect) {
+ parent->AddChild(this);
+}
+
+TestView::~TestView() {
+}
+
+// static
+TestView* TestView::Build(int id, const gfx::Rect& rect) {
+ return new TestView(id, rect);
+}
+
+// static
+TestView* TestView::Build(int id, const gfx::Rect& rect, mojo::View* parent) {
+ return new TestView(id, rect, parent);
+}
+
+} // namespace window_manager
diff --git a/mojo/services/window_manager/window_manager_test_util.h b/mojo/services/window_manager/window_manager_test_util.h
new file mode 100644
index 0000000..62424c7
--- /dev/null
+++ b/mojo/services/window_manager/window_manager_test_util.h
@@ -0,0 +1,43 @@
+// Copyright 2014 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 SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_TEST_UTIL_H_
+#define SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_TEST_UTIL_H_
+
+#include <set>
+
+#include "mojo/services/window_manager/view_target.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/lib/view_private.h"
+#include "third_party/mojo_services/src/view_manager/public/cpp/view.h"
+
+namespace gfx {
+class Rect;
+}
+
+namespace window_manager {
+
+// A wrapper around View so we can instantiate these directly without a
+// ViewManager.
+class TestView : public mojo::View {
+ public:
+ TestView(int id, const gfx::Rect& rect);
+ TestView(int id, const gfx::Rect& rect, mojo::View* parent);
+ ~TestView();
+
+ // Builds a child view as a pointer. The caller is responsible for making
+ // sure that the root of any tree allocated this way is Destroy()ed.
+ static TestView* Build(int id, const gfx::Rect& rect);
+ static TestView* Build(int id, const gfx::Rect& rect, View* parent);
+
+ ViewTarget* target() { return target_; }
+
+ private:
+ ViewTarget* target_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestView);
+};
+
+} // namespace window_manager
+
+#endif // SERVICES_WINDOW_MANAGER_WINDOW_MANAGER_TEST_UTIL_H_