| // 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. |
| { |
| auto it2 = frame_tree_node_id_map_.find(frame_info->frame_tree_node_id_); |
| if (it2 != frame_tree_node_id_map_.end() && it2->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(); |
| } |