[Side Panel] Delay view detachment to prevent flickering [chromium/src : main]

0 views
Skip to first unread message

Shu Yang (Gerrit)

unread,
Jul 1, 2026, 12:42:29 AM (8 days ago) Jul 1
to Linyu He, Chromium LUCI CQ, Moe Adel, chromium...@chromium.org, Charles Hager, Mark Schillaci, Sinan Sahin, nemco...@google.com
Attention needed from Linyu He

Shu Yang voted and added 1 comment

Votes added by Shu Yang

Commit-Queue+1

1 comment

Patchset-level comments
Open in Gerrit

Related details

Attention is currently required from:
  • Linyu He
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: Id08144de3f941e930956f4932f2f5b4e257a3907
Gerrit-Change-Number: 7962991
Gerrit-PatchSet: 8
Gerrit-Owner: Shu Yang <shu...@google.com>
Gerrit-Reviewer: Linyu He <lin...@google.com>
Gerrit-Reviewer: Shu Yang <shu...@google.com>
Gerrit-CC: Charles Hager <clh...@google.com>
Gerrit-CC: Mark Schillaci <mschi...@google.com>
Gerrit-CC: Moe Adel <ad...@google.com>
Gerrit-CC: Sinan Sahin <sinan...@google.com>
Gerrit-Attention: Linyu He <lin...@google.com>
Gerrit-Comment-Date: Wed, 01 Jul 2026 04:42:20 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: Yes
satisfied_requirement
unsatisfied_requirement
open
diffy

Linyu He (Gerrit)

unread,
Jul 1, 2026, 7:33:55 PM (7 days ago) Jul 1
to Shu Yang, Chromium LUCI CQ, Moe Adel, chromium...@chromium.org, Charles Hager, Mark Schillaci, Sinan Sahin, nemco...@google.com
Attention needed from Shu Yang

Linyu He added 4 comments

File chrome/browser/ui/side_panel_container/internal/BUILD.gn
Line 129, Patchset 8 (Latest): "//components/thin_webview:java",
Linyu He . unresolved

