Refactor SubAppsInstallDialogController to separate view and logic [chromium/src : main]

0 views
Skip to first unread message

Edman Anjos (Gerrit)

unread,
Jan 9, 2026, 11:31:47 AM (3 days ago) Jan 9
to Edman Anjos, Andrew Rayskiy, Dibyajyoti Pal, Chromium LUCI CQ, chromium...@chromium.org, Simon Hangl, dibyapal+wa...@chromium.org, dmurph+watc...@chromium.org, giovax...@chromium.org, kuragin+web-ap...@chromium.org, loyso...@chromium.org, mgiuca...@chromium.org, philli...@chromium.org, rmcelra...@chromium.org, webap...@microsoft.com, zelin+watch-we...@chromium.org
Attention needed from Andrew Rayskiy and Dibyajyoti Pal

Edman Anjos added 9 comments

Patchset-level comments
File-level comment, Patchset 3 (Latest):
Edman Anjos . resolved

fixed the comments, PTAL

File chrome/browser/ui/web_applications/sub_apps_install_dialog_controller.cc
Line 241, Patchset 1: controller->callback_ = std::move(callback);
Andrew Rayskiy . resolved

Pass to the constructor directly?

Edman Anjos

Done

Line 251, Patchset 1:views::Widget* SubAppsInstallDialogController::CreateWidget(
Andrew Rayskiy . resolved

Can't we switch to `constrained_window::ShowWebModalDialogViewsOwned()`? It will make the stored `raw_ptr` a bit less ugly and b) automatically destroy the widget once `AddCallInfo` goes out of scope?

Edman Anjos

Changed to be tab owned

Line 274, Patchset 1: base::Unretained(controller_ptr)),
Dibyajyoti Pal . resolved

Can you share why it's safe to use `base::Unretained()` here? Can we use `base::WeakPtr` instead?

Edman Anjos

Done, changed to weak_ptr.

My rationale in that PS was that the widget owns the controller, but I'm not entirely sure I missed something. The close flow of dialogs looks complicated.

