mirror of
				https://bitbucket.org/chromiumembedded/cef
				synced 2025-06-05 21:39:12 +02:00 
			
		
		
		
	Fix ceftest failures with BackForwardCache enabled (see issue #2421)
When BackForwardCache is enabled the old RFH tree may be cached instead of being immediately deleted as a result of main frame navigation. If a RFH is cached then delivery of the CefFrameHandler::OnFrameDetached callback will be delayed until the the RFH is ejected from the cache (possibly not occurring until the browser is destroyed). This change in OnFrameDetached timing was causing FrameHandlerTest.OrderSubCrossOrigin* to fail, and the inclusion of cached frames in CefBrowserInfo::GetAllFrames was causing FrameTest.NestedIframesDiffOrigin to fail. BackForwardCache is currently being tested via field trials (see https://crbug.com/1171298) and can be explicitly disabled using the `--disable-back-forward-cache` or `--disable-features=BackForwardCache` command-line flags.
This commit is contained in:
		@@ -243,6 +243,13 @@ void CefBrowserContentsDelegate::RenderFrameHostChanged(
 | 
			
		||||
  RenderFrameCreated(new_host);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CefBrowserContentsDelegate::RenderFrameHostStateChanged(
 | 
			
		||||
    content::RenderFrameHost* host,
 | 
			
		||||
    content::RenderFrameHost::LifecycleState old_state,
 | 
			
		||||
    content::RenderFrameHost::LifecycleState new_state) {
 | 
			
		||||
  browser_info_->FrameHostStateChanged(host, old_state, new_state);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CefBrowserContentsDelegate::RenderFrameDeleted(
 | 
			
		||||
    content::RenderFrameHost* render_frame_host) {
 | 
			
		||||
  const auto frame_id = CefFrameHostImpl::MakeFrameId(render_frame_host);
 | 
			
		||||
 
 | 
			
		||||
@@ -108,6 +108,10 @@ class CefBrowserContentsDelegate : public content::WebContentsDelegate,
 | 
			
		||||
  void RenderFrameCreated(content::RenderFrameHost* render_frame_host) override;
 | 
			
		||||
  void RenderFrameHostChanged(content::RenderFrameHost* old_host,
 | 
			
		||||
                              content::RenderFrameHost* new_host) override;
 | 
			
		||||
  void RenderFrameHostStateChanged(
 | 
			
		||||
      content::RenderFrameHost* host,
 | 
			
		||||
      content::RenderFrameHost::LifecycleState old_state,
 | 
			
		||||
      content::RenderFrameHost::LifecycleState new_state) override;
 | 
			
		||||
  void RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override;
 | 
			
		||||
  void RenderViewReady() override;
 | 
			
		||||
  void RenderProcessGone(base::TerminationStatus status) override;
 | 
			
		||||
 
 | 
			
		||||
@@ -153,6 +153,32 @@ void CefBrowserInfo::MaybeCreateFrame(content::RenderFrameHost* host,
 | 
			
		||||
  frame_info_set_.insert(base::WrapUnique(frame_info));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CefBrowserInfo::FrameHostStateChanged(
 | 
			
		||||
    content::RenderFrameHost* host,
 | 
			
		||||
    content::RenderFrameHost::LifecycleState old_state,
 | 
			
		||||
    content::RenderFrameHost::LifecycleState new_state) {
 | 
			
		||||
  CEF_REQUIRE_UIT();
 | 
			
		||||
 | 
			
		||||
  // We currently only care about BackForwardCache state changes.
 | 
			
		||||
  bool added_to_bfcache =
 | 
			
		||||
      new_state ==
 | 
			
		||||
      content::RenderFrameHost::LifecycleState::kInBackForwardCache;
 | 
			
		||||
  bool removed_from_bfcache =
 | 
			
		||||
      old_state ==
 | 
			
		||||
      content::RenderFrameHost::LifecycleState::kInBackForwardCache;
 | 
			
		||||
  if (!added_to_bfcache && !removed_from_bfcache)
 | 
			
		||||
    return;
 | 
			
		||||
 | 
			
		||||
  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());
 | 
			
		||||
  DCHECK((!it->second->is_in_bfcache_ && added_to_bfcache) ||
 | 
			
		||||
         (it->second->is_in_bfcache_ && removed_from_bfcache));
 | 
			
		||||
  it->second->is_in_bfcache_ = added_to_bfcache;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CefBrowserInfo::RemoveFrame(content::RenderFrameHost* host) {
 | 
			
		||||
  CEF_REQUIRE_UIT();
 | 
			
		||||
 | 
			
		||||
@@ -190,8 +216,8 @@ void CefBrowserInfo::RemoveFrame(content::RenderFrameHost* host) {
 | 
			
		||||
    // Explicitly Detach everything but the current main frame.
 | 
			
		||||
    const auto& frame_info = *it2;
 | 
			
		||||
    if (frame_info->frame_ && !frame_info->IsCurrentMainFrame()) {
 | 
			
		||||
      frame_info->frame_->Detach();
 | 
			
		||||
      MaybeNotifyFrameDetached(browser_, frame_info->frame_);
 | 
			
		||||
      if (frame_info->frame_->Detach())
 | 
			
		||||
        MaybeNotifyFrameDetached(browser_, frame_info->frame_);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    frame_info_set_.erase(it2);
 | 
			
		||||
@@ -314,8 +340,9 @@ 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_)
 | 
			
		||||
    if (info->frame_ && !info->is_speculative_ && !info->is_in_bfcache_) {
 | 
			
		||||
      frames.insert(info->frame_);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  return frames;
 | 
			
		||||
}
 | 
			
		||||
@@ -438,8 +465,8 @@ void CefBrowserInfo::SetMainFrame(CefRefPtr<CefBrowserHostBase> browser,
 | 
			
		||||
  CefRefPtr<CefFrameHostImpl> old_frame;
 | 
			
		||||
  if (main_frame_) {
 | 
			
		||||
    old_frame = main_frame_;
 | 
			
		||||
    old_frame->Detach();
 | 
			
		||||
    MaybeNotifyFrameDetached(browser, old_frame);
 | 
			
		||||
    if (old_frame->Detach())
 | 
			
		||||
      MaybeNotifyFrameDetached(browser, old_frame);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  main_frame_ = frame;
 | 
			
		||||
@@ -520,8 +547,8 @@ void CefBrowserInfo::RemoveAllFrames(
 | 
			
		||||
  // Explicitly Detach everything but the current main frame.
 | 
			
		||||
  for (auto& info : frame_info_set_) {
 | 
			
		||||
    if (info->frame_ && !info->IsCurrentMainFrame()) {
 | 
			
		||||
      info->frame_->Detach();
 | 
			
		||||
      MaybeNotifyFrameDetached(old_browser, info->frame_);
 | 
			
		||||
      if (info->frame_->Detach())
 | 
			
		||||
        MaybeNotifyFrameDetached(old_browser, info->frame_);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -19,10 +19,7 @@
 | 
			
		||||
#include "base/memory/weak_ptr.h"
 | 
			
		||||
#include "base/synchronization/lock.h"
 | 
			
		||||
#include "base/values.h"
 | 
			
		||||
 | 
			
		||||
namespace content {
 | 
			
		||||
class RenderFrameHost;
 | 
			
		||||
}
 | 
			
		||||
#include "content/public/browser/render_frame_host.h"
 | 
			
		||||
 | 
			
		||||
class CefBrowserHostBase;
 | 
			
		||||
class CefFrameHandler;
 | 
			
		||||
@@ -62,6 +59,13 @@ class CefBrowserInfo : public base::RefCountedThreadSafe<CefBrowserInfo> {
 | 
			
		||||
  // true).
 | 
			
		||||
  void MaybeCreateFrame(content::RenderFrameHost* host, bool is_guest_view);
 | 
			
		||||
 | 
			
		||||
  // Used to track state changes such as entering/exiting the BackForwardCache.
 | 
			
		||||
  // Called from CefBrowserContentsDelegate::RenderFrameHostStateChanged.
 | 
			
		||||
  void FrameHostStateChanged(
 | 
			
		||||
      content::RenderFrameHost* host,
 | 
			
		||||
      content::RenderFrameHost::LifecycleState old_state,
 | 
			
		||||
      content::RenderFrameHost::LifecycleState new_state);
 | 
			
		||||
 | 
			
		||||
  // Remove the frame record for |host|. Called for the main frame when the
 | 
			
		||||
  // RenderView is destroyed, or for a sub-frame when the associated RenderFrame
 | 
			
		||||
  // is destroyed in the renderer process.
 | 
			
		||||
@@ -170,7 +174,7 @@ class CefBrowserInfo : public base::RefCountedThreadSafe<CefBrowserInfo> {
 | 
			
		||||
    ~FrameInfo();
 | 
			
		||||
 | 
			
		||||
    inline bool IsCurrentMainFrame() const {
 | 
			
		||||
      return frame_ && is_main_frame_ && !is_speculative_;
 | 
			
		||||
      return frame_ && is_main_frame_ && !is_speculative_ && !is_in_bfcache_;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    content::RenderFrameHost* host_;
 | 
			
		||||
@@ -179,6 +183,7 @@ class CefBrowserInfo : public base::RefCountedThreadSafe<CefBrowserInfo> {
 | 
			
		||||
    bool is_guest_view_;
 | 
			
		||||
    bool is_main_frame_;
 | 
			
		||||
    bool is_speculative_;
 | 
			
		||||
    bool is_in_bfcache_ = false;
 | 
			
		||||
    CefRefPtr<CefFrameHostImpl> frame_;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -427,18 +427,22 @@ content::RenderFrameHost* CefFrameHostImpl::GetRenderFrameHost() const {
 | 
			
		||||
  return render_frame_host_;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void CefFrameHostImpl::Detach() {
 | 
			
		||||
bool CefFrameHostImpl::Detach() {
 | 
			
		||||
  CEF_REQUIRE_UIT();
 | 
			
		||||
 | 
			
		||||
  // May be called multiple times (e.g. from CefBrowserInfo SetMainFrame and
 | 
			
		||||
  // RemoveFrame).
 | 
			
		||||
  bool first_detach = false;
 | 
			
		||||
 | 
			
		||||
  // Should not be called for temporary frames.
 | 
			
		||||
  DCHECK(!is_temporary());
 | 
			
		||||
 | 
			
		||||
  {
 | 
			
		||||
    base::AutoLock lock_scope(state_lock_);
 | 
			
		||||
 | 
			
		||||
    // Should be called only once.
 | 
			
		||||
    DCHECK(browser_info_);
 | 
			
		||||
    browser_info_ = nullptr;
 | 
			
		||||
    if (browser_info_) {
 | 
			
		||||
      first_detach = true;
 | 
			
		||||
      browser_info_ = nullptr;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // In case we never attached, clean up.
 | 
			
		||||
@@ -449,6 +453,8 @@ void CefFrameHostImpl::Detach() {
 | 
			
		||||
  render_frame_.reset();
 | 
			
		||||
  render_frame_host_ = nullptr;
 | 
			
		||||
  is_attached_ = false;
 | 
			
		||||
 | 
			
		||||
  return first_detach;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// static
 | 
			
		||||
 
 | 
			
		||||
@@ -115,8 +115,9 @@ class CefFrameHostImpl : public CefFrame, public cef::mojom::BrowserFrame {
 | 
			
		||||
 | 
			
		||||
  // Owned frame objects will be detached explicitly when the associated
 | 
			
		||||
  // RenderFrame is deleted. Temporary frame objects will be detached
 | 
			
		||||
  // implicitly via CefBrowserInfo::browser() returning nullptr.
 | 
			
		||||
  void Detach();
 | 
			
		||||
  // implicitly via CefBrowserInfo::browser() returning nullptr. Returns true
 | 
			
		||||
  // if this was the first call to Detach() for the frame.
 | 
			
		||||
  bool Detach();
 | 
			
		||||
 | 
			
		||||
  // cef::mojom::BrowserFrame methods forwarded from CefBrowserFrame.
 | 
			
		||||
  void SendMessage(const std::string& name, base::Value arguments) override;
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,7 @@
 | 
			
		||||
 | 
			
		||||
#include <limits>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <queue>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <string>
 | 
			
		||||
@@ -101,9 +102,18 @@ struct FrameStatus {
 | 
			
		||||
  int64 frame_id() const { return frame_id_; }
 | 
			
		||||
  bool is_main() const { return is_main_; }
 | 
			
		||||
 | 
			
		||||
  bool AllQueriesDelivered() const {
 | 
			
		||||
  bool AllQueriesDelivered(std::string* msg = nullptr) const {
 | 
			
		||||
    EXPECT_UI_THREAD();
 | 
			
		||||
    return delivered_query_ct_ == expected_query_ct_;
 | 
			
		||||
    const int expected_ct = is_temporary_ ? 0 : expected_query_ct_;
 | 
			
		||||
#if VERBOSE_DEBUGGING
 | 
			
		||||
    if (msg) {
 | 
			
		||||
      std::stringstream ss;
 | 
			
		||||
      ss << ident_str_ << "(expected=" << expected_ct
 | 
			
		||||
         << " delivered=" << delivered_query_ct_ << ")";
 | 
			
		||||
      *msg += ss.str();
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    return delivered_query_ct_ == expected_ct;
 | 
			
		||||
  }
 | 
			
		||||
  int QueriesDeliveredCount() const {
 | 
			
		||||
    EXPECT_UI_THREAD();
 | 
			
		||||
@@ -114,7 +124,22 @@ struct FrameStatus {
 | 
			
		||||
    return frame->GetIdentifier() == frame_id();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool IsLoaded() const { return got_callback_[LOAD_END]; }
 | 
			
		||||
  bool IsLoaded(std::string* msg = nullptr) const {
 | 
			
		||||
#if VERBOSE_DEBUGGING
 | 
			
		||||
    if (msg) {
 | 
			
		||||
      std::stringstream ss;
 | 
			
		||||
      ss << ident_str_ << "(";
 | 
			
		||||
      for (int i = 0; i <= LOAD_END; ++i) {
 | 
			
		||||
        ss << GetCallbackName(i) << "=" << got_callback_[i];
 | 
			
		||||
        if (i < LOAD_END)
 | 
			
		||||
          ss << " ";
 | 
			
		||||
      }
 | 
			
		||||
      ss << ")";
 | 
			
		||||
      *msg += ss.str();
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
    return got_callback_[LOAD_END];
 | 
			
		||||
  }
 | 
			
		||||
  bool IsDetached() const { return got_callback_[FRAME_DETACHED]; }
 | 
			
		||||
 | 
			
		||||
  void SetIsFirstMain(bool val) {
 | 
			
		||||
@@ -130,6 +155,12 @@ struct FrameStatus {
 | 
			
		||||
    is_last_main_ = val;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void SetIsTemporary(bool val) {
 | 
			
		||||
    EXPECT_FALSE(is_main_);
 | 
			
		||||
    is_temporary_ = val;
 | 
			
		||||
  }
 | 
			
		||||
  bool IsTemporary() const { return is_temporary_; }
 | 
			
		||||
 | 
			
		||||
  void SetAdditionalDebugInfo(const std::string& debug_info) {
 | 
			
		||||
    debug_info_ = debug_info;
 | 
			
		||||
  }
 | 
			
		||||
@@ -297,13 +328,19 @@ struct FrameStatus {
 | 
			
		||||
    // Verify that all expected callbacks have executed.
 | 
			
		||||
    VerifyCallbackStatus(__FUNCTION__, CALLBACK_LAST + 1);
 | 
			
		||||
 | 
			
		||||
    // Verify that all expected messages have been sent and received.
 | 
			
		||||
    EXPECT_EQ(expected_query_ct_, delivered_query_ct_);
 | 
			
		||||
    EXPECT_EQ(0U, pending_queries_.size());
 | 
			
		||||
    while (!pending_queries_.empty()) {
 | 
			
		||||
      ADD_FAILURE() << "Query sent but not received: "
 | 
			
		||||
                    << pending_queries_.front();
 | 
			
		||||
      pending_queries_.pop();
 | 
			
		||||
    if (is_temporary_) {
 | 
			
		||||
      // Should not receive any queries.
 | 
			
		||||
      EXPECT_FALSE(is_main_);
 | 
			
		||||
      EXPECT_EQ(0, delivered_query_ct_);
 | 
			
		||||
    } else {
 | 
			
		||||
      // Verify that all expected messages have been sent and received.
 | 
			
		||||
      EXPECT_EQ(expected_query_ct_, delivered_query_ct_);
 | 
			
		||||
      EXPECT_EQ(0U, pending_queries_.size());
 | 
			
		||||
      while (!pending_queries_.empty()) {
 | 
			
		||||
        ADD_FAILURE() << "Query sent but not received: "
 | 
			
		||||
                      << pending_queries_.front();
 | 
			
		||||
        pending_queries_.pop();
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -340,6 +377,14 @@ struct FrameStatus {
 | 
			
		||||
      if (callback == MAIN_FRAME_CHANGED_REMOVED && is_last_main_) {
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
    } else if (is_temporary_) {
 | 
			
		||||
      // For cross-process sub-frame navigation a sub-frame is first created in
 | 
			
		||||
      // the parent's renderer process. That sub-frame is then discarded after
 | 
			
		||||
      // the real cross-origin sub-frame is created in a different renderer
 | 
			
		||||
      // process. These discarded sub-frames will get OnFrameCreated/
 | 
			
		||||
      // OnFrameAttached immediately followed by OnFrameDetached.
 | 
			
		||||
      return callback == FRAME_CREATED || callback == FRAME_ATTACHED ||
 | 
			
		||||
             callback == FRAME_DETACHED;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
@@ -442,6 +487,7 @@ struct FrameStatus {
 | 
			
		||||
 | 
			
		||||
  bool is_first_main_ = false;
 | 
			
		||||
  bool is_last_main_ = false;
 | 
			
		||||
  bool is_temporary_ = false;
 | 
			
		||||
  std::string debug_info_;
 | 
			
		||||
 | 
			
		||||
  bool got_before_close_ = false;
 | 
			
		||||
@@ -642,22 +688,35 @@ class OrderMainTestHandler : public RoutingTestHandler, public CefFrameHandler {
 | 
			
		||||
 | 
			
		||||
  virtual std::string GetAdditionalDebugInfo() const { return std::string(); }
 | 
			
		||||
 | 
			
		||||
  virtual bool AllQueriesDelivered() const {
 | 
			
		||||
  virtual bool AllQueriesDelivered(std::string* msg = nullptr) const {
 | 
			
		||||
    EXPECT_UI_THREAD();
 | 
			
		||||
    if (pending_main_frame_)
 | 
			
		||||
      return false;
 | 
			
		||||
    return current_main_frame_->AllQueriesDelivered();
 | 
			
		||||
    return current_main_frame_->AllQueriesDelivered(msg);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  virtual bool AllFramesLoaded() const {
 | 
			
		||||
  virtual bool AllFramesLoaded(std::string* msg = nullptr) const {
 | 
			
		||||
    EXPECT_UI_THREAD();
 | 
			
		||||
    if (pending_main_frame_)
 | 
			
		||||
      return false;
 | 
			
		||||
    return current_main_frame_->IsLoaded();
 | 
			
		||||
    return current_main_frame_->IsLoaded(msg);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void MaybeDestroyTest() {
 | 
			
		||||
#if VERBOSE_DEBUGGING
 | 
			
		||||
    std::string delivered_msg, loaded_msg;
 | 
			
		||||
    const bool all_queries_delivered = AllQueriesDelivered(&delivered_msg);
 | 
			
		||||
    const bool all_frames_loaded = AllFramesLoaded(&loaded_msg);
 | 
			
		||||
    LOG(INFO) << (current_main_frame_ ? current_main_frame_->GetDebugString()
 | 
			
		||||
                                      : "")
 | 
			
		||||
              << " AllQueriesDelivered=" << all_queries_delivered << " {"
 | 
			
		||||
              << delivered_msg << "}"
 | 
			
		||||
              << " AllFramesLoaded=" << all_frames_loaded << " {" << loaded_msg
 | 
			
		||||
              << "}";
 | 
			
		||||
    if (all_queries_delivered && all_frames_loaded) {
 | 
			
		||||
#else
 | 
			
		||||
    if (AllQueriesDelivered() && AllFramesLoaded()) {
 | 
			
		||||
#endif
 | 
			
		||||
      const std::string& next_url = GetNextMainURL();
 | 
			
		||||
      if (!next_url.empty()) {
 | 
			
		||||
        if (!IsCrossOrigin()) {
 | 
			
		||||
@@ -818,9 +877,15 @@ class FrameStatusMap {
 | 
			
		||||
      : expected_frame_ct_(expected_frame_ct) {}
 | 
			
		||||
  ~FrameStatusMap() { EXPECT_TRUE(frame_map_.empty()); }
 | 
			
		||||
 | 
			
		||||
  bool Contains(CefRefPtr<CefFrame> frame) const {
 | 
			
		||||
    return frame_map_.find(frame->GetIdentifier()) != frame_map_.end();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  FrameStatus* CreateFrameStatus(CefRefPtr<CefFrame> frame) {
 | 
			
		||||
    EXPECT_UI_THREAD();
 | 
			
		||||
 | 
			
		||||
    EXPECT_LT(size(), expected_frame_ct_);
 | 
			
		||||
 | 
			
		||||
    const int64 id = frame->GetIdentifier();
 | 
			
		||||
    EXPECT_NE(kInvalidFrameId, id);
 | 
			
		||||
    EXPECT_EQ(frame_map_.find(id), frame_map_.end());
 | 
			
		||||
@@ -854,27 +919,57 @@ class FrameStatusMap {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool AllQueriesDelivered() const {
 | 
			
		||||
    if (size() != expected_frame_ct_)
 | 
			
		||||
  bool AllQueriesDelivered(std::string* msg = nullptr) const {
 | 
			
		||||
    if (size() != expected_frame_ct_) {
 | 
			
		||||
#if VERBOSE_DEBUGGING
 | 
			
		||||
      if (msg) {
 | 
			
		||||
        std::stringstream ss;
 | 
			
		||||
        ss << " SUB COUNT MISMATCH! size=" << size()
 | 
			
		||||
           << " expected=" << expected_frame_ct_;
 | 
			
		||||
        *msg += ss.str();
 | 
			
		||||
      }
 | 
			
		||||
#endif
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Map::const_iterator it = frame_map_.begin();
 | 
			
		||||
    for (; it != frame_map_.end(); ++it) {
 | 
			
		||||
      if (!it->second->AllQueriesDelivered())
 | 
			
		||||
      if (!it->second->AllQueriesDelivered(msg)) {
 | 
			
		||||
#if VERBOSE_DEBUGGING
 | 
			
		||||
        if (msg) {
 | 
			
		||||
          *msg += " " + it->second->GetDebugString() + " PENDING";
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool AllFramesLoaded() const {
 | 
			
		||||
    if (size() != expected_frame_ct_)
 | 
			
		||||
  bool AllFramesLoaded(std::string* msg = nullptr) const {
 | 
			
		||||
    if (size() != expected_frame_ct_) {
 | 
			
		||||
#if VERBOSE_DEBUGGING
 | 
			
		||||
      if (msg) {
 | 
			
		||||
        std::stringstream ss;
 | 
			
		||||
        ss << " SUB COUNT MISMATCH! size=" << size()
 | 
			
		||||
           << " expected=" << expected_frame_ct_;
 | 
			
		||||
        *msg += ss.str();
 | 
			
		||||
      }
 | 
			
		||||
#endif
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Map::const_iterator it = frame_map_.begin();
 | 
			
		||||
    for (; it != frame_map_.end(); ++it) {
 | 
			
		||||
      if (!it->second->IsLoaded())
 | 
			
		||||
      if (!it->second->IsTemporary() && !it->second->IsLoaded(msg)) {
 | 
			
		||||
#if VERBOSE_DEBUGGING
 | 
			
		||||
        if (msg) {
 | 
			
		||||
          *msg += " " + it->second->GetDebugString() + " PENDING";
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
@@ -923,14 +1018,14 @@ class OrderSubTestHandler : public NavigateOrderMainTestHandler {
 | 
			
		||||
    SUBFRAME_CHILDREN,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  OrderSubTestHandler(bool cross_origin, int additional_nav_ct, TestMode mode)
 | 
			
		||||
  OrderSubTestHandler(bool cross_origin,
 | 
			
		||||
                      int additional_nav_ct,
 | 
			
		||||
                      TestMode mode,
 | 
			
		||||
                      size_t expected_frame_ct = 2U)
 | 
			
		||||
      : NavigateOrderMainTestHandler(cross_origin, additional_nav_ct),
 | 
			
		||||
        test_mode_(mode),
 | 
			
		||||
        expected_frame_ct_(2U) {}
 | 
			
		||||
  ~OrderSubTestHandler() override {
 | 
			
		||||
    EXPECT_FALSE(current_frame_map_);
 | 
			
		||||
    EXPECT_FALSE(previous_frame_map_);
 | 
			
		||||
  }
 | 
			
		||||
        expected_frame_ct_(expected_frame_ct) {}
 | 
			
		||||
  ~OrderSubTestHandler() override { EXPECT_TRUE(frame_maps_.empty()); }
 | 
			
		||||
 | 
			
		||||
  void RunTest() override {
 | 
			
		||||
    for (int i = 0; i <= additional_nav_ct(); i++) {
 | 
			
		||||
@@ -944,9 +1039,9 @@ class OrderSubTestHandler : public NavigateOrderMainTestHandler {
 | 
			
		||||
  void OnBeforeClose(CefRefPtr<CefBrowser> browser) override {
 | 
			
		||||
    NavigateOrderMainTestHandler::OnBeforeClose(browser);
 | 
			
		||||
 | 
			
		||||
    if (previous_frame_map_) {
 | 
			
		||||
    for (auto& map : frame_maps_) {
 | 
			
		||||
      // Also need to notify any sub-frames.
 | 
			
		||||
      previous_frame_map_->OnBeforeClose(browser);
 | 
			
		||||
      map->OnBeforeClose(browser);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@@ -957,7 +1052,8 @@ class OrderSubTestHandler : public NavigateOrderMainTestHandler {
 | 
			
		||||
               bool persistent,
 | 
			
		||||
               CefRefPtr<Callback> callback) override {
 | 
			
		||||
    if (!frame->IsMain()) {
 | 
			
		||||
      auto status = current_frame_map_->GetFrameStatus(frame);
 | 
			
		||||
      auto map = GetFrameMap(frame);
 | 
			
		||||
      auto status = map->GetFrameStatus(frame);
 | 
			
		||||
      status->OnQuery(browser, frame, request);
 | 
			
		||||
      if (status->AllQueriesDelivered()) {
 | 
			
		||||
        MaybeDestroyTest();
 | 
			
		||||
@@ -973,9 +1069,8 @@ class OrderSubTestHandler : public NavigateOrderMainTestHandler {
 | 
			
		||||
                      CefRefPtr<CefFrame> frame) override {
 | 
			
		||||
    if (!frame->IsMain()) {
 | 
			
		||||
      // Potentially the first notification of a new sub-frame after navigation.
 | 
			
		||||
      MaybeCreateFrameMap();
 | 
			
		||||
 | 
			
		||||
      auto status = current_frame_map_->CreateFrameStatus(frame);
 | 
			
		||||
      auto map = GetOrCreateFrameMap(frame);
 | 
			
		||||
      auto status = map->CreateFrameStatus(frame);
 | 
			
		||||
      status->SetAdditionalDebugInfo(GetAdditionalDebugInfo());
 | 
			
		||||
      status->OnFrameCreated(browser, frame);
 | 
			
		||||
      return;
 | 
			
		||||
@@ -987,7 +1082,8 @@ class OrderSubTestHandler : public NavigateOrderMainTestHandler {
 | 
			
		||||
  void OnFrameAttached(CefRefPtr<CefBrowser> browser,
 | 
			
		||||
                       CefRefPtr<CefFrame> frame) override {
 | 
			
		||||
    if (!frame->IsMain()) {
 | 
			
		||||
      auto status = current_frame_map_->GetFrameStatus(frame);
 | 
			
		||||
      auto map = GetFrameMap(frame);
 | 
			
		||||
      auto status = map->GetFrameStatus(frame);
 | 
			
		||||
      status->OnFrameAttached(browser, frame);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
@@ -1000,14 +1096,13 @@ class OrderSubTestHandler : public NavigateOrderMainTestHandler {
 | 
			
		||||
    if (!frame->IsMain()) {
 | 
			
		||||
      // Potentially the last notification for an old sub-frame after
 | 
			
		||||
      // navigation.
 | 
			
		||||
      MaybeSwapFrameMap();
 | 
			
		||||
 | 
			
		||||
      auto status = previous_frame_map_->GetFrameStatus(frame);
 | 
			
		||||
      auto map = GetFrameMap(frame);
 | 
			
		||||
      auto status = map->GetFrameStatus(frame);
 | 
			
		||||
      status->OnFrameDetached(browser, frame);
 | 
			
		||||
 | 
			
		||||
      if (AllFramesDetached()) {
 | 
			
		||||
      if (map->AllFramesDetached()) {
 | 
			
		||||
        // Verify results from the previous navigation.
 | 
			
		||||
        VerifyAndClearSubFrameTestResults();
 | 
			
		||||
        VerifyAndClearSubFrameTestResults(map);
 | 
			
		||||
      }
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
@@ -1019,7 +1114,8 @@ class OrderSubTestHandler : public NavigateOrderMainTestHandler {
 | 
			
		||||
                   CefRefPtr<CefFrame> frame,
 | 
			
		||||
                   TransitionType transition_type) override {
 | 
			
		||||
    if (!frame->IsMain()) {
 | 
			
		||||
      auto status = current_frame_map_->GetFrameStatus(frame);
 | 
			
		||||
      auto map = GetFrameMap(frame);
 | 
			
		||||
      auto status = map->GetFrameStatus(frame);
 | 
			
		||||
      status->OnLoadStart(browser, frame);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
@@ -1031,7 +1127,8 @@ class OrderSubTestHandler : public NavigateOrderMainTestHandler {
 | 
			
		||||
                 CefRefPtr<CefFrame> frame,
 | 
			
		||||
                 int httpStatusCode) override {
 | 
			
		||||
    if (!frame->IsMain()) {
 | 
			
		||||
      auto status = current_frame_map_->GetFrameStatus(frame);
 | 
			
		||||
      auto map = GetFrameMap(frame);
 | 
			
		||||
      auto status = map->GetFrameStatus(frame);
 | 
			
		||||
      status->OnLoadEnd(browser, frame);
 | 
			
		||||
      return;
 | 
			
		||||
    }
 | 
			
		||||
@@ -1072,59 +1169,105 @@ class OrderSubTestHandler : public NavigateOrderMainTestHandler {
 | 
			
		||||
    return std::string();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool AllQueriesDelivered() const override {
 | 
			
		||||
    if (!NavigateOrderMainTestHandler::AllQueriesDelivered())
 | 
			
		||||
  bool AllQueriesDelivered(std::string* msg = nullptr) const override {
 | 
			
		||||
    if (!NavigateOrderMainTestHandler::AllQueriesDelivered(msg)) {
 | 
			
		||||
#if VERBOSE_DEBUGGING
 | 
			
		||||
      if (msg)
 | 
			
		||||
        *msg += " MAIN PENDING";
 | 
			
		||||
#endif
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!current_frame_map_)
 | 
			
		||||
    if (frame_maps_.empty()) {
 | 
			
		||||
#if VERBOSE_DEBUGGING
 | 
			
		||||
      if (msg)
 | 
			
		||||
        *msg += " NO SUBS";
 | 
			
		||||
#endif
 | 
			
		||||
      return false;
 | 
			
		||||
    return current_frame_map_->AllQueriesDelivered();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!frame_maps_.back()->AllQueriesDelivered(msg)) {
 | 
			
		||||
#if VERBOSE_DEBUGGING
 | 
			
		||||
      if (msg)
 | 
			
		||||
        *msg += " SUBS PENDING";
 | 
			
		||||
#endif
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool AllFramesLoaded() const override {
 | 
			
		||||
    if (!NavigateOrderMainTestHandler::AllFramesLoaded())
 | 
			
		||||
  bool AllFramesLoaded(std::string* msg = nullptr) const override {
 | 
			
		||||
    if (!NavigateOrderMainTestHandler::AllFramesLoaded(msg)) {
 | 
			
		||||
#if VERBOSE_DEBUGGING
 | 
			
		||||
      if (msg)
 | 
			
		||||
        *msg += " MAIN PENDING";
 | 
			
		||||
#endif
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!current_frame_map_)
 | 
			
		||||
    if (frame_maps_.empty()) {
 | 
			
		||||
#if VERBOSE_DEBUGGING
 | 
			
		||||
      if (msg)
 | 
			
		||||
        *msg += " NO SUBS";
 | 
			
		||||
#endif
 | 
			
		||||
      return false;
 | 
			
		||||
    return current_frame_map_->AllFramesLoaded();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!frame_maps_.back()->AllFramesLoaded(msg)) {
 | 
			
		||||
#if VERBOSE_DEBUGGING
 | 
			
		||||
      if (msg)
 | 
			
		||||
        *msg += " SUBS PENDING";
 | 
			
		||||
#endif
 | 
			
		||||
      return false;
 | 
			
		||||
    }
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void VerifyTestResults() override {
 | 
			
		||||
    NavigateOrderMainTestHandler::VerifyTestResults();
 | 
			
		||||
    EXPECT_FALSE(current_frame_map_);
 | 
			
		||||
    EXPECT_FALSE(previous_frame_map_);
 | 
			
		||||
    EXPECT_TRUE(frame_maps_.empty());
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // All sub-frame objects should already have received all callbacks.
 | 
			
		||||
  void VerifyAndClearSubFrameTestResults() {
 | 
			
		||||
    EXPECT_TRUE(previous_frame_map_);
 | 
			
		||||
    previous_frame_map_->VerifyAndClearTestResults();
 | 
			
		||||
    delete previous_frame_map_;
 | 
			
		||||
    previous_frame_map_ = nullptr;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  FrameStatusMap* current_frame_map() const { return current_frame_map_; }
 | 
			
		||||
  FrameStatusMap* previous_frame_map() const { return previous_frame_map_; }
 | 
			
		||||
  size_t expected_frame_ct() const { return expected_frame_ct_; }
 | 
			
		||||
 | 
			
		||||
 private:
 | 
			
		||||
  void MaybeCreateFrameMap() {
 | 
			
		||||
    if (!current_frame_map_)
 | 
			
		||||
      current_frame_map_ = new FrameStatusMap(expected_frame_ct_);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  void MaybeSwapFrameMap() {
 | 
			
		||||
    if (!previous_frame_map_) {
 | 
			
		||||
      EXPECT_TRUE(current_frame_map_);
 | 
			
		||||
      previous_frame_map_ = current_frame_map_;
 | 
			
		||||
      current_frame_map_ = nullptr;
 | 
			
		||||
  FrameStatusMap* GetFrameMap(CefRefPtr<CefFrame> frame) const {
 | 
			
		||||
    for (auto& map : frame_maps_) {
 | 
			
		||||
      if (map->Contains(frame))
 | 
			
		||||
        return map.get();
 | 
			
		||||
    }
 | 
			
		||||
    return nullptr;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool AllFramesDetached() const {
 | 
			
		||||
    EXPECT_TRUE(previous_frame_map_);
 | 
			
		||||
    return previous_frame_map_->AllFramesDetached();
 | 
			
		||||
 private:
 | 
			
		||||
  // All sub-frame objects should already have received all callbacks.
 | 
			
		||||
  void VerifyAndClearSubFrameTestResults(FrameStatusMap* map) {
 | 
			
		||||
    map->VerifyAndClearTestResults();
 | 
			
		||||
 | 
			
		||||
    bool found = false;
 | 
			
		||||
    FrameStatusMapVector::iterator it = frame_maps_.begin();
 | 
			
		||||
    for (; it != frame_maps_.end(); ++it) {
 | 
			
		||||
      if ((*it).get() == map) {
 | 
			
		||||
        frame_maps_.erase(it);
 | 
			
		||||
        found = true;
 | 
			
		||||
        break;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    EXPECT_TRUE(found);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  FrameStatusMap* GetOrCreateFrameMap(CefRefPtr<CefFrame> frame) {
 | 
			
		||||
    if (auto map = GetFrameMap(frame))
 | 
			
		||||
      return map;
 | 
			
		||||
 | 
			
		||||
    if (frame_maps_.empty() ||
 | 
			
		||||
        frame_maps_.back()->size() >= expected_frame_ct_) {
 | 
			
		||||
      // Start a new frame map.
 | 
			
		||||
      auto map = std::make_unique<FrameStatusMap>(expected_frame_ct_);
 | 
			
		||||
      frame_maps_.push_back(std::move(map));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return frame_maps_.back().get();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const TestMode test_mode_;
 | 
			
		||||
@@ -1132,8 +1275,8 @@ class OrderSubTestHandler : public NavigateOrderMainTestHandler {
 | 
			
		||||
  // The expected number of sub-frames.
 | 
			
		||||
  const size_t expected_frame_ct_;
 | 
			
		||||
 | 
			
		||||
  FrameStatusMap* current_frame_map_ = nullptr;
 | 
			
		||||
  FrameStatusMap* previous_frame_map_ = nullptr;
 | 
			
		||||
  using FrameStatusMapVector = std::vector<std::unique_ptr<FrameStatusMap>>;
 | 
			
		||||
  FrameStatusMapVector frame_maps_;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}  // namespace
 | 
			
		||||
@@ -1202,7 +1345,10 @@ namespace {
 | 
			
		||||
class CrossOriginOrderSubTestHandler : public OrderSubTestHandler {
 | 
			
		||||
 public:
 | 
			
		||||
  CrossOriginOrderSubTestHandler(int additional_nav_ct, TestMode mode)
 | 
			
		||||
      : OrderSubTestHandler(/*cross_origin=*/true, additional_nav_ct, mode) {}
 | 
			
		||||
      : OrderSubTestHandler(/*cross_origin=*/true,
 | 
			
		||||
                            additional_nav_ct,
 | 
			
		||||
                            mode,
 | 
			
		||||
                            /*expected_frame_ct=*/4U) {}
 | 
			
		||||
 | 
			
		||||
  void OnFrameDetached(CefRefPtr<CefBrowser> browser,
 | 
			
		||||
                       CefRefPtr<CefFrame> frame) override {
 | 
			
		||||
@@ -1212,26 +1358,11 @@ class CrossOriginOrderSubTestHandler : public OrderSubTestHandler {
 | 
			
		||||
    // get OnFrameCreated/OnFrameAttached immediately followed by
 | 
			
		||||
    // OnFrameDetached.
 | 
			
		||||
    if (!frame->IsMain()) {
 | 
			
		||||
      FrameStatus* status = nullptr;
 | 
			
		||||
      if (current_frame_map())
 | 
			
		||||
        status = current_frame_map()->GetFrameStatus(frame);
 | 
			
		||||
      if (!status && previous_frame_map())
 | 
			
		||||
        status = previous_frame_map()->GetFrameStatus(frame);
 | 
			
		||||
      auto map = GetFrameMap(frame);
 | 
			
		||||
      auto status = map->GetFrameStatus(frame);
 | 
			
		||||
      if (status && !status->DidGetCallback(FrameStatus::LOAD_START)) {
 | 
			
		||||
        status->SetIsTemporary(true);
 | 
			
		||||
        temp_frame_detached_ct_++;
 | 
			
		||||
 | 
			
		||||
#if VERBOSE_DEBUGGING
 | 
			
		||||
        LOG(INFO) << status->GetDebugString()
 | 
			
		||||
                  << " callback OnFrameDetached(discarded)";
 | 
			
		||||
#endif
 | 
			
		||||
        EXPECT_TRUE(status->DidGetCallback(FrameStatus::FRAME_CREATED));
 | 
			
		||||
        EXPECT_TRUE(status->DidGetCallback(FrameStatus::FRAME_ATTACHED));
 | 
			
		||||
 | 
			
		||||
        // Should not receive any queries.
 | 
			
		||||
        EXPECT_EQ(status->QueriesDeliveredCount(), 0);
 | 
			
		||||
 | 
			
		||||
        current_frame_map()->RemoveFrameStatus(frame);
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1255,7 +1386,7 @@ class CrossOriginOrderSubTestHandler : public OrderSubTestHandler {
 | 
			
		||||
    OrderSubTestHandler::VerifyTestResults();
 | 
			
		||||
 | 
			
		||||
    const size_t expected_temp_ct =
 | 
			
		||||
        expected_frame_ct() * (1U + additional_nav_ct());
 | 
			
		||||
        (expected_frame_ct() / 2U) * (1U + additional_nav_ct());
 | 
			
		||||
    EXPECT_EQ(expected_temp_ct, temp_frame_detached_ct_);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user