Add Smart Lock warm welcome on Windows.
[chromium-blink-merge.git] / components / html_viewer / html_frame_tree_manager.cc
blob7de88a19f6208526e50646c9146069398da806ac
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/view_manager/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 mojo::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 if (view_connect_type == web_view::VIEW_CONNECT_TYPE_USE_EXISTING &&
95 !frame_tree) {
96 DVLOG(1) << "was told to use existing view but do not have frame tree";
97 return nullptr;
100 if (!frame_tree) {
101 frame_tree = new HTMLFrameTreeManager(global_state);
102 frame_tree->Init(delegate, view, frame_data, change_id);
103 if (frame_data[0]->frame_id == view->id())
104 (*instances_)[frame_data[0]->frame_id] = frame_tree;
105 } else if (view_connect_type == web_view::VIEW_CONNECT_TYPE_USE_EXISTING) {
106 HTMLFrame* existing_frame = frame_tree->root_->FindFrame(view_id);
107 if (!existing_frame) {
108 DVLOG(1) << "was told to use existing view but could not find view";
109 return nullptr;
111 if (!existing_frame->IsLocal()) {
112 DVLOG(1) << "was told to use existing view, but frame is remote";
113 return nullptr;
115 existing_frame->SwapDelegate(delegate);
116 } else {
117 // We're going to share a frame tree. There are two possibilities:
118 // . We already know about the frame, in which case we swap it to local.
119 // . We don't know about the frame (most likely because of timing issues),
120 // but we better know about the parent. Create a new frame for it.
121 CHECK(view->id() != frame_data[0]->frame_id);
122 HTMLFrame* existing_frame = frame_tree->root_->FindFrame(view->id());
123 size_t frame_data_index = 0u;
124 CHECK(FindFrameDataIndex(frame_data, view->id(), &frame_data_index));
125 const web_view::FrameDataPtr& data = frame_data[frame_data_index];
126 if (existing_frame) {
127 CHECK(!existing_frame->IsLocal());
128 existing_frame->SwapToLocal(delegate, view, data->client_properties);
129 } else {
130 // If we can't find the frame and the change_id of the incoming
131 // tree is before the change id we've processed, then we removed the
132 // frame and need do nothing.
133 if (change_id < frame_tree->change_id_)
134 return nullptr;
136 // We removed the frame but it hasn't been acked yet.
137 if (frame_tree->pending_remove_ids_.count(view->id()))
138 return nullptr;
140 // TODO(sky): if change_id > frame_tree->change_id_ then this needs
141 // to wait and bind once the change has been processed.
143 HTMLFrame* parent = frame_tree->root_->FindFrame(data->parent_id);
144 CHECK(parent);
145 HTMLFrame::CreateParams params(frame_tree, parent, view->id(), view,
146 data->client_properties, delegate);
147 delegate->GetHTMLFactory()->CreateHTMLFrame(&params);
151 HTMLFrame* frame = frame_tree->root_->FindFrame(view_id);
152 DCHECK(frame);
153 frame->Bind(frame_tree_server.Pass(), frame_tree_client_request.Pass());
154 return frame;
157 // static
158 HTMLFrameTreeManager* HTMLFrameTreeManager::FindFrameTreeWithRoot(
159 uint32_t root_frame_id) {
160 return (!base::CommandLine::ForCurrentProcess()->HasSwitch(
161 web_view::switches::kOOPIFAlwaysCreateNewFrameTree) &&
162 instances_ && instances_->count(root_frame_id))
163 ? (*instances_)[root_frame_id]
164 : nullptr;
167 blink::WebView* HTMLFrameTreeManager::GetWebView() {
168 return root_->web_view();
171 void HTMLFrameTreeManager::AddObserver(HTMLFrameTreeManagerObserver* observer) {
172 observers_.AddObserver(observer);
175 void HTMLFrameTreeManager::RemoveObserver(
176 HTMLFrameTreeManagerObserver* observer) {
177 observers_.RemoveObserver(observer);
180 void HTMLFrameTreeManager::OnFrameDestroyed(HTMLFrame* frame) {
181 if (frame == root_)
182 root_ = nullptr;
184 if (frame == local_root_)
185 local_root_ = nullptr;
187 if (!in_process_on_frame_removed_)
188 pending_remove_ids_.insert(frame->id());
190 if (!local_root_ || !local_root_->HasLocalDescendant())
191 delete this;
194 HTMLFrameTreeManager::HTMLFrameTreeManager(GlobalState* global_state)
195 : global_state_(global_state),
196 root_(nullptr),
197 local_root_(nullptr),
198 change_id_(0u),
199 in_process_on_frame_removed_(false),
200 weak_factory_(this) {}
202 HTMLFrameTreeManager::~HTMLFrameTreeManager() {
203 DCHECK(!root_ || !local_root_);
204 RemoveFromInstances();
206 FOR_EACH_OBSERVER(HTMLFrameTreeManagerObserver, observers_,
207 OnHTMLFrameTreeManagerDestroyed());
210 void HTMLFrameTreeManager::Init(
211 HTMLFrameDelegate* delegate,
212 mojo::View* local_view,
213 const mojo::Array<web_view::FrameDataPtr>& frame_data,
214 uint32_t change_id) {
215 change_id_ = change_id;
216 root_ = BuildFrameTree(delegate, frame_data, local_view->id(), local_view);
217 local_root_ = root_->FindFrame(local_view->id());
218 CHECK(local_root_);
219 local_root_->UpdateFocus();
222 HTMLFrame* HTMLFrameTreeManager::BuildFrameTree(
223 HTMLFrameDelegate* delegate,
224 const mojo::Array<web_view::FrameDataPtr>& frame_data,
225 uint32_t local_frame_id,
226 mojo::View* local_view) {
227 std::vector<HTMLFrame*> parents;
228 HTMLFrame* root = nullptr;
229 HTMLFrame* last_frame = nullptr;
230 for (size_t i = 0; i < frame_data.size(); ++i) {
231 if (last_frame && frame_data[i]->parent_id == last_frame->id()) {
232 parents.push_back(last_frame);
233 } else if (!parents.empty()) {
234 while (parents.back()->id() != frame_data[i]->parent_id)
235 parents.pop_back();
237 HTMLFrame::CreateParams params(this,
238 !parents.empty() ? parents.back() : nullptr,
239 frame_data[i]->frame_id, local_view,
240 frame_data[i]->client_properties, nullptr);
241 if (frame_data[i]->frame_id == local_frame_id)
242 params.delegate = delegate;
244 HTMLFrame* frame = delegate->GetHTMLFactory()->CreateHTMLFrame(&params);
245 if (!last_frame)
246 root = frame;
247 else
248 DCHECK(frame->parent());
249 last_frame = frame;
251 return root;
254 void HTMLFrameTreeManager::RemoveFromInstances() {
255 for (auto pair : *instances_) {
256 if (pair.second == this) {
257 instances_->erase(pair.first);
258 return;
263 bool HTMLFrameTreeManager::PrepareForStructureChange(HTMLFrame* source,
264 uint32_t change_id) {
265 // The change ids may differ if multiple HTMLDocuments are attached to the
266 // same tree (which means we have multiple FrameTreeClients for the same
267 // tree).
268 if (change_id != (change_id_ + 1))
269 return false;
271 // We only process changes for the topmost local root.
272 if (source != local_root_)
273 return false;
275 // Update the id as the change is going to be applied (or we can assume it
276 // will be applied if we get here).
277 change_id_ = change_id;
278 return true;
281 void HTMLFrameTreeManager::ProcessOnFrameAdded(
282 HTMLFrame* source,
283 uint32_t change_id,
284 web_view::FrameDataPtr frame_data) {
285 if (!PrepareForStructureChange(source, change_id))
286 return;
288 ChangeIdAdvancedNotifier notifier(&observers_);
290 HTMLFrame* parent = root_->FindFrame(frame_data->parent_id);
291 if (!parent) {
292 DVLOG(1) << "Received invalid parent in OnFrameAdded "
293 << frame_data->parent_id;
294 return;
296 if (root_->FindFrame(frame_data->frame_id)) {
297 DVLOG(1) << "Child with id already exists in OnFrameAdded "
298 << frame_data->frame_id;
299 return;
302 // Because notification is async it's entirely possible for us to create a
303 // new frame, and remove it before we get the add from the server. This check
304 // ensures we don't add back a frame we explicitly removed.
305 if (pending_remove_ids_.count(frame_data->frame_id))
306 return;
308 HTMLFrame::CreateParams params(this, parent, frame_data->frame_id, nullptr,
309 frame_data->client_properties, nullptr);
310 // |parent| takes ownership of created HTMLFrame.
311 source->GetLocalRoot()->delegate_->GetHTMLFactory()->CreateHTMLFrame(&params);
314 void HTMLFrameTreeManager::ProcessOnFrameRemoved(HTMLFrame* source,
315 uint32_t change_id,
316 uint32_t frame_id) {
317 if (!PrepareForStructureChange(source, change_id))
318 return;
320 ChangeIdAdvancedNotifier notifier(&observers_);
322 pending_remove_ids_.erase(frame_id);
324 HTMLFrame* frame = root_->FindFrame(frame_id);
325 if (!frame) {
326 DVLOG(1) << "OnFrameRemoved with unknown frame " << frame_id;
327 return;
330 // We shouldn't see requests to remove the root.
331 if (frame == root_) {
332 DVLOG(1) << "OnFrameRemoved supplied root; ignoring";
333 return;
336 // Requests to remove local frames are followed by the View being destroyed.
337 // We handle destruction there.
338 if (frame->IsLocal())
339 return;
341 DCHECK(!in_process_on_frame_removed_);
342 in_process_on_frame_removed_ = true;
343 base::WeakPtr<HTMLFrameTreeManager> ref(weak_factory_.GetWeakPtr());
344 frame->Close();
345 if (!ref)
346 return; // We were deleted.
348 in_process_on_frame_removed_ = false;
351 void HTMLFrameTreeManager::ProcessOnFrameClientPropertyChanged(
352 HTMLFrame* source,
353 uint32_t frame_id,
354 const mojo::String& name,
355 mojo::Array<uint8_t> new_data) {
356 if (source != local_root_)
357 return;
359 HTMLFrame* frame = root_->FindFrame(frame_id);
360 if (frame)
361 frame->SetValueFromClientProperty(name, new_data.Pass());
364 } // namespace mojo