Introduce ClickToolJavaScriptFeature and click_tool.ts. [chromium/src : main]

0 views
Skip to first unread message

Kirubel Aklilu (Gerrit)

unread,
Feb 12, 2026, 12:52:55 PM (12 days ago) Feb 12
to Fikre Mengistu, Mike Dougherty, Matt Reichhoff, Chromium LUCI CQ, AyeAye, ios-revie...@chromium.org, marq+...@chromium.org, ios-r...@chromium.org
Attention needed from Mike Dougherty

Kirubel Aklilu added 8 comments

Patchset-level comments
File-level comment, Patchset 20:
Mike Dougherty . resolved

I've added some comments inline. Additionally, please consider opting in to enable `kAssertOnJavaScriptErrors` for your new tests if possible. I'm currently working to enable it everywhere, but am unable to do so yet. However, if it works for your tests, especially, minimally for JavascriptTests which won't be affected by errors in other scripts, it will help maintain your scripts from accidentally throwing any JS errors which will cause problems in the future.

Example CL: https://chromium-review.googlesource.com/c/chromium/src/+/7560593

Kirubel Aklilu

Thanks for flagging, I've enabled this flag in these tests and they still pass.

File ios/chrome/browser/intelligence/actuation/model/tools/click_tool_java_script_feature.h
File-level comment, Patchset 20:
Mike Dougherty . resolved

please add tests for this feature. I'm not sure if this is the best example, but here is one that I was able to quickly find: https://source.chromium.org/chromium/chromium/src/+/main:ios/chrome/browser/reader_mode/model/reader_mode_java_script_feature_unittest.mm

