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.
Files changed (91) hide show
  1. package/eslint.config.mjs +3 -1
  2. package/front_end/core/common/ParsedURL.ts +5 -1
  3. package/front_end/core/host/UserMetricsEnums.ts +2 -1
  4. package/front_end/core/root/ExperimentNames.ts +1 -0
  5. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +2 -7
  6. package/front_end/core/text_utils/Markdown.ts +73 -0
  7. package/front_end/core/text_utils/text_utils.ts +2 -0
  8. package/front_end/devtools_compatibility.js +2 -1
  9. package/front_end/entrypoints/main/MainImpl.ts +8 -0
  10. package/front_end/generated/InspectorBackendCommands.ts +6 -4
  11. package/front_end/generated/protocol-mapping.d.ts +8 -0
  12. package/front_end/generated/protocol-proxy-api.d.ts +6 -0
  13. package/front_end/generated/protocol.ts +22 -3
  14. package/front_end/models/ai_assistance/AiAgent2.ts +13 -1
  15. package/front_end/models/ai_assistance/AiConversation.ts +1 -1
  16. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +13 -19
  17. package/front_end/models/change_tracker/ChangeTracker.ts +5 -64
  18. package/front_end/models/comment_manager/CD4ABridge.ts +5 -0
  19. package/front_end/models/comment_manager/CommentManager.ts +3 -5
  20. package/front_end/models/comment_manager/CommentThread.ts +30 -13
  21. package/front_end/models/issues_manager/CookieDeprecationMetadataIssue.ts +6 -7
  22. package/front_end/models/issues_manager/DeprecationIssue.ts +1 -1
  23. package/front_end/models/issues_manager/MarkdownIssueDescription.ts +18 -52
  24. package/front_end/models/issues_manager/SRIMessageSignatureIssue.ts +2 -4
  25. package/front_end/models/issues_manager/descriptions/cookieWarnMetadataGrantRead.md +0 -1
  26. package/front_end/models/issues_manager/descriptions/cookieWarnMetadataGrantReadOptOut.md +5 -0
  27. package/front_end/models/issues_manager/descriptions/cookieWarnMetadataGrantSet.md +0 -1
  28. package/front_end/models/issues_manager/descriptions/cookieWarnMetadataGrantSetOptOut.md +5 -0
  29. package/front_end/models/issues_manager/descriptions/deprecation.md +1 -1
  30. package/front_end/models/issues_manager/descriptions/sriValidationFailedIntegrityMismatch.md +2 -0
  31. package/front_end/models/persistence/AutomaticFileSystemWorkspaceBinding.ts +1 -1
  32. package/front_end/models/persistence/NetworkPersistenceManager.ts +14 -2
  33. package/front_end/panels/common/CommentThreadWidget.ts +29 -10
  34. package/front_end/panels/common/CommentsOverlayWidget.ts +50 -14
  35. package/front_end/panels/common/CommentsStatusBarPill.ts +40 -7
  36. package/front_end/panels/common/commentThreadWidget.css +11 -0
  37. package/front_end/panels/common/commentsStatusBarPill.css +13 -0
  38. package/front_end/panels/elements/StylePropertiesSection.ts +10 -0
  39. package/front_end/panels/elements/StylePropertyTreeElement.ts +1 -1
  40. package/front_end/panels/elements/StylesSidebarPane.ts +101 -49
  41. package/front_end/panels/elements/components/CSSQuery.ts +1 -1
  42. package/front_end/panels/elements/components/cssQuery.css +4 -0
  43. package/front_end/panels/elements/elements.ts +0 -1
  44. package/front_end/panels/elements/stylesSidebarPane.css +6 -0
  45. package/front_end/panels/issues/IssueView.ts +5 -1
  46. package/front_end/panels/sources/ResourceOriginPlugin.ts +8 -3
  47. package/front_end/panels/sources/ScopeChainSidebarPane.ts +3 -4
  48. package/front_end/panels/sources/SourcesView.ts +127 -147
  49. package/front_end/panels/sources/TabbedEditorContainer.ts +518 -246
  50. package/front_end/panels/sources/UISourceCodeFrame.ts +9 -0
  51. package/front_end/third_party/chromium/README.chromium +1 -1
  52. package/front_end/third_party/chromium/client-variations/ClientVariations.js +25 -96
  53. package/front_end/third_party/codemirror.next/chunk/angular.js +1 -1
  54. package/front_end/third_party/codemirror.next/chunk/codemirror.js +1 -1
  55. package/front_end/third_party/codemirror.next/chunk/codemirror.js.map +1 -1
  56. package/front_end/third_party/codemirror.next/chunk/cpp.js +1 -1
  57. package/front_end/third_party/codemirror.next/chunk/cpp.js.map +1 -1
  58. package/front_end/third_party/codemirror.next/chunk/java.js +1 -1
  59. package/front_end/third_party/codemirror.next/chunk/java.js.map +1 -1
  60. package/front_end/third_party/codemirror.next/chunk/legacy.js +1 -1
  61. package/front_end/third_party/codemirror.next/chunk/less.js +1 -1
  62. package/front_end/third_party/codemirror.next/chunk/less.js.map +1 -1
  63. package/front_end/third_party/codemirror.next/chunk/markdown.js +1 -1
  64. package/front_end/third_party/codemirror.next/chunk/markdown.js.map +1 -1
  65. package/front_end/third_party/codemirror.next/chunk/php.js +1 -1
  66. package/front_end/third_party/codemirror.next/chunk/php.js.map +1 -1
  67. package/front_end/third_party/codemirror.next/chunk/python.js +1 -1
  68. package/front_end/third_party/codemirror.next/chunk/python.js.map +1 -1
  69. package/front_end/third_party/codemirror.next/chunk/sass.js +1 -1
  70. package/front_end/third_party/codemirror.next/chunk/sass.js.map +1 -1
  71. package/front_end/third_party/codemirror.next/chunk/svelte.js +1 -1
  72. package/front_end/third_party/codemirror.next/chunk/svelte.js.map +1 -1
  73. package/front_end/third_party/codemirror.next/chunk/vue.js +1 -1
  74. package/front_end/third_party/codemirror.next/chunk/wast.js +1 -1
  75. package/front_end/third_party/codemirror.next/chunk/wast.js.map +1 -1
  76. package/front_end/third_party/codemirror.next/chunk/xml.js +1 -1
  77. package/front_end/third_party/codemirror.next/chunk/xml.js.map +1 -1
  78. package/front_end/third_party/codemirror.next/codemirror.next.js +1 -1
  79. package/front_end/third_party/codemirror.next/package.json +4 -1
  80. package/front_end/third_party/codemirror.next/rebuild.sh +1 -1
  81. package/front_end/ui/comments/CommentOverlayManager.ts +11 -10
  82. package/front_end/ui/components/markdown_view/MarkdownPlaceholderLitRenderer.ts +59 -0
  83. package/front_end/ui/components/markdown_view/markdown_view.ts +2 -0
  84. package/front_end/ui/legacy/TabbedPane.ts +51 -40
  85. package/front_end/ui/legacy/Treeoutline.ts +28 -19
  86. package/front_end/ui/legacy/UIUtils.ts +1 -1
  87. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +424 -570
  88. package/front_end/ui/legacy/components/object_ui/objectPropertiesSection.css +2 -27
  89. package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
  90. package/mcp/mcp.ts +1 -0
  91. package/package.json +2 -1
