mirror of
				https://bitbucket.org/chromiumembedded/cef
				synced 2025-06-05 21:39:12 +02:00 
			
		
		
		
	The behavior has changed as follows with NetworkService enabled: - All pending and in-progress requests will now be aborted when the CEF context or associated browser is destroyed. The OnResourceLoadComplete callback will now also be called in this case for in-progress requests that have a handler. - The CefResourceHandler::Cancel method will now always be called when resource handling is complete, irrespective of whether handling completed successfully. - Request callbacks that arrive after the OnBeforeClose callback for the associated browser (which may happen for in-progress requests that are aborted on browser destruction) will now always have a non-nullptr CefBrowser parameter. - Allow empty parameters to CefRequest and CefResponse methods where it makes sense (e.g. resetting default response state, or clearing a referrer value). - Fixed a reference loop that was keeping CefResourceHandler objects from being destroyed if they were holding a callback reference (from ProcessRequest, ReadResponse, etc.) during CEF context or associated browser destruction. - Fixed an issue where the main frame was not detached on browser destruction which could cause a crash due to RFH use-after-free (see issue #2498). To test: All unit tests pass as expected.
		
			
				
	
	
		
			370 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			370 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Copyright (c) 2012 The Chromium Embedded Framework Authors. All rights
 | |
| // reserved. Use of this source code is governed by a BSD-style license that can
 | |
| // be found in the LICENSE file.
 | |
| 
 | |
| #include "libcef/browser/browser_info.h"
 | |
| 
 | |
| #include "libcef/browser/browser_host_impl.h"
 | |
| #include "libcef/browser/thread_util.h"
 | |
| #include "libcef/common/values_impl.h"
 | |
| 
 | |
| #include "base/logging.h"
 | |
| #include "content/browser/frame_host/frame_tree_node.h"
 | |
| #include "content/browser/frame_host/render_frame_host_impl.h"
 | |
| #include "content/public/browser/render_process_host.h"
 | |
| #include "ipc/ipc_message.h"
 | |
| 
 | |
| CefBrowserInfo::FrameInfo::~FrameInfo() {
 | |
|   if (frame_ && !is_main_frame_) {
 | |
|     // Disassociate sub-frames from the browser.
 | |
|     frame_->Detach();
 | |
|   }
 | |
| }
 | |
| 
 | |
| CefBrowserInfo::CefBrowserInfo(int browser_id,
 | |
|                                bool is_popup,
 | |
|                                bool is_windowless,
 | |
|                                CefRefPtr<CefDictionaryValue> extra_info)
 | |
|     : browser_id_(browser_id),
 | |
|       is_popup_(is_popup),
 | |
|       is_windowless_(is_windowless),
 | |
|       extra_info_(extra_info) {
 | |
|   DCHECK_GT(browser_id, 0);
 | |
| }
 | |
| 
 | |
| CefBrowserInfo::~CefBrowserInfo() {}
 | |
| 
 | |
| CefRefPtr<CefBrowserHostImpl> CefBrowserInfo::browser() const {
 | |
|   base::AutoLock lock_scope(lock_);
 | |
|   return browser_;
 | |
| }
 | |
| 
 | |
| void CefBrowserInfo::SetBrowser(CefRefPtr<CefBrowserHostImpl> browser) {
 | |
|   base::AutoLock lock_scope(lock_);
 | |
|   browser_ = browser;
 | |
| 
 | |
|   if (!browser) {
 | |
|     RemoveAllFrames();
 | |
|   }
 | |
| }
 | |
| 
 | |
| void CefBrowserInfo::MaybeCreateFrame(content::RenderFrameHost* host,
 | |
|                                       bool is_guest_view) {
 | |
|   CEF_REQUIRE_UIT();
 | |
| 
 | |
|   const auto frame_id = CefFrameHostImpl::MakeFrameId(host);
 | |
|   const int frame_tree_node_id = host->GetFrameTreeNodeId();
 | |
|   const bool is_main_frame = (host->GetParent() == nullptr);
 | |
| 
 | |
|   // A speculative RFH will be created in response to a browser-initiated
 | |
|   // cross-origin navigation (e.g. via LoadURL) and eventually either discarded
 | |
|   // or swapped in based on whether the navigation is committed. We'll create a
 | |
|   // frame object for the speculative RFH so that it can be found by
 | |
|   // frame/routing ID. However, we won't replace the main frame with a
 | |
|   // speculative RFH until after it's swapped in, and we'll generally prefer to
 | |
|   // return a non-speculative RFH for the same node ID if one exists.
 | |
|   const bool is_speculative = (static_cast<content::RenderFrameHostImpl*>(host)
 | |
|                                    ->frame_tree_node()
 | |
|                                    ->render_manager()
 | |
|                                    ->current_frame_host() != host);
 | |
| 
 | |
|   base::AutoLock lock_scope(lock_);
 | |
|   DCHECK(browser_);
 | |
| 
 | |
|   const auto it = frame_id_map_.find(frame_id);
 | |
|   if (it != frame_id_map_.end()) {
 | |
|     auto info = it->second;
 | |
| 
 | |
| #if DCHECK_IS_ON()
 | |
|     // Check that the frame info hasn't changed unexpectedly.
 | |
|     DCHECK_EQ(info->frame_id_, frame_id);
 | |
|     DCHECK_EQ(info->frame_tree_node_id_, frame_tree_node_id);
 | |
|     DCHECK_EQ(info->is_guest_view_, is_guest_view);
 | |
|     DCHECK_EQ(info->is_main_frame_, is_main_frame);
 | |
| #endif
 | |
| 
 | |
|     if (!info->is_guest_view_ && info->is_speculative_ && !is_speculative) {
 | |
|       // Upgrade the frame info from speculative to non-speculative.
 | |
|       if (info->is_main_frame_) {
 | |
|         if (main_frame_) {
 | |
|           // Update the existing main frame object.
 | |
|           main_frame_->SetRenderFrameHost(host);
 | |
|           info->frame_ = main_frame_;
 | |
|         } else {
 | |
|           // Set the main frame object.
 | |
|           main_frame_ = info->frame_;
 | |
|         }
 | |
|       }
 | |
|       info->is_speculative_ = false;
 | |
|       MaybeUpdateFrameTreeNodeIdMap(info);
 | |
|     }
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   auto frame_info = new FrameInfo;
 | |
|   frame_info->host_ = host;
 | |
|   frame_info->frame_id_ = frame_id;
 | |
|   frame_info->frame_tree_node_id_ = frame_tree_node_id;
 | |
|   frame_info->is_guest_view_ = is_guest_view;
 | |
|   frame_info->is_main_frame_ = is_main_frame;
 | |
|   frame_info->is_speculative_ = is_speculative;
 | |
| 
 | |
|   // Guest views don't get their own CefBrowser or CefFrame objects.
 | |
|   if (!is_guest_view) {
 | |
|     if (is_main_frame && main_frame_ && !is_speculative) {
 | |
|       // Update the existing main frame object.
 | |
|       main_frame_->SetRenderFrameHost(host);
 | |
|       frame_info->frame_ = main_frame_;
 | |
|     } else {
 | |
|       // Create a new frame object.
 | |
|       frame_info->frame_ = new CefFrameHostImpl(this, host);
 | |
|       if (is_main_frame && !is_speculative) {
 | |
|         main_frame_ = frame_info->frame_;
 | |
|       }
 | |
|     }
 | |
| #if DCHECK_IS_ON()
 | |
|     // Check that the frame info hasn't changed unexpectedly.
 | |
|     DCHECK_EQ(frame_id, frame_info->frame_->GetIdentifier());
 | |
|     DCHECK_EQ(frame_info->is_main_frame_, frame_info->frame_->IsMain());
 | |
| #endif
 | |
|   }
 | |
| 
 | |
|   browser_->request_context()->OnRenderFrameCreated(
 | |
|       host->GetProcess()->GetID(), host->GetRoutingID(), frame_tree_node_id,
 | |
|       is_main_frame, is_guest_view);
 | |
| 
 | |
|   // Populate the lookup maps.
 | |
|   frame_id_map_.insert(std::make_pair(frame_id, frame_info));
 | |
|   MaybeUpdateFrameTreeNodeIdMap(frame_info);
 | |
| 
 | |
|   // And finally set the ownership.
 | |
|   frame_info_set_.insert(base::WrapUnique(frame_info));
 | |
| }
 | |
| 
 | |
| void CefBrowserInfo::RemoveFrame(content::RenderFrameHost* host) {
 | |
|   CEF_REQUIRE_UIT();
 | |
| 
 | |
|   base::AutoLock lock_scope(lock_);
 | |
| 
 | |
|   const auto frame_id = CefFrameHostImpl::MakeFrameId(host);
 | |
| 
 | |
|   auto it = frame_id_map_.find(frame_id);
 | |
|   DCHECK(it != frame_id_map_.end());
 | |
| 
 | |
|   auto frame_info = it->second;
 | |
| 
 | |
|   browser_->request_context()->OnRenderFrameDeleted(
 | |
|       host->GetProcess()->GetID(), host->GetRoutingID(),
 | |
|       frame_info->frame_tree_node_id_, frame_info->is_main_frame_,
 | |
|       frame_info->is_guest_view_);
 | |
| 
 | |
|   // Remove from the lookup maps.
 | |
|   frame_id_map_.erase(it);
 | |
| 
 | |
|   // A new RFH with the same node ID may be added before the old RFH is deleted,
 | |
|   // or this might be a speculative RFH. Therefore only delete the map entry if
 | |
|   // it's currently pointing to the to-be-deleted frame info object.
 | |
|   if (frame_tree_node_id_map_.find(frame_info->frame_tree_node_id_)->second ==
 | |
|       frame_info) {
 | |
|     frame_tree_node_id_map_.erase(frame_info->frame_tree_node_id_);
 | |
|   }
 | |
| 
 | |
|   // And finally delete the frame info.
 | |
|   auto it2 = frame_info_set_.find(frame_info);
 | |
|   frame_info_set_.erase(it2);
 | |
| }
 | |
| 
 | |
| CefRefPtr<CefFrameHostImpl> CefBrowserInfo::GetMainFrame() {
 | |
|   base::AutoLock lock_scope(lock_);
 | |
|   DCHECK(browser_);
 | |
|   if (!main_frame_) {
 | |
|     // Create a temporary object that will eventually be updated with real
 | |
|     // routing information.
 | |
|     main_frame_ =
 | |
|         new CefFrameHostImpl(this, true, CefFrameHostImpl::kInvalidFrameId);
 | |
|   }
 | |
|   return main_frame_;
 | |
| }
 | |
| 
 | |
| CefRefPtr<CefFrameHostImpl> CefBrowserInfo::CreateTempSubFrame(
 | |
|     int64_t parent_frame_id) {
 | |
|   CefRefPtr<CefFrameHostImpl> parent = GetFrameForId(parent_frame_id);
 | |
|   if (!parent)
 | |
|     parent = GetMainFrame();
 | |
|   return new CefFrameHostImpl(this, false, parent->GetIdentifier());
 | |
| }
 | |
| 
 | |
| CefRefPtr<CefFrameHostImpl> CefBrowserInfo::GetFrameForHost(
 | |
|     const content::RenderFrameHost* host,
 | |
|     bool* is_guest_view) const {
 | |
|   if (is_guest_view)
 | |
|     *is_guest_view = false;
 | |
| 
 | |
|   if (!host)
 | |
|     return nullptr;
 | |
| 
 | |
|   return GetFrameForId(CefFrameHostImpl::MakeFrameId(host), is_guest_view);
 | |
| }
 | |
| 
 | |
| CefRefPtr<CefFrameHostImpl> CefBrowserInfo::GetFrameForRoute(
 | |
|     int32_t render_process_id,
 | |
|     int32_t render_routing_id,
 | |
|     bool* is_guest_view) const {
 | |
|   if (is_guest_view)
 | |
|     *is_guest_view = false;
 | |
| 
 | |
|   if (render_process_id < 0 || render_routing_id < 0)
 | |
|     return nullptr;
 | |
| 
 | |
|   return GetFrameForId(
 | |
|       CefFrameHostImpl::MakeFrameId(render_process_id, render_routing_id),
 | |
|       is_guest_view);
 | |
| }
 | |
| 
 | |
| CefRefPtr<CefFrameHostImpl> CefBrowserInfo::GetFrameForId(
 | |
|     int64_t frame_id,
 | |
|     bool* is_guest_view) const {
 | |
|   if (is_guest_view)
 | |
|     *is_guest_view = false;
 | |
| 
 | |
|   if (frame_id < 0)
 | |
|     return nullptr;
 | |
| 
 | |
|   base::AutoLock lock_scope(lock_);
 | |
| 
 | |
|   const auto it = frame_id_map_.find(frame_id);
 | |
|   if (it != frame_id_map_.end()) {
 | |
|     const auto info = it->second;
 | |
| 
 | |
|     if (info->is_guest_view_) {
 | |
|       if (is_guest_view)
 | |
|         *is_guest_view = true;
 | |
|       return nullptr;
 | |
|     }
 | |
| 
 | |
|     if (info->is_speculative_) {
 | |
|       if (info->is_main_frame_ && main_frame_) {
 | |
|         // Always prefer the non-speculative main frame.
 | |
|         return main_frame_;
 | |
|       } else {
 | |
|         // Always prefer an existing non-speculative frame for the same node ID.
 | |
|         bool is_guest_view_tmp;
 | |
|         auto frame = GetFrameForFrameTreeNodeInternal(info->frame_tree_node_id_,
 | |
|                                                       &is_guest_view_tmp);
 | |
|         if (is_guest_view_tmp) {
 | |
|           if (is_guest_view)
 | |
|             *is_guest_view = true;
 | |
|           return nullptr;
 | |
|         }
 | |
|         if (frame)
 | |
|           return frame;
 | |
|       }
 | |
| 
 | |
|       LOG(WARNING) << "Returning a speculative frame for frame id " << frame_id;
 | |
|     }
 | |
| 
 | |
|     DCHECK(info->frame_);
 | |
|     return info->frame_;
 | |
|   }
 | |
| 
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| CefRefPtr<CefFrameHostImpl> CefBrowserInfo::GetFrameForFrameTreeNode(
 | |
|     int frame_tree_node_id,
 | |
|     bool* is_guest_view) const {
 | |
|   if (is_guest_view)
 | |
|     *is_guest_view = false;
 | |
| 
 | |
|   if (frame_tree_node_id < 0)
 | |
|     return nullptr;
 | |
| 
 | |
|   base::AutoLock lock_scope(lock_);
 | |
|   return GetFrameForFrameTreeNodeInternal(frame_tree_node_id, is_guest_view);
 | |
| }
 | |
| 
 | |
| CefBrowserInfo::FrameHostList CefBrowserInfo::GetAllFrames() const {
 | |
|   base::AutoLock lock_scope(lock_);
 | |
|   FrameHostList frames;
 | |
|   for (const auto& info : frame_info_set_) {
 | |
|     if (info->frame_ && !info->is_speculative_)
 | |
|       frames.insert(info->frame_);
 | |
|   }
 | |
|   return frames;
 | |
| }
 | |
| 
 | |
| void CefBrowserInfo::MaybeUpdateFrameTreeNodeIdMap(FrameInfo* info) {
 | |
|   lock_.AssertAcquired();
 | |
| 
 | |
|   auto it = frame_tree_node_id_map_.find(info->frame_tree_node_id_);
 | |
|   const bool has_entry = (it != frame_tree_node_id_map_.end());
 | |
| 
 | |
|   if (has_entry && it->second == info) {
 | |
|     // Already mapping to |info|.
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // Don't replace an existing node ID entry with a speculative RFH, but do
 | |
|   // add an entry if one doesn't already exist.
 | |
|   if (!info->is_speculative_ || !has_entry) {
 | |
|     // A new RFH with the same node ID may be added before the old RFH is
 | |
|     // deleted. To avoid duplicate entries in the map remove the old entry, if
 | |
|     // any, before adding the new entry.
 | |
|     if (has_entry)
 | |
|       frame_tree_node_id_map_.erase(it);
 | |
| 
 | |
|     frame_tree_node_id_map_.insert(
 | |
|         std::make_pair(info->frame_tree_node_id_, info));
 | |
|   }
 | |
| }
 | |
| 
 | |
| CefRefPtr<CefFrameHostImpl> CefBrowserInfo::GetFrameForFrameTreeNodeInternal(
 | |
|     int frame_tree_node_id,
 | |
|     bool* is_guest_view) const {
 | |
|   if (is_guest_view)
 | |
|     *is_guest_view = false;
 | |
| 
 | |
|   lock_.AssertAcquired();
 | |
| 
 | |
|   const auto it = frame_tree_node_id_map_.find(frame_tree_node_id);
 | |
|   if (it != frame_tree_node_id_map_.end()) {
 | |
|     const auto info = it->second;
 | |
| 
 | |
|     LOG_IF(WARNING, info->is_speculative_)
 | |
|         << "Returning a speculative frame for node id " << frame_tree_node_id;
 | |
| 
 | |
|     if (info->is_guest_view_) {
 | |
|       if (is_guest_view)
 | |
|         *is_guest_view = true;
 | |
|       return nullptr;
 | |
|     }
 | |
| 
 | |
|     DCHECK(info->frame_);
 | |
|     return info->frame_;
 | |
|   }
 | |
| 
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| void CefBrowserInfo::RemoveAllFrames() {
 | |
|   lock_.AssertAcquired();
 | |
| 
 | |
|   // Clear the lookup maps.
 | |
|   frame_id_map_.clear();
 | |
|   frame_tree_node_id_map_.clear();
 | |
| 
 | |
|   // Explicitly Detach main frames.
 | |
|   for (auto& info : frame_info_set_) {
 | |
|     if (info->frame_ && info->is_main_frame_)
 | |
|       info->frame_->Detach();
 | |
|   }
 | |
| 
 | |
|   if (main_frame_) {
 | |
|     main_frame_->Detach();
 | |
|     main_frame_ = nullptr;
 | |
|   }
 | |
| 
 | |
|   // And finally delete the frame info.
 | |
|   frame_info_set_.clear();
 | |
| }
 |