| // 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 "services/ui/view_manager/view_registry.h" |
| |
| #include <algorithm> |
| #include <cmath> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/strings/stringprintf.h" |
| #include "mojo/services/ui/views/cpp/formatting.h" |
| #include "services/ui/view_manager/view_impl.h" |
| #include "services/ui/view_manager/view_tree_impl.h" |
| |
| namespace view_manager { |
| namespace { |
| constexpr uint32_t kSceneResourceId = 1u; |
| constexpr uint32_t kRootNodeId = mojo::gfx::composition::kSceneRootNodeId; |
| |
| bool Validate(const mojo::ui::DisplayMetrics& value) { |
| return std::isnormal(value.device_pixel_ratio) && |
| value.device_pixel_ratio > 0.f; |
| } |
| |
| bool Validate(const mojo::ui::ViewLayout& value) { |
| return value.size && value.size->width >= 0 && value.size->height >= 0; |
| } |
| |
| bool Validate(const mojo::ui::ViewProperties& value) { |
| if (value.display_metrics && !Validate(*value.display_metrics)) |
| return false; |
| if (value.view_layout && !Validate(*value.view_layout)) |
| return false; |
| return true; |
| } |
| |
| // Returns true if the properties are valid and are sufficient for |
| // operating the view tree. |
| bool IsComplete(const mojo::ui::ViewProperties& value) { |
| return Validate(value) && value.view_layout && value.display_metrics; |
| } |
| |
| void ApplyOverrides(mojo::ui::ViewProperties* value, |
| const mojo::ui::ViewProperties* overrides) { |
| if (!overrides) |
| return; |
| if (overrides->display_metrics) |
| value->display_metrics = overrides->display_metrics.Clone(); |
| if (overrides->view_layout) |
| value->view_layout = overrides->view_layout.Clone(); |
| } |
| } // namespace |
| |
| ViewRegistry::ViewRegistry(mojo::gfx::composition::CompositorPtr compositor) |
| : compositor_(compositor.Pass()) {} |
| |
| ViewRegistry::~ViewRegistry() {} |
| |
| // REGISTERING ASSOCIATES |
| |
| void ViewRegistry::RegisterViewAssociate( |
| mojo::ui::ViewInspector* view_inspector, |
| mojo::ui::ViewAssociatePtr view_associate, |
| mojo::InterfaceRequest<mojo::ui::ViewAssociateOwner> view_associate_owner, |
| const mojo::String& label) { |
| associate_table_.RegisterViewAssociate(view_inspector, view_associate.Pass(), |
| view_associate_owner.Pass(), label); |
| } |
| |
| void ViewRegistry::FinishedRegisteringViewAssociates() { |
| associate_table_.FinishedRegisteringViewAssociates(); |
| }; |
| |
| // CREATE / DESTROY VIEWS |
| |
| void ViewRegistry::CreateView( |
| mojo::InterfaceRequest<mojo::ui::View> view_request, |
| mojo::InterfaceRequest<mojo::ui::ViewOwner> view_owner_request, |
| mojo::ui::ViewListenerPtr view_listener, |
| const mojo::String& label) { |
| DCHECK(view_request.is_pending()); |
| DCHECK(view_owner_request.is_pending()); |
| DCHECK(view_listener); |
| |
| auto view_token = mojo::ui::ViewToken::New(); |
| view_token->value = next_view_token_value_++; |
| CHECK(view_token->value); |
| CHECK(!FindView(view_token->value)); |
| |
| // Create the state and bind the interfaces to it. |
| std::string sanitized_label = |
| label.get().substr(0, mojo::ui::kLabelMaxLength); |
| ViewState* view_state = |
| new ViewState(this, view_token.Pass(), view_request.Pass(), |
| view_listener.Pass(), sanitized_label); |
| view_state->BindOwner(view_owner_request.Pass()); |
| |
| // Add to registry and return token. |
| views_by_token_.emplace(view_state->view_token()->value, view_state); |
| DVLOG(1) << "CreateView: view=" << view_state; |
| } |
| |
| void ViewRegistry::OnViewDied(ViewState* view_state, |
| const std::string& reason) { |
| DCHECK(IsViewStateRegisteredDebug(view_state)); |
| DVLOG(1) << "OnViewDied: view=" << view_state << ", reason=" << reason; |
| |
| UnregisterView(view_state); |
| } |
| |
| void ViewRegistry::UnregisterView(ViewState* view_state) { |
| DCHECK(IsViewStateRegisteredDebug(view_state)); |
| DVLOG(1) << "UnregisterView: view=" << view_state; |
| |
| HijackView(view_state); |
| UnregisterChildren(view_state); |
| |
| // Remove from registry. |
| if (view_state->scene_token()) |
| views_by_scene_token_.erase(view_state->scene_token()->value); |
| views_by_token_.erase(view_state->view_token()->value); |
| delete view_state; |
| } |
| |
| // CREATE / DESTROY VIEW TREES |
| |
| void ViewRegistry::CreateViewTree( |
| mojo::InterfaceRequest<mojo::ui::ViewTree> view_tree_request, |
| mojo::ui::ViewTreeListenerPtr view_tree_listener, |
| const mojo::String& label) { |
| DCHECK(view_tree_request.is_pending()); |
| DCHECK(view_tree_listener); |
| |
| auto view_tree_token = mojo::ui::ViewTreeToken::New(); |
| view_tree_token->value = next_view_tree_token_value_++; |
| CHECK(view_tree_token->value); |
| CHECK(!FindViewTree(view_tree_token->value)); |
| |
| // Create the state and bind the interfaces to it. |
| std::string sanitized_label = |
| label.get().substr(0, mojo::ui::kLabelMaxLength); |
| ViewTreeState* tree_state = |
| new ViewTreeState(this, view_tree_token.Pass(), view_tree_request.Pass(), |
| view_tree_listener.Pass(), sanitized_label); |
| |
| // Add to registry. |
| view_trees_by_token_.emplace(tree_state->view_tree_token()->value, |
| tree_state); |
| DVLOG(1) << "CreateViewTree: tree=" << tree_state; |
| } |
| |
| void ViewRegistry::OnViewTreeDied(ViewTreeState* tree_state, |
| const std::string& reason) { |
| DCHECK(IsViewTreeStateRegisteredDebug(tree_state)); |
| DVLOG(1) << "OnViewTreeDied: tree=" << tree_state << ", reason=" << reason; |
| |
| UnregisterViewTree(tree_state); |
| } |
| |
| void ViewRegistry::UnregisterViewTree(ViewTreeState* tree_state) { |
| DCHECK(IsViewTreeStateRegisteredDebug(tree_state)); |
| DVLOG(1) << "UnregisterViewTree: tree=" << tree_state; |
| |
| UnregisterChildren(tree_state); |
| |
| // Remove from registry. |
| view_trees_by_token_.erase(tree_state->view_tree_token()->value); |
| delete tree_state; |
| } |
| |
| // LIFETIME |
| |
| void ViewRegistry::UnregisterViewContainer( |
| ViewContainerState* container_state) { |
| DCHECK(IsViewContainerStateRegisteredDebug(container_state)); |
| |
| ViewState* view_state = container_state->AsViewState(); |
| if (view_state) |
| UnregisterView(view_state); |
| else |
| UnregisterViewTree(container_state->AsViewTreeState()); |
| } |
| |
| void ViewRegistry::UnregisterViewStub(std::unique_ptr<ViewStub> view_stub) { |
| DCHECK(view_stub); |
| |
| ViewState* view_state = view_stub->ReleaseView(); |
| if (view_state) |
| UnregisterView(view_state); |
| } |
| |
| void ViewRegistry::UnregisterChildren(ViewContainerState* container_state) { |
| DCHECK(IsViewContainerStateRegisteredDebug(container_state)); |
| |
| // Recursively unregister all children since they will become unowned |
| // at this point taking care to unlink each one before its unregistration. |
| for (auto& child : container_state->UnlinkAllChildren()) |
| UnregisterViewStub(std::move(child)); |
| } |
| |
| // SCENE MANAGEMENT |
| |
| void ViewRegistry::CreateScene( |
| ViewState* view_state, |
| mojo::InterfaceRequest<mojo::gfx::composition::Scene> scene) { |
| DCHECK(IsViewStateRegisteredDebug(view_state)); |
| DCHECK(scene.is_pending()); |
| DVLOG(1) << "CreateScene: view=" << view_state; |
| |
| compositor_->CreateScene( |
| scene.Pass(), view_state->label(), |
| base::Bind(&ViewRegistry::OnViewSceneTokenAvailable, |
| base::Unretained(this), view_state->GetWeakPtr())); |
| } |
| |
| void ViewRegistry::OnViewSceneTokenAvailable( |
| base::WeakPtr<ViewState> view_state_weak, |
| mojo::gfx::composition::SceneTokenPtr scene_token) { |
| DCHECK(scene_token); |
| ViewState* view_state = view_state_weak.get(); |
| if (!view_state) |
| return; |
| |
| DCHECK(IsViewStateRegisteredDebug(view_state)); |
| DVLOG(2) << "OnSceneCreated: view=" << view_state |
| << ", scene_token=" << scene_token; |
| |
| if (view_state->scene_token()) |
| views_by_scene_token_.erase(view_state->scene_token()->value); |
| views_by_scene_token_.emplace(scene_token->value, view_state); |
| |
| view_state->set_scene_token(scene_token.Pass()); |
| |
| PublishStubScene(view_state); |
| } |
| |
| void ViewRegistry::OnStubSceneTokenAvailable( |
| base::WeakPtr<ViewStub> view_stub_weak, |
| mojo::gfx::composition::SceneTokenPtr scene_token) { |
| DCHECK(scene_token); |
| |
| ViewStub* view_stub = view_stub_weak.get(); |
| if (!view_stub || view_stub->is_unavailable()) |
| return; |
| |
| DVLOG(2) << "OnStubSceneCreated: view_state=" << view_stub->state() |
| << ", scene_token=" << scene_token; |
| |
| // Store the scene token. |
| DCHECK(view_stub->is_linked()); |
| view_stub->SetStubSceneToken(scene_token.Clone()); |
| if (view_stub->state()) |
| PublishStubScene(view_stub->state()); |
| |
| // Send view info to the container including the scene token. |
| auto view_info = mojo::ui::ViewInfo::New(); |
| view_info->scene_token = scene_token.Pass(); |
| if (view_stub->container()) { |
| SendChildAttached(view_stub->container(), view_stub->key(), |
| view_info.Pass()); |
| } |
| |
| // If this is the root of the tree, update the renderer now that we |
| // know the scene token. |
| if (view_stub->is_root_of_tree()) |
| SetRendererRootScene(view_stub->tree()); |
| } |
| |
| void ViewRegistry::PublishStubScene(ViewState* view_state) { |
| DCHECK(IsViewStateRegisteredDebug(view_state)); |
| |
| if (!view_state->view_stub()) |
| return; |
| |
| DCHECK(view_state->view_stub()->stub_scene()); // we know view is attached |
| DVLOG(2) << "PublishStubScene: view=" << view_state |
| << ", view_stub=" << view_state->view_stub() << ", stub_scene_token=" |
| << view_state->view_stub()->stub_scene_token(); |
| |
| auto update = mojo::gfx::composition::SceneUpdate::New(); |
| update->clear_resources = true; |
| update->clear_nodes = true; |
| |
| if (view_state->scene_token() && view_state->issued_properties()) { |
| auto scene_resource = mojo::gfx::composition::Resource::New(); |
| scene_resource->set_scene(mojo::gfx::composition::SceneResource::New()); |
| scene_resource->get_scene()->scene_token = |
| view_state->scene_token()->Clone(); |
| update->resources.insert(kSceneResourceId, scene_resource.Pass()); |
| |
| const mojo::ui::ViewLayoutPtr& layout = |
| view_state->issued_properties()->view_layout; |
| DCHECK(layout && layout->size); |
| |
| auto root_node = mojo::gfx::composition::Node::New(); |
| root_node->content_clip = mojo::RectF::New(); |
| root_node->content_clip->width = layout->size->width; |
| root_node->content_clip->height = layout->size->height; |
| root_node->op = mojo::gfx::composition::NodeOp::New(); |
| root_node->op->set_scene(mojo::gfx::composition::SceneNodeOp::New()); |
| root_node->op->get_scene()->scene_resource_id = kSceneResourceId; |
| root_node->op->get_scene()->scene_version = |
| view_state->issued_scene_version(); |
| update->nodes.insert(kRootNodeId, root_node.Pass()); |
| } |
| view_state->view_stub()->stub_scene()->Update(update.Pass()); |
| |
| auto metadata = mojo::gfx::composition::SceneMetadata::New(); |
| metadata->version = view_state->view_stub()->scene_version(); |
| view_state->view_stub()->stub_scene()->Publish(metadata.Pass()); |
| |
| if (view_state->view_stub()->is_root_of_tree()) |
| SetRendererRootScene(view_state->view_stub()->tree()); |
| } |
| |
| // RENDERING |
| |
| void ViewRegistry::SetRenderer(ViewTreeState* tree_state, |
| mojo::gfx::composition::RendererPtr renderer) { |
| DCHECK(IsViewTreeStateRegisteredDebug(tree_state)); |
| DVLOG(1) << "SetRenderer: tree=" << tree_state; |
| |
| if (renderer) { |
| renderer.set_connection_error_handler( |
| base::Bind(&ViewRegistry::OnRendererDied, base::Unretained(this), |
| base::Unretained(tree_state))); |
| } |
| |
| tree_state->SetRenderer(renderer.Pass()); |
| SetRendererRootScene(tree_state); |
| } |
| |
| void ViewRegistry::OnRendererDied(ViewTreeState* tree_state) { |
| DCHECK(IsViewTreeStateRegisteredDebug(tree_state)); |
| DVLOG(1) << "OnRendererDied: tree=" << tree_state; |
| |
| tree_state->SetRenderer(nullptr); |
| SendRendererDied(tree_state); |
| } |
| |
| void ViewRegistry::SetRendererRootScene(ViewTreeState* tree_state) { |
| DCHECK(IsViewTreeStateRegisteredDebug(tree_state)); |
| |
| if (!tree_state->renderer()) |
| return; |
| |
| // TODO(jeffbrown): Avoid sending the same information if already set. |
| |
| ViewStub* root_stub = tree_state->GetRoot(); |
| if (root_stub && root_stub->stub_scene_token() && root_stub->properties() && |
| IsComplete(*root_stub->properties())) { |
| const mojo::ui::ViewLayoutPtr& layout = |
| root_stub->properties()->view_layout; |
| DCHECK(layout && layout->size); |
| |
| auto viewport = mojo::Rect::New(); |
| viewport->width = layout->size->width; |
| viewport->height = layout->size->height; |
| DVLOG(2) << "SetRootScene: tree=" << tree_state |
| << ", scene_token=" << root_stub->stub_scene_token() |
| << ", scene_version=" << root_stub->scene_version() |
| << ", viewport=" << viewport; |
| tree_state->renderer()->SetRootScene(root_stub->stub_scene_token()->Clone(), |
| root_stub->scene_version(), |
| viewport.Pass()); |
| return; |
| } |
| |
| DVLOG(2) << "ClearRootScene: tree=" << tree_state; |
| tree_state->renderer()->ClearRootScene(); |
| } |
| |
| // TREE MANIPULATION |
| |
| void ViewRegistry::AddChild( |
| ViewContainerState* container_state, |
| uint32_t child_key, |
| mojo::InterfaceHandle<mojo::ui::ViewOwner> child_view_owner) { |
| DCHECK(IsViewContainerStateRegisteredDebug(container_state)); |
| DCHECK(child_view_owner); |
| DVLOG(1) << "AddChild: container=" << container_state |
| << ", child_key=" << child_key; |
| |
| // Ensure there are no other children with the same key. |
| if (container_state->children().find(child_key) != |
| container_state->children().end()) { |
| LOG(ERROR) << "Attempted to add a child with a duplicate key: " |
| << "container=" << container_state |
| << ", child_key=" << child_key; |
| UnregisterViewContainer(container_state); |
| return; |
| } |
| |
| // If this is a view tree, ensure it only has one root. |
| ViewTreeState* view_tree_state = container_state->AsViewTreeState(); |
| if (view_tree_state && !container_state->children().empty()) { |
| LOG(ERROR) << "Attempted to add a second child to a view tree: " |
| << "container=" << container_state |
| << ", child_key=" << child_key; |
| UnregisterViewContainer(container_state); |
| return; |
| } |
| |
| // Add a stub, pending resolution of the view owner. |
| // Assuming the stub isn't removed prematurely, |OnViewResolved| will be |
| // called asynchronously with the result of the resolution. |
| container_state->LinkChild(child_key, std::unique_ptr<ViewStub>(new ViewStub( |
| this, child_view_owner.Pass()))); |
| } |
| |
| void ViewRegistry::RemoveChild(ViewContainerState* container_state, |
| uint32_t child_key, |
| mojo::InterfaceRequest<mojo::ui::ViewOwner> |
| transferred_view_owner_request) { |
| DCHECK(IsViewContainerStateRegisteredDebug(container_state)); |
| DVLOG(1) << "RemoveChild: container=" << container_state |
| << ", child_key=" << child_key; |
| |
| // Ensure the child key exists in the container. |
| auto child_it = container_state->children().find(child_key); |
| if (child_it == container_state->children().end()) { |
| LOG(ERROR) << "Attempted to remove a child with an invalid key: " |
| << "container=" << container_state |
| << ", child_key=" << child_key; |
| UnregisterViewContainer(container_state); |
| return; |
| } |
| |
| // Unlink the child from its container. |
| TransferOrUnregisterViewStub(container_state->UnlinkChild(child_key), |
| transferred_view_owner_request.Pass()); |
| |
| // If the root was removed, tell the renderer. |
| ViewTreeState* tree_state = container_state->AsViewTreeState(); |
| if (tree_state) |
| SetRendererRootScene(tree_state); |
| } |
| |
| void ViewRegistry::SetChildProperties( |
| ViewContainerState* container_state, |
| uint32_t child_key, |
| uint32_t child_scene_version, |
| mojo::ui::ViewPropertiesPtr child_properties) { |
| DCHECK(IsViewContainerStateRegisteredDebug(container_state)); |
| DVLOG(1) << "SetChildProperties: container=" << container_state |
| << ", child_key=" << child_key |
| << ", child_scene_version=" << child_scene_version |
| << ", child_properties=" << child_properties; |
| |
| // Check whether the properties are well-formed. |
| if (child_properties && !Validate(*child_properties)) { |
| LOG(ERROR) << "Attempted to set invalid child view properties: " |
| << "container=" << container_state << ", child_key=" << child_key |
| << ", child_scene_version=" << child_scene_version |
| << ", child_properties=" << child_properties; |
| UnregisterViewContainer(container_state); |
| return; |
| } |
| |
| // Check whether the child key exists in the container. |
| auto child_it = container_state->children().find(child_key); |
| if (child_it == container_state->children().end()) { |
| LOG(ERROR) << "Attempted to modify child with an invalid key: " |
| << "container=" << container_state << ", child_key=" << child_key |
| << ", child_scene_version=" << child_scene_version |
| << ", child_properties=" << child_properties; |
| UnregisterViewContainer(container_state); |
| return; |
| } |
| |
| // Immediately discard requests on unavailable views. |
| ViewStub* child_stub = child_it->second.get(); |
| if (child_stub->is_unavailable()) |
| return; |
| |
| // Store the updated properties specified by the container if changed. |
| if (child_scene_version == child_stub->scene_version() && |
| child_properties.Equals(child_stub->properties())) |
| return; |
| |
| // Apply the change. |
| child_stub->SetProperties(child_scene_version, child_properties.Pass()); |
| if (child_stub->state()) |
| UpdateViewProperties(child_stub->state()); |
| } |
| |
| void ViewRegistry::OnViewResolved(ViewStub* view_stub, |
| mojo::ui::ViewTokenPtr view_token) { |
| DCHECK(view_stub); |
| |
| ViewState* view_state = view_token ? FindView(view_token->value) : nullptr; |
| if (view_state) |
| AttachResolvedViewAndNotify(view_stub, view_state); |
| else |
| ReleaseUnavailableViewAndNotify(view_stub); |
| } |
| |
| void ViewRegistry::AttachResolvedViewAndNotify(ViewStub* view_stub, |
| ViewState* view_state) { |
| DCHECK(view_stub); |
| DCHECK(IsViewStateRegisteredDebug(view_state)); |
| DVLOG(2) << "AttachViewStubAndNotify: view=" << view_state; |
| |
| // Create the scene and get its token asynchronously. |
| // TODO(jeffbrown): It would be really nice to have a way to pipeline |
| // getting the scene token. |
| mojo::gfx::composition::ScenePtr stub_scene; |
| compositor_->CreateScene( |
| mojo::GetProxy(&stub_scene), |
| base::StringPrintf("*%s", view_state->label().c_str()), |
| base::Bind(&ViewRegistry::OnStubSceneTokenAvailable, |
| base::Unretained(this), view_stub->GetWeakPtr())); |
| |
| // Hijack the view from its current container, if needed. |
| HijackView(view_state); |
| |
| // Attach the view. |
| view_state->ReleaseOwner(); // don't need the ViewOwner pipe anymore |
| view_stub->AttachView(view_state, stub_scene.Pass()); |
| |
| // Update view properties which may inherit context from the container. |
| UpdateViewProperties(view_state); |
| } |
| |
| void ViewRegistry::ReleaseUnavailableViewAndNotify(ViewStub* view_stub) { |
| DCHECK(view_stub); |
| DVLOG(2) << "ReleaseUnavailableViewAndNotify: key=" << view_stub->key(); |
| |
| ViewState* view_state = view_stub->ReleaseView(); |
| DCHECK(!view_state); |
| |
| if (view_stub->container()) |
| SendChildUnavailable(view_stub->container(), view_stub->key()); |
| } |
| |
| void ViewRegistry::HijackView(ViewState* view_state) { |
| DCHECK(IsViewStateRegisteredDebug(view_state)); |
| |
| ViewStub* view_stub = view_state->view_stub(); |
| if (view_stub) |
| ReleaseUnavailableViewAndNotify(view_stub); |
| } |
| |
| void ViewRegistry::TransferOrUnregisterViewStub( |
| std::unique_ptr<ViewStub> view_stub, |
| mojo::InterfaceRequest<mojo::ui::ViewOwner> |
| transferred_view_owner_request) { |
| DCHECK(view_stub); |
| |
| if (transferred_view_owner_request.is_pending()) { |
| if (view_stub->state()) { |
| InvalidateViewProperties(view_stub->state()); |
| view_stub->ReleaseView()->BindOwner( |
| transferred_view_owner_request.Pass()); |
| return; |
| } |
| if (view_stub->is_pending()) { |
| // TODO(jeffbrown): Handle transfer of pending view |
| CHECK(false); |
| return; |
| } |
| } |
| UnregisterViewStub(std::move(view_stub)); |
| } |
| |
| // VIEW PROPERTIES |
| |
| void ViewRegistry::UpdateViewProperties(ViewState* view_state) { |
| DCHECK(IsViewStateRegisteredDebug(view_state)); |
| DVLOG(2) << "UpdateViewProperties: view=" << view_state; |
| |
| // TODO(jeffbrown): If view property changes are frequent, it might be a |
| // good idea to coalesce processing of them into a scheduled invalidation |
| // cycle. |
| |
| mojo::ui::ViewPropertiesPtr properties = ResolveViewProperties(view_state); |
| if (!properties) { |
| InvalidateViewProperties(view_state); |
| return; |
| } |
| |
| DCHECK(IsComplete(*properties)); |
| view_state->set_issued_properties_valid(true); |
| if (view_state->issued_properties() && |
| properties->Equals(*view_state->issued_properties())) |
| return; |
| |
| view_state->IssueProperties(properties.Pass()); |
| SendPropertiesChanged(view_state, view_state->issued_scene_version(), |
| view_state->issued_properties()->Clone()); |
| |
| PublishStubScene(view_state); |
| |
| for (const auto& pair : view_state->children()) { |
| if (pair.second->state()) |
| UpdateViewProperties(pair.second->state()); |
| } |
| } |
| |
| void ViewRegistry::InvalidateViewProperties(ViewState* view_state) { |
| DCHECK(IsViewStateRegisteredDebug(view_state)); |
| DVLOG(2) << "InvalidateViewProperties: view=" << view_state; |
| |
| if (!view_state->issued_properties_valid()) |
| return; |
| |
| view_state->set_issued_properties_valid(false); |
| |
| for (const auto& pair : view_state->children()) { |
| if (pair.second->state()) |
| InvalidateViewProperties(pair.second->state()); |
| } |
| } |
| |
| mojo::ui::ViewPropertiesPtr ViewRegistry::ResolveViewProperties( |
| ViewState* view_state) { |
| DCHECK(IsViewStateRegisteredDebug(view_state)); |
| |
| ViewStub* view_stub = view_state->view_stub(); |
| if (!view_stub || !view_stub->properties()) |
| return nullptr; |
| |
| if (view_stub->parent()) { |
| if (!view_stub->parent()->issued_properties_valid()) |
| return nullptr; |
| DCHECK(view_stub->parent()->issued_properties()); |
| mojo::ui::ViewPropertiesPtr properties = |
| view_stub->parent()->issued_properties().Clone(); |
| ApplyOverrides(properties.get(), view_stub->properties().get()); |
| return properties.Pass(); |
| } else if (view_stub->is_root_of_tree()) { |
| if (!view_stub->properties() || !IsComplete(*view_stub->properties())) { |
| DVLOG(2) << "View tree properties are incomplete: root=" << view_state |
| << ", properties=" << view_stub->properties(); |
| return nullptr; |
| } |
| return view_stub->properties().Clone(); |
| } else { |
| return nullptr; |
| } |
| } |
| |
| // VIEW AND VIEW TREE SERVICE PROVIDERS |
| |
| void ViewRegistry::ConnectToViewService( |
| ViewState* view_state, |
| const mojo::String& service_name, |
| mojo::ScopedMessagePipeHandle client_handle) { |
| DCHECK(IsViewStateRegisteredDebug(view_state)); |
| |
| associate_table_.ConnectToViewService(view_state->view_token()->Clone(), |
| service_name, client_handle.Pass()); |
| } |
| |
| void ViewRegistry::ConnectToViewTreeService( |
| ViewTreeState* tree_state, |
| const mojo::String& service_name, |
| mojo::ScopedMessagePipeHandle client_handle) { |
| DCHECK(IsViewTreeStateRegisteredDebug(tree_state)); |
| |
| associate_table_.ConnectToViewTreeService( |
| tree_state->view_tree_token()->Clone(), service_name, |
| client_handle.Pass()); |
| } |
| |
| // VIEW INSPECTOR |
| |
| void ViewRegistry::GetHitTester( |
| mojo::ui::ViewTreeTokenPtr view_tree_token, |
| mojo::InterfaceRequest<mojo::gfx::composition::HitTester> |
| hit_tester_request, |
| const GetHitTesterCallback& callback) { |
| DCHECK(view_tree_token); |
| DCHECK(hit_tester_request.is_pending()); |
| DVLOG(1) << "GetHitTester: tree=" << view_tree_token; |
| |
| ViewTreeState* view_tree = FindViewTree(view_tree_token->value); |
| if (!view_tree) { |
| callback.Run(false); |
| return; |
| } |
| |
| view_tree->RequestHitTester(hit_tester_request.Pass(), callback); |
| } |
| |
| void ViewRegistry::ResolveScenes( |
| mojo::Array<mojo::gfx::composition::SceneTokenPtr> scene_tokens, |
| const ResolveScenesCallback& callback) { |
| DCHECK(scene_tokens); |
| |
| mojo::Array<mojo::ui::ViewTokenPtr> result; |
| result.resize(scene_tokens.size()); |
| |
| size_t index = 0; |
| for (const auto& scene_token : scene_tokens.storage()) { |
| DCHECK(scene_token); |
| auto it = views_by_scene_token_.find(scene_token->value); |
| if (it != views_by_scene_token_.end()) |
| result[index] = it->second->view_token()->Clone(); |
| index++; |
| } |
| |
| DVLOG(1) << "ResolveScenes: scene_tokens=" << scene_tokens |
| << ", result=" << result; |
| callback.Run(result.Pass()); |
| } |
| |
| // EXTERNAL SIGNALING |
| |
| void ViewRegistry::SendPropertiesChanged( |
| ViewState* view_state, |
| uint32_t scene_version, |
| mojo::ui::ViewPropertiesPtr properties) { |
| DCHECK(view_state); |
| DCHECK(properties); |
| DCHECK(view_state->view_listener()); |
| |
| // TODO: Detect ANRs |
| DVLOG(1) << "SendPropertiesChanged: view_state=" << view_state |
| << ", scene_version=" << scene_version |
| << ", properties=" << properties; |
| view_state->view_listener()->OnPropertiesChanged( |
| view_state->issued_scene_version(), |
| view_state->issued_properties()->Clone(), base::Bind(&base::DoNothing)); |
| } |
| |
| void ViewRegistry::SendRendererDied(ViewTreeState* tree_state) { |
| DCHECK(IsViewTreeStateRegisteredDebug(tree_state)); |
| DCHECK(tree_state->view_tree_listener()); |
| |
| // TODO: Detect ANRs |
| DVLOG(1) << "SendRendererDied: tree_state=" << tree_state; |
| tree_state->view_tree_listener()->OnRendererDied( |
| base::Bind(&base::DoNothing)); |
| } |
| |
| void ViewRegistry::SendChildAttached(ViewContainerState* container_state, |
| uint32_t child_key, |
| mojo::ui::ViewInfoPtr child_view_info) { |
| DCHECK(container_state); |
| DCHECK(child_view_info); |
| |
| if (!container_state->view_container_listener()) |
| return; |
| |
| // TODO: Detect ANRs |
| DVLOG(1) << "SendChildAttached: container_state=" << container_state |
| << ", child_key=" << child_key |
| << ", child_view_info=" << child_view_info; |
| container_state->view_container_listener()->OnChildAttached( |
| child_key, child_view_info.Pass(), base::Bind(&base::DoNothing)); |
| } |
| |
| void ViewRegistry::SendChildUnavailable(ViewContainerState* container_state, |
| uint32_t child_key) { |
| DCHECK(container_state); |
| |
| if (!container_state->view_container_listener()) |
| return; |
| |
| // TODO: Detect ANRs |
| DVLOG(1) << "SendChildUnavailable: container=" << container_state |
| << ", child_key=" << child_key; |
| container_state->view_container_listener()->OnChildUnavailable( |
| child_key, base::Bind(&base::DoNothing)); |
| } |
| |
| // LOOKUP |
| |
| ViewState* ViewRegistry::FindView(uint32_t view_token_value) { |
| auto it = views_by_token_.find(view_token_value); |
| return it != views_by_token_.end() ? it->second : nullptr; |
| } |
| |
| ViewTreeState* ViewRegistry::FindViewTree(uint32_t view_tree_token_value) { |
| auto it = view_trees_by_token_.find(view_tree_token_value); |
| return it != view_trees_by_token_.end() ? it->second : nullptr; |
| } |
| |
| } // namespace view_manager |