1 // Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
2 // reserved. Use of this source code is governed by a BSD-style license that can
3 // be found in the LICENSE file.
4
5 #include "libcef/browser/browser_info.h"
6
7 #include "libcef/browser/browser_host_base.h"
8 #include "libcef/browser/thread_util.h"
9 #include "libcef/common/values_impl.h"
10
11 #include "base/logging.h"
12 #include "content/browser/renderer_host/frame_tree_node.h"
13 #include "content/browser/renderer_host/render_frame_host_impl.h"
14 #include "content/public/browser/render_process_host.h"
15 #include "ipc/ipc_message.h"
16
~FrameInfo()17 CefBrowserInfo::FrameInfo::~FrameInfo() {
18 if (frame_ && !is_main_frame_) {
19 // Disassociate sub-frames from the browser.
20 frame_->Detach();
21 }
22 }
23
CefBrowserInfo(int browser_id,bool is_popup,bool is_windowless,CefRefPtr<CefDictionaryValue> extra_info)24 CefBrowserInfo::CefBrowserInfo(int browser_id,
25 bool is_popup,
26 bool is_windowless,
27 CefRefPtr<CefDictionaryValue> extra_info)
28 : browser_id_(browser_id),
29 is_popup_(is_popup),
30 is_windowless_(is_windowless),
31 extra_info_(extra_info) {
32 DCHECK_GT(browser_id, 0);
33 }
34
~CefBrowserInfo()35 CefBrowserInfo::~CefBrowserInfo() {}
36
browser() const37 CefRefPtr<CefBrowserHostBase> CefBrowserInfo::browser() const {
38 base::AutoLock lock_scope(lock_);
39 return browser_;
40 }
41
SetBrowser(CefRefPtr<CefBrowserHostBase> browser)42 void CefBrowserInfo::SetBrowser(CefRefPtr<CefBrowserHostBase> browser) {
43 base::AutoLock lock_scope(lock_);
44 browser_ = browser;
45
46 if (!browser) {
47 RemoveAllFrames();
48 }
49 }
50
MaybeCreateFrame(content::RenderFrameHost * host,bool is_guest_view)51 void CefBrowserInfo::MaybeCreateFrame(content::RenderFrameHost* host,
52 bool is_guest_view) {
53 CEF_REQUIRE_UIT();
54
55 const auto frame_id = CefFrameHostImpl::MakeFrameId(host);
56 const int frame_tree_node_id = host->GetFrameTreeNodeId();
57 const bool is_main_frame = (host->GetParent() == nullptr);
58
59 // A speculative RFH will be created in response to a browser-initiated
60 // cross-origin navigation (e.g. via LoadURL) and eventually either discarded
61 // or swapped in based on whether the navigation is committed. We'll create a
62 // frame object for the speculative RFH so that it can be found by
63 // frame/routing ID. However, we won't replace the main frame with a
64 // speculative RFH until after it's swapped in, and we'll generally prefer to
65 // return a non-speculative RFH for the same node ID if one exists.
66 const bool is_speculative = (static_cast<content::RenderFrameHostImpl*>(host)
67 ->frame_tree_node()
68 ->render_manager()
69 ->current_frame_host() != host);
70
71 base::AutoLock lock_scope(lock_);
72 DCHECK(browser_);
73
74 const auto it = frame_id_map_.find(frame_id);
75 if (it != frame_id_map_.end()) {
76 auto info = it->second;
77
78 #if DCHECK_IS_ON()
79 // Check that the frame info hasn't changed unexpectedly.
80 DCHECK_EQ(info->frame_id_, frame_id);
81 DCHECK_EQ(info->frame_tree_node_id_, frame_tree_node_id);
82 DCHECK_EQ(info->is_guest_view_, is_guest_view);
83 DCHECK_EQ(info->is_main_frame_, is_main_frame);
84 #endif
85
86 if (!info->is_guest_view_ && info->is_speculative_ && !is_speculative) {
87 // Upgrade the frame info from speculative to non-speculative.
88 if (info->is_main_frame_) {
89 if (main_frame_) {
90 // Update the existing main frame object.
91 main_frame_->SetRenderFrameHost(host);
92 info->frame_ = main_frame_;
93 } else {
94 // Set the main frame object.
95 main_frame_ = info->frame_;
96 }
97 }
98 info->is_speculative_ = false;
99 MaybeUpdateFrameTreeNodeIdMap(info);
100 }
101 return;
102 }
103
104 auto frame_info = new FrameInfo;
105 frame_info->host_ = host;
106 frame_info->frame_id_ = frame_id;
107 frame_info->frame_tree_node_id_ = frame_tree_node_id;
108 frame_info->is_guest_view_ = is_guest_view;
109 frame_info->is_main_frame_ = is_main_frame;
110 frame_info->is_speculative_ = is_speculative;
111
112 // Guest views don't get their own CefBrowser or CefFrame objects.
113 if (!is_guest_view) {
114 if (is_main_frame && main_frame_ && !is_speculative) {
115 // Update the existing main frame object.
116 main_frame_->SetRenderFrameHost(host);
117 frame_info->frame_ = main_frame_;
118 } else {
119 // Create a new frame object.
120 frame_info->frame_ = new CefFrameHostImpl(this, host);
121 if (is_main_frame && !is_speculative) {
122 main_frame_ = frame_info->frame_;
123 }
124 }
125 #if DCHECK_IS_ON()
126 // Check that the frame info hasn't changed unexpectedly.
127 DCHECK_EQ(frame_id, frame_info->frame_->GetIdentifier());
128 DCHECK_EQ(frame_info->is_main_frame_, frame_info->frame_->IsMain());
129 #endif
130 }
131
132 browser_->request_context()->OnRenderFrameCreated(
133 host->GetProcess()->GetID(), host->GetRoutingID(), frame_tree_node_id,
134 is_main_frame, is_guest_view);
135
136 // Populate the lookup maps.
137 frame_id_map_.insert(std::make_pair(frame_id, frame_info));
138 MaybeUpdateFrameTreeNodeIdMap(frame_info);
139
140 // And finally set the ownership.
141 frame_info_set_.insert(base::WrapUnique(frame_info));
142 }
143
RemoveFrame(content::RenderFrameHost * host)144 void CefBrowserInfo::RemoveFrame(content::RenderFrameHost* host) {
145 CEF_REQUIRE_UIT();
146
147 base::AutoLock lock_scope(lock_);
148
149 const auto frame_id = CefFrameHostImpl::MakeFrameId(host);
150
151 auto it = frame_id_map_.find(frame_id);
152 DCHECK(it != frame_id_map_.end());
153
154 auto frame_info = it->second;
155
156 browser_->request_context()->OnRenderFrameDeleted(
157 host->GetProcess()->GetID(), host->GetRoutingID(),
158 frame_info->frame_tree_node_id_, frame_info->is_main_frame_,
159 frame_info->is_guest_view_);
160
161 // Remove from the lookup maps.
162 frame_id_map_.erase(it);
163
164 // A new RFH with the same node ID may be added before the old RFH is deleted,
165 // or this might be a speculative RFH. Therefore only delete the map entry if
166 // it's currently pointing to the to-be-deleted frame info object.
167 {
168 auto it2 = frame_tree_node_id_map_.find(frame_info->frame_tree_node_id_);
169 if (it2 != frame_tree_node_id_map_.end() && it2->second == frame_info) {
170 frame_tree_node_id_map_.erase(frame_info->frame_tree_node_id_);
171 }
172 }
173
174 // And finally delete the frame info.
175 {
176 auto it2 = frame_info_set_.find(frame_info);
177 frame_info_set_.erase(it2);
178 }
179 }
180
GetMainFrame()181 CefRefPtr<CefFrameHostImpl> CefBrowserInfo::GetMainFrame() {
182 base::AutoLock lock_scope(lock_);
183 DCHECK(browser_);
184 if (!main_frame_) {
185 // Create a temporary object that will eventually be updated with real
186 // routing information.
187 main_frame_ =
188 new CefFrameHostImpl(this, true, CefFrameHostImpl::kInvalidFrameId);
189 }
190 return main_frame_;
191 }
192
CreateTempSubFrame(int64_t parent_frame_id)193 CefRefPtr<CefFrameHostImpl> CefBrowserInfo::CreateTempSubFrame(
194 int64_t parent_frame_id) {
195 CefRefPtr<CefFrameHostImpl> parent = GetFrameForId(parent_frame_id);
196 if (!parent)
197 parent = GetMainFrame();
198 return new CefFrameHostImpl(this, false, parent->GetIdentifier());
199 }
200
GetFrameForHost(const content::RenderFrameHost * host,bool * is_guest_view) const201 CefRefPtr<CefFrameHostImpl> CefBrowserInfo::GetFrameForHost(
202 const content::RenderFrameHost* host,
203 bool* is_guest_view) const {
204 if (is_guest_view)
205 *is_guest_view = false;
206
207 if (!host)
208 return nullptr;
209
210 return GetFrameForId(CefFrameHostImpl::MakeFrameId(host), is_guest_view);
211 }
212
GetFrameForRoute(int32_t render_process_id,int32_t render_routing_id,bool * is_guest_view) const213 CefRefPtr<CefFrameHostImpl> CefBrowserInfo::GetFrameForRoute(
214 int32_t render_process_id,
215 int32_t render_routing_id,
216 bool* is_guest_view) const {
217 if (is_guest_view)
218 *is_guest_view = false;
219
220 if (render_process_id < 0 || render_routing_id < 0)
221 return nullptr;
222
223 return GetFrameForId(
224 CefFrameHostImpl::MakeFrameId(render_process_id, render_routing_id),
225 is_guest_view);
226 }
227
GetFrameForId(int64_t frame_id,bool * is_guest_view) const228 CefRefPtr<CefFrameHostImpl> CefBrowserInfo::GetFrameForId(
229 int64_t frame_id,
230 bool* is_guest_view) const {
231 if (is_guest_view)
232 *is_guest_view = false;
233
234 if (frame_id < 0)
235 return nullptr;
236
237 base::AutoLock lock_scope(lock_);
238
239 const auto it = frame_id_map_.find(frame_id);
240 if (it != frame_id_map_.end()) {
241 const auto info = it->second;
242
243 if (info->is_guest_view_) {
244 if (is_guest_view)
245 *is_guest_view = true;
246 return nullptr;
247 }
248
249 if (info->is_speculative_) {
250 if (info->is_main_frame_ && main_frame_) {
251 // Always prefer the non-speculative main frame.
252 return main_frame_;
253 } else {
254 // Always prefer an existing non-speculative frame for the same node ID.
255 bool is_guest_view_tmp;
256 auto frame = GetFrameForFrameTreeNodeInternal(info->frame_tree_node_id_,
257 &is_guest_view_tmp);
258 if (is_guest_view_tmp) {
259 if (is_guest_view)
260 *is_guest_view = true;
261 return nullptr;
262 }
263 if (frame)
264 return frame;
265 }
266
267 LOG(WARNING) << "Returning a speculative frame for frame id " << frame_id;
268 }
269
270 DCHECK(info->frame_);
271 return info->frame_;
272 }
273
274 return nullptr;
275 }
276
GetFrameForFrameTreeNode(int frame_tree_node_id,bool * is_guest_view) const277 CefRefPtr<CefFrameHostImpl> CefBrowserInfo::GetFrameForFrameTreeNode(
278 int frame_tree_node_id,
279 bool* is_guest_view) const {
280 if (is_guest_view)
281 *is_guest_view = false;
282
283 if (frame_tree_node_id < 0)
284 return nullptr;
285
286 base::AutoLock lock_scope(lock_);
287 return GetFrameForFrameTreeNodeInternal(frame_tree_node_id, is_guest_view);
288 }
289
GetAllFrames() const290 CefBrowserInfo::FrameHostList CefBrowserInfo::GetAllFrames() const {
291 base::AutoLock lock_scope(lock_);
292 FrameHostList frames;
293 for (const auto& info : frame_info_set_) {
294 if (info->frame_ && !info->is_speculative_)
295 frames.insert(info->frame_);
296 }
297 return frames;
298 }
299
NavigationLock()300 CefBrowserInfo::NavigationLock::NavigationLock() : weak_ptr_factory_(this) {}
301
~NavigationLock()302 CefBrowserInfo::NavigationLock::~NavigationLock() {
303 CEF_REQUIRE_UIT();
304 if (pending_action_) {
305 CEF_POST_TASK(CEF_UIT, std::move(pending_action_));
306 }
307 }
308
309 scoped_refptr<CefBrowserInfo::NavigationLock>
CreateNavigationLock()310 CefBrowserInfo::CreateNavigationLock() {
311 CEF_REQUIRE_UIT();
312 scoped_refptr<NavigationLock> lock;
313 if (!navigation_lock_) {
314 lock = new NavigationLock();
315 navigation_lock_ = lock->weak_ptr_factory_.GetWeakPtr();
316 } else {
317 lock = navigation_lock_.get();
318 }
319 return lock;
320 }
321
IsNavigationLocked(base::OnceClosure pending_action)322 bool CefBrowserInfo::IsNavigationLocked(base::OnceClosure pending_action) {
323 CEF_REQUIRE_UIT();
324 if (navigation_lock_) {
325 navigation_lock_->pending_action_ = std::move(pending_action);
326 return true;
327 }
328 return false;
329 }
330
MaybeUpdateFrameTreeNodeIdMap(FrameInfo * info)331 void CefBrowserInfo::MaybeUpdateFrameTreeNodeIdMap(FrameInfo* info) {
332 lock_.AssertAcquired();
333
334 auto it = frame_tree_node_id_map_.find(info->frame_tree_node_id_);
335 const bool has_entry = (it != frame_tree_node_id_map_.end());
336
337 if (has_entry && it->second == info) {
338 // Already mapping to |info|.
339 return;
340 }
341
342 // Don't replace an existing node ID entry with a speculative RFH, but do
343 // add an entry if one doesn't already exist.
344 if (!info->is_speculative_ || !has_entry) {
345 // A new RFH with the same node ID may be added before the old RFH is
346 // deleted. To avoid duplicate entries in the map remove the old entry, if
347 // any, before adding the new entry.
348 if (has_entry)
349 frame_tree_node_id_map_.erase(it);
350
351 frame_tree_node_id_map_.insert(
352 std::make_pair(info->frame_tree_node_id_, info));
353 }
354 }
355
GetFrameForFrameTreeNodeInternal(int frame_tree_node_id,bool * is_guest_view) const356 CefRefPtr<CefFrameHostImpl> CefBrowserInfo::GetFrameForFrameTreeNodeInternal(
357 int frame_tree_node_id,
358 bool* is_guest_view) const {
359 if (is_guest_view)
360 *is_guest_view = false;
361
362 lock_.AssertAcquired();
363
364 const auto it = frame_tree_node_id_map_.find(frame_tree_node_id);
365 if (it != frame_tree_node_id_map_.end()) {
366 const auto info = it->second;
367
368 LOG_IF(WARNING, info->is_speculative_)
369 << "Returning a speculative frame for node id " << frame_tree_node_id;
370
371 if (info->is_guest_view_) {
372 if (is_guest_view)
373 *is_guest_view = true;
374 return nullptr;
375 }
376
377 DCHECK(info->frame_);
378 return info->frame_;
379 }
380
381 return nullptr;
382 }
383
RemoveAllFrames()384 void CefBrowserInfo::RemoveAllFrames() {
385 lock_.AssertAcquired();
386
387 // Clear the lookup maps.
388 frame_id_map_.clear();
389 frame_tree_node_id_map_.clear();
390
391 // Explicitly Detach main frames.
392 for (auto& info : frame_info_set_) {
393 if (info->frame_ && info->is_main_frame_)
394 info->frame_->Detach();
395 }
396
397 if (main_frame_) {
398 main_frame_->Detach();
399 main_frame_ = nullptr;
400 }
401
402 // And finally delete the frame info.
403 frame_info_set_.clear();
404 }
405