Roll src/third_party/WebKit c63b89c:29324ab (svn 202546:202547)
[chromium-blink-merge.git] / components / html_viewer / html_frame_tree_manager.cc
blob2c204f01354236d13989734316060581383ce1aa
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(const mojo::Array<web_view::FrameDataPtr>& frame_data,
34 uint32_t frame_id,
35 size_t* index) {
36 for (size_t i = 0; i < frame_data.size(); ++i) {
37 if (frame_data[i]->frame_id == frame_id) {
38 *index = i;
39 return true;
42 return false;
45 } // namespace
47 // Object that calls OnHTMLFrameTreeManagerChangeIdAdvanced() from the
48 // destructor.
49 class HTMLFrameTreeManager::ChangeIdAdvancedNotifier {
50 public:
51 explicit ChangeIdAdvancedNotifier(
52 base::ObserverList<HTMLFrameTreeManagerObserver>* observers)
53 : observers_(observers) {}
54 ~ChangeIdAdvancedNotifier() {
55 FOR_EACH_OBSERVER(HTMLFrameTreeManagerObserver, *observers_,
56 OnHTMLFrameTreeManagerChangeIdAdvanced());
59 private:
60 base::ObserverList<HTMLFrameTreeManagerObserver>* observers_;
62 DISALLOW_COPY_AND_ASSIGN(ChangeIdAdvancedNotifier);
65 // static
66 HTMLFrameTreeManager::TreeMap* HTMLFrameTreeManager::instances_ = nullptr;
68 // static
69 HTMLFrame* HTMLFrameTreeManager::CreateFrameAndAttachToTree(
70 GlobalState* global_state,
71 mus::View* view,
72 scoped_ptr<DocumentResourceWaiter> resource_waiter,
73 HTMLFrameDelegate* delegate) {
74 if (!instances_)
75 instances_ = new TreeMap;
77 mojo::InterfaceRequest<web_view::FrameTreeClient> frame_tree_client_request;
78 web_view::FrameTreeServerPtr frame_tree_server;
79 mojo::Array<web_view::FrameDataPtr> frame_data;
80 uint32_t change_id;
81 uint32_t view_id;
82 web_view::ViewConnectType view_connect_type;
83 web_view::FrameTreeClient::OnConnectCallback on_connect_callback;
84 resource_waiter->Release(&frame_tree_client_request, &frame_tree_server,
85 &frame_data, &change_id, &view_id,
86 &view_connect_type, &on_connect_callback);
87 resource_waiter.reset();
89 on_connect_callback.Run();
91 HTMLFrameTreeManager* frame_tree =
92 FindFrameTreeWithRoot(frame_data[0]->frame_id);
94 DCHECK(!frame_tree || change_id <= frame_tree->change_id_);
96 if (view_connect_type == web_view::VIEW_CONNECT_TYPE_USE_EXISTING &&
97 !frame_tree) {
98 DVLOG(1) << "was told to use existing view but do not have frame tree";
99 return nullptr;
102 if (!frame_tree) {
103 frame_tree = new HTMLFrameTreeManager(global_state);
104 frame_tree->Init(delegate, view, frame_data, change_id);
105 if (frame_data[0]->frame_id == view->id())
106 (*instances_)[frame_data[0]->frame_id] = frame_tree;
107 } else if (view_connect_type == web_view::VIEW_CONNECT_TYPE_USE_EXISTING) {
108 HTMLFrame* existing_frame = frame_tree->root_->FindFrame(view_id);
109 if (!existing_frame) {
110 DVLOG(1) << "was told to use existing view but could not find view";
111 return nullptr;
113 if (!existing_frame->IsLocal()) {
114 DVLOG(1) << "was told to use existing view, but frame is remote";
115 return nullptr;
117 existing_frame->SwapDelegate(delegate);
118 } else {
119 // We're going to share a frame tree. We should know about the frame.
120 CHECK(view->id() != frame_data[0]->frame_id);
121 HTMLFrame* existing_frame = frame_tree->root_->FindFrame(view->id());
122 if (existing_frame) {
123 CHECK(!existing_frame->IsLocal());
124 size_t frame_data_index = 0u;
125 CHECK(FindFrameDataIndex(frame_data, view->id(), &frame_data_index));
126 const web_view::FrameDataPtr& data = frame_data[frame_data_index];
127 existing_frame->SwapToLocal(delegate, view, data->client_properties);
128 } else {
129 // If we can't find the frame and the change_id of the incoming
130 // tree is before the change id we've processed, then we removed the
131 // frame and need do nothing.
132 if (change_id < frame_tree->change_id_)
133 return nullptr;
135 // We removed the frame but it hasn't been acked yet.
136 if (frame_tree->pending_remove_ids_.count(view->id()))
137 return nullptr;
139 // We don't know about the frame, but should. Something is wrong.
140 DVLOG(1) << "unable to locate frame to attach to";
141 return nullptr;
145 HTMLFrame* frame = frame_tree->root_->FindFrame(view_id);
146 DCHECK(frame);
147 frame->Bind(frame_tree_server.Pass(), frame_tree_client_request.Pass());
148 return frame;
151 // static
152 HTMLFrameTreeManager* HTMLFrameTreeManager::FindFrameTreeWithRoot(
153 uint32_t root_frame_id) {
154 return (!base::CommandLine::ForCurrentProcess()->HasSwitch(
155 web_view::switches::kOOPIFAlwaysCreateNewFrameTree) &&
156 instances_ && instances_->count(root_frame_id))
157 ? (*instances_)[root_frame_id]
158 : nullptr;
161 blink::WebView* HTMLFrameTreeManager::GetWebView() {
162 return root_->web_view();
165 void HTMLFrameTreeManager::AddObserver(HTMLFrameTreeManagerObserver* observer) {
166 observers_.AddObserver(observer);
169 void HTMLFrameTreeManager::RemoveObserver(
170 HTMLFrameTreeManagerObserver* observer) {
171 observers_.RemoveObserver(observer);
174 void HTMLFrameTreeManager::OnFrameDestroyed(HTMLFrame* frame) {
175 if (frame == root_)
176 root_ = nullptr;
178 if (frame == local_root_)
179 local_root_ = nullptr;
181 if (!in_process_on_frame_removed_)
182 pending_remove_ids_.insert(frame->id());
184 if (!local_root_ || !local_root_->HasLocalDescendant())
185 delete this;
188 HTMLFrameTreeManager::HTMLFrameTreeManager(GlobalState* global_state)
189 : global_state_(global_state),
190 root_(nullptr),
191 local_root_(nullptr),
192 change_id_(0u),
193 in_process_on_frame_removed_(false),
194 weak_factory_(this) {}
196 HTMLFrameTreeManager::~HTMLFrameTreeManager() {
197 DCHECK(!root_ || !local_root_);
198 RemoveFromInstances();
200 FOR_EACH_OBSERVER(HTMLFrameTreeManagerObserver, observers_,
201 OnHTMLFrameTreeManagerDestroyed());
204 void HTMLFrameTreeManager::Init(
205 HTMLFrameDelegate* delegate,
206 mus::View* local_view,
207 const mojo::Array<web_view::FrameDataPtr>& frame_data,
208 uint32_t change_id) {
209 change_id_ = change_id;
210 root_ = BuildFrameTree(delegate, frame_data, local_view->id(), local_view);
211 local_root_ = root_->FindFrame(local_view->id());
212 CHECK(local_root_);
213 local_root_->UpdateFocus();
216 HTMLFrame* HTMLFrameTreeManager::BuildFrameTree(
217 HTMLFrameDelegate* delegate,
218 const mojo::Array<web_view::FrameDataPtr>& frame_data,
219 uint32_t local_frame_id,
220 mus::View* local_view) {
221 std::vector<HTMLFrame*> parents;
222 HTMLFrame* root = nullptr;
223 HTMLFrame* last_frame = nullptr;
224 for (size_t i = 0; i < frame_data.size(); ++i) {
225 if (last_frame && frame_data[i]->parent_id == last_frame->id()) {
226 parents.push_back(last_frame);
227 } else if (!parents.empty()) {
228 while (parents.back()->id() != frame_data[i]->parent_id)
229 parents.pop_back();
231 HTMLFrame::CreateParams params(this,
232 !parents.empty() ? parents.back() : nullptr,
233 frame_data[i]->frame_id, local_view,
234 frame_data[i]->client_properties, nullptr);
235 if (frame_data[i]->frame_id == local_frame_id)
236 params.delegate = delegate;
238 HTMLFrame* frame = delegate->GetHTMLFactory()->CreateHTMLFrame(&params);
239 if (!last_frame)
240 root = frame;
241 else
242 DCHECK(frame->parent());
243 last_frame = frame;
245 return root;
248 void HTMLFrameTreeManager::RemoveFromInstances() {
249 for (auto pair : *instances_) {
250 if (pair.second == this) {
251 instances_->erase(pair.first);
252 return;
257 bool HTMLFrameTreeManager::PrepareForStructureChange(HTMLFrame* source,
258 uint32_t change_id) {
259 // The change ids may differ if multiple HTMLDocuments are attached to the
260 // same tree (which means we have multiple FrameTreeClients for the same
261 // tree).
262 if (change_id != (change_id_ + 1))
263 return false;
265 // We only process changes for the topmost local root.
266 if (source != local_root_)
267 return false;
269 // Update the id as the change is going to be applied (or we can assume it
270 // will be applied if we get here).
271 change_id_ = change_id;
272 return true;
275 void HTMLFrameTreeManager::ProcessOnFrameAdded(
276 HTMLFrame* source,
277 uint32_t change_id,
278 web_view::FrameDataPtr frame_data) {
279 if (!PrepareForStructureChange(source, change_id))
280 return;
282 ChangeIdAdvancedNotifier notifier(&observers_);
284 HTMLFrame* parent = root_->FindFrame(frame_data->parent_id);
285 if (!parent) {
286 DVLOG(1) << "Received invalid parent in OnFrameAdded "
287 << frame_data->parent_id;
288 return;
290 if (root_->FindFrame(frame_data->frame_id)) {
291 DVLOG(1) << "Child with id already exists in OnFrameAdded "
292 << frame_data->frame_id;
293 return;
296 // Because notification is async it's entirely possible for us to create a
297 // new frame, and remove it before we get the add from the server. This check
298 // ensures we don't add back a frame we explicitly removed.
299 if (pending_remove_ids_.count(frame_data->frame_id))
300 return;
302 HTMLFrame::CreateParams params(this, parent, frame_data->frame_id, nullptr,
303 frame_data->client_properties, nullptr);
304 // |parent| takes ownership of created HTMLFrame.
305 source->GetFirstAncestorWithDelegate()
306 ->delegate_->GetHTMLFactory()
307 ->CreateHTMLFrame(&params);
310 void HTMLFrameTreeManager::ProcessOnFrameRemoved(HTMLFrame* source,
311 uint32_t change_id,
312 uint32_t frame_id) {
313 if (!PrepareForStructureChange(source, change_id))
314 return;
316 ChangeIdAdvancedNotifier notifier(&observers_);
318 pending_remove_ids_.erase(frame_id);
320 HTMLFrame* frame = root_->FindFrame(frame_id);
321 if (!frame) {
322 DVLOG(1) << "OnFrameRemoved with unknown frame " << frame_id;
323 return;
326 // We shouldn't see requests to remove the root.
327 if (frame == root_) {
328 DVLOG(1) << "OnFrameRemoved supplied root; ignoring";
329 return;
332 // Requests to remove local frames are followed by the View being destroyed.
333 // We handle destruction there.
334 if (frame->IsLocal())
335 return;
337 DCHECK(!in_process_on_frame_removed_);
338 in_process_on_frame_removed_ = true;
339 base::WeakPtr<HTMLFrameTreeManager> ref(weak_factory_.GetWeakPtr());
340 frame->Close();
341 if (!ref)
342 return; // We were deleted.
344 in_process_on_frame_removed_ = false;
347 void HTMLFrameTreeManager::ProcessOnFrameClientPropertyChanged(
348 HTMLFrame* source,
349 uint32_t frame_id,
350 const mojo::String& name,
351 mojo::Array<uint8_t> new_data) {
352 if (source != local_root_)
353 return;
355 HTMLFrame* frame = root_->FindFrame(frame_id);
356 if (frame)
357 frame->SetValueFromClientProperty(name, new_data.Pass());
360 } // namespace mojo