Implementing CDP command to allow the trigger of an extension action [chromium/src : main]

0 views
Skip to first unread message

Devlin Cronin (Gerrit)

unread,
Dec 8, 2025, 4:39:44 PM12/8/25
to Nicholas Roscino, Devlin Cronin, Code Review Nudger, Chromium LUCI CQ, Alex Rudenko, chromium...@chromium.org, devtools...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, chromium-a...@chromium.org, devtools-re...@chromium.org, extension...@chromium.org
Attention needed from Nicholas Roscino

Devlin Cronin added 14 comments

Patchset-level comments
File-level comment, Patchset 9 (Latest):
Devlin Cronin . unresolved

Thanks for the CL!

It's also been a few years since the doc was written -- can we make sure security has signed off on this new capability? (If that review has already happened, feel free to point me to it!)

Commit Message
Line 12, Patchset 9 (Latest):Bug: 455536313
Devlin Cronin . unresolved

is there a reason this needs to be restricted?

File chrome/browser/devtools/protocol/devtools_extensions_browsertest.cc
Line 511, Patchset 9 (Latest):}
Devlin Cronin . unresolved

can we also add a test that exercises dispatching action.onClicked (i.e., has neither a popup nor a side panel)?

File chrome/browser/devtools/protocol/extensions_handler.h
Line 29, Patchset 9 (Latest): const protocol::String& in_extensionId) override;
Devlin Cronin . unresolved

style should be "extension_id"

Line 8, Patchset 9 (Latest):#include "base/observer_list.h"
Devlin Cronin . unresolved

Is this needed?

File chrome/browser/devtools/protocol/extensions_handler.cc
Line 25, Patchset 9 (Latest):#include "chrome/common/extensions/api/tabs.h"
Devlin Cronin . unresolved

This is a lot of new includes; are they all needed?

