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"
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"
21 DECLARE_VIEW_PROPERTY_TYPE(web_view::Frame
*);
25 // Used to find the Frame associated with a View.
26 DEFINE_LOCAL_VIEW_PROPERTY_KEY(Frame
*, kFrame
, nullptr);
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();
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
,
53 ViewOwnership view_ownership
,
54 FrameTreeClient
* frame_tree_client
,
55 scoped_ptr
<FrameUserData
> user_data
,
56 const ClientPropertyMap
& client_properties
)
62 view_ownership_(view_ownership
),
63 user_data_(user_data
.Pass()),
64 frame_tree_client_(frame_tree_client
),
67 client_properties_(client_properties
),
69 navigate_weak_ptr_factory_(this) {
76 view_
->RemoveObserver(this);
77 while (!children_
.empty())
80 parent_
->Remove(this);
82 view_
->ClearLocalProperty(kFrame
);
83 if (view_ownership_
== ViewOwnership::OWNS_VIEW
)
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);
98 InitClient(ClientType::NEW_APP
, nullptr);
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 {
112 for (const Frame
* child
: children_
) {
113 const Frame
* match
= child
->FindFrame(id
);
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 {
130 for (const Frame
* child
: children_
) {
131 if (child
->IsLoading())
137 double Frame::GatherProgress(int* frame_count
) const {
139 double progress
= progress_
;
140 for (const Frame
* child
: children_
)
141 progress
+= child
->GatherProgress(frame_count
);
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
,
165 base::Bind(&OnConnectAck
, base::Passed(&frame_tree_server_binding
)));
166 tree_
->delegate_
->DidStartNavigation(this);
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
,
178 while (!children_
.empty())
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();
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
) {
209 DCHECK_EQ(id_
, view
->id());
211 view_
->SetLocalProperty(kFrame
, this);
212 view_
->AddObserver(this);
213 if (pending_navigate_
.get())
214 StartNavigate(pending_navigate_
.Pass());
217 Frame
* Frame::GetAncestorWithFrameTreeClient() {
219 while (frame
&& !frame
->frame_tree_client_
)
220 frame
= frame
->parent_
;
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
254 // TODO(sky): consider state and what is not legal while waiting.
256 pending_navigate_
= request
.Pass();
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());
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(),
290 void Frame::LoadingStartedImpl() {
294 tree_
->LoadingStateChanged();
297 void Frame::LoadingStoppedImpl() {
300 tree_
->LoadingStateChanged();
303 void Frame::ProgressChangedImpl(double progress
) {
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()) {
316 client_properties_
.erase(iter
);
318 std::vector
<uint8_t> as_vector(value
.To
<std::vector
<uint8_t>>());
319 if (already_in_map
&& iter
->second
== as_vector
)
321 client_properties_
[name
] = as_vector
;
323 tree_
->ClientPropertyChanged(this, name
, value
);
326 Frame
* Frame::FindTargetFrame(uint32_t frame_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.";
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
,
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
) {
385 parent_
->Remove(this);
387 // Reset |view_ownership_| so we don't attempt to delete |view_| in the
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);
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
,
411 DCHECK(target
->GetAncestorWithFrameTreeClient());
412 target
->GetAncestorWithFrameTreeClient()
413 ->frame_tree_client_
->OnPostMessageEvent(source_frame_id
, target_frame_id
,
417 void Frame::LoadingStarted(uint32_t frame_id
) {
418 Frame
* target_frame
= FindTargetFrame(frame_id
);
420 target_frame
->LoadingStartedImpl();
423 void Frame::LoadingStopped(uint32_t frame_id
) {
424 Frame
* target_frame
= FindTargetFrame(frame_id
);
426 target_frame
->LoadingStoppedImpl();
429 void Frame::ProgressChanged(uint32_t frame_id
, double progress
) {
430 Frame
* target_frame
= FindTargetFrame(frame_id
);
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
);
440 target_frame
->SetClientPropertyImpl(name
, value
.Pass());
443 void Frame::OnCreatedFrame(
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
451 if (FindFrame(frame_id
)) {
452 // TODO(sky): kill connection here?
453 DVLOG(1) << "OnCreatedLocalFrame supplied id of existing frame.";
457 Frame
* parent_frame
= FindFrame(parent_id
);
459 DVLOG(1) << "OnCreatedLocalFrame supplied invalid parent_id.";
463 if (parent_frame
!= this && parent_frame
->frame_tree_client_
) {
464 DVLOG(1) << "OnCreatedLocalFrame supplied parent from another connection.";
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
);
478 DVLOG(1) << "RequestNavigate EXIT_FRAME with no matching frame";
481 if (target_frame
!= tree_
->root()) {
482 target_frame
->StartNavigate(request
.Pass());
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
) {
494 } // namespace web_view