Roll src/third_party/skia d32087a:1052f51
[chromium-blink-merge.git] / components / web_view / frame.cc
blob83832a8de1fef8fb855951f4c348ea3b93d8e4ba
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/web_view/frame.h"
7 #include <algorithm>
9 #include "base/auto_reset.h"
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/stl_util.h"
13 #include "components/mus/public/cpp/view.h"
14 #include "components/mus/public/cpp/view_property.h"
15 #include "components/web_view/frame_tree.h"
16 #include "components/web_view/frame_tree_delegate.h"
17 #include "components/web_view/frame_user_data.h"
18 #include "components/web_view/frame_utils.h"
19 #include "mojo/application/public/interfaces/shell.mojom.h"
21 using mus::View;
23 DECLARE_VIEW_PROPERTY_TYPE(web_view::Frame*);
25 namespace web_view {
27 // Used to find the Frame associated with a View.
28 DEFINE_LOCAL_VIEW_PROPERTY_KEY(Frame*, kFrame, nullptr);
30 namespace {
32 const uint32_t kNoParentId = 0u;
33 const mus::ConnectionSpecificId kInvalidConnectionId = 0u;
35 FrameDataPtr FrameToFrameData(const Frame* frame) {
36 FrameDataPtr frame_data(FrameData::New());
37 frame_data->frame_id = frame->id();
38 frame_data->parent_id = frame->parent() ? frame->parent()->id() : kNoParentId;
39 frame_data->client_properties =
40 mojo::Map<mojo::String, mojo::Array<uint8_t>>::From(
41 frame->client_properties());
42 return frame_data.Pass();
45 } // namespace
47 struct Frame::FrameTreeServerBinding {
48 scoped_ptr<FrameUserData> user_data;
49 scoped_ptr<mojo::Binding<FrameTreeServer>> frame_tree_server_binding;
52 Frame::Frame(FrameTree* tree,
53 View* view,
54 uint32_t frame_id,
55 uint32_t app_id,
56 ViewOwnership view_ownership,
57 FrameTreeClient* frame_tree_client,
58 scoped_ptr<FrameUserData> user_data,
59 const ClientPropertyMap& client_properties)
60 : tree_(tree),
61 view_(nullptr),
62 embedded_connection_id_(kInvalidConnectionId),
63 id_(frame_id),
64 app_id_(app_id),
65 parent_(nullptr),
66 view_ownership_(view_ownership),
67 user_data_(user_data.Pass()),
68 frame_tree_client_(frame_tree_client),
69 loading_(false),
70 progress_(0.f),
71 client_properties_(client_properties),
72 embed_weak_ptr_factory_(this),
73 navigate_weak_ptr_factory_(this) {
74 if (view)
75 SetView(view);
78 Frame::~Frame() {
79 if (view_)
80 view_->RemoveObserver(this);
81 while (!children_.empty())
82 delete children_[0];
83 if (parent_)
84 parent_->Remove(this);
85 if (view_) {
86 view_->ClearLocalProperty(kFrame);
87 if (view_ownership_ == ViewOwnership::OWNS_VIEW)
88 view_->Destroy();
90 tree_->delegate_->DidDestroyFrame(this);
93 void Frame::Init(Frame* parent,
94 mojo::ViewTreeClientPtr view_tree_client,
95 mojo::InterfaceRequest<FrameTreeServer> server_request) {
97 // Set the FrameTreeClient to null so that we don't notify the client of the
98 // add before OnConnect().
99 base::AutoReset<FrameTreeClient*> frame_tree_client_resetter(
100 &frame_tree_client_, nullptr);
101 if (parent)
102 parent->Add(this);
105 const ClientType client_type = server_request.is_pending()
106 ? ClientType::NEW_CHILD_FRAME
107 : ClientType::EXISTING_FRAME_NEW_APP;
108 InitClient(client_type, nullptr, view_tree_client.Pass(),
109 server_request.Pass());
111 tree_->delegate_->DidCreateFrame(this);
114 // static
115 Frame* Frame::FindFirstFrameAncestor(View* view) {
116 while (view && !view->GetLocalProperty(kFrame))
117 view = view->parent();
118 return view ? view->GetLocalProperty(kFrame) : nullptr;
121 const Frame* Frame::FindFrame(uint32_t id) const {
122 if (id == id_)
123 return this;
125 for (const Frame* child : children_) {
126 const Frame* match = child->FindFrame(id);
127 if (match)
128 return match;
130 return nullptr;
133 bool Frame::HasAncestor(const Frame* frame) const {
134 const Frame* current = this;
135 while (current && current != frame)
136 current = current->parent_;
137 return current == frame;
140 bool Frame::IsLoading() const {
141 if (loading_)
142 return true;
143 for (const Frame* child : children_) {
144 if (child->IsLoading())
145 return true;
147 return false;
150 double Frame::GatherProgress(int* frame_count) const {
151 ++(*frame_count);
152 double progress = progress_;
153 for (const Frame* child : children_)
154 progress += child->GatherProgress(frame_count);
155 return progress_;
158 void Frame::InitClient(
159 ClientType client_type,
160 scoped_ptr<FrameTreeServerBinding> frame_tree_server_binding,
161 mojo::ViewTreeClientPtr view_tree_client,
162 mojo::InterfaceRequest<FrameTreeServer> server_request) {
163 if (client_type == ClientType::EXISTING_FRAME_NEW_APP &&
164 view_tree_client.get()) {
165 embedded_connection_id_ = kInvalidConnectionId;
166 embed_weak_ptr_factory_.InvalidateWeakPtrs();
167 view_->Embed(
168 view_tree_client.Pass(), mojo::ViewTree::ACCESS_POLICY_DEFAULT,
169 base::Bind(&Frame::OnEmbedAck, embed_weak_ptr_factory_.GetWeakPtr()));
172 if (client_type == ClientType::NEW_CHILD_FRAME) {
173 // Don't install an error handler. We allow for the target to only
174 // implement ViewTreeClient.
175 // This frame (and client) was created by an existing FrameTreeClient. There
176 // is no need to send it OnConnect().
177 frame_tree_server_binding_.reset(
178 new mojo::Binding<FrameTreeServer>(this, server_request.Pass()));
179 frame_tree_client_->OnConnect(
180 nullptr, tree_->change_id(), id_, VIEW_CONNECT_TYPE_USE_NEW,
181 mojo::Array<FrameDataPtr>(),
182 base::Bind(&OnConnectAck, base::Passed(&frame_tree_server_binding)));
183 } else {
184 std::vector<const Frame*> frames;
185 tree_->root()->BuildFrameTree(&frames);
187 mojo::Array<FrameDataPtr> array(frames.size());
188 for (size_t i = 0; i < frames.size(); ++i)
189 array[i] = FrameToFrameData(frames[i]).Pass();
191 FrameTreeServerPtr frame_tree_server_ptr;
192 // Don't install an error handler. We allow for the target to only
193 // implement ViewTreeClient.
194 frame_tree_server_binding_.reset(new mojo::Binding<FrameTreeServer>(
195 this, GetProxy(&frame_tree_server_ptr).Pass()));
196 frame_tree_client_->OnConnect(
197 frame_tree_server_ptr.Pass(), tree_->change_id(), id_,
198 client_type == ClientType::EXISTING_FRAME_SAME_APP
199 ? VIEW_CONNECT_TYPE_USE_EXISTING
200 : VIEW_CONNECT_TYPE_USE_NEW,
201 array.Pass(),
202 base::Bind(&OnConnectAck, base::Passed(&frame_tree_server_binding)));
203 tree_->delegate_->DidStartNavigation(this);
207 // static
208 void Frame::OnConnectAck(
209 scoped_ptr<FrameTreeServerBinding> frame_tree_server_binding) {}
211 void Frame::ChangeClient(FrameTreeClient* frame_tree_client,
212 scoped_ptr<FrameUserData> user_data,
213 mojo::ViewTreeClientPtr view_tree_client,
214 uint32_t app_id) {
215 while (!children_.empty())
216 delete children_[0];
218 ClientType client_type = view_tree_client.get() == nullptr
219 ? ClientType::EXISTING_FRAME_SAME_APP
220 : ClientType::EXISTING_FRAME_NEW_APP;
221 scoped_ptr<FrameTreeServerBinding> frame_tree_server_binding;
223 if (client_type == ClientType::EXISTING_FRAME_SAME_APP) {
224 // See comment in InitClient() for details.
225 frame_tree_server_binding.reset(new FrameTreeServerBinding);
226 frame_tree_server_binding->user_data = user_data_.Pass();
227 frame_tree_server_binding->frame_tree_server_binding =
228 frame_tree_server_binding_.Pass();
229 } else {
230 loading_ = false;
231 progress_ = 0.f;
234 user_data_ = user_data.Pass();
235 frame_tree_client_ = frame_tree_client;
236 frame_tree_server_binding_.reset();
237 app_id_ = app_id;
239 InitClient(client_type, frame_tree_server_binding.Pass(),
240 view_tree_client.Pass(), nullptr);
243 void Frame::OnEmbedAck(bool success, mus::ConnectionSpecificId connection_id) {
244 if (success)
245 embedded_connection_id_ = connection_id;
248 void Frame::SetView(mus::View* view) {
249 DCHECK(!view_);
250 DCHECK_EQ(id_, view->id());
251 view_ = view;
252 view_->SetLocalProperty(kFrame, this);
253 view_->AddObserver(this);
254 if (pending_navigate_.get())
255 StartNavigate(pending_navigate_.Pass());
258 void Frame::BuildFrameTree(std::vector<const Frame*>* frames) const {
259 frames->push_back(this);
260 for (const Frame* frame : children_)
261 frame->BuildFrameTree(frames);
264 void Frame::Add(Frame* node) {
265 DCHECK(!node->parent_);
267 node->parent_ = this;
268 children_.push_back(node);
270 tree_->root()->NotifyAdded(this, node, tree_->AdvanceChangeID());
273 void Frame::Remove(Frame* node) {
274 DCHECK_EQ(node->parent_, this);
275 auto iter = std::find(children_.begin(), children_.end(), node);
276 DCHECK(iter != children_.end());
277 node->parent_ = nullptr;
278 children_.erase(iter);
280 tree_->root()->NotifyRemoved(this, node, tree_->AdvanceChangeID());
283 void Frame::StartNavigate(mojo::URLRequestPtr request) {
284 pending_navigate_.reset();
286 // We need a View to navigate. When we get the View we'll complete the
287 // navigation.
288 if (!view_) {
289 pending_navigate_ = request.Pass();
290 return;
293 // Drop any pending navigation requests.
294 navigate_weak_ptr_factory_.InvalidateWeakPtrs();
296 tree_->delegate_->CanNavigateFrame(
297 this, request.Pass(),
298 base::Bind(&Frame::OnCanNavigateFrame,
299 navigate_weak_ptr_factory_.GetWeakPtr()));
302 void Frame::OnCanNavigateFrame(uint32_t app_id,
303 FrameTreeClient* frame_tree_client,
304 scoped_ptr<FrameUserData> user_data,
305 mojo::ViewTreeClientPtr view_tree_client) {
306 if (AreAppIdsEqual(app_id, app_id_)) {
307 // The app currently rendering the frame will continue rendering it. In this
308 // case we do not use the ViewTreeClient (because the app has a View already
309 // and ends up reusing it).
310 DCHECK(!view_tree_client.get());
311 } else {
312 frame_tree_client_->OnWillNavigate();
313 DCHECK(view_tree_client.get());
315 ChangeClient(frame_tree_client, user_data.Pass(), view_tree_client.Pass(),
316 app_id);
319 void Frame::NotifyAdded(const Frame* source,
320 const Frame* added_node,
321 uint32_t change_id) {
322 // |frame_tree_client_| may be null during initial frame creation and
323 // parenting.
324 if (frame_tree_client_)
325 frame_tree_client_->OnFrameAdded(change_id, FrameToFrameData(added_node));
327 for (Frame* child : children_)
328 child->NotifyAdded(source, added_node, change_id);
331 void Frame::NotifyRemoved(const Frame* source,
332 const Frame* removed_node,
333 uint32_t change_id) {
334 frame_tree_client_->OnFrameRemoved(change_id, removed_node->id());
336 for (Frame* child : children_)
337 child->NotifyRemoved(source, removed_node, change_id);
340 void Frame::NotifyClientPropertyChanged(const Frame* source,
341 const mojo::String& name,
342 const mojo::Array<uint8_t>& value) {
343 if (this != source)
344 frame_tree_client_->OnFrameClientPropertyChanged(source->id(), name,
345 value.Clone());
347 for (Frame* child : children_)
348 child->NotifyClientPropertyChanged(source, name, value);
351 void Frame::NotifyFrameLoadingStateChanged(const Frame* frame, bool loading) {
352 frame_tree_client_->OnFrameLoadingStateChanged(frame->id(), loading);
355 void Frame::NotifyDispatchFrameLoadEvent(const Frame* frame) {
356 frame_tree_client_->OnDispatchFrameLoadEvent(frame->id());
359 void Frame::OnTreeChanged(const TreeChangeParams& params) {
360 if (params.new_parent && this == tree_->root()) {
361 Frame* child_frame = FindFrame(params.target->id());
362 if (child_frame && !child_frame->view_)
363 child_frame->SetView(params.target);
367 void Frame::OnViewDestroying(mus::View* view) {
368 if (parent_)
369 parent_->Remove(this);
371 // Reset |view_ownership_| so we don't attempt to delete |view_| in the
372 // destructor.
373 view_ownership_ = ViewOwnership::DOESNT_OWN_VIEW;
375 if (tree_->root() == this) {
376 view_->RemoveObserver(this);
377 view_ = nullptr;
378 return;
381 delete this;
384 void Frame::OnViewEmbeddedAppDisconnected(mus::View* view) {
385 // See FrameTreeDelegate::OnViewEmbeddedAppDisconnected() for details of when
386 // this happens.
388 // Currently we have no way to distinguish between the cases that lead to this
389 // being called, so we assume we can continue on. Continuing on is important
390 // for html as it's entirely possible for a page to create a frame, navigate
391 // to a bogus url and expect the frame to still exist.
392 tree_->delegate_->OnViewEmbeddedInFrameDisconnected(this);
395 void Frame::PostMessageEventToFrame(uint32_t target_frame_id,
396 HTMLMessageEventPtr event) {
397 // NOTE: |target_frame_id| is allowed to be from another connection.
398 Frame* target = tree_->root()->FindFrame(target_frame_id);
399 if (!target || target == this ||
400 !tree_->delegate_->CanPostMessageEventToFrame(this, target, event.get()))
401 return;
403 target->frame_tree_client_->OnPostMessageEvent(id_, target_frame_id,
404 event.Pass());
407 void Frame::LoadingStateChanged(bool loading, double progress) {
408 bool loading_state_changed = loading_ != loading;
409 loading_ = loading;
410 progress_ = progress;
411 tree_->LoadingStateChanged();
413 if (loading_state_changed && parent_ &&
414 !AreAppIdsEqual(app_id_, parent_->app_id_)) {
415 // We need to notify the parent if it is in a different app, so that it can
416 // keep track of this frame's loading state. If the parent is in the same
417 // app, we assume that the loading state is propagated directly within the
418 // app itself and no notification is needed from our side.
419 parent_->NotifyFrameLoadingStateChanged(this, loading_);
423 void Frame::TitleChanged(const mojo::String& title) {
424 // Only care about title changes on the root frame.
425 if (!parent_)
426 tree_->TitleChanged(title);
429 void Frame::DidCommitProvisionalLoad() {
430 tree_->DidCommitProvisionalLoad(this);
433 void Frame::SetClientProperty(const mojo::String& name,
434 mojo::Array<uint8_t> value) {
435 auto iter = client_properties_.find(name);
436 const bool already_in_map = (iter != client_properties_.end());
437 if (value.is_null()) {
438 if (!already_in_map)
439 return;
440 client_properties_.erase(iter);
441 } else {
442 std::vector<uint8_t> as_vector(value.To<std::vector<uint8_t>>());
443 if (already_in_map && iter->second == as_vector)
444 return;
445 client_properties_[name] = as_vector;
447 tree_->ClientPropertyChanged(this, name, value);
450 void Frame::OnCreatedFrame(
451 mojo::InterfaceRequest<FrameTreeServer> server_request,
452 FrameTreeClientPtr client,
453 uint32_t frame_id,
454 mojo::Map<mojo::String, mojo::Array<uint8_t>> client_properties) {
455 if ((frame_id >> 16) != embedded_connection_id_) {
456 // TODO(sky): kill connection here?
457 // TODO(sky): there is a race in that there is no guarantee we received the
458 // connection id before the frame tries to create a new frame. Ideally we
459 // could pause the frame until we get the connection id, but bindings don't
460 // offer such an API.
461 DVLOG(1) << "OnCreatedFrame supplied invalid frame id, expecting"
462 << embedded_connection_id_;
463 return;
466 if (FindFrame(frame_id)) {
467 // TODO(sky): kill connection here?
468 DVLOG(1) << "OnCreatedFrame supplied id of existing frame.";
469 return;
472 Frame* child_frame = tree_->CreateChildFrame(
473 this, server_request.Pass(), client.Pass(), frame_id, app_id_,
474 client_properties.To<ClientPropertyMap>());
475 child_frame->embedded_connection_id_ = embedded_connection_id_;
478 void Frame::RequestNavigate(NavigationTargetType target_type,
479 uint32_t target_frame_id,
480 mojo::URLRequestPtr request) {
481 if (target_type == NAVIGATION_TARGET_TYPE_EXISTING_FRAME) {
482 // |target_frame| is allowed to come from another connection.
483 Frame* target_frame = tree_->root()->FindFrame(target_frame_id);
484 if (!target_frame) {
485 DVLOG(1) << "RequestNavigate EXISTING_FRAME with no matching frame";
486 return;
488 if (target_frame != tree_->root()) {
489 target_frame->StartNavigate(request.Pass());
490 return;
492 // Else case if |target_frame| == root. Treat at top level request.
494 tree_->delegate_->NavigateTopLevel(this, request.Pass());
497 void Frame::DidNavigateLocally(const mojo::String& url) {
498 NOTIMPLEMENTED();
501 void Frame::DispatchLoadEventToParent() {
502 if (parent_ && !AreAppIdsEqual(app_id_, parent_->app_id_)) {
503 // Send notification to fire a load event in the parent, if the parent is in
504 // a different app. If the parent is in the same app, we assume that the app
505 // itself handles firing load event directly and no notification is needed
506 // from our side.
507 parent_->NotifyDispatchFrameLoadEvent(this);
511 } // namespace web_view