Skip to content

Commit 8b38da8

Browse files
Lightning00BladeDevtools-frontend LUCI CQ
authored andcommitted
[AI Assistance] Use DevTools simple selector
Bug: 393267006 Change-Id: I7888f477e214c560abece0cfa6eca1b12c57f762 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6298062 Reviewed-by: Alex Rudenko <[email protected]> Commit-Queue: Nikolay Vitkov <[email protected]>
1 parent 8828ddd commit 8b38da8

File tree

2 files changed

+85
-9
lines changed

2 files changed

+85
-9
lines changed

front_end/panels/ai_assistance/ExtensionScope.test.ts

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,26 @@ import {createCSSStyle, getMatchedStyles, ruleMatch} from '../../testing/StyleHe
99
import * as ExtensionScope from './ExtensionScope.js';
1010
import * as Injected from './injected.js';
1111

12-
async function getSelector(payload: Partial<SDK.CSSMatchedStyles.CSSMatchedStylesPayload>) {
12+
function createNode(options?: {getAttribute?: (attribute: string) => string | undefined}) {
1313
const node = sinon.createStubInstance(SDK.DOMModel.DOMNode);
1414
node.id = 1 as Protocol.DOM.NodeId;
1515
// Needed to process the inline styles
1616
node.nodeType.returns(Node.ELEMENT_NODE);
17+
node.localName.returns('div');
18+
node.simpleSelector.callThrough();
19+
if (options?.getAttribute) {
20+
node.getAttribute.callsFake(options.getAttribute);
21+
}
22+
return node;
23+
}
24+
25+
async function getSelector(
26+
payload: Partial<SDK.CSSMatchedStyles.CSSMatchedStylesPayload>,
27+
node?: SDK.DOMModel.DOMNode,
28+
) {
29+
if (!node) {
30+
node = createNode();
31+
}
1732

1833
const matchedStyles = await getMatchedStyles({
1934
node,
@@ -31,7 +46,51 @@ describe('ExtensionScope', () => {
3146
},
3247
];
3348

34-
describe('getSelector', () => {
49+
describe('getSimpleSelector', () => {
50+
it('should work with node that has classes', () => {
51+
const node = createNode({
52+
getAttribute: attribute => {
53+
if (attribute === 'class') {
54+
return 'my-class-a my-class-b';
55+
}
56+
57+
return undefined;
58+
}
59+
});
60+
const selector = ExtensionScope.ExtensionScope.getSelectorForNode(node);
61+
assert.strictEqual(selector, '.my-class-a.my-class-b');
62+
});
63+
64+
it('should exclude ai generated class', () => {
65+
const node = createNode({
66+
getAttribute: attribute => {
67+
if (attribute === 'class') {
68+
return `my-class-a my-class-b ${Injected.AI_ASSISTANCE_CSS_CLASS_NAME}-2`;
69+
}
70+
71+
return undefined;
72+
}
73+
});
74+
const selector = ExtensionScope.ExtensionScope.getSelectorForNode(node);
75+
assert.strictEqual(selector, '.my-class-a.my-class-b');
76+
});
77+
78+
it('should work with node has classes that need escaping', () => {
79+
const node = createNode({
80+
getAttribute: attribute => {
81+
if (attribute === 'class') {
82+
return `my.special-class my-class-b ${Injected.AI_ASSISTANCE_CSS_CLASS_NAME}-2`;
83+
}
84+
85+
return undefined;
86+
}
87+
});
88+
const selector = ExtensionScope.ExtensionScope.getSelectorForNode(node);
89+
assert.strictEqual(selector, '.my\\.special-class.my-class-b');
90+
});
91+
});
92+
93+
describe('getSelectorFromRules', () => {
3594
it('should work with empty styles', async () => {
3695
const selector = await getSelector({});
3796
assert.strictEqual(selector, '');

front_end/panels/ai_assistance/ExtensionScope.ts

Lines changed: 24 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,15 @@ export class ExtensionScope {
197197
return selectors.at(0)?.text.replace(':visited', '') ?? '';
198198
}
199199

200+
static getSelectorForNode(node: SDK.DOMModel.DOMNode): string {
201+
return node.simpleSelector()
202+
.split('.')
203+
.filter(chunk => {
204+
return !chunk.startsWith(AI_ASSISTANCE_CSS_CLASS_NAME);
205+
})
206+
.join('.');
207+
}
208+
200209
async #computeSelectorFromElement(remoteObject: SDK.RemoteObject.RemoteObject): Promise<string> {
201210
if (!remoteObject.objectId) {
202211
throw new Error('DOMModel is not found');
@@ -217,13 +226,21 @@ export class ExtensionScope {
217226
throw new Error('Node is not found');
218227
}
219228

220-
const matchedStyles = await cssModel.getMatchedStyles(node.id);
229+
try {
230+
const matchedStyles = await cssModel.getMatchedStyles(node.id);
221231

222-
if (!matchedStyles) {
223-
throw new Error('No Matching styles');
232+
if (!matchedStyles) {
233+
throw new Error('No Matching styles');
234+
}
235+
236+
const selector = ExtensionScope.getSelectorForRule(matchedStyles);
237+
if (selector) {
238+
return selector;
239+
}
240+
} catch {
224241
}
225242

226-
return ExtensionScope.getSelectorForRule(matchedStyles);
243+
return ExtensionScope.getSelectorForNode(node);
227244
}
228245

229246
async #bindingCalled(executionContext: SDK.RuntimeModel.ExecutionContext, event: {
@@ -248,11 +265,11 @@ export class ExtensionScope {
248265
]);
249266

250267
const arg = JSON.parse(args.object.value) as Omit<FreestyleCallbackArgs, 'element'>;
251-
let selector = arg.selector;
252268

269+
// TODO: Should this a be a *?
270+
let selector = '';
253271
try {
254-
const computedSelector = await this.#computeSelectorFromElement(element.object);
255-
selector = computedSelector || selector;
272+
selector = await this.#computeSelectorFromElement(element.object);
256273
} catch (err) {
257274
console.error(err);
258275
} finally {

0 commit comments

Comments
 (0)