Index: mojo/services/view_manager/view_manager_service_impl.cc |
diff --git a/mojo/services/view_manager/view_manager_service_impl.cc b/mojo/services/view_manager/view_manager_service_impl.cc |
index 96cf1021eabcee262b2b57231782b32b16d08949..1f169b9f3c1e07637979fd40aeca2346a209aad3 100644 |
--- a/mojo/services/view_manager/view_manager_service_impl.cc |
+++ b/mojo/services/view_manager/view_manager_service_impl.cc |
@@ -17,21 +17,6 @@ |
namespace mojo { |
namespace view_manager { |
namespace service { |
-namespace { |
- |
-// Places |node| in |nodes| and recurses through the children. |
-void GetDescendants(const Node* node, std::vector<const Node*>* nodes) { |
- if (!node) |
- return; |
- |
- nodes->push_back(node); |
- |
- std::vector<const Node*> children(node->GetChildren()); |
- for (size_t i = 0 ; i < children.size(); ++i) |
- GetDescendants(children[i], nodes); |
-} |
- |
-} // namespace |
ViewManagerServiceImpl::ViewManagerServiceImpl( |
RootNodeManager* root_node_manager, |
@@ -241,15 +226,24 @@ bool ViewManagerServiceImpl::CanRemoveNodeFromParent(const Node* node) const { |
if (!parent) |
return false; |
- // Always allow the remove if there are no roots. Otherwise the remove is |
- // allowed if the parent is a descendant of the roots, or the node and its |
- // parent were created by this connection. We explicitly disallow removal of |
- // the node from its parent if the parent isn't visible to this connection |
- // (not in roots). |
- return (roots_.empty() || |
- (IsNodeDescendantOfRoots(parent) || |
- (node->id().connection_id == id_ && |
- parent->id().connection_id == id_))); |
+ if (roots_.empty()) |
+ return true; // Root can do anything. |
+ |
+ if (node->id().connection_id != id_) |
+ return false; // Can only unparent nodes we created. |
+ |
+ if (roots_.count(NodeIdToTransportId(parent->id())) > 0) |
+ return true; // We can always remove from one of our roots. |
+ |
+ // Don't allow removing from nodes from other connections that aren't in our |
+ // root list. |
+ if (parent->id().connection_id != id_) |
+ return false; |
+ |
+ // Allow the remove as long as we haven't embedded another node at |parent|. |
+ ViewManagerServiceImpl* connection = |
+ root_node_manager_->GetConnectionWithRoot(parent->id()); |
+ return !connection || connection == this; |
} |
bool ViewManagerServiceImpl::CanAddNode(const Node* parent, |
@@ -263,12 +257,15 @@ bool ViewManagerServiceImpl::CanAddNode(const Node* parent, |
if (roots_.empty()) |
return true; // No restriction if there are no roots. |
+ if (child->id().connection_id != id_) |
+ return false; // Can't move children from different connections. |
+ |
if (!IsNodeDescendantOfRoots(parent) && parent->id().connection_id != id_) |
return false; // |parent| is not visible to this connection. |
- // Allow the add if the child is already a descendant of the roots or was |
- // created by this connection. |
- return (IsNodeDescendantOfRoots(child) || child->id().connection_id == id_); |
+ // Only allow the add if we haven't given one of the ancestors of |parent| to |
+ // another node. That is, Embed() hasn't been invoked with one of our nodes. |
+ return !IsNodeEmbeddedInAnotherConnection(parent); |
} |
bool ViewManagerServiceImpl::CanReorderNode(const Node* node, |
@@ -324,8 +321,16 @@ bool ViewManagerServiceImpl::CanSetFocus(const Node* node) const { |
} |
bool ViewManagerServiceImpl::CanGetNodeTree(const Node* node) const { |
- return node && |
- (IsNodeDescendantOfRoots(node) || node->id().connection_id == id_); |
+ if (!node) |
+ return false; |
+ |
+ if (roots_.empty()) |
+ return true; |
+ |
+ if (node->id().connection_id == id_) |
+ return true; |
+ |
+ return roots_.count(NodeIdToTransportId(node->id())) > 0; |
} |
bool ViewManagerServiceImpl::CanEmbed(Id transport_node_id) const { |
@@ -341,6 +346,16 @@ bool ViewManagerServiceImpl::CanSetNodeVisibility(const Node* node, |
node->IsVisible() != visibile; |
} |
+bool ViewManagerServiceImpl::CanDescendIntoNodeForNodeTree( |
+ const Node* node) const { |
+ if (roots_.empty()) |
+ return true; |
+ |
+ ViewManagerServiceImpl* connection = |
+ root_node_manager_->GetConnectionWithRoot(node->id()); |
+ return !connection || connection == this; |
+} |
+ |
bool ViewManagerServiceImpl::DeleteNodeImpl(ViewManagerServiceImpl* source, |
const NodeId& node_id) { |
DCHECK_EQ(node_id.connection_id, id_); |
@@ -404,6 +419,18 @@ void ViewManagerServiceImpl::RemoveFromKnown(const Node* node, |
RemoveFromKnown(children[i], local_nodes); |
} |
+bool ViewManagerServiceImpl::IsNodeEmbeddedInAnotherConnection( |
+ const Node* node) const { |
+ while (node) { |
+ const ViewManagerServiceImpl* connection = |
+ root_node_manager_->GetConnectionWithRoot(node->id()); |
+ if (connection) |
+ return connection != this; |
+ node = node->GetParent(); |
+ } |
+ return false; |
+} |
+ |
void ViewManagerServiceImpl::AddRoot(const NodeId& node_id) { |
const Id transport_node_id(NodeIdToTransportId(node_id)); |
CHECK(roots_.count(transport_node_id) == 0); |
@@ -448,13 +475,26 @@ void ViewManagerServiceImpl::RemoveRoot(const NodeId& node_id) { |
local_nodes[i]->GetParent()->Remove(local_nodes[i]); |
} |
+void ViewManagerServiceImpl::RemoveChildrenAsPartOfEmbed( |
+ const NodeId& node_id) { |
+ // Let the root do what it wants. |
+ if (roots_.empty()) |
+ return; |
+ |
+ Node* node = GetNode(node_id); |
+ CHECK(node); |
+ CHECK(node->id().connection_id == node_id.connection_id); |
+ std::vector<Node*> children = node->GetChildren(); |
+ for (size_t i = 0; i < children.size(); ++i) |
+ node->Remove(children[i]); |
+} |
+ |
bool ViewManagerServiceImpl::IsNodeDescendantOfRoots(const Node* node) const { |
if (roots_.empty()) |
return true; |
if (!node) |
return false; |
- const Id invalid_node_id = |
- NodeIdToTransportId(InvalidNodeId()); |
+ const Id invalid_node_id = NodeIdToTransportId(InvalidNodeId()); |
for (NodeIdSet::const_iterator i = roots_.begin(); i != roots_.end(); ++i) { |
if (*i == invalid_node_id) |
continue; |
@@ -533,6 +573,24 @@ Array<NodeDataPtr> ViewManagerServiceImpl::NodesToNodeDatas( |
return array.Pass(); |
} |
+void ViewManagerServiceImpl::GetNodeTreeImpl( |
+ const Node* node, |
+ std::vector<const Node*>* nodes) const { |
+ DCHECK(node); |
+ |
+ if (!CanGetNodeTree(node)) |
+ return; |
+ |
+ nodes->push_back(node); |
+ |
+ if (!CanDescendIntoNodeForNodeTree(node)) |
+ return; |
+ |
+ std::vector<const Node*> children(node->GetChildren()); |
+ for (size_t i = 0 ; i < children.size(); ++i) |
+ GetNodeTreeImpl(children[i], nodes); |
+} |
+ |
void ViewManagerServiceImpl::CreateNode( |
Id transport_node_id, |
const Callback<void(ErrorCode)>& callback) { |
@@ -612,9 +670,11 @@ void ViewManagerServiceImpl::GetNodeTree( |
Node* node = GetNode(NodeIdFromTransportId(node_id)); |
std::vector<const Node*> nodes; |
if (CanGetNodeTree(node)) { |
- GetDescendants(node, &nodes); |
+ GetNodeTreeImpl(node, &nodes); |
+#if !defined(NDEBUG) |
for (size_t i = 0; i < nodes.size(); ++i) |
- known_nodes_.insert(NodeIdToTransportId(nodes[i]->id())); |
+ DCHECK_GT(known_nodes_.count(NodeIdToTransportId(nodes[i]->id())), 0u); |
+#endif |
} |
callback.Run(NodesToNodeDatas(nodes)); |
} |
@@ -743,6 +803,7 @@ void ViewManagerServiceImpl::Embed(const String& url, |
(!connection_by_url && !connection_with_node_as_root)) && |
(!connection_by_url || !connection_by_url->HasRoot(node_id))) { |
RootNodeManager::ScopedChange change(this, root_node_manager_, true); |
+ RemoveChildrenAsPartOfEmbed(node_id); |
// Never message the originating connection. |
root_node_manager_->OnConnectionMessagedClient(id_); |
if (connection_with_node_as_root) |