1 // Copyright 2015 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.
5 #include "components/html_viewer/html_frame_tree_manager.h"
9 #include "base/command_line.h"
10 #include "base/logging.h"
11 #include "components/html_viewer/blink_basic_type_converters.h"
12 #include "components/html_viewer/blink_url_request_type_converters.h"
13 #include "components/html_viewer/document_resource_waiter.h"
14 #include "components/html_viewer/global_state.h"
15 #include "components/html_viewer/html_factory.h"
16 #include "components/html_viewer/html_frame.h"
17 #include "components/html_viewer/html_frame_delegate.h"
18 #include "components/html_viewer/html_frame_tree_manager_observer.h"
19 #include "components/mus/public/cpp/view_tree_connection.h"
20 #include "components/web_view/web_view_switches.h"
21 #include "third_party/WebKit/public/web/WebLocalFrame.h"
22 #include "third_party/WebKit/public/web/WebRemoteFrame.h"
23 #include "third_party/WebKit/public/web/WebTreeScopeType.h"
24 #include "third_party/WebKit/public/web/WebView.h"
25 #include "ui/gfx/geometry/dip_util.h"
26 #include "ui/gfx/geometry/size.h"
28 namespace html_viewer
{
31 // Returns the index of the FrameData with the id of |frame_id| in |index|. On
32 // success returns true, otherwise false.
33 bool FindFrameDataIndex(
34 const mojo::Array
<web_view::mojom::FrameDataPtr
>& frame_data
,
37 for (size_t i
= 0; i
< frame_data
.size(); ++i
) {
38 if (frame_data
[i
]->frame_id
== frame_id
) {
48 // Object that calls OnHTMLFrameTreeManagerChangeIdAdvanced() from the
50 class HTMLFrameTreeManager::ChangeIdAdvancedNotifier
{
52 explicit ChangeIdAdvancedNotifier(
53 base::ObserverList
<HTMLFrameTreeManagerObserver
>* observers
)
54 : observers_(observers
) {}
55 ~ChangeIdAdvancedNotifier() {
56 FOR_EACH_OBSERVER(HTMLFrameTreeManagerObserver
, *observers_
,
57 OnHTMLFrameTreeManagerChangeIdAdvanced());
61 base::ObserverList
<HTMLFrameTreeManagerObserver
>* observers_
;
63 DISALLOW_COPY_AND_ASSIGN(ChangeIdAdvancedNotifier
);
67 HTMLFrameTreeManager::TreeMap
* HTMLFrameTreeManager::instances_
= nullptr;
70 HTMLFrame
* HTMLFrameTreeManager::CreateFrameAndAttachToTree(
71 GlobalState
* global_state
,
73 scoped_ptr
<DocumentResourceWaiter
> resource_waiter
,
74 HTMLFrameDelegate
* delegate
) {
76 instances_
= new TreeMap
;
78 mojo::InterfaceRequest
<web_view::mojom::FrameClient
> frame_client_request
;
79 web_view::mojom::FramePtr server_frame
;
80 mojo::Array
<web_view::mojom::FrameDataPtr
> frame_data
;
83 web_view::mojom::ViewConnectType view_connect_type
;
84 web_view::mojom::FrameClient::OnConnectCallback on_connect_callback
;
85 resource_waiter
->Release(&frame_client_request
, &server_frame
, &frame_data
,
86 &change_id
, &view_id
, &view_connect_type
,
87 &on_connect_callback
);
88 resource_waiter
.reset();
90 on_connect_callback
.Run();
92 HTMLFrameTreeManager
* frame_tree
=
93 FindFrameTreeWithRoot(frame_data
[0]->frame_id
);
95 DCHECK(!frame_tree
|| change_id
<= frame_tree
->change_id_
);
97 if (view_connect_type
== web_view::mojom::VIEW_CONNECT_TYPE_USE_EXISTING
&&
99 DVLOG(1) << "was told to use existing view but do not have frame tree";
104 frame_tree
= new HTMLFrameTreeManager(global_state
);
105 frame_tree
->Init(delegate
, view
, frame_data
, change_id
);
106 if (frame_data
[0]->frame_id
== view
->id())
107 (*instances_
)[frame_data
[0]->frame_id
] = frame_tree
;
108 } else if (view_connect_type
==
109 web_view::mojom::VIEW_CONNECT_TYPE_USE_EXISTING
) {
110 HTMLFrame
* existing_frame
= frame_tree
->root_
->FindFrame(view_id
);
111 if (!existing_frame
) {
112 DVLOG(1) << "was told to use existing view but could not find view";
115 if (!existing_frame
->IsLocal()) {
116 DVLOG(1) << "was told to use existing view, but frame is remote";
119 existing_frame
->SwapDelegate(delegate
);
121 // We're going to share a frame tree. We should know about the frame.
122 CHECK(view
->id() != frame_data
[0]->frame_id
);
123 HTMLFrame
* existing_frame
= frame_tree
->root_
->FindFrame(view
->id());
124 if (existing_frame
) {
125 CHECK(!existing_frame
->IsLocal());
126 size_t frame_data_index
= 0u;
127 CHECK(FindFrameDataIndex(frame_data
, view
->id(), &frame_data_index
));
128 const web_view::mojom::FrameDataPtr
& data
= frame_data
[frame_data_index
];
129 existing_frame
->SwapToLocal(delegate
, view
, data
->client_properties
);
131 // If we can't find the frame and the change_id of the incoming
132 // tree is before the change id we've processed, then we removed the
133 // frame and need do nothing.
134 if (change_id
< frame_tree
->change_id_
)
137 // We removed the frame but it hasn't been acked yet.
138 if (frame_tree
->pending_remove_ids_
.count(view
->id()))
141 // We don't know about the frame, but should. Something is wrong.
142 DVLOG(1) << "unable to locate frame to attach to";
147 HTMLFrame
* frame
= frame_tree
->root_
->FindFrame(view_id
);
149 frame
->Bind(server_frame
.Pass(), frame_client_request
.Pass());
154 HTMLFrameTreeManager
* HTMLFrameTreeManager::FindFrameTreeWithRoot(
155 uint32_t root_frame_id
) {
156 return (!base::CommandLine::ForCurrentProcess()->HasSwitch(
157 web_view::switches::kOOPIFAlwaysCreateNewFrameTree
) &&
158 instances_
&& instances_
->count(root_frame_id
))
159 ? (*instances_
)[root_frame_id
]
163 blink::WebView
* HTMLFrameTreeManager::GetWebView() {
164 return root_
->web_view();
167 void HTMLFrameTreeManager::AddObserver(HTMLFrameTreeManagerObserver
* observer
) {
168 observers_
.AddObserver(observer
);
171 void HTMLFrameTreeManager::RemoveObserver(
172 HTMLFrameTreeManagerObserver
* observer
) {
173 observers_
.RemoveObserver(observer
);
176 void HTMLFrameTreeManager::OnFrameDestroyed(HTMLFrame
* frame
) {
180 if (frame
== local_root_
)
181 local_root_
= nullptr;
183 if (!in_process_on_frame_removed_
)
184 pending_remove_ids_
.insert(frame
->id());
186 if (!local_root_
|| !local_root_
->HasLocalDescendant())
190 HTMLFrameTreeManager::HTMLFrameTreeManager(GlobalState
* global_state
)
191 : global_state_(global_state
),
193 local_root_(nullptr),
195 in_process_on_frame_removed_(false),
196 weak_factory_(this) {}
198 HTMLFrameTreeManager::~HTMLFrameTreeManager() {
199 DCHECK(!root_
|| !local_root_
);
200 RemoveFromInstances();
202 FOR_EACH_OBSERVER(HTMLFrameTreeManagerObserver
, observers_
,
203 OnHTMLFrameTreeManagerDestroyed());
206 void HTMLFrameTreeManager::Init(
207 HTMLFrameDelegate
* delegate
,
208 mus::View
* local_view
,
209 const mojo::Array
<web_view::mojom::FrameDataPtr
>& frame_data
,
210 uint32_t change_id
) {
211 change_id_
= change_id
;
212 root_
= BuildFrameTree(delegate
, frame_data
, local_view
->id(), local_view
);
213 local_root_
= root_
->FindFrame(local_view
->id());
215 local_root_
->UpdateFocus();
218 HTMLFrame
* HTMLFrameTreeManager::BuildFrameTree(
219 HTMLFrameDelegate
* delegate
,
220 const mojo::Array
<web_view::mojom::FrameDataPtr
>& frame_data
,
221 uint32_t local_frame_id
,
222 mus::View
* local_view
) {
223 std::vector
<HTMLFrame
*> parents
;
224 HTMLFrame
* root
= nullptr;
225 HTMLFrame
* last_frame
= nullptr;
226 for (size_t i
= 0; i
< frame_data
.size(); ++i
) {
227 if (last_frame
&& frame_data
[i
]->parent_id
== last_frame
->id()) {
228 parents
.push_back(last_frame
);
229 } else if (!parents
.empty()) {
230 while (parents
.back()->id() != frame_data
[i
]->parent_id
)
233 HTMLFrame::CreateParams
params(this,
234 !parents
.empty() ? parents
.back() : nullptr,
235 frame_data
[i
]->frame_id
, local_view
,
236 frame_data
[i
]->client_properties
, nullptr);
237 if (frame_data
[i
]->frame_id
== local_frame_id
)
238 params
.delegate
= delegate
;
240 HTMLFrame
* frame
= delegate
->GetHTMLFactory()->CreateHTMLFrame(¶ms
);
244 DCHECK(frame
->parent());
250 void HTMLFrameTreeManager::RemoveFromInstances() {
251 for (auto pair
: *instances_
) {
252 if (pair
.second
== this) {
253 instances_
->erase(pair
.first
);
259 bool HTMLFrameTreeManager::PrepareForStructureChange(HTMLFrame
* source
,
260 uint32_t change_id
) {
261 // The change ids may differ if multiple HTMLDocuments are attached to the
262 // same tree (which means we have multiple FrameClient for the same tree).
263 if (change_id
!= (change_id_
+ 1))
266 // We only process changes for the topmost local root.
267 if (source
!= local_root_
)
270 // Update the id as the change is going to be applied (or we can assume it
271 // will be applied if we get here).
272 change_id_
= change_id
;
276 void HTMLFrameTreeManager::ProcessOnFrameAdded(
279 web_view::mojom::FrameDataPtr frame_data
) {
280 if (!PrepareForStructureChange(source
, change_id
))
283 ChangeIdAdvancedNotifier
notifier(&observers_
);
285 HTMLFrame
* parent
= root_
->FindFrame(frame_data
->parent_id
);
287 DVLOG(1) << "Received invalid parent in OnFrameAdded "
288 << frame_data
->parent_id
;
291 if (root_
->FindFrame(frame_data
->frame_id
)) {
292 DVLOG(1) << "Child with id already exists in OnFrameAdded "
293 << frame_data
->frame_id
;
297 // Because notification is async it's entirely possible for us to create a
298 // new frame, and remove it before we get the add from the server. This check
299 // ensures we don't add back a frame we explicitly removed.
300 if (pending_remove_ids_
.count(frame_data
->frame_id
))
303 HTMLFrame::CreateParams
params(this, parent
, frame_data
->frame_id
, nullptr,
304 frame_data
->client_properties
, nullptr);
305 // |parent| takes ownership of created HTMLFrame.
306 source
->GetFirstAncestorWithDelegate()
307 ->delegate_
->GetHTMLFactory()
308 ->CreateHTMLFrame(¶ms
);
311 void HTMLFrameTreeManager::ProcessOnFrameRemoved(HTMLFrame
* source
,
314 if (!PrepareForStructureChange(source
, change_id
))
317 ChangeIdAdvancedNotifier
notifier(&observers_
);
319 pending_remove_ids_
.erase(frame_id
);
321 HTMLFrame
* frame
= root_
->FindFrame(frame_id
);
323 DVLOG(1) << "OnFrameRemoved with unknown frame " << frame_id
;
327 // We shouldn't see requests to remove the root.
328 if (frame
== root_
) {
329 DVLOG(1) << "OnFrameRemoved supplied root; ignoring";
333 // Requests to remove local frames are followed by the View being destroyed.
334 // We handle destruction there.
335 if (frame
->IsLocal())
338 DCHECK(!in_process_on_frame_removed_
);
339 in_process_on_frame_removed_
= true;
340 base::WeakPtr
<HTMLFrameTreeManager
> ref(weak_factory_
.GetWeakPtr());
343 return; // We were deleted.
345 in_process_on_frame_removed_
= false;
348 void HTMLFrameTreeManager::ProcessOnFrameClientPropertyChanged(
351 const mojo::String
& name
,
352 mojo::Array
<uint8_t> new_data
) {
353 if (source
!= local_root_
)
356 HTMLFrame
* frame
= root_
->FindFrame(frame_id
);
358 frame
->SetValueFromClientProperty(name
, new_data
.Pass());