(Note you should avoid using `OverrideJavaScriptFeatures` as it will likely cause more trouble than it's worth for such a high level feature.)

Kirubel Aklilu

Done

File ios/chrome/browser/intelligence/actuation/model/tools/click_tool_java_script_feature.mm
Line 22, Patchset 20:const base::TimeDelta kJavascriptExecutionTimeout = base::Seconds(5);
Mike Dougherty . resolved

this is extremly long, the default for executing JS is 100ms. Is there a reason this one was used?

https://source.chromium.org/chromium/chromium/src/+/main:ios/web/js_messaging/web_frame_impl.mm;l=80;bpv=1;bpt=1?q=CallJavaScriptFunctionInContentWorld&start=11

Kirubel Aklilu

I didn't know there was a default, thanks for pointing it out! Since [CallJavaScriptFunction](https://source.chromium.org/chromium/chromium/src/+/main:ios/web/public/js_messaging/java_script_feature.h;l=209;drc=15bd3b5e7b88bad5db5dbc3305e5b3c9e7c5c9d8) required both a timeout and a callback, I assumed there was no default. Otherwise, I'd have expected CallJavaScriptFunction could be called with just a callback.

I've updated this to use that default. I'd chosen 5s since that's what Autofill is using.

Line 39, Patchset 20: // TODO (crbug.com/476090817) - Inject in all frames once we can
Mike Dougherty . unresolved

you might not be able to perform a "click" in an iframe directly. I'm not sure how all these tools will be hooked together, but ptal at the context menu logic and see how clicks are always initiated from the global page coordinates and then messages are sent to iframes within the script rather than from the native side. This is necessary because the native side doesn't have the full layout of the webpage so it can't do the necessary coordinate translations from global points to iframe ones.

Kirubel Aklilu

Agreed that we can't directly click in an iframe with this approach, I'd discussed this in the doc [here](https://docs.google.com/document/d/18owGXUeF1fKLxQTOB_999vuiHeDFY_BIrgzvRW-jn7Y/edit?resourcekey=0-hGeELLnA9OrfwX7zMcOjHg&tab=t.opqz585th1ag#heading=h.74r5hxnods1g).

From the JS, can't we return the relevant data needed for coordinate translations so that the native side can call the desired WebFrame with the correct arguments? I think this is aligned with the Remote Frame Registration approach that autofill currently uses.

I agree that we can use an approach similar to context menu. I think the main benefit is that it requires less hops between native & JS code.

In any case, I plan to handle this in a future CL and have a separate bug for it.

Line 61, Patchset 20: web::WebFrame* main_frame = manager->GetMainWebFrame();
Mike Dougherty . unresolved

It would be better to pass the WebFrame directly into this function instead. As-is, the caller of ClickToolJavaScriptFeature::Click would have no control over the domain the click is performed on.

Kirubel Aklilu

I don't think this is currently feasible since the caller of this JS feature won't know the relevant WebFrame by coordinate until we execute the JS on the page.

If we solve the iframe problem by passing a frame identifier from JS to native then I agree we could make this method take in the web::WebFrame since we'll want to click into.

File ios/chrome/browser/intelligence/actuation/model/tools/resources/click_tool.ts
File-level comment, Patchset 20:
Mike Dougherty . resolved

scripts should have tests based upon `JavascriptTest, https://source.chromium.org/chromium/chromium/src/+/main:ios/web/public/test/javascript_test.h

This allows for basic testing of script functionality without the need for spinning up all the surrounding //ios/web intrastructure.

Kirubel Aklilu

I've added tests in the child CL, https://crrev.com/c/7544258. I didn't pull them into this CL to keep it smaller for review. I've now pulled them into this CL so we land it together.

Line 17, Patchset 20: * @param clickType The type of click (0=UNKNOWN, 1=LEFT, 2=RIGHT).
Mike Dougherty . resolved

why the weird numeric mapping? Should the expected value from MouseEvent.button just be accepted instead? (0/default for left click, 2 for right click)

Kirubel Aklilu

These are the values set in the proto by the caller (see [actions_data.proto](https://source.chromium.org/chromium/chromium/src/+/main:components/optimization_guide/proto/features/actions_data.proto;l=41;drc=bf712ec1a13783224debb691ba88ad5c15b93194)). We're using this proto to stay consistent with Desktop.

Line 58, Patchset 20: return element.dispatchEvent(
Mike Dougherty . resolved

this should be heavily tested via Javascript test as it could break at any time if iOS changes behavior in WebKit and without automated testing, maybe even with it, such failure would likely go unnoticed.

Kirubel Aklilu

Testing was added in a downstream CL. I've pulled it into this CL and moved some of the actuation-specific stuff into a child CL.

The tests listen for and verify these events are dispatched.

Open in Gerrit

Related details

Attention is currently required from:
  • Mike Dougherty
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: I5ea6bcd99ee5617bf025f710a0af6c076a6a6964
Gerrit-Change-Number: 7487709
Gerrit-PatchSet: 24
Gerrit-Owner: Kirubel Aklilu <kak...@chromium.org>
Gerrit-Reviewer: Kirubel Aklilu <kak...@chromium.org>
Gerrit-Reviewer: Matt Reichhoff <mreic...@chromium.org>
Gerrit-Reviewer: Mike Dougherty <mich...@chromium.org>
Gerrit-CC: Fikre Mengistu <fikrem...@chromium.org>
Gerrit-Attention: Mike Dougherty <mich...@chromium.org>
Gerrit-Comment-Date: Thu, 12 Feb 2026 17:52:47 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: Mike Dougherty <mich...@chromium.org>
satisfied_requirement
unsatisfied_requirement
open
diffy

Mike Dougherty (Gerrit)

unread,
Feb 12, 2026, 2:46:13 PM (12 days ago) Feb 12
to Kirubel Aklilu, Fikre Mengistu, Matt Reichhoff, Chromium LUCI CQ, AyeAye, ios-revie...@chromium.org, marq+...@chromium.org, ios-r...@chromium.org
Attention needed from Kirubel Aklilu

Mike Dougherty added 6 comments

File ios/chrome/browser/intelligence/actuation/model/tools/click_tool_java_script_feature.mm
Line 61, Patchset 20: web::WebFrame* main_frame = manager->GetMainWebFrame();
Mike Dougherty . unresolved

It would be better to pass the WebFrame directly into this function instead. As-is, the caller of ClickToolJavaScriptFeature::Click would have no control over the domain the click is performed on.

Kirubel Aklilu

I don't think this is currently feasible since the caller of this JS feature won't know the relevant WebFrame by coordinate until we execute the JS on the page.

If we solve the iframe problem by passing a frame identifier from JS to native then I agree we could make this method take in the web::WebFrame since we'll want to click into.

Mike Dougherty

I don't know how this feature will be called from higher level feature code, but it would be better even if the caller just obtained the WebFrame itself from the WebState and passed it in. As-is, this leaves ClickToolJavaScriptFeature open to vulnerabilities.

The problem with the current implementation is the caller has no guarantee that the click occurs on the expected domain. For example:

  • example.com is loaded, `web_state->GetLastCommittedURL()` is example.com (and maybe feature code even checks that) Activation Tool works to determine it wants to click on something on example.com
  • WebPage navigation is triggered to go to evil.com
  • Feature code calls `ClickToolJavaScriptFeature::Click(web_state,...)`
  • This feature will then click on evil.com

The problem is that this existing API doesn't even provide an opportunity to the feature code to even check which domain will clicked on.

Instead, the feature code should obtain the WebFrame, do validation that the security origin is as expected, and then pass that in here. In that case, if the webpage has navigated to evil.com, the JS will not be executed.

In general, all features should accept a WebFrame pointer instead of WebState.

File ios/chrome/browser/intelligence/actuation/model/tools/click_tool_js_unittest.mm
File-level comment, Patchset 24 (Latest):
Mike Dougherty . unresolved

I've left specific comments below, but please make sure tests are consistent with those already in the codebase. This file feels very inconsistent with the codebase.

go/objc-style#optimize-for-the-reader-not-the-writer

Line 89, Patchset 24 (Latest): <html>
Mike Dougherty . unresolved

This is a fairly long/complex sample page. I would put it into a test only html file instead of inline.

Most tests use existing pages, but this shows how arbitrary files can be served with the embedded test server: https://source.chromium.org/chromium/chromium/src/+/main:ios/web/navigation/history_state_operations_inttest.mm;l=64?q=file:test%20pony%20file:%5Eios

Line 253, Patchset 24 (Latest): }
Mike Dougherty . unresolved

This tests the implementation detail of the click tool JS (sending events) rather than ensuring the tool actually works. There should be tests to ensure that the click on the button actually worked. For example, consider having the button change some visible state on the webpage (like modifying some text) and then validate that the click on the button worked by checking the value of the visible text.

Line 256, Patchset 24 (Latest):INSTANTIATE_TEST_SUITE_P(
Mike Dougherty . unresolved

Why is a parameter based test used here instead of separate tests? I don't think this is the best format. It seems much more convoluted and it risks having all tests cases disabled even if only a single test case were to start failing.

You should move shared logic into helper functions on the test class (like `MakeExpectedEvents`), free functions, or use SetUp as is the common test file format in almost all test files I've seen.

Parameterized helpers should be used only if you want to run many tests in different cases. For example, with and without a feature flag implemented.

Line 326, Patchset 24 (Latest):std::ostream& operator<<(std::ostream& os, const ClickInput& input) {
Mike Dougherty . unresolved

IMO, these custom print functions further confirm that this isn't the right way to write these tests. Even just based on codebase consistency, there are zero other ios tests which use this pattern.

https://source.chromium.org/search?q=%22operator%3C%3C%22%20file:unittest%20file:%5Eios&sq=&ss=chromium%2Fchromium%2Fsrc

Open in Gerrit

Related details

Attention is currently required from:
  • Kirubel Aklilu
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: I5ea6bcd99ee5617bf025f710a0af6c076a6a6964
Gerrit-Change-Number: 7487709
Gerrit-PatchSet: 24
Gerrit-Owner: Kirubel Aklilu <kak...@chromium.org>
Gerrit-Reviewer: Kirubel Aklilu <kak...@chromium.org>
Gerrit-Reviewer: Matt Reichhoff <mreic...@chromium.org>
Gerrit-Reviewer: Mike Dougherty <mich...@chromium.org>
Gerrit-CC: Fikre Mengistu <fikrem...@chromium.org>
Gerrit-Attention: Kirubel Aklilu <kak...@chromium.org>
Gerrit-Comment-Date: Thu, 12 Feb 2026 19:46:03 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: Kirubel Aklilu <kak...@chromium.org>
Comment-In-Reply-To: Mike Dougherty <mich...@chromium.org>
satisfied_requirement
unsatisfied_requirement
open
diffy

Kirubel Aklilu (Gerrit)

unread,
Feb 12, 2026, 5:38:27 PM (12 days ago) Feb 12
to Fikre Mengistu, Mike Dougherty, Matt Reichhoff, Chromium LUCI CQ, AyeAye, ios-revie...@chromium.org, marq+...@chromium.org, ios-r...@chromium.org
Attention needed from Mike Dougherty

Kirubel Aklilu added 7 comments

File ios/chrome/browser/intelligence/actuation/model/tools/click_tool_java_script_feature.mm
Line 61, Patchset 20: web::WebFrame* main_frame = manager->GetMainWebFrame();
Mike Dougherty . unresolved

It would be better to pass the WebFrame directly into this function instead. As-is, the caller of ClickToolJavaScriptFeature::Click would have no control over the domain the click is performed on.

Kirubel Aklilu

I don't think this is currently feasible since the caller of this JS feature won't know the relevant WebFrame by coordinate until we execute the JS on the page.

If we solve the iframe problem by passing a frame identifier from JS to native then I agree we could make this method take in the web::WebFrame since we'll want to click into.

Mike Dougherty

I don't know how this feature will be called from higher level feature code, but it would be better even if the caller just obtained the WebFrame itself from the WebState and passed it in. As-is, this leaves ClickToolJavaScriptFeature open to vulnerabilities.

The problem with the current implementation is the caller has no guarantee that the click occurs on the expected domain. For example:

  • example.com is loaded, `web_state->GetLastCommittedURL()` is example.com (and maybe feature code even checks that) Activation Tool works to determine it wants to click on something on example.com
  • WebPage navigation is triggered to go to evil.com
  • Feature code calls `ClickToolJavaScriptFeature::Click(web_state,...)`
  • This feature will then click on evil.com

The problem is that this existing API doesn't even provide an opportunity to the feature code to even check which domain will clicked on.

Instead, the feature code should obtain the WebFrame, do validation that the security origin is as expected, and then pass that in here. In that case, if the webpage has navigated to evil.com, the JS will not be executed.

In general, all features should accept a WebFrame pointer instead of WebState.

Kirubel Aklilu

OK, I've updated this to use WebFrame. I'll figure out how to identify the right frame and plumb it here in a future CL.

FWIW, I think this scenario may not be possible based on how the wider actuation feature will work, but it seems reasonable to prevent it in this manner.

In general, all features should accept a WebFrame pointer instead of WebState.

I'd recommend adding this guideline to ios/web/public/js_messaging/README.md. There are [several examples](https://source.chromium.org/search?q=f:java_script_feature.mm%20%22GetMainWebFrame%22) of JSFeatures that take in a WebState and use it to get the primary main frame.

File ios/chrome/browser/intelligence/actuation/model/tools/click_tool_js_unittest.mm
File-level comment, Patchset 24:
Mike Dougherty . resolved

I've left specific comments below, but please make sure tests are consistent with those already in the codebase. This file feels very inconsistent with the codebase.

go/objc-style#optimize-for-the-reader-not-the-writer

Kirubel Aklilu

I couldn't glean a well-established pattern since there is only a handful tests that extend web::JavaScriptTest and they use a variety of approaches.

Acknowledge the concern to optimize for readability. I'd thought the parameterized approach was clear and minimized the loc needed to write & review, but that's may just be my opinion.

Line 89, Patchset 24: <html>
Mike Dougherty . resolved

This is a fairly long/complex sample page. I would put it into a test only html file instead of inline.

Most tests use existing pages, but this shows how arbitrary files can be served with the embedded test server: https://source.chromium.org/chromium/chromium/src/+/main:ios/web/navigation/history_state_operations_inttest.mm;l=64?q=file:test%20pony%20file:%5Eios

Kirubel Aklilu

Done, thank you for the example

Line 89, Patchset 24: <html>
Mike Dougherty . resolved

This is a fairly long/complex sample page. I would put it into a test only html file instead of inline.

Most tests use existing pages, but this shows how arbitrary files can be served with the embedded test server: https://source.chromium.org/chromium/chromium/src/+/main:ios/web/navigation/history_state_operations_inttest.mm;l=64?q=file:test%20pony%20file:%5Eios

Kirubel Aklilu

Done, thanks for the example

Line 253, Patchset 24: }
Mike Dougherty . resolved

This tests the implementation detail of the click tool JS (sending events) rather than ensuring the tool actually works. There should be tests to ensure that the click on the button actually worked. For example, consider having the button change some visible state on the webpage (like modifying some text) and then validate that the click on the button worked by checking the value of the visible text.

Kirubel Aklilu

Done, although I kept the logic in the HTML that records the events and saves it on the window.

Line 256, Patchset 24:INSTANTIATE_TEST_SUITE_P(
Mike Dougherty . resolved

Why is a parameter based test used here instead of separate tests? I don't think this is the best format. It seems much more convoluted and it risks having all tests cases disabled even if only a single test case were to start failing.

You should move shared logic into helper functions on the test class (like `MakeExpectedEvents`), free functions, or use SetUp as is the common test file format in almost all test files I've seen.

Parameterized helpers should be used only if you want to run many tests in different cases. For example, with and without a feature flag implemented.

Kirubel Aklilu

Acknowledge the concern, I've un-parameterized these tests.

I've seen parameterized tests used in this manner before to test a simple function over many (input,output) pairs while minimizing repeated setup. I agree that we can utilize helpers and that separate test cases is better for gardeners.

Line 326, Patchset 24:std::ostream& operator<<(std::ostream& os, const ClickInput& input) {
Mike Dougherty . resolved

IMO, these custom print functions further confirm that this isn't the right way to write these tests. Even just based on codebase consistency, there are zero other ios tests which use this pattern.

https://source.chromium.org/search?q=%22operator%3C%3C%22%20file:unittest%20file:%5Eios&sq=&ss=chromium%2Fchromium%2Fsrc

Kirubel Aklilu

Done, I'd added these to make debugging the parameterized tests easier. I've seen code like this in other parts of chrome but ack that it's not in ios.

Open in Gerrit

Related details

Attention is currently required from:
  • Mike Dougherty
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: I5ea6bcd99ee5617bf025f710a0af6c076a6a6964
Gerrit-Change-Number: 7487709
Gerrit-PatchSet: 25
Gerrit-Owner: Kirubel Aklilu <kak...@chromium.org>
Gerrit-Reviewer: Kirubel Aklilu <kak...@chromium.org>
Gerrit-Reviewer: Matt Reichhoff <mreic...@chromium.org>
Gerrit-Reviewer: Mike Dougherty <mich...@chromium.org>
Gerrit-CC: Fikre Mengistu <fikrem...@chromium.org>
Gerrit-Attention: Mike Dougherty <mich...@chromium.org>
Gerrit-Comment-Date: Thu, 12 Feb 2026 22:38:21 +0000
satisfied_requirement
unsatisfied_requirement
open
diffy

Kirubel Aklilu (Gerrit)

unread,
Feb 13, 2026, 10:15:57 AM (11 days ago) Feb 13
to Rohit Rao, Mike Dougherty, Fikre Mengistu, Matt Reichhoff, Chromium LUCI CQ, AyeAye, ios-revie...@chromium.org, marq+...@chromium.org, ios-r...@chromium.org
Attention needed from Rohit Rao

Kirubel Aklilu added 1 comment

Patchset-level comments
File-level comment, Patchset 25 (Latest):
Kirubel Aklilu . resolved

+rohitrao@, can you review these changes?

moving michaeldo@ to cc since he's OOO for the next week.

Open in Gerrit

Related details

Attention is currently required from:
  • Rohit Rao
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: I5ea6bcd99ee5617bf025f710a0af6c076a6a6964
Gerrit-Change-Number: 7487709
Gerrit-PatchSet: 25
Gerrit-Owner: Kirubel Aklilu <kak...@chromium.org>
Gerrit-Reviewer: Kirubel Aklilu <kak...@chromium.org>
Gerrit-Reviewer: Matt Reichhoff <mreic...@chromium.org>
Gerrit-Reviewer: Rohit Rao <rohi...@chromium.org>
Gerrit-CC: Fikre Mengistu <fikrem...@chromium.org>
Gerrit-CC: Mike Dougherty <mich...@chromium.org>
Gerrit-Attention: Rohit Rao <rohi...@chromium.org>
Gerrit-Comment-Date: Fri, 13 Feb 2026 15:15:50 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
satisfied_requirement
unsatisfied_requirement
open
diffy

Mike Dougherty (Gerrit)

unread,
2:07 PM (5 hours ago) 2:07 PM
to Kirubel Aklilu, Code Review Nudger, Rohit Rao, Fikre Mengistu, Matt Reichhoff, Chromium LUCI CQ, AyeAye, ios-revie...@chromium.org, marq+...@chromium.org, ios-r...@chromium.org
Attention needed from Kirubel Aklilu

Mike Dougherty added 7 comments

File ios/chrome/browser/intelligence/actuation/model/tools/click_tool_java_script_feature.mm
Line 39, Patchset 20: // TODO (crbug.com/476090817) - Inject in all frames once we can
Mike Dougherty . resolved

you might not be able to perform a "click" in an iframe directly. I'm not sure how all these tools will be hooked together, but ptal at the context menu logic and see how clicks are always initiated from the global page coordinates and then messages are sent to iframes within the script rather than from the native side. This is necessary because the native side doesn't have the full layout of the webpage so it can't do the necessary coordinate translations from global points to iframe ones.

Kirubel Aklilu

Agreed that we can't directly click in an iframe with this approach, I'd discussed this in the doc [here](https://docs.google.com/document/d/18owGXUeF1fKLxQTOB_999vuiHeDFY_BIrgzvRW-jn7Y/edit?resourcekey=0-hGeELLnA9OrfwX7zMcOjHg&tab=t.opqz585th1ag#heading=h.74r5hxnods1g).

From the JS, can't we return the relevant data needed for coordinate translations so that the native side can call the desired WebFrame with the correct arguments? I think this is aligned with the Remote Frame Registration approach that autofill currently uses.

I agree that we can use an approach similar to context menu. I think the main benefit is that it requires less hops between native & JS code.

In any case, I plan to handle this in a future CL and have a separate bug for it.

Mike Dougherty

I am not aware of how autofill does this today, if they pass the coordinates back to the native side it should work for you too.

Line 61, Patchset 20: web::WebFrame* main_frame = manager->GetMainWebFrame();
Mike Dougherty . resolved

It would be better to pass the WebFrame directly into this function instead. As-is, the caller of ClickToolJavaScriptFeature::Click would have no control over the domain the click is performed on.

Kirubel Aklilu

I don't think this is currently feasible since the caller of this JS feature won't know the relevant WebFrame by coordinate until we execute the JS on the page.

If we solve the iframe problem by passing a frame identifier from JS to native then I agree we could make this method take in the web::WebFrame since we'll want to click into.

Mike Dougherty

I don't know how this feature will be called from higher level feature code, but it would be better even if the caller just obtained the WebFrame itself from the WebState and passed it in. As-is, this leaves ClickToolJavaScriptFeature open to vulnerabilities.

The problem with the current implementation is the caller has no guarantee that the click occurs on the expected domain. For example:

  • example.com is loaded, `web_state->GetLastCommittedURL()` is example.com (and maybe feature code even checks that) Activation Tool works to determine it wants to click on something on example.com
  • WebPage navigation is triggered to go to evil.com
  • Feature code calls `ClickToolJavaScriptFeature::Click(web_state,...)`
  • This feature will then click on evil.com

The problem is that this existing API doesn't even provide an opportunity to the feature code to even check which domain will clicked on.

Instead, the feature code should obtain the WebFrame, do validation that the security origin is as expected, and then pass that in here. In that case, if the webpage has navigated to evil.com, the JS will not be executed.

In general, all features should accept a WebFrame pointer instead of WebState.

Kirubel Aklilu

OK, I've updated this to use WebFrame. I'll figure out how to identify the right frame and plumb it here in a future CL.

FWIW, I think this scenario may not be possible based on how the wider actuation feature will work, but it seems reasonable to prevent it in this manner.

In general, all features should accept a WebFrame pointer instead of WebState.

I'd recommend adding this guideline to ios/web/public/js_messaging/README.md. There are [several examples](https://source.chromium.org/search?q=f:java_script_feature.mm%20%22GetMainWebFrame%22) of JSFeatures that take in a WebState and use it to get the primary main frame.

Mike Dougherty

Understood, I've made a note to cleanup that readme. There is a note about using WebFrame directly, but it's at the very end of the file, buried in a paragraph about various topics so I'll clean that up. I'll also review the existing features taking a WebState.

File ios/chrome/browser/intelligence/actuation/model/tools/click_tool_java_script_feature_unittest.mm
Line 37, Patchset 28 (Latest):class ClickToolJavaScriptFeatureTest : public PlatformTest {
Mike Dougherty . unresolved

is there a reason that PlatformTest is used instead of `WebTestWithWebState` which will setup the WebState for you?

Line 87, Patchset 28 (Latest): ASSERT_TRUE(base::test::ios::WaitUntilConditionOrTimeout(
Mike Dougherty . unresolved

you don't need to check for __gCrWeb if you instead wiat for the WebFrame instead. It's also possible that this wold pass, but your future calls to get the main frame fail since frame registration is async.

I would update this to simply wait for the main frame similar to this test:
https://source.chromium.org/chromium/chromium/src/+/main:ios/web/js_features/clipboard/clipboard_java_script_feature_unittest.mm;drc=75bd37cd98d8654e740d5b267c1be6fbf81c6ede;l=135?q=file:%5Eios%2Fweb%20wait%20mainwebframe

I thought WaitForMainFrame() or similar was a common shared helper, but I couldn't find it. (I'll look into adding it into a shared location in the future.)

File ios/chrome/browser/intelligence/actuation/model/tools/click_tool_js_unittest.mm
Mike Dougherty . resolved

I've left specific comments below, but please make sure tests are consistent with those already in the codebase. This file feels very inconsistent with the codebase.

go/objc-style#optimize-for-the-reader-not-the-writer

Kirubel Aklilu

I couldn't glean a well-established pattern since there is only a handful tests that extend web::JavaScriptTest and they use a variety of approaches.

Acknowledge the concern to optimize for readability. I'd thought the parameterized approach was clear and minimized the loc needed to write & review, but that's may just be my opinion.

Mike Dougherty

feel free to reach out if you ever need an example for the best way to do something. Or, alternatively, I tend to append `(file:^ios OR (file:^components AND file:ios))` to my code search query and then look at the examples with the most recent copyright date at the top of the file.

Mike Dougherty . unresolved

This is a fairly long/complex sample page. I would put it into a test only html file instead of inline.

Most tests use existing pages, but this shows how arbitrary files can be served with the embedded test server: https://source.chromium.org/chromium/chromium/src/+/main:ios/web/navigation/history_state_operations_inttest.mm;l=64?q=file:test%20pony%20file:%5Eios

Kirubel Aklilu

Done, thanks for the example

Mike Dougherty

np. You should still be able to use the EmbeddedTestServer as in the example instead of manually reading the string file and loading it this way. For example: https://source.chromium.org/search?q=EmbeddedTestServer%20javascripttest%20file:%5Eios&sq=

Line 224, Patchset 28 (Latest):TEST_F(ClickToolJavascriptTest, PHYSICAL_SingleClick_OnButton) {
Mike Dougherty . unresolved

Why is this and "DIPS" above all caps?

Open in Gerrit

Related details

Attention is currently required from:
  • Kirubel Aklilu
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: I5ea6bcd99ee5617bf025f710a0af6c076a6a6964
Gerrit-Change-Number: 7487709
Gerrit-PatchSet: 28
Gerrit-Owner: Kirubel Aklilu <kak...@chromium.org>
Gerrit-Reviewer: Kirubel Aklilu <kak...@chromium.org>
Gerrit-Reviewer: Matt Reichhoff <mreic...@chromium.org>
Gerrit-Reviewer: Mike Dougherty <mich...@chromium.org>
Gerrit-Reviewer: Rohit Rao <rohi...@chromium.org>
Gerrit-CC: Code Review Nudger <android-build...@prod.google.com>
Gerrit-CC: Fikre Mengistu <fikrem...@chromium.org>
Gerrit-Attention: Kirubel Aklilu <kak...@chromium.org>
Gerrit-Comment-Date: Tue, 24 Feb 2026 19:07:17 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
satisfied_requirement
unsatisfied_requirement
open
diffy
Reply all
Reply to author
Forward
0 new messages