Skip to content

Commit 3a003ea

Browse files
authored
Add subject type and method to get metadata by origin (#950)
* Add subject type and method to get metadata by origin * Add comment to enum * Rename type field to subjectType
1 parent dcb74f7 commit 3a003ea

File tree

2 files changed

+82
-12
lines changed

2 files changed

+82
-12
lines changed

src/subject-metadata/SubjectMetadataController.test.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
SubjectMetadataControllerActions,
77
SubjectMetadataControllerEvents,
88
SubjectMetadataControllerMessenger,
9+
SubjectType,
910
} from './SubjectMetadataController';
1011

1112
const controllerName = 'SubjectMetadataController';
@@ -48,17 +49,20 @@ function getSubjectMetadataControllerMessenger() {
4849
*
4950
* @param origin - The subject's origin
5051
* @param name - Optional subject name
52+
* @param subjectType - Optional subject type
5153
* @param opts - Optional extra options for the metadata
5254
* @returns The created metadata object
5355
*/
5456
function getSubjectMetadata(
5557
origin: string,
56-
name?: string,
58+
name: string | null = null,
59+
subjectType: SubjectType | null = null,
5760
opts?: Record<string, Json>,
5861
) {
5962
return {
6063
origin,
61-
name: name ?? null,
64+
name,
65+
subjectType,
6266
iconUrl: null,
6367
extensionId: null,
6468
...opts,
@@ -212,6 +216,34 @@ describe('SubjectMetadataController', () => {
212216
});
213217
});
214218

219+
describe('getSubjectMetadata', () => {
220+
it('returns the subject metadata for the given origin', () => {
221+
const [messenger, hasPermissionsSpy] =
222+
getSubjectMetadataControllerMessenger();
223+
const controller = new SubjectMetadataController({
224+
messenger,
225+
subjectCacheLimit: 1,
226+
});
227+
hasPermissionsSpy.mockImplementationOnce(() => true);
228+
229+
controller.addSubjectMetadata(
230+
getSubjectMetadata('foo.com', 'foo', SubjectType.Snap),
231+
);
232+
233+
controller.addSubjectMetadata(
234+
getSubjectMetadata('bar.io', 'bar', SubjectType.Website),
235+
);
236+
237+
expect(controller.getSubjectMetadata('foo.com')).toStrictEqual(
238+
getSubjectMetadata('foo.com', 'foo', SubjectType.Snap),
239+
);
240+
241+
expect(controller.getSubjectMetadata('bar.io')).toStrictEqual(
242+
getSubjectMetadata('bar.io', 'bar', SubjectType.Website),
243+
);
244+
});
245+
});
246+
215247
describe('trimMetadataState', () => {
216248
it('deletes all subjects without permissions from state', () => {
217249
const [messenger, hasPermissionsSpy] =

src/subject-metadata/SubjectMetadataController.ts

Lines changed: 48 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,30 @@ const controllerName = 'SubjectMetadataController';
1313

1414
type SubjectOrigin = string;
1515

16+
/**
17+
* The different kinds of subjects that MetaMask may interact with, including
18+
* third parties and itself (e.g., when the background communicated with the UI).
19+
*/
20+
export enum SubjectType {
21+
Extension = 'extension',
22+
Internal = 'internal',
23+
Unknown = 'unknown',
24+
Website = 'website',
25+
Snap = 'snap',
26+
}
27+
1628
export type SubjectMetadata = PermissionSubjectMetadata & {
1729
[key: string]: Json;
1830
// TODO:TS4.4 make optional
1931
name: string | null;
32+
subjectType: SubjectType | null;
2033
extensionId: string | null;
2134
iconUrl: string | null;
2235
};
2336

2437
type SubjectMetadataToAdd = PermissionSubjectMetadata & {
2538
name?: string | null;
39+
subjectType?: SubjectType | null;
2640
extensionId?: string | null;
2741
iconUrl?: string | null;
2842
} & Record<string, Json>;
@@ -44,7 +58,14 @@ export type GetSubjectMetadataState = {
4458
handler: () => SubjectMetadataControllerState;
4559
};
4660

47-
export type SubjectMetadataControllerActions = GetSubjectMetadataState;
61+
export type GetSubjectMetadata = {
62+
type: `${typeof controllerName}:getSubjectMetadata`;
63+
handler: (origin: SubjectOrigin) => SubjectMetadata | undefined;
64+
};
65+
66+
export type SubjectMetadataControllerActions =
67+
| GetSubjectMetadataState
68+
| GetSubjectMetadata;
4869

4970
export type SubjectMetadataStateChange = {
5071
type: `${typeof controllerName}:stateChange`;
@@ -80,7 +101,7 @@ export class SubjectMetadataController extends BaseController<
80101
> {
81102
private subjectCacheLimit: number;
82103

83-
private subjectsWithoutPermissionsEcounteredSinceStartup: Set<string>;
104+
private subjectsWithoutPermissionsEncounteredSinceStartup: Set<string>;
84105

85106
private subjectHasPermissions: GenericPermissionController['hasPermissions'];
86107

@@ -110,15 +131,20 @@ export class SubjectMetadataController extends BaseController<
110131

111132
this.subjectHasPermissions = hasPermissions;
112133
this.subjectCacheLimit = subjectCacheLimit;
113-
this.subjectsWithoutPermissionsEcounteredSinceStartup = new Set();
134+
this.subjectsWithoutPermissionsEncounteredSinceStartup = new Set();
135+
136+
this.messagingSystem.registerActionHandler(
137+
`${this.name}:getSubjectMetadata`,
138+
this.getSubjectMetadata.bind(this),
139+
);
114140
}
115141

116142
/**
117143
* Clears the state of this controller. Also resets the cache of subjects
118144
* encountered since startup, so as to not prematurely reach the cache limit.
119145
*/
120146
clearState(): void {
121-
this.subjectsWithoutPermissionsEcounteredSinceStartup.clear();
147+
this.subjectsWithoutPermissionsEncounteredSinceStartup.clear();
122148
this.update((_draftState) => {
123149
return { ...defaultState };
124150
});
@@ -143,20 +169,22 @@ export class SubjectMetadataController extends BaseController<
143169
extensionId: metadata.extensionId || null,
144170
iconUrl: metadata.iconUrl || null,
145171
name: metadata.name || null,
172+
subjectType: metadata.subjectType || null,
146173
};
147174

148175
let originToForget: string | null = null;
149176
// We only delete the oldest encountered subject from the cache, again to
150177
// ensure that the user's experience isn't degraded by missing icons etc.
151178
if (
152-
this.subjectsWithoutPermissionsEcounteredSinceStartup.size >=
179+
this.subjectsWithoutPermissionsEncounteredSinceStartup.size >=
153180
this.subjectCacheLimit
154181
) {
155-
const cachedOrigin = this.subjectsWithoutPermissionsEcounteredSinceStartup
156-
.values()
157-
.next().value;
182+
const cachedOrigin =
183+
this.subjectsWithoutPermissionsEncounteredSinceStartup
184+
.values()
185+
.next().value;
158186

159-
this.subjectsWithoutPermissionsEcounteredSinceStartup.delete(
187+
this.subjectsWithoutPermissionsEncounteredSinceStartup.delete(
160188
cachedOrigin,
161189
);
162190

@@ -165,7 +193,7 @@ export class SubjectMetadataController extends BaseController<
165193
}
166194
}
167195

168-
this.subjectsWithoutPermissionsEcounteredSinceStartup.add(origin);
196+
this.subjectsWithoutPermissionsEncounteredSinceStartup.add(origin);
169197

170198
this.update((draftState) => {
171199
// Typecast: ts(2589)
@@ -176,6 +204,16 @@ export class SubjectMetadataController extends BaseController<
176204
});
177205
}
178206

207+
/**
208+
* Gets the subject metadata for the given origin, if any.
209+
*
210+
* @param origin - The origin for which to get the subject metadata.
211+
* @returns The subject metadata, if any, or `undefined` otherwise.
212+
*/
213+
getSubjectMetadata(origin: SubjectOrigin): SubjectMetadata | undefined {
214+
return this.state.subjectMetadata[origin];
215+
}
216+
179217
/**
180218
* Deletes all subjects without permissions from the controller's state.
181219
*/

0 commit comments

Comments
 (0)