[iOS] Compile page tools feature ineligibility reasons [chromium/src : main]

0 views
Skip to first unread message

Francis Beauchamp (Gerrit)

unread,
11:26 AM (10 hours ago) 11:26 AM
to Nicolas MacBeth, Ibrahim Kanouche, Chromium LUCI CQ, chromium...@chromium.org, feature-me...@chromium.org, ios-revie...@chromium.org, ios-r...@chromium.org, marq+...@chromium.org
Attention needed from Ibrahim Kanouche and Nicolas MacBeth

Francis Beauchamp added 26 comments

Patchset-level comments
File-level comment, Patchset 8 (Latest):
Francis Beauchamp . resolved

Updated according to your comments. I'll double check the whole thing to make sure everything was addressed properly

Commit Message
Line 9, Patchset 6:This change introduces the various reasons for features being made non
available within the page tools menu. The UI that supports the feedback
Nicolas MacBeth . resolved

can you elaborate a little more on the changes in the commit message please?

Francis Beauchamp

Done

File ios/chrome/browser/intelligence/bwg/model/bwg_service.h
Line 41, Patchset 6: // need to know the reason for non eligibility of the profile.
Ibrahim Kanouche . resolved

Nit: "non eligibility" -> "ineligibility".

Francis Beauchamp

Done

File ios/chrome/browser/intelligence/bwg/model/gemini_profile_eligibility_result.h
Line 37, Patchset 6: IneligibilityReasons ineligibilityReasons_;
Nicolas MacBeth . resolved

nit: `ineligibility_reasons_`

Francis Beauchamp

Done

