AsyncBeforeUnload: Run beforeunload handlers asynchronously if possible [chromium/src : main]

0 views
Skip to first unread message

Minoru Chikamune (Gerrit)

unread,
Jan 23, 2026, 4:55:03 AMJan 23
to Minoru Chikamune, Rakina Zata Amni, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
Attention needed from Alex Moshchuk, Charlie Reis and Rakina Zata Amni

Minoru Chikamune added 1 comment

Patchset-level comments
File-level comment, Patchset 47 (Latest):
Minoru Chikamune . resolved

Hi, I implemented the AsyncBeforeUnload feature. Still I'm adding tests, let me start gathering your opinions.

Open in Gerrit

Related details

Attention is currently required from:
  • Alex Moshchuk
  • Charlie Reis
  • Rakina Zata Amni
Submit Requirements:
  • requirement satisfiedCode-Coverage
  • requirement is not satisfiedCode-Owners
  • requirement is not satisfiedCode-Review
  • requirement is not satisfiedReview-Enforcement
Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
Gerrit-MessageType: comment
Gerrit-Project: chromium/src
Gerrit-Branch: main
Gerrit-Change-Id: I7b19ffb64190f94f5e51aa93225c05a2aba0404a
Gerrit-Change-Number: 7413835
Gerrit-PatchSet: 47
Gerrit-Owner: Minoru Chikamune <chik...@chromium.org>
Gerrit-Reviewer: Alex Moshchuk <ale...@chromium.org>
Gerrit-Reviewer: Charlie Reis <cr...@chromium.org>
Gerrit-Reviewer: Minoru Chikamune <chik...@chromium.org>
Gerrit-Reviewer: Rakina Zata Amni <rak...@chromium.org>
Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
Gerrit-CC: Nate Chapin <jap...@chromium.org>
Gerrit-Attention: Alex Moshchuk <ale...@chromium.org>
Gerrit-Attention: Rakina Zata Amni <rak...@chromium.org>
Gerrit-Attention: Charlie Reis <cr...@chromium.org>
Gerrit-Comment-Date: Fri, 23 Jan 2026 09:54:30 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
satisfied_requirement
unsatisfied_requirement
open
diffy

AI Code Reviewer (Gerrit)

unread,
Jan 23, 2026, 4:56:53 AMJan 23
to Minoru Chikamune, Rakina Zata Amni, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
Attention needed from Alex Moshchuk, Charlie Reis and Rakina Zata Amni

AI Code Reviewer added 2 comments

File third_party/blink/renderer/core/dom/document.h
Line 915, Patchset 47 (Latest): bool force_to_proceed,
AI Code Reviewer . unresolved

Blink Style Guide: Prefer enums or StrongAliases to bare bools for function parameters. Consider using a base::StrongAlias<class ForceToProceedTag, bool> or an enum class for the 'force_to_proceed' parameter to improve readability and type safety, especially since it is adjacent to another boolean parameter.

To keep this interaction as brief and non-intrusive as possible, please consider responding with one of following options:
**Done** | **OK But Won't Fix**: reason | **Later**: b/<bug_id> | **Invalid:** reason