Not needed? (`SidePanelContainerCoordinatorImplUnitTest` isn't changed.)

File chrome/browser/ui/side_panel_container/internal/android/java/src/org/chromium/chrome/browser/ui/side_panel_container/SidePanelContainerCoordinatorImpl.java
Line 152, Patchset 8 (Latest): View oldView = mCurrentContent != null ? mCurrentContent.mView : null;
Linyu He . unresolved

Per offline discussion, we'll move the `runOnNextFrame()` logic to `startReplacingPanelContent()` (the method is added in http://crrev.com/c/8028149 and targets the "replace content" case).

Line 157, Patchset 8 (Latest): Runnable removeOldViewRunnable =
Linyu He . unresolved

Q: Will this CL handle rapid tab switches, i.e., `removeOldViewRunnable` doesn't have time to run before the side panel content is replaced again?

One solution can be to keep `removeOldViewRunnable` as a mutable state and set it to null at the end of `runOnNextFrame()`.

If it's not null before `runOnNextFrame()`, immediately run it and replace its value with the new runnable.

Line 179, Patchset 8 (Latest): private @Nullable ThinWebView findThinWebView(View view) {
Linyu He . unresolved

nit: let's move this private method to the end of the class so all public methods stay together.

Open in Gerrit

Related details

Attention is currently required from:
  • Shu Yang
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: Id08144de3f941e930956f4932f2f5b4e257a3907
    Gerrit-Change-Number: 7962991
    Gerrit-PatchSet: 8
    Gerrit-Owner: Shu Yang <shu...@google.com>
    Gerrit-Reviewer: Linyu He <lin...@google.com>
    Gerrit-Reviewer: Shu Yang <shu...@google.com>
    Gerrit-CC: Charles Hager <clh...@google.com>
    Gerrit-CC: Mark Schillaci <mschi...@google.com>
    Gerrit-CC: Moe Adel <ad...@google.com>
    Gerrit-CC: Sinan Sahin <sinan...@google.com>
    Gerrit-Attention: Shu Yang <shu...@google.com>
    Gerrit-Comment-Date: Wed, 01 Jul 2026 23:33:45 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    satisfied_requirement
    unsatisfied_requirement
    open
    diffy

    Shu Yang (Gerrit)

    unread,
    Jul 1, 2026, 9:14:17 PM (7 days ago) Jul 1
    to Linyu He, Chromium LUCI CQ, Moe Adel, chromium...@chromium.org, Charles Hager, Mark Schillaci, Sinan Sahin, nemco...@google.com
    Attention needed from Linyu He

    Shu Yang voted and added 4 comments

    Votes added by Shu Yang

    Commit-Queue+1

    4 comments

    File chrome/browser/ui/side_panel_container/internal/BUILD.gn
    Line 129, Patchset 8: "//components/thin_webview:java",
    Linyu He . resolved

    Not needed? (`SidePanelContainerCoordinatorImplUnitTest` isn't changed.)

    Shu Yang

    Done

    File chrome/browser/ui/side_panel_container/internal/android/java/src/org/chromium/chrome/browser/ui/side_panel_container/SidePanelContainerCoordinatorImpl.java
    Line 152, Patchset 8: View oldView = mCurrentContent != null ? mCurrentContent.mView : null;
    Linyu He . resolved

    Per offline discussion, we'll move the `runOnNextFrame()` logic to `startReplacingPanelContent()` (the method is added in http://crrev.com/c/8028149 and targets the "replace content" case).

    Shu Yang

    Done

    Line 157, Patchset 8: Runnable removeOldViewRunnable =
    Linyu He . resolved

    Q: Will this CL handle rapid tab switches, i.e., `removeOldViewRunnable` doesn't have time to run before the side panel content is replaced again?

    One solution can be to keep `removeOldViewRunnable` as a mutable state and set it to null at the end of `runOnNextFrame()`.

    If it's not null before `runOnNextFrame()`, immediately run it and replace its value with the new runnable.

    Shu Yang

    Thanks for the suggestion! Updated.
    Also added a guard flag inside the runnable to ensure it doesn't execute its logic again when the next frame eventually fires after we've already run it early.

    Line 179, Patchset 8: private @Nullable ThinWebView findThinWebView(View view) {
    Linyu He . resolved

    nit: let's move this private method to the end of the class so all public methods stay together.

    Shu Yang

    Done

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Linyu He
    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: Id08144de3f941e930956f4932f2f5b4e257a3907
      Gerrit-Change-Number: 7962991
      Gerrit-PatchSet: 13
      Gerrit-Owner: Shu Yang <shu...@google.com>
      Gerrit-Reviewer: Linyu He <lin...@google.com>
      Gerrit-Reviewer: Shu Yang <shu...@google.com>
      Gerrit-CC: Charles Hager <clh...@google.com>
      Gerrit-CC: Mark Schillaci <mschi...@google.com>
      Gerrit-CC: Moe Adel <ad...@google.com>
      Gerrit-CC: Sinan Sahin <sinan...@google.com>
      Gerrit-Attention: Linyu He <lin...@google.com>
      Gerrit-Comment-Date: Thu, 02 Jul 2026 01:14:04 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: Yes
      Comment-In-Reply-To: Linyu He <lin...@google.com>
      satisfied_requirement
      unsatisfied_requirement
      open
      diffy

      Shu Yang (Gerrit)

      unread,
      Jul 1, 2026, 11:05:39 PM (7 days ago) Jul 1
      to Linyu He, Chromium LUCI CQ, Moe Adel, chromium...@chromium.org, Charles Hager, Mark Schillaci, Sinan Sahin, nemco...@google.com
      Attention needed from Linyu He

      Shu Yang added 1 comment

      Patchset-level comments
      File-level comment, Patchset 13 (Latest):
      Shu Yang . resolved

      The test failure in MediaNotificationServiceLifecycleTest is not caused by this change.

      Gerrit-Comment-Date: Thu, 02 Jul 2026 03:05:26 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: No
      satisfied_requirement
      unsatisfied_requirement
      open
      diffy

      Linyu He (Gerrit)

      unread,
      Jul 5, 2026, 9:23:33 PM (3 days ago) Jul 5
      to Shu Yang, Chromium LUCI CQ, Moe Adel, chromium...@chromium.org, Charles Hager, Mark Schillaci, Sinan Sahin, nemco...@google.com
      Attention needed from Shu Yang

      Linyu He added 8 comments

      Commit Message
      Line 24, Patchset 13 (Latest):Include-Ci-Only-Tests: true
      Linyu He . unresolved

      To run side panel tests in CQ, we can add:

      ```
      Cq-Include-Trybots: luci.chromium.try:android-desktop-x64-rel
      Include-Ci-Only-Tests: true
      ```

      File chrome/browser/ui/side_panel_container/internal/android/java/src/org/chromium/chrome/browser/ui/side_panel_container/SidePanelContainerCoordinatorImpl.java
      Line 56, Patchset 13 (Latest): private @Nullable Runnable mPendingReplaceRunnable;
      Linyu He . unresolved

      nit: need javadoc for `mPendingReplaceRunnable`
      ```suggestion

          /**
      * {@link Runnable} for {@link #startReplacingPanelContent} to remove the old content View.
      */
      private @Nullable Runnable mPendingReplaceRunnable;
      ```
      Line 176, Patchset 13 (Latest): if (mPendingReplaceRunnable != null) {
      mPendingReplaceRunnable.run();
      }
      Linyu He . unresolved

      Can we set `mPendingReplaceRunnable` to `null` after Line 177? It should always be `null` after it's run.

      ```suggestion
      if (mPendingReplaceRunnable != null) {
      mPendingReplaceRunnable.run();
      mPendingRepalceRunnable = null;
      }
      ```
      Line 180, Patchset 13 (Latest): View oldView = mCurrentContent != null ? mCurrentContent.mView : null;
      Linyu He . unresolved

      For replacing panel content, `oldView` must be non-null, so:

      ```suggestion
      assert mCurrentContent != null : "no content to replace";
      View oldView = mCurrentContent.mView;
      ```
      Line 184, Patchset 13 (Latest):
      // We use a custom Runnable class with a `mRan` flag because ThinWebView's runOnNextFrame()
      // does not support cancellation. If a new content replacement happens before the next
      // frame renders, we immediately run this runnable to clean up the old state. When the
      // next frame eventually fires, the guard flag prevents running the cleanup logic again
      // (which would cause duplicate JNI callbacks and native crashes).
      Linyu He . unresolved

      For this comment, I think it's also important to explain why `ThinWebView#runOnNextFrame()` needs a `Runnable` reference separate from `mPendingReplaceRunnable`, i.e., why `removeOldViewRunnable` and `mPendingReplaceRunnable` may point to different instances and cause bugs.

      Line 197, Patchset 13 (Latest): mRan = true;

      if (mPendingReplaceRunnable == this) {
      mPendingReplaceRunnable = null;
      }
      Linyu He . unresolved

      nit - it's more intuitive to place Line 197 ~ Line 201 at the end of `run()`.

      ```
      @Override
      public void run() {
      if (mRan) return;

      // Do work...

      // Now the method is run.
      mRan = true;
      if (mPendingReplaceRunnable == this) {
      mPendingReplaceRunnable = null;
      }
      }
      ```
      Line 202, Patchset 13 (Latest): if (oldView != null && oldView != newContent.mView) {
      mContainerView.removeView(oldView);
      }
      Linyu He . unresolved

      I think we can remove Line 202.
      (1) `oldView` should always be non-null.
      (2) If new View is the same as `oldView`, `addView()` at Line 183 will crash.

      Line 212, Patchset 13 (Latest): if (thinWebView != null) {
      thinWebView.runOnNextFrame(removeOldViewRunnable);
      } else {
      removeOldViewRunnable.run();
      }
      Linyu He . unresolved

      (1) Let's keep `BuildConfig.IS_FOR_TEST`.
      (2) We should set `mPendingReplaceRunnable` to `null` if the old View is synchronously removed.

      ```suggestion
      if (thinWebView == null || BuildConfig.IS_FOR_TEST) {
      mPendingReplaceRunnable.run();
      mPendingReplaceRunnable = null;
      } else {
      thinWebView.runOnNextFrame(removeOldViewRunnable);
      }
      ```
      Open in Gerrit

      Related details

      Attention is currently required from:
      • Shu Yang
      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: Id08144de3f941e930956f4932f2f5b4e257a3907
        Gerrit-Change-Number: 7962991
        Gerrit-PatchSet: 13
        Gerrit-Owner: Shu Yang <shu...@google.com>
        Gerrit-Reviewer: Linyu He <lin...@google.com>
        Gerrit-Reviewer: Shu Yang <shu...@google.com>
        Gerrit-CC: Charles Hager <clh...@google.com>
        Gerrit-CC: Mark Schillaci <mschi...@google.com>
        Gerrit-CC: Moe Adel <ad...@google.com>
        Gerrit-CC: Sinan Sahin <sinan...@google.com>
        Gerrit-Attention: Shu Yang <shu...@google.com>
        Gerrit-Comment-Date: Mon, 06 Jul 2026 01:23:19 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Shu Yang (Gerrit)

        unread,
        Jul 7, 2026, 10:05:08 PM (23 hours ago) Jul 7
        to Linyu He, Chromium LUCI CQ, Moe Adel, chromium...@chromium.org, Charles Hager, Mark Schillaci, Sinan Sahin, nemco...@google.com
        Attention needed from Linyu He

        Shu Yang added 8 comments

        Commit Message
        Line 24, Patchset 13:Include-Ci-Only-Tests: true
        Linyu He . resolved

        To run side panel tests in CQ, we can add:

        ```
        Cq-Include-Trybots: luci.chromium.try:android-desktop-x64-rel
        Include-Ci-Only-Tests: true
        ```

        Shu Yang

        Done

        File chrome/browser/ui/side_panel_container/internal/android/java/src/org/chromium/chrome/browser/ui/side_panel_container/SidePanelContainerCoordinatorImpl.java
        Line 56, Patchset 13: private @Nullable Runnable mPendingReplaceRunnable;
        Linyu He . resolved

        nit: need javadoc for `mPendingReplaceRunnable`
        ```suggestion

            /**
        * {@link Runnable} for {@link #startReplacingPanelContent} to remove the old content View.
        */
        private @Nullable Runnable mPendingReplaceRunnable;
        ```
        Shu Yang

        Done

        Line 176, Patchset 13: if (mPendingReplaceRunnable != null) {
        mPendingReplaceRunnable.run();
        }
        Linyu He . unresolved

        Can we set `mPendingReplaceRunnable` to `null` after Line 177? It should always be `null` after it's run.

        ```suggestion
        if (mPendingReplaceRunnable != null) {
        mPendingReplaceRunnable.run();
        mPendingRepalceRunnable = null;
        }
        ```
        Shu Yang

        The code currently handles setting mPendingReplaceRunnable to null inside the runnable's run() method itself.
        This check is important because we only want to set mPendingReplaceRunnable to null if it still points to the runnable that is currently executing. If a newer replacement has occurred, mPendingReplaceRunnable will have been updated to the newer runnable, and we shouldn't clear it.
        If we explicitly set it to null in startReplacingPanelContent like this, it is redundant because calling mPendingReplaceRunnable.run() will set it to null anyway.
        Do you still prefer to add it explicitly for readability, or are you comfortable keeping the current implementation since it already handles nulling it out safely?

        Line 180, Patchset 13: View oldView = mCurrentContent != null ? mCurrentContent.mView : null;
        Linyu He . resolved

        For replacing panel content, `oldView` must be non-null, so:

        ```suggestion
        assert mCurrentContent != null : "no content to replace";
        View oldView = mCurrentContent.mView;
        ```

        // We use a custom Runnable class with a `mRan` flag because ThinWebView's runOnNextFrame()
        // does not support cancellation. If a new content replacement happens before the next
        // frame renders, we immediately run this runnable to clean up the old state. When the
        // next frame eventually fires, the guard flag prevents running the cleanup logic again
        // (which would cause duplicate JNI callbacks and native crashes).
        Linyu He . resolved

        For this comment, I think it's also important to explain why `ThinWebView#runOnNextFrame()` needs a `Runnable` reference separate from `mPendingReplaceRunnable`, i.e., why `removeOldViewRunnable` and `mPendingReplaceRunnable` may point to different instances and cause bugs.

        Shu Yang

        Done



        if (mPendingReplaceRunnable == this) {
        mPendingReplaceRunnable = null;
        }
        Linyu He . unresolved

        nit - it's more intuitive to place Line 197 ~ Line 201 at the end of `run()`.

        ```
        @Override
        public void run() {
        if (mRan) return;

        // Do work...

        // Now the method is run.
        mRan = true;
        if (mPendingReplaceRunnable == this) {
        mPendingReplaceRunnable = null;
        }
        }
        ```
        Shu Yang

        I understand that placing it at the end feels more intuitive logically, but keeping mRan = true at the beginning of run() is a defensive programming practice to prevent re-entrancy bugs.

        If onPanelContentReplaced.run() (which calls into native C++ via JNI) synchronously triggers another content replacement:

        1. The new replacement request would call mPendingReplaceRunnable.run() (which is still the current runnable).
        2. If mRan is not yet true, the current runnable would execute again recursively, leading to duplicate JNI callbacks and potential crashes.

        By setting mRan = true at the very beginning, we guarantee that any synchronous re-entrant calls to run() will return immediately.

        Given this, would you still prefer to move it to the end, or should we keep it at the beginning for re-entrancy protection?

        Line 202, Patchset 13: if (oldView != null && oldView != newContent.mView) {
        mContainerView.removeView(oldView);
        }
        Linyu He . resolved

        I think we can remove Line 202.
        (1) `oldView` should always be non-null.
        (2) If new View is the same as `oldView`, `addView()` at Line 183 will crash.

        Shu Yang

        Good catch!
        Done.

        Line 212, Patchset 13: if (thinWebView != null) {
        thinWebView.runOnNextFrame(removeOldViewRunnable);
        } else {
        removeOldViewRunnable.run();
        }
        Linyu He . unresolved

        (1) Let's keep `BuildConfig.IS_FOR_TEST`.
        (2) We should set `mPendingReplaceRunnable` to `null` if the old View is synchronously removed.

        ```suggestion
        if (thinWebView == null || BuildConfig.IS_FOR_TEST) {
        mPendingReplaceRunnable.run();
        mPendingReplaceRunnable = null;
        } else {
        thinWebView.runOnNextFrame(removeOldViewRunnable);
        }
        ```
        Shu Yang

        (1) Done

        (2) Calling mPendingReplaceRunnable.run() executes the custom Runnable we just created, which checks if (mPendingReplaceRunnable == this) and sets it to null internally. Since it is still pointing to the same runnable instance, it will already be set to null by the time run() returns.
        Do you still prefer to add it explicitly for readabilit?

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Linyu He
        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: Id08144de3f941e930956f4932f2f5b4e257a3907
        Gerrit-Change-Number: 7962991
        Gerrit-PatchSet: 15
        Gerrit-Owner: Shu Yang <shu...@google.com>
        Gerrit-Reviewer: Linyu He <lin...@google.com>
        Gerrit-Reviewer: Shu Yang <shu...@google.com>
        Gerrit-CC: Charles Hager <clh...@google.com>
        Gerrit-CC: Mark Schillaci <mschi...@google.com>
        Gerrit-CC: Moe Adel <ad...@google.com>
        Gerrit-CC: Sinan Sahin <sinan...@google.com>
        Gerrit-Attention: Linyu He <lin...@google.com>
        Gerrit-Comment-Date: Wed, 08 Jul 2026 02:04:52 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        Comment-In-Reply-To: Linyu He <lin...@google.com>
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Linyu He (Gerrit)

        unread,
        4:02 AM (17 hours ago) 4:02 AM
        to Shu Yang, Chromium LUCI CQ, Moe Adel, chromium...@chromium.org, Charles Hager, Mark Schillaci, Sinan Sahin, nemco...@google.com
        Attention needed from Shu Yang

        Linyu He added 3 comments

        File chrome/browser/ui/side_panel_container/internal/android/java/src/org/chromium/chrome/browser/ui/side_panel_container/SidePanelContainerCoordinatorImpl.java
        Line 176, Patchset 13: if (mPendingReplaceRunnable != null) {
        mPendingReplaceRunnable.run();
        }
        Linyu He . unresolved

        Can we set `mPendingReplaceRunnable` to `null` after Line 177? It should always be `null` after it's run.

        ```suggestion
        if (mPendingReplaceRunnable != null) {
        mPendingReplaceRunnable.run();
        mPendingRepalceRunnable = null;
        }
        ```
        Shu Yang

        The code currently handles setting mPendingReplaceRunnable to null inside the runnable's run() method itself.
        This check is important because we only want to set mPendingReplaceRunnable to null if it still points to the runnable that is currently executing. If a newer replacement has occurred, mPendingReplaceRunnable will have been updated to the newer runnable, and we shouldn't clear it.
        If we explicitly set it to null in startReplacingPanelContent like this, it is redundant because calling mPendingReplaceRunnable.run() will set it to null anyway.
        Do you still prefer to add it explicitly for readability, or are you comfortable keeping the current implementation since it already handles nulling it out safely?

        Linyu He

        "`mPendingReplaceRunnable.run()` will set `mPendingReplaceRunnable` to null" is quite unusual since usually dereferencing a variable won't make it null.

        I did try to suggest refactoring the code to avoid this unusual behavior, but it seemed hard.

        So we need to make it easier to understand when `mPendingReplaceRunnable` is cleared. We can either explicitly set it to null or add a comment explaining that `mPendingReplaceRunnable.run()` will set `mPendingReplaceRunnable` to null.

        Line 197, Patchset 13: mRan = true;

        if (mPendingReplaceRunnable == this) {
        mPendingReplaceRunnable = null;
        }
        Linyu He . unresolved

        nit - it's more intuitive to place Line 197 ~ Line 201 at the end of `run()`.

        ```
        @Override
        public void run() {
        if (mRan) return;

        // Do work...

        // Now the method is run.
        mRan = true;
        if (mPendingReplaceRunnable == this) {
        mPendingReplaceRunnable = null;
        }
        }
        ```
        Shu Yang

        I understand that placing it at the end feels more intuitive logically, but keeping mRan = true at the beginning of run() is a defensive programming practice to prevent re-entrancy bugs.

        If onPanelContentReplaced.run() (which calls into native C++ via JNI) synchronously triggers another content replacement:

        1. The new replacement request would call mPendingReplaceRunnable.run() (which is still the current runnable).
        2. If mRan is not yet true, the current runnable would execute again recursively, leading to duplicate JNI callbacks and potential crashes.

        By setting mRan = true at the very beginning, we guarantee that any synchronous re-entrant calls to run() will return immediately.

        Given this, would you still prefer to move it to the end, or should we keep it at the beginning for re-entrancy protection?

        Linyu He

        keeping mRan = true at the beginning of run() is a defensive programming practice to prevent re-entrancy bugs.

        SG, but please add comments to explain this is for preventing re-entrancy bugs so that no future CLs will move `mRan = true`.

        For the `mPendingReplaceRunnable` logic, can we still moved it to the end of the method?

        ```
        if (mRan) return;

        // Immediately set mRun to true to prevent reentrancy.
        mRan = true;

        // Do work.

        // If the work is for the current runnable, clear the runnable.

        if (mPendingReplaceRunnable == this) {
        mPendingReplaceRunnable = null;
        }
        ```
        Line 212, Patchset 13: if (thinWebView != null) {
        thinWebView.runOnNextFrame(removeOldViewRunnable);
        } else {
        removeOldViewRunnable.run();
        }
        Linyu He . unresolved

        (1) Let's keep `BuildConfig.IS_FOR_TEST`.
        (2) We should set `mPendingReplaceRunnable` to `null` if the old View is synchronously removed.

        ```suggestion
        if (thinWebView == null || BuildConfig.IS_FOR_TEST) {
        mPendingReplaceRunnable.run();
        mPendingReplaceRunnable = null;
        } else {
        thinWebView.runOnNextFrame(removeOldViewRunnable);
        }
        ```
        Shu Yang

        (1) Done

        (2) Calling mPendingReplaceRunnable.run() executes the custom Runnable we just created, which checks if (mPendingReplaceRunnable == this) and sets it to null internally. Since it is still pointing to the same runnable instance, it will already be set to null by the time run() returns.
        Do you still prefer to add it explicitly for readabilit?

        Attention is currently required from:
        • Shu Yang
        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: Id08144de3f941e930956f4932f2f5b4e257a3907
        Gerrit-Change-Number: 7962991
        Gerrit-PatchSet: 15
        Gerrit-Owner: Shu Yang <shu...@google.com>
        Gerrit-Reviewer: Linyu He <lin...@google.com>
        Gerrit-Reviewer: Shu Yang <shu...@google.com>
        Gerrit-CC: Charles Hager <clh...@google.com>
        Gerrit-CC: Mark Schillaci <mschi...@google.com>
        Gerrit-CC: Moe Adel <ad...@google.com>
        Gerrit-CC: Sinan Sahin <sinan...@google.com>
        Gerrit-Attention: Shu Yang <shu...@google.com>
        Gerrit-Comment-Date: Wed, 08 Jul 2026 08:01:51 +0000
        Gerrit-HasComments: Yes
        Gerrit-Has-Labels: No
        Comment-In-Reply-To: Shu Yang <shu...@google.com>
        Comment-In-Reply-To: Linyu He <lin...@google.com>
        satisfied_requirement
        unsatisfied_requirement
        open
        diffy

        Shu Yang (Gerrit)

        unread,
        6:20 PM (3 hours ago) 6:20 PM
        to Linyu He, Chromium LUCI CQ, Moe Adel, chromium...@chromium.org, Charles Hager, Mark Schillaci, Sinan Sahin, nemco...@google.com
        Attention needed from Linyu He

        Shu Yang added 3 comments

        File chrome/browser/ui/side_panel_container/internal/android/java/src/org/chromium/chrome/browser/ui/side_panel_container/SidePanelContainerCoordinatorImpl.java
        Line 176, Patchset 13: if (mPendingReplaceRunnable != null) {
        mPendingReplaceRunnable.run();
        }
        Linyu He . resolved

        Can we set `mPendingReplaceRunnable` to `null` after Line 177? It should always be `null` after it's run.

        ```suggestion
        if (mPendingReplaceRunnable != null) {
        mPendingReplaceRunnable.run();
        mPendingRepalceRunnable = null;
        }
        ```
        Shu Yang

        The code currently handles setting mPendingReplaceRunnable to null inside the runnable's run() method itself.
        This check is important because we only want to set mPendingReplaceRunnable to null if it still points to the runnable that is currently executing. If a newer replacement has occurred, mPendingReplaceRunnable will have been updated to the newer runnable, and we shouldn't clear it.
        If we explicitly set it to null in startReplacingPanelContent like this, it is redundant because calling mPendingReplaceRunnable.run() will set it to null anyway.
        Do you still prefer to add it explicitly for readability, or are you comfortable keeping the current implementation since it already handles nulling it out safely?

        Linyu He

        "`mPendingReplaceRunnable.run()` will set `mPendingReplaceRunnable` to null" is quite unusual since usually dereferencing a variable won't make it null.

        I did try to suggest refactoring the code to avoid this unusual behavior, but it seemed hard.

        So we need to make it easier to understand when `mPendingReplaceRunnable` is cleared. We can either explicitly set it to null or add a comment explaining that `mPendingReplaceRunnable.run()` will set `mPendingReplaceRunnable` to null.

        Shu Yang

        sg! Now I explicitly set mPendingReplaceRunnable = null in the synchronous block, while adding a comment explaining that this is done for readability even though it's technically redundant.

        Line 197, Patchset 13: mRan = true;

        if (mPendingReplaceRunnable == this) {
        mPendingReplaceRunnable = null;
        }
        Linyu He . resolved
        Shu Yang

        Yes we should be able to move the mPendingReplaceRunnable logic to the end of the method.

        Line 212, Patchset 13: if (thinWebView != null) {
        thinWebView.runOnNextFrame(removeOldViewRunnable);
        } else {
        removeOldViewRunnable.run();
        }
        Linyu He . resolved

        (1) Let's keep `BuildConfig.IS_FOR_TEST`.
        (2) We should set `mPendingReplaceRunnable` to `null` if the old View is synchronously removed.

        ```suggestion
        if (thinWebView == null || BuildConfig.IS_FOR_TEST) {
        mPendingReplaceRunnable.run();
        mPendingReplaceRunnable = null;
        } else {
        thinWebView.runOnNextFrame(removeOldViewRunnable);
        }
        ```
        Shu Yang

        (1) Done

        (2) Calling mPendingReplaceRunnable.run() executes the custom Runnable we just created, which checks if (mPendingReplaceRunnable == this) and sets it to null internally. Since it is still pointing to the same runnable instance, it will already be set to null by the time run() returns.
        Do you still prefer to add it explicitly for readabilit?

        Linyu He

        For (2), please see this comment: https://chromium-review.git.corp.google.com/c/chromium/src/+/7962991/comment/5ede631a_ef8ce103/

        Shu Yang

        Done

        Open in Gerrit

        Related details

        Attention is currently required from:
        • Linyu He
        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: Id08144de3f941e930956f4932f2f5b4e257a3907
          Gerrit-Change-Number: 7962991
          Gerrit-PatchSet: 16
          Gerrit-Owner: Shu Yang <shu...@google.com>
          Gerrit-Reviewer: Linyu He <lin...@google.com>
          Gerrit-Reviewer: Shu Yang <shu...@google.com>
          Gerrit-CC: Charles Hager <clh...@google.com>
          Gerrit-CC: Mark Schillaci <mschi...@google.com>
          Gerrit-CC: Moe Adel <ad...@google.com>
          Gerrit-CC: Sinan Sahin <sinan...@google.com>
          Gerrit-Attention: Linyu He <lin...@google.com>
          Gerrit-Comment-Date: Wed, 08 Jul 2026 22:19:59 +0000
          satisfied_requirement
          unsatisfied_requirement
          open
          diffy

          Linyu He (Gerrit)

          unread,
          6:27 PM (3 hours ago) 6:27 PM
          to Shu Yang, Chromium LUCI CQ, Moe Adel, chromium...@chromium.org, Charles Hager, Mark Schillaci, Sinan Sahin, nemco...@google.com
          Attention needed from Shu Yang

          Linyu He voted and added 1 comment

          Votes added by Linyu He

          Code-Review+1

          1 comment

          Patchset-level comments
          Open in Gerrit

          Related details

          Attention is currently required from:
          • Shu Yang
          Submit Requirements:
          • requirement satisfiedCode-Coverage
          • requirement satisfiedCode-Owners
          • requirement satisfiedCode-Review
          • requirement 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: Id08144de3f941e930956f4932f2f5b4e257a3907
          Gerrit-Change-Number: 7962991
          Gerrit-PatchSet: 16
          Gerrit-Owner: Shu Yang <shu...@google.com>
          Gerrit-Reviewer: Linyu He <lin...@google.com>
          Gerrit-Reviewer: Shu Yang <shu...@google.com>
          Gerrit-CC: Charles Hager <clh...@google.com>
          Gerrit-CC: Mark Schillaci <mschi...@google.com>
          Gerrit-CC: Moe Adel <ad...@google.com>
          Gerrit-CC: Sinan Sahin <sinan...@google.com>
          Gerrit-Attention: Shu Yang <shu...@google.com>
          Gerrit-Comment-Date: Wed, 08 Jul 2026 22:27:29 +0000
          Gerrit-HasComments: Yes
          Gerrit-Has-Labels: Yes
          satisfied_requirement
          open
          diffy

          Shu Yang (Gerrit)

          unread,
          6:34 PM (3 hours ago) 6:34 PM
          to Linyu He, Chromium LUCI CQ, Moe Adel, chromium...@chromium.org, Charles Hager, Mark Schillaci, Sinan Sahin, nemco...@google.com

          Shu Yang voted Commit-Queue+2

          Commit-Queue+2
          Open in Gerrit

          Related details

          Attention set is empty
          Submit Requirements:
          • requirement satisfiedCode-Coverage
          • requirement satisfiedCode-Owners
          • requirement satisfiedCode-Review
          • requirement 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: Id08144de3f941e930956f4932f2f5b4e257a3907
          Gerrit-Change-Number: 7962991
          Gerrit-PatchSet: 16
          Gerrit-Owner: Shu Yang <shu...@google.com>
          Gerrit-Reviewer: Linyu He <lin...@google.com>
          Gerrit-Reviewer: Shu Yang <shu...@google.com>
          Gerrit-CC: Charles Hager <clh...@google.com>
          Gerrit-CC: Mark Schillaci <mschi...@google.com>
          Gerrit-CC: Moe Adel <ad...@google.com>
          Gerrit-CC: Sinan Sahin <sinan...@google.com>
          Gerrit-Comment-Date: Wed, 08 Jul 2026 22:34:29 +0000
          Gerrit-HasComments: No
          Gerrit-Has-Labels: Yes
          satisfied_requirement
          open
          diffy
          Reply all
          Reply to author
          Forward
          0 new messages