Line 29, Patchset 6: const IneligibilityReasons& ineligibilityReasons() const {
Nicolas MacBeth . resolved

nit: ineligibility_reasons()

Francis Beauchamp

Probably a nitwish, but it would be nice if `git cl format` would allow formatting names so they align with language specific conventions. I find it to be quite distracting to remember if this is objc or c++ and should use snake, camel case or whatnot. Are we allowed to make changes to those scripts? I'd be willing to investigate the possibility of making that change.

Line 26, Patchset 6: bool isEligible() const { return is_eligible_; }
Nicolas MacBeth . resolved

if you do keep this class, `IsEligibile()`

Francis Beauchamp

I think I want all of this object to be immutable. I'll probably make the properties constants. The idea is that the static "builder" functions should be the only way to create this.

Francis Beauchamp

Done

File-level comment, Patchset 6:
Nicolas MacBeth . resolved

instead of this whole file/class, what about wrapping ineligibility reasons in a `std::optional`? would be quite idiomatic and also no need for this whole and its maintenance. no a strong opinion, food for though

Francis Beauchamp

So my naive long term goal would be to delegate more of the computation of eligibility in here if possible since it has grown a lot over the last few weeks (i.e. remove the cognitive burden of needing ~8 booleans to evaluate eligibility in the service). I'm not certain an `std::optional` gracefully allows for that, but maybe I'me foreseeing something for which we have no need? Would be curious to know your thoughts on this.

Nicolas MacBeth

std::optional could wrap the presence or not of ineligibility reasons, instead of the class in its current state, but if you prefer to move the actual computation of it down to this class, then that makes more sense to me!

Francis Beauchamp

I'll try your approach but for more insight, what I had in mind somewhat aligns with the [expected](https://en.cppreference.com/w/cpp/utility/expected.html) type.

File ios/chrome/browser/intelligence/page_action_menu/coordinator/page_action_menu_mediator.mm
Line 139, Patchset 4 (Parent):- (BOOL)isLensAvailableForTraitCollection:(UITraitCollection*)traitCollection {
Francis Beauchamp . resolved

Note to the reviewer: these functions have been replaced with their "entry point" counterparts, which adds a new abstraction layer for supporting the footer items linked to ineligibility reasons.

Francis Beauchamp

Done

Line 150, Patchset 6: if (!_geminiService) {
Ibrahim Kanouche . resolved

This method is missing the check for `_geminiTabHelper`. The previous implementation `isGeminiAvailable` checked for both `_geminiService` and `_geminiTabHelper`. Is this intentional?

Francis Beauchamp

Not intentional; it seems like a regression as Nic suggested in the comment above. The code should now align with the desired behaviour.

Line 151, Patchset 6 (Parent): return _geminiTabHelper->IsGeminiAvailableForWebState() &&
Nicolas MacBeth . resolved

is it intended that we no longer check the WebState eligibility?

Francis Beauchamp

I need to reassess; I've made this CL a while ago and this is possible that it was an oversight on my end.

Nicolas MacBeth

Ack

Francis Beauchamp

I've updated the code as you ad Ibrahim suggested; this should no longer be an issue but feel free to double check again.

Line 157, Patchset 6: if (result.isEligible()) {
Ibrahim Kanouche . resolved

This no longer checks `_geminiTabHelper->IsGeminiAvailableForWebState()`. This means Gemini could be enabled on pages where it is meant to be disabled (like internal chrome:// pages).

You should probably return `initWithEnabled:YES` only if `result.isEligible()` AND `_geminiTabHelper->IsGeminiAvailableForWebState()` are both true.

Francis Beauchamp

I've updated the checks here if you want to double check.

If unwrapping the optional yields a value, the profile is ineligible. We look at the ineligibility reasons that support footer items to populate the "view model".

Otherwise, the profile is eligible and we simply check if Gemini is eligible for the webState in order to set the entry point availability.

Line 161, Patchset 6: if (result.ineligibilityReasons().chrome_enterprise) {
// TODO(crbug.com/485297147): Add string when the footer UI gets
// implemented.
return [[PageActionMenuContentEntryPoint alloc]
initWithEnabled:NO
footerItem:
[[ContentEntryPointUnavailabilityItem alloc]
initWithText:@""
andIcon:
[CustomSymbolWithPointSize(
kEnterpriseSymbol, kFeatureRowIconSize)
imageWithRenderingMode:
UIImageRenderingModeAlwaysTemplate]
andActionIdentifier:nil]];
}
Nicolas MacBeth . resolved

instead, can we add a TODO? I prefer to have landed code in a state that is not broken on its own (no string). thanks!

Francis Beauchamp

I would be tempted to agree in spirit, but the UI doesn't exist yet here; none of the parameters in the `footerIem` are going to be reflected anywhere in the page tools. The next cl in the chain will use the footer item to showcase the unavailability, while this CL only relies on the `enabled`. A possible solution to this would be to temporarily make the `footerItem` parameter `nil`, although the end result is exactly the same for all code paths and behaviour.

Nicolas MacBeth

Acknowledged, ok either way then for me

Francis Beauchamp

I'll do the change, I'll just need to rebase a few CLs down the line and I'm more than happy if it mitigates your concerns.

Francis Beauchamp

Done

Line 190, Patchset 6: if (!featureAvailable) {
// TODO(crbug.com/485297147): Add string when the footer UI gets
// implemented.
return [[PageActionMenuContentEntryPoint alloc]
initWithEnabled:NO
footerItem:
[[ContentEntryPointUnavailabilityItem alloc]
initWithText:@""
andIcon:
[CustomSymbolWithPointSize(
kEnterpriseSymbol, kFeatureRowIconSize)
imageWithRenderingMode:
UIImageRenderingModeAlwaysTemplate]
andActionIdentifier:nil]];
}

if (!hasDefaultSearchEngine) {
// TODO(crbug.com/485297147): Add string when the footer UI gets
// implemented.
return [[PageActionMenuContentEntryPoint alloc]
initWithEnabled:NO
footerItem:[[ContentEntryPointUnavailabilityItem alloc]
initWithText:@""]];
}
Nicolas MacBeth . resolved

same TODO comment for these

Francis Beauchamp

Done

Line 215, Patchset 6: //  Disabled without disclaimer
Nicolas MacBeth . resolved

nit: period at the end of comment.

Francis Beauchamp

Done

File ios/chrome/browser/intelligence/page_action_menu/ui/page_action_menu_content_entry_point.h
Line 31, Patchset 6:- (instancetype)initWithEnabled:(BOOL)enabled;
Nicolas MacBeth . resolved

here and for above interface: set as designated initializer and `NS_UNAVAILABLE` the normal `init` (assuming we only want the user to use these)

Francis Beauchamp

Done

Line 19, Patchset 6:- (instancetype)initWithText:(NSString*)text
Ibrahim Kanouche . resolved

The `and` prefix for subsequent parameters is usually omitted: `initWithText:(NSString*)text icon:(UIImage*)icon actionIdentifier:(NSString*)actionIdentifier;`

Francis Beauchamp

I'll update accordingly; wasn't aware we had that in our style guide.

Line 19, Patchset 6:- (instancetype)initWithText:(NSString*)text
Ibrahim Kanouche . unresolved

The `and` prefix for subsequent parameters is usually omitted: `initWithText:(NSString*)text icon:(UIImage*)icon actionIdentifier:(NSString*)actionIdentifier;`

Francis Beauchamp

Is that always the case? That goes against usual objc

Line 14, Patchset 6:@property(nonatomic, copy) NSString* text;
@property(nonatomic, strong) UIImage* icon;
@property(nonatomic, copy) NSString* actionIdentifier;
Nicolas MacBeth . resolved

here and below, will you need them to be public? if not, they can be ivars instead

also, comments for these please

Francis Beauchamp

I think the confusion comes from the fact that these are used in the next CL by the view controller. They cannot be ivars since the UI will need to access these properties.

Line 12, Patchset 6:@interface ContentEntryPointUnavailabilityItem : NSObject
Nicolas MacBeth . resolved

nit: for this and interface below, comments

Francis Beauchamp

Kind of a parallel question; do we generate documentation with our comments? I don't see us using the `///` format which allows for that in iOS code. It comes with the additional drawback of `alt + click` never giving descriptions for any of the commented declarations.

Nicolas MacBeth

some people use it, but you'll see it quite rarely indeed!

Francis Beauchamp

Acknowledged

Line 10, Patchset 6:@class CrURL;
Nicolas MacBeth . resolved

still needed?

Francis Beauchamp

Should go in the garbage, I moved from a URL to a simple string identifier since the URLs opened link to internal pages.

Nicolas MacBeth

Ack

Francis Beauchamp

Done

Line 10, Patchset 6:@class CrURL;
Nicolas MacBeth . resolved

still needed?

Francis Beauchamp

Should go in the garbage, I moved from a URL to a simple string identifier since the URLs opened link to internal pages.

Nicolas MacBeth

Ack

Francis Beauchamp

Done

File ios/chrome/browser/intelligence/page_action_menu/ui/page_action_menu_content_entry_point.mm
Line 12, Patchset 6: self = [self initWithEnabled:enabled footerItem:nil];
Ibrahim Kanouche . resolved

This can be simplified to:
`return [self initWithEnabled:enabled footerItem:nil];`

Francis Beauchamp

Done

Line 31, Patchset 6: DCHECK(text);
Nicolas MacBeth . resolved

prefer `CHECK`

Francis Beauchamp

Done

Line 44, Patchset 6: _actionIdentifier = actionIdentifier;
Nicolas MacBeth . resolved

`copy` as well?

Francis Beauchamp

Done

File ios/chrome/browser/intelligence/page_action_menu/ui/page_action_menu_view_controller.mm
Line 205, Patchset 6:- (void)pageLoadStatusChanged {
Nicolas MacBeth . resolved

in the current state of the patch, this no longer checks webstate and only profile level checks, so does this need to stay?

Francis Beauchamp

Should be fixed by checking the web state now.

Line 627, Patchset 4:// Handles toggle switch changes for permission-based features.
Francis Beauchamp . resolved

Note to the reviewer: I have moved these functions to regroup all of the handlers together.

Francis Beauchamp

Done

Open in Gerrit

Related details

Attention is currently required from:
  • Ibrahim Kanouche
  • Nicolas MacBeth
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: I86220841ee5faa24d3807ecd8b4edd25ccf7231c
Gerrit-Change-Number: 7674377
Gerrit-PatchSet: 8
Gerrit-Owner: Francis Beauchamp <fbeau...@google.com>
Gerrit-Reviewer: Francis Beauchamp <fbeau...@google.com>
Gerrit-Reviewer: Ibrahim Kanouche <kano...@google.com>
Gerrit-Reviewer: Nicolas MacBeth <nicolas...@google.com>
Gerrit-Attention: Nicolas MacBeth <nicolas...@google.com>
Gerrit-Attention: Ibrahim Kanouche <kano...@google.com>
Gerrit-Comment-Date: Tue, 24 Mar 2026 15:26:37 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: No
Comment-In-Reply-To: Nicolas MacBeth <nicolas...@google.com>
Comment-In-Reply-To: Ibrahim Kanouche <kano...@google.com>
Comment-In-Reply-To: Francis Beauchamp <fbeau...@google.com>
satisfied_requirement
unsatisfied_requirement
open
diffy

Nicolas MacBeth (Gerrit)

unread,
1:25 PM (8 hours ago) 1:25 PM
to Francis Beauchamp, Ibrahim Kanouche, Chromium LUCI CQ, chromium...@chromium.org, feature-me...@chromium.org, ios-revie...@chromium.org, ios-r...@chromium.org, marq+...@chromium.org
Attention needed from Francis Beauchamp and Ibrahim Kanouche

Nicolas MacBeth voted and added 6 comments

Votes added by Nicolas MacBeth

Code-Review+1

6 comments

Patchset-level comments
Nicolas MacBeth . resolved

lgtm with a few nits, thanks Francis!

File ios/chrome/browser/intelligence/bwg/model/bwg_service.h
Line 44, Patchset 8 (Latest): // are deemed eligible for Gemini will result in std::nulopt.
Nicolas MacBeth . unresolved

nit: nullopt

File ios/chrome/browser/intelligence/bwg/model/bwg_service.mm
Line 100, Patchset 8 (Latest): return is_eligible ? std::optional<gemini::IneligibilityReasons>()
Nicolas MacBeth . unresolved

`std::nullopt`?

File ios/chrome/browser/intelligence/page_action_menu/coordinator/page_action_menu_mediator.mm
Line 167, Patchset 8 (Latest): return [[PageActionMenuContentEntryPoint alloc]
initWithEnabled:_geminiTabHelper->IsGeminiAvailableForWebState()];
Nicolas MacBeth . unresolved

might be a larger UX question so could definitely be a follow-up CL, but this would mean that the user would see some eligibility issue string, fix it, and then can come back and still not be able to use Gemini due to the webstate not being eligibile. should we instead only surface the eligibility issue if the webstate is valid to use gemini on?

File ios/chrome/browser/intelligence/page_action_menu/ui/page_action_menu_content_entry_point.h
Line 24, Patchset 8 (Latest):- (instancetype)initWithText:(NSString*)text
Nicolas MacBeth . unresolved

nit: comment for this initializer too

Line 20, Patchset 8 (Latest):@property(nonatomic, copy) NSString* actionIdentifier;
Nicolas MacBeth . unresolved

not sure what this is for in upcoming CLs, but consider using `GURL`. OK to proceed without though for now

Open in Gerrit

Related details

Attention is currently required from:
  • Francis Beauchamp
  • Ibrahim Kanouche
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: I86220841ee5faa24d3807ecd8b4edd25ccf7231c
Gerrit-Change-Number: 7674377
Gerrit-PatchSet: 8
Gerrit-Owner: Francis Beauchamp <fbeau...@google.com>
Gerrit-Reviewer: Francis Beauchamp <fbeau...@google.com>
Gerrit-Reviewer: Ibrahim Kanouche <kano...@google.com>
Gerrit-Reviewer: Nicolas MacBeth <nicolas...@google.com>
Gerrit-Attention: Ibrahim Kanouche <kano...@google.com>
Gerrit-Attention: Francis Beauchamp <fbeau...@google.com>
Gerrit-Comment-Date: Tue, 24 Mar 2026 17:25:35 +0000
Gerrit-HasComments: Yes
Gerrit-Has-Labels: Yes
satisfied_requirement
unsatisfied_requirement
open
diffy

Francis Beauchamp (Gerrit)

unread,
3:03 PM (7 hours ago) 3:03 PM
to Nicolas MacBeth, Ibrahim Kanouche, Chromium LUCI CQ, chromium...@chromium.org, feature-me...@chromium.org, ios-revie...@chromium.org, ios-r...@chromium.org, marq+...@chromium.org
Attention needed from Ibrahim Kanouche

Francis Beauchamp added 5 comments

File ios/chrome/browser/intelligence/bwg/model/bwg_service.h
Line 44, Patchset 8: // are deemed eligible for Gemini will result in std::nulopt.
Nicolas MacBeth . resolved

nit: nullopt

Francis Beauchamp

Done

File ios/chrome/browser/intelligence/bwg/model/bwg_service.mm
Line 100, Patchset 8: return is_eligible ? std::optional<gemini::IneligibilityReasons>()
Nicolas MacBeth . resolved

`std::nullopt`?

Francis Beauchamp

Compiler was not happy with it because of the ternary, so I had to be extra specific about the associated type of that optional (using the default constructor). I tend to prefer single return points to having multiple return paths like this:

```
if (is_eligible) {
return std::nullopt;
}

return ineligibility_reasons;
```

File ios/chrome/browser/intelligence/page_action_menu/coordinator/page_action_menu_mediator.mm
Line 167, Patchset 8: return [[PageActionMenuContentEntryPoint alloc]
initWithEnabled:_geminiTabHelper->IsGeminiAvailableForWebState()];
Nicolas MacBeth . resolved

might be a larger UX question so could definitely be a follow-up CL, but this would mean that the user would see some eligibility issue string, fix it, and then can come back and still not be able to use Gemini due to the webstate not being eligibile. should we instead only surface the eligibility issue if the webstate is valid to use gemini on?

Francis Beauchamp

Like we discussed offline, I believe this might be something to be investigated at the UX level.

File ios/chrome/browser/intelligence/page_action_menu/ui/page_action_menu_content_entry_point.h
Line 24, Patchset 8:- (instancetype)initWithText:(NSString*)text
Nicolas MacBeth . resolved

nit: comment for this initializer too

Francis Beauchamp

Done

Line 20, Patchset 8:@property(nonatomic, copy) NSString* actionIdentifier;
Nicolas MacBeth . resolved

not sure what this is for in upcoming CLs, but consider using `GURL`. OK to proceed without though for now

Francis Beauchamp

The way the link is handled here is following the pattern we have in the consent screen. We associate an identifier to the link, and as the link gets tapped in the text view, we do not open an URL. Instead, we simply `switch` on the string and trigger the specific logic associated with that action identifier.

See [here](https://source.chromium.org/chromium/chromium/src/+/main:ios/chrome/browser/intelligence/bwg/ui/gemini_consent_view_controller.mm;l=533) for a similar pattern.

Open in Gerrit

Related details

Attention is currently required from:
  • Ibrahim Kanouche
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: I86220841ee5faa24d3807ecd8b4edd25ccf7231c
    Gerrit-Change-Number: 7674377
    Gerrit-PatchSet: 9
    Gerrit-Owner: Francis Beauchamp <fbeau...@google.com>
    Gerrit-Reviewer: Francis Beauchamp <fbeau...@google.com>
    Gerrit-Reviewer: Ibrahim Kanouche <kano...@google.com>
    Gerrit-Reviewer: Nicolas MacBeth <nicolas...@google.com>
    Gerrit-Attention: Ibrahim Kanouche <kano...@google.com>
    Gerrit-Comment-Date: Tue, 24 Mar 2026 19:03:38 +0000
    satisfied_requirement
    open
    diffy
    Reply all
    Reply to author
    Forward
    0 new messages