Line 173, Patchset 9 (Latest): const std::string& extensionId) {
Devlin Cronin . unresolved

extension_id

Line 180, Patchset 9 (Latest): auto* web_content = host->GetWebContents();
Devlin Cronin . unresolved

nit: web_contents

Line 197, Patchset 9 (Latest): auto* ctx = host->GetBrowserContext();
Devlin Cronin . unresolved

nit: `context` or `browser_context` ([avoid uncommon abbreviations](https://google.github.io/styleguide/cppguide.html#General_Naming_Rules))

Line 199, Patchset 9 (Latest): constexpr bool kGrantTabPermissions = true;
Devlin Cronin . unresolved

Do we want this to grant tab permissions? I didn't see a reference to that decision or discussion in the design doc.

Line 215, Patchset 9 (Latest): extension_action_trigger_service->TriggerAction(extension.value()->id(),
Devlin Cronin . unresolved

This looks like it effectively results in triggering the action twice(ish), once through the ExtensionActionRunner and then again here, indirectly, via the observer.

File chrome/browser/extensions/extension_action_trigger_service.h
Line 15, Patchset 9 (Latest):class ExtensionActionTriggerService : public KeyedService {
Devlin Cronin . unresolved

This seems unnecessarily complicated.

@emil...@chromium.org, what's the best way to trigger an action via C++?

File chrome/test/data/devtools/extensions/popup_action/background.js
Line 5, Patchset 9 (Latest):chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
Devlin Cronin . unresolved

this structure would be easier if we just called notifyPass() (or sent a message via chrome.test.sendMessage('popup opened')) from the popup

File chrome/test/data/devtools/extensions/side_panel_action/background.js
Line 6, Patchset 9 (Latest): chrome.test.notifyPass();
Devlin Cronin . unresolved

sending this pass signal here means we don't test the side panel's appearing. Should we instead have this test use [setPanelBehavior](https://developer.chrome.com/docs/extensions/reference/api/sidePanel#method-setPanelBehavior) so that clicking on the button triggers the panel, and then send a message from the panel itself?

Open in Gerrit

Related details

Attention is currently required from:
  • Nicholas Roscino
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: I48c0d659ddd9378fcbe8c9de292824182e45aa3a
Gerrit-Change-Number: 7132423
Gerrit-PatchSet: 9
Gerrit-Owner: Nicholas Roscino <nros...@chromium.org>
Gerrit-Reviewer: Alex Rudenko <alexr...@chromium.org>
Gerrit-Reviewer: Devlin Cronin <rdevlin...@chromium.org>
Gerrit-CC: Code Review Nudger <android-build...@prod.google.com>
Gerrit-Attention: Nicholas Roscino <nros...@chromium.org>
Gerrit-Comment-Date: Mon, 08 Dec 2025 21:39:30 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
satisfied_requirement
unsatisfied_requirement
open
diffy

Nicholas Roscino (Gerrit)

unread,
Jan 16, 2026, 6:34:13 AM (yesterday) Jan 16
to Devlin Cronin, Code Review Nudger, Chromium LUCI CQ, Alex Rudenko, chromium...@chromium.org, devtools...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, chromium-a...@chromium.org, devtools-re...@chromium.org, extension...@chromium.org
Attention needed from Alex Rudenko and Devlin Cronin

Nicholas Roscino added 15 comments

Patchset-level comments
Devlin Cronin . unresolved

Thanks for the CL!

It's also been a few years since the doc was written -- can we make sure security has signed off on this new capability? (If that review has already happened, feel free to point me to it!)

Nicholas Roscino

I think you might have opened the old version that was the one I used as a baseline to create the new one! You can find the new one at this go link: go/chrome-devtools:enabling-extension-action-via-cdp-design. LMK what you think!

Commit Message
Line 12, Patchset 9:Bug: 455536313
Devlin Cronin . unresolved

is there a reason this needs to be restricted?

Nicholas Roscino

Not sure what are we talking about here. You mean that the issue could be public on crbug?

File chrome/browser/devtools/protocol/devtools_extensions_browsertest.cc
Line 448, Patchset 4: GURL url = embedded_test_server()->GetURL("/blank.html");
Alex Rudenko . resolved

can we use `about:blank`?

Nicholas Roscino

I tried but this was not working. The test was hanging and failing. I tried with some other pages like other tests were doing and it passed. I think there should be a way to make it work also with about:blank but didn't spend much time on this. Do you have any idea?

Nicholas Roscino

Done

Line 511, Patchset 9:}
Devlin Cronin . resolved

can we also add a test that exercises dispatching action.onClicked (i.e., has neither a popup nor a side panel)?

Nicholas Roscino

Done

File chrome/browser/devtools/protocol/extensions_handler.h
Line 29, Patchset 9: const protocol::String& in_extensionId) override;
Devlin Cronin . resolved

style should be "extension_id"

Nicholas Roscino

Done

Line 8, Patchset 9:#include "base/observer_list.h"
Devlin Cronin . resolved

Is this needed?

Nicholas Roscino

Done

File chrome/browser/devtools/protocol/extensions_handler.cc
Line 25, Patchset 9:#include "chrome/common/extensions/api/tabs.h"
Devlin Cronin . resolved

This is a lot of new includes; are they all needed?

Nicholas Roscino

Done

Line 173, Patchset 9: const std::string& extensionId) {
Devlin Cronin . resolved

extension_id

Nicholas Roscino

Done

Line 180, Patchset 9: auto* web_content = host->GetWebContents();
Devlin Cronin . resolved

nit: web_contents

Nicholas Roscino

Done

Line 197, Patchset 9: auto* ctx = host->GetBrowserContext();
Devlin Cronin . resolved

nit: `context` or `browser_context` ([avoid uncommon abbreviations](https://google.github.io/styleguide/cppguide.html#General_Naming_Rules))

Nicholas Roscino

Done

Line 199, Patchset 9: constexpr bool kGrantTabPermissions = true;
Devlin Cronin . unresolved

Do we want this to grant tab permissions? I didn't see a reference to that decision or discussion in the design doc.

Nicholas Roscino

I tried to address this in the doc. The idea is that this simulates the user clicking on the UI.
The triggering code was taken from the ui part that handles the user click interaction. Since we want to make sure to replicate the same behavior I also added the GrantTabPermission to the automation (UI code: https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/extensions/extension_action_view_model.cc;drc=eb33936323cf453f064c374c3176ffc202c61239;l=407).
Since all this block runs behind a specific flag that enables extension debugging features (--enable-unsafe-extension-debugging) I don't think this exposes a user to some security risks, am I missing something here?

Line 215, Patchset 9: extension_action_trigger_service->TriggerAction(extension.value()->id(),
Devlin Cronin . unresolved

This looks like it effectively results in triggering the action twice(ish), once through the ExtensionActionRunner and then again here, indirectly, via the observer.

Nicholas Roscino

With the `ExtensionActionRunner` you are referring to the call to `RunAction`?
If that is the case, for my understanding of the RunAction method, that just returns the type of action that has to be executed unless there are no side panel and no popup to show. In that case it triggers the ExtensionAction itself but returns an action of kNone.
The TriggerAction method is triggering the action on the observer (ExtensionActionViewModel::OnExtensionActionTriggered) that is checking for the action type, if it is kNone, it does nothing. Otherwise it runs either the code for the Popup or the side panel.
Let me know if I am missing something tho!

File chrome/test/data/blank.html
Line 1, Patchset 4:<html><head><title>blank page</title></head>
Alex Rudenko . resolved

is it needed?

Nicholas Roscino

This was the result of what I said in https://chromium-review.googlesource.com/c/chromium/src/+/7132423/comment/073077bf_9cdf3395/ but would be glad if I can avoid this and remove it.

Nicholas Roscino

Done

File chrome/test/data/devtools/extensions/popup_action/background.js
Line 5, Patchset 9:chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
Devlin Cronin . resolved

this structure would be easier if we just called notifyPass() (or sent a message via chrome.test.sendMessage('popup opened')) from the popup

Nicholas Roscino

Done

File chrome/test/data/devtools/extensions/side_panel_action/background.js
Line 6, Patchset 9: chrome.test.notifyPass();
Devlin Cronin . resolved

sending this pass signal here means we don't test the side panel's appearing. Should we instead have this test use [setPanelBehavior](https://developer.chrome.com/docs/extensions/reference/api/sidePanel#method-setPanelBehavior) so that clicking on the button triggers the panel, and then send a message from the panel itself?

Nicholas Roscino

Done

Open in Gerrit

Related details

Attention is currently required from:
  • Alex Rudenko
  • Devlin Cronin
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: I48c0d659ddd9378fcbe8c9de292824182e45aa3a
Gerrit-Change-Number: 7132423
Gerrit-PatchSet: 11
Gerrit-Owner: Nicholas Roscino <nros...@chromium.org>
Gerrit-Reviewer: Alex Rudenko <alexr...@chromium.org>
Gerrit-Reviewer: Devlin Cronin <rdevlin...@chromium.org>
Gerrit-CC: Code Review Nudger <android-build...@prod.google.com>
Gerrit-Attention: Devlin Cronin <rdevlin...@chromium.org>
Gerrit-Attention: Alex Rudenko <alexr...@chromium.org>
Gerrit-Comment-Date: Fri, 16 Jan 2026 11:33:58 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: Devlin Cronin <rdevlin...@chromium.org>
Comment-In-Reply-To: Alex Rudenko <alexr...@chromium.org>
Comment-In-Reply-To: Nicholas Roscino <nros...@chromium.org>
satisfied_requirement
unsatisfied_requirement
open
diffy

Emilia Paz (Gerrit)

unread,
Jan 16, 2026, 1:55:09 PM (yesterday) Jan 16
to Nicholas Roscino, Devlin Cronin, Code Review Nudger, Chromium LUCI CQ, Alex Rudenko, chromium...@chromium.org, devtools...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, chromium-a...@chromium.org, devtools-re...@chromium.org, extension...@chromium.org
Attention needed from Alex Rudenko, Devlin Cronin and Nicholas Roscino

Emilia Paz added 3 comments

Patchset-level comments
File-level comment, Patchset 11 (Latest):
Emilia Paz . resolved

Thanks Nicholas! Gave an example of how to use ExtensionActionViewModel::ExecuteUserAction without introducing a factory, although unsure if possible with CDP

File chrome/browser/devtools/protocol/extensions_handler.cc
Line 199, Patchset 9: constexpr bool kGrantTabPermissions = true;
Devlin Cronin . unresolved

Do we want this to grant tab permissions? I didn't see a reference to that decision or discussion in the design doc.

Nicholas Roscino

I tried to address this in the doc. The idea is that this simulates the user clicking on the UI.
The triggering code was taken from the ui part that handles the user click interaction. Since we want to make sure to replicate the same behavior I also added the GrantTabPermission to the automation (UI code: https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/extensions/extension_action_view_model.cc;drc=eb33936323cf453f064c374c3176ffc202c61239;l=407).
Since all this block runs behind a specific flag that enables extension debugging features (--enable-unsafe-extension-debugging) I don't think this exposes a user to some security risks, am I missing something here?

Emilia Paz

Not familiar with CDP. Is `ExtensionsHandler::TriggerAction` intended to be triggered from an user action? Tab permissions can only be granted if the action is executed by the user

File chrome/browser/extensions/extension_action_trigger_service.h
Line 15, Patchset 9:class ExtensionActionTriggerService : public KeyedService {
Devlin Cronin . unresolved

This seems unnecessarily complicated.

@emil...@chromium.org, what's the best way to trigger an action via C++?

Emilia Paz

An action is triggered via `ExtensionActionRunner::RunAction`. Since running an action should be user initiated, it gets called from `ExtensionActionViewModel::ExecuteUserAction(InvocationSource source)` which does:

  • if action cannot be run on web contents: shows context menu as fallback
  • closes extensions menu if opened
  • runs actions
  • records invocation source
  • triggers popup or side panel, if neccesary

My understanding is if CSP command is a user-initiated command and mimics "clicking on the action", then all these items in `ExtensionActionViewModel::ExecuteUserAction` should be covered too. Thus, ideally we use that method.

`ExtensionActionViewModel` can be accessed through the extensions container, owned by the browser window. For example, developers private api [triggers](https://source.chromium.org/chromium/chromium/src/+/448017a4bb0441509cafdf0123f5664f27ea16bc:chrome/browser/extensions/api/extension_action/extension_action_api_non_android.cc;l=83-108;bpv=0;bpt=0) the popup via `browser.window()->GetExtensionsContainer()->ShowToolbarActionPopupForAPICall()`. We could have something similar if there is access to the browser/browserwindowinterface (not sure if that's possible, from a quick look didn't see browser usage on extensions handler)

Open in Gerrit

Related details

Attention is currently required from:
  • Alex Rudenko
  • Devlin Cronin
  • Nicholas Roscino
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: I48c0d659ddd9378fcbe8c9de292824182e45aa3a
Gerrit-Change-Number: 7132423
Gerrit-PatchSet: 11
Gerrit-Owner: Nicholas Roscino <nros...@chromium.org>
Gerrit-Reviewer: Alex Rudenko <alexr...@chromium.org>
Gerrit-Reviewer: Devlin Cronin <rdevlin...@chromium.org>
Gerrit-CC: Code Review Nudger <android-build...@prod.google.com>
Gerrit-CC: Emilia Paz <emil...@chromium.org>
Gerrit-Attention: Devlin Cronin <rdevlin...@chromium.org>
Gerrit-Attention: Alex Rudenko <alexr...@chromium.org>
Gerrit-Attention: Nicholas Roscino <nros...@chromium.org>
Gerrit-Comment-Date: Fri, 16 Jan 2026 18:54:58 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: Devlin Cronin <rdevlin...@chromium.org>
Comment-In-Reply-To: Nicholas Roscino <nros...@chromium.org>
satisfied_requirement
unsatisfied_requirement
open
diffy

Devlin Cronin (Gerrit)

unread,
Jan 16, 2026, 7:25:04 PM (19 hours ago) Jan 16
to Nicholas Roscino, Devlin Cronin, Code Review Nudger, Chromium LUCI CQ, Alex Rudenko, chromium...@chromium.org, devtools...@chromium.org, blink-re...@chromium.org, blink-...@chromium.org, chromium-a...@chromium.org, devtools-re...@chromium.org, extension...@chromium.org
Attention needed from Alex Rudenko and Nicholas Roscino

Devlin Cronin added 5 comments

Patchset-level comments
Devlin Cronin . resolved

Thanks, Nicholas! I took a fairly light pass at this one, since I'm still very much hoping we can simplify the flow to trigger the action, inline with Emilia's comments.

Commit Message
Line 12, Patchset 9:Bug: 455536313
Devlin Cronin . unresolved

is there a reason this needs to be restricted?

Nicholas Roscino

Not sure what are we talking about here. You mean that the issue could be public on crbug?

Devlin Cronin

Correct. Chromium is open source, and bugs should generally be open, as well, unless there's compelling reasons for them to be restricted (e.g., security vulnerabilities that we're fixing or it being tied to internal-only projects).

File chrome/browser/devtools/protocol/extensions_handler.cc
Line 196, Patchset 11 (Latest): return protocol::Response::ServerError("ExtensionActionRunner not found.");
Devlin Cronin . unresolved

This seems fairly meaningless to a (web / extension) developer. Maybe just "Can't run on this target" or similar?

Line 199, Patchset 9: constexpr bool kGrantTabPermissions = true;
Devlin Cronin . unresolved

Do we want this to grant tab permissions? I didn't see a reference to that decision or discussion in the design doc.

Nicholas Roscino

I tried to address this in the doc. The idea is that this simulates the user clicking on the UI.
The triggering code was taken from the ui part that handles the user click interaction. Since we want to make sure to replicate the same behavior I also added the GrantTabPermission to the automation (UI code: https://source.chromium.org/chromium/chromium/src/+/main:chrome/browser/ui/extensions/extension_action_view_model.cc;drc=eb33936323cf453f064c374c3176ffc202c61239;l=407).
Since all this block runs behind a specific flag that enables extension debugging features (--enable-unsafe-extension-debugging) I don't think this exposes a user to some security risks, am I missing something here?

Emilia Paz

Not familiar with CDP. Is `ExtensionsHandler::TriggerAction` intended to be triggered from an user action? Tab permissions can only be granted if the action is executed by the user

Devlin Cronin

Ack; thanks, Nicholas. I'm okay with this, as long as this remains behind those flags. It makes sense that we want it to closely emulate user actions in order to be able write automated tests reflecting those.

Line 215, Patchset 9: extension_action_trigger_service->TriggerAction(extension.value()->id(),
Devlin Cronin . unresolved

This looks like it effectively results in triggering the action twice(ish), once through the ExtensionActionRunner and then again here, indirectly, via the observer.

Nicholas Roscino

With the `ExtensionActionRunner` you are referring to the call to `RunAction`?
If that is the case, for my understanding of the RunAction method, that just returns the type of action that has to be executed unless there are no side panel and no popup to show. In that case it triggers the ExtensionAction itself but returns an action of kNone.
The TriggerAction method is triggering the action on the observer (ExtensionActionViewModel::OnExtensionActionTriggered) that is checking for the action type, if it is kNone, it does nothing. Otherwise it runs either the code for the Popup or the side panel.
Let me know if I am missing something tho!

Devlin Cronin

The ExtensionActionRunner doesn't show any UI (side panel or popup), but it *does* do everything else, including:

  • Granting tab permissions
  • Firing action.onClicked in the extension

The former is, I suppose, safe to fire twice (though kind of awkward), but the latter would be a functional bug that could result in broken functionality. For instance, let's say you just had an extension that counted the number of times the action was clicked by listening to the onClicked event. After each call to this, it would result in _two_ triggers.

Open in Gerrit

Related details

Attention is currently required from:
  • Alex Rudenko
  • Nicholas Roscino
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: I48c0d659ddd9378fcbe8c9de292824182e45aa3a
Gerrit-Change-Number: 7132423
Gerrit-PatchSet: 11
Gerrit-Owner: Nicholas Roscino <nros...@chromium.org>
Gerrit-Reviewer: Alex Rudenko <alexr...@chromium.org>
Gerrit-Reviewer: Devlin Cronin <rdevlin...@chromium.org>
Gerrit-CC: Code Review Nudger <android-build...@prod.google.com>
Gerrit-CC: Emilia Paz <emil...@chromium.org>
Gerrit-Attention: Alex Rudenko <alexr...@chromium.org>
Gerrit-Attention: Nicholas Roscino <nros...@chromium.org>
Gerrit-Comment-Date: Sat, 17 Jan 2026 00:24:54 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: Devlin Cronin <rdevlin...@chromium.org>
Comment-In-Reply-To: Emilia Paz <emil...@chromium.org>
Comment-In-Reply-To: Nicholas Roscino <nros...@chromium.org>
satisfied_requirement
unsatisfied_requirement
open
diffy
Reply all
Reply to author
Forward
0 new messages