package/eslint.config.mjs CHANGED
@@ -59,6 +59,7 @@ export default defineConfig([
59
59
  'scripts/migration/**/*.js',
60
60
  'scripts/protocol_typescript/*.js',
61
61
  'scripts/deps/tests/fixtures',
62
+ 'scripts/gn_deps_verifier/tests/fixtures',
62
63
  'test/**/fixtures/',
63
64
  'test/e2e/**/*.js',
64
65
  'test/shared/**/*.js',
@@ -740,7 +741,8 @@ export default defineConfig([
740
741
  'front_end/**/*.docs.ts',
741
742
  'test/**/*.ts',
742
743
  '**/testing/*.ts',
743
- 'scripts/eslint_rules/test/**/*',
744
+ 'scripts/eslint_rules/tests/**/*',
745
+ 'scripts/gn_deps_verifier/tests/**/*',
744
746
  'extensions/cxx_debugging/e2e/**/*.ts',
745
747
  'extensions/cxx_debugging/tests/**/*.ts',
746
748
  ],
@@ -179,7 +179,11 @@ export class ParsedURL {
179
179
  preEncodedPath = 'file:///' + preEncodedPath;
180
180
  }
181
181
  }
182
- return new URL(preEncodedPath).toString() as Platform.DevToolsPath.UrlString;
182
+ let url = new URL(preEncodedPath).toString();
183
+ while (url.endsWith('/') && url.length > 'file:///'.length) {
184
+ url = url.substring(0, url.length - 1);
185
+ }
186
+ return url as Platform.DevToolsPath.UrlString;
183
187
  }
184
188
 
185
189
  static relativePathToUrlString(
@@ -498,10 +498,11 @@ export enum DevtoolsExperiments {
498
498
  'durable-messages' = 110,
499
499
  'jpeg-xl' = 111,
500
500
  'plus-button' = 112,
501
+ 'source-map-scopes-in-sources-panel' = 113,
501
502
  /* eslint-enable @typescript-eslint/naming-convention */
502
503
 
503
504
  // Increment this when new experiments are added.
504
- MAX_VALUE = 113,
505
+ MAX_VALUE = 114,
505
506
  }
506
507
 
507
508
  /** Update DevToolsIssuesPanelIssueExpanded from tools/metrics/histograms/enums.xml if new enum is added. **/
@@ -9,6 +9,7 @@ export enum ExperimentName {
9
9
  DURABLE_MESSAGES = 'durable-messages',
10
10
  JPEG_XL = 'jpeg-xl',
11
11
  PLUS_BUTTON = 'plus-button',
12
+ SOURCE_MAP_SCOPES_IN_SOURCES_PANEL = 'source-map-scopes-in-sources-panel',
12
13
  // Adding or removing an entry from this enum?
13
14
  // You will need to update:
14
15
  // 1. DevToolsExperiments enum in host/UserMetrics.ts
@@ -56,13 +56,8 @@ export class BaseVariableMatch implements Match {
56
56
  }
57
57
  }
58
58
 
59
- // TODO(b/533783988): Remove ValueName handling once https://code.haverbeke.berlin/lezer/css/issues/36 is fixed and rolled.
60
59
  function isVariableNameNode(node: CodeMirror.SyntaxNode|null|undefined, ast: SyntaxTree): boolean {
61
- if (node?.name !== 'VariableName' && node?.name !== 'ValueName') {
62
- return false;
63
- }
64
- const text = ast.text(node);
65
- return text.length > 2 && text.startsWith('--');
60
+ return node?.name === 'VariableName' && ast.text(node).length > 2;
66
61
  }
67
62
 
68
63
  const BaseVariableMatcherBase: MatcherClass<BaseVariableMatch> = matcherBase(BaseVariableMatch);
@@ -1122,7 +1117,7 @@ export class CustomFunctionMatcher extends CustomFunctionMatcherBase {
1122
1117
  if (node.name !== 'CallExpression') {
1123
1118
  return null;
1124
1119
  }
1125
- const callee = matching.ast.text(node.getChild('VariableName') ?? node.getChild('Callee'));
1120
+ const callee = matching.ast.text(node.getChild('VariableName'));
1126
1121
  if (!callee || callee.length <= 2 || !callee.startsWith('--')) {
1127
1122
  return null;
1128
1123
  }
@@ -0,0 +1,73 @@
1
+ // Copyright 2026 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ import * as Marked from '../../third_party/marked/marked.js';
6
+
7
+ export interface PlaceholderToken extends Marked.Marked.Tokens.Generic {
8
+ type: 'placeholder';
9
+ raw: string;
10
+ key: string;
11
+ }
12
+
13
+ export const VALID_PLACEHOLDER_MATCH_PATTERN: RegExp = /\{(PLACEHOLDER_[a-zA-Z][a-zA-Z0-9_]*)\}/g;
14
+ const VALID_PLACEHOLDER_NAME_PATTERN = /^PLACEHOLDER_[a-zA-Z][a-zA-Z0-9_]*$/;
15
+
16
+ const placeholderExtension: Marked.Marked.TokenizerExtension = {
17
+ name: 'placeholder',
18
+ level: 'inline',
19
+ start(src: string): number |
20
+ undefined {
21
+ return src.match(/\{PLACEHOLDER_[a-zA-Z][a-zA-Z0-9_]*\}/)?.index;
22
+ },
23
+ tokenizer(src: string): PlaceholderToken |
24
+ undefined {
25
+ const match = /^\{(PLACEHOLDER_[a-zA-Z][a-zA-Z0-9_]*)\}/.exec(src);
26
+ if (match) {
27
+ return {
28
+ type: 'placeholder',
29
+ raw: match[0],
30
+ key: match[1],
31
+ };
32
+ }
33
+ return undefined;
34
+ },
35
+ };
36
+
37
+ export function validateSubstitutions(rawMarkdown: string, substitutions?: Map<string, string>): void {
38
+ if (!substitutions) {
39
+ return;
40
+ }
41
+ const unusedPlaceholders = new Set(substitutions.keys());
42
+ for (const key of unusedPlaceholders) {
43
+ if (!VALID_PLACEHOLDER_NAME_PATTERN.test(key)) {
44
+ throw new Error(`Invalid placeholder '${key}' provided in the substitutions map.`);
45
+ }
46
+ }
47
+ for (const [, placeholder] of rawMarkdown.matchAll(VALID_PLACEHOLDER_MATCH_PATTERN)) {
48
+ if (!substitutions.has(placeholder)) {
49
+ throw new Error(`No replacement provided for placeholder '${placeholder}'.`);
50
+ }
51
+ unusedPlaceholders.delete(placeholder);
52
+ }
53
+ if (unusedPlaceholders.size > 0) {
54
+ throw new Error(`Unused replacements provided: ${[...unusedPlaceholders]}`);
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Tokenizes markdown into a Marked AST while preserving `{PLACEHOLDER_*}` tokens
60
+ * as `PlaceholderToken` AST nodes instead of substituting strings before lexing.
61
+ */
62
+ export function tokenizeWithPlaceholders(markdown: string|Marked.Marked.Token[],
63
+ substitutions?: Map<string, string>): Marked.Marked.Token[] {
64
+ const rawMarkdown = typeof markdown === 'string' ? markdown : markdown.map(token => token.raw).join('');
65
+ validateSubstitutions(rawMarkdown, substitutions);
66
+ if (typeof markdown !== 'string' && !rawMarkdown.includes('{PLACEHOLDER_')) {
67
+ return markdown;
68
+ }
69
+ const markedInstance = new Marked.Marked.Marked({
70
+ extensions: [placeholderExtension],
71
+ });
72
+ return markedInstance.lexer(rawMarkdown);
73
+ }
@@ -5,6 +5,7 @@
5
5
  import * as CodeMirrorUtils from './CodeMirrorUtils.js';
6
6
  import * as ContentData from './ContentData.js';
7
7
  import * as ContentProvider from './ContentProvider.js';
8
+ import * as Markdown from './Markdown.js';
8
9
  import * as StaticContentProvider from './StaticContentProvider.js';
9
10
  import * as StreamingContentData from './StreamingContentData.js';
10
11
  import * as Text from './Text.js';
@@ -17,6 +18,7 @@ export {
17
18
  CodeMirrorUtils,
18
19
  ContentData,
19
20
  ContentProvider,
21
+ Markdown,
20
22
  StaticContentProvider,
21
23
  StreamingContentData,
22
24
  Text,
@@ -876,7 +876,8 @@
876
876
  'durable-messages': 110,
877
877
  'jpeg-xl': 111,
878
878
  'plus-button': 112,
879
- MAX_VALUE: 113,
879
+ 'source-map-scopes-in-sources-panel': 113,
880
+ MAX_VALUE: 114,
880
881
  },
881
882
  IssueExpanded: {
882
883
  CrossOriginEmbedderPolicy: 0,
@@ -415,6 +415,14 @@ export class MainImpl {
415
415
  requiresChromeRestart: false,
416
416
  });
417
417
 
418
+ Root.Runtime.experiments.register({
419
+ name: Root.ExperimentNames.ExperimentName.SOURCE_MAP_SCOPES_IN_SOURCES_PANEL,
420
+ title: 'Source map scopes in the Sources panel',
421
+ aboutFlag: 'devtools-source-map-scopes-in-sources-panel',
422
+ isEnabled: Root.Runtime.hostConfig.devToolsSourceMapScopesInSourcesPanel?.enabled ?? false,
423
+ requiresChromeRestart: false,
424
+ });
425
+
418
426
  for (const experiment of Root.Runtime.experiments.allConfigurableExperiments()) {
419
427
  if (experiment.isEnabled()) {
420
428
  Host.userMetrics.experimentEnabledAtLaunch(experiment.name);
@@ -73,7 +73,7 @@ inspectorBackend.registerType("Animation.KeyframesRule", [{"name": "name", "type
73
73
  inspectorBackend.registerType("Animation.KeyframeStyle", [{"name": "offset", "type": "string", "optional": false, "description": "Keyframe's time offset.", "typeRef": null}, {"name": "easing", "type": "string", "optional": false, "description": "`AnimationEffect`'s timing function.", "typeRef": null}]);
74
74
 
75
75
  // Audits.
76
- inspectorBackend.registerEnum("Audits.CookieExclusionReason", {ExcludeSameSiteUnspecifiedTreatedAsLax: "ExcludeSameSiteUnspecifiedTreatedAsLax", ExcludeSameSiteNoneInsecure: "ExcludeSameSiteNoneInsecure", ExcludeSameSiteLax: "ExcludeSameSiteLax", ExcludeSameSiteStrict: "ExcludeSameSiteStrict", ExcludeDomainNonASCII: "ExcludeDomainNonASCII", ExcludeThirdPartyCookieBlockedInFirstPartySet: "ExcludeThirdPartyCookieBlockedInFirstPartySet", ExcludeThirdPartyPhaseout: "ExcludeThirdPartyPhaseout", ExcludePortMismatch: "ExcludePortMismatch", ExcludeSchemeMismatch: "ExcludeSchemeMismatch"});
76
+ inspectorBackend.registerEnum("Audits.CookieExclusionReason", {ExcludeSameSiteUnspecifiedTreatedAsLax: "ExcludeSameSiteUnspecifiedTreatedAsLax", ExcludeSameSiteNoneInsecure: "ExcludeSameSiteNoneInsecure", ExcludeSameSiteLax: "ExcludeSameSiteLax", ExcludeSameSiteStrict: "ExcludeSameSiteStrict", ExcludeDomainNonASCII: "ExcludeDomainNonASCII", ExcludeThirdPartyPhaseout: "ExcludeThirdPartyPhaseout", ExcludePortMismatch: "ExcludePortMismatch", ExcludeSchemeMismatch: "ExcludeSchemeMismatch"});
77
77
  inspectorBackend.registerEnum("Audits.CookieWarningReason", {WarnSameSiteUnspecifiedCrossSiteContext: "WarnSameSiteUnspecifiedCrossSiteContext", WarnSameSiteNoneInsecure: "WarnSameSiteNoneInsecure", WarnSameSiteUnspecifiedLaxAllowUnsafe: "WarnSameSiteUnspecifiedLaxAllowUnsafe", WarnSameSiteStrictLaxDowngradeStrict: "WarnSameSiteStrictLaxDowngradeStrict", WarnSameSiteStrictCrossDowngradeStrict: "WarnSameSiteStrictCrossDowngradeStrict", WarnSameSiteStrictCrossDowngradeLax: "WarnSameSiteStrictCrossDowngradeLax", WarnSameSiteLaxCrossDowngradeStrict: "WarnSameSiteLaxCrossDowngradeStrict", WarnSameSiteLaxCrossDowngradeLax: "WarnSameSiteLaxCrossDowngradeLax", WarnAttributeValueExceedsMaxSize: "WarnAttributeValueExceedsMaxSize", WarnDomainNonASCII: "WarnDomainNonASCII", WarnThirdPartyPhaseout: "WarnThirdPartyPhaseout", WarnCrossSiteRedirectDowngradeChangesInclusion: "WarnCrossSiteRedirectDowngradeChangesInclusion", WarnDeprecationTrialMetadata: "WarnDeprecationTrialMetadata", WarnThirdPartyCookieHeuristic: "WarnThirdPartyCookieHeuristic"});
78
78
  inspectorBackend.registerEnum("Audits.CookieOperation", {SetCookie: "SetCookie", ReadCookie: "ReadCookie"});
79
79
  inspectorBackend.registerEnum("Audits.InsightType", {GitHubResource: "GitHubResource", GracePeriod: "GracePeriod", Heuristics: "Heuristics"});
@@ -815,8 +815,8 @@ inspectorBackend.registerEnum("Network.TrustTokenOperationType", {Issuance: "Iss
815
815
  inspectorBackend.registerEnum("Network.AlternateProtocolUsage", {AlternativeJobWonWithoutRace: "alternativeJobWonWithoutRace", AlternativeJobWonRace: "alternativeJobWonRace", MainJobWonRace: "mainJobWonRace", MappingMissing: "mappingMissing", Broken: "broken", DnsAlpnH3JobWonWithoutRace: "dnsAlpnH3JobWonWithoutRace", DnsAlpnH3JobWonRace: "dnsAlpnH3JobWonRace", UnspecifiedReason: "unspecifiedReason"});
816
816
  inspectorBackend.registerEnum("Network.ServiceWorkerRouterSource", {Network: "network", Cache: "cache", FetchEvent: "fetch-event", RaceNetworkAndFetchHandler: "race-network-and-fetch-handler", RaceNetworkAndCache: "race-network-and-cache"});
817
817
  inspectorBackend.registerEnum("Network.InitiatorType", {Parser: "parser", Script: "script", Preload: "preload", SignedExchange: "SignedExchange", Preflight: "preflight", FedCM: "FedCM", Other: "other"});
818
- inspectorBackend.registerEnum("Network.SetCookieBlockedReason", {SecureOnly: "SecureOnly", SameSiteStrict: "SameSiteStrict", SameSiteLax: "SameSiteLax", SameSiteUnspecifiedTreatedAsLax: "SameSiteUnspecifiedTreatedAsLax", SameSiteNoneInsecure: "SameSiteNoneInsecure", UserPreferences: "UserPreferences", ThirdPartyPhaseout: "ThirdPartyPhaseout", ThirdPartyBlockedInFirstPartySet: "ThirdPartyBlockedInFirstPartySet", SyntaxError: "SyntaxError", SchemeNotSupported: "SchemeNotSupported", OverwriteSecure: "OverwriteSecure", InvalidDomain: "InvalidDomain", InvalidPrefix: "InvalidPrefix", UnknownError: "UnknownError", SchemefulSameSiteStrict: "SchemefulSameSiteStrict", SchemefulSameSiteLax: "SchemefulSameSiteLax", SchemefulSameSiteUnspecifiedTreatedAsLax: "SchemefulSameSiteUnspecifiedTreatedAsLax", NameValuePairExceedsMaxSize: "NameValuePairExceedsMaxSize", DisallowedCharacter: "DisallowedCharacter", NoCookieContent: "NoCookieContent"});
819
- inspectorBackend.registerEnum("Network.CookieBlockedReason", {SecureOnly: "SecureOnly", NotOnPath: "NotOnPath", DomainMismatch: "DomainMismatch", SameSiteStrict: "SameSiteStrict", SameSiteLax: "SameSiteLax", SameSiteUnspecifiedTreatedAsLax: "SameSiteUnspecifiedTreatedAsLax", SameSiteNoneInsecure: "SameSiteNoneInsecure", UserPreferences: "UserPreferences", ThirdPartyPhaseout: "ThirdPartyPhaseout", ThirdPartyBlockedInFirstPartySet: "ThirdPartyBlockedInFirstPartySet", UnknownError: "UnknownError", SchemefulSameSiteStrict: "SchemefulSameSiteStrict", SchemefulSameSiteLax: "SchemefulSameSiteLax", SchemefulSameSiteUnspecifiedTreatedAsLax: "SchemefulSameSiteUnspecifiedTreatedAsLax", NameValuePairExceedsMaxSize: "NameValuePairExceedsMaxSize", PortMismatch: "PortMismatch", SchemeMismatch: "SchemeMismatch", AnonymousContext: "AnonymousContext"});
818
+ inspectorBackend.registerEnum("Network.SetCookieBlockedReason", {SecureOnly: "SecureOnly", SameSiteStrict: "SameSiteStrict", SameSiteLax: "SameSiteLax", SameSiteUnspecifiedTreatedAsLax: "SameSiteUnspecifiedTreatedAsLax", SameSiteNoneInsecure: "SameSiteNoneInsecure", UserPreferences: "UserPreferences", ThirdPartyPhaseout: "ThirdPartyPhaseout", SyntaxError: "SyntaxError", SchemeNotSupported: "SchemeNotSupported", OverwriteSecure: "OverwriteSecure", InvalidDomain: "InvalidDomain", InvalidPrefix: "InvalidPrefix", UnknownError: "UnknownError", SchemefulSameSiteStrict: "SchemefulSameSiteStrict", SchemefulSameSiteLax: "SchemefulSameSiteLax", SchemefulSameSiteUnspecifiedTreatedAsLax: "SchemefulSameSiteUnspecifiedTreatedAsLax", NameValuePairExceedsMaxSize: "NameValuePairExceedsMaxSize", DisallowedCharacter: "DisallowedCharacter", NoCookieContent: "NoCookieContent"});
819
+ inspectorBackend.registerEnum("Network.CookieBlockedReason", {SecureOnly: "SecureOnly", NotOnPath: "NotOnPath", DomainMismatch: "DomainMismatch", SameSiteStrict: "SameSiteStrict", SameSiteLax: "SameSiteLax", SameSiteUnspecifiedTreatedAsLax: "SameSiteUnspecifiedTreatedAsLax", SameSiteNoneInsecure: "SameSiteNoneInsecure", UserPreferences: "UserPreferences", ThirdPartyPhaseout: "ThirdPartyPhaseout", UnknownError: "UnknownError", SchemefulSameSiteStrict: "SchemefulSameSiteStrict", SchemefulSameSiteLax: "SchemefulSameSiteLax", SchemefulSameSiteUnspecifiedTreatedAsLax: "SchemefulSameSiteUnspecifiedTreatedAsLax", NameValuePairExceedsMaxSize: "NameValuePairExceedsMaxSize", PortMismatch: "PortMismatch", SchemeMismatch: "SchemeMismatch", AnonymousContext: "AnonymousContext"});
820
820
  inspectorBackend.registerEnum("Network.CookieExemptionReason", {None: "None", UserSetting: "UserSetting", EnterprisePolicy: "EnterprisePolicy", StorageAccess: "StorageAccess", TopLevelStorageAccess: "TopLevelStorageAccess", Scheme: "Scheme", SameSiteNoneCookiesInSandbox: "SameSiteNoneCookiesInSandbox"});
821
821
  inspectorBackend.registerEnum("Network.AuthChallengeSource", {Server: "Server", Proxy: "Proxy"});
822
822
  inspectorBackend.registerEnum("Network.AuthChallengeResponseResponse", {Default: "Default", CancelAuth: "CancelAuth", ProvideCredentials: "ProvideCredentials"});
@@ -1183,7 +1183,7 @@ inspectorBackend.registerType("Page.FrameResource", [{"name": "url", "type": "st
1183
1183
  inspectorBackend.registerType("Page.FrameResourceTree", [{"name": "frame", "type": "object", "optional": false, "description": "Frame information for this tree item.", "typeRef": "Page.Frame"}, {"name": "childFrames", "type": "array", "optional": true, "description": "Child frames.", "typeRef": "Page.FrameResourceTree"}, {"name": "resources", "type": "array", "optional": false, "description": "Information about frame resources.", "typeRef": "Page.FrameResource"}]);
1184
1184
  inspectorBackend.registerType("Page.FrameTree", [{"name": "frame", "type": "object", "optional": false, "description": "Frame information for this tree item.", "typeRef": "Page.Frame"}, {"name": "childFrames", "type": "array", "optional": true, "description": "Child frames.", "typeRef": "Page.FrameTree"}]);
1185
1185
  inspectorBackend.registerType("Page.NavigationEntry", [{"name": "id", "type": "number", "optional": false, "description": "Unique id of the navigation history entry.", "typeRef": null}, {"name": "url", "type": "string", "optional": false, "description": "URL of the navigation history entry.", "typeRef": null}, {"name": "userTypedURL", "type": "string", "optional": false, "description": "URL that the user typed in the url bar.", "typeRef": null}, {"name": "title", "type": "string", "optional": false, "description": "Title of the navigation history entry.", "typeRef": null}, {"name": "transitionType", "type": "string", "optional": false, "description": "Transition type.", "typeRef": "Page.TransitionType"}]);
1186
- inspectorBackend.registerType("Page.ScreencastFrameMetadata", [{"name": "offsetTop", "type": "number", "optional": false, "description": "Top offset in DIP.", "typeRef": null}, {"name": "pageScaleFactor", "type": "number", "optional": false, "description": "Page scale factor.", "typeRef": null}, {"name": "deviceWidth", "type": "number", "optional": false, "description": "Device screen width in DIP.", "typeRef": null}, {"name": "deviceHeight", "type": "number", "optional": false, "description": "Device screen height in DIP.", "typeRef": null}, {"name": "scrollOffsetX", "type": "number", "optional": false, "description": "Position of horizontal scroll in CSS pixels.", "typeRef": null}, {"name": "scrollOffsetY", "type": "number", "optional": false, "description": "Position of vertical scroll in CSS pixels.", "typeRef": null}, {"name": "timestamp", "type": "number", "optional": true, "description": "Frame swap timestamp.", "typeRef": "Network.TimeSinceEpoch"}]);
1186
+ inspectorBackend.registerType("Page.ScreencastFrameMetadata", [{"name": "offsetTop", "type": "number", "optional": false, "description": "Top offset in DIP.", "typeRef": null}, {"name": "pageScaleFactor", "type": "number", "optional": false, "description": "Page scale factor.", "typeRef": null}, {"name": "deviceWidth", "type": "number", "optional": false, "description": "Device screen width in DIP.", "typeRef": null}, {"name": "deviceHeight", "type": "number", "optional": false, "description": "Device screen height in DIP.", "typeRef": null}, {"name": "scrollOffsetX", "type": "number", "optional": false, "description": "Position of horizontal scroll in CSS pixels.", "typeRef": null}, {"name": "scrollOffsetY", "type": "number", "optional": false, "description": "Position of vertical scroll in CSS pixels.", "typeRef": null}, {"name": "timestamp", "type": "number", "optional": true, "description": "Frame swap timestamp.", "typeRef": "Network.TimeSinceEpoch"}, {"name": "monotonicTimestamp", "type": "number", "optional": true, "description": "Frame swap timestamp as monotonic time.", "typeRef": "Network.MonotonicTime"}]);
1187
1187
  inspectorBackend.registerType("Page.AppManifestError", [{"name": "message", "type": "string", "optional": false, "description": "Error message.", "typeRef": null}, {"name": "critical", "type": "number", "optional": false, "description": "If critical, this is a non-recoverable parse error.", "typeRef": null}, {"name": "line", "type": "number", "optional": false, "description": "Error line.", "typeRef": null}, {"name": "column", "type": "number", "optional": false, "description": "Error column.", "typeRef": null}]);
1188
1188
  inspectorBackend.registerType("Page.AppManifestParsedProperties", [{"name": "scope", "type": "string", "optional": false, "description": "Computed scope value", "typeRef": null}]);
1189
1189
  inspectorBackend.registerType("Page.LayoutViewport", [{"name": "pageX", "type": "number", "optional": false, "description": "Horizontal offset relative to the document (CSS pixels).", "typeRef": null}, {"name": "pageY", "type": "number", "optional": false, "description": "Vertical offset relative to the document (CSS pixels).", "typeRef": null}, {"name": "clientWidth", "type": "number", "optional": false, "description": "Width (CSS pixels), excludes scrollbar if present.", "typeRef": null}, {"name": "clientHeight", "type": "number", "optional": false, "description": "Height (CSS pixels), excludes scrollbar if present.", "typeRef": null}]);
@@ -1362,6 +1362,7 @@ inspectorBackend.registerCommand("Storage.untrackIndexedDBForStorageKey", [{"nam
1362
1362
  inspectorBackend.registerCommand("Storage.getTrustTokens", [], ["tokens"], "Returns the number of stored Trust Tokens per issuer for the current browsing context.");
1363
1363
  inspectorBackend.registerCommand("Storage.clearTrustTokens", [{"name": "issuerOrigin", "type": "string", "optional": false, "description": "", "typeRef": null}], ["didDeleteTokens"], "Removes all Trust Tokens issued by the provided issuerOrigin. Leaves other stored data, including the issuer's Redemption Records, intact.");
1364
1364
  inspectorBackend.registerCommand("Storage.getPrivateVerificationTokens", [], ["tokens"], "Returns all stored Private Verification Tokens for the current browsing context.");
1365
+ inspectorBackend.registerCommand("Storage.getPrivateVerificationTokensIssuerConfigs", [], ["configs"], "Returns the configured Private Verification Tokens issuers and their redeemer origins.");
1365
1366
  inspectorBackend.registerCommand("Storage.clearPrivateVerificationTokens", [{"name": "issuerOrigin", "type": "string", "optional": false, "description": "", "typeRef": null}], [], "Removes all Private Verification Tokens issued by the provided issuerOrigin.");
1366
1367
  inspectorBackend.registerCommand("Storage.deletePrivateVerificationToken", [{"name": "tokenId", "type": "string", "optional": false, "description": "", "typeRef": null}], [], "Removes a specific Private Verification Token by its ID.");
1367
1368
  inspectorBackend.registerCommand("Storage.setPrivateVerificationTokensTracking", [{"name": "enable", "type": "boolean", "optional": false, "description": "", "typeRef": null}], [], "Set tracking for Private Verification Tokens.");
@@ -1371,6 +1372,7 @@ inspectorBackend.registerCommand("Storage.runBounceTrackingMitigations", [], ["d
1371
1372
  inspectorBackend.registerType("Storage.UsageForType", [{"name": "storageType", "type": "string", "optional": false, "description": "Name of storage type.", "typeRef": "Storage.StorageType"}, {"name": "usage", "type": "number", "optional": false, "description": "Storage usage (bytes).", "typeRef": null}]);
1372
1373
  inspectorBackend.registerType("Storage.TrustTokens", [{"name": "issuerOrigin", "type": "string", "optional": false, "description": "", "typeRef": null}, {"name": "count", "type": "number", "optional": false, "description": "", "typeRef": null}]);
1373
1374
  inspectorBackend.registerType("Storage.PrivateVerificationToken", [{"name": "id", "type": "string", "optional": false, "description": "Unique identifier of the token in the database.", "typeRef": null}, {"name": "issuerOrigin", "type": "string", "optional": false, "description": "Origin of the token issuer.", "typeRef": null}, {"name": "keyId", "type": "number", "optional": false, "description": "Public key ID used to issue the token.", "typeRef": null}, {"name": "expiration", "type": "number", "optional": false, "description": "Expiration timestamp in seconds since the epoch.", "typeRef": "Network.TimeSinceEpoch"}, {"name": "creationTime", "type": "number", "optional": false, "description": "Token creation timestamp in seconds since the epoch.", "typeRef": "Network.TimeSinceEpoch"}, {"name": "version", "type": "number", "optional": false, "description": "Token protocol version.", "typeRef": null}, {"name": "token", "type": "string", "optional": false, "description": "Base64-encoded serialized token.", "typeRef": null}]);
1375
+ inspectorBackend.registerType("Storage.PrivateVerificationTokensIssuerConfig", [{"name": "issuerOrigin", "type": "string", "optional": false, "description": "Origin of the token issuer.", "typeRef": null}, {"name": "redeemerOrigins", "type": "array", "optional": false, "description": "Origins authorized to redeem tokens from this issuer.", "typeRef": "string"}]);
1374
1376
  inspectorBackend.registerType("Storage.StorageBucket", [{"name": "storageKey", "type": "string", "optional": false, "description": "", "typeRef": "Storage.SerializedStorageKey"}, {"name": "name", "type": "string", "optional": true, "description": "If not specified, it is the default bucket of the storageKey.", "typeRef": null}]);
1375
1377
  inspectorBackend.registerType("Storage.StorageBucketInfo", [{"name": "bucket", "type": "object", "optional": false, "description": "", "typeRef": "Storage.StorageBucket"}, {"name": "id", "type": "string", "optional": false, "description": "", "typeRef": null}, {"name": "expiration", "type": "number", "optional": false, "description": "", "typeRef": "Network.TimeSinceEpoch"}, {"name": "quota", "type": "number", "optional": false, "description": "Storage quota (bytes).", "typeRef": null}, {"name": "persistent", "type": "boolean", "optional": false, "description": "", "typeRef": null}, {"name": "durability", "type": "string", "optional": false, "description": "", "typeRef": "Storage.StorageBucketsDurability"}]);
1376
1378
 
@@ -4954,6 +4954,14 @@ export namespace ProtocolMapping {
4954
4954
  paramsType: [];
4955
4955
  returnType: Protocol.Storage.GetPrivateVerificationTokensResponse;
4956
4956
  };
4957
+ /**
4958
+ * Returns the configured Private Verification Tokens issuers and their redeemer
4959
+ * origins.
4960
+ */
4961
+ 'Storage.getPrivateVerificationTokensIssuerConfigs': {
4962
+ paramsType: [];
4963
+ returnType: Protocol.Storage.GetPrivateVerificationTokensIssuerConfigsResponse;
4964
+ };
4957
4965
  /**
4958
4966
  * Removes all Private Verification Tokens issued by the provided issuerOrigin.
4959
4967
  */
@@ -4331,6 +4331,12 @@ declare namespace ProtocolProxyApi {
4331
4331
  */
4332
4332
  invoke_getPrivateVerificationTokens(): Promise<Protocol.Storage.GetPrivateVerificationTokensResponse>;
4333
4333
 
4334
+ /**
4335
+ * Returns the configured Private Verification Tokens issuers and their redeemer
4336
+ * origins.
4337
+ */
4338
+ invoke_getPrivateVerificationTokensIssuerConfigs(): Promise<Protocol.Storage.GetPrivateVerificationTokensIssuerConfigsResponse>;
4339
+
4334
4340
  /**
4335
4341
  * Removes all Private Verification Tokens issued by the provided issuerOrigin.
4336
4342
  */
@@ -875,7 +875,6 @@ export namespace Audits {
875
875
  ExcludeSameSiteLax = 'ExcludeSameSiteLax',
876
876
  ExcludeSameSiteStrict = 'ExcludeSameSiteStrict',
877
877
  ExcludeDomainNonASCII = 'ExcludeDomainNonASCII',
878
- ExcludeThirdPartyCookieBlockedInFirstPartySet = 'ExcludeThirdPartyCookieBlockedInFirstPartySet',
879
878
  ExcludeThirdPartyPhaseout = 'ExcludeThirdPartyPhaseout',
880
879
  ExcludePortMismatch = 'ExcludePortMismatch',
881
880
  ExcludeSchemeMismatch = 'ExcludeSchemeMismatch',
@@ -11318,7 +11317,6 @@ export namespace Network {
11318
11317
  SameSiteNoneInsecure = 'SameSiteNoneInsecure',
11319
11318
  UserPreferences = 'UserPreferences',
11320
11319
  ThirdPartyPhaseout = 'ThirdPartyPhaseout',
11321
- ThirdPartyBlockedInFirstPartySet = 'ThirdPartyBlockedInFirstPartySet',
11322
11320
  SyntaxError = 'SyntaxError',
11323
11321
  SchemeNotSupported = 'SchemeNotSupported',
11324
11322
  OverwriteSecure = 'OverwriteSecure',
@@ -11346,7 +11344,6 @@ export namespace Network {
11346
11344
  SameSiteNoneInsecure = 'SameSiteNoneInsecure',
11347
11345
  UserPreferences = 'UserPreferences',
11348
11346
  ThirdPartyPhaseout = 'ThirdPartyPhaseout',
11349
- ThirdPartyBlockedInFirstPartySet = 'ThirdPartyBlockedInFirstPartySet',
11350
11347
  UnknownError = 'UnknownError',
11351
11348
  SchemefulSameSiteStrict = 'SchemefulSameSiteStrict',
11352
11349
  SchemefulSameSiteLax = 'SchemefulSameSiteLax',
@@ -15136,6 +15133,10 @@ export namespace Page {
15136
15133
  * Frame swap timestamp.
15137
15134
  */
15138
15135
  timestamp?: Network.TimeSinceEpoch;
15136
+ /**
15137
+ * Frame swap timestamp as monotonic time.
15138
+ */
15139
+ monotonicTimestamp?: Network.MonotonicTime;
15139
15140
  }
15140
15141
 
15141
15142
  /**
@@ -18507,6 +18508,20 @@ export namespace Storage {
18507
18508
  token: string;
18508
18509
  }
18509
18510
 
18511
+ /**
18512
+ * Configuration for a Private Verification Tokens issuer.
18513
+ */
18514
+ export interface PrivateVerificationTokensIssuerConfig {
18515
+ /**
18516
+ * Origin of the token issuer.
18517
+ */
18518
+ issuerOrigin: string;
18519
+ /**
18520
+ * Origins authorized to redeem tokens from this issuer.
18521
+ */
18522
+ redeemerOrigins: string[];
18523
+ }
18524
+
18510
18525
  export const enum StorageBucketsDurability {
18511
18526
  Relaxed = 'relaxed',
18512
18527
  Strict = 'strict',
@@ -18720,6 +18735,10 @@ export namespace Storage {
18720
18735
  tokens: PrivateVerificationToken[];
18721
18736
  }
18722
18737
 
18738
+ export interface GetPrivateVerificationTokensIssuerConfigsResponse extends ProtocolResponseWithError {
18739
+ configs: PrivateVerificationTokensIssuerConfig[];
18740
+ }
18741
+
18723
18742
  export interface ClearPrivateVerificationTokensRequest {
18724
18743
  issuerOrigin: string;
18725
18744
  }
@@ -141,7 +141,19 @@ export class AiAgent2 extends AiAgent<unknown> {
141
141
  readonly #declaredTools = new Set<string>();
142
142
 
143
143
  constructor(opts: AiAgent2Options) {
144
- super(opts);
144
+ super({
145
+ ...opts,
146
+ allowedOrigin: opts.allowedOrigin ?? (() => {
147
+ const lock = opts.originLock();
148
+ if (lock.status === 'BLOCKED_BY_NAVIGATION') {
149
+ return {blocked: true};
150
+ }
151
+ if (lock.status === 'ESTABLISHED_ORIGIN') {
152
+ return {origin: lock.origin};
153
+ }
154
+ return {origin: undefined};
155
+ }),
156
+ });
145
157
  this.#changes = opts.changeManager ?? new ChangeManager(opts.targetManager);
146
158
  this.#lighthouseRecording = opts.lighthouseRecording;
147
159
  this.#performanceRecordAndReload = opts.performanceRecordAndReload;
@@ -434,7 +434,7 @@ export class AiConversation {
434
434
  } = {},
435
435
  ): AsyncGenerator<ResponseData, void, void> {
436
436
  this.#navigationOccurredDuringRun = false;
437
- const originAtRunStart = getPrimaryPageSecurityOrigin(this.#targetManager);
437
+ const originAtRunStart = this.#origin ?? getPrimaryPageSecurityOrigin(this.#targetManager);
438
438
  const listener = (): void => {
439
439
  // Prevent the agent from executing tools or reading data from an untrusted origin
440
440
  // if the page navigates unexpectedly during execution.
@@ -148,13 +148,12 @@ export class ContextSelectionAgent extends AiAgent<never> {
148
148
  };
149
149
  }
150
150
  const origin = allowedOriginResult.origin;
151
- if (origin?.isOpaque()) {
151
+ if (!origin || origin.isOpaque()) {
152
152
  return {
153
153
  error: 'No requests recorded by DevTools',
154
154
  };
155
155
  }
156
156
 
157
- const allowedSecurityOrigin = origin ?? null;
158
157
  let hasCrossOriginRequest = false;
159
158
  const requestsToShow: NetworkRequest[] = [];
160
159
  for (const request of this.#networkLog.requests()) {
@@ -166,7 +165,7 @@ export class ContextSelectionAgent extends AiAgent<never> {
166
165
  * during the loading of the target page, and do not leak URLs from
167
166
  * other pages.
168
167
  */
169
- if (allowedSecurityOrigin && !request.initiatorSecurityOrigin().isSameOriginWith(allowedSecurityOrigin)) {
168
+ if (!isOriginAllowedByLock({status: 'ESTABLISHED_ORIGIN', origin}, request.initiatorSecurityOrigin())) {
170
169
  hasCrossOriginRequest = true;
171
170
  continue;
172
171
  }
@@ -184,7 +183,7 @@ export class ContextSelectionAgent extends AiAgent<never> {
184
183
  if (requests.length === 0) {
185
184
  return {
186
185
  error: hasCrossOriginRequest ?
187
- `No requests showing with origin ${origin?.siteId() ?? ''}. Tell the user to start a new chat` :
186
+ `No requests showing with origin ${origin.siteId()}. Tell the user to start a new chat` :
188
187
  'No requests recorded by DevTools',
189
188
  };
190
189
  }
@@ -231,18 +230,17 @@ export class ContextSelectionAgent extends AiAgent<never> {
231
230
  };
232
231
  }
233
232
  const origin = allowedOriginResult.origin;
234
- if (origin?.isOpaque()) {
233
+ if (!origin || origin.isOpaque()) {
235
234
  return {
236
235
  error: 'No request found',
237
236
  };
238
237
  }
239
- const allowedSecurityOrigin = origin ?? null;
240
238
  const request = this.#networkLog.requests().find(req => {
241
239
  if (req.requestId() !== id) {
242
240
  return false;
243
241
  }
244
242
 
245
- return !allowedSecurityOrigin || req.initiatorSecurityOrigin().isSameOriginWith(allowedSecurityOrigin);
243
+ return isOriginAllowedByLock({status: 'ESTABLISHED_ORIGIN', origin}, req.initiatorSecurityOrigin());
246
244
  });
247
245
 
248
246
  if (request) {
@@ -288,13 +286,16 @@ export class ContextSelectionAgent extends AiAgent<never> {
288
286
  };
289
287
  }
290
288
  const origin = allowedOriginResult.origin;
289
+ if (!origin || origin.isOpaque()) {
290
+ return {
291
+ result: [],
292
+ };
293
+ }
291
294
 
292
295
  const files: Array<{file: string, id: number | undefined}> = [];
293
296
  const uiSourceCodes: Workspace.UISourceCode.UISourceCode[] = [];
294
297
  for (const file of ContextSelectionAgent.getUISourceCodes(this.#workspace)) {
295
- const fileSecurityOrigin = file.securityOrigin();
296
-
297
- if (origin && !fileSecurityOrigin.isSameOriginWith(origin)) {
298
+ if (!isOriginAllowedByLock({status: 'ESTABLISHED_ORIGIN', origin}, file.securityOrigin())) {
298
299
  continue;
299
300
  }
300
301
 
@@ -347,14 +348,7 @@ export class ContextSelectionAgent extends AiAgent<never> {
347
348
  };
348
349
  }
349
350
  const origin = allowedOriginResult.origin;
350
-
351
- const file = ContextSelectionAgent.getUISourceCodes(this.#workspace).find(file => {
352
- if (ContextSelectionAgent.uiSourceCodeId.get(file) !== params.id) {
353
- return false;
354
- }
355
- const fileSecurityOrigin = file.securityOrigin();
356
- return !origin || fileSecurityOrigin.isSameOriginWith(origin);
357
- });
351
+ const file = ContextSelectionAgent.getSourceById(params.id, origin, this.#workspace);
358
352
 
359
353
  if (!file) {
360
354
  return {
@@ -524,7 +518,7 @@ export class ContextSelectionAgent extends AiAgent<never> {
524
518
  };
525
519
  }
526
520
  const origin = allowedOriginResult.origin;
527
- if (!origin) {
521
+ if (!origin || origin.isOpaque()) {
528
522
  return {
529
523
  error: 'Unable to find page storage.',
530
524
  };
@@ -5,23 +5,11 @@
5
5
  import * as Root from '../../core/root/root.js';
6
6
  import type * as CommentManager from '../comment_manager/comment_manager.js';
7
7
 
8
- export type ChangeRecord = CommentManager.CommentManager.ChangeRecord;
9
-
10
- export const MAX_RECORDS = 1000;
11
-
12
8
  export class ChangeTracker {
13
- readonly #records = new Map<string, ChangeRecord>();
14
9
  readonly #commentManager: CommentManager.CommentManager.CommentManager;
15
- readonly #maxRecords: number;
16
- #lastRecord?: ChangeRecord;
17
10
 
18
- constructor(commentManager: CommentManager.CommentManager.CommentManager, maxRecords: number = MAX_RECORDS) {
11
+ constructor(commentManager: CommentManager.CommentManager.CommentManager) {
19
12
  this.#commentManager = commentManager;
20
- this.#maxRecords = Math.max(1, maxRecords);
21
- }
22
-
23
- get maxRecords(): number {
24
- return this.#maxRecords;
25
13
  }
26
14
 
27
15
  get isTracking(): boolean {
@@ -31,59 +19,12 @@ export class ChangeTracker {
31
19
  trackChange(
32
20
  description: string,
33
21
  anchor: CommentManager.CommentManager.CommentAnchorSignature,
34
- ): ChangeRecord|null {
22
+ ): void {
35
23
  if (!this.isTracking) {
36
- return null;
37
- }
38
- const record: ChangeRecord = {
39
- id: crypto.randomUUID(),
40
- description,
41
- timestamp: Date.now(),
42
- };
43
- while (this.#records.size >= this.#maxRecords) {
44
- this.#evictOldestRecord();
45
- }
46
-
47
- const thread = this.#commentManager.createCommentThread(anchor, undefined, undefined, [record]);
48
- this.#records.set(thread.id, record);
49
- this.#lastRecord = record;
50
- return record;
51
- }
52
-
53
- #evictOldestRecord(): void {
54
- // Delete the oldest record for which the comment thread has no user comments.
55
- // If all comment threads have user comments, delete the oldest record.
56
- for (const threadId of this.#records.keys()) {
57
- const thread = this.#commentManager.getCommentThread(threadId);
58
- if (!thread || thread.comments.length === 0) {
59
- this.#commentManager.removeCommentThread(threadId);
60
- this.#records.delete(threadId);
61
- return;
62
- }
24
+ return;
63
25
  }
64
26
 
65
- const oldestThreadId = this.#records.keys().next().value;
66
- if (oldestThreadId) {
67
- this.#records.delete(oldestThreadId);
68
- }
69
- }
70
-
71
- getChanges(): ChangeRecord[] {
72
- return Array.from(this.#records.values());
73
- }
74
-
75
- getLastChange(): ChangeRecord|undefined {
76
- return this.#lastRecord;
77
- }
78
-
79
- clear(): void {
80
- for (const threadId of this.#records.keys()) {
81
- const thread = this.#commentManager.getCommentThread(threadId);
82
- if (!thread || thread.comments.length === 0) {
83
- this.#commentManager.removeCommentThread(threadId);
84
- }
85
- }
86
- this.#records.clear();
87
- this.#lastRecord = undefined;
27
+ const thread = this.#commentManager.createCommentThread(anchor, description, 'DEVELOPER', true);
28
+ thread.save();
88
29
  }
89
30
  }
@@ -87,8 +87,13 @@ export class CD4ABridge extends Common.ObjectWrapper.ObjectWrapper<EventTypes> {
87
87
 
88
88
  #formatCommentText(thread: CommentManager.CommentThread): string {
89
89
  const rawText = thread.comments[0]?.text ?? '';
90
+
90
91
  const details: string[] = [];
91
92
 
93
+ if (thread.anchor.vePath) {
94
+ details.push(`- DevTools VEPath: ${thread.anchor.vePath}`);
95
+ }
96
+
92
97
  if (thread.anchor.textSignature) {
93
98
  details.push(`- DevTools element: ${thread.anchor.textSignature}`);
94
99
  }
@@ -5,7 +5,6 @@
5
5
  import * as Common from '../../core/common/common.js';
6
6
 
7
7
  import {
8
- type ChangeRecord,
9
8
  type Comment,
10
9
  type CommentAnchorSignature,
11
10
  CommentThread,
@@ -18,7 +17,6 @@ import {
18
17
  } from './CommentThread.js';
19
18
 
20
19
  export {
21
- type ChangeRecord,
22
20
  type Comment,
23
21
  type CommentAnchorSignature,
24
22
  CommentThread,
@@ -92,7 +90,7 @@ export class CommentManager extends Common.ObjectWrapper.ObjectWrapper<EventType
92
90
  anchor: CommentAnchorSignature,
93
91
  text?: string,
94
92
  author: 'DEVELOPER'|'AGENT' = 'DEVELOPER',
95
- changes?: ChangeRecord[],
93
+ isGeneratedComment?: boolean,
96
94
  ): CommentThread {
97
95
  const comments: Comment[] = text ? [{
98
96
  author,
@@ -103,7 +101,7 @@ export class CommentManager extends Common.ObjectWrapper.ObjectWrapper<EventType
103
101
  const thread = new CommentThread({
104
102
  anchor,
105
103
  comments,
106
- changes,
104
+ isGeneratedComment,
107
105
  });
108
106
  thread.addEventListener(CommentThreadEvents.CHANGED, this.#onThreadChanged, this);
109
107
 
@@ -123,7 +121,7 @@ export class CommentManager extends Common.ObjectWrapper.ObjectWrapper<EventType
123
121
  takeComments(): CommentThread[] {
124
122
  const threads: CommentThread[] = [];
125
123
  for (const thread of this.#commentThreads.values()) {
126
- if (thread.status === 'ACTIVE' && !thread.transmitted) {
124
+ if (thread.status === 'SENT_TO_AGENT' && !thread.transmitted) {
127
125
  thread.transmitted = true;
128
126
  threads.push(thread);
129
127
  }