OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "components/html_viewer/html_frame_tree_manager.h" | |
6 | |
7 #include <stddef.h> | |
8 #include <algorithm> | |
9 #include <utility> | |
10 | |
11 #include "base/command_line.h" | |
12 #include "base/logging.h" | |
13 #include "base/macros.h" | |
14 #include "components/html_viewer/blink_basic_type_converters.h" | |
15 #include "components/html_viewer/blink_url_request_type_converters.h" | |
16 #include "components/html_viewer/document_resource_waiter.h" | |
17 #include "components/html_viewer/global_state.h" | |
18 #include "components/html_viewer/html_factory.h" | |
19 #include "components/html_viewer/html_frame.h" | |
20 #include "components/html_viewer/html_frame_delegate.h" | |
21 #include "components/html_viewer/html_frame_tree_manager_observer.h" | |
22 #include "components/mus/public/cpp/window_tree_connection.h" | |
23 #include "components/web_view/web_view_switches.h" | |
24 #include "third_party/WebKit/public/web/WebLocalFrame.h" | |
25 #include "third_party/WebKit/public/web/WebRemoteFrame.h" | |
26 #include "third_party/WebKit/public/web/WebTreeScopeType.h" | |
27 #include "third_party/WebKit/public/web/WebView.h" | |
28 #include "ui/gfx/geometry/dip_util.h" | |
29 #include "ui/gfx/geometry/size.h" | |
30 | |
31 namespace html_viewer { | |
32 namespace { | |
33 | |
34 // Returns the index of the FrameData with the id of |frame_id| in |index|. On | |
35 // success returns true, otherwise false. | |
36 bool FindFrameDataIndex( | |
37 const mojo::Array<web_view::mojom::FrameDataPtr>& frame_data, | |
38 uint32_t frame_id, | |
39 size_t* index) { | |
40 for (size_t i = 0; i < frame_data.size(); ++i) { | |
41 if (frame_data[i]->frame_id == frame_id) { | |
42 *index = i; | |
43 return true; | |
44 } | |
45 } | |
46 return false; | |
47 } | |
48 | |
49 } // namespace | |
50 | |
51 // Object that calls OnHTMLFrameTreeManagerChangeIdAdvanced() from the | |
52 // destructor. | |
53 class HTMLFrameTreeManager::ChangeIdAdvancedNotifier { | |
54 public: | |
55 explicit ChangeIdAdvancedNotifier( | |
56 base::ObserverList<HTMLFrameTreeManagerObserver>* observers) | |
57 : observers_(observers) {} | |
58 ~ChangeIdAdvancedNotifier() { | |
59 FOR_EACH_OBSERVER(HTMLFrameTreeManagerObserver, *observers_, | |
60 OnHTMLFrameTreeManagerChangeIdAdvanced()); | |
61 } | |
62 | |
63 private: | |
64 base::ObserverList<HTMLFrameTreeManagerObserver>* observers_; | |
65 | |
66 DISALLOW_COPY_AND_ASSIGN(ChangeIdAdvancedNotifier); | |
67 }; | |
68 | |
69 // static | |
70 HTMLFrameTreeManager::TreeMap* HTMLFrameTreeManager::instances_ = nullptr; | |
71 | |
72 // static | |
73 HTMLFrame* HTMLFrameTreeManager::CreateFrameAndAttachToTree( | |
74 GlobalState* global_state, | |
75 mus::Window* window, | |
76 scoped_ptr<DocumentResourceWaiter> resource_waiter, | |
77 HTMLFrameDelegate* delegate) { | |
78 if (!instances_) | |
79 instances_ = new TreeMap; | |
80 | |
81 mojo::InterfaceRequest<web_view::mojom::FrameClient> frame_client_request; | |
82 web_view::mojom::FramePtr server_frame; | |
83 mojo::Array<web_view::mojom::FrameDataPtr> frame_data; | |
84 uint32_t change_id; | |
85 uint32_t window_id; | |
86 web_view::mojom::WindowConnectType window_connect_type; | |
87 web_view::mojom::FrameClient::OnConnectCallback on_connect_callback; | |
88 resource_waiter->Release(&frame_client_request, &server_frame, &frame_data, | |
89 &change_id, &window_id, &window_connect_type, | |
90 &on_connect_callback); | |
91 resource_waiter.reset(); | |
92 | |
93 on_connect_callback.Run(); | |
94 | |
95 HTMLFrameTreeManager* frame_tree = | |
96 FindFrameTreeWithRoot(frame_data[0]->frame_id); | |
97 | |
98 DCHECK(!frame_tree || change_id <= frame_tree->change_id_); | |
99 | |
100 DVLOG(2) << "HTMLFrameTreeManager::CreateFrameAndAttachToTree " | |
101 << " frame_tree=" << frame_tree << " use_existing=" | |
102 << (window_connect_type == | |
103 web_view::mojom::WindowConnectType::USE_EXISTING) | |
104 << " frame_id=" << window_id; | |
105 if (window_connect_type == web_view::mojom::WindowConnectType::USE_EXISTING && | |
106 !frame_tree) { | |
107 DVLOG(1) << "was told to use existing window but do not have frame tree"; | |
108 return nullptr; | |
109 } | |
110 | |
111 if (!frame_tree) { | |
112 frame_tree = new HTMLFrameTreeManager(global_state); | |
113 frame_tree->Init(delegate, window, frame_data, change_id); | |
114 (*instances_)[frame_data[0]->frame_id] = frame_tree; | |
115 } else if (window_connect_type == | |
116 web_view::mojom::WindowConnectType::USE_EXISTING) { | |
117 HTMLFrame* existing_frame = frame_tree->root_->FindFrame(window_id); | |
118 if (!existing_frame) { | |
119 DVLOG(1) << "was told to use existing window but could not find window"; | |
120 return nullptr; | |
121 } | |
122 if (!existing_frame->IsLocal()) { | |
123 DVLOG(1) << "was told to use existing window, but frame is remote"; | |
124 return nullptr; | |
125 } | |
126 existing_frame->SwapDelegate(delegate); | |
127 } else { | |
128 // We're going to share a frame tree. We should know about the frame. | |
129 CHECK(window->id() != frame_data[0]->frame_id); | |
130 HTMLFrame* existing_frame = frame_tree->root_->FindFrame(window->id()); | |
131 if (existing_frame) { | |
132 CHECK(!existing_frame->IsLocal()); | |
133 size_t frame_data_index = 0u; | |
134 CHECK(FindFrameDataIndex(frame_data, window->id(), &frame_data_index)); | |
135 const web_view::mojom::FrameDataPtr& data = frame_data[frame_data_index]; | |
136 existing_frame->SwapToLocal(delegate, window, data->client_properties); | |
137 } else { | |
138 // If we can't find the frame and the change_id of the incoming | |
139 // tree is before the change id we've processed, then we removed the | |
140 // frame and need do nothing. | |
141 if (change_id < frame_tree->change_id_) | |
142 return nullptr; | |
143 | |
144 // We removed the frame but it hasn't been acked yet. | |
145 if (frame_tree->pending_remove_ids_.count(window->id())) | |
146 return nullptr; | |
147 | |
148 // We don't know about the frame, but should. Something is wrong. | |
149 DVLOG(1) << "unable to locate frame to attach to"; | |
150 return nullptr; | |
151 } | |
152 } | |
153 | |
154 HTMLFrame* frame = frame_tree->root_->FindFrame(window_id); | |
155 DCHECK(frame); | |
156 frame->Bind(std::move(server_frame), std::move(frame_client_request)); | |
157 return frame; | |
158 } | |
159 | |
160 // static | |
161 HTMLFrameTreeManager* HTMLFrameTreeManager::FindFrameTreeWithRoot( | |
162 uint32_t root_frame_id) { | |
163 return (!base::CommandLine::ForCurrentProcess()->HasSwitch( | |
164 web_view::switches::kOOPIFAlwaysCreateNewFrameTree) && | |
165 instances_ && instances_->count(root_frame_id)) | |
166 ? (*instances_)[root_frame_id] | |
167 : nullptr; | |
168 } | |
169 | |
170 blink::WebView* HTMLFrameTreeManager::GetWebView() { | |
171 return root_->web_view(); | |
172 } | |
173 | |
174 void HTMLFrameTreeManager::AddObserver(HTMLFrameTreeManagerObserver* observer) { | |
175 observers_.AddObserver(observer); | |
176 } | |
177 | |
178 void HTMLFrameTreeManager::RemoveObserver( | |
179 HTMLFrameTreeManagerObserver* observer) { | |
180 observers_.RemoveObserver(observer); | |
181 } | |
182 | |
183 void HTMLFrameTreeManager::OnFrameDestroyed(HTMLFrame* frame) { | |
184 if (!in_process_on_frame_removed_) | |
185 pending_remove_ids_.insert(frame->id()); | |
186 | |
187 if (frame == root_) { | |
188 // If |root_| is removed before |local_frame_|, we don't have a way to | |
189 // select a new local frame when |local_frame_| is removed. On the other | |
190 // hand, it is impossible to remove |root_| after all local frames are gone, | |
191 // because by that time this object has already been deleted. | |
192 CHECK_EQ(root_, local_frame_); | |
193 root_ = nullptr; | |
194 } | |
195 | |
196 if (frame != local_frame_) | |
197 return; | |
198 | |
199 // |local_frame_| is being removed. We need to find out whether we have local | |
200 // frames that are not descendants of |local_frame_|, and if yes select a new | |
201 // |local_frame_|. | |
202 local_frame_ = FindNewLocalFrame(); | |
203 if (!local_frame_) | |
204 delete this; | |
205 } | |
206 | |
207 HTMLFrameTreeManager::HTMLFrameTreeManager(GlobalState* global_state) | |
208 : global_state_(global_state), | |
209 root_(nullptr), | |
210 local_frame_(nullptr), | |
211 change_id_(0u), | |
212 in_process_on_frame_removed_(false), | |
213 weak_factory_(this) {} | |
214 | |
215 HTMLFrameTreeManager::~HTMLFrameTreeManager() { | |
216 DCHECK(!local_frame_); | |
217 RemoveFromInstances(); | |
218 | |
219 FOR_EACH_OBSERVER(HTMLFrameTreeManagerObserver, observers_, | |
220 OnHTMLFrameTreeManagerDestroyed()); | |
221 } | |
222 | |
223 void HTMLFrameTreeManager::Init( | |
224 HTMLFrameDelegate* delegate, | |
225 mus::Window* local_window, | |
226 const mojo::Array<web_view::mojom::FrameDataPtr>& frame_data, | |
227 uint32_t change_id) { | |
228 change_id_ = change_id; | |
229 root_ = | |
230 BuildFrameTree(delegate, frame_data, local_window->id(), local_window); | |
231 local_frame_ = root_->FindFrame(local_window->id()); | |
232 CHECK(local_frame_); | |
233 local_frame_->UpdateFocus(); | |
234 } | |
235 | |
236 HTMLFrame* HTMLFrameTreeManager::BuildFrameTree( | |
237 HTMLFrameDelegate* delegate, | |
238 const mojo::Array<web_view::mojom::FrameDataPtr>& frame_data, | |
239 uint32_t local_frame_id, | |
240 mus::Window* local_window) { | |
241 std::vector<HTMLFrame*> parents; | |
242 HTMLFrame* root = nullptr; | |
243 HTMLFrame* last_frame = nullptr; | |
244 for (size_t i = 0; i < frame_data.size(); ++i) { | |
245 if (last_frame && frame_data[i]->parent_id == last_frame->id()) { | |
246 parents.push_back(last_frame); | |
247 } else if (!parents.empty()) { | |
248 while (parents.back()->id() != frame_data[i]->parent_id) | |
249 parents.pop_back(); | |
250 } | |
251 HTMLFrame::CreateParams params(this, | |
252 !parents.empty() ? parents.back() : nullptr, | |
253 frame_data[i]->frame_id, local_window, | |
254 frame_data[i]->client_properties, nullptr); | |
255 if (frame_data[i]->frame_id == local_frame_id) | |
256 params.delegate = delegate; | |
257 | |
258 HTMLFrame* frame = delegate->GetHTMLFactory()->CreateHTMLFrame(¶ms); | |
259 if (!last_frame) | |
260 root = frame; | |
261 else | |
262 DCHECK(frame->parent()); | |
263 last_frame = frame; | |
264 } | |
265 return root; | |
266 } | |
267 | |
268 void HTMLFrameTreeManager::RemoveFromInstances() { | |
269 for (auto pair : *instances_) { | |
270 if (pair.second == this) { | |
271 instances_->erase(pair.first); | |
272 return; | |
273 } | |
274 } | |
275 } | |
276 | |
277 bool HTMLFrameTreeManager::PrepareForStructureChange(HTMLFrame* source, | |
278 uint32_t change_id) { | |
279 // The change ids may differ if multiple HTMLDocuments are attached to the | |
280 // same tree (which means we have multiple FrameClient for the same tree). | |
281 if (change_id != (change_id_ + 1)) | |
282 return false; | |
283 | |
284 // We only process changes for the topmost local root. | |
285 if (source != local_frame_) | |
286 return false; | |
287 | |
288 // Update the id as the change is going to be applied (or we can assume it | |
289 // will be applied if we get here). | |
290 change_id_ = change_id; | |
291 return true; | |
292 } | |
293 | |
294 void HTMLFrameTreeManager::ProcessOnFrameAdded( | |
295 HTMLFrame* source, | |
296 uint32_t change_id, | |
297 web_view::mojom::FrameDataPtr frame_data) { | |
298 if (!PrepareForStructureChange(source, change_id)) | |
299 return; | |
300 | |
301 ChangeIdAdvancedNotifier notifier(&observers_); | |
302 | |
303 HTMLFrame* parent = root_->FindFrame(frame_data->parent_id); | |
304 if (!parent) { | |
305 DVLOG(1) << "Received invalid parent in OnFrameAdded " | |
306 << frame_data->parent_id; | |
307 return; | |
308 } | |
309 if (root_->FindFrame(frame_data->frame_id)) { | |
310 DVLOG(1) << "Child with id already exists in OnFrameAdded " | |
311 << frame_data->frame_id; | |
312 return; | |
313 } | |
314 | |
315 // Because notification is async it's entirely possible for us to create a | |
316 // new frame, and remove it before we get the add from the server. This check | |
317 // ensures we don't add back a frame we explicitly removed. | |
318 if (pending_remove_ids_.count(frame_data->frame_id)) | |
319 return; | |
320 | |
321 DVLOG(2) << "OnFrameAdded this=" << this | |
322 << " frame_id=" << frame_data->frame_id; | |
323 | |
324 HTMLFrame::CreateParams params(this, parent, frame_data->frame_id, nullptr, | |
325 frame_data->client_properties, nullptr); | |
326 // |parent| takes ownership of created HTMLFrame. | |
327 source->GetFirstAncestorWithDelegate() | |
328 ->delegate_->GetHTMLFactory() | |
329 ->CreateHTMLFrame(¶ms); | |
330 } | |
331 | |
332 void HTMLFrameTreeManager::ProcessOnFrameRemoved(HTMLFrame* source, | |
333 uint32_t change_id, | |
334 uint32_t frame_id) { | |
335 if (!PrepareForStructureChange(source, change_id)) | |
336 return; | |
337 | |
338 ChangeIdAdvancedNotifier notifier(&observers_); | |
339 | |
340 pending_remove_ids_.erase(frame_id); | |
341 | |
342 HTMLFrame* frame = root_->FindFrame(frame_id); | |
343 if (!frame) { | |
344 DVLOG(1) << "OnFrameRemoved with unknown frame " << frame_id; | |
345 return; | |
346 } | |
347 | |
348 // We shouldn't see requests to remove the root. | |
349 if (frame == root_) { | |
350 DVLOG(1) << "OnFrameRemoved supplied root; ignoring"; | |
351 return; | |
352 } | |
353 | |
354 // Requests to remove local frames are followed by the Window being destroyed. | |
355 // We handle destruction there. | |
356 if (frame->IsLocal()) | |
357 return; | |
358 | |
359 DVLOG(2) << "OnFrameRemoved this=" << this << " frame_id=" << frame_id; | |
360 | |
361 DCHECK(!in_process_on_frame_removed_); | |
362 in_process_on_frame_removed_ = true; | |
363 base::WeakPtr<HTMLFrameTreeManager> ref(weak_factory_.GetWeakPtr()); | |
364 frame->Close(); | |
365 if (!ref) | |
366 return; // We were deleted. | |
367 | |
368 in_process_on_frame_removed_ = false; | |
369 } | |
370 | |
371 void HTMLFrameTreeManager::ProcessOnFrameClientPropertyChanged( | |
372 HTMLFrame* source, | |
373 uint32_t frame_id, | |
374 const mojo::String& name, | |
375 mojo::Array<uint8_t> new_data) { | |
376 if (source != local_frame_) | |
377 return; | |
378 | |
379 HTMLFrame* frame = root_->FindFrame(frame_id); | |
380 if (frame) | |
381 frame->SetValueFromClientProperty(name, std::move(new_data)); | |
382 } | |
383 | |
384 HTMLFrame* HTMLFrameTreeManager::FindNewLocalFrame() { | |
385 HTMLFrame* new_local_frame = nullptr; | |
386 | |
387 if (root_) { | |
388 std::queue<HTMLFrame*> nodes; | |
389 nodes.push(root_); | |
390 | |
391 while (!nodes.empty()) { | |
392 HTMLFrame* node = nodes.front(); | |
393 nodes.pop(); | |
394 | |
395 if (node == local_frame_) | |
396 continue; | |
397 | |
398 if (node->IsLocal()) { | |
399 new_local_frame = node; | |
400 break; | |
401 } | |
402 | |
403 for (const auto& child : node->children()) | |
404 nodes.push(child); | |
405 } | |
406 } | |
407 | |
408 return new_local_frame; | |
409 } | |
410 | |
411 } // namespace mojo | |
OLD | NEW |