_This comment was generated by [Experimental Blink C++ Code Review Agent](http://go/blink-c++-code-review-agent)._
_AI reviews can sometimes be inaccurate; We appreciate your 🙏 feedback 🙏 to help us improve._
_[File a bug](http://go/blink-c++-code-review-agent-feedback) | [Provide feedback on chat](https://chat.google.com/room/AAQA0zhQHe0?cls=4) | [Opt-out](https://ganpati2.corp.google.com/group/peep-genai-blink-agent-optout.prod)_

File third_party/blink/renderer/core/loader/frame_loader.h
Line 191, Patchset 47 (Latest): bool force_to_proceed,
AI Code Reviewer . unresolved

Blink Style Guide: Prefer enums or StrongAliases to bare bools for function parameters. Consider using a base::StrongAlias<class ForceToProceedTag, bool> or an enum class for the 'force_to_proceed' parameter to improve readability and type safety.

To keep this interaction as brief and non-intrusive as possible, please consider responding with one of following options:
**Done** | **OK But Won't Fix**: reason | **Later**: b/<bug_id> | **Invalid:** reason


_This comment was generated by [Experimental Blink C++ Code Review Agent](http://go/blink-c++-code-review-agent)._
_AI reviews can sometimes be inaccurate; We appreciate your 🙏 feedback 🙏 to help us improve._
_[File a bug](http://go/blink-c++-code-review-agent-feedback) | [Provide feedback on chat](https://chat.google.com/room/AAQA0zhQHe0?cls=4) | [Opt-out](https://ganpati2.corp.google.com/group/peep-genai-blink-agent-optout.prod)_

Open in Gerrit

Related details

Attention is currently required from:
  • Alex Moshchuk
  • Charlie Reis
  • Rakina Zata Amni
Submit Requirements:
    • requirement satisfiedCode-Coverage
    • requirement is not satisfiedCode-Owners
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: chromium/src
    Gerrit-Branch: main
    Gerrit-Change-Id: I7b19ffb64190f94f5e51aa93225c05a2aba0404a
    Gerrit-Change-Number: 7413835
    Gerrit-PatchSet: 47
    Gerrit-Owner: Minoru Chikamune <chik...@chromium.org>
    Gerrit-Reviewer: Alex Moshchuk <ale...@chromium.org>
    Gerrit-Reviewer: Charlie Reis <cr...@chromium.org>
    Gerrit-Reviewer: Minoru Chikamune <chik...@chromium.org>
    Gerrit-Reviewer: Rakina Zata Amni <rak...@chromium.org>
    Gerrit-CC: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
    Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
    Gerrit-CC: Nate Chapin <jap...@chromium.org>
    Gerrit-Attention: Alex Moshchuk <ale...@chromium.org>
    Gerrit-Attention: Rakina Zata Amni <rak...@chromium.org>
    Gerrit-Attention: Charlie Reis <cr...@chromium.org>
    Gerrit-Comment-Date: Fri, 23 Jan 2026 09:56:48 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    satisfied_requirement
    unsatisfied_requirement
    open
    diffy

    Charlie Reis (Gerrit)

    unread,
    Jan 23, 2026, 5:06:49 PMJan 23
    to Minoru Chikamune, AI Code Reviewer, Rakina Zata Amni, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
    Attention needed from Alex Moshchuk, Charlie Reis, Minoru Chikamune and Rakina Zata Amni

    Charlie Reis added 1 comment

    Patchset-level comments
    Minoru Chikamune . resolved

    Hi, I implemented the AsyncBeforeUnload feature. Still I'm adding tests, let me start gathering your opinions.

    Charlie Reis

    Thanks! I probably won't get through this today, but I'll try to review it soon.

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Alex Moshchuk
    • Charlie Reis
    • Minoru Chikamune
    • Rakina Zata Amni
    Submit Requirements:
    • requirement satisfiedCode-Coverage
    • requirement is not satisfiedCode-Owners
    • requirement is not satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • requirement is not satisfiedReview-Enforcement
    Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
    Gerrit-MessageType: comment
    Gerrit-Project: chromium/src
    Gerrit-Branch: main
    Gerrit-Change-Id: I7b19ffb64190f94f5e51aa93225c05a2aba0404a
    Gerrit-Change-Number: 7413835
    Gerrit-PatchSet: 48
    Gerrit-Owner: Minoru Chikamune <chik...@chromium.org>
    Gerrit-Reviewer: Alex Moshchuk <ale...@chromium.org>
    Gerrit-Reviewer: Charlie Reis <cr...@chromium.org>
    Gerrit-Reviewer: Minoru Chikamune <chik...@chromium.org>
    Gerrit-Reviewer: Rakina Zata Amni <rak...@chromium.org>
    Gerrit-CC: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
    Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
    Gerrit-CC: Nate Chapin <jap...@chromium.org>
    Gerrit-Attention: Alex Moshchuk <ale...@chromium.org>
    Gerrit-Attention: Minoru Chikamune <chik...@chromium.org>
    Gerrit-Attention: Rakina Zata Amni <rak...@chromium.org>
    Gerrit-Attention: Charlie Reis <cr...@chromium.org>
    Gerrit-Comment-Date: Fri, 23 Jan 2026 22:06:38 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    Comment-In-Reply-To: Minoru Chikamune <chik...@chromium.org>
    satisfied_requirement
    unsatisfied_requirement
    open
    diffy

    Minoru Chikamune (Gerrit)

    unread,
    Jan 25, 2026, 7:32:57 PMJan 25
    to Minoru Chikamune, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
    Attention needed from Alex Moshchuk, Charlie Reis and Rakina Zata Amni

    Minoru Chikamune added 2 comments

    File third_party/blink/renderer/core/dom/document.h
    Line 915, Patchset 47: bool force_to_proceed,
    AI Code Reviewer . resolved

    Blink Style Guide: Prefer enums or StrongAliases to bare bools for function parameters. Consider using a base::StrongAlias<class ForceToProceedTag, bool> or an enum class for the 'force_to_proceed' parameter to improve readability and type safety, especially since it is adjacent to another boolean parameter.

    To keep this interaction as brief and non-intrusive as possible, please consider responding with one of following options:
    **Done** | **OK But Won't Fix**: reason | **Later**: b/<bug_id> | **Invalid:** reason


    _This comment was generated by [Experimental Blink C++ Code Review Agent](http://go/blink-c++-code-review-agent)._
    _AI reviews can sometimes be inaccurate; We appreciate your 🙏 feedback 🙏 to help us improve._
    _[File a bug](http://go/blink-c++-code-review-agent-feedback) | [Provide feedback on chat](https://chat.google.com/room/AAQA0zhQHe0?cls=4) | [Opt-out](https://ganpati2.corp.google.com/group/peep-genai-blink-agent-optout.prod)_

    Minoru Chikamune

    OK But Won't Fix: `force_to_proceed` comes from the mojom interface.

    File third_party/blink/renderer/core/loader/frame_loader.h
    Line 191, Patchset 47: bool force_to_proceed,
    AI Code Reviewer . resolved

    Blink Style Guide: Prefer enums or StrongAliases to bare bools for function parameters. Consider using a base::StrongAlias<class ForceToProceedTag, bool> or an enum class for the 'force_to_proceed' parameter to improve readability and type safety.

    To keep this interaction as brief and non-intrusive as possible, please consider responding with one of following options:
    **Done** | **OK But Won't Fix**: reason | **Later**: b/<bug_id> | **Invalid:** reason


    _This comment was generated by [Experimental Blink C++ Code Review Agent](http://go/blink-c++-code-review-agent)._
    _AI reviews can sometimes be inaccurate; We appreciate your 🙏 feedback 🙏 to help us improve._
    _[File a bug](http://go/blink-c++-code-review-agent-feedback) | [Provide feedback on chat](https://chat.google.com/room/AAQA0zhQHe0?cls=4) | [Opt-out](https://ganpati2.corp.google.com/group/peep-genai-blink-agent-optout.prod)_

    Minoru Chikamune

    OK But Won't Fix: `force_to_proceed` comes from the mojom interface.

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Alex Moshchuk
    • Charlie Reis
    • Rakina Zata Amni
    Submit Requirements:
      • requirement satisfiedCode-Coverage
      • requirement is not satisfiedCode-Owners
      • requirement is not satisfiedCode-Review
      • requirement is not satisfiedReview-Enforcement
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: comment
      Gerrit-Project: chromium/src
      Gerrit-Branch: main
      Gerrit-Change-Id: I7b19ffb64190f94f5e51aa93225c05a2aba0404a
      Gerrit-Change-Number: 7413835
      Gerrit-PatchSet: 49
      Gerrit-Owner: Minoru Chikamune <chik...@chromium.org>
      Gerrit-Reviewer: Alex Moshchuk <ale...@chromium.org>
      Gerrit-Reviewer: Charlie Reis <cr...@chromium.org>
      Gerrit-Reviewer: Minoru Chikamune <chik...@chromium.org>
      Gerrit-Reviewer: Rakina Zata Amni <rak...@chromium.org>
      Gerrit-CC: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
      Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
      Gerrit-CC: Nate Chapin <jap...@chromium.org>
      Gerrit-Attention: Alex Moshchuk <ale...@chromium.org>
      Gerrit-Attention: Rakina Zata Amni <rak...@chromium.org>
      Gerrit-Attention: Charlie Reis <cr...@chromium.org>
      Gerrit-Comment-Date: Mon, 26 Jan 2026 00:32:23 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: No
      Comment-In-Reply-To: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
      satisfied_requirement
      unsatisfied_requirement
      open
      diffy

      Rakina Zata Amni (Gerrit)

      unread,
      Jan 26, 2026, 12:10:28 AMJan 26
      to Minoru Chikamune, AI Code Reviewer, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
      Attention needed from Alex Moshchuk, Charlie Reis and Minoru Chikamune

      Rakina Zata Amni added 5 comments

      Patchset-level comments
      File-level comment, Patchset 51 (Latest):
      Rakina Zata Amni . unresolved

      Thanks! I'm still trying to understand the whole beforeunload flow now, but I left some quick comments.. also can you please add a test?

      File content/browser/renderer_host/render_frame_host_impl.cc
      Line 17080, Patchset 51 (Latest): !rfh->HasStickyUserActivation() && navigation_request &&
      Rakina Zata Amni . unresolved

      From this [comment](https://docs.google.com/document/d/1QMAlEocmRVbTWkm5qoRMpn1T0Pb9qmfCSw4ydeZn1So/edit?disco=AAABxMe2Qco) should you be checking the whole subtree instead?

      Line 17091, Patchset 51 (Latest): if (!for_legacy && force_to_proceed) {
      Rakina Zata Amni . unresolved

      Can this actually be put earlier in the function before `before_unload_closure` to avoid nesting?

      Line 17116, Patchset 51 (Latest): initiator->async_beforeunload_pending_replies_.erase(
      Rakina Zata Amni . unresolved

      I wonder if this should be a member of the `NavigationRequest` instead. What happens if multiple navigations happen in the same FrameTree (e.g. a parent and a subframe independently triggered navigation), what does the async beforeunload tracking look like?

      Line 17136, Patchset 51 (Latest): }
      Rakina Zata Amni . unresolved

      Should we return early here?

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Alex Moshchuk
      • Charlie Reis
      • Minoru Chikamune
      Submit Requirements:
        • requirement satisfiedCode-Coverage
        • requirement is not satisfiedCode-Owners
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: chromium/src
        Gerrit-Branch: main
        Gerrit-Change-Id: I7b19ffb64190f94f5e51aa93225c05a2aba0404a
        Gerrit-Change-Number: 7413835
        Gerrit-PatchSet: 51
        Gerrit-Owner: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Reviewer: Charlie Reis <cr...@chromium.org>
        Gerrit-Reviewer: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-CC: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
        Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
        Gerrit-CC: Nate Chapin <jap...@chromium.org>
        Gerrit-Attention: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Attention: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Attention: Charlie Reis <cr...@chromium.org>
        Gerrit-Comment-Date: Mon, 26 Jan 2026 05:09:57 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Minoru Chikamune (Gerrit)

        unread,
        Jan 26, 2026, 2:11:04 AMJan 26
        to Minoru Chikamune, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Charlie Reis and Rakina Zata Amni

        Minoru Chikamune added 3 comments

        File content/browser/renderer_host/render_frame_host_impl.cc
        Line 17080, Patchset 51 (Latest): !rfh->HasStickyUserActivation() && navigation_request &&
        Rakina Zata Amni . unresolved

        From this [comment](https://docs.google.com/document/d/1QMAlEocmRVbTWkm5qoRMpn1T0Pb9qmfCSw4ydeZn1So/edit?disco=AAABxMe2Qco) should you be checking the whole subtree instead?

        Minoru Chikamune

        Oh, yeah. I actually changed my mind while I was writing this CL, because partially runnning beforeunload in the background also improve the performance. But yeah, I'll probably add a feature param so that we can try both.

        Line 17116, Patchset 51 (Latest): initiator->async_beforeunload_pending_replies_.erase(
        Rakina Zata Amni . unresolved

        I wonder if this should be a member of the `NavigationRequest` instead. What happens if multiple navigations happen in the same FrameTree (e.g. a parent and a subframe independently triggered navigation), what does the async beforeunload tracking look like?

        Minoru Chikamune

        Yeah, I wondered the same. I borrowed this idea from the existing `beforeunload_pending_replies_`, which waits for the all replies from the renderer, and it ended up with putting this on the initiator rfh.

        Rakina Zata Amni . unresolved

        Should we return early here?

        Minoru Chikamune

        Actually, it shouldn't. After running beforeunload in the renderer process, I want to proceed the navigation to start fetching the main resource below.

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alex Moshchuk
        • Charlie Reis
        • Rakina Zata Amni
        Gerrit-Attention: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-Attention: Charlie Reis <cr...@chromium.org>
        Gerrit-Comment-Date: Mon, 26 Jan 2026 07:10:42 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        Comment-In-Reply-To: Rakina Zata Amni <rak...@chromium.org>
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Minoru Chikamune (Gerrit)

        unread,
        Jan 26, 2026, 2:21:57 AMJan 26
        to Minoru Chikamune, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Charlie Reis and Rakina Zata Amni

        Minoru Chikamune added 1 comment

        File content/browser/renderer_host/render_frame_host_impl.cc
        Line 17080, Patchset 51 (Latest): !rfh->HasStickyUserActivation() && navigation_request &&
        Rakina Zata Amni . unresolved

        From this [comment](https://docs.google.com/document/d/1QMAlEocmRVbTWkm5qoRMpn1T0Pb9qmfCSw4ydeZn1So/edit?disco=AAABxMe2Qco) should you be checking the whole subtree instead?

        Minoru Chikamune

        Oh, yeah. I actually changed my mind while I was writing this CL, because partially runnning beforeunload in the background also improve the performance. But yeah, I'll probably add a feature param so that we can try both.

        Minoru Chikamune

        I found a bug from your review comment, Rakina. Thank you so much. Currently, The current CL only checks the current frame, but in renderer, FrameLoader::ShouldClose() checks all the descendant frames as well. I'll fix it.

        https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/loader/frame_loader.cc;l=1609;drc=b8e8841f24ebf5743e52266ddb0c28a08b7ee735

        Gerrit-Comment-Date: Mon, 26 Jan 2026 07:21:25 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        Comment-In-Reply-To: Minoru Chikamune <chik...@chromium.org>
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Minoru Chikamune (Gerrit)

        unread,
        Feb 2, 2026, 7:55:54 PMFeb 2
        to Minoru Chikamune, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Charlie Reis and Rakina Zata Amni

        Minoru Chikamune added 1 comment

        Patchset-level comments
        File-level comment, Patchset 78 (Latest):
        Minoru Chikamune . unresolved

        The android-x86-rel failure is likely a pre-existing flake and unrelated to this CL. (See: https://crbug.com/480202119)

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alex Moshchuk
        • Charlie Reis
        • Rakina Zata Amni
        Submit Requirements:
        • requirement satisfiedCode-Coverage
        • requirement is not satisfiedCode-Owners
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: chromium/src
        Gerrit-Branch: main
        Gerrit-Change-Id: I7b19ffb64190f94f5e51aa93225c05a2aba0404a
        Gerrit-Change-Number: 7413835
        Gerrit-PatchSet: 78
        Gerrit-Owner: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Reviewer: Charlie Reis <cr...@chromium.org>
        Gerrit-Reviewer: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-CC: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
        Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
        Gerrit-CC: Nate Chapin <jap...@chromium.org>
        Gerrit-Attention: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Attention: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-Attention: Charlie Reis <cr...@chromium.org>
        Gerrit-Comment-Date: Tue, 03 Feb 2026 00:55:20 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Rakina Zata Amni (Gerrit)

        unread,
        Feb 5, 2026, 2:42:31 AMFeb 5
        to Minoru Chikamune, AI Code Reviewer, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Charlie Reis and Minoru Chikamune

        Rakina Zata Amni added 12 comments

        Patchset-level comments
        File-level comment, Patchset 79 (Latest):
        Rakina Zata Amni . resolved

        Sorry for the delayed review and many questions! (Still trying to understand the flow of beforeunload..)

        File content/browser/renderer_host/async_beforeunload_commit_deferring_condition.cc
        Line 62, Patchset 79 (Latest): // proceed navigation if running the previous page's beforeunload takes too
        // long. (e.g. If the previous page's beforeunload handler has an
        Rakina Zata Amni . unresolved

        Out of curiosity, do we have something like this for the sync version as well? What is the timeout value for that?

        File content/browser/renderer_host/render_frame_host_impl.h
        Line 3934, Patchset 79 (Latest): // while the navigation proceeds. This is only possible if no frame with a
        Rakina Zata Amni . unresolved

        in the subtree? Also how does this interact with `subframes_only` (and maybe `send_ipc` too?)

        Line 3876, Patchset 79 (Latest): // handler asynchronously when there is no user gesture on the frame. (see:
        Rakina Zata Amni . unresolved

        subtree?

        File content/browser/renderer_host/render_frame_host_impl.cc
        Line 11677, Patchset 79 (Latest): async_beforeunload_pending_replies_.clear();
        Rakina Zata Amni . unresolved

        Why is this not in the `is_waiting_for_beforeunload_completion_` if clause?

        Line 12032, Patchset 79 (Latest): // `this` into `beforeunload_pending_replies_` as a placeholder, letting the
        Rakina Zata Amni . unresolved

        Why do we need this placeholder? Also why don't we just put things directly in `async_beforeunload_pending_replies_` instead of swapping here?

        Line 12154, Patchset 79 (Latest): SendBeforeUnloadAsync(is_reload, rfh->GetWeakPtr());
        Rakina Zata Amni . unresolved

        Why do some callers call `SendBeforeUnloadAsync()` but some others call `SendBeforeUnload()` with `should_run_before_unload_asynchronously` to true? What's the difference and in which case should we call which ones? Is it possible to just have one function for async beforeunload?

        Line 17080, Patchset 51: !rfh->HasStickyUserActivation() && navigation_request &&
        Rakina Zata Amni . resolved

        From this [comment](https://docs.google.com/document/d/1QMAlEocmRVbTWkm5qoRMpn1T0Pb9qmfCSw4ydeZn1So/edit?disco=AAABxMe2Qco) should you be checking the whole subtree instead?

        Minoru Chikamune

        Oh, yeah. I actually changed my mind while I was writing this CL, because partially runnning beforeunload in the background also improve the performance. But yeah, I'll probably add a feature param so that we can try both.

        Minoru Chikamune

        I found a bug from your review comment, Rakina. Thank you so much. Currently, The current CL only checks the current frame, but in renderer, FrameLoader::ShouldClose() checks all the descendant frames as well. I'll fix it.

        https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/core/loader/frame_loader.cc;l=1609;drc=b8e8841f24ebf5743e52266ddb0c28a08b7ee735

        Minoru Chikamune

        In the end, I ended up with implementing all-or-nothing. PTAL.

        Rakina Zata Amni

        Acknowledged

        Line 17091, Patchset 51: if (!for_legacy && force_to_proceed) {
        Rakina Zata Amni . resolved

        Can this actually be put earlier in the function before `before_unload_closure` to avoid nesting?

        Minoru Chikamune

        Done

        Rakina Zata Amni

        Acknowledged

        Line 17136, Patchset 51: }
        Rakina Zata Amni . resolved

        Should we return early here?

        Minoru Chikamune

        Actually, it shouldn't. After running beforeunload in the renderer process, I want to proceed the navigation to start fetching the main resource below.

        Minoru Chikamune

        Thank you so much. I ended up to change the logic. PTAL.

        Rakina Zata Amni

        Acknowledged

        Line 17280, Patchset 79 (Latest): if (initiator_rfh && rfh) {
        Rakina Zata Amni . unresolved

        Is it possible to get a reply from an already deleted RFH? Will that cause the navigation to be deferred until the timeout? Is there a better way to handle that?

        Line 17287, Patchset 79 (Latest): if (navigation_request) {
        Rakina Zata Amni . unresolved

        I wonder if this check should be earlier. Are beforeunloads tied to the RFH, or to the navigation? If there's a navigation #1 that triggers a beforeunload async, but then the navigation gets cancelled, and there's a new navigation #2, would that trigger a new beforeunload? If the reply for the first beforeunload came and removed that second beforeunload, is that ok?

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alex Moshchuk
        • Charlie Reis
        • Minoru Chikamune
        Submit Requirements:
        • requirement satisfiedCode-Coverage
        • requirement is not satisfiedCode-Owners
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: chromium/src
        Gerrit-Branch: main
        Gerrit-Change-Id: I7b19ffb64190f94f5e51aa93225c05a2aba0404a
        Gerrit-Change-Number: 7413835
        Gerrit-PatchSet: 79
        Gerrit-Owner: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Reviewer: Charlie Reis <cr...@chromium.org>
        Gerrit-Reviewer: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-CC: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
        Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
        Gerrit-CC: Nate Chapin <jap...@chromium.org>
        Gerrit-Attention: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Attention: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Attention: Charlie Reis <cr...@chromium.org>
        Gerrit-Comment-Date: Thu, 05 Feb 2026 07:41:58 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Minoru Chikamune (Gerrit)

        unread,
        Feb 5, 2026, 7:19:54 AMFeb 5
        to Minoru Chikamune, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Charlie Reis and Rakina Zata Amni

        Minoru Chikamune added 2 comments

        Patchset-level comments
        Minoru Chikamune . resolved

        Thank you for checking! I'll reply them tomorrow.

        File content/browser/renderer_host/async_beforeunload_commit_deferring_condition.cc
        Line 62, Patchset 79 (Latest): // proceed navigation if running the previous page's beforeunload takes too
        // long. (e.g. If the previous page's beforeunload handler has an
        Rakina Zata Amni . unresolved

        Out of curiosity, do we have something like this for the sync version as well? What is the timeout value for that?

        Minoru Chikamune

        `RenderFrameHostImpl::beforeunload_timeout_delay_` is doing it. The timeout value is 500ms. `UnloadTest.CrossSiteInfiniteBeforeUnloadAsync` tests it.


        ```
        constexpr base::TimeDelta kUnloadTimeout = base::Milliseconds(500);
        ```

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alex Moshchuk
        • Charlie Reis
        • Rakina Zata Amni
        Gerrit-Attention: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-Attention: Charlie Reis <cr...@chromium.org>
        Gerrit-Comment-Date: Thu, 05 Feb 2026 12:19:19 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        Comment-In-Reply-To: Rakina Zata Amni <rak...@chromium.org>
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Minoru Chikamune (Gerrit)

        unread,
        Feb 6, 2026, 3:25:44 AMFeb 6
        to Minoru Chikamune, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Charlie Reis and Rakina Zata Amni

        Minoru Chikamune added 7 comments

        File content/browser/renderer_host/render_frame_host_impl.h
        Line 3934, Patchset 79 (Latest): // while the navigation proceeds. This is only possible if no frame with a
        Rakina Zata Amni . unresolved

        in the subtree? Also how does this interact with `subframes_only` (and maybe `send_ipc` too?)

        Minoru Chikamune

        Sure. I'll add about it in the code comment.

        Line 3876, Patchset 79 (Latest): // handler asynchronously when there is no user gesture on the frame. (see:
        Rakina Zata Amni . unresolved

        subtree?

        Minoru Chikamune

        Yeah, the frame that is navigating away and its descendants. I'll update the comment.

        File content/browser/renderer_host/render_frame_host_impl.cc
        Line 11677, Patchset 79 (Latest): async_beforeunload_pending_replies_.clear();
        Rakina Zata Amni . unresolved

        Why is this not in the `is_waiting_for_beforeunload_completion_` if clause?

        Minoru Chikamune

        It's because is_waiting_for_beforeunload_completion_ is already false when the beforeunload is asynchronously running.

        Line 12032, Patchset 79 (Latest): // `this` into `beforeunload_pending_replies_` as a placeholder, letting the
        Rakina Zata Amni . unresolved

        Why do we need this placeholder? Also why don't we just put things directly in `async_beforeunload_pending_replies_` instead of swapping here?

        Minoru Chikamune

        Why do we need this placeholder?

        This is the same behavior as L12018: `beforeunload_pending_replies_.insert(this);`. It adds the frame (navigation root) as a "placeholder", and `RenderFrameHostImpl::ProcessBeforeUnloadCompletedFromFrame()` consumes it (`beforeunload_pending_replies_.erase(frame);`).

        Also why don't we just put things directly in async_beforeunload_pending_replies_ instead of swapping here?

        `RenderFrameHostImpl::CheckOrDispatchBeforeUnloadForFrame()` algorithm relies on `beforeunload_pending_replies_` not to send duplicated request. So, `beforeunload_pending_replies_` should be kept as is until the `ForEachRenderFrameHostImplWithAction` ends.

        Initially, I didn't understand it, and actually, the beforeunload handlers run twice before in the test.

        Line 12154, Patchset 79 (Latest): SendBeforeUnloadAsync(is_reload, rfh->GetWeakPtr());
        Rakina Zata Amni . unresolved

        Why do some callers call `SendBeforeUnloadAsync()` but some others call `SendBeforeUnload()` with `should_run_before_unload_asynchronously` to true? What's the difference and in which case should we call which ones? Is it possible to just have one function for async beforeunload?

        Minoru Chikamune

        `should_run_before_unload_asynchronously` is decided only once for one navigation. After the decision, always use SendBeforeUnloadAsync() when `should_run_before_unload_asynchronously` is true.

        Line 17280, Patchset 79 (Latest): if (initiator_rfh && rfh) {
        Rakina Zata Amni . unresolved

        Is it possible to get a reply from an already deleted RFH? Will that cause the navigation to be deferred until the timeout? Is there a better way to handle that?

        Minoru Chikamune

        Is it possible to get a reply from an already deleted RFH?

        Umm. Probably no. Probably I can get rid of this `&& rfh` condition.

        Line 17287, Patchset 79 (Latest): if (navigation_request) {
        Rakina Zata Amni . unresolved

        I wonder if this check should be earlier. Are beforeunloads tied to the RFH, or to the navigation? If there's a navigation #1 that triggers a beforeunload async, but then the navigation gets cancelled, and there's a new navigation #2, would that trigger a new beforeunload? If the reply for the first beforeunload came and removed that second beforeunload, is that ok?

        Minoru Chikamune

        Yeah... you are right. As you said before [1], async_beforeunload_pending_replies_ should be kept in navigation_request. Otherwise, the code can't handle the scenario you mentioned. Probably running beforeunload handlers asynchronously increases such situation. I'll update this CL.

        [1] https://chromium-review.googlesource.com/c/chromium/src/+/7413835/comment/d54bf1ce_3c1a522e/

        Gerrit-Comment-Date: Fri, 06 Feb 2026 08:25:17 +0000
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Minoru Chikamune (Gerrit)

        unread,
        Feb 6, 2026, 4:31:03 AMFeb 6
        to Minoru Chikamune, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Charlie Reis and Rakina Zata Amni

        Minoru Chikamune added 2 comments

        File content/browser/renderer_host/render_frame_host_impl.h
        Line 3934, Patchset 79: // while the navigation proceeds. This is only possible if no frame with a
        Rakina Zata Amni . unresolved

        in the subtree? Also how does this interact with `subframes_only` (and maybe `send_ipc` too?)

        Minoru Chikamune

        Sure. I'll add about it in the code comment.

        Minoru Chikamune

        Done

        Line 3876, Patchset 79: // handler asynchronously when there is no user gesture on the frame. (see:
        Rakina Zata Amni . unresolved

        subtree?

        Minoru Chikamune

        Yeah, the frame that is navigating away and its descendants. I'll update the comment.

        Minoru Chikamune

        Done

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alex Moshchuk
        • Charlie Reis
        • Rakina Zata Amni
        Submit Requirements:
        • requirement satisfiedCode-Coverage
        • requirement is not satisfiedCode-Owners
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: chromium/src
        Gerrit-Branch: main
        Gerrit-Change-Id: I7b19ffb64190f94f5e51aa93225c05a2aba0404a
        Gerrit-Change-Number: 7413835
        Gerrit-PatchSet: 80
        Gerrit-Owner: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Reviewer: Charlie Reis <cr...@chromium.org>
        Gerrit-Reviewer: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-CC: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
        Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
        Gerrit-CC: Nate Chapin <jap...@chromium.org>
        Gerrit-Attention: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Attention: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-Attention: Charlie Reis <cr...@chromium.org>
        Gerrit-Comment-Date: Fri, 06 Feb 2026 09:30:33 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        Comment-In-Reply-To: Minoru Chikamune <chik...@chromium.org>
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Charlie Reis (Gerrit)

        unread,
        Feb 6, 2026, 6:25:48 PMFeb 6
        to Minoru Chikamune, AI Code Reviewer, Rakina Zata Amni, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Minoru Chikamune and Rakina Zata Amni

        Charlie Reis added 11 comments

        Patchset-level comments
        File-level comment, Patchset 80 (Latest):
        Charlie Reis . resolved

        Sorry for the delay-- I'm glad you've been making progress with Rakina's review. Some initial questions and nits below as I start to go through this. (I haven't reviewed the render_frame_host_impl.cc implementation or tests yet.)

        File content/browser/renderer_host/async_beforeunload_commit_deferring_condition.h
        Line 40, Patchset 80 (Latest): // too long, preventing the navigation from being hung indefinitely.
        Charlie Reis . unresolved

        Just curious-- do we need a separate timer for this if there's already [one in RFHI](https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/render_frame_host_impl.h;drc=976a71ef2a509d4391d814b52b9f4700fdf35789;l=4756)? If RenderFrameHostImpl::BeforeUnloadTimeout() had a way of telling this condition to proceed, would that avoid the need for a second timer?

        I mainly ask because it seems tricky to think through all the possibilities of which timer fires first, if at all. If there's a good way to think about how they relate to each other, that might be worth putting in the comment here.

        Line 26, Patchset 80 (Latest): // asynchronously
        Charlie Reis . unresolved

        minor nit: End with period.

        Line 22, Patchset 80 (Latest):class AsyncBeforeunloadCommitDeferringCondition
        Charlie Reis . unresolved

        minor nit: I think we usually capitalize the U in BeforeUnload, such as in [DispatchBeforeUnload](https://source.chromium.org/chromium/chromium/src/+/main:content/browser/web_contents/web_contents_impl.h;drc=c19b719ae21c09c2052b30b3f8b56e3ce6f51dbe;l=481), NeedToFireBeforeUnloadOrUnloadEvents, BeforeUnloadCompleted, etc.

        Line 20, Patchset 80 (Latest):// to finish just before the navigation is ready to commit, ensuring any
        Charlie Reis . unresolved

        nit: before allowing the navigation to commit

        Line 19, Patchset 80 (Latest):// (due to lack of user gesture), this condition ensures we still wait for them
        Charlie Reis . unresolved

        Maybe add: "and thus an inability to show a dialog"?

        File content/browser/renderer_host/navigation_request.h
        Line 3526, Patchset 80 (Latest): // for a timeout to occur.
        Charlie Reis . unresolved

        Can you clarify in the comment which frames this might include? Is it "this frame and/or its descendant frames ... [excluding those] that don't have beforeunload handlers defined" like beforeunload_pending_replies_? I guess it's slightly trickier since this is on NavigationRequest instead of RFH, but maybe it still applies to the navigating frame and its descendants?

        Hopefully we're careful about not leaving any dangling pointers in here if descendant frames are removed?

        File content/browser/renderer_host/render_frame_host_impl.h
        Line 3941, Patchset 80 (Latest): // sticky user activation, which guarantees that the renderer will not attempt
        // to cancel navigation.
        Charlie Reis . unresolved

        Worth mentioning the dialog here.

        Line 3884, Patchset 80 (Latest): base::WeakPtr<RenderFrameHostImpl> rfh);
        Charlie Reis . unresolved

        At first glance, I'm not sure what `rfh` is meant to be, since this is a function on RenderFrameHost already. Can you document that?

        Line 3879, Patchset 80 (Latest): // The synchronously executed `beforeunload` handlers delay the user perceived
        Charlie Reis . unresolved

        minor nit: Drop "The"

        File content/common/features.cc
        Line 44, Patchset 80 (Latest): base::Milliseconds(500));
        Charlie Reis . unresolved

        Related to my earlier question about the separate timeout, why would we need a different timeout value if we did use one? That's probably worth documenting if we need to keep this.

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alex Moshchuk
        • Minoru Chikamune
        • Rakina Zata Amni
        Gerrit-Attention: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Attention: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-Comment-Date: Fri, 06 Feb 2026 23:25:38 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Minoru Chikamune (Gerrit)

        unread,
        Feb 9, 2026, 3:03:03 AMFeb 9
        to Minoru Chikamune, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Charlie Reis and Rakina Zata Amni

        Minoru Chikamune added 15 comments

        File content/browser/renderer_host/async_beforeunload_commit_deferring_condition.h
        Line 40, Patchset 80: // too long, preventing the navigation from being hung indefinitely.
        Charlie Reis . unresolved

        Just curious-- do we need a separate timer for this if there's already [one in RFHI](https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/render_frame_host_impl.h;drc=976a71ef2a509d4391d814b52b9f4700fdf35789;l=4756)? If RenderFrameHostImpl::BeforeUnloadTimeout() had a way of telling this condition to proceed, would that avoid the need for a second timer?

        I mainly ask because it seems tricky to think through all the possibilities of which timer fires first, if at all. If there's a good way to think about how they relate to each other, that might be worth putting in the comment here.

        Minoru Chikamune

        Currently, I don't have a clear answer, but I agree that it's better to use one timer instead of two timers. I'll check if I can use just one timer.

        Line 26, Patchset 80: // asynchronously
        Charlie Reis . resolved

        minor nit: End with period.

        Minoru Chikamune

        Done

        Line 22, Patchset 80:class AsyncBeforeunloadCommitDeferringCondition
        Charlie Reis . resolved

        minor nit: I think we usually capitalize the U in BeforeUnload, such as in [DispatchBeforeUnload](https://source.chromium.org/chromium/chromium/src/+/main:content/browser/web_contents/web_contents_impl.h;drc=c19b719ae21c09c2052b30b3f8b56e3ce6f51dbe;l=481), NeedToFireBeforeUnloadOrUnloadEvents, BeforeUnloadCompleted, etc.

        Minoru Chikamune

        Sure. I'll update them all.

        Line 20, Patchset 80:// to finish just before the navigation is ready to commit, ensuring any
        Charlie Reis . resolved

        nit: before allowing the navigation to commit

        Minoru Chikamune

        Done

        Line 19, Patchset 80:// (due to lack of user gesture), this condition ensures we still wait for them
        Charlie Reis . resolved

        Maybe add: "and thus an inability to show a dialog"?

        Minoru Chikamune

        Done

        File content/browser/renderer_host/render_frame_host_impl.h
        Line 3941, Patchset 80: // sticky user activation, which guarantees that the renderer will not attempt
        // to cancel navigation.
        Charlie Reis . resolved

        Worth mentioning the dialog here.

        Minoru Chikamune

        Done

        Line 3884, Patchset 80: base::WeakPtr<RenderFrameHostImpl> rfh);
        Charlie Reis . resolved

        At first glance, I'm not sure what `rfh` is meant to be, since this is a function on RenderFrameHost already. Can you document that?

        Minoru Chikamune

        Done

        Line 3879, Patchset 80: // The synchronously executed `beforeunload` handlers delay the user perceived
        Charlie Reis . resolved

        minor nit: Drop "The"

        Minoru Chikamune

        Done

        Line 3934, Patchset 79: // while the navigation proceeds. This is only possible if no frame with a
        Rakina Zata Amni . resolved

        in the subtree? Also how does this interact with `subframes_only` (and maybe `send_ipc` too?)

        Minoru Chikamune

        Sure. I'll add about it in the code comment.

        Minoru Chikamune

        Done

        Minoru Chikamune

        Done

        Line 3876, Patchset 79: // handler asynchronously when there is no user gesture on the frame. (see:
        Rakina Zata Amni . resolved

        subtree?

        Minoru Chikamune

        Yeah, the frame that is navigating away and its descendants. I'll update the comment.

        Minoru Chikamune

        Done

        Minoru Chikamune

        Done

        File content/browser/renderer_host/render_frame_host_impl.cc
        Line 11677, Patchset 79: async_beforeunload_pending_replies_.clear();
        Rakina Zata Amni . unresolved

        Why is this not in the `is_waiting_for_beforeunload_completion_` if clause?

        Minoru Chikamune

        It's because is_waiting_for_beforeunload_completion_ is already false when the beforeunload is asynchronously running.

        Minoru Chikamune

        In the new patch-set, I moved the async_before_unload_pending_replies from RFH to NavigationRequest. And I removed the previous reset code from RFH.

        Line 17116, Patchset 51: initiator->async_beforeunload_pending_replies_.erase(
        Rakina Zata Amni . unresolved

        I wonder if this should be a member of the `NavigationRequest` instead. What happens if multiple navigations happen in the same FrameTree (e.g. a parent and a subframe independently triggered navigation), what does the async beforeunload tracking look like?

        Minoru Chikamune

        Yeah, I wondered the same. I borrowed this idea from the existing `beforeunload_pending_replies_`, which waits for the all replies from the renderer, and it ended up with putting this on the initiator rfh.

        Minoru Chikamune

        In the new patch-set, I moved the async_before_unload_pending_replies from RFH to NavigationRequest. Thank you for pointing this out.

        Probably, the current intention to keep beforeunload_pending_replies_ on RFH was that the beforeunload dialog should be shown once when the user initiate multiple navigations in a short period of time.

        In our case, when `SendBeforeUnloadAsync` code path is used, it does not show dialogs, but perhaps SendBeforeUnloadAsync runs more beforeunload handlers. I'm not super shure if this is ok or not.

        Line 17280, Patchset 79: if (initiator_rfh && rfh) {
        Rakina Zata Amni . resolved

        Is it possible to get a reply from an already deleted RFH? Will that cause the navigation to be deferred until the timeout? Is there a better way to handle that?

        Minoru Chikamune

        Is it possible to get a reply from an already deleted RFH?

        Umm. Probably no. Probably I can get rid of this `&& rfh` condition.

        Minoru Chikamune

        Removed the `&& rfh` condition.

        Line 17287, Patchset 79: if (navigation_request) {
        Rakina Zata Amni . resolved

        I wonder if this check should be earlier. Are beforeunloads tied to the RFH, or to the navigation? If there's a navigation #1 that triggers a beforeunload async, but then the navigation gets cancelled, and there's a new navigation #2, would that trigger a new beforeunload? If the reply for the first beforeunload came and removed that second beforeunload, is that ok?

        Minoru Chikamune

        Yeah... you are right. As you said before [1], async_beforeunload_pending_replies_ should be kept in navigation_request. Otherwise, the code can't handle the scenario you mentioned. Probably running beforeunload handlers asynchronously increases such situation. I'll update this CL.

        [1] https://chromium-review.googlesource.com/c/chromium/src/+/7413835/comment/d54bf1ce_3c1a522e/

        Minoru Chikamune

        In the new patch-set, I moved the async_before_unload_pending_replies from RFH to NavigationRequest.

        File content/common/features.cc
        Line 44, Patchset 80: base::Milliseconds(500));
        Charlie Reis . unresolved

        Related to my earlier question about the separate timeout, why would we need a different timeout value if we did use one? That's probably worth documenting if we need to keep this.

        Minoru Chikamune

        Ditto. Currently, I don't have a clear answer, but I agree that it's better to use one timer instead of two timers. I'll check if I can use just one timer.

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alex Moshchuk
        • Charlie Reis
        • Rakina Zata Amni
        Submit Requirements:
        • requirement satisfiedCode-Coverage
        • requirement is not satisfiedCode-Owners
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: chromium/src
        Gerrit-Branch: main
        Gerrit-Change-Id: I7b19ffb64190f94f5e51aa93225c05a2aba0404a
        Gerrit-Change-Number: 7413835
        Gerrit-PatchSet: 81
        Gerrit-Owner: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Reviewer: Charlie Reis <cr...@chromium.org>
        Gerrit-Reviewer: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-CC: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
        Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
        Gerrit-CC: Nate Chapin <jap...@chromium.org>
        Gerrit-Attention: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Attention: Charlie Reis <cr...@chromium.org>
        Gerrit-Attention: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-Comment-Date: Mon, 09 Feb 2026 08:02:41 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        Comment-In-Reply-To: Minoru Chikamune <chik...@chromium.org>
        Comment-In-Reply-To: Charlie Reis <cr...@chromium.org>
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Minoru Chikamune (Gerrit)

        unread,
        Feb 9, 2026, 3:06:19 AMFeb 9
        to Minoru Chikamune, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Charlie Reis and Rakina Zata Amni

        Minoru Chikamune added 2 comments

        Patchset-level comments
        Rakina Zata Amni . resolved

        Thanks! I'm still trying to understand the whole beforeunload flow now, but I left some quick comments.. also can you please add a test?

        Minoru Chikamune

        Now we have content/browser/renderer_host/render_frame_host_impl_browsertest.cc.

        File-level comment, Patchset 78:
        Minoru Chikamune . resolved

        The android-x86-rel failure is likely a pre-existing flake and unrelated to this CL. (See: https://crbug.com/480202119)

        Minoru Chikamune

        Done

        Gerrit-Comment-Date: Mon, 09 Feb 2026 08:05:57 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        Comment-In-Reply-To: Minoru Chikamune <chik...@chromium.org>
        Comment-In-Reply-To: Rakina Zata Amni <rak...@chromium.org>
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Minoru Chikamune (Gerrit)

        unread,
        Feb 9, 2026, 3:32:46 AMFeb 9
        to Minoru Chikamune, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Charlie Reis, Minoru Chikamune and Rakina Zata Amni

        Minoru Chikamune voted and added 1 comment

        Votes added by Minoru Chikamune

        Commit-Queue+1

        1 comment

        File content/browser/renderer_host/navigation_request.h
        Line 3526, Patchset 80: // for a timeout to occur.
        Charlie Reis . unresolved

        Can you clarify in the comment which frames this might include? Is it "this frame and/or its descendant frames ... [excluding those] that don't have beforeunload handlers defined" like beforeunload_pending_replies_? I guess it's slightly trickier since this is on NavigationRequest instead of RFH, but maybe it still applies to the navigating frame and its descendants?

        Hopefully we're careful about not leaving any dangling pointers in here if descendant frames are removed?

        Minoru Chikamune

        Can you clarify in the comment which frames

        I updated the code comment.

        not leaving any dangling pointers in here if descendant frames are removed?

        Oh, you are right. Probably I need to do something similar to the following logic in the destructor of RFH. I'll update the code.

        ```
        // If another frame is waiting for a beforeunload completion callback from
        // this frame, simulate it now.
        RenderFrameHostImpl* beforeunload_initiator = GetBeforeUnloadInitiator();
        if (beforeunload_initiator && beforeunload_initiator != this) {
        base::TimeTicks approx_renderer_start_time = send_before_unload_start_time_;
        beforeunload_initiator->ProcessBeforeUnloadCompletedFromFrame(
        /*proceed=*/true, /*treat_as_final_completion_callback=*/false, this,
        /*is_frame_being_destroyed=*/true, approx_renderer_start_time,
        base::TimeTicks::Now(), /*for_legacy=*/false);
        }
        ```
        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alex Moshchuk
        • Charlie Reis
        • Minoru Chikamune
        • Rakina Zata Amni
        Gerrit-Attention: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Attention: Charlie Reis <cr...@chromium.org>
        Gerrit-Attention: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-Comment-Date: Mon, 09 Feb 2026 08:32:25 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: Yes
        Comment-In-Reply-To: Charlie Reis <cr...@chromium.org>
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Alex Moshchuk (Gerrit)

        unread,
        Feb 10, 2026, 1:00:26 PMFeb 10
        to Minoru Chikamune, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Charlie Reis, Minoru Chikamune and Rakina Zata Amni

        Alex Moshchuk added 12 comments

        Patchset-level comments
        File-level comment, Patchset 82 (Latest):
        Alex Moshchuk . resolved

        Thanks, adding a few comments as I also try to understand how everything fits together a bit more.

        Commit Message
        Line 13, Patchset 82 (Latest):blocked.
        Alex Moshchuk . unresolved

        Can you also mention why user activation matters? Namely, the lack of user activation implies that beforeunload handlers can't show dialogs that users may use to cancel the navigation.

        Line 21, Patchset 82 (Latest):
        Alex Moshchuk . unresolved

        Probably also worth noting that this optimization affects only navigations. Beforeunload handlers for closing or detaching frames are not affected.

        File content/browser/renderer_host/navigation_request.h
        Line 3533, Patchset 82 (Latest): std::set<raw_ptr<RenderFrameHostImpl, SetExperimental>>
        Alex Moshchuk . unresolved

        Given the possibility that descendant RFHs might be deleted before we get a beforeunload response from them, I wonder if a safer design (improving on beforeunload_pending_replies_) would be GlobalRenderFrameHostIds that we can resolve if needed? Maybe the RFH destruction issue that Charlie mentioned would be easier to handle with such a design. (Also, I'm not sure if we actually need to resolve and call methods on these RFHs, vs just having a way to track whether each one has finished running beforeunload.)

        File content/browser/renderer_host/render_frame_host_impl.h
        Line 3888, Patchset 82 (Latest): // See the fixme comment in `FrameLoader::ShouldClose` function: "There is not
        // yet any way to dispatch events to out-of-process frames."
        Alex Moshchuk . unresolved

        Oh, that comment is super-old and probably predates the OOPIF beforeunload support that we added in M63. It can probably be removed (since we do dispatch beforeunload to OOPIFs, just requiring them all to complete before kicking off the navigation), and I'm not sure it's worth mentioning here.

        File content/browser/renderer_host/render_frame_host_impl.cc
        Line 12034, Patchset 82 (Latest): DCHECK(send_ipc);
        Alex Moshchuk . unresolved

        nit: I think we're supposed to prefer CHECKs in all new code.

        Line 12154, Patchset 79: SendBeforeUnloadAsync(is_reload, rfh->GetWeakPtr());
        Rakina Zata Amni . unresolved

        Why do some callers call `SendBeforeUnloadAsync()` but some others call `SendBeforeUnload()` with `should_run_before_unload_asynchronously` to true? What's the difference and in which case should we call which ones? Is it possible to just have one function for async beforeunload?

        Minoru Chikamune

        `should_run_before_unload_asynchronously` is decided only once for one navigation. After the decision, always use SendBeforeUnloadAsync() when `should_run_before_unload_asynchronously` is true.

        Alex Moshchuk

        +1 to Rakina's question here, as I'm also not sure I understand yet why we need both helpers when it seems like SendBeforeUnload can support both modes (since it takes the `should_run_before_unload_asynchronously` flag). Is it because the ack processing in SendBeforeUnloadAsync is sufficiently different that it'll make SendBeforeUnload too complex if we wanted to fold it in there?

        Also, maybe it would be helpful to walk through an example like your test - how would SendBeforeUnload/SendBeforeUnloadAsync end up getting invoked in that case?

        Line 17274, Patchset 82 (Latest): frame_tree_node_->navigation_request()->GetWeakPtr();
        Alex Moshchuk . unresolved

        Just trying to understand: looks like this is called from CheckOrDispatchBeforeUnloadForFrame for each frame that needs to send a beforeunload IPC to the renderer. Why are we looking up the navigation request from each frame's FTN here? Shouldn't it always be retrieved from the ancestor frame that's navigating and triggering beforeunload handlers in all of its descendants? (I.e., the equivalent of what GetBeforeUnloadInitiator() does.)

        Line 17285, Patchset 82 (Latest): bool proceed, base::TimeTicks renderer_before_unload_start_time,
        base::TimeTicks renderer_before_unload_end_time,
        Alex Moshchuk . unresolved

        Are we going to need to process these for metrics, similarly how they're handled on the old path?

        On that note, how are the NavigationRequest::MaybeRecordTraceEventsAndHistograms() trace events and metrics affected by AsyncBeforeUnload? Will they need to be updated, and if so, will that happen here or in a separate CL?

        File content/browser/renderer_host/render_frame_host_impl_browsertest.cc
        Line 8171, Patchset 82 (Latest): NotifyUserActivation(rfh_a);
        Alex Moshchuk . unresolved

        This test is named *WithoutUserActivation, so I'm confused why we're granting user activation to `rfh_a` here? Why doesn't it cause async beforeunload to be bypassed regardless of AsyncBeforeUnload feature? (More comments to explain the expected flow of the test would be great.)

        Line 8206, Patchset 82 (Latest):
        Alex Moshchuk . unresolved

        We'll probably want at least a few other tests eventually, e.g. for renderer-initiated navigations, for ensuring that tab close doesn't trigger the optimization, for ensuring that user activation in one of the frames turns off the optimization, etc.

        File content/common/features.cc
        Line 44, Patchset 80: base::Milliseconds(500));
        Charlie Reis . unresolved

        Related to my earlier question about the separate timeout, why would we need a different timeout value if we did use one? That's probably worth documenting if we need to keep this.

        Minoru Chikamune

        Ditto. Currently, I don't have a clear answer, but I agree that it's better to use one timer instead of two timers. I'll check if I can use just one timer.

        Alex Moshchuk

        I'd expect that by the time we get to WillCommitNavigation, the beforeunload handlers have been running for some time (since the start of navigation). So I wonder if the timer in AsyncBeforeUnloadCommitDeferringCondition::WillCommitNavigation should be set to the remaining time, so that beforeunload handlers still get 500ms total?

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Charlie Reis
        • Minoru Chikamune
        • Rakina Zata Amni
        Submit Requirements:
        • requirement satisfiedCode-Coverage
        • requirement is not satisfiedCode-Owners
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: chromium/src
        Gerrit-Branch: main
        Gerrit-Change-Id: I7b19ffb64190f94f5e51aa93225c05a2aba0404a
        Gerrit-Change-Number: 7413835
        Gerrit-PatchSet: 82
        Gerrit-Owner: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Reviewer: Charlie Reis <cr...@chromium.org>
        Gerrit-Reviewer: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-CC: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
        Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
        Gerrit-CC: Nate Chapin <jap...@chromium.org>
        Gerrit-Attention: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Attention: Charlie Reis <cr...@chromium.org>
        Gerrit-Attention: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-Comment-Date: Tue, 10 Feb 2026 18:00:13 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        Comment-In-Reply-To: Minoru Chikamune <chik...@chromium.org>
        Comment-In-Reply-To: Rakina Zata Amni <rak...@chromium.org>
        Comment-In-Reply-To: Charlie Reis <cr...@chromium.org>
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Minoru Chikamune (Gerrit)

        unread,
        Feb 12, 2026, 4:49:18 AMFeb 12
        to Minoru Chikamune, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Charlie Reis and Rakina Zata Amni

        Minoru Chikamune voted and added 11 comments

        Votes added by Minoru Chikamune

        Commit-Queue+1

        11 comments

        Commit Message
        Line 13, Patchset 82:blocked.
        Alex Moshchuk . resolved

        Can you also mention why user activation matters? Namely, the lack of user activation implies that beforeunload handlers can't show dialogs that users may use to cancel the navigation.

        Minoru Chikamune

        Done

        Line 21, Patchset 82:
        Alex Moshchuk . resolved

        Probably also worth noting that this optimization affects only navigations. Beforeunload handlers for closing or detaching frames are not affected.

        Minoru Chikamune

        Done

        File content/browser/renderer_host/navigation_request.h
        Line 3533, Patchset 82: std::set<raw_ptr<RenderFrameHostImpl, SetExperimental>>
        Alex Moshchuk . unresolved

        Given the possibility that descendant RFHs might be deleted before we get a beforeunload response from them, I wonder if a safer design (improving on beforeunload_pending_replies_) would be GlobalRenderFrameHostIds that we can resolve if needed? Maybe the RFH destruction issue that Charlie mentioned would be easier to handle with such a design. (Also, I'm not sure if we actually need to resolve and call methods on these RFHs, vs just having a way to track whether each one has finished running beforeunload.)

        Minoru Chikamune

        Oh nice! GlobalRenderFrameHostId is easy way to manage this field. Thank you so much for your advice. I updated the code.

        Line 3526, Patchset 80: // for a timeout to occur.
        Charlie Reis . unresolved

        Can you clarify in the comment which frames this might include? Is it "this frame and/or its descendant frames ... [excluding those] that don't have beforeunload handlers defined" like beforeunload_pending_replies_? I guess it's slightly trickier since this is on NavigationRequest instead of RFH, but maybe it still applies to the navigating frame and its descendants?

        Hopefully we're careful about not leaving any dangling pointers in here if descendant frames are removed?

        Minoru Chikamune

        Can you clarify in the comment which frames

        I updated the code comment.

        not leaving any dangling pointers in here if descendant frames are removed?

        Oh, you are right. Probably I need to do something similar to the following logic in the destructor of RFH. I'll update the code.

        ```
        // If another frame is waiting for a beforeunload completion callback from
        // this frame, simulate it now.
        RenderFrameHostImpl* beforeunload_initiator = GetBeforeUnloadInitiator();
        if (beforeunload_initiator && beforeunload_initiator != this) {
        base::TimeTicks approx_renderer_start_time = send_before_unload_start_time_;
        beforeunload_initiator->ProcessBeforeUnloadCompletedFromFrame(
        /*proceed=*/true, /*treat_as_final_completion_callback=*/false, this,
        /*is_frame_being_destroyed=*/true, approx_renderer_start_time,
        base::TimeTicks::Now(), /*for_legacy=*/false);
        }
        ```
        Minoru Chikamune

        Done. I switched to use GlobalRenderFrameHostId, and created `NavigationRequest::MaybeResumeAsyncBeforeUnloadCommit()`, and now the destructor of RFHI calls it, just like the normal beforeunload handling.

        File content/browser/renderer_host/render_frame_host_impl.h
        Line 3888, Patchset 82: // See the fixme comment in `FrameLoader::ShouldClose` function: "There is not

        // yet any way to dispatch events to out-of-process frames."
        Alex Moshchuk . unresolved

        Oh, that comment is super-old and probably predates the OOPIF beforeunload support that we added in M63. It can probably be removed (since we do dispatch beforeunload to OOPIFs, just requiring them all to complete before kicking off the navigation), and I'm not sure it's worth mentioning here.

        Minoru Chikamune

        I see. But still this code relies on this assumption. The browser process calls the mojo API for each renderers.

        e.g. For RFH tree A(B(A), C(B)), SendBeforeUnloadAsync() is called from CheckOrDispatchBeforeUnloadForFrame for A, B, C if A, B, C is hosted in a separated renderer.

        File content/browser/renderer_host/render_frame_host_impl.cc
        Line 12034, Patchset 82: DCHECK(send_ipc);
        Alex Moshchuk . resolved

        nit: I think we're supposed to prefer CHECKs in all new code.

        Minoru Chikamune

        Done

        Line 12154, Patchset 79: SendBeforeUnloadAsync(is_reload, rfh->GetWeakPtr());
        Rakina Zata Amni . unresolved

        Why do some callers call `SendBeforeUnloadAsync()` but some others call `SendBeforeUnload()` with `should_run_before_unload_asynchronously` to true? What's the difference and in which case should we call which ones? Is it possible to just have one function for async beforeunload?

        Minoru Chikamune

        `should_run_before_unload_asynchronously` is decided only once for one navigation. After the decision, always use SendBeforeUnloadAsync() when `should_run_before_unload_asynchronously` is true.

        Alex Moshchuk

        +1 to Rakina's question here, as I'm also not sure I understand yet why we need both helpers when it seems like SendBeforeUnload can support both modes (since it takes the `should_run_before_unload_asynchronously` flag). Is it because the ack processing in SendBeforeUnloadAsync is sufficiently different that it'll make SendBeforeUnload too complex if we wanted to fold it in there?

        Also, maybe it would be helpful to walk through an example like your test - how would SendBeforeUnload/SendBeforeUnloadAsync end up getting invoked in that case?

        Minoru Chikamune

        I've merged `SendBeforeUnloadAsync` into `SendBeforeUnload` as suggested.

        In the new combined `SendBeforeUnload`, the control flow works as follows:

        1. When `should_run_before_unload_asynchronously` is true, the call to `SendBeforeUnload` (via `CheckOrDispatchBeforeUnloadForFrame`) initiates the asynchronous execution using a specialized callback that notifies the `NavigationRequest` via `MaybeResumeAsyncBeforeUnloadCommit`.
        2. After the dispatch loop (from `ForEachRenderFrameHostImplWithAction` and `CheckOrDispatchBeforeUnloadForFrame`), `CheckOrDispatchBeforeUnloadForSubtree` moves the frame IDs from the synchronous `beforeunload_pending_replies_` to the `NavigationRequest`'s `async_before_unload_pending_replies_`.
        3. Finally, an additional call to `SendBeforeUnload` (at the end of `CheckOrDispatchBeforeUnloadForSubtree`) is made to continue the navigation process. This call uses the standard path (the `for_legacy == true` path) but acknowledges the async state, allowing the navigation to proceed while the background handlers are still running.

        After merging `SendBeforeUnloadAsync` into `SendBeforeUnload`, The `if (navigation_request->get_async_before_unload_pending_replies().empty())` check is required to distinguish the initial async dispatch phase from the subsequent navigation continuation.

        Line 17274, Patchset 82: frame_tree_node_->navigation_request()->GetWeakPtr();
        Alex Moshchuk . unresolved

        Just trying to understand: looks like this is called from CheckOrDispatchBeforeUnloadForFrame for each frame that needs to send a beforeunload IPC to the renderer. Why are we looking up the navigation request from each frame's FTN here? Shouldn't it always be retrieved from the ancestor frame that's navigating and triggering beforeunload handlers in all of its descendants? (I.e., the equivalent of what GetBeforeUnloadInitiator() does.)

        Minoru Chikamune

        In `SendBeforeUnloadAsync`, `this` refers to the RenderFrameHost that initiated the navigation (the root of the navigating subtree), while the `rfh` parameter represents the specific frame (which could be a descendant) that needs to run the `beforeunload` handler.

        Therefore, `frame_tree_node_->navigation_request()` (accessing `this->frame_tree_node_`) correctly retrieves the `NavigationRequest` associated with the navigation root, regardless of which descendant frame is being asked to run its handler. I added a comment to make this relationship clearer.

        Line 17285, Patchset 82: bool proceed, base::TimeTicks renderer_before_unload_start_time,
        base::TimeTicks renderer_before_unload_end_time,
        Alex Moshchuk . unresolved

        Are we going to need to process these for metrics, similarly how they're handled on the old path?

        On that note, how are the NavigationRequest::MaybeRecordTraceEventsAndHistograms() trace events and metrics affected by AsyncBeforeUnload? Will they need to be updated, and if so, will that happen here or in a separate CL?

        Minoru Chikamune

        Are we going to need to process these for metrics, similarly how they're handled on the old path?

        I thought these TimeTicks are no longer important because they are no longer on the critical path.

        On that note, how are the NavigationRequest::MaybeRecordTraceEventsAndHistograms() trace events and metrics affected by AsyncBeforeUnload? Will they need to be updated, and if so, will that happen here or in a separate CL?

        I thought MaybeRecordTraceEventsAndHistograms does not need to be updated because running beforeunload does not block navigation anymore.

        File content/browser/renderer_host/render_frame_host_impl_browsertest.cc
        Line 8171, Patchset 82: NotifyUserActivation(rfh_a);
        Alex Moshchuk . unresolved

        This test is named *WithoutUserActivation, so I'm confused why we're granting user activation to `rfh_a` here? Why doesn't it cause async beforeunload to be bypassed regardless of AsyncBeforeUnload feature? (More comments to explain the expected flow of the test would be great.)

        Minoru Chikamune

        I'm sorry. The test name is misleading. Renamed.

        File content/common/features.cc
        Line 44, Patchset 80: base::Milliseconds(500));
        Charlie Reis . unresolved

        Related to my earlier question about the separate timeout, why would we need a different timeout value if we did use one? That's probably worth documenting if we need to keep this.

        Minoru Chikamune

        Ditto. Currently, I don't have a clear answer, but I agree that it's better to use one timer instead of two timers. I'll check if I can use just one timer.

        Alex Moshchuk

        I'd expect that by the time we get to WillCommitNavigation, the beforeunload handlers have been running for some time (since the start of navigation). So I wonder if the timer in AsyncBeforeUnloadCommitDeferringCondition::WillCommitNavigation should be set to the remaining time, so that beforeunload handlers still get 500ms total?

        Minoru Chikamune

        still get 500ms total

        Yeah, I agree. I'll address it.

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alex Moshchuk
        • Charlie Reis
        • Rakina Zata Amni
        Submit Requirements:
        • requirement satisfiedCode-Coverage
        • requirement is not satisfiedCode-Owners
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: chromium/src
        Gerrit-Branch: main
        Gerrit-Change-Id: I7b19ffb64190f94f5e51aa93225c05a2aba0404a
        Gerrit-Change-Number: 7413835
        Gerrit-PatchSet: 84
        Gerrit-Owner: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Reviewer: Charlie Reis <cr...@chromium.org>
        Gerrit-Reviewer: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-CC: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
        Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
        Gerrit-CC: Nate Chapin <jap...@chromium.org>
        Gerrit-Attention: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Attention: Charlie Reis <cr...@chromium.org>
        Gerrit-Attention: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-Comment-Date: Thu, 12 Feb 2026 09:48:43 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: Yes
        Comment-In-Reply-To: Alex Moshchuk <ale...@chromium.org>
        Comment-In-Reply-To: Minoru Chikamune <chik...@chromium.org>
        Comment-In-Reply-To: Charlie Reis <cr...@chromium.org>
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Minoru Chikamune (Gerrit)

        unread,
        Feb 12, 2026, 5:50:37 AMFeb 12
        to Minoru Chikamune, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Charlie Reis and Rakina Zata Amni

        Minoru Chikamune added 1 comment

        File content/common/features.cc
        Line 44, Patchset 80: base::Milliseconds(500));
        Charlie Reis . unresolved

        Related to my earlier question about the separate timeout, why would we need a different timeout value if we did use one? That's probably worth documenting if we need to keep this.

        Minoru Chikamune

        Ditto. Currently, I don't have a clear answer, but I agree that it's better to use one timer instead of two timers. I'll check if I can use just one timer.

        Alex Moshchuk

        I'd expect that by the time we get to WillCommitNavigation, the beforeunload handlers have been running for some time (since the start of navigation). So I wonder if the timer in AsyncBeforeUnloadCommitDeferringCondition::WillCommitNavigation should be set to the remaining time, so that beforeunload handlers still get 500ms total?

        Minoru Chikamune

        still get 500ms total

        Yeah, I agree. I'll address it.

        Minoru Chikamune

        I ended up to add `navigation_request->StartAsyncBeforeUnloadTimer()` to start the timer when the beforeunload handlers are started.

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alex Moshchuk
        • Charlie Reis
        • Rakina Zata Amni
        Submit Requirements:
        • requirement satisfiedCode-Coverage
        • requirement is not satisfiedCode-Owners
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: chromium/src
        Gerrit-Branch: main
        Gerrit-Change-Id: I7b19ffb64190f94f5e51aa93225c05a2aba0404a
        Gerrit-Change-Number: 7413835
        Gerrit-PatchSet: 86
        Gerrit-Owner: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Reviewer: Charlie Reis <cr...@chromium.org>
        Gerrit-Reviewer: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-CC: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
        Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
        Gerrit-CC: Nate Chapin <jap...@chromium.org>
        Gerrit-Attention: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Attention: Charlie Reis <cr...@chromium.org>
        Gerrit-Attention: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-Comment-Date: Thu, 12 Feb 2026 10:50:11 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Minoru Chikamune (Gerrit)

        unread,
        Feb 12, 2026, 8:11:56 AMFeb 12
        to Minoru Chikamune, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Charlie Reis and Rakina Zata Amni

        Minoru Chikamune added 1 comment

        Patchset-level comments
        File-level comment, Patchset 87 (Latest):
        Minoru Chikamune . resolved

        I think I addressed most of the review comments. PTAL.

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alex Moshchuk
        • Charlie Reis
        • Rakina Zata Amni
        Submit Requirements:
        • requirement satisfiedCode-Coverage
        • requirement is not satisfiedCode-Owners
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: chromium/src
        Gerrit-Branch: main
        Gerrit-Change-Id: I7b19ffb64190f94f5e51aa93225c05a2aba0404a
        Gerrit-Change-Number: 7413835
        Gerrit-PatchSet: 87
        Gerrit-Owner: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Reviewer: Charlie Reis <cr...@chromium.org>
        Gerrit-Reviewer: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-CC: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
        Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
        Gerrit-CC: Nate Chapin <jap...@chromium.org>
        Gerrit-Attention: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Attention: Charlie Reis <cr...@chromium.org>
        Gerrit-Attention: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-Comment-Date: Thu, 12 Feb 2026 13:11:33 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Charlie Reis (Gerrit)

        unread,
        Feb 12, 2026, 7:35:41 PMFeb 12
        to Minoru Chikamune, AI Code Reviewer, Rakina Zata Amni, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Charlie Reis and Rakina Zata Amni

        Charlie Reis added 1 comment

        Patchset-level comments
        Minoru Chikamune . resolved

        I think I addressed most of the review comments. PTAL.

        Charlie Reis

        Thanks! I'm starting to take another look (as Alex is OOO), and I'll try to send comments by tomorrow.

        Gerrit-Comment-Date: Fri, 13 Feb 2026 00:35:30 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        Comment-In-Reply-To: Minoru Chikamune <chik...@chromium.org>
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Minoru Chikamune (Gerrit)

        unread,
        Feb 13, 2026, 12:34:03 AMFeb 13
        to Minoru Chikamune, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Charlie Reis and Rakina Zata Amni

        Minoru Chikamune added 1 comment

        Patchset-level comments
        Minoru Chikamune . resolved

        I think I addressed most of the review comments. PTAL.

        Charlie Reis

        Thanks! I'm starting to take another look (as Alex is OOO), and I'll try to send comments by tomorrow.

        Minoru Chikamune

        Thank you!

        Gerrit-Comment-Date: Fri, 13 Feb 2026 05:33:32 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        Comment-In-Reply-To: Minoru Chikamune <chik...@chromium.org>
        Comment-In-Reply-To: Charlie Reis <cr...@chromium.org>
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Charlie Reis (Gerrit)

        unread,
        Feb 13, 2026, 7:21:44 PMFeb 13
        to Minoru Chikamune, AI Code Reviewer, Rakina Zata Amni, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Minoru Chikamune and Rakina Zata Amni

        Charlie Reis added 7 comments

        Patchset-level comments
        Minoru Chikamune . resolved

        I think I addressed most of the review comments. PTAL.

        Charlie Reis

        Thanks! I'm starting to take another look (as Alex is OOO), and I'll try to send comments by tomorrow.

        Minoru Chikamune

        Thank you!

        Charlie Reis

        Thanks for your patience! I wasn't able to go through the whole algorithm, but I wanted to leave some comments before disappearing. Hopefully Rakina and Alex can continue to help while I'm OOO. Thanks!

        Commit Message
        Line 20, Patchset 87 (Latest):dialogs that users may use to cancel the navigation. If even a single
        frame has been interacted with, the browser reverts to synchronous
        Charlie Reis . unresolved

        with a beforeunload handler

        (Correct? It sounds like it might be ok to proceed if a frame on the page has a user activation but no beforeunload handler?)

        Not sure how easy it is to test a case like this (and the simpler case where a frame with an activation and a handler prevents the optimization), but that might be worth considering, if it's not already in the CL.

        File content/browser/renderer_host/async_before_unload_commit_deferring_condition.h
        Line 22, Patchset 87 (Latest):// chance to complete.
        Charlie Reis . unresolved

        It might be worth adding a bit of reasoning so that we don't try to remove this later without realizing the risks. Something like:

        This prevents the risk that beforeunload handlers may attempt observable actions after another page has committed and the page is in a pending deletion state.

        File content/browser/renderer_host/async_beforeunload_commit_deferring_condition.h
        Line 40, Patchset 80: // too long, preventing the navigation from being hung indefinitely.
        Charlie Reis . unresolved

        Just curious-- do we need a separate timer for this if there's already [one in RFHI](https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/render_frame_host_impl.h;drc=976a71ef2a509d4391d814b52b9f4700fdf35789;l=4756)? If RenderFrameHostImpl::BeforeUnloadTimeout() had a way of telling this condition to proceed, would that avoid the need for a second timer?

        I mainly ask because it seems tricky to think through all the possibilities of which timer fires first, if at all. If there's a good way to think about how they relate to each other, that might be worth putting in the comment here.

        Minoru Chikamune

        Currently, I don't have a clear answer, but I agree that it's better to use one timer instead of two timers. I'll check if I can use just one timer.

        Charlie Reis

        Thanks. I left another comment about this since I see we still have two timers, and I'm not sure how to reason about them.

        File content/browser/renderer_host/navigation_request.h
        Line 3537, Patchset 87 (Latest): // navigation from being hung indefinitely.
        Charlie Reis . unresolved

        As before, can we document how this interacts with the [other beforeunload timer](https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/render_frame_host_impl.cc;drc=97c0a7967c365a7092885334a52c68d1c43efb91;l=11958), if we need to have both? Do we have a guarantee which one will fire first, or do we need to reason about what happens if either one fires first?

        I'm still not sure I understand why we need two timers, especially if they're set around the same time and have the same duration. Maybe that can be made clearer.

        Line 3525, Patchset 87 (Latest): // only the highest ancestor among them is added to this set. This ancestor
        Charlie Reis . unresolved

        Is this a well-defined concept? Normally we refer to local frame roots.

        What would happen in a page like A(B1, B2), where B1 and B2 are siblings in the same SiteInstanceGroup? Would both be in the set? I assume so, based on the mentions of "subtree" and "descendants," and I just got a bit confused by the "multiple frames in the same SiteInstanceGroup/process" phrasing.

        File content/browser/renderer_host/render_frame_host_impl.cc
        Line 17285, Patchset 82: bool proceed, base::TimeTicks renderer_before_unload_start_time,
        base::TimeTicks renderer_before_unload_end_time,
        Alex Moshchuk . unresolved

        Are we going to need to process these for metrics, similarly how they're handled on the old path?

        On that note, how are the NavigationRequest::MaybeRecordTraceEventsAndHistograms() trace events and metrics affected by AsyncBeforeUnload? Will they need to be updated, and if so, will that happen here or in a separate CL?

        Minoru Chikamune

        Are we going to need to process these for metrics, similarly how they're handled on the old path?

        I thought these TimeTicks are no longer important because they are no longer on the critical path.

        On that note, how are the NavigationRequest::MaybeRecordTraceEventsAndHistograms() trace events and metrics affected by AsyncBeforeUnload? Will they need to be updated, and if so, will that happen here or in a separate CL?

        I thought MaybeRecordTraceEventsAndHistograms does not need to be updated because running beforeunload does not block navigation anymore.

        Charlie Reis

        Hmm, we may want to think about this a bit more.

        (1) Beforeunload can still block navigation if the commit deferring condition runs, and it would probably be good to have metrics for how much of an effect that has in practice. Hopefully we don't hit it much.

        (2) The beforeunload handlers are still running and could be shown in a separate (non-nested) trace event / metric if we wanted to. That's maybe less important, but might explain more about how the events overlap in a trace (which would be educational). Maybe a metric wouldn't be required for that, though, unless there's a reason we're curious about it. (It might help explain what portion of the time ended up in the commit deferring condition, for example.)

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alex Moshchuk
        • Minoru Chikamune
        • Rakina Zata Amni
        Gerrit-Attention: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Attention: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-Comment-Date: Sat, 14 Feb 2026 00:21:37 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        Comment-In-Reply-To: Alex Moshchuk <ale...@chromium.org>
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Minoru Chikamune (Gerrit)

        unread,
        Feb 16, 2026, 4:34:24 AMFeb 16
        to Minoru Chikamune, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Charlie Reis and Rakina Zata Amni

        Minoru Chikamune added 11 comments

        Commit Message
        Line 20, Patchset 87:dialogs that users may use to cancel the navigation. If even a single

        frame has been interacted with, the browser reverts to synchronous
        Charlie Reis . unresolved

        with a beforeunload handler

        (Correct? It sounds like it might be ok to proceed if a frame on the page has a user activation but no beforeunload handler?)

        Not sure how easy it is to test a case like this (and the simpler case where a frame with an activation and a handler prevents the optimization), but that might be worth considering, if it's not already in the CL.

        Minoru Chikamune

        with a beforeunload handler

        Yes, correct. I updated the CL description.

        Not sure how easy it is to test a case like this (and the simpler case where a frame with an activation and a handler prevents the optimization), but that might be worth considering, if it's not already in the CL.

        I think the current `RenderFrameHostImplAsyncBeforeUnloadBrowserTest.AsyncExecution` covers the half of it.

        A(B,C):

        • B,C has beforeunload handler, A doesn't.
        • A has an activation, B,C don't.
        • And confirms that the beforeunload handler runs asynchronously.

        I added `RenderFrameHostImplAsyncBeforeUnloadBrowserTest.SyncFallbackWithUserActivation` to confirm the case where this optimization is not triggered.

        A(B,C):

        • B,C has beforeunload handler, A doesn't.
        • A,B has an activation, C doesn't.
        File content/browser/renderer_host/async_before_unload_commit_deferring_condition.h
        Line 22, Patchset 87:// chance to complete.
        Charlie Reis . unresolved

        It might be worth adding a bit of reasoning so that we don't try to remove this later without realizing the risks. Something like:

        This prevents the risk that beforeunload handlers may attempt observable actions after another page has committed and the page is in a pending deletion state.

        Minoru Chikamune

        Thank you! Updated.

        File content/browser/renderer_host/navigation_request.h
        Line 3537, Patchset 87: // navigation from being hung indefinitely.
        Charlie Reis . unresolved

        As before, can we document how this interacts with the [other beforeunload timer](https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/render_frame_host_impl.cc;drc=97c0a7967c365a7092885334a52c68d1c43efb91;l=11958), if we need to have both? Do we have a guarantee which one will fire first, or do we need to reason about what happens if either one fires first?

        I'm still not sure I understand why we need two timers, especially if they're set around the same time and have the same duration. Maybe that can be made clearer.

        Minoru Chikamune

        Ah, sorry. I should have addressed this. I updated the code to explicitly stop RFHI::beforeunload_timeout_ before starting NavigationRequest::async_before_unload_timeout_. I hope this makes it clear which timer is responsible for each phase:

        1. `RFHI::beforeunload_timeout_`: Manages the synchronous phase that blocks the navigation from starting.
        2. `NavigationRequest::async_before_unload_timeout_`: Manages the asynchronous phase where handlers run in the background and only block the navigation from committing.

        By stopping the first timer before starting the second, we ensure they never overlap or race against each other.

        Line 3525, Patchset 87: // only the highest ancestor among them is added to this set. This ancestor
        Charlie Reis . unresolved

        Is this a well-defined concept? Normally we refer to local frame roots.

        What would happen in a page like A(B1, B2), where B1 and B2 are siblings in the same SiteInstanceGroup? Would both be in the set? I assume so, based on the mentions of "subtree" and "descendants," and I just got a bit confused by the "multiple frames in the same SiteInstanceGroup/process" phrasing.

        Minoru Chikamune

        Thanks! The previous code comment was not accurate. I've updated the comment to use "local frame roots".

        Line 3533, Patchset 82: std::set<raw_ptr<RenderFrameHostImpl, SetExperimental>>
        Alex Moshchuk . resolved

        Given the possibility that descendant RFHs might be deleted before we get a beforeunload response from them, I wonder if a safer design (improving on beforeunload_pending_replies_) would be GlobalRenderFrameHostIds that we can resolve if needed? Maybe the RFH destruction issue that Charlie mentioned would be easier to handle with such a design. (Also, I'm not sure if we actually need to resolve and call methods on these RFHs, vs just having a way to track whether each one has finished running beforeunload.)

        Minoru Chikamune

        Oh nice! GlobalRenderFrameHostId is easy way to manage this field. Thank you so much for your advice. I updated the code.

        Minoru Chikamune

        Done

        Line 3526, Patchset 80: // for a timeout to occur.
        Charlie Reis . resolved

        Can you clarify in the comment which frames this might include? Is it "this frame and/or its descendant frames ... [excluding those] that don't have beforeunload handlers defined" like beforeunload_pending_replies_? I guess it's slightly trickier since this is on NavigationRequest instead of RFH, but maybe it still applies to the navigating frame and its descendants?

        Hopefully we're careful about not leaving any dangling pointers in here if descendant frames are removed?

        Minoru Chikamune

        Can you clarify in the comment which frames

        I updated the code comment.

        not leaving any dangling pointers in here if descendant frames are removed?

        Oh, you are right. Probably I need to do something similar to the following logic in the destructor of RFH. I'll update the code.

        ```
        // If another frame is waiting for a beforeunload completion callback from
        // this frame, simulate it now.
        RenderFrameHostImpl* beforeunload_initiator = GetBeforeUnloadInitiator();
        if (beforeunload_initiator && beforeunload_initiator != this) {
        base::TimeTicks approx_renderer_start_time = send_before_unload_start_time_;
        beforeunload_initiator->ProcessBeforeUnloadCompletedFromFrame(
        /*proceed=*/true, /*treat_as_final_completion_callback=*/false, this,
        /*is_frame_being_destroyed=*/true, approx_renderer_start_time,
        base::TimeTicks::Now(), /*for_legacy=*/false);
        }
        ```
        Minoru Chikamune

        Done. I switched to use GlobalRenderFrameHostId, and created `NavigationRequest::MaybeResumeAsyncBeforeUnloadCommit()`, and now the destructor of RFHI calls it, just like the normal beforeunload handling.

        Minoru Chikamune

        Done

        File content/browser/renderer_host/render_frame_host_impl.cc
        Line 11677, Patchset 79: async_beforeunload_pending_replies_.clear();
        Rakina Zata Amni . resolved

        Why is this not in the `is_waiting_for_beforeunload_completion_` if clause?

        Minoru Chikamune

        It's because is_waiting_for_beforeunload_completion_ is already false when the beforeunload is asynchronously running.

        Minoru Chikamune

        In the new patch-set, I moved the async_before_unload_pending_replies from RFH to NavigationRequest. And I removed the previous reset code from RFH.

        Minoru Chikamune

        Done

        Line 17116, Patchset 51: initiator->async_beforeunload_pending_replies_.erase(
        Rakina Zata Amni . resolved

        I wonder if this should be a member of the `NavigationRequest` instead. What happens if multiple navigations happen in the same FrameTree (e.g. a parent and a subframe independently triggered navigation), what does the async beforeunload tracking look like?

        Minoru Chikamune

        Yeah, I wondered the same. I borrowed this idea from the existing `beforeunload_pending_replies_`, which waits for the all replies from the renderer, and it ended up with putting this on the initiator rfh.

        Minoru Chikamune

        In the new patch-set, I moved the async_before_unload_pending_replies from RFH to NavigationRequest. Thank you for pointing this out.

        Probably, the current intention to keep beforeunload_pending_replies_ on RFH was that the beforeunload dialog should be shown once when the user initiate multiple navigations in a short period of time.

        In our case, when `SendBeforeUnloadAsync` code path is used, it does not show dialogs, but perhaps SendBeforeUnloadAsync runs more beforeunload handlers. I'm not super shure if this is ok or not.

        Minoru Chikamune

        Done

        Line 17285, Patchset 82: bool proceed, base::TimeTicks renderer_before_unload_start_time,
        base::TimeTicks renderer_before_unload_end_time,
        Alex Moshchuk . unresolved

        Are we going to need to process these for metrics, similarly how they're handled on the old path?

        On that note, how are the NavigationRequest::MaybeRecordTraceEventsAndHistograms() trace events and metrics affected by AsyncBeforeUnload? Will they need to be updated, and if so, will that happen here or in a separate CL?

        Minoru Chikamune

        Are we going to need to process these for metrics, similarly how they're handled on the old path?

        I thought these TimeTicks are no longer important because they are no longer on the critical path.

        On that note, how are the NavigationRequest::MaybeRecordTraceEventsAndHistograms() trace events and metrics affected by AsyncBeforeUnload? Will they need to be updated, and if so, will that happen here or in a separate CL?

        I thought MaybeRecordTraceEventsAndHistograms does not need to be updated because running beforeunload does not block navigation anymore.

        Charlie Reis

        Hmm, we may want to think about this a bit more.

        (1) Beforeunload can still block navigation if the commit deferring condition runs, and it would probably be good to have metrics for how much of an effect that has in practice. Hopefully we don't hit it much.

        (2) The beforeunload handlers are still running and could be shown in a separate (non-nested) trace event / metric if we wanted to. That's maybe less important, but might explain more about how the events overlap in a trace (which would be educational). Maybe a metric wouldn't be required for that, though, unless there's a reason we're curious about it. (It might help explain what portion of the time ended up in the commit deferring condition, for example.)

        Minoru Chikamune

        (1) Beforeunload can still block navigation if the commit deferring condition runs, and it would probably be good to have metrics for how much of an effect that has in practice. Hopefully we don't hit it much.

        I see. Currently, the `commit deferring condition` duration is included in `ReceiveResponseToCommit`.

        e.g. Tracelog of `RenderFrameHostImplAsyncBeforeUnloadBrowserTest.AsyncBeforeUnload`:
        https://ui.perfetto.dev/#!/?s=97a4c641f9ab5ecd206ddccc3dcea9ce98c6c3b8

        (2) The beforeunload handlers are still running and could be shown in a separate (non-nested) trace event / metric if we wanted to. That's maybe less important, but might explain more about how the events overlap in a trace (which would be educational). Maybe a metric wouldn't be required for that, though, unless there's a reason we're curious about it. (It might help explain what portion of the time ended up in the commit deferring condition, for example.)

        OK, I'll add a non-nested trace event in a follow-up CL.

        File content/browser/renderer_host/render_frame_host_impl_browsertest.cc
        Line 8171, Patchset 82: NotifyUserActivation(rfh_a);
        Alex Moshchuk . resolved

        This test is named *WithoutUserActivation, so I'm confused why we're granting user activation to `rfh_a` here? Why doesn't it cause async beforeunload to be bypassed regardless of AsyncBeforeUnload feature? (More comments to explain the expected flow of the test would be great.)

        Minoru Chikamune

        I'm sorry. The test name is misleading. Renamed.

        Minoru Chikamune

        Done

        Alex Moshchuk . unresolved

        We'll probably want at least a few other tests eventually, e.g. for renderer-initiated navigations, for ensuring that tab close doesn't trigger the optimization, for ensuring that user activation in one of the frames turns off the optimization, etc.

        Minoru Chikamune

        Sure. I added several tests, but I failed to add a test for ensuring that tab close doesn't trigger the optimization. TabClose seems tricky...

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alex Moshchuk
        • Charlie Reis
        • Rakina Zata Amni
        Submit Requirements:
        • requirement satisfiedCode-Coverage
        • requirement is not satisfiedCode-Owners
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: chromium/src
        Gerrit-Branch: main
        Gerrit-Change-Id: I7b19ffb64190f94f5e51aa93225c05a2aba0404a
        Gerrit-Change-Number: 7413835
        Gerrit-PatchSet: 88
        Gerrit-Owner: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Reviewer: Charlie Reis <cr...@chromium.org>
        Gerrit-Reviewer: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-CC: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
        Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
        Gerrit-CC: Nate Chapin <jap...@chromium.org>
        Gerrit-Attention: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Attention: Charlie Reis <cr...@chromium.org>
        Gerrit-Attention: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-Comment-Date: Mon, 16 Feb 2026 09:33:50 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        Comment-In-Reply-To: Alex Moshchuk <ale...@chromium.org>
        Comment-In-Reply-To: Minoru Chikamune <chik...@chromium.org>
        Comment-In-Reply-To: Charlie Reis <cr...@chromium.org>
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Minoru Chikamune (Gerrit)

        unread,
        Feb 17, 2026, 3:16:17 AMFeb 17
        to Minoru Chikamune, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Charlie Reis and Rakina Zata Amni

        Minoru Chikamune added 1 comment

        File content/browser/renderer_host/render_frame_host_impl_browsertest.cc
        Alex Moshchuk . unresolved

        We'll probably want at least a few other tests eventually, e.g. for renderer-initiated navigations, for ensuring that tab close doesn't trigger the optimization, for ensuring that user activation in one of the frames turns off the optimization, etc.

        Minoru Chikamune

        Sure. I added several tests, but I failed to add a test for ensuring that tab close doesn't trigger the optimization. TabClose seems tricky...

        Minoru Chikamune

        Now I added `TabCloseDoesNotUseOptimization` test. PTAL.

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alex Moshchuk
        • Charlie Reis
        • Rakina Zata Amni
        Submit Requirements:
        • requirement satisfiedCode-Coverage
        • requirement is not satisfiedCode-Owners
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: chromium/src
        Gerrit-Branch: main
        Gerrit-Change-Id: I7b19ffb64190f94f5e51aa93225c05a2aba0404a
        Gerrit-Change-Number: 7413835
        Gerrit-PatchSet: 91
        Gerrit-Owner: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Reviewer: Charlie Reis <cr...@chromium.org>
        Gerrit-Reviewer: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-CC: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
        Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
        Gerrit-CC: Nate Chapin <jap...@chromium.org>
        Gerrit-Attention: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Attention: Charlie Reis <cr...@chromium.org>
        Gerrit-Attention: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-Comment-Date: Tue, 17 Feb 2026 08:15:43 +0000
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Rakina Zata Amni (Gerrit)

        unread,
        Feb 17, 2026, 9:15:04 PMFeb 17
        to Minoru Chikamune, AI Code Reviewer, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Charlie Reis and Minoru Chikamune

        Rakina Zata Amni added 4 comments

        Rakina Zata Amni . resolved

        (thanks for the patience, I finally got through most of the beforeunload flow and starting to understand what's going on)

        File content/browser/renderer_host/async_beforeunload_commit_deferring_condition.cc
        Line 62, Patchset 79: // proceed navigation if running the previous page's beforeunload takes too

        // long. (e.g. If the previous page's beforeunload handler has an
        Rakina Zata Amni . resolved

        Out of curiosity, do we have something like this for the sync version as well? What is the timeout value for that?

        Minoru Chikamune

        `RenderFrameHostImpl::beforeunload_timeout_delay_` is doing it. The timeout value is 500ms. `UnloadTest.CrossSiteInfiniteBeforeUnloadAsync` tests it.


        ```
        constexpr base::TimeDelta kUnloadTimeout = base::Milliseconds(500);
        ```

        Rakina Zata Amni

        Acknowledged

        File content/browser/renderer_host/render_frame_host_impl.cc
        Line 12032, Patchset 79: // `this` into `beforeunload_pending_replies_` as a placeholder, letting the
        Rakina Zata Amni . unresolved

        Why do we need this placeholder? Also why don't we just put things directly in `async_beforeunload_pending_replies_` instead of swapping here?

        Minoru Chikamune

        Why do we need this placeholder?

        This is the same behavior as L12018: `beforeunload_pending_replies_.insert(this);`. It adds the frame (navigation root) as a "placeholder", and `RenderFrameHostImpl::ProcessBeforeUnloadCompletedFromFrame()` consumes it (`beforeunload_pending_replies_.erase(frame);`).

        Also why don't we just put things directly in async_beforeunload_pending_replies_ instead of swapping here?

        `RenderFrameHostImpl::CheckOrDispatchBeforeUnloadForFrame()` algorithm relies on `beforeunload_pending_replies_` not to send duplicated request. So, `beforeunload_pending_replies_` should be kept as is until the `ForEachRenderFrameHostImplWithAction` ends.

        Initially, I didn't understand it, and actually, the beforeunload handlers run twice before in the test.

        Rakina Zata Amni

        Ah ok, hmm we do erase it from `ProcessBeforeUnloadCompletedFromFrame()` but does anything prevent us from just not adding anything at all? We don't actually check whether the RFH is in the list when erasing. But maybe it's not important, it's either confusing here or confusing there.

        I'm wondering if we can just combine `beforeunload_pending_replies_` and `async_beforeunload_pending_replies_` by just adding a bool differentiating the type (async/sync) there so we can not do this swapping and placeholder and ensuring every time we want to clear `beforeunload_pending_replies_` the async ones get cleared as well.

        But I guess you moved it to NavigationRequest because of my [comment](https://chromium-review.googlesource.com/c/chromium/src/+/7413835/comment/3c83d833_4b258612/) about the beforeunload being tied to the navigation instead of the RFH, which I kinda feel it should be. But having `async_beforeunload_pending_replies_` in NavigationRequest means we can trigger beforeunload multiple times for different navigations happening in different frames at the same time, but the sync version will only trigger the beforeunload once. We can't really move `beforeunload_pending_replies_` to NavigationRequest because that can also be used for frame deletion. Maybe it's fine to keep `async_beforeunload_pending_replies_` in RFH too to not differentiate the sync vs async behavior further? WDYT? I think I need @ale...@chromium.org's opinion on this too since he's more familiar with beforeunload :)

        Line 12154, Patchset 79: SendBeforeUnloadAsync(is_reload, rfh->GetWeakPtr());
        Rakina Zata Amni . unresolved

        Why do some callers call `SendBeforeUnloadAsync()` but some others call `SendBeforeUnload()` with `should_run_before_unload_asynchronously` to true? What's the difference and in which case should we call which ones? Is it possible to just have one function for async beforeunload?

        Minoru Chikamune

        `should_run_before_unload_asynchronously` is decided only once for one navigation. After the decision, always use SendBeforeUnloadAsync() when `should_run_before_unload_asynchronously` is true.

        Alex Moshchuk

        +1 to Rakina's question here, as I'm also not sure I understand yet why we need both helpers when it seems like SendBeforeUnload can support both modes (since it takes the `should_run_before_unload_asynchronously` flag). Is it because the ack processing in SendBeforeUnloadAsync is sufficiently different that it'll make SendBeforeUnload too complex if we wanted to fold it in there?

        Also, maybe it would be helpful to walk through an example like your test - how would SendBeforeUnload/SendBeforeUnloadAsync end up getting invoked in that case?

        Minoru Chikamune

        I've merged `SendBeforeUnloadAsync` into `SendBeforeUnload` as suggested.

        In the new combined `SendBeforeUnload`, the control flow works as follows:

        1. When `should_run_before_unload_asynchronously` is true, the call to `SendBeforeUnload` (via `CheckOrDispatchBeforeUnloadForFrame`) initiates the asynchronous execution using a specialized callback that notifies the `NavigationRequest` via `MaybeResumeAsyncBeforeUnloadCommit`.
        2. After the dispatch loop (from `ForEachRenderFrameHostImplWithAction` and `CheckOrDispatchBeforeUnloadForFrame`), `CheckOrDispatchBeforeUnloadForSubtree` moves the frame IDs from the synchronous `beforeunload_pending_replies_` to the `NavigationRequest`'s `async_before_unload_pending_replies_`.
        3. Finally, an additional call to `SendBeforeUnload` (at the end of `CheckOrDispatchBeforeUnloadForSubtree`) is made to continue the navigation process. This call uses the standard path (the `for_legacy == true` path) but acknowledges the async state, allowing the navigation to proceed while the background handlers are still running.

        After merging `SendBeforeUnloadAsync` into `SendBeforeUnload`, The `if (navigation_request->get_async_before_unload_pending_replies().empty())` check is required to distinguish the initial async dispatch phase from the subsequent navigation continuation.

        Rakina Zata Amni

        Thank you for the explanation! I think the hard to comprehend part is the part to continue the navigation (the if clause for `for_legacy` + now `should_run_before_unload_asynchronously`) that doesn't actually send anything to the renderer and just wants to continue the navigation (synchronously or asynchronously). That part doesn't matter at all for cases where we actually want to run the beforeunload in the renderer, and only matters for the additional call outside of the iteration that you mentioned.

        Would it be possible to move that part to the caller of `SendBeforeUnload` that's outside of the iteration? We need the `before_unload_closure` but maybe we can just make that a member function? Then you also don't need the `get_async_before_unload_pending_replies()` check to differentiate the iteration vs the extra call?

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alex Moshchuk
        • Charlie Reis
        • Minoru Chikamune
        Submit Requirements:
        • requirement satisfiedCode-Coverage
        • requirement is not satisfiedCode-Owners
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: chromium/src
        Gerrit-Branch: main
        Gerrit-Change-Id: I7b19ffb64190f94f5e51aa93225c05a2aba0404a
        Gerrit-Change-Number: 7413835
        Gerrit-PatchSet: 92
        Gerrit-Owner: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Reviewer: Charlie Reis <cr...@chromium.org>
        Gerrit-Reviewer: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-CC: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
        Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
        Gerrit-CC: Nate Chapin <jap...@chromium.org>
        Gerrit-Attention: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Attention: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Attention: Charlie Reis <cr...@chromium.org>
        Gerrit-Comment-Date: Wed, 18 Feb 2026 02:14:40 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        Comment-In-Reply-To: Alex Moshchuk <ale...@chromium.org>
        Comment-In-Reply-To: Minoru Chikamune <chik...@chromium.org>
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Minoru Chikamune (Gerrit)

        unread,
        Feb 23, 2026, 7:07:16 PMFeb 23
        to Minoru Chikamune, Code Review Nudger, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk and Charlie Reis

        Minoru Chikamune added 3 comments

        File content/browser/renderer_host/async_before_unload_commit_deferring_condition.h
        Line 22, Patchset 87:// chance to complete.
        Charlie Reis . resolved

        It might be worth adding a bit of reasoning so that we don't try to remove this later without realizing the risks. Something like:

        This prevents the risk that beforeunload handlers may attempt observable actions after another page has committed and the page is in a pending deletion state.

        Minoru Chikamune

        Thank you! Updated.

        Minoru Chikamune

        Done

        File content/browser/renderer_host/navigation_request.h
        Line 3525, Patchset 87: // only the highest ancestor among them is added to this set. This ancestor
        Charlie Reis . resolved

        Is this a well-defined concept? Normally we refer to local frame roots.

        What would happen in a page like A(B1, B2), where B1 and B2 are siblings in the same SiteInstanceGroup? Would both be in the set? I assume so, based on the mentions of "subtree" and "descendants," and I just got a bit confused by the "multiple frames in the same SiteInstanceGroup/process" phrasing.

        Minoru Chikamune

        Thanks! The previous code comment was not accurate. I've updated the comment to use "local frame roots".

        Minoru Chikamune

        Done

        File content/common/features.cc
        Line 44, Patchset 80: base::Milliseconds(500));
        Charlie Reis . resolved

        Related to my earlier question about the separate timeout, why would we need a different timeout value if we did use one? That's probably worth documenting if we need to keep this.

        Minoru Chikamune

        Ditto. Currently, I don't have a clear answer, but I agree that it's better to use one timer instead of two timers. I'll check if I can use just one timer.

        Alex Moshchuk

        I'd expect that by the time we get to WillCommitNavigation, the beforeunload handlers have been running for some time (since the start of navigation). So I wonder if the timer in AsyncBeforeUnloadCommitDeferringCondition::WillCommitNavigation should be set to the remaining time, so that beforeunload handlers still get 500ms total?

        Minoru Chikamune

        still get 500ms total

        Yeah, I agree. I'll address it.

        Minoru Chikamune

        I ended up to add `navigation_request->StartAsyncBeforeUnloadTimer()` to start the timer when the beforeunload handlers are started.

        Minoru Chikamune

        Done

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alex Moshchuk
        • Charlie Reis
        Submit Requirements:
        • requirement satisfiedCode-Coverage
        • requirement is not satisfiedCode-Owners
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: chromium/src
        Gerrit-Branch: main
        Gerrit-Change-Id: I7b19ffb64190f94f5e51aa93225c05a2aba0404a
        Gerrit-Change-Number: 7413835
        Gerrit-PatchSet: 95
        Gerrit-Owner: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Reviewer: Charlie Reis <cr...@chromium.org>
        Gerrit-Reviewer: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-CC: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
        Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
        Gerrit-CC: Code Review Nudger <android-build...@prod.google.com>
        Gerrit-CC: Nate Chapin <jap...@chromium.org>
        Gerrit-Attention: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Attention: Charlie Reis <cr...@chromium.org>
        Gerrit-Comment-Date: Tue, 24 Feb 2026 00:06:52 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        Comment-In-Reply-To: Alex Moshchuk <ale...@chromium.org>
        Comment-In-Reply-To: Minoru Chikamune <chik...@chromium.org>
        Comment-In-Reply-To: Charlie Reis <cr...@chromium.org>
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Minoru Chikamune (Gerrit)

        unread,
        Feb 24, 2026, 12:42:57 AMFeb 24
        to Minoru Chikamune, Code Review Nudger, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Charlie Reis and Rakina Zata Amni

        Minoru Chikamune added 4 comments

        File content/browser/renderer_host/async_beforeunload_commit_deferring_condition.h
        Line 40, Patchset 80: // too long, preventing the navigation from being hung indefinitely.
        Charlie Reis . unresolved

        Just curious-- do we need a separate timer for this if there's already [one in RFHI](https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/render_frame_host_impl.h;drc=976a71ef2a509d4391d814b52b9f4700fdf35789;l=4756)? If RenderFrameHostImpl::BeforeUnloadTimeout() had a way of telling this condition to proceed, would that avoid the need for a second timer?

        I mainly ask because it seems tricky to think through all the possibilities of which timer fires first, if at all. If there's a good way to think about how they relate to each other, that might be worth putting in the comment here.

        Minoru Chikamune

        Currently, I don't have a clear answer, but I agree that it's better to use one timer instead of two timers. I'll check if I can use just one timer.

        Charlie Reis

        Thanks. I left another comment about this since I see we still have two timers, and I'm not sure how to reason about them.

        Minoru Chikamune

        I ended up using a separate timer, but I stopped the timer (RenderFrameHostImpl::beforeunload_timeout_) when we run AsyncBeforeUnload path to avoid having two timers running.

        File content/browser/renderer_host/render_frame_host_impl.cc
        Line 12032, Patchset 79: // `this` into `beforeunload_pending_replies_` as a placeholder, letting the
        Rakina Zata Amni . unresolved

        Why do we need this placeholder? Also why don't we just put things directly in `async_beforeunload_pending_replies_` instead of swapping here?

        Minoru Chikamune

        Why do we need this placeholder?

        This is the same behavior as L12018: `beforeunload_pending_replies_.insert(this);`. It adds the frame (navigation root) as a "placeholder", and `RenderFrameHostImpl::ProcessBeforeUnloadCompletedFromFrame()` consumes it (`beforeunload_pending_replies_.erase(frame);`).

        Also why don't we just put things directly in async_beforeunload_pending_replies_ instead of swapping here?

        `RenderFrameHostImpl::CheckOrDispatchBeforeUnloadForFrame()` algorithm relies on `beforeunload_pending_replies_` not to send duplicated request. So, `beforeunload_pending_replies_` should be kept as is until the `ForEachRenderFrameHostImplWithAction` ends.

        Initially, I didn't understand it, and actually, the beforeunload handlers run twice before in the test.

        Rakina Zata Amni

        Ah ok, hmm we do erase it from `ProcessBeforeUnloadCompletedFromFrame()` but does anything prevent us from just not adding anything at all? We don't actually check whether the RFH is in the list when erasing. But maybe it's not important, it's either confusing here or confusing there.

        I'm wondering if we can just combine `beforeunload_pending_replies_` and `async_beforeunload_pending_replies_` by just adding a bool differentiating the type (async/sync) there so we can not do this swapping and placeholder and ensuring every time we want to clear `beforeunload_pending_replies_` the async ones get cleared as well.

        But I guess you moved it to NavigationRequest because of my [comment](https://chromium-review.googlesource.com/c/chromium/src/+/7413835/comment/3c83d833_4b258612/) about the beforeunload being tied to the navigation instead of the RFH, which I kinda feel it should be. But having `async_beforeunload_pending_replies_` in NavigationRequest means we can trigger beforeunload multiple times for different navigations happening in different frames at the same time, but the sync version will only trigger the beforeunload once. We can't really move `beforeunload_pending_replies_` to NavigationRequest because that can also be used for frame deletion. Maybe it's fine to keep `async_beforeunload_pending_replies_` in RFH too to not differentiate the sync vs async behavior further? WDYT? I think I need @ale...@chromium.org's opinion on this too since he's more familiar with beforeunload :)

        Minoru Chikamune

        Thank you for your comment. This is important decision. I'll wait for alexmos@'s opinion.

        Line 12154, Patchset 79: SendBeforeUnloadAsync(is_reload, rfh->GetWeakPtr());
        Rakina Zata Amni . unresolved

        Why do some callers call `SendBeforeUnloadAsync()` but some others call `SendBeforeUnload()` with `should_run_before_unload_asynchronously` to true? What's the difference and in which case should we call which ones? Is it possible to just have one function for async beforeunload?

        Minoru Chikamune

        `should_run_before_unload_asynchronously` is decided only once for one navigation. After the decision, always use SendBeforeUnloadAsync() when `should_run_before_unload_asynchronously` is true.

        Alex Moshchuk

        +1 to Rakina's question here, as I'm also not sure I understand yet why we need both helpers when it seems like SendBeforeUnload can support both modes (since it takes the `should_run_before_unload_asynchronously` flag). Is it because the ack processing in SendBeforeUnloadAsync is sufficiently different that it'll make SendBeforeUnload too complex if we wanted to fold it in there?

        Also, maybe it would be helpful to walk through an example like your test - how would SendBeforeUnload/SendBeforeUnloadAsync end up getting invoked in that case?

        Minoru Chikamune

        I've merged `SendBeforeUnloadAsync` into `SendBeforeUnload` as suggested.

        In the new combined `SendBeforeUnload`, the control flow works as follows:

        1. When `should_run_before_unload_asynchronously` is true, the call to `SendBeforeUnload` (via `CheckOrDispatchBeforeUnloadForFrame`) initiates the asynchronous execution using a specialized callback that notifies the `NavigationRequest` via `MaybeResumeAsyncBeforeUnloadCommit`.
        2. After the dispatch loop (from `ForEachRenderFrameHostImplWithAction` and `CheckOrDispatchBeforeUnloadForFrame`), `CheckOrDispatchBeforeUnloadForSubtree` moves the frame IDs from the synchronous `beforeunload_pending_replies_` to the `NavigationRequest`'s `async_before_unload_pending_replies_`.
        3. Finally, an additional call to `SendBeforeUnload` (at the end of `CheckOrDispatchBeforeUnloadForSubtree`) is made to continue the navigation process. This call uses the standard path (the `for_legacy == true` path) but acknowledges the async state, allowing the navigation to proceed while the background handlers are still running.

        After merging `SendBeforeUnloadAsync` into `SendBeforeUnload`, The `if (navigation_request->get_async_before_unload_pending_replies().empty())` check is required to distinguish the initial async dispatch phase from the subsequent navigation continuation.

        Rakina Zata Amni

        Thank you for the explanation! I think the hard to comprehend part is the part to continue the navigation (the if clause for `for_legacy` + now `should_run_before_unload_asynchronously`) that doesn't actually send anything to the renderer and just wants to continue the navigation (synchronously or asynchronously). That part doesn't matter at all for cases where we actually want to run the beforeunload in the renderer, and only matters for the additional call outside of the iteration that you mentioned.

        Would it be possible to move that part to the caller of `SendBeforeUnload` that's outside of the iteration? We need the `before_unload_closure` but maybe we can just make that a member function? Then you also don't need the `get_async_before_unload_pending_replies()` check to differentiate the iteration vs the extra call?

        Minoru Chikamune

        Sorry for my late reply; I somehow missed your comment.

        Would it be possible to move that part to the caller of SendBeforeUnload

        That makes sense. I'll update the patch to reflect this.

        File content/browser/renderer_host/render_frame_host_impl_browsertest.cc
        Line 8206, Patchset 82:
        Alex Moshchuk . resolved

        We'll probably want at least a few other tests eventually, e.g. for renderer-initiated navigations, for ensuring that tab close doesn't trigger the optimization, for ensuring that user activation in one of the frames turns off the optimization, etc.

        Minoru Chikamune

        Sure. I added several tests, but I failed to add a test for ensuring that tab close doesn't trigger the optimization. TabClose seems tricky...

        Minoru Chikamune

        Now I added `TabCloseDoesNotUseOptimization` test. PTAL.

        Minoru Chikamune

        Done

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alex Moshchuk
        • Charlie Reis
        • Rakina Zata Amni
        Submit Requirements:
        • requirement satisfiedCode-Coverage
        • requirement is not satisfiedCode-Owners
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: chromium/src
        Gerrit-Branch: main
        Gerrit-Change-Id: I7b19ffb64190f94f5e51aa93225c05a2aba0404a
        Gerrit-Change-Number: 7413835
        Gerrit-PatchSet: 96
        Gerrit-Owner: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Reviewer: Charlie Reis <cr...@chromium.org>
        Gerrit-Reviewer: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-CC: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
        Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
        Gerrit-CC: Code Review Nudger <android-build...@prod.google.com>
        Gerrit-CC: Nate Chapin <jap...@chromium.org>
        Gerrit-Attention: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Attention: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-Attention: Charlie Reis <cr...@chromium.org>
        Gerrit-Comment-Date: Tue, 24 Feb 2026 05:42:25 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        Comment-In-Reply-To: Alex Moshchuk <ale...@chromium.org>
        Comment-In-Reply-To: Minoru Chikamune <chik...@chromium.org>
        Comment-In-Reply-To: Charlie Reis <cr...@chromium.org>
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Minoru Chikamune (Gerrit)

        unread,
        Feb 26, 2026, 6:05:50 AMFeb 26
        to Minoru Chikamune, Code Review Nudger, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, asvitki...@chromium.org, chromiumme...@microsoft.com, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Charlie Reis and Rakina Zata Amni

        Minoru Chikamune added 1 comment

        File content/browser/renderer_host/render_frame_host_impl.cc
        Line 12154, Patchset 79: SendBeforeUnloadAsync(is_reload, rfh->GetWeakPtr());
        Rakina Zata Amni . unresolved

        Why do some callers call `SendBeforeUnloadAsync()` but some others call `SendBeforeUnload()` with `should_run_before_unload_asynchronously` to true? What's the difference and in which case should we call which ones? Is it possible to just have one function for async beforeunload?

        Minoru Chikamune

        `should_run_before_unload_asynchronously` is decided only once for one navigation. After the decision, always use SendBeforeUnloadAsync() when `should_run_before_unload_asynchronously` is true.

        Alex Moshchuk

        +1 to Rakina's question here, as I'm also not sure I understand yet why we need both helpers when it seems like SendBeforeUnload can support both modes (since it takes the `should_run_before_unload_asynchronously` flag). Is it because the ack processing in SendBeforeUnloadAsync is sufficiently different that it'll make SendBeforeUnload too complex if we wanted to fold it in there?

        Also, maybe it would be helpful to walk through an example like your test - how would SendBeforeUnload/SendBeforeUnloadAsync end up getting invoked in that case?

        Minoru Chikamune

        I've merged `SendBeforeUnloadAsync` into `SendBeforeUnload` as suggested.

        In the new combined `SendBeforeUnload`, the control flow works as follows:

        1. When `should_run_before_unload_asynchronously` is true, the call to `SendBeforeUnload` (via `CheckOrDispatchBeforeUnloadForFrame`) initiates the asynchronous execution using a specialized callback that notifies the `NavigationRequest` via `MaybeResumeAsyncBeforeUnloadCommit`.
        2. After the dispatch loop (from `ForEachRenderFrameHostImplWithAction` and `CheckOrDispatchBeforeUnloadForFrame`), `CheckOrDispatchBeforeUnloadForSubtree` moves the frame IDs from the synchronous `beforeunload_pending_replies_` to the `NavigationRequest`'s `async_before_unload_pending_replies_`.
        3. Finally, an additional call to `SendBeforeUnload` (at the end of `CheckOrDispatchBeforeUnloadForSubtree`) is made to continue the navigation process. This call uses the standard path (the `for_legacy == true` path) but acknowledges the async state, allowing the navigation to proceed while the background handlers are still running.

        After merging `SendBeforeUnloadAsync` into `SendBeforeUnload`, The `if (navigation_request->get_async_before_unload_pending_replies().empty())` check is required to distinguish the initial async dispatch phase from the subsequent navigation continuation.

        Rakina Zata Amni

        Thank you for the explanation! I think the hard to comprehend part is the part to continue the navigation (the if clause for `for_legacy` + now `should_run_before_unload_asynchronously`) that doesn't actually send anything to the renderer and just wants to continue the navigation (synchronously or asynchronously). That part doesn't matter at all for cases where we actually want to run the beforeunload in the renderer, and only matters for the additional call outside of the iteration that you mentioned.

        Would it be possible to move that part to the caller of `SendBeforeUnload` that's outside of the iteration? We need the `before_unload_closure` but maybe we can just make that a member function? Then you also don't need the `get_async_before_unload_pending_replies()` check to differentiate the iteration vs the extra call?

        Minoru Chikamune

        Sorry for my late reply; I somehow missed your comment.

        Would it be possible to move that part to the caller of SendBeforeUnload

        That makes sense. I'll update the patch to reflect this.

        Minoru Chikamune

        I splitted SendBeforeUnload into SendBeforeUnload and ContinueNavigationAfterBeforeUnloadCheck. After that, I also tried to remove the code to run ContinueNavigationAfterBeforeUnloadCheck in SendBeforeUnload, but it requires test updates. I'm currently working on fixing tests.

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alex Moshchuk
        • Charlie Reis
        • Rakina Zata Amni
        Submit Requirements:
        • requirement satisfiedCode-Coverage
        • requirement is not satisfiedCode-Owners
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: chromium/src
        Gerrit-Branch: main
        Gerrit-Change-Id: I7b19ffb64190f94f5e51aa93225c05a2aba0404a
        Gerrit-Change-Number: 7413835
        Gerrit-PatchSet: 103
        Gerrit-Owner: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Reviewer: Charlie Reis <cr...@chromium.org>
        Gerrit-Reviewer: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-CC: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
        Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
        Gerrit-CC: Code Review Nudger <android-build...@prod.google.com>
        Gerrit-CC: Nate Chapin <jap...@chromium.org>
        Gerrit-Attention: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Attention: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-Attention: Charlie Reis <cr...@chromium.org>
        Gerrit-Comment-Date: Thu, 26 Feb 2026 11:05:20 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        Comment-In-Reply-To: Alex Moshchuk <ale...@chromium.org>
        Comment-In-Reply-To: Minoru Chikamune <chik...@chromium.org>
        Comment-In-Reply-To: Rakina Zata Amni <rak...@chromium.org>
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Minoru Chikamune (Gerrit)

        unread,
        Feb 26, 2026, 8:25:25 AMFeb 26
        to Minoru Chikamune, Code Review Nudger, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, asvitki...@chromium.org, chromiumme...@microsoft.com, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Charlie Reis and Rakina Zata Amni

        Minoru Chikamune added 1 comment

        Patchset-level comments
        File-level comment, Patchset 104 (Latest):
        Minoru Chikamune . resolved

        I think this CL is ready to be reviewed. PTAL.

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alex Moshchuk
        • Charlie Reis
        • Rakina Zata Amni
        Submit Requirements:
        • requirement satisfiedCode-Coverage
        • requirement is not satisfiedCode-Owners
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: chromium/src
        Gerrit-Branch: main
        Gerrit-Change-Id: I7b19ffb64190f94f5e51aa93225c05a2aba0404a
        Gerrit-Change-Number: 7413835
        Gerrit-PatchSet: 104
        Gerrit-Owner: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Reviewer: Charlie Reis <cr...@chromium.org>
        Gerrit-Reviewer: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-CC: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
        Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
        Gerrit-CC: Code Review Nudger <android-build...@prod.google.com>
        Gerrit-CC: Nate Chapin <jap...@chromium.org>
        Gerrit-Attention: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Attention: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-Attention: Charlie Reis <cr...@chromium.org>
        Gerrit-Comment-Date: Thu, 26 Feb 2026 13:24:43 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Minoru Chikamune (Gerrit)

        unread,
        Mar 2, 2026, 1:34:08 AM (11 days ago) Mar 2
        to Minoru Chikamune, Code Review Nudger, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, asvitki...@chromium.org, chromiumme...@microsoft.com, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Charlie Reis and Rakina Zata Amni

        Minoru Chikamune added 1 comment

        File content/browser/renderer_host/render_frame_host_impl.cc
        Minoru Chikamune

        Now I fixed the tests (https://crrev.com/c/7616469), and I cleaned up the code related to this.

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alex Moshchuk
        • Charlie Reis
        • Rakina Zata Amni
        Submit Requirements:
        • requirement satisfiedCode-Coverage
        • requirement is not satisfiedCode-Owners
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: chromium/src
        Gerrit-Branch: main
        Gerrit-Change-Id: I7b19ffb64190f94f5e51aa93225c05a2aba0404a
        Gerrit-Change-Number: 7413835
        Gerrit-PatchSet: 106
        Gerrit-Owner: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Reviewer: Charlie Reis <cr...@chromium.org>
        Gerrit-Reviewer: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-CC: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
        Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
        Gerrit-CC: Code Review Nudger <android-build...@prod.google.com>
        Gerrit-CC: Nate Chapin <jap...@chromium.org>
        Gerrit-Attention: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Attention: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-Attention: Charlie Reis <cr...@chromium.org>
        Gerrit-Comment-Date: Mon, 02 Mar 2026 06:34:02 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Minoru Chikamune (Gerrit)

        unread,
        Mar 4, 2026, 2:42:37 AM (9 days ago) Mar 4
        to Minoru Chikamune, Code Review Nudger, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, asvitki...@chromium.org, chromiumme...@microsoft.com, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Charlie Reis and Rakina Zata Amni

        Minoru Chikamune added 6 comments

        Commit Message
        Line 20, Patchset 87:dialogs that users may use to cancel the navigation. If even a single
        frame has been interacted with, the browser reverts to synchronous
        Charlie Reis . resolved

        with a beforeunload handler

        (Correct? It sounds like it might be ok to proceed if a frame on the page has a user activation but no beforeunload handler?)

        Not sure how easy it is to test a case like this (and the simpler case where a frame with an activation and a handler prevents the optimization), but that might be worth considering, if it's not already in the CL.

        Minoru Chikamune

        with a beforeunload handler

        Yes, correct. I updated the CL description.

        Not sure how easy it is to test a case like this (and the simpler case where a frame with an activation and a handler prevents the optimization), but that might be worth considering, if it's not already in the CL.

        I think the current `RenderFrameHostImplAsyncBeforeUnloadBrowserTest.AsyncExecution` covers the half of it.

        A(B,C):

        • B,C has beforeunload handler, A doesn't.
        • A has an activation, B,C don't.
        • And confirms that the beforeunload handler runs asynchronously.

        I added `RenderFrameHostImplAsyncBeforeUnloadBrowserTest.SyncFallbackWithUserActivation` to confirm the case where this optimization is not triggered.

        A(B,C):

        • B,C has beforeunload handler, A doesn't.
        • A,B has an activation, C doesn't.
        Minoru Chikamune

        Done

        File content/browser/renderer_host/async_beforeunload_commit_deferring_condition.h
        Line 40, Patchset 80: // too long, preventing the navigation from being hung indefinitely.
        Charlie Reis . resolved

        Just curious-- do we need a separate timer for this if there's already [one in RFHI](https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/render_frame_host_impl.h;drc=976a71ef2a509d4391d814b52b9f4700fdf35789;l=4756)? If RenderFrameHostImpl::BeforeUnloadTimeout() had a way of telling this condition to proceed, would that avoid the need for a second timer?

        I mainly ask because it seems tricky to think through all the possibilities of which timer fires first, if at all. If there's a good way to think about how they relate to each other, that might be worth putting in the comment here.

        Minoru Chikamune

        Currently, I don't have a clear answer, but I agree that it's better to use one timer instead of two timers. I'll check if I can use just one timer.

        Charlie Reis

        Thanks. I left another comment about this since I see we still have two timers, and I'm not sure how to reason about them.

        Minoru Chikamune

        I ended up using a separate timer, but I stopped the timer (RenderFrameHostImpl::beforeunload_timeout_) when we run AsyncBeforeUnload path to avoid having two timers running.

        Minoru Chikamune

        Done

        File content/browser/renderer_host/navigation_request.h
        Line 3537, Patchset 87: // navigation from being hung indefinitely.
        Charlie Reis . resolved

        As before, can we document how this interacts with the [other beforeunload timer](https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/render_frame_host_impl.cc;drc=97c0a7967c365a7092885334a52c68d1c43efb91;l=11958), if we need to have both? Do we have a guarantee which one will fire first, or do we need to reason about what happens if either one fires first?

        I'm still not sure I understand why we need two timers, especially if they're set around the same time and have the same duration. Maybe that can be made clearer.

        Minoru Chikamune

        Ah, sorry. I should have addressed this. I updated the code to explicitly stop RFHI::beforeunload_timeout_ before starting NavigationRequest::async_before_unload_timeout_. I hope this makes it clear which timer is responsible for each phase:

        1. `RFHI::beforeunload_timeout_`: Manages the synchronous phase that blocks the navigation from starting.
        2. `NavigationRequest::async_before_unload_timeout_`: Manages the asynchronous phase where handlers run in the background and only block the navigation from committing.

        By stopping the first timer before starting the second, we ensure they never overlap or race against each other.

        Minoru Chikamune

        Done

        File content/browser/renderer_host/render_frame_host_impl.h
        Line 3888, Patchset 82: // See the fixme comment in `FrameLoader::ShouldClose` function: "There is not
        // yet any way to dispatch events to out-of-process frames."
        Alex Moshchuk . resolved

        Oh, that comment is super-old and probably predates the OOPIF beforeunload support that we added in M63. It can probably be removed (since we do dispatch beforeunload to OOPIFs, just requiring them all to complete before kicking off the navigation), and I'm not sure it's worth mentioning here.

        Minoru Chikamune

        I see. But still this code relies on this assumption. The browser process calls the mojo API for each renderers.

        e.g. For RFH tree A(B(A), C(B)), SendBeforeUnloadAsync() is called from CheckOrDispatchBeforeUnloadForFrame for A, B, C if A, B, C is hosted in a separated renderer.

        Minoru Chikamune

        Done

        File content/browser/renderer_host/render_frame_host_impl.cc
        Line 12154, Patchset 79: SendBeforeUnloadAsync(is_reload, rfh->GetWeakPtr());
        Rakina Zata Amni . resolved

        Why do some callers call `SendBeforeUnloadAsync()` but some others call `SendBeforeUnload()` with `should_run_before_unload_asynchronously` to true? What's the difference and in which case should we call which ones? Is it possible to just have one function for async beforeunload?

        Minoru Chikamune

        `should_run_before_unload_asynchronously` is decided only once for one navigation. After the decision, always use SendBeforeUnloadAsync() when `should_run_before_unload_asynchronously` is true.

        Alex Moshchuk

        +1 to Rakina's question here, as I'm also not sure I understand yet why we need both helpers when it seems like SendBeforeUnload can support both modes (since it takes the `should_run_before_unload_asynchronously` flag). Is it because the ack processing in SendBeforeUnloadAsync is sufficiently different that it'll make SendBeforeUnload too complex if we wanted to fold it in there?

        Also, maybe it would be helpful to walk through an example like your test - how would SendBeforeUnload/SendBeforeUnloadAsync end up getting invoked in that case?

        Minoru Chikamune

        I've merged `SendBeforeUnloadAsync` into `SendBeforeUnload` as suggested.

        In the new combined `SendBeforeUnload`, the control flow works as follows:

        1. When `should_run_before_unload_asynchronously` is true, the call to `SendBeforeUnload` (via `CheckOrDispatchBeforeUnloadForFrame`) initiates the asynchronous execution using a specialized callback that notifies the `NavigationRequest` via `MaybeResumeAsyncBeforeUnloadCommit`.
        2. After the dispatch loop (from `ForEachRenderFrameHostImplWithAction` and `CheckOrDispatchBeforeUnloadForFrame`), `CheckOrDispatchBeforeUnloadForSubtree` moves the frame IDs from the synchronous `beforeunload_pending_replies_` to the `NavigationRequest`'s `async_before_unload_pending_replies_`.
        3. Finally, an additional call to `SendBeforeUnload` (at the end of `CheckOrDispatchBeforeUnloadForSubtree`) is made to continue the navigation process. This call uses the standard path (the `for_legacy == true` path) but acknowledges the async state, allowing the navigation to proceed while the background handlers are still running.

        After merging `SendBeforeUnloadAsync` into `SendBeforeUnload`, The `if (navigation_request->get_async_before_unload_pending_replies().empty())` check is required to distinguish the initial async dispatch phase from the subsequent navigation continuation.

        Rakina Zata Amni

        Thank you for the explanation! I think the hard to comprehend part is the part to continue the navigation (the if clause for `for_legacy` + now `should_run_before_unload_asynchronously`) that doesn't actually send anything to the renderer and just wants to continue the navigation (synchronously or asynchronously). That part doesn't matter at all for cases where we actually want to run the beforeunload in the renderer, and only matters for the additional call outside of the iteration that you mentioned.

        Would it be possible to move that part to the caller of `SendBeforeUnload` that's outside of the iteration? We need the `before_unload_closure` but maybe we can just make that a member function? Then you also don't need the `get_async_before_unload_pending_replies()` check to differentiate the iteration vs the extra call?

        Minoru Chikamune

        Sorry for my late reply; I somehow missed your comment.

        Would it be possible to move that part to the caller of SendBeforeUnload

        That makes sense. I'll update the patch to reflect this.

        Minoru Chikamune

        I splitted SendBeforeUnload into SendBeforeUnload and ContinueNavigationAfterBeforeUnloadCheck. After that, I also tried to remove the code to run ContinueNavigationAfterBeforeUnloadCheck in SendBeforeUnload, but it requires test updates. I'm currently working on fixing tests.

        Minoru Chikamune

        Now I fixed the tests (https://crrev.com/c/7616469), and I cleaned up the code related to this.

        Minoru Chikamune

        Done

        Line 17274, Patchset 82: frame_tree_node_->navigation_request()->GetWeakPtr();
        Alex Moshchuk . resolved

        Just trying to understand: looks like this is called from CheckOrDispatchBeforeUnloadForFrame for each frame that needs to send a beforeunload IPC to the renderer. Why are we looking up the navigation request from each frame's FTN here? Shouldn't it always be retrieved from the ancestor frame that's navigating and triggering beforeunload handlers in all of its descendants? (I.e., the equivalent of what GetBeforeUnloadInitiator() does.)

        Minoru Chikamune

        In `SendBeforeUnloadAsync`, `this` refers to the RenderFrameHost that initiated the navigation (the root of the navigating subtree), while the `rfh` parameter represents the specific frame (which could be a descendant) that needs to run the `beforeunload` handler.

        Therefore, `frame_tree_node_->navigation_request()` (accessing `this->frame_tree_node_`) correctly retrieves the `NavigationRequest` associated with the navigation root, regardless of which descendant frame is being asked to run its handler. I added a comment to make this relationship clearer.

        Minoru Chikamune

        Done

        Gerrit-Comment-Date: Wed, 04 Mar 2026 07:42:07 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        Comment-In-Reply-To: Alex Moshchuk <ale...@chromium.org>
        Comment-In-Reply-To: Minoru Chikamune <chik...@chromium.org>
        Comment-In-Reply-To: Charlie Reis <cr...@chromium.org>
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Minoru Chikamune (Gerrit)

        unread,
        Mar 4, 2026, 3:08:39 AM (9 days ago) Mar 4
        to Minoru Chikamune, Code Review Nudger, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, asvitki...@chromium.org, chromiumme...@microsoft.com, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Charlie Reis and Rakina Zata Amni

        Minoru Chikamune added 1 comment

        File content/browser/renderer_host/async_beforeunload_commit_deferring_condition.h
        Line 40, Patchset 80: // too long, preventing the navigation from being hung indefinitely.
        Charlie Reis . unresolved

        Just curious-- do we need a separate timer for this if there's already [one in RFHI](https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/render_frame_host_impl.h;drc=976a71ef2a509d4391d814b52b9f4700fdf35789;l=4756)? If RenderFrameHostImpl::BeforeUnloadTimeout() had a way of telling this condition to proceed, would that avoid the need for a second timer?

        I mainly ask because it seems tricky to think through all the possibilities of which timer fires first, if at all. If there's a good way to think about how they relate to each other, that might be worth putting in the comment here.

        Minoru Chikamune

        Currently, I don't have a clear answer, but I agree that it's better to use one timer instead of two timers. I'll check if I can use just one timer.

        Charlie Reis

        Thanks. I left another comment about this since I see we still have two timers, and I'm not sure how to reason about them.

        Minoru Chikamune

        I ended up using a separate timer, but I stopped the timer (RenderFrameHostImpl::beforeunload_timeout_) when we run AsyncBeforeUnload path to avoid having two timers running.

        Minoru Chikamune

        Done

        Minoru Chikamune

        (reopen)

        Gerrit-Comment-Date: Wed, 04 Mar 2026 08:08:10 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Alex Moshchuk (Gerrit)

        unread,
        Mar 4, 2026, 6:17:45 PM (8 days ago) Mar 4
        to Minoru Chikamune, Code Review Nudger, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, asvitki...@chromium.org, chromiumme...@microsoft.com, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Charlie Reis, Minoru Chikamune and Rakina Zata Amni

        Alex Moshchuk added 19 comments

        Patchset-level comments
        File-level comment, Patchset 106 (Latest):
        Alex Moshchuk . resolved

        Thank you for your patience! I finally got to do another pass here, after our team was distracted with a reorg over the past several days.

        The question about keeping replies on NavRequest vs RFH is indeed a complex one - I wrote down some considerations below, and we might want to discuss further in our upcoming CSA/Loading sync.

        File content/browser/renderer_host/async_before_unload_commit_deferring_condition.cc
        Line 45, Patchset 106 (Latest): if (!navigation_request.IsAsyncBeforeUnloadTimerRunning() ||
        navigation_request.async_before_unload_pending_replies().empty()) {
        Alex Moshchuk . unresolved

        Could we expose another helper on NavigationRequest to help determine if an async beforeunload is still in progress, so that code outside NavigationRequest doesn't need to know about the timer and the pending replies, which are more implementation details?

        Separately, is it sufficient to only check async_before_unload_pending_replies().empty() here? Otherwise this seems to imply that the async beforeunload timer could be running while the async_before_unload_pending_replies() is empty - but that doesn't seem possible because you stop the timer after the replies become empty.

        File content/browser/renderer_host/navigation_request.h
        Line 1774, Patchset 106 (Latest): // have finished executing.
        Alex Moshchuk . unresolved

        nit: describe what the `acked_rfh_id` argument represents?

        File content/browser/renderer_host/navigation_request.cc
        Line 12333, Patchset 106 (Latest): if (acked_rfh_id) {
        Alex Moshchuk . unresolved

        nit: mention that this might be nullopt when the timer expires?

        File content/browser/renderer_host/render_frame_host_impl.h
        Line 3880, Patchset 106 (Latest): // are tests and android WebView using NavigationThrottles to navigate from
        Alex Moshchuk . unresolved

        minor nit: capitalize

        Line 3888, Patchset 82: // See the fixme comment in `FrameLoader::ShouldClose` function: "There is not
        // yet any way to dispatch events to out-of-process frames."
        Alex Moshchuk . resolved

        Oh, that comment is super-old and probably predates the OOPIF beforeunload support that we added in M63. It can probably be removed (since we do dispatch beforeunload to OOPIFs, just requiring them all to complete before kicking off the navigation), and I'm not sure it's worth mentioning here.

        Minoru Chikamune

        I see. But still this code relies on this assumption. The browser process calls the mojo API for each renderers.

        e.g. For RFH tree A(B(A), C(B)), SendBeforeUnloadAsync() is called from CheckOrDispatchBeforeUnloadForFrame for A, B, C if A, B, C is hosted in a separated renderer.

        Alex Moshchuk

        Agreed, and resolving since it looks like the new version of this comment doesn't mention the fixme.

        File content/browser/renderer_host/render_frame_host_impl.cc
        Line 3028, Patchset 106 (Latest): node = FrameTreeNode::From(node->parent())) {
        Alex Moshchuk . unresolved

        It's generally more correct to walk the parent chain via RFH::GetParent() (since the FTN's current RFH might've changed - though this might not matter here), and also we've been [discouraging](https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/render_frame_host_impl.h;l=4519-4521;drc=a1527fccf3804c7fc0f4fc4f474a260d4b248298) new uses of frame_tree_node_. So maybe it's worth changing the loop to use that and only converting to FTN when retrieving the NavigationRequest below?

        On that note, one question here about another race condition: Suppose A navigates to B, and A is running unload handlers and is pending deletion. B has a beforeunload handler and starts to navigate to C, and we kick off async beforeunload for it. Before B's beforeunload ack arrives, A's unload handler finishes running and we get here to destroy A's RFH. We will look at the FTN (shared for A/B/C) and it seems that we will find the NavigationRequest for navigating B to C, and resume it potentially incorrectly. Do we need to guard against this happening? Or is this ok because A's ID won't be in async_before_unload_pending_replies_ and nothing really goes wrong in that case? Would it be possible to add a test for this?

        Line 12032, Patchset 79: // `this` into `beforeunload_pending_replies_` as a placeholder, letting the
        Rakina Zata Amni . unresolved

        Why do we need this placeholder? Also why don't we just put things directly in `async_beforeunload_pending_replies_` instead of swapping here?

        Minoru Chikamune

        Why do we need this placeholder?

        This is the same behavior as L12018: `beforeunload_pending_replies_.insert(this);`. It adds the frame (navigation root) as a "placeholder", and `RenderFrameHostImpl::ProcessBeforeUnloadCompletedFromFrame()` consumes it (`beforeunload_pending_replies_.erase(frame);`).

        Also why don't we just put things directly in async_beforeunload_pending_replies_ instead of swapping here?

        `RenderFrameHostImpl::CheckOrDispatchBeforeUnloadForFrame()` algorithm relies on `beforeunload_pending_replies_` not to send duplicated request. So, `beforeunload_pending_replies_` should be kept as is until the `ForEachRenderFrameHostImplWithAction` ends.

        Initially, I didn't understand it, and actually, the beforeunload handlers run twice before in the test.

        Rakina Zata Amni

        Ah ok, hmm we do erase it from `ProcessBeforeUnloadCompletedFromFrame()` but does anything prevent us from just not adding anything at all? We don't actually check whether the RFH is in the list when erasing. But maybe it's not important, it's either confusing here or confusing there.

        I'm wondering if we can just combine `beforeunload_pending_replies_` and `async_beforeunload_pending_replies_` by just adding a bool differentiating the type (async/sync) there so we can not do this swapping and placeholder and ensuring every time we want to clear `beforeunload_pending_replies_` the async ones get cleared as well.

        But I guess you moved it to NavigationRequest because of my [comment](https://chromium-review.googlesource.com/c/chromium/src/+/7413835/comment/3c83d833_4b258612/) about the beforeunload being tied to the navigation instead of the RFH, which I kinda feel it should be. But having `async_beforeunload_pending_replies_` in NavigationRequest means we can trigger beforeunload multiple times for different navigations happening in different frames at the same time, but the sync version will only trigger the beforeunload once. We can't really move `beforeunload_pending_replies_` to NavigationRequest because that can also be used for frame deletion. Maybe it's fine to keep `async_beforeunload_pending_replies_` in RFH too to not differentiate the sync vs async behavior further? WDYT? I think I need @ale...@chromium.org's opinion on this too since he's more familiar with beforeunload :)

        Minoru Chikamune

        Thank you for your comment. This is important decision. I'll wait for alexmos@'s opinion.

        Alex Moshchuk

        This is a very good question, with pros and cons to both approaches and it feels like there's no ideal solution. :/

        Advantages of reusing beforeunload_pending_replies_:

        • No need to move the pending replies to another list.
        • Centralizes beforeunload logic for both navigations and tab/frame close and minimizes differences between sync/async logic.
        • Already tries to handle various races with races (e.g., navigation getting canceled/replaced while a beforeunload is running, navigation interleaving with tab close, etc.). One example: suppose we kicked off async beforeunload for a navigation, then the user initiates tab close before we receive the beforeunload ACK. Presumably we wouldn't want to execute beforeunload more than once? Do we now have to search all NavigationRequests for each frame to see if we're already running beforeunload in them, in addition to looking at RFH::beforeunload_pending_replies_ / is_waiting_for_beforeunload_completion_? And we could end up with a mix of handlers tracked differently in beforeunload_pending_replies_ vs async_beforeunload_pending_replies_?

        Drawbacks of reusing beforeunload_pending_replies_ / advantanges of async_beforeunload_pending_replies_ in NavigationRequest:

        • https://crbug.com/467325165 showed that beforeunload_pending_replies_ is potentially error-prone: RFHs in that list were potentially being unexpectedly freed with an experimental feature that postponed RFH destruction (the feature never shipped, though). Reusing it for async beforeunload would widen the time window during which bugs like this can happen. I like that async_beforeunload_pending_replies_ uses RFH IDs instead, and ideally we'd move beforeunload_pending_replies_ to that approach as well.
        • As Rakina mentioned, keeping this on NavigationRequest "feels right" and minimizes chances of misattributing incoming beforeunload ACKs to incorrect navigations.
        • beforeunload_pending_replies_ (and potentially other related beforeunload state on RFH) was not designed to be valid beyond the point at which we start the navigation and kick off DidStartNavigation, etc, so reusing it might increase chances of bugs.

        In an ideal world, I feel we should have a BeforeUnloadManager, which keeps all beforeunload-related state, and which either RFH or NavigationRequest can own. But I realize that's a large refactor of the current implementation, so may not be feasible in the short term.

        For the purposes of unblocking this CL - I'm going back and forth on this, but I think ultimately "async beforeunload" is a different enough concept where it makes sense to track it separately, rather than change lifetimes and expectations for all the existing RFH beforeunload state. E.g., is_waiting_for_beforeunload_completion_, has_shown_beforeunload_dialog_, and related state, all assume the sync beforeunload flow and timing, and I worry that we might get things wrong if we extend some (but not all) of that state to also work with the async flow, such as allowing dialogs after DidStartNavigation. I think this does risk potentially running beforeunload handlers multiple times in some of the race cases I mentioned, but maybe that's not that bad, and maybe beforeunload handlers should already be prepared to handle this. From this perspective, I think I'm also ok having a separate timer and ensuring it's mutually exclusive with the existing one - that might be simpler than sharing a timer and figuring out which callback path to take when it fires.

        Also, to avoid moving over the replies, we could consider passing in a reference to the list of pending replies (either RFHI::beforeunload_pending_replies_ or NavRequest::async_beforeunload_pending_replies_) to CheckOrDispatchBeforeUnloadForFrame() from the start. That would probably involve converting beforeunload_pending_replies_ to use IDs as well, though, which is extra work but good cleanup (and could be done as a prerequisite).

        Re: `beforeunload_pending_replies_.insert(this);`. The latest PS doesn't use `ProcessBeforeUnloadCompletedFromFrame()` for resuming async beforeunload, and instead uses `navigation_request->MaybeResumeAsyncBeforeUnloadCommit()`, which doesn't look at beforeunload_pending_replies_. So I want to check if it's still needed?

        Line 12079, Patchset 106 (Latest): // to `SendBeforeUnload` immediately stops `beforeunload_timeout_` in
        Alex Moshchuk . unresolved

        ContinueNavigationAfterBeforeUnloadCheck?

        Line 12121, Patchset 106 (Latest): // handler. See description of SendBeforeUnload() for details on simulating
        Alex Moshchuk . unresolved

        Update to ContinueNavigationAfterBeforeUnloadCheck?

        Line 17212, Patchset 106 (Latest): // proceed on the browser process by checking the
        Alex Moshchuk . unresolved

        nit: I'm remove this part and instead append "in ShouldRunBeforeUnloadAsynchronously()" to the end of this sentence.

        Line 17275, Patchset 106 (Latest): /*renderer_before_unload_end_time=*/base::TimeTicks::Now(), for_legacy);
        Alex Moshchuk . unresolved

        This isn't correct for the async beforeunload case, right? Is this something you plan to follow up on separately?

        Line 17295, Patchset 106 (Latest): is_waiting_for_beforeunload_completion_ &&
        Alex Moshchuk . unresolved

        Where/when does this get reset for the async beforeunload flow? Would it happen as part of ProcessBeforeUnloadCompleted, similarly to the legacy beforeunload case?

        File content/browser/renderer_host/render_frame_host_impl_browsertest.cc
        Line 8299, Patchset 106 (Latest):IN_PROC_BROWSER_TEST_P(RenderFrameHostImplAsyncBeforeUnloadBrowserTest,
        Alex Moshchuk . unresolved

        Consider adding quick summaries of what each test covers, to easily determine what to expect for each one.

        Line 8323, Patchset 106 (Latest): // Disable beforeunload timer to prevent flakiness.
        Alex Moshchuk . unresolved

        Should this helper turn off the new timer as well (for tests that don't expect it to run), to similarly prevent flakiness on slower bots?

        Line 8385, Patchset 106 (Latest): dialog_manager()->Wait();
        Alex Moshchuk . unresolved

        After this line, could we verify that the NavigationRequest is in the proper state, i.e. that it hasn't moved past the sync beforeunload phase?

        Line 8464, Patchset 106 (Latest): // BEFORE the browser is notified. Therefore, the async beforeunload
        // optimization should not be triggered.
        Alex Moshchuk . unresolved

        What if we started on A(B) and did a renderer-initiated navigation to C? We would run A's beforeunload in the renderer, but B's beforeunload would be kicked off by the browser. Would that latter part be allowed to use async beforeunload or not? Could we cover that in a separate test?

        File content/common/features.cc
        Line 42, Patchset 106 (Latest):// haven't interact with the frame. (See: https://crbug.com/475716933)
        Alex Moshchuk . unresolved

        nit: hasn't interacted

        File third_party/blink/public/mojom/frame/frame.mojom
        Line 970, Patchset 106 (Latest): // for example, when beforeunload handlers are being run asynchronously.
        Alex Moshchuk . unresolved

        Maybe leave a link to crbug for more information here as well?

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Charlie Reis
        • Minoru Chikamune
        • Rakina Zata Amni
        Gerrit-Attention: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Attention: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-Attention: Charlie Reis <cr...@chromium.org>
        Gerrit-Comment-Date: Wed, 04 Mar 2026 23:17:35 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        Comment-In-Reply-To: Alex Moshchuk <ale...@chromium.org>
        Comment-In-Reply-To: Minoru Chikamune <chik...@chromium.org>
        Comment-In-Reply-To: Rakina Zata Amni <rak...@chromium.org>
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Minoru Chikamune (Gerrit)

        unread,
        Mar 6, 2026, 3:00:02 AM (7 days ago) Mar 6
        to Minoru Chikamune, Code Review Nudger, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, asvitki...@chromium.org, chromiumme...@microsoft.com, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Charlie Reis and Rakina Zata Amni

        Minoru Chikamune added 12 comments

        File content/browser/renderer_host/async_before_unload_commit_deferring_condition.cc
        Line 45, Patchset 106: if (!navigation_request.IsAsyncBeforeUnloadTimerRunning() ||
        navigation_request.async_before_unload_pending_replies().empty()) {
        Alex Moshchuk . resolved

        Could we expose another helper on NavigationRequest to help determine if an async beforeunload is still in progress, so that code outside NavigationRequest doesn't need to know about the timer and the pending replies, which are more implementation details?

        Separately, is it sufficient to only check async_before_unload_pending_replies().empty() here? Otherwise this seems to imply that the async beforeunload timer could be running while the async_before_unload_pending_replies() is empty - but that doesn't seem possible because you stop the timer after the replies become empty.

        Minoru Chikamune

        Agreed. Updated.

        File content/browser/renderer_host/navigation_request.h
        Line 1774, Patchset 106: // have finished executing.
        Alex Moshchuk . resolved

        nit: describe what the `acked_rfh_id` argument represents?

        Minoru Chikamune

        Done

        File content/browser/renderer_host/navigation_request.cc
        Line 12333, Patchset 106: if (acked_rfh_id) {
        Alex Moshchuk . resolved

        nit: mention that this might be nullopt when the timer expires?

        Minoru Chikamune

        Updated the code comment.

        File content/browser/renderer_host/render_frame_host_impl.h
        Line 3880, Patchset 106: // are tests and android WebView using NavigationThrottles to navigate from
        Alex Moshchuk . resolved

        minor nit: capitalize

        Minoru Chikamune

        Done

        File content/browser/renderer_host/render_frame_host_impl.cc

        Thank you for your deep insights and the detailed review. I also feel that keeping the state on `NavigationRequest` is safer and less prone to bugs. Regarding the timers, I agree that keeping them separate feels more secure as it avoids logic changes to the existing synchronous path.

        The idea of passing `beforeunload_pending_replies_` to CheckOrDispatchBeforeUnloadForFrame makes a lot of sense. I’d like to explore that as a separate cleanup CL.

        As for `beforeunload_pending_replies_.insert(this);`, I'd prefer to keep it as is for now. This placeholder ensures that the subsequent call to `ContinueNavigationAfterBeforeUnloadCheck` correctly triggers `ProcessBeforeUnloadCompleted{FromFrame}` and its following cleanup logic, including the critical update of the `navigationStart` timestamp. Without this, we risk missing that timestamp replacement, which could lead to artificial performance regressions in our metrics.

        Line 12079, Patchset 106: // to `SendBeforeUnload` immediately stops `beforeunload_timeout_` in
        Alex Moshchuk . resolved

        ContinueNavigationAfterBeforeUnloadCheck?

        Minoru Chikamune

        Done

        Line 12121, Patchset 106: // handler. See description of SendBeforeUnload() for details on simulating
        Alex Moshchuk . resolved

        Update to ContinueNavigationAfterBeforeUnloadCheck?

        Minoru Chikamune

        Done

        Line 17212, Patchset 106: // proceed on the browser process by checking the
        Alex Moshchuk . resolved

        nit: I'm remove this part and instead append "in ShouldRunBeforeUnloadAsynchronously()" to the end of this sentence.

        Minoru Chikamune

        Thanks! Updated.

        Line 17275, Patchset 106: /*renderer_before_unload_end_time=*/base::TimeTicks::Now(), for_legacy);
        Alex Moshchuk . unresolved

        This isn't correct for the async beforeunload case, right? Is this something you plan to follow up on separately?

        Minoru Chikamune

        Oh, you are right. But...

        While not strictly accurate because the handlers are still running in the background, using `base::TimeTicks::Now()` here is intentional to avoid artificial performance regressions.

        As you know, in the current synchronous path, `navigationStart` is determined by the time when the `beforeunload` handlers finish executing (which is passed as `renderer_before_unload_end_time`). In the asynchronous path, this code location—where we decide to unblock the navigation and proceed—is the closest logical equivalent to the end of that blocking phase.

        If we were to use a different timestamp, it would shift the reported `navigationStart` time, which could make it appear as though the navigation is taking longer in our performance metrics, even though user-perceived performance has actually improved. I'd like to keep this as is for now to maintain consistency with existing timing calculations.

        I added a code comment about it.

        Line 17295, Patchset 106: is_waiting_for_beforeunload_completion_ &&
        Alex Moshchuk . unresolved

        Where/when does this get reset for the async beforeunload flow? Would it happen as part of ProcessBeforeUnloadCompleted, similarly to the legacy beforeunload case?

        Minoru Chikamune

        Yes, it is reset as part of `ProcessBeforeUnloadCompleted`, similarly to the legacy beforeunload case.

        When we decide to run handlers asynchronously in `CheckOrDispatchBeforeUnloadForSubtree`, we insert `this` RFH into `beforeunload_pending_replies_` as a placeholder and then call `ContinueNavigationAfterBeforeUnloadCheck(false)`. This method calls `ProcessBeforeUnloadCompleted`, then it calls `ProcessBeforeUnloadCompletedFromFrame`, which removes the placeholder from the pending set and clears `is_waiting_for_beforeunload_completion_`.

        For AsyncBeforeUnload case, is_waiting_for_beforeunload_completion_ == false means that the synchronous 'blocking' phase has ended and can proceed navigation.

        File content/common/features.cc
        Line 42, Patchset 106:// haven't interact with the frame. (See: https://crbug.com/475716933)
        Alex Moshchuk . resolved

        nit: hasn't interacted

        Minoru Chikamune

        Thanks! Fixed.

        File third_party/blink/public/mojom/frame/frame.mojom
        Line 970, Patchset 106: // for example, when beforeunload handlers are being run asynchronously.
        Alex Moshchuk . resolved

        Maybe leave a link to crbug for more information here as well?

        Minoru Chikamune

        Sure. Added.

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alex Moshchuk
        • Charlie Reis
        • Rakina Zata Amni
        Gerrit-Attention: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Attention: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-Attention: Charlie Reis <cr...@chromium.org>
        Gerrit-Comment-Date: Fri, 06 Mar 2026 07:59:36 +0000
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Minoru Chikamune (Gerrit)

        unread,
        Mar 6, 2026, 6:04:23 AM (7 days ago) Mar 6
        to Minoru Chikamune, Code Review Nudger, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, asvitki...@chromium.org, chromiumme...@microsoft.com, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Charlie Reis and Rakina Zata Amni

        Minoru Chikamune added 2 comments

        File content/browser/renderer_host/render_frame_host_impl.cc
        Line 3028, Patchset 106: node = FrameTreeNode::From(node->parent())) {
        Alex Moshchuk . unresolved

        It's generally more correct to walk the parent chain via RFH::GetParent() (since the FTN's current RFH might've changed - though this might not matter here), and also we've been [discouraging](https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/render_frame_host_impl.h;l=4519-4521;drc=a1527fccf3804c7fc0f4fc4f474a260d4b248298) new uses of frame_tree_node_. So maybe it's worth changing the loop to use that and only converting to FTN when retrieving the NavigationRequest below?

        On that note, one question here about another race condition: Suppose A navigates to B, and A is running unload handlers and is pending deletion. B has a beforeunload handler and starts to navigate to C, and we kick off async beforeunload for it. Before B's beforeunload ack arrives, A's unload handler finishes running and we get here to destroy A's RFH. We will look at the FTN (shared for A/B/C) and it seems that we will find the NavigationRequest for navigating B to C, and resume it potentially incorrectly. Do we need to guard against this happening? Or is this ok because A's ID won't be in async_before_unload_pending_replies_ and nothing really goes wrong in that case? Would it be possible to add a test for this?

        Minoru Chikamune

        So maybe it's worth changing the loop to use that and only converting to FTN when retrieving the NavigationRequest below?

        Thanks! Done.

        about another race condition

        I added a guard condition.

        Would it be possible to add a test for this?

        Sorry, not yet.

        Minoru Chikamune

        The idea of passing beforeunload_pending_replies_ to CheckOrDispatchBeforeUnloadForFrame

        I updated this CL.

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alex Moshchuk
        • Charlie Reis
        • Rakina Zata Amni
        Submit Requirements:
        • requirement satisfiedCode-Coverage
        • requirement is not satisfiedCode-Owners
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: chromium/src
        Gerrit-Branch: main
        Gerrit-Change-Id: I7b19ffb64190f94f5e51aa93225c05a2aba0404a
        Gerrit-Change-Number: 7413835
        Gerrit-PatchSet: 110
        Gerrit-Owner: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Reviewer: Charlie Reis <cr...@chromium.org>
        Gerrit-Reviewer: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-CC: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
        Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
        Gerrit-CC: Code Review Nudger <android-build...@prod.google.com>
        Gerrit-CC: Nate Chapin <jap...@chromium.org>
        Gerrit-Attention: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Attention: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-Attention: Charlie Reis <cr...@chromium.org>
        Gerrit-Comment-Date: Fri, 06 Mar 2026 11:03:52 +0000
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Minoru Chikamune (Gerrit)

        unread,
        Mar 9, 2026, 10:33:17 AM (3 days ago) Mar 9
        to Minoru Chikamune, Code Review Nudger, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, asvitki...@chromium.org, chromiumme...@microsoft.com, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Charlie Reis and Rakina Zata Amni

        Minoru Chikamune added 3 comments

        File content/browser/renderer_host/render_frame_host_impl_browsertest.cc
        Line 8299, Patchset 106:IN_PROC_BROWSER_TEST_P(RenderFrameHostImplAsyncBeforeUnloadBrowserTest,
        Alex Moshchuk . resolved

        Consider adding quick summaries of what each test covers, to easily determine what to expect for each one.

        Minoru Chikamune

        Done

        Line 8323, Patchset 106: // Disable beforeunload timer to prevent flakiness.
        Alex Moshchuk . resolved

        Should this helper turn off the new timer as well (for tests that don't expect it to run), to similarly prevent flakiness on slower bots?

        Minoru Chikamune

        Thanks! I modified the following code in `RenderFrameHostImpl::CheckOrDispatchBeforeUnloadForSubtree`.

            // `beforeunload_timeout_` may be null in tests (e.g., when
        // DisableBeforeUnloadHangMonitorForTesting() is used). In such cases, we
        // also skip starting the asynchronous beforeunload timer to maintain
        // consistent behavior and avoid flakiness on slow bots.
        if (beforeunload_timeout_) {
        beforeunload_timeout_->Stop();
        frame_tree_node_->navigation_request()->StartAsyncBeforeUnloadTimer();
        }
        Line 8385, Patchset 106: dialog_manager()->Wait();
        Alex Moshchuk . resolved

        After this line, could we verify that the NavigationRequest is in the proper state, i.e. that it hasn't moved past the sync beforeunload phase?

        Minoru Chikamune

        Done

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alex Moshchuk
        • Charlie Reis
        • Rakina Zata Amni
        Submit Requirements:
        • requirement satisfiedCode-Coverage
        • requirement is not satisfiedCode-Owners
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: chromium/src
        Gerrit-Branch: main
        Gerrit-Change-Id: I7b19ffb64190f94f5e51aa93225c05a2aba0404a
        Gerrit-Change-Number: 7413835
        Gerrit-PatchSet: 113
        Gerrit-Owner: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Reviewer: Charlie Reis <cr...@chromium.org>
        Gerrit-Reviewer: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-CC: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
        Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
        Gerrit-CC: Code Review Nudger <android-build...@prod.google.com>
        Gerrit-CC: Nate Chapin <jap...@chromium.org>
        Gerrit-Attention: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Attention: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-Attention: Charlie Reis <cr...@chromium.org>
        Gerrit-Comment-Date: Mon, 09 Mar 2026 14:32:52 +0000
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Minoru Chikamune (Gerrit)

        unread,
        Mar 10, 2026, 1:20:30 AM (3 days ago) Mar 10
        to Minoru Chikamune, Code Review Nudger, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, asvitki...@chromium.org, chromiumme...@microsoft.com, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Charlie Reis and Rakina Zata Amni

        Minoru Chikamune added 2 comments

        File content/browser/renderer_host/render_frame_host_impl.cc
        Line 3028, Patchset 106: node = FrameTreeNode::From(node->parent())) {
        Alex Moshchuk . resolved

        It's generally more correct to walk the parent chain via RFH::GetParent() (since the FTN's current RFH might've changed - though this might not matter here), and also we've been [discouraging](https://source.chromium.org/chromium/chromium/src/+/main:content/browser/renderer_host/render_frame_host_impl.h;l=4519-4521;drc=a1527fccf3804c7fc0f4fc4f474a260d4b248298) new uses of frame_tree_node_. So maybe it's worth changing the loop to use that and only converting to FTN when retrieving the NavigationRequest below?

        On that note, one question here about another race condition: Suppose A navigates to B, and A is running unload handlers and is pending deletion. B has a beforeunload handler and starts to navigate to C, and we kick off async beforeunload for it. Before B's beforeunload ack arrives, A's unload handler finishes running and we get here to destroy A's RFH. We will look at the FTN (shared for A/B/C) and it seems that we will find the NavigationRequest for navigating B to C, and resume it potentially incorrectly. Do we need to guard against this happening? Or is this ok because A's ID won't be in async_before_unload_pending_replies_ and nothing really goes wrong in that case? Would it be possible to add a test for this?

        Minoru Chikamune

        So maybe it's worth changing the loop to use that and only converting to FTN when retrieving the NavigationRequest below?

        Thanks! Done.

        about another race condition

        I added a guard condition.

        Would it be possible to add a test for this?

        Sorry, not yet.

        Minoru Chikamune

        I initially added the following guard condition, but I removed it because `NavigationRequest::MaybeResumeAsyncBeforeUnloadCommit()` checks it. I added a test to confirm it. See `RaceConditionDuringOldFrameDestruction` in browsertest. But thank you for your review!

        ```
        // If `NavigationRequest` is waiting for asynchronous beforeunload completion
        // callback from this frame, try to proceed the all navigations that are
        // unblocked by this frame.
        for (RenderFrameHostImpl* rfh = this; rfh; rfh = rfh->GetParent()) {
        if (NavigationRequest* navigation_request =
        rfh->frame_tree_node()->navigation_request()) {
        // A FrameTreeNode can only have one NavigationRequest at a time, but it
        // might not be waiting for the RenderFrameHost being destroyed. For
        // example, if frame A navigates to B, A is pending deletion. If B then
        // starts a navigation to C which runs beforeunload handlers
        // asynchronously, that navigation from B to C will be waiting for B's
        // beforeunload ACK. When A's unload handler eventually finishes and A is
        // destroyed, we must not incorrectly resume the navigation from B to C.
        if (navigation_request->async_before_unload_pending_replies().contains(
        GetGlobalId())) {
        navigation_request->MaybeResumeAsyncBeforeUnloadCommit(GetGlobalId());
        }
        }
        }
        ```
        File content/browser/renderer_host/render_frame_host_impl_browsertest.cc
        Line 8464, Patchset 106: // BEFORE the browser is notified. Therefore, the async beforeunload

        // optimization should not be triggered.
        Alex Moshchuk . resolved

        What if we started on A(B) and did a renderer-initiated navigation to C? We would run A's beforeunload in the renderer, but B's beforeunload would be kicked off by the browser. Would that latter part be allowed to use async beforeunload or not? Could we cover that in a separate test?

        Minoru Chikamune

        Would that latter part be allowed to use async beforeunload or not?

        Yes, it's allowed to use async beforeunload. I added a test for it. Thank you for pointing it out.

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alex Moshchuk
        • Charlie Reis
        • Rakina Zata Amni
        Submit Requirements:
        • requirement satisfiedCode-Coverage
        • requirement is not satisfiedCode-Owners
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: chromium/src
        Gerrit-Branch: main
        Gerrit-Change-Id: I7b19ffb64190f94f5e51aa93225c05a2aba0404a
        Gerrit-Change-Number: 7413835
        Gerrit-PatchSet: 116
        Gerrit-Owner: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Reviewer: Charlie Reis <cr...@chromium.org>
        Gerrit-Reviewer: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-CC: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
        Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
        Gerrit-CC: Code Review Nudger <android-build...@prod.google.com>
        Gerrit-CC: Nate Chapin <jap...@chromium.org>
        Gerrit-Attention: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Attention: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-Attention: Charlie Reis <cr...@chromium.org>
        Gerrit-Comment-Date: Tue, 10 Mar 2026 05:19:57 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        Comment-In-Reply-To: Alex Moshchuk <ale...@chromium.org>
        Comment-In-Reply-To: Minoru Chikamune <chik...@chromium.org>
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Minoru Chikamune (Gerrit)

        unread,
        Mar 10, 2026, 4:44:52 AM (3 days ago) Mar 10
        to Minoru Chikamune, Code Review Nudger, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, asvitki...@chromium.org, chromiumme...@microsoft.com, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Charlie Reis and Rakina Zata Amni

        Minoru Chikamune added 1 comment

        Patchset-level comments
        File-level comment, Patchset 117 (Latest):
        Minoru Chikamune . resolved

        I believe I've addressed all the major review comments in this patch set. Looking forward to your next review.

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alex Moshchuk
        • Charlie Reis
        • Rakina Zata Amni
        Submit Requirements:
        • requirement satisfiedCode-Coverage
        • requirement is not satisfiedCode-Owners
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: chromium/src
        Gerrit-Branch: main
        Gerrit-Change-Id: I7b19ffb64190f94f5e51aa93225c05a2aba0404a
        Gerrit-Change-Number: 7413835
        Gerrit-PatchSet: 117
        Gerrit-Owner: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Reviewer: Charlie Reis <cr...@chromium.org>
        Gerrit-Reviewer: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-CC: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
        Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
        Gerrit-CC: Code Review Nudger <android-build...@prod.google.com>
        Gerrit-CC: Nate Chapin <jap...@chromium.org>
        Gerrit-Attention: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Attention: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-Attention: Charlie Reis <cr...@chromium.org>
        Gerrit-Comment-Date: Tue, 10 Mar 2026 08:44:22 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Alex Moshchuk (Gerrit)

        unread,
        Mar 11, 2026, 9:40:44 PM (yesterday) Mar 11
        to Minoru Chikamune, Code Review Nudger, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, asvitki...@chromium.org, chromiumme...@microsoft.com, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Charlie Reis, Minoru Chikamune and Rakina Zata Amni

        Alex Moshchuk added 13 comments

        Patchset-level comments
        Alex Moshchuk . resolved

        Thanks, I think this is getting close! Just a few more minor comments below.

        File content/browser/renderer_host/render_frame_host_impl.cc
        Line 6952, Patchset 117 (Latest): // When `for_legacy` is true callers should supply
        Alex Moshchuk . unresolved

        nit: update this comment

        Alex Moshchuk

        Ack - I think I'm ok with the how things are in the latest PS of the CL. @rak...@chromium.org, does it look acceptable to you?

        Line 12205, Patchset 117 (Latest): // consistent behavior and avoid flakiness on slow bots.
        Alex Moshchuk . unresolved

        Let's explain that beforeunload_timeout_ *will* be non-null for all other cases, since it's already set by the caller of this function (DispatchBeforeUnload).

        Line 12212, Patchset 117 (Latest): // proceed while we track the real replies asynchronously.
        Alex Moshchuk . unresolved
        I'm still torn about needing to insert the placeholder ID here. I don't think it's needed for correctness or for resuming the navigation, because when the posted task runs ProcessBeforeUnloadCompletedFromFrame, it would execute:
        ```
        beforeunload_pending_replies_.erase(frame->GetGlobalId()); // No-op, already empty
        if (!beforeunload_pending_replies_.empty()) { // Returns false, proceeds
        return;
        }
        ```

        But I guess I'm ok with the ID being there, because (1) it matches the kForLegacy path, (2) it helps us keep an invariant that there's something in beforeunload_pending_replies_ while a navigation is blocked in the beforeunload phase (especially during the time after ContinueNavigationAfterBeforeUnloadCheck posts a task to proceed but before it runs). So given (2), I'm ok with it. But let's maybe modify the comment to explain this - while this isn't strictly necessary for resuming the navigation in ProcessBeforeUnloadCompletedFromFrame (which will let it proceed either way), we don't want to keep this set empty while the navigation is still blocked in the beforeunload phase (which violates an invariant that while is_waiting_for_beforeunload_completion_ is true, this set should have at least one frame we're waiting for).

        Line 17275, Patchset 106: /*renderer_before_unload_end_time=*/base::TimeTicks::Now(), for_legacy);
        Alex Moshchuk . resolved

        This isn't correct for the async beforeunload case, right? Is this something you plan to follow up on separately?

        Minoru Chikamune

        Oh, you are right. But...

        While not strictly accurate because the handlers are still running in the background, using `base::TimeTicks::Now()` here is intentional to avoid artificial performance regressions.

        As you know, in the current synchronous path, `navigationStart` is determined by the time when the `beforeunload` handlers finish executing (which is passed as `renderer_before_unload_end_time`). In the asynchronous path, this code location—where we decide to unblock the navigation and proceed—is the closest logical equivalent to the end of that blocking phase.

        If we were to use a different timestamp, it would shift the reported `navigationStart` time, which could make it appear as though the navigation is taking longer in our performance metrics, even though user-perceived performance has actually improved. I'd like to keep this as is for now to maintain consistency with existing timing calculations.

        I added a code comment about it.

        Alex Moshchuk

        Thanks - acknowledged.

        Line 17285, Patchset 82: bool proceed, base::TimeTicks renderer_before_unload_start_time,
        base::TimeTicks renderer_before_unload_end_time,
        Alex Moshchuk . resolved

        Are we going to need to process these for metrics, similarly how they're handled on the old path?

        On that note, how are the NavigationRequest::MaybeRecordTraceEventsAndHistograms() trace events and metrics affected by AsyncBeforeUnload? Will they need to be updated, and if so, will that happen here or in a separate CL?

        Minoru Chikamune

        Are we going to need to process these for metrics, similarly how they're handled on the old path?

        I thought these TimeTicks are no longer important because they are no longer on the critical path.

        On that note, how are the NavigationRequest::MaybeRecordTraceEventsAndHistograms() trace events and metrics affected by AsyncBeforeUnload? Will they need to be updated, and if so, will that happen here or in a separate CL?

        I thought MaybeRecordTraceEventsAndHistograms does not need to be updated because running beforeunload does not block navigation anymore.

        Charlie Reis

        Hmm, we may want to think about this a bit more.

        (1) Beforeunload can still block navigation if the commit deferring condition runs, and it would probably be good to have metrics for how much of an effect that has in practice. Hopefully we don't hit it much.

        (2) The beforeunload handlers are still running and could be shown in a separate (non-nested) trace event / metric if we wanted to. That's maybe less important, but might explain more about how the events overlap in a trace (which would be educational). Maybe a metric wouldn't be required for that, though, unless there's a reason we're curious about it. (It might help explain what portion of the time ended up in the commit deferring condition, for example.)

        Minoru Chikamune

        (1) Beforeunload can still block navigation if the commit deferring condition runs, and it would probably be good to have metrics for how much of an effect that has in practice. Hopefully we don't hit it much.

        I see. Currently, the `commit deferring condition` duration is included in `ReceiveResponseToCommit`.

        e.g. Tracelog of `RenderFrameHostImplAsyncBeforeUnloadBrowserTest.AsyncBeforeUnload`:
        https://ui.perfetto.dev/#!/?s=97a4c641f9ab5ecd206ddccc3dcea9ce98c6c3b8

        (2) The beforeunload handlers are still running and could be shown in a separate (non-nested) trace event / metric if we wanted to. That's maybe less important, but might explain more about how the events overlap in a trace (which would be educational). Maybe a metric wouldn't be required for that, though, unless there's a reason we're curious about it. (It might help explain what portion of the time ended up in the commit deferring condition, for example.)

        OK, I'll add a non-nested trace event in a follow-up CL.

        Alex Moshchuk

        Resolving, still it seems like we'll revisit this in a followup.

        Line 17295, Patchset 106: is_waiting_for_beforeunload_completion_ &&
        Alex Moshchuk . resolved

        Where/when does this get reset for the async beforeunload flow? Would it happen as part of ProcessBeforeUnloadCompleted, similarly to the legacy beforeunload case?

        Minoru Chikamune

        Yes, it is reset as part of `ProcessBeforeUnloadCompleted`, similarly to the legacy beforeunload case.

        When we decide to run handlers asynchronously in `CheckOrDispatchBeforeUnloadForSubtree`, we insert `this` RFH into `beforeunload_pending_replies_` as a placeholder and then call `ContinueNavigationAfterBeforeUnloadCheck(false)`. This method calls `ProcessBeforeUnloadCompleted`, then it calls `ProcessBeforeUnloadCompletedFromFrame`, which removes the placeholder from the pending set and clears `is_waiting_for_beforeunload_completion_`.

        For AsyncBeforeUnload case, is_waiting_for_beforeunload_completion_ == false means that the synchronous 'blocking' phase has ended and can proceed navigation.

        Alex Moshchuk

        Acknowledged

        File content/browser/renderer_host/render_frame_host_impl_browsertest.cc
        Line 8399, Patchset 117 (Latest): return;
        Alex Moshchuk . unresolved

        GTEST_SKIP might be nicer here.

        Line 8534, Patchset 117 (Latest): return;
        Alex Moshchuk . unresolved

        GTEST_SKIP?

        Line 8557, Patchset 117 (Latest): base::test::RunUntil([&]() { return rfh_a->IsPendingDeletion(); }));
        Alex Moshchuk . unresolved

        This might not be needed - I think `rfh_a->IsPendingDeletion()` should become true as part of A->B commit, so should already be true here. So maybe just `ASSERT_TRUE(rfh_a->IsPendingDeletion();` is sufficient?

        Line 8586, Patchset 117 (Latest): // If the guard in ~RFHI was missing or broken, it would have been resumed
        Alex Moshchuk . unresolved

        There is no guard in the latest version, right? We just rely on the fact that A's ID won't be in the set of pending replies? Update comment?

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Charlie Reis
        • Minoru Chikamune
        • Rakina Zata Amni
        Gerrit-Attention: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Attention: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-Attention: Charlie Reis <cr...@chromium.org>
        Gerrit-Comment-Date: Thu, 12 Mar 2026 01:40:31 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        Comment-In-Reply-To: Alex Moshchuk <ale...@chromium.org>
        Comment-In-Reply-To: Minoru Chikamune <chik...@chromium.org>
        Comment-In-Reply-To: Rakina Zata Amni <rak...@chromium.org>
        Comment-In-Reply-To: Charlie Reis <cr...@chromium.org>
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Minoru Chikamune (Gerrit)

        unread,
        4:37 AM (18 hours ago) 4:37 AM
        to Minoru Chikamune, Code Review Nudger, AI Code Reviewer, Rakina Zata Amni, Charlie Reis, Alex Moshchuk, Chromium Metrics Reviews, Nate Chapin, AyeAye, Chromium LUCI CQ, chromium...@chromium.org, asvitki...@chromium.org, chromiumme...@microsoft.com, penghuan...@chromium.org, cblume...@chromium.org, asvitkine...@chromium.org, blink-re...@chromium.org, loading...@chromium.org, gavinp...@chromium.org, blink-revi...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, kinuko...@chromium.org, alexmo...@chromium.org, creis...@chromium.org, navigation...@chromium.org
        Attention needed from Alex Moshchuk, Charlie Reis and Rakina Zata Amni

        Minoru Chikamune added 9 comments

        Patchset-level comments
        Alex Moshchuk . resolved

        Thanks, I think this is getting close! Just a few more minor comments below.

        Minoru Chikamune

        Thanks!

        File content/browser/renderer_host/render_frame_host_impl.cc
        Line 6952, Patchset 117: // When `for_legacy` is true callers should supply
        Alex Moshchuk . resolved

        nit: update this comment

        Minoru Chikamune

        Done

        Line 12205, Patchset 117: // consistent behavior and avoid flakiness on slow bots.
        Alex Moshchuk . resolved

        Let's explain that beforeunload_timeout_ *will* be non-null for all other cases, since it's already set by the caller of this function (DispatchBeforeUnload).

        Minoru Chikamune

        Done

        Line 12212, Patchset 117: // proceed while we track the real replies asynchronously.
        Alex Moshchuk . resolved
        I'm still torn about needing to insert the placeholder ID here. I don't think it's needed for correctness or for resuming the navigation, because when the posted task runs ProcessBeforeUnloadCompletedFromFrame, it would execute:
        ```
        beforeunload_pending_replies_.erase(frame->GetGlobalId()); // No-op, already empty
        if (!beforeunload_pending_replies_.empty()) { // Returns false, proceeds
        return;
        }
        ```

        But I guess I'm ok with the ID being there, because (1) it matches the kForLegacy path, (2) it helps us keep an invariant that there's something in beforeunload_pending_replies_ while a navigation is blocked in the beforeunload phase (especially during the time after ContinueNavigationAfterBeforeUnloadCheck posts a task to proceed but before it runs). So given (2), I'm ok with it. But let's maybe modify the comment to explain this - while this isn't strictly necessary for resuming the navigation in ProcessBeforeUnloadCompletedFromFrame (which will let it proceed either way), we don't want to keep this set empty while the navigation is still blocked in the beforeunload phase (which violates an invariant that while is_waiting_for_beforeunload_completion_ is true, this set should have at least one frame we're waiting for).

        Minoru Chikamune

        Thank you for the very thoughtful review. I agree with your reasoning. I've updated the comment.

        File content/browser/renderer_host/render_frame_host_impl_browsertest.cc
        Line 8399, Patchset 117: return;
        Alex Moshchuk . resolved

        GTEST_SKIP might be nicer here.

        Minoru Chikamune

        Done

        Line 8493, Patchset 117: return;
        Alex Moshchuk . resolved
        Minoru Chikamune

        Done

        Line 8534, Patchset 117: return;
        Alex Moshchuk . resolved

        GTEST_SKIP?

        Minoru Chikamune

        Done

        Line 8557, Patchset 117: base::test::RunUntil([&]() { return rfh_a->IsPendingDeletion(); }));
        Alex Moshchuk . resolved

        This might not be needed - I think `rfh_a->IsPendingDeletion()` should become true as part of A->B commit, so should already be true here. So maybe just `ASSERT_TRUE(rfh_a->IsPendingDeletion();` is sufficient?

        Minoru Chikamune

        Done

        Line 8586, Patchset 117: // If the guard in ~RFHI was missing or broken, it would have been resumed
        Alex Moshchuk . resolved

        There is no guard in the latest version, right? We just rely on the fact that A's ID won't be in the set of pending replies? Update comment?

        Minoru Chikamune

        Oh, right. I forgot to update this comment. Thanks!

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Alex Moshchuk
        • Charlie Reis
        • Rakina Zata Amni
        Submit Requirements:
        • requirement satisfiedCode-Coverage
        • requirement is not satisfiedCode-Owners
        • requirement is not satisfiedCode-Review
        • requirement is not satisfiedNo-Unresolved-Comments
        • requirement is not satisfiedReview-Enforcement
        Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
        Gerrit-MessageType: comment
        Gerrit-Project: chromium/src
        Gerrit-Branch: main
        Gerrit-Change-Id: I7b19ffb64190f94f5e51aa93225c05a2aba0404a
        Gerrit-Change-Number: 7413835
        Gerrit-PatchSet: 118
        Gerrit-Owner: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Reviewer: Charlie Reis <cr...@chromium.org>
        Gerrit-Reviewer: Minoru Chikamune <chik...@chromium.org>
        Gerrit-Reviewer: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-CC: AI Code Reviewer <peep-gen...@system.gserviceaccount.com>
        Gerrit-CC: Chromium Metrics Reviews <chromium-met...@google.com>
        Gerrit-CC: Code Review Nudger <android-build...@prod.google.com>
        Gerrit-CC: Nate Chapin <jap...@chromium.org>
        Gerrit-Attention: Alex Moshchuk <ale...@chromium.org>
        Gerrit-Attention: Rakina Zata Amni <rak...@chromium.org>
        Gerrit-Attention: Charlie Reis <cr...@chromium.org>
        Gerrit-Comment-Date: Thu, 12 Mar 2026 08:36:40 +0000
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy
        Reply all
        Reply to author
        Forward
        0 new messages