diff options
author | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-17 06:36:09 +0000 |
---|---|---|
committer | sky@chromium.org <sky@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-17 06:36:09 +0000 |
commit | ff84e05ab824d27fc77890028afa15b2635ff4cd (patch) | |
tree | f090e906af48b1959e58d0f0fad97b844e6be49a | |
parent | 36b7914e8fa4ca5b0f83a44456c633223560c03d (diff) | |
download | chromium_src-ff84e05ab824d27fc77890028afa15b2635ff4cd.zip chromium_src-ff84e05ab824d27fc77890028afa15b2635ff4cd.tar.gz chromium_src-ff84e05ab824d27fc77890028afa15b2635ff4cd.tar.bz2 |
Makes it so a node can only the root of one connection at a time
This resulted in needing to make structural changes. For example, if
connection 2 has a root of A, and A has the children B and C then if A
is assigned to another connection (by way of the owner calling Embed
again), then connection 2 is told A has been deleted and B and C are
removed as children of A.
Because this does structural changes the server change id is advanced.
BUG=389339
TEST=covered by unit tests
R=ben@chromium.org
Review URL: https://codereview.chromium.org/397803003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@283663 0039d316-1c4b-4281-b951-d872f2087c98
7 files changed, 318 insertions, 96 deletions
diff --git a/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.cc b/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.cc index a949da8..4a69ff0 100644 --- a/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.cc +++ b/mojo/services/public/cpp/view_manager/lib/view_manager_client_impl.cc @@ -478,6 +478,7 @@ class EmbedTransaction : public ViewManagerTransaction { private: // Overridden from ViewManagerTransaction: virtual void DoCommit() OVERRIDE { + GetAndAdvanceNextServerChangeId(); service()->Embed(url_, node_id_, ActionCompletedCallback()); } virtual void DoActionCompleted(bool success) OVERRIDE { diff --git a/mojo/services/public/interfaces/view_manager/view_manager.mojom b/mojo/services/public/interfaces/view_manager/view_manager.mojom index 0624f59..e65cc6c 100644 --- a/mojo/services/public/interfaces/view_manager/view_manager.mojom +++ b/mojo/services/public/interfaces/view_manager/view_manager.mojom @@ -127,12 +127,20 @@ interface ViewManagerService { SetFocus(uint32 node_id) => (bool success); // Embeds the app for |url| in the specified node. More specifically this - // creates a new connection to the specified url, expecting to get an + // creates a new connection to the specified url, expecting to get a // ViewManagerClient and configures it with the root node |node|. Fails // if |node| was not created by this connection. + // // If a particular client invokes Embed() multiple times with the same url, // the connection is reused. When this happens the ViewManagerClient is // notified of the additional roots by way of OnRootAdded(). + // + // A node may only be a root of one connection at a time. Subsequent calls to + // Embed() for the same node result in the node being removed from the + // current connection. The current connection is told this by way of + // OnNodeDeleted(). + // + // This advances the server change id. Embed(string url, uint32 node_id) => (bool success); // TODO(sky): move these to a separate interface when FIFO works. diff --git a/mojo/services/view_manager/root_node_manager.cc b/mojo/services/view_manager/root_node_manager.cc index 4da56aa..1b19bf5 100644 --- a/mojo/services/view_manager/root_node_manager.cc +++ b/mojo/services/view_manager/root_node_manager.cc @@ -33,6 +33,10 @@ RootNodeManager::ScopedChange::~ScopedChange() { root_->FinishChange(); } +void RootNodeManager::ScopedChange::SendServerChangeIdAdvanced() { + root_->SendServerChangeIdAdvanced(); +} + RootNodeManager::Context::Context() { // Pass in false as native viewport creates the PlatformEventSource. aura::Env::CreateInstance(false); @@ -141,6 +145,16 @@ ViewManagerServiceImpl* RootNodeManager::GetConnectionByCreator( return NULL; } +ViewManagerServiceImpl* RootNodeManager::GetConnectionWithRoot( + const NodeId& id) { + for (ConnectionMap::const_iterator i = connection_map_.begin(); + i != connection_map_.end(); ++i) { + if (i->second->HasRoot(id)) + return i->second; + } + return NULL; +} + void RootNodeManager::DispatchViewInputEventToWindowManager( const View* view, const ui::Event* event) { @@ -236,6 +250,15 @@ void RootNodeManager::FinishChange() { current_change_ = NULL; } +void RootNodeManager::SendServerChangeIdAdvanced() { + CHECK(current_change_); + for (ConnectionMap::iterator i = connection_map_.begin(); + i != connection_map_.end(); ++i) { + if (!DidConnectionMessageClient(i->first)) + i->second->client()->OnServerChangeIdAdvanced(next_server_change_id_ + 1); + } +} + ViewManagerServiceImpl* RootNodeManager::EmbedImpl( const ConnectionSpecificId creator_id, const String& url, @@ -261,6 +284,7 @@ ViewManagerServiceImpl* RootNodeManager::EmbedImpl( root_id); BindToPipe(connection, pipe.handle0.Pass()); connections_created_by_connect_.insert(connection); + OnConnectionMessagedClient(connection->id()); return connection; } diff --git a/mojo/services/view_manager/root_node_manager.h b/mojo/services/view_manager/root_node_manager.h index 62932ab..0b20fdc 100644 --- a/mojo/services/view_manager/root_node_manager.h +++ b/mojo/services/view_manager/root_node_manager.h @@ -69,6 +69,10 @@ class MOJO_VIEW_MANAGER_EXPORT RootNodeManager return message_ids_.count(connection_id) > 0; } + // Sends OnServerChangeIdAdvanced() to all connections that have not yet + // been messaged. + void SendServerChangeIdAdvanced(); + private: RootNodeManager* root_; const ConnectionSpecificId connection_id_; @@ -133,6 +137,9 @@ class MOJO_VIEW_MANAGER_EXPORT RootNodeManager ConnectionSpecificId creator_id, const std::string& url) const; + // Returns the ViewManagerServiceImpl that has |id| as a root. + ViewManagerServiceImpl* GetConnectionWithRoot(const NodeId& id); + void DispatchViewInputEventToWindowManager(const View* view, const ui::Event* event); @@ -178,6 +185,9 @@ class MOJO_VIEW_MANAGER_EXPORT RootNodeManager // Balances a call to PrepareForChange(). void FinishChange(); + // See description in ScopedChange. + void SendServerChangeIdAdvanced(); + // Returns true if the specified connection originated the current change. bool IsChangeSource(ConnectionSpecificId connection_id) const { return current_change_ && current_change_->connection_id() == connection_id; diff --git a/mojo/services/view_manager/view_manager_service_impl.cc b/mojo/services/view_manager/view_manager_service_impl.cc index b98b99d..793a4543 100644 --- a/mojo/services/view_manager/view_manager_service_impl.cc +++ b/mojo/services/view_manager/view_manager_service_impl.cc @@ -86,6 +86,10 @@ const View* ViewManagerServiceImpl::GetView(const ViewId& id) const { return root_node_manager_->GetView(id); } +bool ViewManagerServiceImpl::HasRoot(const NodeId& id) const { + return roots_.find(NodeIdToTransportId(id)) != roots_.end(); +} + void ViewManagerServiceImpl::OnViewManagerServiceImplDestroyed( ConnectionSpecificId id) { if (creator_id_ == id) @@ -119,7 +123,7 @@ void ViewManagerServiceImpl::ProcessNodeHierarchyChanged( if (node->id().connection_id != id_ && !IsNodeDescendantOfRoots(node)) { // Node was a descendant of roots and is no longer, treat it as though the // node was deleted. - RemoveFromKnown(node); + RemoveFromKnown(node, NULL); client()->OnNodeDeleted(NodeIdToTransportId(node->id()), server_change_id); root_node_manager_->OnConnectionMessagedClient(id_); @@ -407,21 +411,24 @@ void ViewManagerServiceImpl::GetUnknownNodesFrom( GetUnknownNodesFrom(children[i], nodes); } -void ViewManagerServiceImpl::RemoveFromKnown(const Node* node) { - if (node->id().connection_id == id_) +void ViewManagerServiceImpl::RemoveFromKnown(const Node* node, + std::vector<Node*>* local_nodes) { + if (node->id().connection_id == id_) { + if (local_nodes) + local_nodes->push_back(GetNode(node->id())); return; + } known_nodes_.erase(NodeIdToTransportId(node->id())); std::vector<const Node*> children = node->GetChildren(); for (size_t i = 0; i < children.size(); ++i) - RemoveFromKnown(children[i]); + RemoveFromKnown(children[i], local_nodes); } -bool ViewManagerServiceImpl::AddRoot(Id transport_node_id) { - if (roots_.count(transport_node_id) > 0) - return false; +void ViewManagerServiceImpl::AddRoot(const NodeId& node_id) { + const Id transport_node_id(NodeIdToTransportId(node_id)); + CHECK(roots_.count(transport_node_id) == 0); std::vector<const Node*> to_send; - const NodeId node_id(NodeIdFromTransportId(transport_node_id)); CHECK_EQ(creator_id_, node_id.connection_id); roots_.insert(transport_node_id); Node* node = GetNode(node_id); @@ -435,7 +442,31 @@ bool ViewManagerServiceImpl::AddRoot(Id transport_node_id) { } client()->OnRootAdded(NodesToNodeDatas(to_send)); - return true; + root_node_manager_->OnConnectionMessagedClient(id_); +} + +void ViewManagerServiceImpl::RemoveRoot(const NodeId& node_id) { + const Id transport_node_id(NodeIdToTransportId(node_id)); + CHECK(roots_.count(transport_node_id) > 0); + + roots_.erase(transport_node_id); + if (roots_.empty()) + roots_.insert(NodeIdToTransportId(InvalidNodeId())); + + // No need to do anything if we created the node. + if (node_id.connection_id == id_) + return; + + client()->OnNodeDeleted(transport_node_id, + root_node_manager_->next_server_change_id()); + root_node_manager_->OnConnectionMessagedClient(id_); + + // This connection no longer knows about the node. Unparent any nodes that + // were parented to nodes in the root. + std::vector<Node*> local_nodes; + RemoveFromKnown(GetNode(node_id), &local_nodes); + for (size_t i = 0; i < local_nodes.size(); ++i) + local_nodes[i]->GetParent()->Remove(local_nodes[i]); } bool ViewManagerServiceImpl::IsNodeDescendantOfRoots(const Node* node) const { @@ -737,13 +768,30 @@ void ViewManagerServiceImpl::Embed(const String& url, const Callback<void(bool)>& callback) { bool success = CanEmbed(transport_node_id); if (success) { - // We may already have this connection, if so reuse it. - ViewManagerServiceImpl* existing_connection = + // Only allow a node to be the root for one connection. + const NodeId node_id(NodeIdFromTransportId(transport_node_id)); + ViewManagerServiceImpl* connection_by_url = root_node_manager_->GetConnectionByCreator(id_, url.To<std::string>()); - if (existing_connection) - success = existing_connection->AddRoot(transport_node_id); - else - root_node_manager_->Embed(id_, url, transport_node_id); + ViewManagerServiceImpl* connection_with_node_as_root = + root_node_manager_->GetConnectionWithRoot(node_id); + if ((connection_by_url != connection_with_node_as_root || + (!connection_by_url && !connection_with_node_as_root)) && + (!connection_by_url || !connection_by_url->HasRoot(node_id))) { + RootNodeManager::ScopedChange change( + this, root_node_manager_, + RootNodeManager::CHANGE_TYPE_ADVANCE_SERVER_CHANGE_ID, true); + // Never message the originating connection. + root_node_manager_->OnConnectionMessagedClient(id_); + if (connection_with_node_as_root) + connection_with_node_as_root->RemoveRoot(node_id); + if (connection_by_url) + connection_by_url->AddRoot(node_id); + else + root_node_manager_->Embed(id_, url, transport_node_id); + change.SendServerChangeIdAdvanced(); + } else { + success = false; + } } callback.Run(success); } diff --git a/mojo/services/view_manager/view_manager_service_impl.h b/mojo/services/view_manager/view_manager_service_impl.h index bf95854..b9b6de27 100644 --- a/mojo/services/view_manager/view_manager_service_impl.h +++ b/mojo/services/view_manager/view_manager_service_impl.h @@ -68,6 +68,9 @@ class MOJO_VIEW_MANAGER_EXPORT ViewManagerServiceImpl } const View* GetView(const ViewId& id) const; + // Returns true if this has |id| as a root. + bool HasRoot(const NodeId& id) const; + // Invoked when a connection is destroyed. void OnViewManagerServiceImplDestroyed(ConnectionSpecificId id); @@ -141,13 +144,16 @@ class MOJO_VIEW_MANAGER_EXPORT ViewManagerServiceImpl void GetUnknownNodesFrom(const Node* node, std::vector<const Node*>* nodes); // Removes |node| and all its descendants from |known_nodes_|. This does not - // recurse through nodes that were created by this connection. - void RemoveFromKnown(const Node* node); + // recurse through nodes that were created by this connection. All nodes owned + // by this connection are added to |local_nodes|. + void RemoveFromKnown(const Node* node, std::vector<Node*>* local_nodes); + + // Adds |node_id| to the set of roots this connection knows about. The caller + // should have verified |node_id| is not among the roots of this connection. + void AddRoot(const NodeId& node_id); - // Adds |transport_node_id| to the set of roots this connection knows about. - // Returns true if |transport_node_id| was not already a root for this - // connection. - bool AddRoot(Id transport_node_id); + // Removes |node_id| from the set of roots this connection knows about. + void RemoveRoot(const NodeId& node_id); // Returns true if |node| is a non-null and a descendant of |roots_| (or // |roots_| is empty). diff --git a/mojo/services/view_manager/view_manager_unittest.cc b/mojo/services/view_manager/view_manager_unittest.cc index da5ae76..e630147 100644 --- a/mojo/services/view_manager/view_manager_unittest.cc +++ b/mojo/services/view_manager/view_manager_unittest.cc @@ -37,6 +37,7 @@ namespace service { namespace { const char kTestServiceURL[] = "mojo:test_url"; +const char kTestServiceURL2[] = "mojo:test_url2"; // ViewManagerProxy is a proxy to an ViewManagerService. It handles invoking // ViewManagerService functions on the right thread in a synchronous manner @@ -89,6 +90,11 @@ class ViewManagerProxy : public TestChangeTracker::Delegate { router_->CloseMessagePipe(); } + void ClearChanges() { + changes_.clear(); + tracker_->changes()->clear(); + } + // The following functions are cover methods for ViewManagerService. They // block until the result is received. bool CreateNode(Id node_id) { @@ -167,11 +173,11 @@ class ViewManagerProxy : public TestChangeTracker::Delegate { base::Unretained(this), nodes)); RunMainLoop(); } - bool Embed(const Id node_id) { + bool Embed(const Id node_id, const char* url) { changes_.clear(); base::AutoReset<bool> auto_reset(&in_embed_, true); bool result = false; - view_manager_->Embed(kTestServiceURL, node_id, + view_manager_->Embed(url, node_id, base::Bind(&ViewManagerProxy::GotResult, base::Unretained(this), &result)); RunMainLoop(); @@ -460,6 +466,10 @@ class ViewManagerTest : public testing::Test { scoped_ptr<ServiceLoader>(new EmbedServiceLoader()), GURL(kTestServiceURL)); + test_helper_.SetLoaderForURL( + scoped_ptr<ServiceLoader>(new EmbedServiceLoader()), + GURL(kTestServiceURL2)); + test_helper_.service_manager()->ConnectToService( GURL("mojo:mojo_view_manager"), &view_manager_init_); @@ -479,7 +489,7 @@ class ViewManagerTest : public testing::Test { protected: void EstablishSecondConnectionWithRoot(Id root_id) { - ASSERT_TRUE(connection_->Embed(root_id)); + ASSERT_TRUE(connection_->Embed(root_id, kTestServiceURL)); connection2_ = ViewManagerProxy::WaitForInstance(); ASSERT_TRUE(connection2_ != NULL); connection2_->DoRunLoopUntilChangesCount(1); @@ -580,24 +590,24 @@ TEST_F(ViewManagerTest, AddRemoveNotify) { // Make 3 a child of 2. { - ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 2), BuildNodeId(1, 3), 1)); + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 2), BuildNodeId(1, 3), 2)); EXPECT_TRUE(connection_->changes().empty()); connection2_->DoRunLoopUntilChangesCount(1); const Changes changes(ChangesToDescription1(connection2_->changes())); ASSERT_EQ(1u, changes.size()); - EXPECT_EQ("ServerChangeIdAdvanced 2", changes[0]); + EXPECT_EQ("ServerChangeIdAdvanced 3", changes[0]); } // Remove 3 from its parent. { - ASSERT_TRUE(connection_->RemoveNodeFromParent(BuildNodeId(1, 3), 2)); + ASSERT_TRUE(connection_->RemoveNodeFromParent(BuildNodeId(1, 3), 3)); EXPECT_TRUE(connection_->changes().empty()); connection2_->DoRunLoopUntilChangesCount(1); const Changes changes(ChangesToDescription1(connection2_->changes())); ASSERT_EQ(1u, changes.size()); - EXPECT_EQ("ServerChangeIdAdvanced 3", changes[0]); + EXPECT_EQ("ServerChangeIdAdvanced 4", changes[0]); } } @@ -610,17 +620,17 @@ TEST_F(ViewManagerTest, AddNodeWithNoChange) { // Make 3 a child of 2. { - ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 2), BuildNodeId(1, 3), 1)); + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 2), BuildNodeId(1, 3), 2)); connection2_->DoRunLoopUntilChangesCount(1); const Changes changes(ChangesToDescription1(connection2_->changes())); ASSERT_EQ(1u, changes.size()); - EXPECT_EQ("ServerChangeIdAdvanced 2", changes[0]); + EXPECT_EQ("ServerChangeIdAdvanced 3", changes[0]); } // Try again, this should fail. { - EXPECT_FALSE(connection_->AddNode(BuildNodeId(1, 2), BuildNodeId(1, 3), 2)); + EXPECT_FALSE(connection_->AddNode(BuildNodeId(1, 2), BuildNodeId(1, 3), 3)); } } @@ -633,16 +643,16 @@ TEST_F(ViewManagerTest, AddAncestorFails) { // Make 3 a child of 2. { - ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 2), BuildNodeId(1, 3), 1)); + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 2), BuildNodeId(1, 3), 2)); connection2_->DoRunLoopUntilChangesCount(1); const Changes changes(ChangesToDescription1(connection2_->changes())); ASSERT_EQ(1u, changes.size()); - EXPECT_EQ("ServerChangeIdAdvanced 2", changes[0]); + EXPECT_EQ("ServerChangeIdAdvanced 3", changes[0]); } // Try to make 2 a child of 3, this should fail since 2 is an ancestor of 3. { - EXPECT_FALSE(connection_->AddNode(BuildNodeId(1, 3), BuildNodeId(1, 2), 2)); + EXPECT_FALSE(connection_->AddNode(BuildNodeId(1, 3), BuildNodeId(1, 2), 3)); } } @@ -665,23 +675,23 @@ TEST_F(ViewManagerTest, AddToRoot) { // Make 3 a child of 21. { - ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 21), BuildNodeId(1, 3), 1)); + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 21), BuildNodeId(1, 3), 2)); connection2_->DoRunLoopUntilChangesCount(1); const Changes changes(ChangesToDescription1(connection2_->changes())); ASSERT_EQ(1u, changes.size()); - EXPECT_EQ("ServerChangeIdAdvanced 2", changes[0]); + EXPECT_EQ("ServerChangeIdAdvanced 3", changes[0]); } // Make 21 a child of 1. { - ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 21), 2)); + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 21), 3)); connection2_->DoRunLoopUntilChangesCount(1); const Changes changes(ChangesToDescription1(connection2_->changes())); ASSERT_EQ(1u, changes.size()); EXPECT_EQ( - "HierarchyChanged change_id=2 node=1,21 new_parent=1,1 old_parent=null", + "HierarchyChanged change_id=3 node=1,21 new_parent=1,1 old_parent=null", changes[0]); } } @@ -697,7 +707,7 @@ TEST_F(ViewManagerTest, NodeHierarchyChangedNodes) { // Make 2 a child of 1. { - ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 2), 2)); + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 2), 3)); // Client 2 should get a hierarchy change that includes the new nodes as it // has not yet seen them. @@ -705,7 +715,7 @@ TEST_F(ViewManagerTest, NodeHierarchyChangedNodes) { const Changes changes(ChangesToDescription1(connection2_->changes())); ASSERT_EQ(1u, changes.size()); EXPECT_EQ( - "HierarchyChanged change_id=2 node=1,2 new_parent=1,1 old_parent=null", + "HierarchyChanged change_id=3 node=1,2 new_parent=1,1 old_parent=null", changes[0]); EXPECT_EQ("[node=1,2 parent=1,1 view=null]," "[node=1,11 parent=1,2 view=null]", @@ -714,7 +724,7 @@ TEST_F(ViewManagerTest, NodeHierarchyChangedNodes) { // Add 1 to the root. { - ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 1), 3)); + ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 1), 4)); // Client 2 should get a hierarchy change that includes the new nodes as it // has not yet seen them. @@ -722,21 +732,21 @@ TEST_F(ViewManagerTest, NodeHierarchyChangedNodes) { const Changes changes(ChangesToDescription1(connection2_->changes())); ASSERT_EQ(1u, changes.size()); EXPECT_EQ( - "HierarchyChanged change_id=3 node=1,1 new_parent=null old_parent=null", + "HierarchyChanged change_id=4 node=1,1 new_parent=null old_parent=null", changes[0]); EXPECT_EQ(std::string(), ChangeNodeDescription(connection2_->changes())); } // Remove 1 from its parent. { - ASSERT_TRUE(connection_->RemoveNodeFromParent(BuildNodeId(1, 1), 4)); + ASSERT_TRUE(connection_->RemoveNodeFromParent(BuildNodeId(1, 1), 5)); EXPECT_TRUE(connection_->changes().empty()); connection2_->DoRunLoopUntilChangesCount(1); const Changes changes(ChangesToDescription1(connection2_->changes())); ASSERT_EQ(1u, changes.size()); EXPECT_EQ( - "HierarchyChanged change_id=4 node=1,1 new_parent=null old_parent=null", + "HierarchyChanged change_id=5 node=1,1 new_parent=null old_parent=null", changes[0]); } @@ -744,13 +754,13 @@ TEST_F(ViewManagerTest, NodeHierarchyChangedNodes) { ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 111))); { ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 11), BuildNodeId(1, 111), - 5)); + 6)); connection2_->DoRunLoopUntilChangesCount(1); const Changes changes(ChangesToDescription1(connection2_->changes())); ASSERT_EQ(1u, changes.size()); EXPECT_EQ( - "HierarchyChanged change_id=5 node=1,111 new_parent=1,11 " + "HierarchyChanged change_id=6 node=1,111 new_parent=1,11 " "old_parent=null", changes[0]); EXPECT_EQ("[node=1,111 parent=1,11 view=null]", ChangeNodeDescription(connection2_->changes())); @@ -758,13 +768,13 @@ TEST_F(ViewManagerTest, NodeHierarchyChangedNodes) { // Reattach 1 to the root. { - ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 1), 6)); + ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 1), 7)); connection2_->DoRunLoopUntilChangesCount(1); const Changes changes(ChangesToDescription1(connection2_->changes())); ASSERT_EQ(1u, changes.size()); EXPECT_EQ( - "HierarchyChanged change_id=6 node=1,1 new_parent=null old_parent=null", + "HierarchyChanged change_id=7 node=1,1 new_parent=null old_parent=null", changes[0]); EXPECT_EQ(std::string(), ChangeNodeDescription(connection2_->changes())); } @@ -793,23 +803,23 @@ TEST_F(ViewManagerTest, NodeHierarchyChangedAddingKnownToUnknown) { // Remove 11, should result in a delete (since 11 is no longer in connection // 2's root). { - ASSERT_TRUE(connection_->RemoveNodeFromParent(BuildNodeId(1, 11), 4)); + ASSERT_TRUE(connection_->RemoveNodeFromParent(BuildNodeId(1, 11), 5)); connection2_->DoRunLoopUntilChangesCount(1); const Changes changes(ChangesToDescription1(connection2_->changes())); ASSERT_EQ(1u, changes.size()); - EXPECT_EQ("NodeDeleted change_id=4 node=1,11", changes[0]); + EXPECT_EQ("NodeDeleted change_id=5 node=1,11", changes[0]); } // Add 2 to 1. { - ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 2), 5)); + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 2), 6)); connection2_->DoRunLoopUntilChangesCount(1); const Changes changes(ChangesToDescription1(connection2_->changes())); ASSERT_EQ(1u, changes.size()); EXPECT_EQ( - "HierarchyChanged change_id=5 node=1,2 new_parent=1,1 old_parent=null", + "HierarchyChanged change_id=6 node=1,2 new_parent=1,1 old_parent=null", changes[0]); EXPECT_EQ("[node=1,2 parent=1,1 view=null]," "[node=1,21 parent=1,2 view=null]", @@ -845,55 +855,55 @@ TEST_F(ViewManagerTest, ReorderNode) { ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false)); { - connection_->ReorderNode(node2_id, node3_id, ORDER_DIRECTION_ABOVE, 6); + connection_->ReorderNode(node2_id, node3_id, ORDER_DIRECTION_ABOVE, 7); connection2_->DoRunLoopUntilChangesCount(1); const Changes changes(ChangesToDescription1(connection2_->changes())); ASSERT_EQ(1u, changes.size()); EXPECT_EQ( - "Reordered change_id=6 node=1,2 relative=1,3 direction=above", + "Reordered change_id=7 node=1,2 relative=1,3 direction=above", changes[0]); } { - connection_->ReorderNode(node2_id, node3_id, ORDER_DIRECTION_BELOW, 7); + connection_->ReorderNode(node2_id, node3_id, ORDER_DIRECTION_BELOW, 8); connection2_->DoRunLoopUntilChangesCount(1); const Changes changes(ChangesToDescription1(connection2_->changes())); ASSERT_EQ(1u, changes.size()); EXPECT_EQ( - "Reordered change_id=7 node=1,2 relative=1,3 direction=below", + "Reordered change_id=8 node=1,2 relative=1,3 direction=below", changes[0]); } { // node2 is already below node3. EXPECT_FALSE( - connection_->ReorderNode(node2_id, node3_id, ORDER_DIRECTION_BELOW, 8)); + connection_->ReorderNode(node2_id, node3_id, ORDER_DIRECTION_BELOW, 9)); } { // node4 & 5 are unknown to connection2_. EXPECT_FALSE(connection2_->ReorderNode( - node4_id, node5_id, ORDER_DIRECTION_ABOVE, 8)); + node4_id, node5_id, ORDER_DIRECTION_ABOVE, 9)); } { // node6 & node3 have different parents. EXPECT_FALSE( - connection_->ReorderNode(node3_id, node6_id, ORDER_DIRECTION_ABOVE, 8)); + connection_->ReorderNode(node3_id, node6_id, ORDER_DIRECTION_ABOVE, 9)); } { // Non-existent node-ids EXPECT_FALSE(connection_->ReorderNode( - BuildNodeId(1, 27), BuildNodeId(1, 28), ORDER_DIRECTION_ABOVE, 8)); + BuildNodeId(1, 27), BuildNodeId(1, 28), ORDER_DIRECTION_ABOVE, 9)); } { // node7 & node8 are un-parented. EXPECT_FALSE( - connection_->ReorderNode(node7_id, node8_id, ORDER_DIRECTION_ABOVE, 8)); + connection_->ReorderNode(node7_id, node8_id, ORDER_DIRECTION_ABOVE, 9)); } } @@ -905,23 +915,23 @@ TEST_F(ViewManagerTest, DeleteNode) { // Make 2 a child of 1. { - ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 2), 1)); + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 2), 2)); connection2_->DoRunLoopUntilChangesCount(1); const Changes changes(ChangesToDescription1(connection2_->changes())); ASSERT_EQ(1u, changes.size()); - EXPECT_EQ("HierarchyChanged change_id=1 node=1,2 new_parent=1,1 " + EXPECT_EQ("HierarchyChanged change_id=2 node=1,2 new_parent=1,1 " "old_parent=null", changes[0]); } // Delete 2. { - ASSERT_TRUE(connection_->DeleteNode(BuildNodeId(1, 2), 2)); + ASSERT_TRUE(connection_->DeleteNode(BuildNodeId(1, 2), 3)); EXPECT_TRUE(connection_->changes().empty()); connection2_->DoRunLoopUntilChangesCount(1); const Changes changes(ChangesToDescription1(connection2_->changes())); ASSERT_EQ(1u, changes.size()); - EXPECT_EQ("NodeDeleted change_id=2 node=1,2", changes[0]); + EXPECT_EQ("NodeDeleted change_id=3 node=1,2", changes[0]); } } @@ -946,12 +956,12 @@ TEST_F(ViewManagerTest, ReuseDeletedNodeId) { // Add 2 to 1. { - ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 2), 1)); + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 2), 2)); connection2_->DoRunLoopUntilChangesCount(1); const Changes changes(ChangesToDescription1(connection2_->changes())); EXPECT_EQ( - "HierarchyChanged change_id=1 node=1,2 new_parent=1,1 old_parent=null", + "HierarchyChanged change_id=2 node=1,2 new_parent=1,1 old_parent=null", changes[0]); EXPECT_EQ("[node=1,2 parent=1,1 view=null]", ChangeNodeDescription(connection2_->changes())); @@ -959,23 +969,23 @@ TEST_F(ViewManagerTest, ReuseDeletedNodeId) { // Delete 2. { - ASSERT_TRUE(connection_->DeleteNode(BuildNodeId(1, 2), 2)); + ASSERT_TRUE(connection_->DeleteNode(BuildNodeId(1, 2), 3)); connection2_->DoRunLoopUntilChangesCount(1); const Changes changes(ChangesToDescription1(connection2_->changes())); ASSERT_EQ(1u, changes.size()); - EXPECT_EQ("NodeDeleted change_id=2 node=1,2", changes[0]); + EXPECT_EQ("NodeDeleted change_id=3 node=1,2", changes[0]); } // Create 2 again, and add it back to 1. Should get the same notification. ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 2))); { - ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 2), 3)); + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 2), 4)); connection2_->DoRunLoopUntilChangesCount(1); const Changes changes(ChangesToDescription1(connection2_->changes())); EXPECT_EQ( - "HierarchyChanged change_id=3 node=1,2 new_parent=1,1 old_parent=null", + "HierarchyChanged change_id=4 node=1,2 new_parent=1,1 old_parent=null", changes[0]); EXPECT_EQ("[node=1,2 parent=1,1 view=null]", ChangeNodeDescription(connection2_->changes())); @@ -1035,16 +1045,16 @@ TEST_F(ViewManagerTest, DeleteNodeWithView) { // Delete node 2. The second connection should not see this because the node // was not known to it. { - ASSERT_TRUE(connection_->DeleteNode(BuildNodeId(1, 2), 1)); + ASSERT_TRUE(connection_->DeleteNode(BuildNodeId(1, 2), 2)); connection2_->DoRunLoopUntilChangesCount(1); const Changes changes(ChangesToDescription1(connection2_->changes())); ASSERT_EQ(1u, changes.size()); - EXPECT_EQ("ServerChangeIdAdvanced 2", changes[0]); + EXPECT_EQ("ServerChangeIdAdvanced 3", changes[0]); } // Parent 3 to 1. - ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 3), 2)); + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 3), 3)); connection2_->DoRunLoopUntilChangesCount(1); // Set view 11 on node 3. @@ -1059,12 +1069,12 @@ TEST_F(ViewManagerTest, DeleteNodeWithView) { // Delete 3. { - ASSERT_TRUE(connection_->DeleteNode(BuildNodeId(1, 3), 3)); + ASSERT_TRUE(connection_->DeleteNode(BuildNodeId(1, 3), 4)); connection2_->DoRunLoopUntilChangesCount(1); const Changes changes(ChangesToDescription1(connection2_->changes())); ASSERT_EQ(1u, changes.size()); - EXPECT_EQ("NodeDeleted change_id=3 node=1,3", changes[0]); + EXPECT_EQ("NodeDeleted change_id=4 node=1,3", changes[0]); } } @@ -1103,14 +1113,14 @@ TEST_F(ViewManagerTest, GetNodeTree) { // Create 11 in first connection and make it a child of 1. ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 11))); - ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 1), 1)); - ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 11), 2)); + ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 1), 2)); + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 1), BuildNodeId(1, 11), 3)); // Create two nodes in second connection, 2 and 3, both children of 1. ASSERT_TRUE(connection2_->CreateNode(BuildNodeId(2, 2))); ASSERT_TRUE(connection2_->CreateNode(BuildNodeId(2, 3))); - ASSERT_TRUE(connection2_->AddNode(BuildNodeId(1, 1), BuildNodeId(2, 2), 3)); - ASSERT_TRUE(connection2_->AddNode(BuildNodeId(1, 1), BuildNodeId(2, 3), 4)); + ASSERT_TRUE(connection2_->AddNode(BuildNodeId(1, 1), BuildNodeId(2, 2), 4)); + ASSERT_TRUE(connection2_->AddNode(BuildNodeId(1, 1), BuildNodeId(2, 3), 5)); // Attach view to node 11 in the first connection. ASSERT_TRUE(connection_->CreateView(BuildViewId(1, 51))); @@ -1182,7 +1192,7 @@ TEST_F(ViewManagerTest, SetRoots) { { ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false)); - ASSERT_TRUE(connection_->Embed(BuildNodeId(1, 3))); + ASSERT_TRUE(connection_->Embed(BuildNodeId(1, 3), kTestServiceURL)); connection2_->DoRunLoopUntilChangesCount(1); const Changes changes(ChangesToDescription1(connection2_->changes())); ASSERT_EQ(1u, changes.size()); @@ -1194,22 +1204,22 @@ TEST_F(ViewManagerTest, SetRoots) { // Create 4 and add it to the root, connection 2 should only get id advanced. { ASSERT_TRUE(connection_->CreateNode(BuildNodeId(1, 4))); - ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 4), 2)); + ASSERT_TRUE(connection_->AddNode(BuildNodeId(0, 1), BuildNodeId(1, 4), 4)); connection2_->DoRunLoopUntilChangesCount(1); const Changes changes(ChangesToDescription1(connection2_->changes())); ASSERT_EQ(1u, changes.size()); - EXPECT_EQ("ServerChangeIdAdvanced 3", changes[0]); + EXPECT_EQ("ServerChangeIdAdvanced 5", changes[0]); } // Move 4 under 3, this should expose 4 to the client. { - ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 3), BuildNodeId(1, 4), 3)); + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 3), BuildNodeId(1, 4), 5)); connection2_->DoRunLoopUntilChangesCount(1); const Changes changes(ChangesToDescription1(connection2_->changes())); ASSERT_EQ(1u, changes.size()); EXPECT_EQ( - "HierarchyChanged change_id=3 node=1,4 new_parent=1,3 " + "HierarchyChanged change_id=5 node=1,4 new_parent=1,3 " "old_parent=null", changes[0]); EXPECT_EQ("[node=1,4 parent=1,3 view=null]", ChangeNodeDescription(connection2_->changes())); @@ -1217,22 +1227,22 @@ TEST_F(ViewManagerTest, SetRoots) { // Move 4 under 2, since 2 isn't a root client should get a delete. { - ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 2), BuildNodeId(1, 4), 4)); + ASSERT_TRUE(connection_->AddNode(BuildNodeId(1, 2), BuildNodeId(1, 4), 6)); connection2_->DoRunLoopUntilChangesCount(1); const Changes changes(ChangesToDescription1(connection2_->changes())); ASSERT_EQ(1u, changes.size()); - EXPECT_EQ("NodeDeleted change_id=4 node=1,4", changes[0]); + EXPECT_EQ("NodeDeleted change_id=6 node=1,4", changes[0]); } // Delete 4, client shouldn't receive a delete since it should no longer know // about 4. { - ASSERT_TRUE(connection_->DeleteNode(BuildNodeId(1, 4), 5)); + ASSERT_TRUE(connection_->DeleteNode(BuildNodeId(1, 4), 7)); connection2_->DoRunLoopUntilChangesCount(1); const Changes changes(ChangesToDescription1(connection2_->changes())); ASSERT_EQ(1u, changes.size()); - EXPECT_EQ("ServerChangeIdAdvanced 6", changes[0]); + EXPECT_EQ("ServerChangeIdAdvanced 8", changes[0]); } } @@ -1267,17 +1277,17 @@ TEST_F(ViewManagerTest, CantRemoveNodesInOtherRoots) { ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(false)); // Connection 2 should not be able to remove node 2 or 1 from its parent. - ASSERT_FALSE(connection2_->RemoveNodeFromParent(BuildNodeId(1, 2), 3)); - ASSERT_FALSE(connection2_->RemoveNodeFromParent(BuildNodeId(1, 1), 3)); + ASSERT_FALSE(connection2_->RemoveNodeFromParent(BuildNodeId(1, 2), 4)); + ASSERT_FALSE(connection2_->RemoveNodeFromParent(BuildNodeId(1, 1), 4)); // Create nodes 10 and 11 in 2. ASSERT_TRUE(connection2_->CreateNode(BuildNodeId(2, 10))); ASSERT_TRUE(connection2_->CreateNode(BuildNodeId(2, 11))); // Parent 11 to 10. - ASSERT_TRUE(connection2_->AddNode(BuildNodeId(2, 10), BuildNodeId(2, 11), 3)); + ASSERT_TRUE(connection2_->AddNode(BuildNodeId(2, 10), BuildNodeId(2, 11), 4)); // Remove 11 from 10. - ASSERT_TRUE(connection2_->RemoveNodeFromParent( BuildNodeId(2, 11), 4)); + ASSERT_TRUE(connection2_->RemoveNodeFromParent( BuildNodeId(2, 11), 5)); // Verify nothing was actually removed. { @@ -1348,12 +1358,12 @@ TEST_F(ViewManagerTest, ConnectTwice) { // Try to connect again to 1,1, this should fail as already connected to that // root. { - ASSERT_FALSE(connection_->Embed(BuildNodeId(1, 1))); + ASSERT_FALSE(connection_->Embed(BuildNodeId(1, 1), kTestServiceURL)); } // Connecting to 1,2 should succeed and end up in connection2. { - ASSERT_TRUE(connection_->Embed(BuildNodeId(1, 2))); + ASSERT_TRUE(connection_->Embed(BuildNodeId(1, 2), kTestServiceURL)); connection2_->DoRunLoopUntilChangesCount(1); const Changes changes(ChangesToDescription1(connection2_->changes())); ASSERT_EQ(1u, changes.size()); @@ -1384,6 +1394,121 @@ TEST_F(ViewManagerTest, OnViewInput) { } } +TEST_F(ViewManagerTest, EmbedWithSameNodeId) { + ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); + + // Create a third connection. + ViewManagerProxy* connection3 = NULL; + { + ASSERT_TRUE(connection_->Embed(BuildNodeId(1, 1), kTestServiceURL2)); + connection3 = ViewManagerProxy::WaitForInstance(); + ASSERT_TRUE(connection3 != NULL); + connection3->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection3->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ("OnConnectionEstablished creator=mojo:test_url", changes[0]); + } + + // Connection2 should have been told the node was deleted. + { + connection2_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection2_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ("NodeDeleted change_id=2 node=1,1", changes[0]); + } + + // Connection2 has no root. Verify it can't see node 1,1 anymore. + { + std::vector<TestNode> nodes; + connection2_->GetNodeTree(BuildNodeId(1, 1), &nodes); + EXPECT_TRUE(nodes.empty()); + } + + connection3->Destroy(); +} + +TEST_F(ViewManagerTest, EmbedWithSameNodeId2) { + ASSERT_NO_FATAL_FAILURE(EstablishSecondConnection(true)); + + // Create a third connection. + ViewManagerProxy* connection3 = NULL; + { + ASSERT_TRUE(connection_->Embed(BuildNodeId(1, 1), kTestServiceURL2)); + connection3 = ViewManagerProxy::WaitForInstance(); + ASSERT_TRUE(connection3 != NULL); + connection3->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection3->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ("OnConnectionEstablished creator=mojo:test_url", changes[0]); + } + + // Connection2 should have been told the node was deleted. + connection2_->DoRunLoopUntilChangesCount(1); + connection2_->ClearChanges(); + + // Create a node in the third connection and parent it to the root. + ASSERT_TRUE(connection3->CreateNode(BuildNodeId(3, 1))); + ASSERT_TRUE(connection3->AddNode(BuildNodeId(1, 1), BuildNodeId(3, 1), 3)); + + // Connection 1 should have been told about the add (it owns the node). + { + connection_->DoRunLoopUntilChangesCount(1); + const Changes changes(ChangesToDescription1(connection_->changes())); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ( + "HierarchyChanged change_id=3 node=3,1 new_parent=1,1 old_parent=null", + changes[0]); + } + + // connection2_ should get an advance. + connection2_->DoRunLoopUntilChangesCount(1); + connection2_->ClearChanges(); + + // Embed 1,1 back in connection 2. + { + // 2 should be told about the new embed. + ASSERT_TRUE(connection_->Embed(BuildNodeId(1, 1), kTestServiceURL)); + connection2_->DoRunLoopUntilChangesCount(1); + const std::vector<Change>& changes(connection2_->changes()); + ASSERT_EQ(1u, changes.size()); + EXPECT_EQ("OnRootAdded", ChangesToDescription1(changes)[0]); + EXPECT_EQ("[node=1,1 parent=null view=null]", + ChangeNodeDescription(changes)); + + // And 3 should get a delete. + connection3->DoRunLoopUntilChangesCount(1); + ASSERT_EQ(1u, connection3->changes().size()); + EXPECT_EQ("NodeDeleted change_id=4 node=1,1", + ChangesToDescription1(connection3->changes())[0]); + } + + // Connection3 has no root. Verify it can't see node 1,1 anymore. + { + std::vector<TestNode> nodes; + connection3->GetNodeTree(BuildNodeId(1, 1), &nodes); + EXPECT_TRUE(nodes.empty()); + } + + // Verify 3,1 is no longer parented to 1,1. We have to do this from 1,1 as + // connection3 can no longer see 1,1. + { + std::vector<TestNode> nodes; + connection_->GetNodeTree(BuildNodeId(1, 1), &nodes); + ASSERT_EQ(1u, nodes.size()); + EXPECT_EQ("node=1,1 parent=null view=null", nodes[0].ToString()); + } + + // Verify connection3 can still see the node it created 3,1. + { + std::vector<TestNode> nodes; + connection3->GetNodeTree(BuildNodeId(3, 1), &nodes); + ASSERT_EQ(1u, nodes.size()); + EXPECT_EQ("node=3,1 parent=null view=null", nodes[0].ToString()); + } + + connection3->Destroy(); +} + // TODO(sky): add coverage of test that destroys connections and ensures other // connections get deletion notification (or advanced server id). |