Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / components / html_viewer / html_frame_tree_manager.cc
blobe8b2de45ff157da11fd3f2f7e12ac58d4eab29dc
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"
7 #include <algorithm>
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 {
29 namespace {
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,
35 uint32_t frame_id,
36 size_t* index) {
37 for (size_t i = 0; i < frame_data.size(); ++i) {
38 if (frame_data[i]->frame_id == frame_id) {
39 *index = i;
40 return true;
43 return false;
46 } // namespace
48 // Object that calls OnHTMLFrameTreeManagerChangeIdAdvanced() from the
49 // destructor.
50 class HTMLFrameTreeManager::ChangeIdAdvancedNotifier {
51 public:
52 explicit ChangeIdAdvancedNotifier(
53 base::ObserverList<HTMLFrameTreeManagerObserver>* observers)
54 : observers_(observers) {}
55 ~ChangeIdAdvancedNotifier() {
56 FOR_EACH_OBSERVER(HTMLFrameTreeManagerObserver, *observers_,
57 OnHTMLFrameTreeManagerChangeIdAdvanced());
60 private:
61 base::ObserverList<HTMLFrameTreeManagerObserver>* observers_;
63 DISALLOW_COPY_AND_ASSIGN(ChangeIdAdvancedNotifier);
66 // static
67 HTMLFrameTreeManager::TreeMap* HTMLFrameTreeManager::instances_ = nullptr;
69 // static
70 HTMLFrame* HTMLFrameTreeManager::CreateFrameAndAttachToTree(
71 GlobalState* global_state,
72 mus::View* view,
73 scoped_ptr<DocumentResourceWaiter> resource_waiter,
74 HTMLFrameDelegate* delegate) {
75 if (!instances_)
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;
81 uint32_t change_id;
82 uint32_t view_id;
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 &&
98 !frame_tree) {
99 DVLOG(1) << "was told to use existing view but do not have frame tree";
100 return nullptr;
103 if (!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";
113 return nullptr;
115 if (!existing_frame->IsLocal()) {
116 DVLOG(1) << "was told to use existing view, but frame is remote";
117 return nullptr;
119 existing_frame->SwapDelegate(delegate);
120 } else {
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);
130 } else {
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_)
135 return nullptr;
137 // We removed the frame but it hasn't been acked yet.
138 if (frame_tree->pending_remove_ids_.count(view->id()))
139 return nullptr;
141 // We don't know about the frame, but should. Something is wrong.
142 DVLOG(1) << "unable to locate frame to attach to";
143 return nullptr;
147 HTMLFrame* frame = frame_tree->root_->FindFrame(view_id);
148 DCHECK(frame);
149 frame->Bind(server_frame.Pass(), frame_client_request.Pass());
150 return frame;
153 // static
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]
160 : nullptr;
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) {
177 if (frame == root_)
178 root_ = nullptr;
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())
187 delete this;
190 HTMLFrameTreeManager::HTMLFrameTreeManager(GlobalState* global_state)
191 : global_state_(global_state),
192 root_(nullptr),
193 local_root_(nullptr),
194 change_id_(0u),
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());
214 CHECK(local_root_);
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)
231 parents.pop_back();
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(&params);
241 if (!last_frame)
242 root = frame;
243 else
244 DCHECK(frame->parent());
245 last_frame = frame;
247 return root;
250 void HTMLFrameTreeManager::RemoveFromInstances() {
251 for (auto pair : *instances_) {
252 if (pair.second == this) {
253 instances_->erase(pair.first);
254 return;
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))
264 return false;
266 // We only process changes for the topmost local root.
267 if (source != local_root_)
268 return false;
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;
273 return true;
276 void HTMLFrameTreeManager::ProcessOnFrameAdded(
277 HTMLFrame* source,
278 uint32_t change_id,
279 web_view::mojom::FrameDataPtr frame_data) {
280 if (!PrepareForStructureChange(source, change_id))
281 return;
283 ChangeIdAdvancedNotifier notifier(&observers_);
285 HTMLFrame* parent = root_->FindFrame(frame_data->parent_id);
286 if (!parent) {
287 DVLOG(1) << "Received invalid parent in OnFrameAdded "
288 << frame_data->parent_id;
289 return;
291 if (root_->FindFrame(frame_data->frame_id)) {
292 DVLOG(1) << "Child with id already exists in OnFrameAdded "
293 << frame_data->frame_id;
294 return;
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))
301 return;
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(&params);
311 void HTMLFrameTreeManager::ProcessOnFrameRemoved(HTMLFrame* source,
312 uint32_t change_id,
313 uint32_t frame_id) {
314 if (!PrepareForStructureChange(source, change_id))
315 return;
317 ChangeIdAdvancedNotifier notifier(&observers_);
319 pending_remove_ids_.erase(frame_id);
321 HTMLFrame* frame = root_->FindFrame(frame_id);
322 if (!frame) {
323 DVLOG(1) << "OnFrameRemoved with unknown frame " << frame_id;
324 return;
327 // We shouldn't see requests to remove the root.
328 if (frame == root_) {
329 DVLOG(1) << "OnFrameRemoved supplied root; ignoring";
330 return;
333 // Requests to remove local frames are followed by the View being destroyed.
334 // We handle destruction there.
335 if (frame->IsLocal())
336 return;
338 DCHECK(!in_process_on_frame_removed_);
339 in_process_on_frame_removed_ = true;
340 base::WeakPtr<HTMLFrameTreeManager> ref(weak_factory_.GetWeakPtr());
341 frame->Close();
342 if (!ref)
343 return; // We were deleted.
345 in_process_on_frame_removed_ = false;
348 void HTMLFrameTreeManager::ProcessOnFrameClientPropertyChanged(
349 HTMLFrame* source,
350 uint32_t frame_id,
351 const mojo::String& name,
352 mojo::Array<uint8_t> new_data) {
353 if (source != local_root_)
354 return;
356 HTMLFrame* frame = root_->FindFrame(frame_id);
357 if (frame)
358 frame->SetValueFromClientProperty(name, new_data.Pass());
361 } // namespace mojo