| // Copyright 2025 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/web_contents_based_canceller.h" |
| |
| #include "base/memory/ptr_util.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/web_contents.h" |
| namespace content { |
| |
| // static |
| std::unique_ptr<WebContentsBasedCanceller> WebContentsBasedCanceller::Create( |
| RenderFrameHost* rfh, |
| CancelCondition condition) { |
| // `make_unique` would force the constructor to be public. |
| auto canceller = |
| base::WrapUnique(new WebContentsBasedCanceller(rfh, condition)); |
| if (canceller->CanShow()) { |
| return canceller; |
| } |
| return nullptr; |
| } |
| |
| WebContentsBasedCanceller::WebContentsBasedCanceller( |
| RenderFrameHost* render_frame_host, |
| CancelCondition condition) |
| : WebContentsObserver(WebContents::FromRenderFrameHost(render_frame_host)), |
| condition_(condition), |
| document_(render_frame_host->GetWeakDocumentPtr()) {} |
| |
| WebContentsBasedCanceller::~WebContentsBasedCanceller() = default; |
| |
| bool WebContentsBasedCanceller::CanShow() { |
| RenderFrameHost* render_frame_host = document_.AsRenderFrameHostIfValid(); |
| if (!render_frame_host) { |
| return false; |
| } |
| return CanShowForVisibility(web_contents()->GetVisibility()) && |
| CanShowForRFHActiveState(); |
| } |
| |
| bool WebContentsBasedCanceller::CanShowForVisibility(Visibility visibility) { |
| return condition_ != CancelCondition::kVisibility || |
| visibility != Visibility::HIDDEN; |
| } |
| |
| bool WebContentsBasedCanceller::CanShowForRFHActiveState() { |
| RenderFrameHost* render_frame_host = document_.AsRenderFrameHostIfValid(); |
| return render_frame_host && render_frame_host->IsActive(); |
| } |
| |
| void WebContentsBasedCanceller::SetCancelCallback( |
| CancelCallback cancel_callback) { |
| CHECK(cancel_callback_.is_null()); |
| // Check all conditions immediately. This ensures that even if |
| // SetCancelCallback is called later (in a different task), we are not leaving |
| // a window for a race. |
| if (!CanShow()) { |
| std::move(cancel_callback).Run(); |
| return; |
| } |
| cancel_callback_ = std::move(cancel_callback); |
| } |
| |
| void WebContentsBasedCanceller::OnVisibilityChanged(Visibility visibility) { |
| // TODO(https://crbug.com/446032849): Remove this. |
| VLOG(1) << "Visibility changed: " << static_cast<int>(visibility); |
| #if BUILDFLAG(IS_ANDROID) |
| // TODO(crbug.com/457495639): We need a different way to detect when a |
| // WebContents is no longer displayed to the user for android since the |
| // intent to select a file always causes a HIDDEN event as the whole app |
| // receives onStop(). |
| return; |
| #else |
| if (cancel_callback_.is_null()) { |
| return; |
| } |
| if (!CanShowForVisibility(visibility)) { |
| // TODO(https://crbug.com/446032849): Remove this. |
| VLOG(1) << "Cancelling"; |
| std::move(cancel_callback_).Run(); |
| } |
| #endif |
| } |
| |
| void WebContentsBasedCanceller::RenderFrameHostStateChanged( |
| RenderFrameHost* changed_render_frame_host, |
| RenderFrameHost::LifecycleState old_state, |
| RenderFrameHost::LifecycleState new_state) { |
| // TODO(https://crbug.com/446032849): Remove this. |
| VLOG(1) << "State changed: " << static_cast<int>(new_state); |
| if (cancel_callback_.is_null()) { |
| return; |
| } |
| |
| if (!CanShowForRFHActiveState()) { |
| VLOG(1) << "Cancelling."; |
| std::move(cancel_callback_).Run(); |
| return; |
| } |
| } |
| |
| void WebContentsBasedCanceller::DidFinishNavigation( |
| NavigationHandle* navigation_handle) { |
| VLOG(1) << "Finished navigation"; |
| if (!document_.AsRenderFrameHostIfValid()) { |
| // TODO(https://crbug.com/446032849): Remove this. |
| VLOG(1) << "Cancelling"; |
| std::move(cancel_callback_).Run(); |
| } |
| } |
| |
| } // namespace content |