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