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/html_viewer/html_frame_tree_manager.h"
9 #include "base/command_line.h"
10 #include "base/logging.h"
11 #include "components/html_viewer/blink_basic_type_converters.h"
12 #include "components/html_viewer/blink_url_request_type_converters.h"
13 #include "components/html_viewer/document_resource_waiter.h"
14 #include "components/html_viewer/global_state.h"
15 #include "components/html_viewer/html_frame.h"
16 #include "components/html_viewer/html_frame_delegate.h"
17 #include "components/html_viewer/html_viewer_switches.h"
18 #include "components/view_manager/public/cpp/view_manager.h"
19 #include "third_party/WebKit/public/web/WebLocalFrame.h"
20 #include "third_party/WebKit/public/web/WebRemoteFrame.h"
21 #include "third_party/WebKit/public/web/WebTreeScopeType.h"
22 #include "third_party/WebKit/public/web/WebView.h"
23 #include "ui/gfx/geometry/dip_util.h"
24 #include "ui/gfx/geometry/size.h"
26 namespace html_viewer
{
29 // Returns the index of the FrameData with the id of |frame_id| in |index|. On
30 // success returns true, otherwise false.
31 bool FindFrameDataIndex(const mojo::Array
<mandoline::FrameDataPtr
>& frame_data
,
34 for (size_t i
= 0; i
< frame_data
.size(); ++i
) {
35 if (frame_data
[i
]->frame_id
== frame_id
) {
46 HTMLFrameTreeManager::TreeMap
* HTMLFrameTreeManager::instances_
= nullptr;
49 HTMLFrame
* HTMLFrameTreeManager::CreateFrameAndAttachToTree(
50 GlobalState
* global_state
,
52 scoped_ptr
<DocumentResourceWaiter
> resource_waiter
,
53 HTMLFrameDelegate
* delegate
) {
55 instances_
= new TreeMap
;
57 mojo::InterfaceRequest
<mandoline::FrameTreeClient
> frame_tree_client_request
;
58 mandoline::FrameTreeServerPtr frame_tree_server
;
59 mojo::Array
<mandoline::FrameDataPtr
> frame_data
;
61 resource_waiter
->Release(&frame_tree_client_request
, &frame_tree_server
,
62 &frame_data
, &change_id
);
63 resource_waiter
.reset();
65 HTMLFrameTreeManager
* frame_tree
= nullptr;
67 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
68 switches::kOOPIFAlwaysCreateNewFrameTree
)) {
69 if (instances_
->count(frame_data
[0]->frame_id
))
70 frame_tree
= (*instances_
)[frame_data
[0]->frame_id
];
74 frame_tree
= new HTMLFrameTreeManager(global_state
);
75 frame_tree
->Init(delegate
, view
, frame_data
, change_id
);
76 if (frame_data
[0]->frame_id
== view
->id())
77 (*instances_
)[frame_data
[0]->frame_id
] = frame_tree
;
79 // We're going to share a frame tree. There are two possibilities:
80 // . We already know about the frame, in which case we swap it to local.
81 // . We don't know about the frame (most likely because of timing issues),
82 // but we better know about the parent. Create a new frame for it.
83 CHECK(view
->id() != frame_data
[0]->frame_id
);
84 HTMLFrame
* existing_frame
= frame_tree
->root_
->FindFrame(view
->id());
85 size_t frame_data_index
= 0u;
86 CHECK(FindFrameDataIndex(frame_data
, view
->id(), &frame_data_index
));
87 const mandoline::FrameDataPtr
& data
= frame_data
[frame_data_index
];
89 CHECK(!existing_frame
->IsLocal());
90 existing_frame
->SwapToLocal(delegate
, view
, data
->client_properties
);
92 HTMLFrame
* parent
= frame_tree
->root_
->FindFrame(data
->parent_id
);
94 HTMLFrame::CreateParams
params(frame_tree
, parent
, view
->id(), view
,
95 data
->client_properties
, delegate
);
96 delegate
->CreateHTMLFrame(¶ms
);
100 HTMLFrame
* frame
= frame_tree
->root_
->FindFrame(view
->id());
102 frame
->Bind(frame_tree_server
.Pass(), frame_tree_client_request
.Pass());
106 blink::WebView
* HTMLFrameTreeManager::GetWebView() {
107 return root_
->web_view();
110 void HTMLFrameTreeManager::OnFrameDestroyed(HTMLFrame
* frame
) {
114 if (frame
== local_root_
)
115 local_root_
= nullptr;
117 if (!in_process_on_frame_removed_
)
118 pending_remove_ids_
.insert(frame
->id());
120 if (!local_root_
|| !local_root_
->HasLocalDescendant())
124 HTMLFrameTreeManager::HTMLFrameTreeManager(GlobalState
* global_state
)
125 : global_state_(global_state
),
127 local_root_(nullptr),
129 in_process_on_frame_removed_(false),
130 weak_factory_(this) {}
132 HTMLFrameTreeManager::~HTMLFrameTreeManager() {
133 DCHECK(!root_
|| !local_root_
);
134 RemoveFromInstances();
137 void HTMLFrameTreeManager::Init(
138 HTMLFrameDelegate
* delegate
,
139 mojo::View
* local_view
,
140 const mojo::Array
<mandoline::FrameDataPtr
>& frame_data
,
141 uint32_t change_id
) {
142 change_id_
= change_id
;
143 root_
= BuildFrameTree(delegate
, frame_data
, local_view
->id(), local_view
);
144 local_root_
= root_
->FindFrame(local_view
->id());
146 local_root_
->UpdateFocus();
149 HTMLFrame
* HTMLFrameTreeManager::BuildFrameTree(
150 HTMLFrameDelegate
* delegate
,
151 const mojo::Array
<mandoline::FrameDataPtr
>& frame_data
,
152 uint32_t local_frame_id
,
153 mojo::View
* local_view
) {
154 std::vector
<HTMLFrame
*> parents
;
155 HTMLFrame
* root
= nullptr;
156 HTMLFrame
* last_frame
= nullptr;
157 for (size_t i
= 0; i
< frame_data
.size(); ++i
) {
158 if (last_frame
&& frame_data
[i
]->parent_id
== last_frame
->id()) {
159 parents
.push_back(last_frame
);
160 } else if (!parents
.empty()) {
161 while (parents
.back()->id() != frame_data
[i
]->parent_id
)
164 HTMLFrame::CreateParams
params(this,
165 !parents
.empty() ? parents
.back() : nullptr,
166 frame_data
[i
]->frame_id
, local_view
,
167 frame_data
[i
]->client_properties
, nullptr);
168 if (frame_data
[i
]->frame_id
== local_frame_id
)
169 params
.delegate
= delegate
;
171 HTMLFrame
* frame
= delegate
->CreateHTMLFrame(¶ms
);
175 DCHECK(frame
->parent());
181 void HTMLFrameTreeManager::RemoveFromInstances() {
182 for (auto pair
: *instances_
) {
183 if (pair
.second
== this) {
184 instances_
->erase(pair
.first
);
190 bool HTMLFrameTreeManager::PrepareForStructureChange(HTMLFrame
* source
,
191 uint32_t change_id
) {
192 // The change ids may differ if multiple HTMLDocuments are attached to the
193 // same tree (which means we have multiple FrameTreeClients for the same
195 if (change_id
!= (change_id_
+ 1))
198 // We only process changes for the topmost local root.
199 if (source
!= local_root_
)
202 // Update the id as the change is going to be applied (or we can assume it
203 // will be applied if we get here).
204 change_id_
= change_id
;
208 void HTMLFrameTreeManager::ProcessOnFrameAdded(
211 mandoline::FrameDataPtr frame_data
) {
212 if (!PrepareForStructureChange(source
, change_id
))
215 HTMLFrame
* parent
= root_
->FindFrame(frame_data
->parent_id
);
217 DVLOG(1) << "Received invalid parent in OnFrameAdded "
218 << frame_data
->parent_id
;
221 if (root_
->FindFrame(frame_data
->frame_id
)) {
222 DVLOG(1) << "Child with id already exists in OnFrameAdded "
223 << frame_data
->frame_id
;
227 // Because notification is async it's entirely possible for us to create a
228 // new frame, and remove it before we get the add from the server. This check
229 // ensures we don't add back a frame we explicitly removed.
230 if (pending_remove_ids_
.count(frame_data
->frame_id
))
233 HTMLFrame::CreateParams
params(this, parent
, frame_data
->frame_id
, nullptr,
234 frame_data
->client_properties
, nullptr);
235 // |parent| takes ownership of created HTMLFrame.
236 source
->GetLocalRoot()->delegate_
->CreateHTMLFrame(¶ms
);
239 void HTMLFrameTreeManager::ProcessOnFrameRemoved(HTMLFrame
* source
,
242 if (!PrepareForStructureChange(source
, change_id
))
245 pending_remove_ids_
.erase(frame_id
);
247 HTMLFrame
* frame
= root_
->FindFrame(frame_id
);
249 DVLOG(1) << "OnFrameRemoved with unknown frame " << frame_id
;
253 // We shouldn't see requests to remove the root.
254 if (frame
== root_
) {
255 DVLOG(1) << "OnFrameRemoved supplied root; ignoring";
259 // Requests to remove local frames are followed by the View being destroyed.
260 // We handle destruction there.
261 if (frame
->IsLocal())
264 DCHECK(!in_process_on_frame_removed_
);
265 in_process_on_frame_removed_
= true;
266 base::WeakPtr
<HTMLFrameTreeManager
> ref(weak_factory_
.GetWeakPtr());
269 return; // We were deleted.
271 in_process_on_frame_removed_
= false;
274 void HTMLFrameTreeManager::ProcessOnFrameClientPropertyChanged(
277 const mojo::String
& name
,
278 mojo::Array
<uint8_t> new_data
) {
279 if (source
!= local_root_
)
282 HTMLFrame
* frame
= root_
->FindFrame(frame_id
);
284 frame
->SetValueFromClientProperty(name
, new_data
.Pass());