Rename GetIconID to GetIconId
[chromium-blink-merge.git] / components / web_view / frame.cc
blob2a35e801731394e30e94aea634011a25d54c57d1
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"
19 using mojo::View;
21 DECLARE_VIEW_PROPERTY_TYPE(web_view::Frame*);
23 namespace web_view {
25 // Used to find the Frame associated with a View.
26 DEFINE_LOCAL_VIEW_PROPERTY_KEY(Frame*, kFrame, nullptr);
28 namespace {
30 const uint32_t kNoParentId = 0u;
32 FrameDataPtr FrameToFrameData(const Frame* frame) {
33 FrameDataPtr frame_data(FrameData::New());
34 frame_data->frame_id = frame->id();
35 frame_data->parent_id = frame->parent() ? frame->parent()->id() : kNoParentId;
36 frame_data->client_properties =
37 mojo::Map<mojo::String, mojo::Array<uint8_t>>::From(
38 frame->client_properties());
39 return frame_data.Pass();
42 } // namespace
44 struct Frame::FrameTreeServerBinding {
45 scoped_ptr<FrameUserData> user_data;
46 scoped_ptr<mojo::Binding<FrameTreeServer>> frame_tree_server_binding;
49 Frame::Frame(FrameTree* tree,
50 View* view,
51 uint32_t frame_id,
52 uint32_t app_id,
53 ViewOwnership view_ownership,
54 FrameTreeClient* frame_tree_client,
55 scoped_ptr<FrameUserData> user_data,
56 const ClientPropertyMap& client_properties)
57 : tree_(tree),
58 view_(nullptr),
59 id_(frame_id),
60 app_id_(app_id),
61 parent_(nullptr),
62 view_ownership_(view_ownership),
63 user_data_(user_data.Pass()),
64 frame_tree_client_(frame_tree_client),
65 loading_(false),
66 progress_(0.f),
67 client_properties_(client_properties),
68 weak_factory_(this),
69 navigate_weak_ptr_factory_(this) {
70 if (view)
71 SetView(view);
74 Frame::~Frame() {
75 if (view_)
76 view_->RemoveObserver(this);
77 while (!children_.empty())
78 delete children_[0];
79 if (parent_)
80 parent_->Remove(this);
81 if (view_) {
82 view_->ClearLocalProperty(kFrame);
83 if (view_ownership_ == ViewOwnership::OWNS_VIEW)
84 view_->Destroy();
88 void Frame::Init(Frame* parent) {
90 // Set the FrameTreeClient to null so that we don't notify the client of the
91 // add before OnConnect().
92 base::AutoReset<FrameTreeClient*> frame_tree_client_resetter(
93 &frame_tree_client_, nullptr);
94 if (parent)
95 parent->Add(this);
98 InitClient(ClientType::NEW_APP, nullptr);
101 // static
102 Frame* Frame::FindFirstFrameAncestor(View* view) {
103 while (view && !view->GetLocalProperty(kFrame))
104 view = view->parent();
105 return view ? view->GetLocalProperty(kFrame) : nullptr;
108 const Frame* Frame::FindFrame(uint32_t id) const {
109 if (id == id_)
110 return this;
112 for (const Frame* child : children_) {
113 const Frame* match = child->FindFrame(id);
114 if (match)
115 return match;
117 return nullptr;
120 bool Frame::HasAncestor(const Frame* frame) const {
121 const Frame* current = this;
122 while (current && current != frame)
123 current = current->parent_;
124 return current == frame;
127 bool Frame::IsLoading() const {
128 if (loading_)
129 return true;
130 for (const Frame* child : children_) {
131 if (child->IsLoading())
132 return true;
134 return false;
137 double Frame::GatherProgress(int* frame_count) const {
138 ++(*frame_count);
139 double progress = progress_;
140 for (const Frame* child : children_)
141 progress += child->GatherProgress(frame_count);
142 return progress_;
145 void Frame::InitClient(
146 ClientType client_type,
147 scoped_ptr<FrameTreeServerBinding> frame_tree_server_binding) {
148 std::vector<const Frame*> frames;
149 tree_->root()->BuildFrameTree(&frames);
151 mojo::Array<FrameDataPtr> array(frames.size());
152 for (size_t i = 0; i < frames.size(); ++i)
153 array[i] = FrameToFrameData(frames[i]).Pass();
155 // TODO(sky): error handling.
156 FrameTreeServerPtr frame_tree_server_ptr;
157 frame_tree_server_binding_.reset(new mojo::Binding<FrameTreeServer>(
158 this, GetProxy(&frame_tree_server_ptr).Pass()));
159 if (frame_tree_client_) {
160 frame_tree_client_->OnConnect(
161 frame_tree_server_ptr.Pass(), tree_->change_id(), view_->id(),
162 client_type == ClientType::SAME_APP ? VIEW_CONNECT_TYPE_USE_EXISTING
163 : VIEW_CONNECT_TYPE_USE_NEW,
164 array.Pass(),
165 base::Bind(&OnConnectAck, base::Passed(&frame_tree_server_binding)));
166 tree_->delegate_->DidStartNavigation(this);
170 // static
171 void Frame::OnConnectAck(
172 scoped_ptr<FrameTreeServerBinding> frame_tree_server_binding) {}
174 void Frame::ChangeClient(FrameTreeClient* frame_tree_client,
175 scoped_ptr<FrameUserData> user_data,
176 mojo::ViewTreeClientPtr view_tree_client,
177 uint32_t app_id) {
178 while (!children_.empty())
179 delete children_[0];
181 ClientType client_type = view_tree_client.get() == nullptr
182 ? ClientType::SAME_APP
183 : ClientType::NEW_APP;
184 scoped_ptr<FrameTreeServerBinding> frame_tree_server_binding;
186 if (client_type == ClientType::SAME_APP) {
187 // See comment in InitClient() for details.
188 frame_tree_server_binding.reset(new FrameTreeServerBinding);
189 frame_tree_server_binding->user_data = user_data_.Pass();
190 frame_tree_server_binding->frame_tree_server_binding =
191 frame_tree_server_binding_.Pass();
194 user_data_ = user_data.Pass();
195 frame_tree_client_ = frame_tree_client;
196 frame_tree_server_binding_.reset();
197 loading_ = false;
198 progress_ = 0.f;
199 app_id_ = app_id;
201 if (client_type == ClientType::NEW_APP)
202 view_->Embed(view_tree_client.Pass());
204 InitClient(client_type, frame_tree_server_binding.Pass());
207 void Frame::SetView(mojo::View* view) {
208 DCHECK(!view_);
209 DCHECK_EQ(id_, view->id());
210 view_ = view;
211 view_->SetLocalProperty(kFrame, this);
212 view_->AddObserver(this);
213 if (pending_navigate_.get())
214 StartNavigate(pending_navigate_.Pass());
217 Frame* Frame::GetAncestorWithFrameTreeClient() {
218 Frame* frame = this;
219 while (frame && !frame->frame_tree_client_)
220 frame = frame->parent_;
221 return frame;
224 void Frame::BuildFrameTree(std::vector<const Frame*>* frames) const {
225 frames->push_back(this);
226 for (const Frame* frame : children_)
227 frame->BuildFrameTree(frames);
230 void Frame::Add(Frame* node) {
231 DCHECK(!node->parent_);
233 node->parent_ = this;
234 children_.push_back(node);
236 tree_->root()->NotifyAdded(this, node, tree_->AdvanceChangeID());
239 void Frame::Remove(Frame* node) {
240 DCHECK_EQ(node->parent_, this);
241 auto iter = std::find(children_.begin(), children_.end(), node);
242 DCHECK(iter != children_.end());
243 node->parent_ = nullptr;
244 children_.erase(iter);
246 tree_->root()->NotifyRemoved(this, node, tree_->AdvanceChangeID());
249 void Frame::StartNavigate(mojo::URLRequestPtr request) {
250 pending_navigate_.reset();
252 // We need a View to navigate. When we get the View we'll complete the
253 // navigation.
254 // TODO(sky): consider state and what is not legal while waiting.
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_ && !FrameTree::AlwaysCreateNewFrameTree()) {
276 // The app currently rendering the frame will continue rendering it. In this
277 // case we do not use the ViewTreeClient (because the app has a View already
278 // and ends up reusing it).
279 DCHECK(!view_tree_client.get());
280 } else {
281 Frame* ancestor_with_frame_tree_client = GetAncestorWithFrameTreeClient();
282 DCHECK(ancestor_with_frame_tree_client);
283 ancestor_with_frame_tree_client->frame_tree_client_->OnWillNavigate(id_);
284 DCHECK(view_tree_client.get());
286 ChangeClient(frame_tree_client, user_data.Pass(), view_tree_client.Pass(),
287 app_id);
290 void Frame::LoadingStartedImpl() {
291 DCHECK(!loading_);
292 loading_ = true;
293 progress_ = 0.f;
294 tree_->LoadingStateChanged();
297 void Frame::LoadingStoppedImpl() {
298 DCHECK(loading_);
299 loading_ = false;
300 tree_->LoadingStateChanged();
303 void Frame::ProgressChangedImpl(double progress) {
304 DCHECK(loading_);
305 progress_ = progress;
306 tree_->ProgressChanged();
309 void Frame::SetClientPropertyImpl(const mojo::String& name,
310 mojo::Array<uint8_t> value) {
311 auto iter = client_properties_.find(name);
312 const bool already_in_map = (iter != client_properties_.end());
313 if (value.is_null()) {
314 if (!already_in_map)
315 return;
316 client_properties_.erase(iter);
317 } else {
318 std::vector<uint8_t> as_vector(value.To<std::vector<uint8_t>>());
319 if (already_in_map && iter->second == as_vector)
320 return;
321 client_properties_[name] = as_vector;
323 tree_->ClientPropertyChanged(this, name, value);
326 Frame* Frame::FindTargetFrame(uint32_t frame_id) {
327 if (frame_id == id_)
328 return this; // Common case.
330 // TODO(sky): I need a way to sanity check frame_id here, but the connection
331 // id isn't known to us.
333 Frame* frame = FindFrame(frame_id);
334 if (frame && frame->frame_tree_client_) {
335 // The frame has it's own client/server pair. It should make requests using
336 // the server it has rather than an ancestor.
337 DVLOG(1) << "ignore request for a frame that has its own client.";
338 return nullptr;
341 return frame;
344 void Frame::NotifyAdded(const Frame* source,
345 const Frame* added_node,
346 uint32_t change_id) {
347 if (frame_tree_client_)
348 frame_tree_client_->OnFrameAdded(change_id, FrameToFrameData(added_node));
350 for (Frame* child : children_)
351 child->NotifyAdded(source, added_node, change_id);
354 void Frame::NotifyRemoved(const Frame* source,
355 const Frame* removed_node,
356 uint32_t change_id) {
357 if (frame_tree_client_)
358 frame_tree_client_->OnFrameRemoved(change_id, removed_node->id());
360 for (Frame* child : children_)
361 child->NotifyRemoved(source, removed_node, change_id);
364 void Frame::NotifyClientPropertyChanged(const Frame* source,
365 const mojo::String& name,
366 const mojo::Array<uint8_t>& value) {
367 if (this != source && frame_tree_client_)
368 frame_tree_client_->OnFrameClientPropertyChanged(source->id(), name,
369 value.Clone());
371 for (Frame* child : children_)
372 child->NotifyClientPropertyChanged(source, name, value);
375 void Frame::OnTreeChanged(const TreeChangeParams& params) {
376 if (params.new_parent && this == tree_->root()) {
377 Frame* child_frame = FindFrame(params.target->id());
378 if (child_frame && !child_frame->view_)
379 child_frame->SetView(params.target);
383 void Frame::OnViewDestroying(mojo::View* view) {
384 if (parent_)
385 parent_->Remove(this);
387 // Reset |view_ownership_| so we don't attempt to delete |view_| in the
388 // destructor.
389 view_ownership_ = ViewOwnership::DOESNT_OWN_VIEW;
391 // TODO(sky): Change browser to create a child for each FrameTree.
392 if (tree_->root() == this) {
393 view_->RemoveObserver(this);
394 view_ = nullptr;
395 return;
398 delete this;
401 void Frame::PostMessageEventToFrame(uint32_t source_frame_id,
402 uint32_t target_frame_id,
403 HTMLMessageEventPtr event) {
404 Frame* source = tree_->root()->FindFrame(source_frame_id);
405 Frame* target = tree_->root()->FindFrame(target_frame_id);
406 if (!target || !source || source == target || !tree_->delegate_ ||
407 !tree_->delegate_->CanPostMessageEventToFrame(source, target,
408 event.get()))
409 return;
411 DCHECK(target->GetAncestorWithFrameTreeClient());
412 target->GetAncestorWithFrameTreeClient()
413 ->frame_tree_client_->OnPostMessageEvent(source_frame_id, target_frame_id,
414 event.Pass());
417 void Frame::LoadingStarted(uint32_t frame_id) {
418 Frame* target_frame = FindTargetFrame(frame_id);
419 if (target_frame)
420 target_frame->LoadingStartedImpl();
423 void Frame::LoadingStopped(uint32_t frame_id) {
424 Frame* target_frame = FindTargetFrame(frame_id);
425 if (target_frame)
426 target_frame->LoadingStoppedImpl();
429 void Frame::ProgressChanged(uint32_t frame_id, double progress) {
430 Frame* target_frame = FindTargetFrame(frame_id);
431 if (target_frame)
432 target_frame->ProgressChangedImpl(progress);
435 void Frame::SetClientProperty(uint32_t frame_id,
436 const mojo::String& name,
437 mojo::Array<uint8_t> value) {
438 Frame* target_frame = FindTargetFrame(frame_id);
439 if (target_frame)
440 target_frame->SetClientPropertyImpl(name, value.Pass());
443 void Frame::OnCreatedFrame(
444 uint32_t parent_id,
445 uint32_t frame_id,
446 mojo::Map<mojo::String, mojo::Array<uint8_t>> client_properties) {
447 // TODO(sky): I need a way to verify the id. Unfortunately the code here
448 // doesn't know the connection id of the embedder, so it's not possible to
449 // do it.
451 if (FindFrame(frame_id)) {
452 // TODO(sky): kill connection here?
453 DVLOG(1) << "OnCreatedLocalFrame supplied id of existing frame.";
454 return;
457 Frame* parent_frame = FindFrame(parent_id);
458 if (!parent_frame) {
459 DVLOG(1) << "OnCreatedLocalFrame supplied invalid parent_id.";
460 return;
463 if (parent_frame != this && parent_frame->frame_tree_client_) {
464 DVLOG(1) << "OnCreatedLocalFrame supplied parent from another connection.";
465 return;
468 tree_->CreateSharedFrame(parent_frame, frame_id, app_id_,
469 client_properties.To<ClientPropertyMap>());
472 void Frame::RequestNavigate(NavigationTargetType target_type,
473 uint32_t target_frame_id,
474 mojo::URLRequestPtr request) {
475 if (target_type == NAVIGATION_TARGET_TYPE_EXISTING_FRAME) {
476 Frame* target_frame = tree_->root()->FindFrame(target_frame_id);
477 if (!target_frame) {
478 DVLOG(1) << "RequestNavigate EXIT_FRAME with no matching frame";
479 return;
481 if (target_frame != tree_->root()) {
482 target_frame->StartNavigate(request.Pass());
483 return;
485 // Else case if |target_frame| == root. Treat at top level request.
487 tree_->delegate_->NavigateTopLevel(this, request.Pass());
490 void Frame::DidNavigateLocally(uint32_t frame_id, const mojo::String& url) {
491 NOTIMPLEMENTED();
494 } // namespace web_view