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 "mandoline/tab/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 "mandoline/tab/frame_tree.h"
16 #include "mandoline/tab/frame_tree_delegate.h"
17 #include "mandoline/tab/frame_user_data.h"
21 DECLARE_VIEW_PROPERTY_TYPE(mandoline::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 Frame::Frame(FrameTree
* tree
,
47 ViewOwnership view_ownership
,
48 FrameTreeClient
* frame_tree_client
,
49 scoped_ptr
<FrameUserData
> user_data
,
50 const ClientPropertyMap
& client_properties
)
55 view_ownership_(view_ownership
),
56 user_data_(user_data
.Pass()),
57 frame_tree_client_(frame_tree_client
),
60 client_properties_(client_properties
),
61 frame_tree_server_binding_(this),
69 view_
->RemoveObserver(this);
70 while (!children_
.empty())
73 parent_
->Remove(this);
75 view_
->ClearLocalProperty(kFrame
);
76 if (view_ownership_
== ViewOwnership::OWNS_VIEW
)
81 void Frame::Init(Frame
* parent
) {
83 // Set the FrameTreeClient to null so that we don't notify the client of the
84 // add before OnConnect().
85 base::AutoReset
<FrameTreeClient
*> frame_tree_client_resetter(
86 &frame_tree_client_
, nullptr);
95 Frame
* Frame::FindFirstFrameAncestor(View
* view
) {
96 while (view
&& !view
->GetLocalProperty(kFrame
))
97 view
= view
->parent();
98 return view
? view
->GetLocalProperty(kFrame
) : nullptr;
101 const Frame
* Frame::FindFrame(uint32_t id
) const {
105 for (const Frame
* child
: children_
) {
106 const Frame
* match
= child
->FindFrame(id
);
113 bool Frame::HasAncestor(const Frame
* frame
) const {
114 const Frame
* current
= this;
115 while (current
&& current
!= frame
)
116 current
= current
->parent_
;
117 return current
== frame
;
120 bool Frame::IsLoading() const {
123 for (const Frame
* child
: children_
) {
124 if (child
->IsLoading())
130 double Frame::GatherProgress(int* frame_count
) const {
132 double progress
= progress_
;
133 for (const Frame
* child
: children_
)
134 progress
+= child
->GatherProgress(frame_count
);
138 void Frame::InitClient() {
139 std::vector
<const Frame
*> frames
;
140 tree_
->root()->BuildFrameTree(&frames
);
142 mojo::Array
<FrameDataPtr
> array(frames
.size());
143 for (size_t i
= 0; i
< frames
.size(); ++i
)
144 array
[i
] = FrameToFrameData(frames
[i
]).Pass();
146 // TODO(sky): error handling.
147 FrameTreeServerPtr frame_tree_server_ptr
;
148 frame_tree_server_binding_
.Bind(GetProxy(&frame_tree_server_ptr
).Pass());
149 if (frame_tree_client_
) {
150 frame_tree_client_
->OnConnect(frame_tree_server_ptr
.Pass(),
151 tree_
->change_id(), array
.Pass());
152 tree_
->delegate_
->DidStartNavigation(this);
156 void Frame::OnWillNavigateAck(FrameTreeClient
* frame_tree_client
,
157 scoped_ptr
<FrameUserData
> user_data
,
158 mojo::ViewTreeClientPtr view_tree_client
) {
159 while (!children_
.empty())
162 user_data_
= user_data
.Pass();
163 frame_tree_client_
= frame_tree_client
;
164 frame_tree_server_binding_
.Close();
168 view_
->Embed(view_tree_client
.Pass());
172 void Frame::SetView(mojo::View
* view
) {
174 DCHECK_EQ(id_
, view
->id());
176 view_
->SetLocalProperty(kFrame
, this);
177 view_
->AddObserver(this);
178 if (pending_navigate_
.get())
179 StartNavigate(pending_navigate_
.Pass());
182 Frame
* Frame::GetAncestorWithFrameTreeClient() {
184 while (frame
&& !frame
->frame_tree_client_
)
185 frame
= frame
->parent_
;
189 void Frame::BuildFrameTree(std::vector
<const Frame
*>* frames
) const {
190 frames
->push_back(this);
191 for (const Frame
* frame
: children_
)
192 frame
->BuildFrameTree(frames
);
195 void Frame::Add(Frame
* node
) {
196 DCHECK(!node
->parent_
);
198 node
->parent_
= this;
199 children_
.push_back(node
);
201 tree_
->root()->NotifyAdded(this, node
, tree_
->AdvanceChangeID());
204 void Frame::Remove(Frame
* node
) {
205 DCHECK_EQ(node
->parent_
, this);
206 auto iter
= std::find(children_
.begin(), children_
.end(), node
);
207 DCHECK(iter
!= children_
.end());
208 node
->parent_
= nullptr;
209 children_
.erase(iter
);
211 tree_
->root()->NotifyRemoved(this, node
, tree_
->AdvanceChangeID());
214 void Frame::StartNavigate(mojo::URLRequestPtr request
) {
215 pending_navigate_
.reset();
217 // We need a View to navigate. When we get the View we'll complete the
219 // TODO(sky): consider state and what is not legal while waiting.
221 pending_navigate_
= request
.Pass();
225 FrameTreeClient
* frame_tree_client
= nullptr;
226 scoped_ptr
<FrameUserData
> user_data
;
227 mojo::ViewTreeClientPtr view_tree_client
;
228 if (!tree_
->delegate_
->CanNavigateFrame(this, request
.Pass(),
229 &frame_tree_client
, &user_data
,
230 &view_tree_client
)) {
234 // TODO(sky): consider what state this should correspond to. Should we
235 // disallow certain operations until we get the ack?
236 Frame
* ancestor_with_frame_tree_client
= GetAncestorWithFrameTreeClient();
237 DCHECK(ancestor_with_frame_tree_client
);
238 ancestor_with_frame_tree_client
->frame_tree_client_
->OnWillNavigate(
239 id_
, base::Bind(&Frame::OnWillNavigateAck
, weak_factory_
.GetWeakPtr(),
240 frame_tree_client
, base::Passed(&user_data
),
241 base::Passed(&view_tree_client
)));
244 void Frame::LoadingStartedImpl() {
248 tree_
->LoadingStateChanged();
251 void Frame::LoadingStoppedImpl() {
254 tree_
->LoadingStateChanged();
257 void Frame::ProgressChangedImpl(double progress
) {
259 progress_
= progress
;
260 tree_
->ProgressChanged();
263 void Frame::SetClientPropertyImpl(const mojo::String
& name
,
264 mojo::Array
<uint8_t> value
) {
265 auto iter
= client_properties_
.find(name
);
266 const bool already_in_map
= (iter
!= client_properties_
.end());
267 if (value
.is_null()) {
270 client_properties_
.erase(iter
);
272 std::vector
<uint8_t> as_vector(value
.To
<std::vector
<uint8_t>>());
273 if (already_in_map
&& iter
->second
== as_vector
)
275 client_properties_
[name
] = as_vector
;
277 tree_
->ClientPropertyChanged(this, name
, value
);
280 Frame
* Frame::FindTargetFrame(uint32_t frame_id
) {
282 return this; // Common case.
284 // TODO(sky): I need a way to sanity check frame_id here, but the connection
285 // id isn't known to us.
287 Frame
* frame
= FindFrame(frame_id
);
288 if (frame
->frame_tree_client_
) {
289 // The frame has it's own client/server pair. It should make requests using
290 // the server it has rather than an ancestor.
291 DVLOG(1) << "ignore request for a frame that has its own client.";
298 void Frame::NotifyAdded(const Frame
* source
,
299 const Frame
* added_node
,
300 uint32_t change_id
) {
301 if (frame_tree_client_
)
302 frame_tree_client_
->OnFrameAdded(change_id
, FrameToFrameData(added_node
));
304 for (Frame
* child
: children_
)
305 child
->NotifyAdded(source
, added_node
, change_id
);
308 void Frame::NotifyRemoved(const Frame
* source
,
309 const Frame
* removed_node
,
310 uint32_t change_id
) {
311 if (frame_tree_client_
)
312 frame_tree_client_
->OnFrameRemoved(change_id
, removed_node
->id());
314 for (Frame
* child
: children_
)
315 child
->NotifyRemoved(source
, removed_node
, change_id
);
318 void Frame::NotifyClientPropertyChanged(const Frame
* source
,
319 const mojo::String
& name
,
320 const mojo::Array
<uint8_t>& value
) {
321 if (this != source
&& frame_tree_client_
)
322 frame_tree_client_
->OnFrameClientPropertyChanged(source
->id(), name
,
325 for (Frame
* child
: children_
)
326 child
->NotifyClientPropertyChanged(source
, name
, value
);
329 void Frame::OnTreeChanged(const TreeChangeParams
& params
) {
330 if (params
.new_parent
&& this == tree_
->root()) {
331 Frame
* child_frame
= FindFrame(params
.target
->id());
332 if (child_frame
&& !child_frame
->view_
)
333 child_frame
->SetView(params
.target
);
337 void Frame::OnViewDestroying(mojo::View
* view
) {
339 parent_
->Remove(this);
341 // Reset |view_ownership_| so we don't attempt to delete |view_| in the
343 view_ownership_
= ViewOwnership::DOESNT_OWN_VIEW
;
345 // TODO(sky): Change browser to create a child for each FrameTree.
346 if (tree_
->root() == this) {
347 view_
->RemoveObserver(this);
355 void Frame::PostMessageEventToFrame(uint32_t source_frame_id
,
356 uint32_t target_frame_id
,
357 HTMLMessageEventPtr event
) {
358 Frame
* source
= tree_
->root()->FindFrame(source_frame_id
);
359 Frame
* target
= tree_
->root()->FindFrame(target_frame_id
);
360 if (!target
|| !source
|| source
== target
|| !tree_
->delegate_
||
361 !tree_
->delegate_
->CanPostMessageEventToFrame(source
, target
,
365 DCHECK(target
->GetAncestorWithFrameTreeClient());
366 target
->GetAncestorWithFrameTreeClient()
367 ->frame_tree_client_
->OnPostMessageEvent(source_frame_id
, target_frame_id
,
371 void Frame::LoadingStarted(uint32_t frame_id
) {
372 Frame
* target_frame
= FindTargetFrame(frame_id
);
374 target_frame
->LoadingStartedImpl();
377 void Frame::LoadingStopped(uint32_t frame_id
) {
378 Frame
* target_frame
= FindTargetFrame(frame_id
);
380 target_frame
->LoadingStoppedImpl();
383 void Frame::ProgressChanged(uint32_t frame_id
, double progress
) {
384 Frame
* target_frame
= FindTargetFrame(frame_id
);
386 target_frame
->ProgressChangedImpl(progress
);
389 void Frame::SetClientProperty(uint32_t frame_id
,
390 const mojo::String
& name
,
391 mojo::Array
<uint8_t> value
) {
392 Frame
* target_frame
= FindTargetFrame(frame_id
);
394 target_frame
->SetClientPropertyImpl(name
, value
.Pass());
397 void Frame::OnCreatedFrame(
400 mojo::Map
<mojo::String
, mojo::Array
<uint8_t>> client_properties
) {
401 // TODO(sky): I need a way to verify the id. Unfortunately the code here
402 // doesn't know the connection id of the embedder, so it's not possible to
405 if (FindFrame(frame_id
)) {
406 // TODO(sky): kill connection here?
407 DVLOG(1) << "OnCreatedLocalFrame supplied id of existing frame.";
411 Frame
* parent_frame
= FindFrame(parent_id
);
413 DVLOG(1) << "OnCreatedLocalFrame supplied invalid parent_id.";
417 if (parent_frame
!= this && parent_frame
->frame_tree_client_
) {
418 DVLOG(1) << "OnCreatedLocalFrame supplied parent from another connection.";
422 tree_
->CreateSharedFrame(parent_frame
, frame_id
,
423 client_properties
.To
<ClientPropertyMap
>());
426 void Frame::RequestNavigate(mandoline::NavigationTargetType target_type
,
427 uint32_t target_frame_id
,
428 mojo::URLRequestPtr request
) {
429 if (target_type
== NAVIGATION_TARGET_TYPE_EXISTING_FRAME
) {
430 Frame
* target_frame
= tree_
->root()->FindFrame(target_frame_id
);
432 DVLOG(1) << "RequestNavigate EXIT_FRAME with no matching frame";
435 if (target_frame
!= tree_
->root()) {
436 target_frame
->StartNavigate(request
.Pass());
439 // Else case if |target_frame| == root. Treat at top level request.
441 tree_
->delegate_
->NavigateTopLevel(this, request
.Pass());
444 void Frame::DidNavigateLocally(uint32_t frame_id
, const mojo::String
& url
) {
448 } // namespace mandoline