Line 280, Patchset 1: .SetDialogDestroyingCallback(base::BindOnce(
Andrew Rayskiy . unresolved

It might be a bit clearer to inherit `SubAppsInstallDialogController` from `ui::DialogModelDelegate` and pass it to `ui::DialogModel::Builder()` so that all closures use the same unretained ptr.

Or, given that this looks like a tab-scoped dialog, rely on [tab_dialog_manager](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/views/web_apps/protocol_handler_picker_coordinator.cc;l=225-226;drc=4641a043d7914ce31515751c357158d46f4c937a;bpv=0;bpt=1)

Dibyajyoti Pal

This, or it might also be nice to completely move the `views` specific logic to a separate entity (like a function of some sorts), so that the controller and view is separated, as [said here](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/chrome_browser_design_principles.md#:~:text=For%20simple%20features%20that%20do%20not%20require%20data%20persistence%2C%20we%20only%20require%20separation%20of%20controller%20from%20view.).

We do this for all web app related dialogs. We have the [controller](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/views/web_apps/web_app_modal_dialog_delegate.h), and then the views are separate, like [this dialog](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/views/web_apps/web_app_simple_install_dialog.cc).

Regarding the use of `tab_dialog_manager`, from the comments above `SubAppsInstallDialogController::Show()`, it seems like it is a browser modal dialog? In that case, `CreateBrowserModalDialogViews()` SGTM.

Edman Anjos

I split the view/controller, and IMO the result is much better. Thanks a lot for the pointers to `web_app_simple_install_dialog`, very helpful!

I'm not sure about the `CreateBrowserModalDialogViews` vs `ShowWebModalDialogViews` choice. I think it should be a tab modal dialog. IWAs don't open in a tab, but I'm not entirely sure what should happen to the dialog e.g. on navigation. If we surface subapps to PWAs, then probably tab modal makes more sense?

I'm currently using `ShowWebModalDialogViews`. Let me know what you think.

Line 294, Patchset 1: if (callback_) {
Andrew Rayskiy . resolved

I'd `CHECK()` here and elsewhere? There's also a magic thing called `Widget::MakeCloseSynchronous()`, but I don't remember what exactly it does

Edman Anjos

Done, added CHECK in OnAccept.

There's also a magic thing called Widget::MakeCloseSynchronous(), but I don't remember what exactly it does

It's not clear to me what to do with this. If this is still relevant, can you please phrase the comment in an actionable way?

Line 305, Patchset 1:void SubAppsInstallDialogController::OnClose() {
Dibyajyoti Pal . resolved

I believe the dialog destroying callback will also be called when the dialog is being canceled.

Dibyajyoti Pal

Having `base::Unretained()` in the callbacks here make me scared that since the controller gets destroyed on cancelling it (as it's lifetime is tied to a widget), the dialog destruction callback runs again, and tries to access `callback_`. Won't that create reentrancy issues?

It's weird that the `DialogCancelled` test does not crash here, I wonder if there are better semantics of widget ownership that can be used here. Maybe `AddCallInfo` can store a unique pointer to the widget so that the order of destruction can be controlled with the more fine-grained `MakeCloseSynchronous()` [1]?

[1] https://source.chromium.org/chromium/chromium/src/+/main:ui/views/widget/widget.h;l=876-902?q=views::Widget&ss=chromium%2Fchromium%2Fsrc

Edman Anjos

Done, changed to weak ptrs.

My understanding of the call ordering here is the callback passed to `SetDialogDestroyingCallback` is called before the widget is destroyed, not after. So the controller should still be alive at that point.

Line 299, Patchset 1:void SubAppsInstallDialogController::OnCancel() {
if (callback_) {
std::move(callback_).Run(false);
}
}

void SubAppsInstallDialogController::OnClose() {
if (callback_) {
std::move(callback_).Run(false);
}
}
Dibyajyoti Pal . resolved

If both of them are doing the same thing, can we instead make this one function (say `SubAppsIntallDialogController::OnClose()` and use it for both the callback in `AddCancelButton()` and `SetDialogDestroyingCallback()`?

Edman Anjos

Done

There is the slight difference that `OnCancel` is called at most once and `callback_` must be valid, while `OnClose` is called at least once and `callback_` may be invalid.

I unified this in `OnClose` and tried to explain this in a comment.

File chrome/browser/ui/web_applications/sub_apps_service_impl.cc
Line 210, Patchset 1: install_dialog_widget->CloseWithReason(
Dibyajyoti Pal . resolved

Hmmm, Gemini pointed out an edge case that might be happening here.

If this is being called when `AddCallInfo` is being destructed, the callback being run as part of destruction is `SubAppsServiceImpl::ProcessDialogResponse()`. Will the `CHECK_DEREF` call fail in that case?

Dibyajyoti Pal

Asynchronous widget closing can be a massive problem with reentrancy issues, can we instead use `MakeCloseSynchronous()` [1], and store an `unique_ptr<>` to the widget here so that the lifetime is more properly controlled? It seems like `CloseWithReason()` is not suggested anymmore to be used [2].

[1] https://source.chromium.org/chromium/chromium/src/+/main:ui/views/widget/widget.h;l=876-902;bpv=1;bpt=1?q=views::Widget&ss=chromium%2Fchromium%2Fsrc
[2] https://source.chromium.org/chromium/chromium/src/+/main:ui/views/widget/widget.h;l=866-868;bpv=1;bpt=1?q=views::Widget&ss=chromium%2Fchromium%2Fsrc

Edman Anjos

I've change the ownership of the widget from `AddCallInfo` to being a tab modal. I believe that fixes the reentrancy concerns.

Open in Gerrit

Related details

Attention is currently required from:
  • Andrew Rayskiy
  • Dibyajyoti Pal
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: Ic9c6f7128d74055cfab3d6334caea59a83c992cd
Gerrit-Change-Number: 7415787
Gerrit-PatchSet: 3
Gerrit-Owner: Edman Anjos <ed...@chromium.org>
Gerrit-Reviewer: Andrew Rayskiy <green...@google.com>
Gerrit-Reviewer: Dibyajyoti Pal <diby...@chromium.org>
Gerrit-Reviewer: Edman Anjos <ed...@chromium.org>
Gerrit-CC: Simon Hangl <sim...@google.com>
Gerrit-Attention: Dibyajyoti Pal <diby...@chromium.org>
Gerrit-Attention: Andrew Rayskiy <green...@google.com>
Gerrit-Comment-Date: Fri, 09 Jan 2026 16:31:32 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: Dibyajyoti Pal <diby...@chromium.org>
Comment-In-Reply-To: Andrew Rayskiy <green...@google.com>
satisfied_requirement
unsatisfied_requirement
open
diffy

Andrew Rayskiy (Gerrit)

unread,
Jan 9, 2026, 11:56:00 AM (3 days ago) Jan 9
to Edman Anjos, Dibyajyoti Pal, Chromium LUCI CQ, chromium...@chromium.org, Simon Hangl, dibyapal+wa...@chromium.org, dmurph+watc...@chromium.org, giovax...@chromium.org, kuragin+web-ap...@chromium.org, loyso...@chromium.org, mgiuca...@chromium.org, philli...@chromium.org, rmcelra...@chromium.org, webap...@microsoft.com, zelin+watch-we...@chromium.org
Attention needed from Dibyajyoti Pal and Edman Anjos

Andrew Rayskiy voted Code-Review+1

Code-Review+1
Open in Gerrit

Related details

Attention is currently required from:
  • Dibyajyoti Pal
  • Edman Anjos
Submit Requirements:
    • requirement satisfiedCode-Coverage
    • requirement is not satisfiedCode-Owners
    • requirement satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • 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: Ic9c6f7128d74055cfab3d6334caea59a83c992cd
    Gerrit-Change-Number: 7415787
    Gerrit-PatchSet: 3
    Gerrit-Owner: Edman Anjos <ed...@chromium.org>
    Gerrit-Reviewer: Andrew Rayskiy <green...@google.com>
    Gerrit-Reviewer: Dibyajyoti Pal <diby...@chromium.org>
    Gerrit-Reviewer: Edman Anjos <ed...@chromium.org>
    Gerrit-CC: Simon Hangl <sim...@google.com>
    Gerrit-Attention: Dibyajyoti Pal <diby...@chromium.org>
    Gerrit-Attention: Edman Anjos <ed...@chromium.org>
    Gerrit-Comment-Date: Fri, 09 Jan 2026 16:55:42 +0000
    Gerrit-HasComments: No
    Gerrit-Has-Labels: Yes
    satisfied_requirement
    unsatisfied_requirement
    open
    diffy

    Dibyajyoti Pal (Gerrit)

    unread,
    Jan 9, 2026, 1:41:19 PM (3 days ago) Jan 9
    to Edman Anjos, Andrew Rayskiy, Chromium LUCI CQ, chromium...@chromium.org, Simon Hangl, dibyapal+wa...@chromium.org, dmurph+watc...@chromium.org, giovax...@chromium.org, kuragin+web-ap...@chromium.org, loyso...@chromium.org, mgiuca...@chromium.org, philli...@chromium.org, rmcelra...@chromium.org, webap...@microsoft.com, zelin+watch-we...@chromium.org
    Attention needed from Edman Anjos

    Dibyajyoti Pal voted and added 9 comments

    Votes added by Dibyajyoti Pal

    Code-Review+1

    9 comments

    Patchset-level comments
    Dibyajyoti Pal . resolved

    Not a lot of blocking feedback, so LGTM'ing to unblock.

    File chrome/browser/ui/views/web_apps/sub_apps_install_dialog.cc
    Line 222, Patchset 3 (Latest): HandleAutomaticActionForTesting( // IN-TEST
    Dibyajyoti Pal . unresolved
    Is this being used anywhere? If not, can we remove?

    I find this a bit weird, that we completely skip the dialog for testing, [but we have precedence for it in web_applications/ code](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/web_applications/test/web_app_install_test_utils.cc;l=53-63?q=AcceptDialogCallback) anyway so it's fine.
    Line 248, Patchset 3 (Latest): .AddOkButton(
    Dibyajyoti Pal . unresolved

    One quick thing to note here, by default the focus will be on the `OK` button. So it could be possible that a "malicious" IWA uses keyloggers to press Enter fast enough to automatically install a bunch of sub apps. Consider making either the Cancel button as the default, or the NONE button [1] using `OverrideDefaultButton()`.

    [1] https://source.chromium.org/search?q=%22.OverrideDefaultButton(ui::mojom::DialogButton::kNone)%22&sq=&ss=chromium%2Fchromium%2Fsrc

    File chrome/browser/ui/views/web_apps/sub_apps_install_dialog_controller.h
    Line 43, Patchset 3 (Latest): // To be called when the dialog is cancelled, dismissed, or destryoed.
    Dibyajyoti Pal . unresolved

    Please fix this WARNING reported by Spellchecker: "destryoed" is a possible misspelling of "destroyed".

    To bypass Spellchecker, a...

    "destryoed" is a possible misspelling of "destroyed".

    To bypass Spellchecker, add a footer with DISABLE_SPELLCHECKER

    Line 28, Patchset 3 (Latest): SetAutomaticActionForTesting(DialogActionForTesting auto_accept);
    Dibyajyoti Pal . unresolved

    nit: Add a comment about what this function does ("used to accept/cancel the dialog in testing").

    File chrome/browser/ui/views/web_apps/sub_apps_install_dialog_controller.cc
    Line 46, Patchset 3 (Latest): return true;
    Dibyajyoti Pal . unresolved

    This condition at first seemed unreachable, as `g_dialog_override_for_testing` is guaranteed to either exist with values, or to be `std::nullopt`, but then I realized this is to return true if `g_dialog_override_for_testing` is set after the callback has run. Maybe we can rewrite as the following using early exits to improve readability?

    ```
    bool SubAppsInstallDialogController::
    HandleAutomaticActionForTesting( // IN-TEST
    base::OnceCallback<void(bool)>& callback) {
    if (!g_dialog_override_for_testing) {
    return false;
    }

    switch (g_dialog_override_for_testing.value()) {
    case DialogActionForTesting::kAccept:
    std::move(callback).Run(true);
    break;
    case DialogActionForTesting::kCancel:
    std::move(callback).Run(false);
    break;
    return true;
    }
    }
    ```
    Line 66, Patchset 3 (Latest): std::move(callback_).Run(false);
    Dibyajyoti Pal . unresolved

    nitty-nit: Personally I prefer early exits and then doing the task the function is supposed to do for readability, like:

    ```
    if(!callback_) {
    return;
    }
    std::move(callback_).Run(false);
    ```

    But this is fine too.

    File chrome/browser/ui/web_applications/sub_apps_install_dialog_controller.cc
    Line 280, Patchset 1: .SetDialogDestroyingCallback(base::BindOnce(
    Andrew Rayskiy . unresolved

    It might be a bit clearer to inherit `SubAppsInstallDialogController` from `ui::DialogModelDelegate` and pass it to `ui::DialogModel::Builder()` so that all closures use the same unretained ptr.

    Or, given that this looks like a tab-scoped dialog, rely on [tab_dialog_manager](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/views/web_apps/protocol_handler_picker_coordinator.cc;l=225-226;drc=4641a043d7914ce31515751c357158d46f4c937a;bpv=0;bpt=1)

    Dibyajyoti Pal

    This, or it might also be nice to completely move the `views` specific logic to a separate entity (like a function of some sorts), so that the controller and view is separated, as [said here](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/chrome_browser_design_principles.md#:~:text=For%20simple%20features%20that%20do%20not%20require%20data%20persistence%2C%20we%20only%20require%20separation%20of%20controller%20from%20view.).

    We do this for all web app related dialogs. We have the [controller](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/views/web_apps/web_app_modal_dialog_delegate.h), and then the views are separate, like [this dialog](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/views/web_apps/web_app_simple_install_dialog.cc).

    Regarding the use of `tab_dialog_manager`, from the comments above `SubAppsInstallDialogController::Show()`, it seems like it is a browser modal dialog? In that case, `CreateBrowserModalDialogViews()` SGTM.

    Edman Anjos

    I split the view/controller, and IMO the result is much better. Thanks a lot for the pointers to `web_app_simple_install_dialog`, very helpful!

    I'm not sure about the `CreateBrowserModalDialogViews` vs `ShowWebModalDialogViews` choice. I think it should be a tab modal dialog. IWAs don't open in a tab, but I'm not entirely sure what should happen to the dialog e.g. on navigation. If we surface subapps to PWAs, then probably tab modal makes more sense?

    I'm currently using `ShowWebModalDialogViews`. Let me know what you think.

    Dibyajyoti Pal

    I was pointing out that your initial implementation of using `CreateBrowserModalDialogViews()` was good enough actually. Can you share why we moved to using a web contents modal dialog? In my head, sub apps are going to be related to IWAs and IWAs can't open in a new browser tab, it makes more sense to be tied to a browser window, right? But do let me know if I'm missing something.

    File chrome/browser/ui/web_applications/sub_apps_service_impl.cc
    Line 428, Patchset 3 (Latest): CHECK_DEREF(base::FindOrNull(add_call_info_, add_call_id));
    Dibyajyoti Pal . unresolved

    This seems redundant if we exit early in the beginning of the function, let's remove that?

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Edman Anjos
    Submit Requirements:
    • requirement satisfiedCode-Coverage
    • requirement satisfiedCode-Owners
    • requirement satisfiedCode-Review
    • requirement is not satisfiedNo-Unresolved-Comments
    • 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: Ic9c6f7128d74055cfab3d6334caea59a83c992cd
    Gerrit-Change-Number: 7415787
    Gerrit-PatchSet: 3
    Gerrit-Owner: Edman Anjos <ed...@chromium.org>
    Gerrit-Reviewer: Andrew Rayskiy <green...@google.com>
    Gerrit-Reviewer: Dibyajyoti Pal <diby...@chromium.org>
    Gerrit-Reviewer: Edman Anjos <ed...@chromium.org>
    Gerrit-CC: Simon Hangl <sim...@google.com>
    Gerrit-Attention: Edman Anjos <ed...@chromium.org>
    Gerrit-Comment-Date: Fri, 09 Jan 2026 18:41:04 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: Yes
    Comment-In-Reply-To: Dibyajyoti Pal <diby...@chromium.org>
    Comment-In-Reply-To: Andrew Rayskiy <green...@google.com>
    Comment-In-Reply-To: Edman Anjos <ed...@chromium.org>
    satisfied_requirement
    unsatisfied_requirement
    open
    diffy

    Andrew Rayskiy (Gerrit)

    unread,
    6:25 AM (12 hours ago) 6:25 AM
    to Edman Anjos, Chromium LUCI CQ, chromium...@chromium.org, Simon Hangl, dibyapal+wa...@chromium.org, dmurph+watc...@chromium.org, giovax...@chromium.org, kuragin+web-ap...@chromium.org, loyso...@chromium.org, mgiuca...@chromium.org, philli...@chromium.org, rmcelra...@chromium.org, webap...@microsoft.com, zelin+watch-we...@chromium.org
    Attention needed from Edman Anjos

    Andrew Rayskiy added 1 comment

    File chrome/browser/ui/views/web_apps/sub_apps_install_dialog.cc
    Line 246, Patchset 3 (Latest): base::BindRepeating(OpenAppSettingsForParentApp, parent_app_id,
    Andrew Rayskiy . unresolved

    nit: binding a `Profile*` like that doesn't sound right

    Gerrit-Comment-Date: Mon, 12 Jan 2026 11:25:35 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    satisfied_requirement
    unsatisfied_requirement
    open
    diffy

    Edman Anjos (Gerrit)

    unread,
    9:47 AM (9 hours ago) 9:47 AM
    to Edman Anjos, Andrew Rayskiy, Dibyajyoti Pal, Chromium LUCI CQ, chromium...@chromium.org, Simon Hangl, dibyapal+wa...@chromium.org, dmurph+watc...@chromium.org, giovax...@chromium.org, kuragin+web-ap...@chromium.org, loyso...@chromium.org, mgiuca...@chromium.org, philli...@chromium.org, rmcelra...@chromium.org, webap...@microsoft.com, zelin+watch-we...@chromium.org
    Attention needed from Andrew Rayskiy and Dibyajyoti Pal

    Edman Anjos added 9 comments

    File chrome/browser/ui/views/web_apps/sub_apps_install_dialog.cc
    Line 222, Patchset 3: HandleAutomaticActionForTesting( // IN-TEST
    Dibyajyoti Pal . resolved
    Is this being used anywhere? If not, can we remove?

    I find this a bit weird, that we completely skip the dialog for testing, [but we have precedence for it in web_applications/ code](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/web_applications/test/web_app_install_test_utils.cc;l=53-63?q=AcceptDialogCallback) anyway so it's fine.
    Edman Anjos

    It is used in a few browsertests via `SetAutomaticActionForTesting`. I agree this is not ideal, and filed crbug.com/474650885 to consider removing it.

    Line 246, Patchset 3: base::BindRepeating(OpenAppSettingsForParentApp, parent_app_id,
    Andrew Rayskiy . resolved

    nit: binding a `Profile*` like that doesn't sound right

    Edman Anjos

    Changed the function to take a weak pointer.

    There's a similar function `OpenAppSettingsForInstalledRelatedApp` at https://crsrc.org/c/chrome/browser/ui/web_applications/web_app_ui_utils.h;l=41;drc=3c59c0d203ad0e7e9688ebe9901341604856faac that takes a `Profile*`.

    Should we update that as well?

    Line 248, Patchset 3: .AddOkButton(
    Dibyajyoti Pal . resolved

    One quick thing to note here, by default the focus will be on the `OK` button. So it could be possible that a "malicious" IWA uses keyloggers to press Enter fast enough to automatically install a bunch of sub apps. Consider making either the Cancel button as the default, or the NONE button [1] using `OverrideDefaultButton()`.

    [1] https://source.chromium.org/search?q=%22.OverrideDefaultButton(ui::mojom::DialogButton::kNone)%22&sq=&ss=chromium%2Fchromium%2Fsrc

    Edman Anjos

    Done, changed to `kNone`

    that's a good point, thanks

    File chrome/browser/ui/views/web_apps/sub_apps_install_dialog_controller.h
    Line 43, Patchset 3: // To be called when the dialog is cancelled, dismissed, or destryoed.
    Dibyajyoti Pal . resolved

    Please fix this WARNING reported by Spellchecker: "destryoed" is a possible misspelling of "destroyed".

    To bypass Spellchecker, a...

    "destryoed" is a possible misspelling of "destroyed".

    To bypass Spellchecker, add a footer with DISABLE_SPELLCHECKER

    Edman Anjos

    Done

    Line 28, Patchset 3: SetAutomaticActionForTesting(DialogActionForTesting auto_accept);
    Dibyajyoti Pal . resolved

    nit: Add a comment about what this function does ("used to accept/cancel the dialog in testing").

    Edman Anjos

    Done

    File chrome/browser/ui/views/web_apps/sub_apps_install_dialog_controller.cc
    Line 46, Patchset 3: return true;
    Dibyajyoti Pal . resolved

    This condition at first seemed unreachable, as `g_dialog_override_for_testing` is guaranteed to either exist with values, or to be `std::nullopt`, but then I realized this is to return true if `g_dialog_override_for_testing` is set after the callback has run. Maybe we can rewrite as the following using early exits to improve readability?

    ```
    bool SubAppsInstallDialogController::
    HandleAutomaticActionForTesting( // IN-TEST
    base::OnceCallback<void(bool)>& callback) {
    if (!g_dialog_override_for_testing) {
    return false;
    }

    switch (g_dialog_override_for_testing.value()) {
    case DialogActionForTesting::kAccept:
    std::move(callback).Run(true);
    break;
    case DialogActionForTesting::kCancel:
    std::move(callback).Run(false);
    break;
    return true;
    }
    }
    ```
    Edman Anjos

    Done

    Line 66, Patchset 3: std::move(callback_).Run(false);
    Dibyajyoti Pal . resolved

    nitty-nit: Personally I prefer early exits and then doing the task the function is supposed to do for readability, like:

    ```
    if(!callback_) {
    return;
    }
    std::move(callback_).Run(false);
    ```

    But this is fine too.

    Edman Anjos

    Done

    File chrome/browser/ui/web_applications/sub_apps_install_dialog_controller.cc
    Line 280, Patchset 1: .SetDialogDestroyingCallback(base::BindOnce(
    Andrew Rayskiy . resolved

    It might be a bit clearer to inherit `SubAppsInstallDialogController` from `ui::DialogModelDelegate` and pass it to `ui::DialogModel::Builder()` so that all closures use the same unretained ptr.

    Or, given that this looks like a tab-scoped dialog, rely on [tab_dialog_manager](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/views/web_apps/protocol_handler_picker_coordinator.cc;l=225-226;drc=4641a043d7914ce31515751c357158d46f4c937a;bpv=0;bpt=1)

    Dibyajyoti Pal

    This, or it might also be nice to completely move the `views` specific logic to a separate entity (like a function of some sorts), so that the controller and view is separated, as [said here](https://chromium.googlesource.com/chromium/src/+/HEAD/docs/chrome_browser_design_principles.md#:~:text=For%20simple%20features%20that%20do%20not%20require%20data%20persistence%2C%20we%20only%20require%20separation%20of%20controller%20from%20view.).

    We do this for all web app related dialogs. We have the [controller](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/views/web_apps/web_app_modal_dialog_delegate.h), and then the views are separate, like [this dialog](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/views/web_apps/web_app_simple_install_dialog.cc).

    Regarding the use of `tab_dialog_manager`, from the comments above `SubAppsInstallDialogController::Show()`, it seems like it is a browser modal dialog? In that case, `CreateBrowserModalDialogViews()` SGTM.

    Edman Anjos

    I split the view/controller, and IMO the result is much better. Thanks a lot for the pointers to `web_app_simple_install_dialog`, very helpful!

    I'm not sure about the `CreateBrowserModalDialogViews` vs `ShowWebModalDialogViews` choice. I think it should be a tab modal dialog. IWAs don't open in a tab, but I'm not entirely sure what should happen to the dialog e.g. on navigation. If we surface subapps to PWAs, then probably tab modal makes more sense?

    I'm currently using `ShowWebModalDialogViews`. Let me know what you think.

    Dibyajyoti Pal

    I was pointing out that your initial implementation of using `CreateBrowserModalDialogViews()` was good enough actually. Can you share why we moved to using a web contents modal dialog? In my head, sub apps are going to be related to IWAs and IWAs can't open in a new browser tab, it makes more sense to be tied to a browser window, right? But do let me know if I'm missing something.

    Edman Anjos

    Changed to `CreateBrowserModalDialogViews`

    My thought process was that if subapps becomes available for PWAs it can be called from a regular tab and IIUC `ShowWebModalDialogViews` would then make more sense.

    File chrome/browser/ui/web_applications/sub_apps_service_impl.cc
    Line 428, Patchset 3: CHECK_DEREF(base::FindOrNull(add_call_info_, add_call_id));
    Dibyajyoti Pal . resolved

    This seems redundant if we exit early in the beginning of the function, let's remove that?

    Edman Anjos

    Reverted back to CHECK_DEREF

    looking closely I think the CHECK_DEREF is more appropriate. AFAICT the `add_call_id` should be valid when this is called

    Open in Gerrit

    Related details

    Attention is currently required from:
    • Andrew Rayskiy
    • Dibyajyoti Pal
    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: Ic9c6f7128d74055cfab3d6334caea59a83c992cd
    Gerrit-Change-Number: 7415787
    Gerrit-PatchSet: 4
    Gerrit-Owner: Edman Anjos <ed...@chromium.org>
    Gerrit-Reviewer: Andrew Rayskiy <green...@google.com>
    Gerrit-Reviewer: Dibyajyoti Pal <diby...@chromium.org>
    Gerrit-Reviewer: Edman Anjos <ed...@chromium.org>
    Gerrit-CC: Simon Hangl <sim...@google.com>
    Gerrit-Attention: Dibyajyoti Pal <diby...@chromium.org>
    Gerrit-Attention: Andrew Rayskiy <green...@google.com>
    Gerrit-Comment-Date: Mon, 12 Jan 2026 14:46:58 +0000
    Gerrit-HasComments: Yes
    Gerrit-Has-Labels: No
    satisfied_requirement
    unsatisfied_requirement
    open
    diffy

    Andrew Rayskiy (Gerrit)

    unread,
    9:54 AM (9 hours ago) 9:54 AM
    to Edman Anjos, Dibyajyoti Pal, Chromium LUCI CQ, chromium...@chromium.org, Simon Hangl, dibyapal+wa...@chromium.org, dmurph+watc...@chromium.org, giovax...@chromium.org, kuragin+web-ap...@chromium.org, loyso...@chromium.org, mgiuca...@chromium.org, philli...@chromium.org, rmcelra...@chromium.org, webap...@microsoft.com, zelin+watch-we...@chromium.org
    Attention needed from Dibyajyoti Pal and Edman Anjos

    Andrew Rayskiy voted Code-Review+1

    Code-Review+1
    Open in Gerrit

    Related details

    Attention is currently required from:
    • Dibyajyoti Pal
    • Edman Anjos
    Submit Requirements:
      • requirement satisfiedCode-Coverage
      • requirement is not 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: Ic9c6f7128d74055cfab3d6334caea59a83c992cd
      Gerrit-Change-Number: 7415787
      Gerrit-PatchSet: 4
      Gerrit-Owner: Edman Anjos <ed...@chromium.org>
      Gerrit-Reviewer: Andrew Rayskiy <green...@google.com>
      Gerrit-Reviewer: Dibyajyoti Pal <diby...@chromium.org>
      Gerrit-Reviewer: Edman Anjos <ed...@chromium.org>
      Gerrit-CC: Simon Hangl <sim...@google.com>
      Gerrit-Attention: Dibyajyoti Pal <diby...@chromium.org>
      Gerrit-Attention: Edman Anjos <ed...@chromium.org>
      Gerrit-Comment-Date: Mon, 12 Jan 2026 14:53:48 +0000
      Gerrit-HasComments: No
      Gerrit-Has-Labels: Yes
      satisfied_requirement
      unsatisfied_requirement
      open
      diffy

      Dibyajyoti Pal (Gerrit)

      unread,
      11:06 AM (8 hours ago) 11:06 AM
      to Edman Anjos, Andrew Rayskiy, Chromium LUCI CQ, chromium...@chromium.org, Simon Hangl, dibyapal+wa...@chromium.org, dmurph+watc...@chromium.org, giovax...@chromium.org, kuragin+web-ap...@chromium.org, loyso...@chromium.org, mgiuca...@chromium.org, philli...@chromium.org, rmcelra...@chromium.org, webap...@microsoft.com, zelin+watch-we...@chromium.org
      Attention needed from Edman Anjos

      Dibyajyoti Pal voted and added 2 comments

      Votes added by Dibyajyoti Pal

      Code-Review+1

      2 comments

      Patchset-level comments
      File-level comment, Patchset 4 (Latest):
      Dibyajyoti Pal . resolved

      LGTM, thanks for iterating on this, I like where we ended up here!

      File chrome/browser/ui/views/web_apps/sub_apps_install_dialog.cc
      Line 246, Patchset 3: base::BindRepeating(OpenAppSettingsForParentApp, parent_app_id,
      Andrew Rayskiy . resolved

      nit: binding a `Profile*` like that doesn't sound right

      Edman Anjos

      Changed the function to take a weak pointer.

      There's a similar function `OpenAppSettingsForInstalledRelatedApp` at https://crsrc.org/c/chrome/browser/ui/web_applications/web_app_ui_utils.h;l=41;drc=3c59c0d203ad0e7e9688ebe9901341604856faac that takes a `Profile*`.

      Should we update that as well?

      Dibyajyoti Pal

      That example is a good example of a function that takes in a `Profile*`, but it's not a good example of binding that Andrew brought up, since the function is not used as a callback anywhere, and is [instead called directly](https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/views/site_data/page_specific_site_data_dialog.cc;l=320-324;drc=aeb46299fefab41457b0c50e5386616d2e8d64a2;bpv=1;bpt=1). This implementation LGTM!

      Open in Gerrit

      Related details

      Attention is currently required from:
      • Edman Anjos
      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: Ic9c6f7128d74055cfab3d6334caea59a83c992cd
      Gerrit-Change-Number: 7415787
      Gerrit-PatchSet: 4
      Gerrit-Owner: Edman Anjos <ed...@chromium.org>
      Gerrit-Reviewer: Andrew Rayskiy <green...@google.com>
      Gerrit-Reviewer: Dibyajyoti Pal <diby...@chromium.org>
      Gerrit-Reviewer: Edman Anjos <ed...@chromium.org>
      Gerrit-CC: Simon Hangl <sim...@google.com>
      Gerrit-Attention: Edman Anjos <ed...@chromium.org>
      Gerrit-Comment-Date: Mon, 12 Jan 2026 16:06:18 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: Yes
      satisfied_requirement
      open
      diffy

      Edman Anjos (Gerrit)

      unread,
      11:32 AM (7 hours ago) 11:32 AM
      to Edman Anjos, Andrew Rayskiy, Chromium LUCI CQ, chromium...@chromium.org, Simon Hangl, dibyapal+wa...@chromium.org, dmurph+watc...@chromium.org, giovax...@chromium.org, kuragin+web-ap...@chromium.org, loyso...@chromium.org, mgiuca...@chromium.org, philli...@chromium.org, rmcelra...@chromium.org, webap...@microsoft.com, zelin+watch-we...@chromium.org

      Edman Anjos voted and added 1 comment

      Votes added by Edman Anjos

      Commit-Queue+2

      1 comment

      Patchset-level comments
      Edman Anjos . resolved

      Thank you!

      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: Ic9c6f7128d74055cfab3d6334caea59a83c992cd
      Gerrit-Change-Number: 7415787
      Gerrit-PatchSet: 4
      Gerrit-Owner: Edman Anjos <ed...@chromium.org>
      Gerrit-Reviewer: Andrew Rayskiy <green...@google.com>
      Gerrit-Reviewer: Dibyajyoti Pal <diby...@chromium.org>
      Gerrit-Reviewer: Edman Anjos <ed...@chromium.org>
      Gerrit-CC: Simon Hangl <sim...@google.com>
      Gerrit-Comment-Date: Mon, 12 Jan 2026 16:32:16 +0000
      Gerrit-HasComments: Yes
      Gerrit-Has-Labels: Yes
      satisfied_requirement
      open
      diffy

      Chromium LUCI CQ (Gerrit)

      unread,
      11:35 AM (7 hours ago) 11:35 AM
      to Edman Anjos, Dibyajyoti Pal, Andrew Rayskiy, chromium...@chromium.org, Simon Hangl, dibyapal+wa...@chromium.org, dmurph+watc...@chromium.org, giovax...@chromium.org, kuragin+web-ap...@chromium.org, loyso...@chromium.org, mgiuca...@chromium.org, philli...@chromium.org, rmcelra...@chromium.org, webap...@microsoft.com, zelin+watch-we...@chromium.org

      Chromium LUCI CQ submitted the change

      Change information

      Commit message:
      Refactor SubAppsInstallDialogController to separate view and logic

      SubAppsInstallDialogController is currently responsible for everything
      related to this dialog.

      This CL splits out the presentation concerns into
      c/b/ui/views/web_apps/sub_apps_install_dialog.cc, so
      SubAppsInstallDialogController remains focused on controller concerns.

      The dialog is now created as a self-owned browser-modal widget using
      CreateBrowserModalDialogViews. The controller is owned by the dialog
      model, which is in turn owned by the widget.
      Bug: 40924577
      Bypass-Check-License: controller files were moved, not added
      Change-Id: Ic9c6f7128d74055cfab3d6334caea59a83c992cd
      Reviewed-by: Andrew Rayskiy <green...@google.com>
      Reviewed-by: Dibyajyoti Pal <diby...@chromium.org>
      Commit-Queue: Edman Anjos <ed...@chromium.org>
      Cr-Commit-Position: refs/heads/main@{#1567801}
      Files:
      • M chrome/browser/ui/BUILD.gn
      • M chrome/browser/ui/views/web_apps/BUILD.gn
      • R chrome/browser/ui/views/web_apps/sub_apps_install_dialog.cc
      • A chrome/browser/ui/views/web_apps/sub_apps_install_dialog_controller.cc
      • A chrome/browser/ui/views/web_apps/sub_apps_install_dialog_controller.h
      • R chrome/browser/ui/views/web_apps/sub_apps_install_dialog_controller_browsertest.cc
      • M chrome/browser/ui/views/web_apps/web_app_integration_test_driver.cc
      • M chrome/browser/ui/web_applications/BUILD.gn
      • M chrome/browser/ui/web_applications/sub_apps_admin_policy_browsertest.cc
      • D chrome/browser/ui/web_applications/sub_apps_install_dialog_controller.h
      • M chrome/browser/ui/web_applications/sub_apps_service_impl.cc
      • M chrome/browser/ui/web_applications/sub_apps_service_impl.h
      • M chrome/browser/ui/web_applications/sub_apps_service_impl_browsertest.cc
      • M chrome/browser/ui/web_applications/web_app_dialogs.h
      • M chrome/browser/ui/web_applications/web_app_ui_utils.cc
      • M chrome/browser/ui/web_applications/web_app_ui_utils.h
      Change size: L
      Delta: 16 files changed, 262 insertions(+), 235 deletions(-)
      Branch: refs/heads/main
      Submit Requirements:
      • requirement satisfiedCode-Review: +1 by Andrew Rayskiy, +1 by Dibyajyoti Pal
      Open in Gerrit
      Inspect html for hidden footers to help with email filtering. To unsubscribe visit settings. DiffyGerrit
      Gerrit-MessageType: merged
      Gerrit-Project: chromium/src
      Gerrit-Branch: main
      Gerrit-Change-Id: Ic9c6f7128d74055cfab3d6334caea59a83c992cd
      Gerrit-Change-Number: 7415787
      Gerrit-PatchSet: 5
      Gerrit-Owner: Edman Anjos <ed...@chromium.org>
      Gerrit-Reviewer: Andrew Rayskiy <green...@google.com>
      Gerrit-Reviewer: Chromium LUCI CQ <chromiu...@luci-project-accounts.iam.gserviceaccount.com>
      Gerrit-Reviewer: Dibyajyoti Pal <diby...@chromium.org>
      Gerrit-Reviewer: Edman Anjos <ed...@chromium.org>
      open
      diffy
      satisfied_requirement
      Reply all
      Reply to author
      Forward
      0 new messages