chrome-devtools-frontend 1.0.1701636 → 1.0.1703574
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/eslint.config.mjs +3 -1
- package/front_end/core/common/ParsedURL.ts +5 -1
- package/front_end/core/host/UserMetricsEnums.ts +2 -1
- package/front_end/core/root/ExperimentNames.ts +1 -0
- package/front_end/core/sdk/CSSPropertyParserMatchers.ts +2 -7
- package/front_end/core/text_utils/Markdown.ts +73 -0
- package/front_end/core/text_utils/text_utils.ts +2 -0
- package/front_end/devtools_compatibility.js +2 -1
- package/front_end/entrypoints/main/MainImpl.ts +8 -0
- package/front_end/generated/InspectorBackendCommands.ts +6 -4
- package/front_end/generated/protocol-mapping.d.ts +8 -0
- package/front_end/generated/protocol-proxy-api.d.ts +6 -0
- package/front_end/generated/protocol.ts +22 -3
- package/front_end/models/ai_assistance/AiAgent2.ts +13 -1
- package/front_end/models/ai_assistance/AiConversation.ts +1 -1
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +13 -19
- package/front_end/models/change_tracker/ChangeTracker.ts +5 -64
- package/front_end/models/comment_manager/CD4ABridge.ts +5 -0
- package/front_end/models/comment_manager/CommentManager.ts +3 -5
- package/front_end/models/comment_manager/CommentThread.ts +30 -13
- package/front_end/models/issues_manager/CookieDeprecationMetadataIssue.ts +6 -7
- package/front_end/models/issues_manager/DeprecationIssue.ts +1 -1
- package/front_end/models/issues_manager/MarkdownIssueDescription.ts +18 -52
- package/front_end/models/issues_manager/SRIMessageSignatureIssue.ts +2 -4
- package/front_end/models/issues_manager/descriptions/cookieWarnMetadataGrantRead.md +0 -1
- package/front_end/models/issues_manager/descriptions/cookieWarnMetadataGrantReadOptOut.md +5 -0
- package/front_end/models/issues_manager/descriptions/cookieWarnMetadataGrantSet.md +0 -1
- package/front_end/models/issues_manager/descriptions/cookieWarnMetadataGrantSetOptOut.md +5 -0
- package/front_end/models/issues_manager/descriptions/deprecation.md +1 -1
- package/front_end/models/issues_manager/descriptions/sriValidationFailedIntegrityMismatch.md +2 -0
- package/front_end/models/persistence/AutomaticFileSystemWorkspaceBinding.ts +1 -1
- package/front_end/models/persistence/NetworkPersistenceManager.ts +14 -2
- package/front_end/panels/common/CommentThreadWidget.ts +29 -10
- package/front_end/panels/common/CommentsOverlayWidget.ts +50 -14
- package/front_end/panels/common/CommentsStatusBarPill.ts +40 -7
- package/front_end/panels/common/commentThreadWidget.css +11 -0
- package/front_end/panels/common/commentsStatusBarPill.css +13 -0
- package/front_end/panels/elements/StylePropertiesSection.ts +10 -0
- package/front_end/panels/elements/StylePropertyTreeElement.ts +1 -1
- package/front_end/panels/elements/StylesSidebarPane.ts +101 -49
- package/front_end/panels/elements/components/CSSQuery.ts +1 -1
- package/front_end/panels/elements/components/cssQuery.css +4 -0
- package/front_end/panels/elements/elements.ts +0 -1
- package/front_end/panels/elements/stylesSidebarPane.css +6 -0
- package/front_end/panels/issues/IssueView.ts +5 -1
- package/front_end/panels/sources/ResourceOriginPlugin.ts +8 -3
- package/front_end/panels/sources/ScopeChainSidebarPane.ts +3 -4
- package/front_end/panels/sources/SourcesView.ts +127 -147
- package/front_end/panels/sources/TabbedEditorContainer.ts +518 -246
- package/front_end/panels/sources/UISourceCodeFrame.ts +9 -0
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/third_party/chromium/client-variations/ClientVariations.js +25 -96
- package/front_end/third_party/codemirror.next/chunk/angular.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/codemirror.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/codemirror.js.map +1 -1
- package/front_end/third_party/codemirror.next/chunk/cpp.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/cpp.js.map +1 -1
- package/front_end/third_party/codemirror.next/chunk/java.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/java.js.map +1 -1
- package/front_end/third_party/codemirror.next/chunk/legacy.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/less.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/less.js.map +1 -1
- package/front_end/third_party/codemirror.next/chunk/markdown.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/markdown.js.map +1 -1
- package/front_end/third_party/codemirror.next/chunk/php.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/php.js.map +1 -1
- package/front_end/third_party/codemirror.next/chunk/python.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/python.js.map +1 -1
- package/front_end/third_party/codemirror.next/chunk/sass.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/sass.js.map +1 -1
- package/front_end/third_party/codemirror.next/chunk/svelte.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/svelte.js.map +1 -1
- package/front_end/third_party/codemirror.next/chunk/vue.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/wast.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/wast.js.map +1 -1
- package/front_end/third_party/codemirror.next/chunk/xml.js +1 -1
- package/front_end/third_party/codemirror.next/chunk/xml.js.map +1 -1
- package/front_end/third_party/codemirror.next/codemirror.next.js +1 -1
- package/front_end/third_party/codemirror.next/package.json +4 -1
- package/front_end/third_party/codemirror.next/rebuild.sh +1 -1
- package/front_end/ui/comments/CommentOverlayManager.ts +11 -10
- package/front_end/ui/components/markdown_view/MarkdownPlaceholderLitRenderer.ts +59 -0
- package/front_end/ui/components/markdown_view/markdown_view.ts +2 -0
- package/front_end/ui/legacy/TabbedPane.ts +51 -40
- package/front_end/ui/legacy/Treeoutline.ts +28 -19
- package/front_end/ui/legacy/UIUtils.ts +1 -1
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +424 -570
- package/front_end/ui/legacy/components/object_ui/objectPropertiesSection.css +2 -27
- package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
- package/mcp/mcp.ts +1 -0
- package/package.json +2 -1
|
@@ -58,13 +58,7 @@ export interface Comment {
|
|
|
58
58
|
timestamp: number;
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
export
|
|
62
|
-
id: string;
|
|
63
|
-
description: string;
|
|
64
|
-
timestamp: number;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export type CommentThreadStatus = 'DRAFT'|'ACTIVE'|'RESOLVED';
|
|
61
|
+
export type CommentThreadStatus = 'DRAFT'|'ACTIVE'|'SENT_TO_AGENT'|'RESOLVED';
|
|
68
62
|
|
|
69
63
|
export const enum Events {
|
|
70
64
|
CHANGED = 'Changed',
|
|
@@ -77,7 +71,8 @@ export interface EventTypes {
|
|
|
77
71
|
export interface CommentThreadOptions {
|
|
78
72
|
anchor: CommentAnchorSignature;
|
|
79
73
|
comments?: Comment[];
|
|
80
|
-
|
|
74
|
+
/** True for comments generated from the change tracker. */
|
|
75
|
+
isGeneratedComment?: boolean;
|
|
81
76
|
}
|
|
82
77
|
|
|
83
78
|
export class CommentThread extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
|
|
@@ -89,33 +84,41 @@ export class CommentThread extends Common.ObjectWrapper.ObjectWrapper<EventTypes
|
|
|
89
84
|
|
|
90
85
|
readonly id: string = crypto.randomUUID();
|
|
91
86
|
readonly anchor: CommentAnchorSignature;
|
|
87
|
+
/** True for comments generated from the change tracker. */
|
|
88
|
+
readonly isGeneratedComment: boolean;
|
|
92
89
|
#savedIndex?: number;
|
|
93
90
|
comments: Comment[];
|
|
94
91
|
status: CommentThreadStatus = 'DRAFT';
|
|
95
92
|
transmitted: boolean = false;
|
|
96
|
-
changes?: ChangeRecord[];
|
|
97
93
|
|
|
98
94
|
constructor(options: CommentThreadOptions) {
|
|
99
95
|
super();
|
|
100
96
|
this.anchor = options.anchor;
|
|
101
97
|
this.comments = options.comments ?? [];
|
|
102
|
-
this.
|
|
98
|
+
this.isGeneratedComment = Boolean(options.isGeneratedComment);
|
|
103
99
|
}
|
|
104
100
|
|
|
105
101
|
get index(): number {
|
|
106
102
|
return this.#savedIndex ?? CommentThread.#nextIndex;
|
|
107
103
|
}
|
|
108
104
|
|
|
109
|
-
|
|
110
|
-
|
|
105
|
+
/**
|
|
106
|
+
* Returns whether it was changed.
|
|
107
|
+
*/
|
|
108
|
+
#saveText(text?: string, author: 'DEVELOPER'|'AGENT' = 'DEVELOPER'): boolean {
|
|
111
109
|
if (text && text.trim().length > 0) {
|
|
112
110
|
this.comments.push({
|
|
113
111
|
author,
|
|
114
112
|
text: text.trim(),
|
|
115
113
|
timestamp: Date.now(),
|
|
116
114
|
});
|
|
117
|
-
|
|
115
|
+
return true;
|
|
118
116
|
}
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
save(text?: string, author: 'DEVELOPER'|'AGENT' = 'DEVELOPER'): void {
|
|
121
|
+
let changed = this.#saveText(text, author);
|
|
119
122
|
if (this.status === 'DRAFT') {
|
|
120
123
|
if (this.#savedIndex === undefined) {
|
|
121
124
|
this.#savedIndex = CommentThread.#nextIndex++;
|
|
@@ -128,6 +131,20 @@ export class CommentThread extends Common.ObjectWrapper.ObjectWrapper<EventTypes
|
|
|
128
131
|
}
|
|
129
132
|
}
|
|
130
133
|
|
|
134
|
+
sendToAgent(text?: string, author: 'DEVELOPER'|'AGENT' = 'DEVELOPER'): void {
|
|
135
|
+
let changed = this.#saveText(text, author);
|
|
136
|
+
if (this.status === 'DRAFT' || this.status === 'ACTIVE') {
|
|
137
|
+
if (this.#savedIndex === undefined) {
|
|
138
|
+
this.#savedIndex = CommentThread.#nextIndex++;
|
|
139
|
+
}
|
|
140
|
+
this.status = 'SENT_TO_AGENT';
|
|
141
|
+
changed = true;
|
|
142
|
+
}
|
|
143
|
+
if (changed) {
|
|
144
|
+
this.dispatchEventToListeners(Events.CHANGED);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
131
148
|
resolve(replyText?: string): void {
|
|
132
149
|
if (replyText && replyText.trim().length > 0) {
|
|
133
150
|
this.comments.push({
|
|
@@ -33,20 +33,19 @@ export class CookieDeprecationMetadataIssue extends Issue<Protocol.Audits.Cookie
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
getDescription(): MarkdownIssueDescription {
|
|
36
|
-
|
|
36
|
+
let fileName =
|
|
37
37
|
this.details().operation === 'SetCookie' ? 'cookieWarnMetadataGrantSet.md' : 'cookieWarnMetadataGrantRead.md';
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
const substitutions = new Map<string, string>();
|
|
40
40
|
if (this.details().isOptOutTopLevel) {
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
fileName = this.details().operation === 'SetCookie' ? 'cookieWarnMetadataGrantSetOptOut.md' :
|
|
42
|
+
'cookieWarnMetadataGrantReadOptOut.md';
|
|
43
|
+
substitutions.set('PLACEHOLDER_optOutPercentage', String(this.details().optOutPercentage));
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
return {
|
|
46
47
|
file: fileName,
|
|
47
|
-
substitutions
|
|
48
|
-
['PLACEHOLDER_topleveloptout', optOutText],
|
|
49
|
-
]),
|
|
48
|
+
substitutions,
|
|
50
49
|
links: [
|
|
51
50
|
{
|
|
52
51
|
link: 'https://goo.gle/changes-to-chrome-browsing',
|
|
@@ -71,8 +71,8 @@ export class DeprecationIssue extends Issue<Protocol.Audits.DeprecationIssueDeta
|
|
|
71
71
|
}
|
|
72
72
|
return resolveLazyDescription({
|
|
73
73
|
file: 'deprecation.md',
|
|
74
|
+
title: i18nLazyString(UIStrings.title),
|
|
74
75
|
substitutions: new Map([
|
|
75
|
-
['PLACEHOLDER_title', i18nLazyString(UIStrings.title)],
|
|
76
76
|
['PLACEHOLDER_message', messageFunction],
|
|
77
77
|
]),
|
|
78
78
|
links,
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// found in the LICENSE file.
|
|
4
4
|
|
|
5
5
|
import * as Platform from '../../core/platform/platform.js';
|
|
6
|
+
import * as TextUtils from '../../core/text_utils/text_utils.js';
|
|
6
7
|
import * as Marked from '../../third_party/marked/marked.js';
|
|
7
8
|
|
|
8
9
|
/**
|
|
@@ -13,12 +14,14 @@ import * as Marked from '../../third_party/marked/marked.js';
|
|
|
13
14
|
*/
|
|
14
15
|
export interface MarkdownIssueDescription {
|
|
15
16
|
file: string;
|
|
17
|
+
title?: string;
|
|
16
18
|
substitutions?: Map<string, string>;
|
|
17
19
|
links: Array<{link: string, linkTitle: string}>;
|
|
18
20
|
}
|
|
19
21
|
|
|
20
22
|
export interface LazyMarkdownIssueDescription {
|
|
21
23
|
file: string;
|
|
24
|
+
title?: () => string;
|
|
22
25
|
substitutions?: Map<string, () => string>;
|
|
23
26
|
links: Array<{link: string, linkTitle: () => string}>;
|
|
24
27
|
}
|
|
@@ -38,8 +41,9 @@ export function resolveLazyDescription(lazyDescription: LazyMarkdownIssueDescrip
|
|
|
38
41
|
substitutionMap.set(key, value());
|
|
39
42
|
});
|
|
40
43
|
|
|
41
|
-
const description = {
|
|
44
|
+
const description: MarkdownIssueDescription = {
|
|
42
45
|
file: lazyDescription.file,
|
|
46
|
+
title: lazyDescription.title?.(),
|
|
43
47
|
links: lazyDescription.links.map(linksMap),
|
|
44
48
|
substitutions: substitutionMap,
|
|
45
49
|
};
|
|
@@ -54,6 +58,7 @@ export interface IssueDescription {
|
|
|
54
58
|
title: string;
|
|
55
59
|
markdown: Marked.Marked.Token[];
|
|
56
60
|
links: Array<{link: string, linkTitle: string}>;
|
|
61
|
+
substitutions?: Map<string, string>;
|
|
57
62
|
}
|
|
58
63
|
|
|
59
64
|
export async function getFileContent(url: URL): Promise<string> {
|
|
@@ -71,9 +76,8 @@ export async function getMarkdownFileContent(filename: string): Promise<string>
|
|
|
71
76
|
|
|
72
77
|
export async function createIssueDescriptionFromMarkdown(description: MarkdownIssueDescription):
|
|
73
78
|
Promise<IssueDescription> {
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
return createIssueDescriptionFromRawMarkdown(rawMarkdownWithPlaceholdersReplaced, description);
|
|
79
|
+
const markdown = await getMarkdownFileContent(description.file);
|
|
80
|
+
return createIssueDescriptionFromRawMarkdown(markdown, description);
|
|
77
81
|
}
|
|
78
82
|
|
|
79
83
|
/**
|
|
@@ -81,62 +85,20 @@ export async function createIssueDescriptionFromMarkdown(description: MarkdownIs
|
|
|
81
85
|
*/
|
|
82
86
|
export function createIssueDescriptionFromRawMarkdown(
|
|
83
87
|
markdown: string, description: MarkdownIssueDescription): IssueDescription {
|
|
84
|
-
const markdownAst =
|
|
85
|
-
const
|
|
86
|
-
if (!
|
|
88
|
+
const markdownAst = TextUtils.Markdown.tokenizeWithPlaceholders(markdown, description.substitutions);
|
|
89
|
+
const markdownTitle = findTitleFromMarkdownAst(markdownAst);
|
|
90
|
+
if (!markdownTitle) {
|
|
87
91
|
throw new Error('Markdown issue descriptions must start with a heading');
|
|
88
92
|
}
|
|
89
93
|
|
|
90
94
|
return {
|
|
91
|
-
title,
|
|
95
|
+
title: description.title ?? markdownTitle,
|
|
92
96
|
markdown: markdownAst.slice(1),
|
|
93
97
|
links: description.links,
|
|
98
|
+
substitutions: description.substitutions,
|
|
94
99
|
};
|
|
95
100
|
}
|
|
96
101
|
|
|
97
|
-
const validPlaceholderMatchPattern = /\{(PLACEHOLDER_[a-zA-Z][a-zA-Z0-9]*)\}/g;
|
|
98
|
-
const validPlaceholderNamePattern = /PLACEHOLDER_[a-zA-Z][a-zA-Z0-9]*/;
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Replaces placeholders in markdown text with a string provided by the
|
|
102
|
-
* `substitutions` map. To keep mental overhead to a minimum, the same
|
|
103
|
-
* syntax is used as for l10n placeholders. Please note that the
|
|
104
|
-
* placeholders require a mandatory 'PLACEHOLDER_' prefix.
|
|
105
|
-
*
|
|
106
|
-
* Example:
|
|
107
|
-
* const str = "This is markdown with `code` and two placeholders, namely {PLACEHOLDER_PH1} and {PLACEHOLDER_PH2}".
|
|
108
|
-
* const result = substitutePlaceholders(str, new Map([['PLACEHOLDER_PH1', 'foo'], ['PLACEHOLDER_PH2', 'bar']]));
|
|
109
|
-
*
|
|
110
|
-
* Exported only for unit testing.
|
|
111
|
-
*/
|
|
112
|
-
export function substitutePlaceholders(markdown: string, substitutions?: Map<string, string>): string {
|
|
113
|
-
const unusedPlaceholders = new Set(substitutions ? substitutions.keys() : []);
|
|
114
|
-
validatePlaceholders(unusedPlaceholders);
|
|
115
|
-
|
|
116
|
-
const result = markdown.replace(validPlaceholderMatchPattern, (_, placeholder) => {
|
|
117
|
-
const replacement = substitutions ? substitutions.get(placeholder) : undefined;
|
|
118
|
-
if (replacement === undefined) {
|
|
119
|
-
throw new Error(`No replacement provided for placeholder '${placeholder}'.`);
|
|
120
|
-
}
|
|
121
|
-
unusedPlaceholders.delete(placeholder);
|
|
122
|
-
return replacement;
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
if (unusedPlaceholders.size > 0) {
|
|
126
|
-
throw new Error(`Unused replacements provided: ${[...unusedPlaceholders]}`);
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
return result;
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/** Ensure that all provided placeholders match the naming pattern. **/
|
|
133
|
-
function validatePlaceholders(placeholders: Set<string>): void {
|
|
134
|
-
const invalidPlaceholders = [...placeholders].filter(placeholder => !validPlaceholderNamePattern.test(placeholder));
|
|
135
|
-
if (invalidPlaceholders.length > 0) {
|
|
136
|
-
throw new Error(`Invalid placeholders provided in the substitutions map: ${invalidPlaceholders}`);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
102
|
export function findTitleFromMarkdownAst(markdownAst: Marked.Marked.Token[]): string|null {
|
|
141
103
|
if (markdownAst.length === 0 || markdownAst[0].type !== 'heading' || markdownAst[0].depth !== 1) {
|
|
142
104
|
return null;
|
|
@@ -148,5 +110,9 @@ export async function getIssueTitleFromMarkdownDescription(description: Markdown
|
|
|
148
110
|
Promise<string|null> {
|
|
149
111
|
const rawMarkdown = await getMarkdownFileContent(description.file);
|
|
150
112
|
const markdownAst = Marked.Marked.lexer(rawMarkdown);
|
|
151
|
-
|
|
113
|
+
const markdownTitle = findTitleFromMarkdownAst(markdownAst);
|
|
114
|
+
if (!markdownTitle) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
return description.title ?? markdownTitle;
|
|
152
118
|
}
|
|
@@ -77,10 +77,8 @@ export class SRIMessageSignatureIssue extends Issue<Protocol.Audits.SRIMessageSi
|
|
|
77
77
|
description.substitutions?.set('PLACEHOLDER_signatureBase', () => details.signatureBase);
|
|
78
78
|
}
|
|
79
79
|
if (details.error === Protocol.Audits.SRIMessageSignatureError.ValidationFailedIntegrityMismatch) {
|
|
80
|
-
description.substitutions?.set('PLACEHOLDER_integrityAssertions',
|
|
81
|
-
|
|
82
|
-
return prefix + this.details().integrityAssertions.join(prefix);
|
|
83
|
-
});
|
|
80
|
+
description.substitutions?.set('PLACEHOLDER_integrityAssertions',
|
|
81
|
+
() => this.details().integrityAssertions.join('\n'));
|
|
84
82
|
}
|
|
85
83
|
return resolveLazyDescription(description);
|
|
86
84
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
# Third-party websites are allowed to read cookies on this page
|
|
2
2
|
|
|
3
3
|
One or more websites are allowed to bypass user settings to read third-party cookies on this page. Web developers should take steps to remove these reads without disrupting user experience. To forcefully block these third-party cookies, update [user settings](manageCookiesHelpPage).
|
|
4
|
-
{PLACEHOLDER_topleveloptout}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Third-party websites are allowed to read cookies on this page
|
|
2
|
+
|
|
3
|
+
One or more websites are allowed to bypass user settings to read third-party cookies on this page. Web developers should take steps to remove these reads without disrupting user experience. To forcefully block these third-party cookies, update [user settings](manageCookiesHelpPage).
|
|
4
|
+
|
|
5
|
+
(Top level site opt-out: {PLACEHOLDER_optOutPercentage}% - [learn more](gracePeriodStagedControlExplainer))
|
|
@@ -1,4 +1,3 @@
|
|
|
1
1
|
# Third-party websites are allowed to set cookies on this page
|
|
2
2
|
|
|
3
3
|
One or more websites are allowed to bypass user settings to set third-party cookies on this page. Web developers should take steps to remove these sets without disrupting user experience. To forcefully block these third-party cookies, update [user settings](manageCookiesHelpPage).
|
|
4
|
-
{PLACEHOLDER_topleveloptout}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
# Third-party websites are allowed to set cookies on this page
|
|
2
|
+
|
|
3
|
+
One or more websites are allowed to bypass user settings to set third-party cookies on this page. Web developers should take steps to remove these sets without disrupting user experience. To forcefully block these third-party cookies, update [user settings](manageCookiesHelpPage).
|
|
4
|
+
|
|
5
|
+
(Top level site opt-out: {PLACEHOLDER_optOutPercentage}% - [learn more](gracePeriodStagedControlExplainer))
|
|
@@ -64,7 +64,7 @@ export class FileSystem implements Workspace.Workspace.Project {
|
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
displayName(): string {
|
|
67
|
-
const
|
|
67
|
+
const root = this.automaticFileSystem.root.replace(Host.Platform.isWin() ? /[/\\]+$/ : /\/+$/, '');
|
|
68
68
|
let slash = root.lastIndexOf('/');
|
|
69
69
|
if (slash === -1 && Host.Platform.isWin()) {
|
|
70
70
|
slash = root.lastIndexOf('\\');
|
|
@@ -270,6 +270,8 @@ export class NetworkPersistenceManager extends Common.ObjectWrapper.ObjectWrappe
|
|
|
270
270
|
encodeURI(pathPart).replace(/[\/\*]/g, match => '%' + match[0].charCodeAt(0).toString(16).toUpperCase());
|
|
271
271
|
if (encodedName === '..') {
|
|
272
272
|
encodedName = '%2E%2E';
|
|
273
|
+
} else if (encodedName === '.') {
|
|
274
|
+
encodedName = '%2E';
|
|
273
275
|
}
|
|
274
276
|
if (Host.Platform.isWin()) {
|
|
275
277
|
// Windows does not allow ':' and '?' in filenames
|
|
@@ -335,7 +337,10 @@ export class NetworkPersistenceManager extends Common.ObjectWrapper.ObjectWrappe
|
|
|
335
337
|
if (!encodedFilePath) {
|
|
336
338
|
return null;
|
|
337
339
|
}
|
|
338
|
-
const encodedPath = Common.ParsedURL.ParsedURL.
|
|
340
|
+
const encodedPath = Common.ParsedURL.ParsedURL.substr(encodedFilePath, 0, encodedFilePath.lastIndexOf('/'));
|
|
341
|
+
if (!encodedPath) {
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
339
344
|
uiSourceCode = await this.#project.createFile(encodedPath, HEADERS_FILENAME, '');
|
|
340
345
|
Host.userMetrics.actionTaken(Host.UserMetrics.Action.HeaderOverrideFileCreated);
|
|
341
346
|
}
|
|
@@ -525,6 +530,10 @@ export class NetworkPersistenceManager extends Common.ObjectWrapper.ObjectWrappe
|
|
|
525
530
|
const encodedFileName = Common.ParsedURL.ParsedURL.substring(encodedPath, lastIndexOfSlash + 1);
|
|
526
531
|
const rawFileName = Common.ParsedURL.ParsedURL.encodedPathToRawPathString(encodedFileName);
|
|
527
532
|
encodedPath = Common.ParsedURL.ParsedURL.substr(encodedPath, 0, lastIndexOfSlash);
|
|
533
|
+
if (!encodedPath || rawFileName === HEADERS_FILENAME) {
|
|
534
|
+
this.#savingForOverrides.delete(uiSourceCode);
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
528
537
|
if (this.#project) {
|
|
529
538
|
await this.#project.createFile(encodedPath, rawFileName, content ?? '', isEncoded);
|
|
530
539
|
}
|
|
@@ -564,7 +573,7 @@ export class NetworkPersistenceManager extends Common.ObjectWrapper.ObjectWrappe
|
|
|
564
573
|
const relativePathParts = FileSystemWorkspaceBinding.relativePath(uiSourceCode);
|
|
565
574
|
// Decode twice to handle paths generated on Windows OS.
|
|
566
575
|
const host = this.decodeLocalPathToUrlPath(this.decodeLocalPathToUrlPath(relativePathParts[0] || '')).toLowerCase();
|
|
567
|
-
return ['chrome:', 'data:', 'blob:', 'javascript:', 'about:', 'mailto:', 'vbscript:'].includes(host) ||
|
|
576
|
+
return ['chrome:', 'data:', 'blob:', 'javascript:', 'about:', 'mailto:', 'vbscript:', '.', '..'].includes(host) ||
|
|
568
577
|
forbiddenUrls.includes(host);
|
|
569
578
|
}
|
|
570
579
|
|
|
@@ -578,6 +587,9 @@ export class NetworkPersistenceManager extends Common.ObjectWrapper.ObjectWrappe
|
|
|
578
587
|
if (!url) {
|
|
579
588
|
return false;
|
|
580
589
|
}
|
|
590
|
+
if ((url.scheme === 'http' || url.scheme === 'https') && (!url.host || url.host === '.' || url.host === '..')) {
|
|
591
|
+
return true;
|
|
592
|
+
}
|
|
581
593
|
return !['http', 'https', 'file'].includes(url.scheme) || forbiddenUrls.includes(url.host);
|
|
582
594
|
}
|
|
583
595
|
|
|
@@ -7,6 +7,7 @@ import '../../ui/components/tooltips/tooltips.js';
|
|
|
7
7
|
import * as i18n from '../../core/i18n/i18n.js';
|
|
8
8
|
import type * as SDK from '../../core/sdk/sdk.js';
|
|
9
9
|
import type * as CommentManager from '../../models/comment_manager/comment_manager.js';
|
|
10
|
+
import * as Buttons from '../../ui/components/buttons/buttons.js';
|
|
10
11
|
import * as Input from '../../ui/components/input/input.js';
|
|
11
12
|
import * as MarkdownView from '../../ui/components/markdown_view/markdown_view.js';
|
|
12
13
|
import * as UI from '../../ui/legacy/legacy.js';
|
|
@@ -41,6 +42,10 @@ const UIStrings = {
|
|
|
41
42
|
* @description aria-label for the comment text area.
|
|
42
43
|
*/
|
|
43
44
|
commentInputAriaLabel: 'Comment input',
|
|
45
|
+
/**
|
|
46
|
+
* @description Tooltip and aria-label for the close button in the comment thread header.
|
|
47
|
+
*/
|
|
48
|
+
close: 'Close',
|
|
44
49
|
} as const;
|
|
45
50
|
|
|
46
51
|
const UIStringsNotTranslate = {
|
|
@@ -70,6 +75,7 @@ export interface ViewInput {
|
|
|
70
75
|
textAreaRef: Lit.Directives.Ref<HTMLTextAreaElement>;
|
|
71
76
|
onAddComment: (text: string) => void;
|
|
72
77
|
onCommentTextChange: (event: Event) => void;
|
|
78
|
+
onClose?: () => void;
|
|
73
79
|
}
|
|
74
80
|
|
|
75
81
|
export type ViewOutput = undefined;
|
|
@@ -87,16 +93,27 @@ export const DEFAULT_VIEW = (input: ViewInput, _output: ViewOutput, target: HTML
|
|
|
87
93
|
widget(DOMNodeLink, {node: input.title.node}) :
|
|
88
94
|
html`<span class="selected-item-text">${input.title.text}</span>`}
|
|
89
95
|
</span>
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
<
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
96
|
+
<div class="header-actions">
|
|
97
|
+
${hasComment ? html`
|
|
98
|
+
<div class="sent-status">
|
|
99
|
+
<devtools-icon
|
|
100
|
+
class="check-icon"
|
|
101
|
+
name="checkmark"
|
|
102
|
+
aria-label=${i18nString(UIStrings.sentCheckmark)}>
|
|
103
|
+
</devtools-icon>
|
|
104
|
+
<span>${i18nString(UIStrings.sent)}</span>
|
|
105
|
+
</div>
|
|
106
|
+
` : Lit.nothing}
|
|
107
|
+
<devtools-button
|
|
108
|
+
class="close-button"
|
|
109
|
+
aria-label=${i18nString(UIStrings.close)}
|
|
110
|
+
.iconName=${'cross'}
|
|
111
|
+
.variant=${Buttons.Button.Variant.ICON}
|
|
112
|
+
.size=${Buttons.Button.Size.SMALL}
|
|
113
|
+
.title=${i18nString(UIStrings.close)}
|
|
114
|
+
@click=${input.onClose}
|
|
115
|
+
></devtools-button>
|
|
116
|
+
</div>
|
|
100
117
|
</div>
|
|
101
118
|
|
|
102
119
|
${hasComment ? html`
|
|
@@ -171,6 +188,7 @@ export class CommentThreadWidget extends UI.Widget.Widget {
|
|
|
171
188
|
#textAreaRef = createRef<HTMLTextAreaElement>();
|
|
172
189
|
#view: View;
|
|
173
190
|
onAddComment?: (text: string) => void;
|
|
191
|
+
onClose?: () => void;
|
|
174
192
|
|
|
175
193
|
constructor(element?: HTMLElement, view: View = DEFAULT_VIEW) {
|
|
176
194
|
super(element);
|
|
@@ -213,6 +231,7 @@ export class CommentThreadWidget extends UI.Widget.Widget {
|
|
|
213
231
|
textAreaRef: this.#textAreaRef,
|
|
214
232
|
onAddComment: this.#handleAddComment,
|
|
215
233
|
onCommentTextChange: this.#handleCommentTextChange,
|
|
234
|
+
onClose: this.onClose,
|
|
216
235
|
};
|
|
217
236
|
this.#view(viewInput, undefined, this.contentElement);
|
|
218
237
|
}
|
|
@@ -31,6 +31,7 @@ export interface ViewInput {
|
|
|
31
31
|
activePin: Comments.CommentOverlayManager.PinPositionData|null;
|
|
32
32
|
title: Title;
|
|
33
33
|
onAddComment: (text: string) => void;
|
|
34
|
+
onCloseCommentThread: () => void;
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
export type View = (
|
|
@@ -103,6 +104,7 @@ const DEFAULT_VIEW: View = (input: ViewInput, _output: undefined, target: HTMLEl
|
|
|
103
104
|
title: input.title,
|
|
104
105
|
comments: [...item.thread.comments],
|
|
105
106
|
onAddComment: input.onAddComment,
|
|
107
|
+
onClose: input.onCloseCommentThread,
|
|
106
108
|
})}
|
|
107
109
|
</div>
|
|
108
110
|
`,
|
|
@@ -124,6 +126,18 @@ export class CommentsOverlayWidget extends UI.Widget.Widget {
|
|
|
124
126
|
#cachedTitle: Title = {text: ''};
|
|
125
127
|
#cachedTitleAnchor: CommentManager.CommentManager.CommentAnchorSignature|null = null;
|
|
126
128
|
|
|
129
|
+
#setActiveThreadId(threadId: string|null): void {
|
|
130
|
+
if (this.#activeThreadId !== threadId) {
|
|
131
|
+
this.#clearCloseTimeout();
|
|
132
|
+
this.#activeThreadId = threadId;
|
|
133
|
+
if (threadId && this.isShowing()) {
|
|
134
|
+
document.documentElement.addEventListener('keydown', this.#onKeyDown);
|
|
135
|
+
} else {
|
|
136
|
+
document.documentElement.removeEventListener('keydown', this.#onKeyDown);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
127
141
|
constructor(
|
|
128
142
|
element: HTMLElement|undefined,
|
|
129
143
|
[commentManager]: UI.Widget.WidgetDependencies<typeof CommentsOverlayWidget>,
|
|
@@ -170,12 +184,16 @@ export class CommentsOverlayWidget extends UI.Widget.Widget {
|
|
|
170
184
|
this.#onAgentAttachedChanged,
|
|
171
185
|
this,
|
|
172
186
|
);
|
|
187
|
+
if (this.#activeThreadId) {
|
|
188
|
+
document.documentElement.addEventListener('keydown', this.#onKeyDown);
|
|
189
|
+
}
|
|
173
190
|
|
|
174
191
|
this.requestUpdate();
|
|
175
192
|
}
|
|
176
193
|
|
|
177
194
|
override willHide(): void {
|
|
178
195
|
this.#clearCloseTimeout();
|
|
196
|
+
document.documentElement.removeEventListener('keydown', this.#onKeyDown);
|
|
179
197
|
this.#commentOverlayManager.stop();
|
|
180
198
|
this.#commentOverlayManager.removeEventListener(
|
|
181
199
|
Comments.CommentOverlayManager.Events.POSITIONS_UPDATED,
|
|
@@ -219,7 +237,7 @@ export class CommentsOverlayWidget extends UI.Widget.Widget {
|
|
|
219
237
|
CommentManager.CommentManager.EventTypes[CommentManager.CommentManager.Events.AGENT_ATTACHED_CHANGED]>,
|
|
220
238
|
): void {
|
|
221
239
|
if (!event.data) {
|
|
222
|
-
this.#
|
|
240
|
+
this.#setActiveThreadId(null);
|
|
223
241
|
}
|
|
224
242
|
this.requestUpdate();
|
|
225
243
|
}
|
|
@@ -230,7 +248,7 @@ export class CommentsOverlayWidget extends UI.Widget.Widget {
|
|
|
230
248
|
): void {
|
|
231
249
|
const isModeActive = event.data;
|
|
232
250
|
if (!isModeActive) {
|
|
233
|
-
this.#
|
|
251
|
+
this.#setActiveThreadId(null);
|
|
234
252
|
}
|
|
235
253
|
const action = UI.ActionRegistry.ActionRegistry.instance().getAction(
|
|
236
254
|
'comments.toggle-comment-mode',
|
|
@@ -242,9 +260,9 @@ export class CommentsOverlayWidget extends UI.Widget.Widget {
|
|
|
242
260
|
#onStateChanged(): void {
|
|
243
261
|
const draftThread = this.#commentManager.getCommentThreads().find(t => t.status === 'DRAFT');
|
|
244
262
|
if (draftThread) {
|
|
245
|
-
this.#
|
|
263
|
+
this.#setActiveThreadId(draftThread.id);
|
|
246
264
|
} else if (this.#activeThreadId && !this.#commentManager.getCommentThread(this.#activeThreadId)) {
|
|
247
|
-
this.#
|
|
265
|
+
this.#setActiveThreadId(null);
|
|
248
266
|
}
|
|
249
267
|
this.requestUpdate();
|
|
250
268
|
}
|
|
@@ -272,7 +290,6 @@ export class CommentsOverlayWidget extends UI.Widget.Widget {
|
|
|
272
290
|
return {node};
|
|
273
291
|
}
|
|
274
292
|
}
|
|
275
|
-
return {text: ''};
|
|
276
293
|
}
|
|
277
294
|
|
|
278
295
|
if (anchor.networkRequestId) {
|
|
@@ -287,19 +304,35 @@ export class CommentsOverlayWidget extends UI.Widget.Widget {
|
|
|
287
304
|
}
|
|
288
305
|
|
|
289
306
|
#handlePinClick = (threadId: string): void => {
|
|
290
|
-
const thread = this.#commentManager.getCommentThread(threadId);
|
|
291
307
|
if (this.#activeThreadId === threadId) {
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
308
|
+
this.#handleCloseCommentThread();
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
this.#commentOverlayManager.clearDraftThreads();
|
|
313
|
+
this.#setActiveThreadId(threadId);
|
|
314
|
+
this.requestUpdate();
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
#handleCloseCommentThread = (): void => {
|
|
318
|
+
if (!this.#activeThreadId) {
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
const thread = this.#commentManager.getCommentThread(this.#activeThreadId);
|
|
322
|
+
if (thread?.status === 'DRAFT') {
|
|
297
323
|
this.#commentOverlayManager.clearDraftThreads();
|
|
298
|
-
this.#activeThreadId = threadId;
|
|
299
324
|
}
|
|
325
|
+
this.#setActiveThreadId(null);
|
|
300
326
|
this.requestUpdate();
|
|
301
327
|
};
|
|
302
328
|
|
|
329
|
+
#onKeyDown = (event: KeyboardEvent): void => {
|
|
330
|
+
if (this.#activeThreadId && event.key === 'Escape' && !event.isComposing) {
|
|
331
|
+
event.consume(true);
|
|
332
|
+
this.#handleCloseCommentThread();
|
|
333
|
+
}
|
|
334
|
+
};
|
|
335
|
+
|
|
303
336
|
override async performUpdate(signal?: AbortSignal): Promise<void> {
|
|
304
337
|
if (!this.#commentManager.isAgentAttached()) {
|
|
305
338
|
this.#view(
|
|
@@ -313,6 +346,7 @@ export class CommentsOverlayWidget extends UI.Widget.Widget {
|
|
|
313
346
|
activePin: null,
|
|
314
347
|
title: {text: ''},
|
|
315
348
|
onAddComment: () => {},
|
|
349
|
+
onCloseCommentThread: this.#handleCloseCommentThread,
|
|
316
350
|
},
|
|
317
351
|
undefined,
|
|
318
352
|
this.contentElement,
|
|
@@ -342,16 +376,18 @@ export class CommentsOverlayWidget extends UI.Widget.Widget {
|
|
|
342
376
|
if (!activeThread) {
|
|
343
377
|
return;
|
|
344
378
|
}
|
|
345
|
-
activeThread.
|
|
379
|
+
activeThread.sendToAgent(text);
|
|
346
380
|
const threadId = activeThread.id;
|
|
381
|
+
this.#clearCloseTimeout();
|
|
347
382
|
this.#closeTimeoutId = window.setTimeout(() => {
|
|
348
383
|
this.#closeTimeoutId = null;
|
|
349
384
|
if (this.#activeThreadId === threadId) {
|
|
350
|
-
this.#
|
|
385
|
+
this.#setActiveThreadId(null);
|
|
351
386
|
this.requestUpdate();
|
|
352
387
|
}
|
|
353
388
|
}, AUTO_CLOSE_DELAY_MS);
|
|
354
389
|
},
|
|
390
|
+
onCloseCommentThread: this.#handleCloseCommentThread,
|
|
355
391
|
};
|
|
356
392
|
this.#view(viewInput, undefined, this.contentElement);
|
|
357
393
|
}
|