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"
18 #include "mojo/application/public/interfaces/shell.mojom.h"
22 DECLARE_VIEW_PROPERTY_TYPE(web_view::Frame
*);
26 // Used to find the Frame associated with a View.
27 DEFINE_LOCAL_VIEW_PROPERTY_KEY(Frame
*, kFrame
, nullptr);
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();
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
,
54 ViewOwnership view_ownership
,
55 FrameTreeClient
* frame_tree_client
,
56 scoped_ptr
<FrameUserData
> user_data
,
57 const ClientPropertyMap
& client_properties
)
63 view_ownership_(view_ownership
),
64 user_data_(user_data
.Pass()),
65 frame_tree_client_(frame_tree_client
),
68 client_properties_(client_properties
),
70 navigate_weak_ptr_factory_(this) {
77 view_
->RemoveObserver(this);
78 while (!children_
.empty())
81 parent_
->Remove(this);
83 view_
->ClearLocalProperty(kFrame
);
84 if (view_ownership_
== ViewOwnership::OWNS_VIEW
)
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);
99 InitClient(ClientType::NEW_APP
, nullptr);
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 {
113 for (const Frame
* child
: children_
) {
114 const Frame
* match
= child
->FindFrame(id
);
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 {
131 for (const Frame
* child
: children_
) {
132 if (child
->IsLoading())
138 double Frame::GatherProgress(int* frame_count
) const {
140 double progress
= progress_
;
141 for (const Frame
* child
: children_
)
142 progress
+= child
->GatherProgress(frame_count
);
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
,
166 base::Bind(&OnConnectAck
, base::Passed(&frame_tree_server_binding
)));
167 tree_
->delegate_
->DidStartNavigation(this);
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
,
179 while (!children_
.empty())
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();
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
) {
210 DCHECK_EQ(id_
, view
->id());
212 view_
->SetLocalProperty(kFrame
, this);
213 view_
->AddObserver(this);
214 if (pending_navigate_
.get())
215 StartNavigate(pending_navigate_
.Pass());
218 Frame
* Frame::GetAncestorWithFrameTreeClient() {
220 while (frame
&& !frame
->frame_tree_client_
)
221 frame
= frame
->parent_
;
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
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_
&& 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());
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(),
291 void Frame::LoadingStartedImpl() {
295 tree_
->LoadingStateChanged();
298 void Frame::LoadingStoppedImpl() {
301 tree_
->LoadingStateChanged();
304 void Frame::ProgressChangedImpl(double progress
) {
306 progress_
= progress
;
307 tree_
->ProgressChanged();
310 void Frame::TitleChangedImpl(const mojo::String
& title
) {
311 // Only care about title changes on the root frame.
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()) {
323 client_properties_
.erase(iter
);
325 std::vector
<uint8_t> as_vector(value
.To
<std::vector
<uint8_t>>());
326 if (already_in_map
&& iter
->second
== as_vector
)
328 client_properties_
[name
] = as_vector
;
330 tree_
->ClientPropertyChanged(this, name
, value
);
333 Frame
* Frame::FindFrameWithIdFromSameApp(uint32_t frame_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.";
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.";
351 // TODO(sky): deal with kInvalidContentHandlerID?
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
,
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
) {
397 parent_
->Remove(this);
399 // Reset |view_ownership_| so we don't attempt to delete |view_| in the
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);
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
,
424 DCHECK(target
->GetAncestorWithFrameTreeClient());
425 target
->GetAncestorWithFrameTreeClient()
426 ->frame_tree_client_
->OnPostMessageEvent(source_frame_id
, target_frame_id
,
430 void Frame::LoadingStarted(uint32_t frame_id
) {
431 Frame
* target_frame
= FindFrameWithIdFromSameApp(frame_id
);
433 target_frame
->LoadingStartedImpl();
436 void Frame::LoadingStopped(uint32_t frame_id
) {
437 Frame
* target_frame
= FindFrameWithIdFromSameApp(frame_id
);
439 target_frame
->LoadingStoppedImpl();
442 void Frame::ProgressChanged(uint32_t frame_id
, double progress
) {
443 Frame
* target_frame
= FindFrameWithIdFromSameApp(frame_id
);
445 target_frame
->ProgressChangedImpl(progress
);
448 void Frame::TitleChanged(uint32_t frame_id
, const mojo::String
& title
) {
449 Frame
* target_frame
= FindFrameWithIdFromSameApp(frame_id
);
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
);
459 target_frame
->SetClientPropertyImpl(name
, value
.Pass());
462 void Frame::OnCreatedFrame(
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
470 if (FindFrame(frame_id
)) {
471 // TODO(sky): kill connection here?
472 DVLOG(1) << "OnCreatedLocalFrame supplied id of existing frame.";
476 Frame
* parent_frame
= FindFrameWithIdFromSameApp(parent_id
);
478 DVLOG(1) << "OnCreatedLocalFrame supplied invalid parent_id.";
482 if (parent_frame
!= this && parent_frame
->frame_tree_client_
) {
483 DVLOG(1) << "OnCreatedLocalFrame supplied parent from another connection.";
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
);
498 DVLOG(1) << "RequestNavigate EXIT_FRAME with no matching frame";
501 if (target_frame
!= tree_
->root()) {
502 target_frame
->StartNavigate(request
.Pass());
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
) {
514 } // namespace web_view