Roll src/third_party/WebKit 3aea697:d9c6159 (svn 201973:201974)
[chromium-blink-merge.git] / components / web_view / frame.cc
blobad6e818d2b767833eb124fa33c29d49e3123a378
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/view_manager/public/cpp/view.h"
14 #include "components/view_manager/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 "mojo/application/public/interfaces/shell.mojom.h"
20 using mojo::View;
22 DECLARE_VIEW_PROPERTY_TYPE(web_view::Frame*);
24 namespace web_view {
26 // Used to find the Frame associated with a View.
27 DEFINE_LOCAL_VIEW_PROPERTY_KEY(Frame*, kFrame, nullptr);
29 namespace {
31 const uint32_t kNoParentId = 0u;
33 FrameDataPtr FrameToFrameData(const Frame* frame) {
34 FrameDataPtr frame_data(FrameData::New());
35 frame_data->frame_id = frame->id();
36 frame_data->parent_id = frame->parent() ? frame->parent()->id() : kNoParentId;
37 frame_data->client_properties =
38 mojo::Map<mojo::String, mojo::Array<uint8_t>>::From(
39 frame->client_properties());
40 return frame_data.Pass();
43 } // namespace
45 struct Frame::FrameTreeServerBinding {
46 scoped_ptr<FrameUserData> user_data;
47 scoped_ptr<mojo::Binding<FrameTreeServer>> frame_tree_server_binding;
50 Frame::Frame(FrameTree* tree,
51 View* view,
52 uint32_t frame_id,
53 uint32_t app_id,
54 ViewOwnership view_ownership,
55 FrameTreeClient* frame_tree_client,
56 scoped_ptr<FrameUserData> user_data,
57 const ClientPropertyMap& client_properties)
58 : tree_(tree),
59 view_(nullptr),
60 id_(frame_id),
61 app_id_(app_id),
62 parent_(nullptr),
63 view_ownership_(view_ownership),
64 user_data_(user_data.Pass()),
65 frame_tree_client_(frame_tree_client),
66 loading_(false),
67 progress_(0.f),
68 client_properties_(client_properties),
69 weak_factory_(this),
70 navigate_weak_ptr_factory_(this) {
71 if (view)
72 SetView(view);
75 Frame::~Frame() {
76 if (view_)
77 view_->RemoveObserver(this);
78 while (!children_.empty())
79 delete children_[0];
80 if (parent_)
81 parent_->Remove(this);
82 if (view_) {
83 view_->ClearLocalProperty(kFrame);
84 if (view_ownership_ == ViewOwnership::OWNS_VIEW)
85 view_->Destroy();
89 void Frame::Init(Frame* parent) {
91 // Set the FrameTreeClient to null so that we don't notify the client of the
92 // add before OnConnect().
93 base::AutoReset<FrameTreeClient*> frame_tree_client_resetter(
94 &frame_tree_client_, nullptr);
95 if (parent)
96 parent->Add(this);
99 InitClient(ClientType::NEW_APP, nullptr);
102 // static
103 Frame* Frame::FindFirstFrameAncestor(View* view) {
104 while (view && !view->GetLocalProperty(kFrame))
105 view = view->parent();
106 return view ? view->GetLocalProperty(kFrame) : nullptr;
109 const Frame* Frame::FindFrame(uint32_t id) const {
110 if (id == id_)
111 return this;
113 for (const Frame* child : children_) {
114 const Frame* match = child->FindFrame(id);
115 if (match)
116 return match;
118 return nullptr;
121 bool Frame::HasAncestor(const Frame* frame) const {
122 const Frame* current = this;
123 while (current && current != frame)
124 current = current->parent_;
125 return current == frame;
128 bool Frame::IsLoading() const {
129 if (loading_)
130 return true;
131 for (const Frame* child : children_) {
132 if (child->IsLoading())
133 return true;
135 return false;
138 double Frame::GatherProgress(int* frame_count) const {
139 ++(*frame_count);
140 double progress = progress_;
141 for (const Frame* child : children_)
142 progress += child->GatherProgress(frame_count);
143 return progress_;
146 void Frame::InitClient(
147 ClientType client_type,
148 scoped_ptr<FrameTreeServerBinding> frame_tree_server_binding) {
149 std::vector<const Frame*> frames;
150 tree_->root()->BuildFrameTree(&frames);
152 mojo::Array<FrameDataPtr> array(frames.size());
153 for (size_t i = 0; i < frames.size(); ++i)
154 array[i] = FrameToFrameData(frames[i]).Pass();
156 // TODO(sky): error handling.
157 FrameTreeServerPtr frame_tree_server_ptr;
158 frame_tree_server_binding_.reset(new mojo::Binding<FrameTreeServer>(
159 this, GetProxy(&frame_tree_server_ptr).Pass()));
160 if (frame_tree_client_) {
161 frame_tree_client_->OnConnect(
162 frame_tree_server_ptr.Pass(), tree_->change_id(), view_->id(),
163 client_type == ClientType::SAME_APP ? VIEW_CONNECT_TYPE_USE_EXISTING
164 : VIEW_CONNECT_TYPE_USE_NEW,
165 array.Pass(),
166 base::Bind(&OnConnectAck, base::Passed(&frame_tree_server_binding)));
167 tree_->delegate_->DidStartNavigation(this);
171 // static
172 void Frame::OnConnectAck(
173 scoped_ptr<FrameTreeServerBinding> frame_tree_server_binding) {}
175 void Frame::ChangeClient(FrameTreeClient* frame_tree_client,
176 scoped_ptr<FrameUserData> user_data,
177 mojo::ViewTreeClientPtr view_tree_client,
178 uint32_t app_id) {
179 while (!children_.empty())
180 delete children_[0];
182 ClientType client_type = view_tree_client.get() == nullptr
183 ? ClientType::SAME_APP
184 : ClientType::NEW_APP;
185 scoped_ptr<FrameTreeServerBinding> frame_tree_server_binding;
187 if (client_type == ClientType::SAME_APP) {
188 // See comment in InitClient() for details.
189 frame_tree_server_binding.reset(new FrameTreeServerBinding);
190 frame_tree_server_binding->user_data = user_data_.Pass();
191 frame_tree_server_binding->frame_tree_server_binding =
192 frame_tree_server_binding_.Pass();
195 user_data_ = user_data.Pass();
196 frame_tree_client_ = frame_tree_client;
197 frame_tree_server_binding_.reset();
198 loading_ = false;
199 progress_ = 0.f;
200 app_id_ = app_id;
202 if (client_type == ClientType::NEW_APP)
203 view_->Embed(view_tree_client.Pass());
205 InitClient(client_type, frame_tree_server_binding.Pass());
208 void Frame::SetView(mojo::View* view) {
209 DCHECK(!view_);
210 DCHECK_EQ(id_, view->id());
211 view_ = view;
212 view_->SetLocalProperty(kFrame, this);
213 view_->AddObserver(this);
214 if (pending_navigate_.get())
215 StartNavigate(pending_navigate_.Pass());
218 Frame* Frame::GetAncestorWithFrameTreeClient() {
219 Frame* frame = this;
220 while (frame && !frame->frame_tree_client_)
221 frame = frame->parent_;
222 return frame;
225 void Frame::BuildFrameTree(std::vector<const Frame*>* frames) const {
226 frames->push_back(this);
227 for (const Frame* frame : children_)
228 frame->BuildFrameTree(frames);
231 void Frame::Add(Frame* node) {
232 DCHECK(!node->parent_);
234 node->parent_ = this;
235 children_.push_back(node);
237 tree_->root()->NotifyAdded(this, node, tree_->AdvanceChangeID());
240 void Frame::Remove(Frame* node) {
241 DCHECK_EQ(node->parent_, this);
242 auto iter = std::find(children_.begin(), children_.end(), node);
243 DCHECK(iter != children_.end());
244 node->parent_ = nullptr;
245 children_.erase(iter);
247 tree_->root()->NotifyRemoved(this, node, tree_->AdvanceChangeID());
250 void Frame::StartNavigate(mojo::URLRequestPtr request) {
251 pending_navigate_.reset();
253 // We need a View to navigate. When we get the View we'll complete the
254 // navigation.
255 if (!view_) {
256 pending_navigate_ = request.Pass();
257 return;
260 // Drop any pending navigation requests.
261 navigate_weak_ptr_factory_.InvalidateWeakPtrs();
263 scoped_ptr<FrameUserData> user_data;
264 mojo::ViewTreeClientPtr view_tree_client;
265 tree_->delegate_->CanNavigateFrame(
266 this, request.Pass(),
267 base::Bind(&Frame::OnCanNavigateFrame,
268 navigate_weak_ptr_factory_.GetWeakPtr()));
271 void Frame::OnCanNavigateFrame(uint32_t app_id,
272 FrameTreeClient* frame_tree_client,
273 scoped_ptr<FrameUserData> user_data,
274 mojo::ViewTreeClientPtr view_tree_client) {
275 if (app_id == app_id_ && app_id != mojo::Shell::kInvalidContentHandlerID &&
276 !FrameTree::AlwaysCreateNewFrameTree()) {
277 // The app currently rendering the frame will continue rendering it. In this
278 // case we do not use the ViewTreeClient (because the app has a View already
279 // and ends up reusing it).
280 DCHECK(!view_tree_client.get());
281 } else {
282 Frame* ancestor_with_frame_tree_client = GetAncestorWithFrameTreeClient();
283 DCHECK(ancestor_with_frame_tree_client);
284 ancestor_with_frame_tree_client->frame_tree_client_->OnWillNavigate(id_);
285 DCHECK(view_tree_client.get());
287 ChangeClient(frame_tree_client, user_data.Pass(), view_tree_client.Pass(),
288 app_id);
291 void Frame::LoadingStartedImpl() {
292 DCHECK(!loading_);
293 loading_ = true;
294 progress_ = 0.f;
295 tree_->LoadingStateChanged();
298 void Frame::LoadingStoppedImpl() {
299 DCHECK(loading_);
300 loading_ = false;
301 tree_->LoadingStateChanged();
304 void Frame::ProgressChangedImpl(double progress) {
305 DCHECK(loading_);
306 progress_ = progress;
307 tree_->ProgressChanged();
310 void Frame::TitleChangedImpl(const mojo::String& title) {
311 // Only care about title changes on the root frame.
312 if (!parent_)
313 tree_->TitleChanged(title);
316 void Frame::SetClientPropertyImpl(const mojo::String& name,
317 mojo::Array<uint8_t> value) {
318 auto iter = client_properties_.find(name);
319 const bool already_in_map = (iter != client_properties_.end());
320 if (value.is_null()) {
321 if (!already_in_map)
322 return;
323 client_properties_.erase(iter);
324 } else {
325 std::vector<uint8_t> as_vector(value.To<std::vector<uint8_t>>());
326 if (already_in_map && iter->second == as_vector)
327 return;
328 client_properties_[name] = as_vector;
330 tree_->ClientPropertyChanged(this, name, value);
333 Frame* Frame::FindFrameWithIdFromSameApp(uint32_t frame_id) {
334 if (frame_id == id_)
335 return this; // Common case.
337 Frame* frame = FindFrame(frame_id);
338 if (frame && frame->frame_tree_client_) {
339 // The frame has it's own client/server pair. It should make requests using
340 // the server it has rather than an ancestor.
341 DVLOG(1) << "ignoring request for a frame that has its own client.";
342 return nullptr;
345 if (frame && frame->app_id_ != app_id_) {
346 // An app is trying to send a message from another frame.
347 DVLOG(1) << "ignoring request for a frame from another app.";
348 return nullptr;
351 // TODO(sky): deal with kInvalidContentHandlerID?
353 return frame;
356 void Frame::NotifyAdded(const Frame* source,
357 const Frame* added_node,
358 uint32_t change_id) {
359 if (frame_tree_client_)
360 frame_tree_client_->OnFrameAdded(change_id, FrameToFrameData(added_node));
362 for (Frame* child : children_)
363 child->NotifyAdded(source, added_node, change_id);
366 void Frame::NotifyRemoved(const Frame* source,
367 const Frame* removed_node,
368 uint32_t change_id) {
369 if (frame_tree_client_)
370 frame_tree_client_->OnFrameRemoved(change_id, removed_node->id());
372 for (Frame* child : children_)
373 child->NotifyRemoved(source, removed_node, change_id);
376 void Frame::NotifyClientPropertyChanged(const Frame* source,
377 const mojo::String& name,
378 const mojo::Array<uint8_t>& value) {
379 if (this != source && frame_tree_client_)
380 frame_tree_client_->OnFrameClientPropertyChanged(source->id(), name,
381 value.Clone());
383 for (Frame* child : children_)
384 child->NotifyClientPropertyChanged(source, name, value);
387 void Frame::OnTreeChanged(const TreeChangeParams& params) {
388 if (params.new_parent && this == tree_->root()) {
389 Frame* child_frame = FindFrame(params.target->id());
390 if (child_frame && !child_frame->view_)
391 child_frame->SetView(params.target);
395 void Frame::OnViewDestroying(mojo::View* view) {
396 if (parent_)
397 parent_->Remove(this);
399 // Reset |view_ownership_| so we don't attempt to delete |view_| in the
400 // destructor.
401 view_ownership_ = ViewOwnership::DOESNT_OWN_VIEW;
403 // TODO(sky): Change browser to create a child for each FrameTree.
404 if (tree_->root() == this) {
405 view_->RemoveObserver(this);
406 view_ = nullptr;
407 return;
410 delete this;
413 void Frame::PostMessageEventToFrame(uint32_t source_frame_id,
414 uint32_t target_frame_id,
415 HTMLMessageEventPtr event) {
416 Frame* source = FindFrameWithIdFromSameApp(source_frame_id);
417 // NOTE: |target_frame_id| is allowed to be from another connection.
418 Frame* target = tree_->root()->FindFrame(target_frame_id);
419 if (!target || !source || source == target || !tree_->delegate_ ||
420 !tree_->delegate_->CanPostMessageEventToFrame(source, target,
421 event.get()))
422 return;
424 DCHECK(target->GetAncestorWithFrameTreeClient());
425 target->GetAncestorWithFrameTreeClient()
426 ->frame_tree_client_->OnPostMessageEvent(source_frame_id, target_frame_id,
427 event.Pass());
430 void Frame::LoadingStarted(uint32_t frame_id) {
431 Frame* target_frame = FindFrameWithIdFromSameApp(frame_id);
432 if (target_frame)
433 target_frame->LoadingStartedImpl();
436 void Frame::LoadingStopped(uint32_t frame_id) {
437 Frame* target_frame = FindFrameWithIdFromSameApp(frame_id);
438 if (target_frame)
439 target_frame->LoadingStoppedImpl();
442 void Frame::ProgressChanged(uint32_t frame_id, double progress) {
443 Frame* target_frame = FindFrameWithIdFromSameApp(frame_id);
444 if (target_frame)
445 target_frame->ProgressChangedImpl(progress);
448 void Frame::TitleChanged(uint32_t frame_id, const mojo::String& title) {
449 Frame* target_frame = FindFrameWithIdFromSameApp(frame_id);
450 if (target_frame)
451 target_frame->TitleChangedImpl(title);
454 void Frame::SetClientProperty(uint32_t frame_id,
455 const mojo::String& name,
456 mojo::Array<uint8_t> value) {
457 Frame* target_frame = FindFrameWithIdFromSameApp(frame_id);
458 if (target_frame)
459 target_frame->SetClientPropertyImpl(name, value.Pass());
462 void Frame::OnCreatedFrame(
463 uint32_t parent_id,
464 uint32_t frame_id,
465 mojo::Map<mojo::String, mojo::Array<uint8_t>> client_properties) {
466 // TODO(sky): I need a way to verify the frame_id. Unfortunately the code here
467 // doesn't know the connection id of the embedder, so it's not possible to
468 // do it.
470 if (FindFrame(frame_id)) {
471 // TODO(sky): kill connection here?
472 DVLOG(1) << "OnCreatedLocalFrame supplied id of existing frame.";
473 return;
476 Frame* parent_frame = FindFrameWithIdFromSameApp(parent_id);
477 if (!parent_frame) {
478 DVLOG(1) << "OnCreatedLocalFrame supplied invalid parent_id.";
479 return;
482 if (parent_frame != this && parent_frame->frame_tree_client_) {
483 DVLOG(1) << "OnCreatedLocalFrame supplied parent from another connection.";
484 return;
487 tree_->CreateSharedFrame(parent_frame, frame_id, app_id_,
488 client_properties.To<ClientPropertyMap>());
491 void Frame::RequestNavigate(NavigationTargetType target_type,
492 uint32_t target_frame_id,
493 mojo::URLRequestPtr request) {
494 if (target_type == NAVIGATION_TARGET_TYPE_EXISTING_FRAME) {
495 // |target_frame| is allowed to come from another connection.
496 Frame* target_frame = tree_->root()->FindFrame(target_frame_id);
497 if (!target_frame) {
498 DVLOG(1) << "RequestNavigate EXIT_FRAME with no matching frame";
499 return;
501 if (target_frame != tree_->root()) {
502 target_frame->StartNavigate(request.Pass());
503 return;
505 // Else case if |target_frame| == root. Treat at top level request.
507 tree_->delegate_->NavigateTopLevel(this, request.Pass());
510 void Frame::DidNavigateLocally(uint32_t frame_id, const mojo::String& url) {
511 NOTIMPLEMENTED();
514 } // namespace web_view