mirror of
				https://bitbucket.org/chromiumembedded/cef
				synced 2025-06-05 21:39:12 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			374 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			374 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.
 | 
						|
  {
 | 
						|
    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();
 | 
						|
}
 |