1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "services/content/public/cpp/navigable_contents_view.h"
6 
7 #include <map>
8 
9 #include "base/bind_helpers.h"
10 #include "base/callback.h"
11 #include "base/no_destructor.h"
12 #include "base/synchronization/atomic_flag.h"
13 #include "base/unguessable_token.h"
14 #include "services/content/public/cpp/navigable_contents.h"
15 #include "ui/accessibility/ax_enums.mojom.h"
16 #include "ui/accessibility/ax_node_data.h"
17 
18 #if defined(TOOLKIT_VIEWS)
19 #include "ui/views/focus/focus_manager.h"  // nogncheck
20 #include "ui/views/layout/fill_layout.h"  // nogncheck
21 #include "ui/views/view.h"                // nogncheck
22 #endif  // defined(TOOLKIT_VIEWS)
23 
24 #if defined(USE_AURA)
25 #include "ui/aura/layout_manager.h"  // nogncheck
26 #include "ui/aura/window.h"          // nogncheck
27 #endif
28 
29 namespace content {
30 
31 namespace {
32 
33 using InProcessEmbeddingMap =
34     std::map<base::UnguessableToken,
35              base::OnceCallback<void(NavigableContentsView*)>>;
36 
GetInProcessEmbeddingMap()37 InProcessEmbeddingMap& GetInProcessEmbeddingMap() {
38   static base::NoDestructor<InProcessEmbeddingMap> embedding_map;
39   return *embedding_map;
40 }
41 
GetInServiceProcessFlag()42 base::AtomicFlag& GetInServiceProcessFlag() {
43   static base::NoDestructor<base::AtomicFlag> in_service_process;
44   return *in_service_process;
45 }
46 
47 #if defined(TOOLKIT_VIEWS) && defined(USE_AURA)
48 
49 // Keeps child windows sized to the same bounds as the owning window.
50 class LocalWindowLayoutManager : public aura::LayoutManager {
51  public:
LocalWindowLayoutManager(aura::Window * owner)52   explicit LocalWindowLayoutManager(aura::Window* owner) : owner_(owner) {}
53   ~LocalWindowLayoutManager() override = default;
54 
55   // aura::LayoutManger:
OnWindowResized()56   void OnWindowResized() override { ResizeChildren(); }
OnWindowAddedToLayout(aura::Window * child)57   void OnWindowAddedToLayout(aura::Window* child) override { ResizeChildren(); }
OnWillRemoveWindowFromLayout(aura::Window * child)58   void OnWillRemoveWindowFromLayout(aura::Window* child) override {}
OnWindowRemovedFromLayout(aura::Window * child)59   void OnWindowRemovedFromLayout(aura::Window* child) override {}
OnChildWindowVisibilityChanged(aura::Window * child,bool visible)60   void OnChildWindowVisibilityChanged(aura::Window* child,
61                                       bool visible) override {}
SetChildBounds(aura::Window * child,const gfx::Rect & requested_bounds)62   void SetChildBounds(aura::Window* child,
63                       const gfx::Rect& requested_bounds) override {}
64 
65  private:
ResizeChildren()66   void ResizeChildren() {
67     for (auto* child : owner_->children())
68       SetChildBoundsDirect(child, gfx::Rect(owner_->bounds().size()));
69   }
70 
71   aura::Window* const owner_;
72 
73   DISALLOW_COPY_AND_ASSIGN(LocalWindowLayoutManager);
74 };
75 
76 // Owns an Aura window which parents another Aura window in the same process,
77 // corresponding to a web contents view hosted in the process.
78 class LocalViewHost : public views::NativeViewHost {
79  public:
LocalViewHost(aura::Window * window,NavigableContents * contents)80   LocalViewHost(aura::Window* window, NavigableContents* contents)
81       : window_(window), contents_(contents) {
82     window_->SetLayoutManager(new LocalWindowLayoutManager(window_));
83   }
84 
85   ~LocalViewHost() override = default;
86 
87   // views::View:
AddedToWidget()88   void AddedToWidget() override {
89     if (!native_view())
90       Attach(window_);
91   }
92 
GetAccessibleNodeData(ui::AXNodeData * node_data)93   void GetAccessibleNodeData(ui::AXNodeData* node_data) override {
94     node_data->role = ax::mojom::Role::kWebView;
95 
96     // The document title is provided to the accessibility system by other
97     // means, so setting it here would be redundant.
98     node_data->SetNameExplicitlyEmpty();
99 
100     if (contents_->content_ax_tree_id() != ui::AXTreeIDUnknown()) {
101       node_data->AddStringAttribute(ax::mojom::StringAttribute::kChildTreeId,
102                                     contents_->content_ax_tree_id().ToString());
103     }
104   }
105 
106  private:
107   aura::Window* const window_;
108   NavigableContents* const contents_;
109 
110   DISALLOW_COPY_AND_ASSIGN(LocalViewHost);
111 };
112 
113 #endif  // defined(TOOLKIT_VIEWS) && defined(USE_AURA)
114 
115 }  // namespace
116 
117 NavigableContentsView::~NavigableContentsView() = default;
118 
119 // static
SetClientRunningInServiceProcess()120 void NavigableContentsView::SetClientRunningInServiceProcess() {
121   GetInServiceProcessFlag().Set();
122 }
123 
124 // static
IsClientRunningInServiceProcess()125 bool NavigableContentsView::IsClientRunningInServiceProcess() {
126   return GetInServiceProcessFlag().IsSet();
127 }
128 
ClearNativeFocus()129 void NavigableContentsView::ClearNativeFocus() {
130 #if defined(TOOLKIT_VIEWS) && defined(USE_AURA)
131   auto* focus_manager = view_->GetFocusManager();
132   if (focus_manager)
133     focus_manager->ClearNativeFocus();
134 #endif  // defined(TOOLKIT_VIEWS) && defined(USE_AURA)
135 }
136 
NotifyAccessibilityTreeChange()137 void NavigableContentsView::NotifyAccessibilityTreeChange() {
138 #if defined(TOOLKIT_VIEWS) && defined(USE_AURA)
139   view_->NotifyAccessibilityEvent(ax::mojom::Event::kChildrenChanged, false);
140 #endif
141 }
142 
NavigableContentsView(NavigableContents * contents)143 NavigableContentsView::NavigableContentsView(NavigableContents* contents)
144     : contents_(contents) {
145 #if defined(TOOLKIT_VIEWS) && defined(USE_AURA)
146   window_ = std::make_unique<aura::Window>(nullptr);
147   window_->set_owned_by_parent(false);
148   window_->SetName("NavigableContentsViewWindow");
149   window_->SetType(aura::client::WINDOW_TYPE_CONTROL);
150   window_->Init(ui::LAYER_NOT_DRAWN);
151   window_->Show();
152 
153   view_ = std::make_unique<LocalViewHost>(window_.get(), contents_);
154   view_->set_owned_by_client();
155 #endif  // defined(TOOLKIT_VIEWS) && defined(USE_AURA)
156 }
157 
EmbedUsingToken(const base::UnguessableToken & token)158 void NavigableContentsView::EmbedUsingToken(
159     const base::UnguessableToken& token) {
160 #if defined(TOOLKIT_VIEWS)
161   DCHECK(IsClientRunningInServiceProcess());
162 
163   // |token| should already have an embed callback entry in the in-process
164   // callback map, injected by the in-process Content Service implementation.
165   auto& embeddings = GetInProcessEmbeddingMap();
166   auto it = embeddings.find(token);
167   if (it == embeddings.end()) {
168     DLOG(ERROR) << "Unable to embed with unknown token " << token.ToString();
169     return;
170   }
171 
172   // Invoke a callback provided by the Content Service's host environment. This
173   // should parent a web content view to our own |view()|, as well as set
174   // |native_view_| to the corresponding web contents' own NativeView.
175   auto callback = std::move(it->second);
176   embeddings.erase(it);
177   std::move(callback).Run(this);
178 #endif  // defined(TOOLKIT_VIEWS)
179 }
180 
181 // static
RegisterInProcessEmbedCallback(const base::UnguessableToken & token,base::OnceCallback<void (NavigableContentsView *)> callback)182 void NavigableContentsView::RegisterInProcessEmbedCallback(
183     const base::UnguessableToken& token,
184     base::OnceCallback<void(NavigableContentsView*)> callback) {
185   GetInProcessEmbeddingMap()[token] = std::move(callback);
186 }
187 
188 }  // namespace content
189