• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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