Skip to content

ui: add API for command filtering for embedders#154

Merged
cdamus merged 2 commits intosokatoafrom
feature/ui-command-filtering
Oct 7, 2025
Merged

ui: add API for command filtering for embedders#154
cdamus merged 2 commits intosokatoafrom
feature/ui-command-filtering

Conversation

@cdamus
Copy link
Collaborator

@cdamus cdamus commented Oct 6, 2025

In some applications that embed the Perfetto UI, there are a number of commands that are not needed. For example, in an application that manages the opening of traces into instances of the Perfetto UI, there is no need for the commands that open and close traces. And other commands may overlap or conflict with other capabilities provided by the host application, which therefore may wish to exclude those commands.

Add an API to the Registry class to permit an embedding application to install a filter that quietly prevents registration of unwanted services. Employ this capability in the CommandManager.

For android-graphics/sokatoa#3871

Comment on lines 109 to 112
override set filter(filter: ((key: string) => boolean) | undefined) {
this.parent.filter = filter;
super.filter = filter;
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For our use case, this makes sense, but I'm not sure that propagating the setting up to the parent (without the parent then propagating it back down to all children?) would make sense as a general API. In practice, do we need this behavior, i.e. do we end up interacting with a child such that we need to make sure that when we set the filter on that child, it makes its way back up to the root Registry?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We do need this behaviour. Most of the commands that are registered on the parent (AppImpl-level, not open-Trace-level) are inapplicable and/or dangerous in Sokatoa.

We could require an application to separately set a filter on the parent if you prefer that. We certainly have access to it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we have direct access to the registries where we need set filters, then I think removing child -> parent propagation would assuage any concerns I have. I think it's the inconsistency between child construction time (propagation parent -> child) and post-construction time (propagation child -> parent) that makes me a little queasy.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. It'll be cleaner this way.

Comment on lines 40 to 47
set filter(filter: ((key: string) => boolean) | undefined) {
this.keyFilter = filter ?? (() => true);

// Run the filter to knock out anything already registered that does not pass it
[...this.registry.keys()]
.filter((key) => !this.keyFilter(key))
.forEach((key) => this.registry.delete(key));
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This also seems a bit odd as API in the following scenario:

  1. Registrants register a variety of items.
  2. Someone sets a filter that allows only subset A through.
  3. Someone sets a filter that allows only subset B through.
  4. Now we end up with only the intersection of subsets A and B, but the filterer at step (3) might reasonably want to get everything in subset B that was ever registered, since the filter for subset A was never what they were interested in (or knew about, or could control, for whatever reason).

That makes me wonder whether it might make sense (and be possible, of course?) to limit what the registry exposes while retaining a reference to the original registrations? Something like

/** retains everything */
registry
/** target for retrieval: contains the subset of items matching current filter */
filteredRegistry

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The purpose is that the "someone" should be an application that embeds Perfetto and is the only authority for such filters. And so wouldn't be inconsistent about it. Nothing in this PR made that clear, of course.

Perhaps this should be a write-once property. Attempts to change the filter after it has been set should throw an error. And then it should be a setFilter() method and not a set filter prop.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Set once behavior would make the expectations explicit 👍

Comment on lines 238 to 245
test('setting filter on child cascades to parent and prunes both registries', () => {
const parent = Registry.kindRegistry<Registrant>();
const a: Registrant = {kind: 'a', n: 1};
const b: Registrant = {kind: 'b', n: 2};
parent.register(a);
parent.register(b);

const child = parent.createChild();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The scenario of interest here would be something like

const parent = Registry.kindRegistry<Registrant>();
const childA = parent.createChild();
const childB = parent.createChild();

childA.filter = (candidate) => isCoolEnough(candidate);
// Should childB be affected, given that the parent is?

Copy link
Collaborator Author

@cdamus cdamus Oct 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It depends on what you mean by "affected."

Assuming that filtering is not inherited, that filters are separately installed at every level of registry delegation, then:

A child should only inherit from a parent the registrations that the parent itself publishes to the world. But the child can override those registrations.

So, if the parent filters out an uncool registration, then the child should not see it. But if the child does not have the cool filter, then the child should be able to add its own registration of an uncool entry.

Assuming that filtering is inherited, then

As always, a child cannot delegate the look-up of an uncool entry to the parent if the parent has a cool filter.

Moreover, a child should not be able to register an uncool entry if the parent has a cool filter.

cdamus added 2 commits October 7, 2025 12:02
In some applications that embed the Perfetto UI, there are a number of
commands that are not needed. For example, in an application that
manages the opening of traces into instances of the Perfetto UI, there
is no need for the commands that open and close traces. And other
commands may overlap or conflict with other capabilities provided by the
host application, which therefore may wish to exclude those commands.

Add an API to the Registry class to permit an embedding application to
install a filter that quietly prevents registration of unwanted
services. Employ this capability in the CommandManager.

Signed-off-by: Christian W. Damus <cdamus.ext@eclipsesource.com>
- don't push child filter to parent
- dynamically inherit parent filter to child
  in addition to child filter
- make the filter write-once and be explicit about its purpose for
  embedder use cases

Signed-off-by: Christian W. Damus <cdamus.ext@eclipsesource.com>
@cdamus cdamus force-pushed the feature/ui-command-filtering branch from 13c37ce to 848a0a3 Compare October 7, 2025 17:51
@cdamus
Copy link
Collaborator Author

cdamus commented Oct 7, 2025

Thanks for the review @colin-grant-work . Commit 848a0a3 should address your comments.

Copy link
Collaborator

@colin-grant-work colin-grant-work left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the changes. LGTM.

@cdamus cdamus merged commit 62f010a into sokatoa Oct 7, 2025
1 check passed
@cdamus cdamus deleted the feature/ui-command-filtering branch October 20, 2025 19:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

Comments