summaryrefslogtreecommitdiffstats
path: root/mojo/services
diff options
context:
space:
mode:
Diffstat (limited to 'mojo/services')
-rw-r--r--mojo/services/public/interfaces/view_manager/view_manager.mojom30
-rw-r--r--mojo/services/view_manager/root_view_manager.cc49
-rw-r--r--mojo/services/view_manager/root_view_manager.h57
-rw-r--r--mojo/services/view_manager/view.cc30
-rw-r--r--mojo/services/view_manager/view.h20
-rw-r--r--mojo/services/view_manager/view_delegate.h35
-rw-r--r--mojo/services/view_manager/view_manager_connection.cc39
-rw-r--r--mojo/services/view_manager/view_manager_connection.h33
-rw-r--r--mojo/services/view_manager/view_manager_connection_unittest.cc309
-rw-r--r--mojo/services/view_manager/view_manager_export.h26
10 files changed, 599 insertions, 29 deletions
diff --git a/mojo/services/public/interfaces/view_manager/view_manager.mojom b/mojo/services/public/interfaces/view_manager/view_manager.mojom
index 45a7e69..456b221 100644
--- a/mojo/services/public/interfaces/view_manager/view_manager.mojom
+++ b/mojo/services/public/interfaces/view_manager/view_manager.mojom
@@ -6,25 +6,33 @@ module mojo.services.view_manager {
// Each View is identified by the following pair. The |manager_id| is assigned
// by the server and uniquely identifies the ViewManager. A value of 0 can be
-// used to indicate 'this' manager. The |view_id| is assigned by the client.
-// Non-negative values may be used. Server uses negative to identify special
-// values. For example, -1 is the root.
+// used to indicate 'this' manager. For received notifications a value of 0 for
+// |manager_id| indicates the view is owned by the ViewManager. The |view_id|
+// is assigned by the client. Non-negative values may be used. Server uses
+// negative to identify special values. For example, -1 is the root.
struct ViewId {
int32 manager_id;
int32 view_id;
};
+
+// Functions that mutate the hierarchy take a |change_id|. |change_id| is an
+// arbitrary value assigned by the client originating the change. It may be
+// used by the client originating the change to later identify the change in
+// an OnViewHierarchyChanged callback. |change_id| is only passed to the client
+// that originated the change. All other clients get a value of 0.
[Peer=ViewManagerClient]
interface ViewManager {
// Creates a new view with the specified id. It is up to the client to ensure
// the id is unique to the connection (the id need not be globally unique).
CreateView(int32 view_id) => (bool success);
- // Reparents a view.
- AddView(ViewId parent, ViewId child) => (bool success);
+ // Reparents a view. See description above class for details of |change_id|.
+ AddView(ViewId parent, ViewId child, int32 change_id) => (bool success);
- // Removes a view from its current parent.
- RemoveFromParent(ViewId view) => (bool success);
+ // Removes a view from its current parent. See description above class for
+ // details of |change_id|.
+ RemoveViewFromParent(ViewId view, int32 change_id) => (bool success);
};
[Peer=ViewManager]
@@ -32,6 +40,14 @@ interface ViewManagerClient {
// Invoked once the connection has been established. |manager_id| is the id
// used to uniquely identify the ViewManager.
OnConnectionEstablished(int32 manager_id);
+
+ // Invoked when a change is done to the hierarchy. |new_parent| and/or
+ // |old_parent| may be NULL. See description above ViewManager for details on
+ // |change_id|.
+ OnViewHierarchyChanged(ViewId view,
+ ViewId new_parent,
+ ViewId old_parent,
+ int32 change_id);
};
}
diff --git a/mojo/services/view_manager/root_view_manager.cc b/mojo/services/view_manager/root_view_manager.cc
index d791cb3..e691314 100644
--- a/mojo/services/view_manager/root_view_manager.cc
+++ b/mojo/services/view_manager/root_view_manager.cc
@@ -17,9 +17,20 @@ const int32_t kRootId = -1;
} // namespace
+RootViewManager::ScopedChange::ScopedChange(ViewManagerConnection* connection,
+ RootViewManager* root,
+ int32_t change_id)
+ : root_(root) {
+ root_->PrepareForChange(connection, change_id);
+}
+
+RootViewManager::ScopedChange::~ScopedChange() {
+ root_->FinishChange();
+}
+
RootViewManager::RootViewManager()
: next_connection_id_(1),
- root_(kRootId) {
+ root_(this, kRootId) {
}
RootViewManager::~RootViewManager() {
@@ -45,6 +56,29 @@ View* RootViewManager::GetView(int32_t manager_id, int32_t view_id) {
return i == connection_map_.end() ? NULL : i->second->GetView(view_id);
}
+void RootViewManager::NotifyViewHierarchyChanged(const ViewId& view,
+ const ViewId& new_parent,
+ const ViewId& old_parent) {
+ for (ConnectionMap::iterator i = connection_map_.begin();
+ i != connection_map_.end(); ++i) {
+ const int32_t change_id = (change_ && i->first == change_->connection_id) ?
+ change_->change_id : 0;
+ i->second->NotifyViewHierarchyChanged(
+ view, new_parent, old_parent, change_id);
+ }
+}
+
+void RootViewManager::PrepareForChange(ViewManagerConnection* connection,
+ int32_t change_id) {
+ DCHECK(!change_.get()); // Should only ever have one change in flight.
+ change_.reset(new Change(connection->id(), change_id));
+}
+
+void RootViewManager::FinishChange() {
+ DCHECK(change_.get()); // PrepareForChange/FinishChange should be balanced.
+ change_.reset();
+}
+
void RootViewManager::OnCreated() {
}
@@ -59,6 +93,19 @@ void RootViewManager::OnEvent(const Event& event,
callback.Run();
}
+ViewId RootViewManager::GetViewId(const View* view) const {
+ ViewId::Builder builder;
+ builder.set_manager_id(0);
+ builder.set_view_id(view->id());
+ return builder.Finish();
+}
+
+void RootViewManager::OnViewHierarchyChanged(const ViewId& view,
+ const ViewId& new_parent,
+ const ViewId& old_parent) {
+ NotifyViewHierarchyChanged(view, new_parent, old_parent);
+}
+
} // namespace view_manager
} // namespace services
} // namespace mojo
diff --git a/mojo/services/view_manager/root_view_manager.h b/mojo/services/view_manager/root_view_manager.h
index 65376ce..dfb7541 100644
--- a/mojo/services/view_manager/root_view_manager.h
+++ b/mojo/services/view_manager/root_view_manager.h
@@ -10,6 +10,8 @@
#include "base/basictypes.h"
#include "mojo/services/native_viewport/native_viewport.mojom.h"
#include "mojo/services/view_manager/view.h"
+#include "mojo/services/view_manager/view_delegate.h"
+#include "mojo/services/view_manager/view_manager_export.h"
namespace mojo {
namespace services {
@@ -21,8 +23,25 @@ class ViewManagerConnection;
// RootViewManager is responsible for managing the set of ViewManagerConnections
// as well as providing the root of the View hierarchy.
-class RootViewManager : public NativeViewportClient {
+class MOJO_VIEW_MANAGER_EXPORT RootViewManager
+ : public NativeViewportClient,
+ public ViewDelegate {
public:
+ // Create when a ViewManagerConnection is about to make a change. Ensures
+ // clients are notified of the correct change id.
+ class ScopedChange {
+ public:
+ ScopedChange(ViewManagerConnection* connection,
+ RootViewManager* root,
+ int32_t change_id);
+ ~ScopedChange();
+
+ private:
+ RootViewManager* root_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedChange);
+ };
+
RootViewManager();
virtual ~RootViewManager();
@@ -36,9 +55,36 @@ class RootViewManager : public NativeViewportClient {
// one.
View* GetView(int32_t manager_id, int32_t view_id);
+ // Notifies all ViewManagerConnections of a hierarchy change.
+ void NotifyViewHierarchyChanged(const ViewId& view,
+ const ViewId& new_parent,
+ const ViewId& old_parent);
+
private:
+ // Tracks a change.
+ struct Change {
+ Change(int32_t connection_id, int32_t change_id)
+ : connection_id(connection_id),
+ change_id(change_id) {
+ }
+
+ int32_t connection_id;
+ int32_t change_id;
+ };
+
typedef std::map<int32_t, ViewManagerConnection*> ConnectionMap;
+ // Invoked when a particular connection is about to make a change. Records
+ // the |change_id| so that it can be supplied to the clients by way of
+ // OnViewHierarchyChanged().
+ // Changes should never nest, meaning each PrepareForChange() must be
+ // balanced with a call to FinishChange() with no PrepareForChange()
+ // in between.
+ void PrepareForChange(ViewManagerConnection* connection, int32_t change_id);
+
+ // Balances a call to PrepareForChange().
+ void FinishChange();
+
// Overridden from NativeViewportClient:
virtual void OnCreated() OVERRIDE;
virtual void OnDestroyed() OVERRIDE;
@@ -46,6 +92,12 @@ class RootViewManager : public NativeViewportClient {
virtual void OnEvent(const Event& event,
const mojo::Callback<void()>& callback) OVERRIDE;
+ // Overriden from ViewDelegate:
+ virtual ViewId GetViewId(const View* view) const OVERRIDE;
+ virtual void OnViewHierarchyChanged(const ViewId& view,
+ const ViewId& new_parent,
+ const ViewId& old_parent) OVERRIDE;
+
// ID to use for next ViewManagerConnection.
int32_t next_connection_id_;
@@ -55,6 +107,9 @@ class RootViewManager : public NativeViewportClient {
// Root of Views that are displayed.
View root_;
+ // If non-null we're processing a change.
+ scoped_ptr<Change> change_;
+
DISALLOW_COPY_AND_ASSIGN(RootViewManager);
};
diff --git a/mojo/services/view_manager/view.cc b/mojo/services/view_manager/view.cc
index 6215854..903e628 100644
--- a/mojo/services/view_manager/view.cc
+++ b/mojo/services/view_manager/view.cc
@@ -4,6 +4,8 @@
#include "mojo/services/view_manager/view.h"
+#include "mojo/public/cpp/bindings/allocation_scope.h"
+#include "mojo/services/view_manager/view_delegate.h"
#include "ui/aura/window_property.h"
DECLARE_WINDOW_PROPERTY_TYPE(mojo::services::view_manager::View*);
@@ -14,9 +16,14 @@ namespace view_manager {
DEFINE_WINDOW_PROPERTY_KEY(View*, kViewKey, NULL);
-// TODO(sky): figure out window delegate.
-View::View(int32_t id) : id_(id), window_(NULL) {
+View::View(ViewDelegate* delegate, const int32_t id)
+ : delegate_(delegate),
+ id_(id),
+ window_(NULL) {
+ DCHECK(delegate); // Must provide a delegate.
window_.set_owned_by_parent(false);
+ window_.AddObserver(this);
+ window_.SetProperty(kViewKey, this);
}
View::~View() {
@@ -36,6 +43,25 @@ void View::Remove(View* child) {
window_.RemoveChild(&child->window_);
}
+ViewId View::GetViewId() const {
+ return delegate_->GetViewId(this);
+}
+
+void View::OnWindowHierarchyChanged(
+ const aura::WindowObserver::HierarchyChangeParams& params) {
+ if (params.target != &window_ || params.receiver != &window_)
+ return;
+ AllocationScope scope;
+ ViewId new_parent_id;
+ if (params.new_parent)
+ new_parent_id = params.new_parent->GetProperty(kViewKey)->GetViewId();
+ ViewId old_parent_id;
+ if (params.old_parent)
+ old_parent_id = params.old_parent->GetProperty(kViewKey)->GetViewId();
+ delegate_->OnViewHierarchyChanged(delegate_->GetViewId(this), new_parent_id,
+ old_parent_id);
+}
+
} // namespace view_manager
} // namespace services
} // namespace mojo
diff --git a/mojo/services/view_manager/view.h b/mojo/services/view_manager/view.h
index 1815f7f..8b954ab 100644
--- a/mojo/services/view_manager/view.h
+++ b/mojo/services/view_manager/view.h
@@ -6,16 +6,23 @@
#define MOJO_SERVICES_VIEW_MANAGER_VIEW_H_
#include "base/logging.h"
+#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h"
+#include "mojo/services/view_manager/view_manager_export.h"
#include "ui/aura/window.h"
+#include "ui/aura/window_observer.h"
namespace mojo {
namespace services {
namespace view_manager {
-class View {
+class RootViewManager;
+class ViewDelegate;
+class ViewId;
+
+class MOJO_VIEW_MANAGER_EXPORT View : public aura::WindowObserver {
public:
- View(int32_t view_id);
- ~View();
+ View(ViewDelegate* delegate, const int32_t id);
+ virtual ~View();
int32 id() const { return id_; }
@@ -25,6 +32,13 @@ class View {
View* GetParent();
private:
+ ViewId GetViewId() const;
+
+ // WindowObserver overrides:
+ virtual void OnWindowHierarchyChanged(
+ const aura::WindowObserver::HierarchyChangeParams& params) OVERRIDE;
+
+ ViewDelegate* delegate_;
const int32_t id_;
aura::Window window_;
diff --git a/mojo/services/view_manager/view_delegate.h b/mojo/services/view_manager/view_delegate.h
new file mode 100644
index 0000000..efd6fa8
--- /dev/null
+++ b/mojo/services/view_manager/view_delegate.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 MOJO_SERVICES_VIEW_MANAGER_VIEW_DELEGATE_H_
+#define MOJO_SERVICES_VIEW_MANAGER_VIEW_DELEGATE_H_
+
+#include "mojo/services/view_manager/view_manager_export.h"
+
+namespace mojo {
+namespace services {
+namespace view_manager {
+
+class View;
+class ViewId;
+
+class MOJO_VIEW_MANAGER_EXPORT ViewDelegate {
+ public:
+ // Returns the ViewId for the view.
+ virtual ViewId GetViewId(const View* view) const = 0;
+
+ // Invoked when the hierarchy has changed.
+ virtual void OnViewHierarchyChanged(const ViewId& view,
+ const ViewId& new_parent,
+ const ViewId& old_parent) = 0;
+
+ protected:
+ virtual ~ViewDelegate() {}
+};
+
+} // namespace view_manager
+} // namespace services
+} // namespace mojo
+
+#endif // MOJO_SERVICES_VIEW_MANAGER_VIEW_DELEGATE_H_
diff --git a/mojo/services/view_manager/view_manager_connection.cc b/mojo/services/view_manager/view_manager_connection.cc
index 5b85d98..12179b8 100644
--- a/mojo/services/view_manager/view_manager_connection.cc
+++ b/mojo/services/view_manager/view_manager_connection.cc
@@ -26,8 +26,8 @@ void ViewManagerConnection::Initialize(
DCHECK_EQ(0, id_); // Should only get Initialize() once.
ServiceConnection<ViewManager, ViewManagerConnection, RootViewManager>::
Initialize(service_factory, client_handle.Pass());
- context()->AddConnection(this);
id_ = context()->GetAndAdvanceNextConnectionId();
+ context()->AddConnection(this);
client()->OnConnectionEstablished(id_);
}
@@ -44,42 +44,67 @@ View* ViewManagerConnection::GetViewById(const ViewId& view_id) {
void ViewManagerConnection::CreateView(
int32_t view_id,
- const mojo::Callback<void(bool)>& callback) {
+ const Callback<void(bool)>& callback) {
// Negative values are reserved.
if (view_map_.find(view_id) != view_map_.end() || view_id < 0) {
callback.Run(false);
return;
}
- view_map_[view_id] = new View(view_id);
+ view_map_[view_id] = new View(this, view_id);
callback.Run(true);
}
void ViewManagerConnection::AddView(
const ViewId& parent_id,
const ViewId& child_id,
- const mojo::Callback<void(bool)>& callback) {
+ int32_t change_id,
+ const Callback<void(bool)>& callback) {
View* parent_view = GetViewById(parent_id);
View* child_view = GetViewById(child_id);
bool success = false;
if (parent_view && child_view && parent_view != child_view) {
+ RootViewManager::ScopedChange change(this, context(), change_id);
parent_view->Add(child_view);
success = true;
}
callback.Run(success);
}
-void ViewManagerConnection::RemoveFromParent(
+void ViewManagerConnection::RemoveViewFromParent(
const ViewId& view_id,
- const mojo::Callback<void(bool)>& callback) {
+ int32_t change_id,
+ const Callback<void(bool)>& callback) {
View* view = GetViewById(view_id);
bool success = false;
if (view && view->GetParent()) {
- view->GetParent()->Add(view);
+ RootViewManager::ScopedChange change(this, context(), change_id);
+ view->GetParent()->Remove(view);
success = true;
}
callback.Run(success);
}
+void ViewManagerConnection::NotifyViewHierarchyChanged(
+ const ViewId& view,
+ const ViewId& new_parent,
+ const ViewId& old_parent,
+ int32_t change_id) {
+ client()->OnViewHierarchyChanged(view, new_parent, old_parent, change_id);
+}
+
+ViewId ViewManagerConnection::GetViewId(const View* view) const {
+ ViewId::Builder builder;
+ builder.set_manager_id(id_);
+ builder.set_view_id(view->id());
+ return builder.Finish();
+}
+
+void ViewManagerConnection::OnViewHierarchyChanged(const ViewId& view,
+ const ViewId& new_parent,
+ const ViewId& old_parent) {
+ context()->NotifyViewHierarchyChanged(view, new_parent, old_parent);
+}
+
} // namespace view_manager
} // namespace services
} // namespace mojo
diff --git a/mojo/services/view_manager/view_manager_connection.h b/mojo/services/view_manager/view_manager_connection.h
index 2b0d4c1..2f75cc3 100644
--- a/mojo/services/view_manager/view_manager_connection.h
+++ b/mojo/services/view_manager/view_manager_connection.h
@@ -10,6 +10,8 @@
#include "base/basictypes.h"
#include "mojo/public/cpp/shell/service.h"
#include "mojo/services/public/interfaces/view_manager/view_manager.mojom.h"
+#include "mojo/services/view_manager/view_delegate.h"
+#include "mojo/services/view_manager/view_manager_export.h"
namespace mojo {
namespace services {
@@ -19,9 +21,10 @@ class RootViewManager;
class View;
// Manages a connection from the client.
-class ViewManagerConnection : public ServiceConnection<ViewManager,
- ViewManagerConnection,
- RootViewManager> {
+class MOJO_VIEW_MANAGER_EXPORT ViewManagerConnection
+ : public ServiceConnection<ViewManager, ViewManagerConnection,
+ RootViewManager>,
+ public ViewDelegate {
public:
ViewManagerConnection();
virtual ~ViewManagerConnection();
@@ -36,6 +39,12 @@ class ViewManagerConnection : public ServiceConnection<ViewManager,
// Returns the View by id.
View* GetView(int32_t id);
+ // Notifies the client of a hierarchy change.
+ void NotifyViewHierarchyChanged(const ViewId& view,
+ const ViewId& new_parent,
+ const ViewId& old_parent,
+ int32_t change_id);
+
private:
typedef std::map<int32_t, View*> ViewMap;
@@ -44,13 +53,21 @@ class ViewManagerConnection : public ServiceConnection<ViewManager,
// Overridden from ViewManager:
virtual void CreateView(int32_t view_id,
- const mojo::Callback<void(bool)>& callback) OVERRIDE;
+ const Callback<void(bool)>& callback) OVERRIDE;
virtual void AddView(const ViewId& parent_id,
const ViewId& child_id,
- const mojo::Callback<void(bool)>& callback) OVERRIDE;
- virtual void RemoveFromParent(
- const ViewId& view_id,
- const mojo::Callback<void(bool)>& callback) OVERRIDE;
+ int32_t change_id,
+ const Callback<void(bool)>& callback) OVERRIDE;
+ virtual void RemoveViewFromParent(
+ const ViewId& view,
+ int32_t change_id,
+ const Callback<void(bool)>& callback) OVERRIDE;
+
+ // Overriden from ViewDelegate:
+ virtual ViewId GetViewId(const View* view) const OVERRIDE;
+ virtual void OnViewHierarchyChanged(const ViewId& view,
+ const ViewId& new_parent,
+ const ViewId& old_parent) OVERRIDE;
// Id of this connection as assigned by RootViewManager. Assigned in
// Initialize().
diff --git a/mojo/services/view_manager/view_manager_connection_unittest.cc b/mojo/services/view_manager/view_manager_connection_unittest.cc
new file mode 100644
index 0000000..611c125
--- /dev/null
+++ b/mojo/services/view_manager/view_manager_connection_unittest.cc
@@ -0,0 +1,309 @@
+// 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/view_manager/view_manager_connection.h"
+
+#include <string>
+#include <vector>
+
+#include "base/bind.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "mojo/public/cpp/bindings/allocation_scope.h"
+#include "mojo/public/cpp/environment/environment.h"
+#include "mojo/services/view_manager/root_view_manager.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace mojo {
+namespace services {
+namespace view_manager {
+
+namespace {
+
+base::RunLoop* current_run_loop = NULL;
+
+// Sets |current_run_loop| and runs it. It is expected that someone else quits
+// the loop.
+void DoRunLoop() {
+ base::RunLoop run_loop;
+ current_run_loop = &run_loop;
+ current_run_loop->Run();
+ current_run_loop = NULL;
+}
+
+// Converts |id| into a string.
+std::string ViewIdToString(const ViewId& id) {
+ return id.is_null() ? "null" :
+ base::StringPrintf("%d,%d", id.manager_id(), id.view_id());
+}
+
+// Boolean callback. Sets |result_cache| to the value of |result| and quits
+// the run loop.
+void BooleanCallback(bool* result_cache, bool result) {
+ *result_cache = result;
+ current_run_loop->Quit();
+}
+
+// Creates a ViewId from the specified parameters.
+ViewId CreateViewId(int32_t manager_id, int32_t view_id) {
+ ViewId::Builder builder;
+ builder.set_manager_id(manager_id);
+ builder.set_view_id(view_id);
+ return builder.Finish();
+}
+
+// Creates a view with the specified id. Returns true on success. Blocks until
+// we get back result from server.
+bool CreateView(ViewManager* view_manager, int32_t id) {
+ bool result = false;
+ view_manager->CreateView(id, base::Bind(&BooleanCallback, &result));
+ DoRunLoop();
+ return result;
+}
+
+// Adds a view, blocking until done.
+bool AddView(ViewManager* view_manager,
+ const ViewId& parent,
+ const ViewId& child,
+ int32_t change_id) {
+ bool result = false;
+ view_manager->AddView(parent, child, change_id,
+ base::Bind(&BooleanCallback, &result));
+ DoRunLoop();
+ return result;
+}
+
+// Removes a view, blocking until done.
+bool RemoveViewFromParent(ViewManager* view_manager,
+ const ViewId& view,
+ int32_t change_id) {
+ bool result = false;
+ view_manager->RemoveViewFromParent(view, change_id,
+ base::Bind(&BooleanCallback, &result));
+ DoRunLoop();
+ return result;
+}
+
+} // namespace
+
+typedef std::vector<std::string> Changes;
+
+class ViewManagerClientImpl : public ViewManagerClient {
+ public:
+ ViewManagerClientImpl() : id_(0), quit_count_(0) {}
+
+ void set_quit_count(int count) { quit_count_ = count; }
+
+ int32_t id() const { return id_; }
+
+ Changes GetAndClearChanges() {
+ Changes changes;
+ changes.swap(changes_);
+ return changes;
+ }
+
+ private:
+ // View overrides:
+ virtual void OnConnectionEstablished(int32_t manager_id) OVERRIDE {
+ id_ = manager_id;
+ current_run_loop->Quit();
+ }
+ virtual void OnViewHierarchyChanged(const ViewId& view,
+ const ViewId& new_parent,
+ const ViewId& old_parent,
+ int32_t change_id) OVERRIDE {
+ changes_.push_back(
+ base::StringPrintf(
+ "change_id=%d view=%s new_parent=%s old_parent=%s",
+ change_id, ViewIdToString(view).c_str(),
+ ViewIdToString(new_parent).c_str(),
+ ViewIdToString(old_parent).c_str()));
+ if (quit_count_ > 0) {
+ if (--quit_count_ == 0)
+ current_run_loop->Quit();
+ }
+ }
+
+ int32_t id_;
+
+ // Used to determine when/if to quit the run loop.
+ int quit_count_;
+
+ Changes changes_;
+
+ DISALLOW_COPY_AND_ASSIGN(ViewManagerClientImpl);
+};
+
+class ViewManagerConnectionTest : public testing::Test {
+ public:
+ ViewManagerConnectionTest() : service_factory_(&root_view_manager_) {}
+
+ virtual void SetUp() OVERRIDE {
+ InterfacePipe<ViewManagerClient, ViewManager> pipe;
+ view_manager_.reset(pipe.handle_to_peer.Pass(), &client_);
+ connection_.Initialize(
+ &service_factory_,
+ ScopedMessagePipeHandle::From(pipe.handle_to_self.Pass()));
+ // Wait for the id.
+ DoRunLoop();
+ }
+
+ protected:
+ // Creates a second connection to the viewmanager.
+ void EstablishSecondConnection() {
+ connection2_.reset(new ViewManagerConnection);
+ InterfacePipe<ViewManagerClient, ViewManager> pipe;
+ view_manager2_.reset(pipe.handle_to_peer.Pass(), &client2_);
+ connection2_->Initialize(
+ &service_factory_,
+ ScopedMessagePipeHandle::From(pipe.handle_to_self.Pass()));
+ // Wait for the id.
+ DoRunLoop();
+ }
+
+ Environment env_;
+ base::MessageLoop loop_;
+ RootViewManager root_view_manager_;
+ ServiceConnector<ViewManagerConnection, RootViewManager> service_factory_;
+ ViewManagerConnection connection_;
+ ViewManagerClientImpl client_;
+ RemotePtr<ViewManager> view_manager_;
+
+ ViewManagerClientImpl client2_;
+ RemotePtr<ViewManager> view_manager2_;
+ scoped_ptr<ViewManagerConnection> connection2_;
+
+ DISALLOW_COPY_AND_ASSIGN(ViewManagerConnectionTest);
+};
+
+// Verifies client gets a valid id.
+TEST_F(ViewManagerConnectionTest, ValidId) {
+ EXPECT_NE(0, client_.id());
+}
+
+// Verifies two clients/connections get different ids.
+TEST_F(ViewManagerConnectionTest, TwoClientsGetDifferentConnectionIds) {
+ EstablishSecondConnection();
+ EXPECT_NE(0, client2_.id());
+ EXPECT_NE(client_.id(), client2_.id());
+}
+
+// Verifies client gets a valid id.
+TEST_F(ViewManagerConnectionTest, CreateView) {
+ ASSERT_TRUE(CreateView(view_manager_.get(), 1));
+
+ // Can't create a view with the same id.
+ ASSERT_FALSE(CreateView(view_manager_.get(), 1));
+}
+
+// Verifies hierarchy changes.
+TEST_F(ViewManagerConnectionTest, AddRemoveNotify) {
+ ASSERT_TRUE(CreateView(view_manager_.get(), 1));
+ ASSERT_TRUE(CreateView(view_manager_.get(), 2));
+
+ EXPECT_TRUE(client_.GetAndClearChanges().empty());
+
+ // Make 2 a child of 1.
+ {
+ AllocationScope scope;
+ ASSERT_TRUE(AddView(view_manager_.get(),
+ CreateViewId(client_.id(), 1),
+ CreateViewId(client_.id(), 2),
+ 11));
+ Changes changes(client_.GetAndClearChanges());
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("change_id=11 view=1,2 new_parent=1,1 old_parent=null",
+ changes[0]);
+ }
+
+ // Remove 2 from its parent.
+ {
+ AllocationScope scope;
+ ASSERT_TRUE(RemoveViewFromParent(view_manager_.get(),
+ CreateViewId(client_.id(), 2),
+ 101));
+ Changes changes(client_.GetAndClearChanges());
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("change_id=101 view=1,2 new_parent=null old_parent=1,1",
+ changes[0]);
+ }
+}
+
+// Verifies hierarchy changes are sent to multiple clients.
+TEST_F(ViewManagerConnectionTest, AddRemoveNotifyMultipleConnections) {
+ EstablishSecondConnection();
+
+ // Create two views in first connection.
+ ASSERT_TRUE(CreateView(view_manager_.get(), 1));
+ ASSERT_TRUE(CreateView(view_manager_.get(), 2));
+
+ EXPECT_TRUE(client_.GetAndClearChanges().empty());
+ EXPECT_TRUE(client2_.GetAndClearChanges().empty());
+
+ // Make 2 a child of 1.
+ {
+ AllocationScope scope;
+ ASSERT_TRUE(AddView(view_manager_.get(),
+ CreateViewId(client_.id(), 1),
+ CreateViewId(client_.id(), 2),
+ 11));
+ Changes changes(client_.GetAndClearChanges());
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("change_id=11 view=1,2 new_parent=1,1 old_parent=null",
+ changes[0]);
+ }
+
+ // Second client should also have received the change.
+ {
+ Changes changes(client2_.GetAndClearChanges());
+ if (changes.empty()) {
+ client2_.set_quit_count(1);
+ DoRunLoop();
+ changes = client2_.GetAndClearChanges();
+ }
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("change_id=0 view=1,2 new_parent=1,1 old_parent=null",
+ changes[0]);
+ }
+}
+
+// Verifies adding to root sends right notifications.
+TEST_F(ViewManagerConnectionTest, AddToRoot) {
+ ASSERT_TRUE(CreateView(view_manager_.get(), 21));
+ ASSERT_TRUE(CreateView(view_manager_.get(), 3));
+
+ EXPECT_TRUE(client_.GetAndClearChanges().empty());
+
+ // Make 3 a child of 21.
+ {
+ AllocationScope scope;
+ ASSERT_TRUE(AddView(view_manager_.get(),
+ CreateViewId(client_.id(), 21),
+ CreateViewId(client_.id(), 3),
+ 11));
+ Changes changes(client_.GetAndClearChanges());
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("change_id=11 view=1,3 new_parent=1,21 old_parent=null",
+ changes[0]);
+ }
+
+ // Make 21 a child of the root.
+ {
+ AllocationScope scope;
+ ASSERT_TRUE(AddView(view_manager_.get(),
+ CreateViewId(0, -1),
+ CreateViewId(client_.id(), 21),
+ 44));
+ Changes changes(client_.GetAndClearChanges());
+ ASSERT_EQ(1u, changes.size());
+ EXPECT_EQ("change_id=44 view=1,21 new_parent=0,-1 old_parent=null",
+ changes[0]);
+ }
+}
+
+} // namespace view_manager
+} // namespace services
+} // namespace mojo
diff --git a/mojo/services/view_manager/view_manager_export.h b/mojo/services/view_manager/view_manager_export.h
new file mode 100644
index 0000000..f48f94e
--- /dev/null
+++ b/mojo/services/view_manager/view_manager_export.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 MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_EXPORT_H_
+#define MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_EXPORT_H_
+
+#if defined(WIN32)
+
+#if defined(MOJO_VIEW_MANAGER_IMPLEMENTATION)
+#define MOJO_VIEW_MANAGER_EXPORT __declspec(dllexport)
+#else
+#define MOJO_VIEW_MANAGER_EXPORT __declspec(dllimport)
+#endif
+
+#else // !defined(WIN32)
+
+#if defined(MOJO_VIEW_MANAGER_IMPLEMENTATION)
+#define MOJO_VIEW_MANAGER_EXPORT __attribute__((visibility("default")))
+#else
+#define MOJO_VIEW_MANAGER_EXPORT
+#endif
+
+#endif // defined(WIN32)
+
+#endif // MOJO_SERVICES_VIEW_MANAGER_VIEW_MANAGER_EXPORT_H_