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); |   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( | void CefBrowserContentsDelegate::RenderFrameDeleted( | ||||||
|     content::RenderFrameHost* render_frame_host) { |     content::RenderFrameHost* render_frame_host) { | ||||||
|   const auto frame_id = CefFrameHostImpl::MakeFrameId(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 RenderFrameCreated(content::RenderFrameHost* render_frame_host) override; | ||||||
|   void RenderFrameHostChanged(content::RenderFrameHost* old_host, |   void RenderFrameHostChanged(content::RenderFrameHost* old_host, | ||||||
|                               content::RenderFrameHost* new_host) override; |                               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 RenderFrameDeleted(content::RenderFrameHost* render_frame_host) override; | ||||||
|   void RenderViewReady() override; |   void RenderViewReady() override; | ||||||
|   void RenderProcessGone(base::TerminationStatus status) 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)); |   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) { | void CefBrowserInfo::RemoveFrame(content::RenderFrameHost* host) { | ||||||
|   CEF_REQUIRE_UIT(); |   CEF_REQUIRE_UIT(); | ||||||
|  |  | ||||||
| @@ -190,7 +216,7 @@ void CefBrowserInfo::RemoveFrame(content::RenderFrameHost* host) { | |||||||
|     // Explicitly Detach everything but the current main frame. |     // Explicitly Detach everything but the current main frame. | ||||||
|     const auto& frame_info = *it2; |     const auto& frame_info = *it2; | ||||||
|     if (frame_info->frame_ && !frame_info->IsCurrentMainFrame()) { |     if (frame_info->frame_ && !frame_info->IsCurrentMainFrame()) { | ||||||
|       frame_info->frame_->Detach(); |       if (frame_info->frame_->Detach()) | ||||||
|         MaybeNotifyFrameDetached(browser_, frame_info->frame_); |         MaybeNotifyFrameDetached(browser_, frame_info->frame_); | ||||||
|     } |     } | ||||||
|  |  | ||||||
| @@ -314,9 +340,10 @@ CefBrowserInfo::FrameHostList CefBrowserInfo::GetAllFrames() const { | |||||||
|   base::AutoLock lock_scope(lock_); |   base::AutoLock lock_scope(lock_); | ||||||
|   FrameHostList frames; |   FrameHostList frames; | ||||||
|   for (const auto& info : frame_info_set_) { |   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_); |       frames.insert(info->frame_); | ||||||
|     } |     } | ||||||
|  |   } | ||||||
|   return frames; |   return frames; | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -438,7 +465,7 @@ void CefBrowserInfo::SetMainFrame(CefRefPtr<CefBrowserHostBase> browser, | |||||||
|   CefRefPtr<CefFrameHostImpl> old_frame; |   CefRefPtr<CefFrameHostImpl> old_frame; | ||||||
|   if (main_frame_) { |   if (main_frame_) { | ||||||
|     old_frame = main_frame_; |     old_frame = main_frame_; | ||||||
|     old_frame->Detach(); |     if (old_frame->Detach()) | ||||||
|       MaybeNotifyFrameDetached(browser, old_frame); |       MaybeNotifyFrameDetached(browser, old_frame); | ||||||
|   } |   } | ||||||
|  |  | ||||||
| @@ -520,7 +547,7 @@ void CefBrowserInfo::RemoveAllFrames( | |||||||
|   // Explicitly Detach everything but the current main frame. |   // Explicitly Detach everything but the current main frame. | ||||||
|   for (auto& info : frame_info_set_) { |   for (auto& info : frame_info_set_) { | ||||||
|     if (info->frame_ && !info->IsCurrentMainFrame()) { |     if (info->frame_ && !info->IsCurrentMainFrame()) { | ||||||
|       info->frame_->Detach(); |       if (info->frame_->Detach()) | ||||||
|         MaybeNotifyFrameDetached(old_browser, info->frame_); |         MaybeNotifyFrameDetached(old_browser, info->frame_); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
|   | |||||||
| @@ -19,10 +19,7 @@ | |||||||
| #include "base/memory/weak_ptr.h" | #include "base/memory/weak_ptr.h" | ||||||
| #include "base/synchronization/lock.h" | #include "base/synchronization/lock.h" | ||||||
| #include "base/values.h" | #include "base/values.h" | ||||||
|  | #include "content/public/browser/render_frame_host.h" | ||||||
| namespace content { |  | ||||||
| class RenderFrameHost; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| class CefBrowserHostBase; | class CefBrowserHostBase; | ||||||
| class CefFrameHandler; | class CefFrameHandler; | ||||||
| @@ -62,6 +59,13 @@ class CefBrowserInfo : public base::RefCountedThreadSafe<CefBrowserInfo> { | |||||||
|   // true). |   // true). | ||||||
|   void MaybeCreateFrame(content::RenderFrameHost* host, bool is_guest_view); |   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 |   // 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 |   // RenderView is destroyed, or for a sub-frame when the associated RenderFrame | ||||||
|   // is destroyed in the renderer process. |   // is destroyed in the renderer process. | ||||||
| @@ -170,7 +174,7 @@ class CefBrowserInfo : public base::RefCountedThreadSafe<CefBrowserInfo> { | |||||||
|     ~FrameInfo(); |     ~FrameInfo(); | ||||||
|  |  | ||||||
|     inline bool IsCurrentMainFrame() const { |     inline bool IsCurrentMainFrame() const { | ||||||
|       return frame_ && is_main_frame_ && !is_speculative_; |       return frame_ && is_main_frame_ && !is_speculative_ && !is_in_bfcache_; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     content::RenderFrameHost* host_; |     content::RenderFrameHost* host_; | ||||||
| @@ -179,6 +183,7 @@ class CefBrowserInfo : public base::RefCountedThreadSafe<CefBrowserInfo> { | |||||||
|     bool is_guest_view_; |     bool is_guest_view_; | ||||||
|     bool is_main_frame_; |     bool is_main_frame_; | ||||||
|     bool is_speculative_; |     bool is_speculative_; | ||||||
|  |     bool is_in_bfcache_ = false; | ||||||
|     CefRefPtr<CefFrameHostImpl> frame_; |     CefRefPtr<CefFrameHostImpl> frame_; | ||||||
|   }; |   }; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -427,19 +427,23 @@ content::RenderFrameHost* CefFrameHostImpl::GetRenderFrameHost() const { | |||||||
|   return render_frame_host_; |   return render_frame_host_; | ||||||
| } | } | ||||||
|  |  | ||||||
| void CefFrameHostImpl::Detach() { | bool CefFrameHostImpl::Detach() { | ||||||
|   CEF_REQUIRE_UIT(); |   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. |   // Should not be called for temporary frames. | ||||||
|   DCHECK(!is_temporary()); |   DCHECK(!is_temporary()); | ||||||
|  |  | ||||||
|   { |   { | ||||||
|     base::AutoLock lock_scope(state_lock_); |     base::AutoLock lock_scope(state_lock_); | ||||||
|  |     if (browser_info_) { | ||||||
|     // Should be called only once. |       first_detach = true; | ||||||
|     DCHECK(browser_info_); |  | ||||||
|       browser_info_ = nullptr; |       browser_info_ = nullptr; | ||||||
|     } |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|   // In case we never attached, clean up. |   // In case we never attached, clean up. | ||||||
|   while (!queued_actions_.empty()) { |   while (!queued_actions_.empty()) { | ||||||
| @@ -449,6 +453,8 @@ void CefFrameHostImpl::Detach() { | |||||||
|   render_frame_.reset(); |   render_frame_.reset(); | ||||||
|   render_frame_host_ = nullptr; |   render_frame_host_ = nullptr; | ||||||
|   is_attached_ = false; |   is_attached_ = false; | ||||||
|  |  | ||||||
|  |   return first_detach; | ||||||
| } | } | ||||||
|  |  | ||||||
| // static | // static | ||||||
|   | |||||||
| @@ -115,8 +115,9 @@ class CefFrameHostImpl : public CefFrame, public cef::mojom::BrowserFrame { | |||||||
|  |  | ||||||
|   // Owned frame objects will be detached explicitly when the associated |   // Owned frame objects will be detached explicitly when the associated | ||||||
|   // RenderFrame is deleted. Temporary frame objects will be detached |   // RenderFrame is deleted. Temporary frame objects will be detached | ||||||
|   // implicitly via CefBrowserInfo::browser() returning nullptr. |   // implicitly via CefBrowserInfo::browser() returning nullptr. Returns true | ||||||
|   void Detach(); |   // if this was the first call to Detach() for the frame. | ||||||
|  |   bool Detach(); | ||||||
|  |  | ||||||
|   // cef::mojom::BrowserFrame methods forwarded from CefBrowserFrame. |   // cef::mojom::BrowserFrame methods forwarded from CefBrowserFrame. | ||||||
|   void SendMessage(const std::string& name, base::Value arguments) override; |   void SendMessage(const std::string& name, base::Value arguments) override; | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ | |||||||
|  |  | ||||||
| #include <limits> | #include <limits> | ||||||
| #include <map> | #include <map> | ||||||
|  | #include <memory> | ||||||
| #include <queue> | #include <queue> | ||||||
| #include <sstream> | #include <sstream> | ||||||
| #include <string> | #include <string> | ||||||
| @@ -101,9 +102,18 @@ struct FrameStatus { | |||||||
|   int64 frame_id() const { return frame_id_; } |   int64 frame_id() const { return frame_id_; } | ||||||
|   bool is_main() const { return is_main_; } |   bool is_main() const { return is_main_; } | ||||||
|  |  | ||||||
|   bool AllQueriesDelivered() const { |   bool AllQueriesDelivered(std::string* msg = nullptr) const { | ||||||
|     EXPECT_UI_THREAD(); |     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 { |   int QueriesDeliveredCount() const { | ||||||
|     EXPECT_UI_THREAD(); |     EXPECT_UI_THREAD(); | ||||||
| @@ -114,7 +124,22 @@ struct FrameStatus { | |||||||
|     return frame->GetIdentifier() == frame_id(); |     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]; } |   bool IsDetached() const { return got_callback_[FRAME_DETACHED]; } | ||||||
|  |  | ||||||
|   void SetIsFirstMain(bool val) { |   void SetIsFirstMain(bool val) { | ||||||
| @@ -130,6 +155,12 @@ struct FrameStatus { | |||||||
|     is_last_main_ = val; |     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) { |   void SetAdditionalDebugInfo(const std::string& debug_info) { | ||||||
|     debug_info_ = debug_info; |     debug_info_ = debug_info; | ||||||
|   } |   } | ||||||
| @@ -297,6 +328,11 @@ struct FrameStatus { | |||||||
|     // Verify that all expected callbacks have executed. |     // Verify that all expected callbacks have executed. | ||||||
|     VerifyCallbackStatus(__FUNCTION__, CALLBACK_LAST + 1); |     VerifyCallbackStatus(__FUNCTION__, CALLBACK_LAST + 1); | ||||||
|  |  | ||||||
|  |     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. |       // Verify that all expected messages have been sent and received. | ||||||
|       EXPECT_EQ(expected_query_ct_, delivered_query_ct_); |       EXPECT_EQ(expected_query_ct_, delivered_query_ct_); | ||||||
|       EXPECT_EQ(0U, pending_queries_.size()); |       EXPECT_EQ(0U, pending_queries_.size()); | ||||||
| @@ -306,6 +342,7 @@ struct FrameStatus { | |||||||
|         pending_queries_.pop(); |         pending_queries_.pop(); | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|   bool DidGetCallback(int callback) const { return got_callback_[callback]; } |   bool DidGetCallback(int callback) const { return got_callback_[callback]; } | ||||||
|  |  | ||||||
| @@ -340,6 +377,14 @@ struct FrameStatus { | |||||||
|       if (callback == MAIN_FRAME_CHANGED_REMOVED && is_last_main_) { |       if (callback == MAIN_FRAME_CHANGED_REMOVED && is_last_main_) { | ||||||
|         return false; |         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; |     return true; | ||||||
| @@ -442,6 +487,7 @@ struct FrameStatus { | |||||||
|  |  | ||||||
|   bool is_first_main_ = false; |   bool is_first_main_ = false; | ||||||
|   bool is_last_main_ = false; |   bool is_last_main_ = false; | ||||||
|  |   bool is_temporary_ = false; | ||||||
|   std::string debug_info_; |   std::string debug_info_; | ||||||
|  |  | ||||||
|   bool got_before_close_ = false; |   bool got_before_close_ = false; | ||||||
| @@ -642,22 +688,35 @@ class OrderMainTestHandler : public RoutingTestHandler, public CefFrameHandler { | |||||||
|  |  | ||||||
|   virtual std::string GetAdditionalDebugInfo() const { return std::string(); } |   virtual std::string GetAdditionalDebugInfo() const { return std::string(); } | ||||||
|  |  | ||||||
|   virtual bool AllQueriesDelivered() const { |   virtual bool AllQueriesDelivered(std::string* msg = nullptr) const { | ||||||
|     EXPECT_UI_THREAD(); |     EXPECT_UI_THREAD(); | ||||||
|     if (pending_main_frame_) |     if (pending_main_frame_) | ||||||
|       return false; |       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(); |     EXPECT_UI_THREAD(); | ||||||
|     if (pending_main_frame_) |     if (pending_main_frame_) | ||||||
|       return false; |       return false; | ||||||
|     return current_main_frame_->IsLoaded(); |     return current_main_frame_->IsLoaded(msg); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   void MaybeDestroyTest() { |   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()) { |     if (AllQueriesDelivered() && AllFramesLoaded()) { | ||||||
|  | #endif | ||||||
|       const std::string& next_url = GetNextMainURL(); |       const std::string& next_url = GetNextMainURL(); | ||||||
|       if (!next_url.empty()) { |       if (!next_url.empty()) { | ||||||
|         if (!IsCrossOrigin()) { |         if (!IsCrossOrigin()) { | ||||||
| @@ -818,9 +877,15 @@ class FrameStatusMap { | |||||||
|       : expected_frame_ct_(expected_frame_ct) {} |       : expected_frame_ct_(expected_frame_ct) {} | ||||||
|   ~FrameStatusMap() { EXPECT_TRUE(frame_map_.empty()); } |   ~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) { |   FrameStatus* CreateFrameStatus(CefRefPtr<CefFrame> frame) { | ||||||
|     EXPECT_UI_THREAD(); |     EXPECT_UI_THREAD(); | ||||||
|  |  | ||||||
|  |     EXPECT_LT(size(), expected_frame_ct_); | ||||||
|  |  | ||||||
|     const int64 id = frame->GetIdentifier(); |     const int64 id = frame->GetIdentifier(); | ||||||
|     EXPECT_NE(kInvalidFrameId, id); |     EXPECT_NE(kInvalidFrameId, id); | ||||||
|     EXPECT_EQ(frame_map_.find(id), frame_map_.end()); |     EXPECT_EQ(frame_map_.find(id), frame_map_.end()); | ||||||
| @@ -854,28 +919,58 @@ class FrameStatusMap { | |||||||
|     } |     } | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool AllQueriesDelivered() const { |   bool AllQueriesDelivered(std::string* msg = nullptr) const { | ||||||
|     if (size() != expected_frame_ct_) |     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; |       return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     Map::const_iterator it = frame_map_.begin(); |     Map::const_iterator it = frame_map_.begin(); | ||||||
|     for (; it != frame_map_.end(); ++it) { |     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 false; | ||||||
|       } |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool AllFramesLoaded() const { |   bool AllFramesLoaded(std::string* msg = nullptr) const { | ||||||
|     if (size() != expected_frame_ct_) |     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; |       return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     Map::const_iterator it = frame_map_.begin(); |     Map::const_iterator it = frame_map_.begin(); | ||||||
|     for (; it != frame_map_.end(); ++it) { |     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 false; | ||||||
|       } |       } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     return true; |     return true; | ||||||
|   } |   } | ||||||
| @@ -923,14 +1018,14 @@ class OrderSubTestHandler : public NavigateOrderMainTestHandler { | |||||||
|     SUBFRAME_CHILDREN, |     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), |       : NavigateOrderMainTestHandler(cross_origin, additional_nav_ct), | ||||||
|         test_mode_(mode), |         test_mode_(mode), | ||||||
|         expected_frame_ct_(2U) {} |         expected_frame_ct_(expected_frame_ct) {} | ||||||
|   ~OrderSubTestHandler() override { |   ~OrderSubTestHandler() override { EXPECT_TRUE(frame_maps_.empty()); } | ||||||
|     EXPECT_FALSE(current_frame_map_); |  | ||||||
|     EXPECT_FALSE(previous_frame_map_); |  | ||||||
|   } |  | ||||||
|  |  | ||||||
|   void RunTest() override { |   void RunTest() override { | ||||||
|     for (int i = 0; i <= additional_nav_ct(); i++) { |     for (int i = 0; i <= additional_nav_ct(); i++) { | ||||||
| @@ -944,9 +1039,9 @@ class OrderSubTestHandler : public NavigateOrderMainTestHandler { | |||||||
|   void OnBeforeClose(CefRefPtr<CefBrowser> browser) override { |   void OnBeforeClose(CefRefPtr<CefBrowser> browser) override { | ||||||
|     NavigateOrderMainTestHandler::OnBeforeClose(browser); |     NavigateOrderMainTestHandler::OnBeforeClose(browser); | ||||||
|  |  | ||||||
|     if (previous_frame_map_) { |     for (auto& map : frame_maps_) { | ||||||
|       // Also need to notify any sub-frames. |       // 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, |                bool persistent, | ||||||
|                CefRefPtr<Callback> callback) override { |                CefRefPtr<Callback> callback) override { | ||||||
|     if (!frame->IsMain()) { |     if (!frame->IsMain()) { | ||||||
|       auto status = current_frame_map_->GetFrameStatus(frame); |       auto map = GetFrameMap(frame); | ||||||
|  |       auto status = map->GetFrameStatus(frame); | ||||||
|       status->OnQuery(browser, frame, request); |       status->OnQuery(browser, frame, request); | ||||||
|       if (status->AllQueriesDelivered()) { |       if (status->AllQueriesDelivered()) { | ||||||
|         MaybeDestroyTest(); |         MaybeDestroyTest(); | ||||||
| @@ -973,9 +1069,8 @@ class OrderSubTestHandler : public NavigateOrderMainTestHandler { | |||||||
|                       CefRefPtr<CefFrame> frame) override { |                       CefRefPtr<CefFrame> frame) override { | ||||||
|     if (!frame->IsMain()) { |     if (!frame->IsMain()) { | ||||||
|       // Potentially the first notification of a new sub-frame after navigation. |       // Potentially the first notification of a new sub-frame after navigation. | ||||||
|       MaybeCreateFrameMap(); |       auto map = GetOrCreateFrameMap(frame); | ||||||
|  |       auto status = map->CreateFrameStatus(frame); | ||||||
|       auto status = current_frame_map_->CreateFrameStatus(frame); |  | ||||||
|       status->SetAdditionalDebugInfo(GetAdditionalDebugInfo()); |       status->SetAdditionalDebugInfo(GetAdditionalDebugInfo()); | ||||||
|       status->OnFrameCreated(browser, frame); |       status->OnFrameCreated(browser, frame); | ||||||
|       return; |       return; | ||||||
| @@ -987,7 +1082,8 @@ class OrderSubTestHandler : public NavigateOrderMainTestHandler { | |||||||
|   void OnFrameAttached(CefRefPtr<CefBrowser> browser, |   void OnFrameAttached(CefRefPtr<CefBrowser> browser, | ||||||
|                        CefRefPtr<CefFrame> frame) override { |                        CefRefPtr<CefFrame> frame) override { | ||||||
|     if (!frame->IsMain()) { |     if (!frame->IsMain()) { | ||||||
|       auto status = current_frame_map_->GetFrameStatus(frame); |       auto map = GetFrameMap(frame); | ||||||
|  |       auto status = map->GetFrameStatus(frame); | ||||||
|       status->OnFrameAttached(browser, frame); |       status->OnFrameAttached(browser, frame); | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
| @@ -1000,14 +1096,13 @@ class OrderSubTestHandler : public NavigateOrderMainTestHandler { | |||||||
|     if (!frame->IsMain()) { |     if (!frame->IsMain()) { | ||||||
|       // Potentially the last notification for an old sub-frame after |       // Potentially the last notification for an old sub-frame after | ||||||
|       // navigation. |       // navigation. | ||||||
|       MaybeSwapFrameMap(); |       auto map = GetFrameMap(frame); | ||||||
|  |       auto status = map->GetFrameStatus(frame); | ||||||
|       auto status = previous_frame_map_->GetFrameStatus(frame); |  | ||||||
|       status->OnFrameDetached(browser, frame); |       status->OnFrameDetached(browser, frame); | ||||||
|  |  | ||||||
|       if (AllFramesDetached()) { |       if (map->AllFramesDetached()) { | ||||||
|         // Verify results from the previous navigation. |         // Verify results from the previous navigation. | ||||||
|         VerifyAndClearSubFrameTestResults(); |         VerifyAndClearSubFrameTestResults(map); | ||||||
|       } |       } | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
| @@ -1019,7 +1114,8 @@ class OrderSubTestHandler : public NavigateOrderMainTestHandler { | |||||||
|                    CefRefPtr<CefFrame> frame, |                    CefRefPtr<CefFrame> frame, | ||||||
|                    TransitionType transition_type) override { |                    TransitionType transition_type) override { | ||||||
|     if (!frame->IsMain()) { |     if (!frame->IsMain()) { | ||||||
|       auto status = current_frame_map_->GetFrameStatus(frame); |       auto map = GetFrameMap(frame); | ||||||
|  |       auto status = map->GetFrameStatus(frame); | ||||||
|       status->OnLoadStart(browser, frame); |       status->OnLoadStart(browser, frame); | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
| @@ -1031,7 +1127,8 @@ class OrderSubTestHandler : public NavigateOrderMainTestHandler { | |||||||
|                  CefRefPtr<CefFrame> frame, |                  CefRefPtr<CefFrame> frame, | ||||||
|                  int httpStatusCode) override { |                  int httpStatusCode) override { | ||||||
|     if (!frame->IsMain()) { |     if (!frame->IsMain()) { | ||||||
|       auto status = current_frame_map_->GetFrameStatus(frame); |       auto map = GetFrameMap(frame); | ||||||
|  |       auto status = map->GetFrameStatus(frame); | ||||||
|       status->OnLoadEnd(browser, frame); |       status->OnLoadEnd(browser, frame); | ||||||
|       return; |       return; | ||||||
|     } |     } | ||||||
| @@ -1072,59 +1169,105 @@ class OrderSubTestHandler : public NavigateOrderMainTestHandler { | |||||||
|     return std::string(); |     return std::string(); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   bool AllQueriesDelivered() const override { |   bool AllQueriesDelivered(std::string* msg = nullptr) const override { | ||||||
|     if (!NavigateOrderMainTestHandler::AllQueriesDelivered()) |     if (!NavigateOrderMainTestHandler::AllQueriesDelivered(msg)) { | ||||||
|  | #if VERBOSE_DEBUGGING | ||||||
|  |       if (msg) | ||||||
|  |         *msg += " MAIN PENDING"; | ||||||
|  | #endif | ||||||
|       return false; |       return false; | ||||||
|  |  | ||||||
|     if (!current_frame_map_) |  | ||||||
|       return false; |  | ||||||
|     return current_frame_map_->AllQueriesDelivered(); |  | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   bool AllFramesLoaded() const override { |     if (frame_maps_.empty()) { | ||||||
|     if (!NavigateOrderMainTestHandler::AllFramesLoaded()) | #if VERBOSE_DEBUGGING | ||||||
|  |       if (msg) | ||||||
|  |         *msg += " NO SUBS"; | ||||||
|  | #endif | ||||||
|       return false; |       return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|     if (!current_frame_map_) |     if (!frame_maps_.back()->AllQueriesDelivered(msg)) { | ||||||
|  | #if VERBOSE_DEBUGGING | ||||||
|  |       if (msg) | ||||||
|  |         *msg += " SUBS PENDING"; | ||||||
|  | #endif | ||||||
|       return false; |       return false; | ||||||
|     return current_frame_map_->AllFramesLoaded(); |     } | ||||||
|  |     return true; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   bool AllFramesLoaded(std::string* msg = nullptr) const override { | ||||||
|  |     if (!NavigateOrderMainTestHandler::AllFramesLoaded(msg)) { | ||||||
|  | #if VERBOSE_DEBUGGING | ||||||
|  |       if (msg) | ||||||
|  |         *msg += " MAIN PENDING"; | ||||||
|  | #endif | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (frame_maps_.empty()) { | ||||||
|  | #if VERBOSE_DEBUGGING | ||||||
|  |       if (msg) | ||||||
|  |         *msg += " NO SUBS"; | ||||||
|  | #endif | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     if (!frame_maps_.back()->AllFramesLoaded(msg)) { | ||||||
|  | #if VERBOSE_DEBUGGING | ||||||
|  |       if (msg) | ||||||
|  |         *msg += " SUBS PENDING"; | ||||||
|  | #endif | ||||||
|  |       return false; | ||||||
|  |     } | ||||||
|  |     return true; | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   void VerifyTestResults() override { |   void VerifyTestResults() override { | ||||||
|     NavigateOrderMainTestHandler::VerifyTestResults(); |     NavigateOrderMainTestHandler::VerifyTestResults(); | ||||||
|     EXPECT_FALSE(current_frame_map_); |     EXPECT_TRUE(frame_maps_.empty()); | ||||||
|     EXPECT_FALSE(previous_frame_map_); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   // 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_; } |   size_t expected_frame_ct() const { return expected_frame_ct_; } | ||||||
|  |  | ||||||
|  |   FrameStatusMap* GetFrameMap(CefRefPtr<CefFrame> frame) const { | ||||||
|  |     for (auto& map : frame_maps_) { | ||||||
|  |       if (map->Contains(frame)) | ||||||
|  |         return map.get(); | ||||||
|  |     } | ||||||
|  |     return nullptr; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  private: |  private: | ||||||
|   void MaybeCreateFrameMap() { |   // All sub-frame objects should already have received all callbacks. | ||||||
|     if (!current_frame_map_) |   void VerifyAndClearSubFrameTestResults(FrameStatusMap* map) { | ||||||
|       current_frame_map_ = new FrameStatusMap(expected_frame_ct_); |     map->VerifyAndClearTestResults(); | ||||||
|   } |  | ||||||
|  |  | ||||||
|   void MaybeSwapFrameMap() { |     bool found = false; | ||||||
|     if (!previous_frame_map_) { |     FrameStatusMapVector::iterator it = frame_maps_.begin(); | ||||||
|       EXPECT_TRUE(current_frame_map_); |     for (; it != frame_maps_.end(); ++it) { | ||||||
|       previous_frame_map_ = current_frame_map_; |       if ((*it).get() == map) { | ||||||
|       current_frame_map_ = nullptr; |         frame_maps_.erase(it); | ||||||
|  |         found = true; | ||||||
|  |         break; | ||||||
|       } |       } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|   bool AllFramesDetached() const { |     EXPECT_TRUE(found); | ||||||
|     EXPECT_TRUE(previous_frame_map_); |   } | ||||||
|     return previous_frame_map_->AllFramesDetached(); |  | ||||||
|  |   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_; |   const TestMode test_mode_; | ||||||
| @@ -1132,8 +1275,8 @@ class OrderSubTestHandler : public NavigateOrderMainTestHandler { | |||||||
|   // The expected number of sub-frames. |   // The expected number of sub-frames. | ||||||
|   const size_t expected_frame_ct_; |   const size_t expected_frame_ct_; | ||||||
|  |  | ||||||
|   FrameStatusMap* current_frame_map_ = nullptr; |   using FrameStatusMapVector = std::vector<std::unique_ptr<FrameStatusMap>>; | ||||||
|   FrameStatusMap* previous_frame_map_ = nullptr; |   FrameStatusMapVector frame_maps_; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| }  // namespace | }  // namespace | ||||||
| @@ -1202,7 +1345,10 @@ namespace { | |||||||
| class CrossOriginOrderSubTestHandler : public OrderSubTestHandler { | class CrossOriginOrderSubTestHandler : public OrderSubTestHandler { | ||||||
|  public: |  public: | ||||||
|   CrossOriginOrderSubTestHandler(int additional_nav_ct, TestMode mode) |   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, |   void OnFrameDetached(CefRefPtr<CefBrowser> browser, | ||||||
|                        CefRefPtr<CefFrame> frame) override { |                        CefRefPtr<CefFrame> frame) override { | ||||||
| @@ -1212,26 +1358,11 @@ class CrossOriginOrderSubTestHandler : public OrderSubTestHandler { | |||||||
|     // get OnFrameCreated/OnFrameAttached immediately followed by |     // get OnFrameCreated/OnFrameAttached immediately followed by | ||||||
|     // OnFrameDetached. |     // OnFrameDetached. | ||||||
|     if (!frame->IsMain()) { |     if (!frame->IsMain()) { | ||||||
|       FrameStatus* status = nullptr; |       auto map = GetFrameMap(frame); | ||||||
|       if (current_frame_map()) |       auto status = map->GetFrameStatus(frame); | ||||||
|         status = current_frame_map()->GetFrameStatus(frame); |  | ||||||
|       if (!status && previous_frame_map()) |  | ||||||
|         status = previous_frame_map()->GetFrameStatus(frame); |  | ||||||
|       if (status && !status->DidGetCallback(FrameStatus::LOAD_START)) { |       if (status && !status->DidGetCallback(FrameStatus::LOAD_START)) { | ||||||
|  |         status->SetIsTemporary(true); | ||||||
|         temp_frame_detached_ct_++; |         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(); |     OrderSubTestHandler::VerifyTestResults(); | ||||||
|  |  | ||||||
|     const size_t expected_temp_ct = |     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_); |     EXPECT_EQ(expected_temp_ct, temp_frame_detached_ct_); | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user