chrome-devtools-frontend 1.0.1701636 → 1.0.1702515

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 (78) hide show
  1. package/eslint.config.mjs +3 -1
  2. package/front_end/core/host/UserMetricsEnums.ts +2 -1
  3. package/front_end/core/root/ExperimentNames.ts +1 -0
  4. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +2 -7
  5. package/front_end/devtools_compatibility.js +2 -1
  6. package/front_end/entrypoints/main/MainImpl.ts +8 -0
  7. package/front_end/generated/InspectorBackendCommands.ts +3 -3
  8. package/front_end/generated/protocol.ts +0 -3
  9. package/front_end/models/ai_assistance/AiAgent2.ts +13 -1
  10. package/front_end/models/ai_assistance/AiConversation.ts +1 -1
  11. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +13 -19
  12. package/front_end/models/change_tracker/ChangeTracker.ts +5 -64
  13. package/front_end/models/comment_manager/CommentManager.ts +3 -5
  14. package/front_end/models/comment_manager/CommentThread.ts +30 -13
  15. package/front_end/models/issues_manager/CookieDeprecationMetadataIssue.ts +6 -7
  16. package/front_end/models/issues_manager/DeprecationIssue.ts +1 -1
  17. package/front_end/models/issues_manager/MarkdownIssueDescription.ts +14 -5
  18. package/front_end/models/issues_manager/SRIMessageSignatureIssue.ts +2 -4
  19. package/front_end/models/issues_manager/descriptions/cookieWarnMetadataGrantRead.md +0 -1
  20. package/front_end/models/issues_manager/descriptions/cookieWarnMetadataGrantReadOptOut.md +5 -0
  21. package/front_end/models/issues_manager/descriptions/cookieWarnMetadataGrantSet.md +0 -1
  22. package/front_end/models/issues_manager/descriptions/cookieWarnMetadataGrantSetOptOut.md +5 -0
  23. package/front_end/models/issues_manager/descriptions/deprecation.md +1 -1
  24. package/front_end/models/issues_manager/descriptions/sriValidationFailedIntegrityMismatch.md +2 -0
  25. package/front_end/models/persistence/NetworkPersistenceManager.ts +14 -2
  26. package/front_end/panels/common/CommentThreadWidget.ts +29 -10
  27. package/front_end/panels/common/CommentsOverlayWidget.ts +50 -14
  28. package/front_end/panels/common/commentThreadWidget.css +11 -0
  29. package/front_end/panels/elements/StylePropertiesSection.ts +10 -0
  30. package/front_end/panels/elements/StylePropertyTreeElement.ts +1 -1
  31. package/front_end/panels/elements/StylesSidebarPane.ts +101 -49
  32. package/front_end/panels/elements/components/CSSQuery.ts +1 -1
  33. package/front_end/panels/elements/components/cssQuery.css +4 -0
  34. package/front_end/panels/elements/elements.ts +0 -1
  35. package/front_end/panels/elements/stylesSidebarPane.css +6 -0
  36. package/front_end/panels/sources/ResourceOriginPlugin.ts +8 -3
  37. package/front_end/panels/sources/ScopeChainSidebarPane.ts +3 -4
  38. package/front_end/panels/sources/TabbedEditorContainer.ts +339 -126
  39. package/front_end/panels/sources/UISourceCodeFrame.ts +9 -0
  40. package/front_end/third_party/chromium/README.chromium +1 -1
  41. package/front_end/third_party/chromium/client-variations/ClientVariations.js +25 -96
  42. package/front_end/third_party/codemirror.next/chunk/angular.js +1 -1
  43. package/front_end/third_party/codemirror.next/chunk/codemirror.js +1 -1
  44. package/front_end/third_party/codemirror.next/chunk/codemirror.js.map +1 -1
  45. package/front_end/third_party/codemirror.next/chunk/cpp.js +1 -1
  46. package/front_end/third_party/codemirror.next/chunk/cpp.js.map +1 -1
  47. package/front_end/third_party/codemirror.next/chunk/java.js +1 -1
  48. package/front_end/third_party/codemirror.next/chunk/java.js.map +1 -1
  49. package/front_end/third_party/codemirror.next/chunk/legacy.js +1 -1
  50. package/front_end/third_party/codemirror.next/chunk/less.js +1 -1
  51. package/front_end/third_party/codemirror.next/chunk/less.js.map +1 -1
  52. package/front_end/third_party/codemirror.next/chunk/markdown.js +1 -1
  53. package/front_end/third_party/codemirror.next/chunk/markdown.js.map +1 -1
  54. package/front_end/third_party/codemirror.next/chunk/php.js +1 -1
  55. package/front_end/third_party/codemirror.next/chunk/php.js.map +1 -1
  56. package/front_end/third_party/codemirror.next/chunk/python.js +1 -1
  57. package/front_end/third_party/codemirror.next/chunk/python.js.map +1 -1
  58. package/front_end/third_party/codemirror.next/chunk/sass.js +1 -1
  59. package/front_end/third_party/codemirror.next/chunk/sass.js.map +1 -1
  60. package/front_end/third_party/codemirror.next/chunk/svelte.js +1 -1
  61. package/front_end/third_party/codemirror.next/chunk/svelte.js.map +1 -1
  62. package/front_end/third_party/codemirror.next/chunk/vue.js +1 -1
  63. package/front_end/third_party/codemirror.next/chunk/wast.js +1 -1
  64. package/front_end/third_party/codemirror.next/chunk/wast.js.map +1 -1
  65. package/front_end/third_party/codemirror.next/chunk/xml.js +1 -1
  66. package/front_end/third_party/codemirror.next/chunk/xml.js.map +1 -1
  67. package/front_end/third_party/codemirror.next/codemirror.next.js +1 -1
  68. package/front_end/third_party/codemirror.next/package.json +4 -1
  69. package/front_end/third_party/codemirror.next/rebuild.sh +1 -1
  70. package/front_end/ui/comments/CommentOverlayManager.ts +11 -10
  71. package/front_end/ui/components/markdown_view/MarkdownPlaceholderLitRenderer.ts +117 -0
  72. package/front_end/ui/components/markdown_view/markdown_view.ts +2 -0
  73. package/front_end/ui/legacy/Treeoutline.ts +28 -19
  74. package/front_end/ui/legacy/UIUtils.ts +1 -1
  75. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +228 -343
  76. package/front_end/ui/legacy/components/object_ui/objectPropertiesSection.css +2 -27
  77. package/mcp/mcp.ts +1 -0
  78. package/package.json +1 -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
  ],
@@ -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
  }
@@ -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"});
@@ -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',
@@ -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
  }
@@ -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
  }
@@ -58,13 +58,7 @@ export interface Comment {
58
58
  timestamp: number;
59
59
  }
60
60
 
61
- export interface ChangeRecord {
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
- changes?: ChangeRecord[];
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.changes = options.changes;
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
- save(text?: string, author: 'DEVELOPER'|'AGENT' = 'DEVELOPER'): void {
110
- let changed = false;
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
- changed = true;
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
- const fileName =
36
+ let fileName =
37
37
  this.details().operation === 'SetCookie' ? 'cookieWarnMetadataGrantSet.md' : 'cookieWarnMetadataGrantRead.md';
38
38
 
39
- let optOutText = '';
39
+ const substitutions = new Map<string, string>();
40
40
  if (this.details().isOptOutTopLevel) {
41
- optOutText = '\n\n (Top level site opt-out: ' + this.details().optOutPercentage +
42
- '% - [learn more](gracePeriodStagedControlExplainer))';
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: new Map([
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,
@@ -13,12 +13,14 @@ import * as Marked from '../../third_party/marked/marked.js';
13
13
  */
14
14
  export interface MarkdownIssueDescription {
15
15
  file: string;
16
+ title?: string;
16
17
  substitutions?: Map<string, string>;
17
18
  links: Array<{link: string, linkTitle: string}>;
18
19
  }
19
20
 
20
21
  export interface LazyMarkdownIssueDescription {
21
22
  file: string;
23
+ title?: () => string;
22
24
  substitutions?: Map<string, () => string>;
23
25
  links: Array<{link: string, linkTitle: () => string}>;
24
26
  }
@@ -38,8 +40,9 @@ export function resolveLazyDescription(lazyDescription: LazyMarkdownIssueDescrip
38
40
  substitutionMap.set(key, value());
39
41
  });
40
42
 
41
- const description = {
43
+ const description: MarkdownIssueDescription = {
42
44
  file: lazyDescription.file,
45
+ title: lazyDescription.title?.(),
43
46
  links: lazyDescription.links.map(linksMap),
44
47
  substitutions: substitutionMap,
45
48
  };
@@ -54,6 +57,7 @@ export interface IssueDescription {
54
57
  title: string;
55
58
  markdown: Marked.Marked.Token[];
56
59
  links: Array<{link: string, linkTitle: string}>;
60
+ substitutions?: Map<string, string>;
57
61
  }
58
62
 
59
63
  export async function getFileContent(url: URL): Promise<string> {
@@ -82,15 +86,16 @@ export async function createIssueDescriptionFromMarkdown(description: MarkdownIs
82
86
  export function createIssueDescriptionFromRawMarkdown(
83
87
  markdown: string, description: MarkdownIssueDescription): IssueDescription {
84
88
  const markdownAst = Marked.Marked.lexer(markdown);
85
- const title = findTitleFromMarkdownAst(markdownAst);
86
- if (!title) {
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
 
@@ -148,5 +153,9 @@ export async function getIssueTitleFromMarkdownDescription(description: Markdown
148
153
  Promise<string|null> {
149
154
  const rawMarkdown = await getMarkdownFileContent(description.file);
150
155
  const markdownAst = Marked.Marked.lexer(rawMarkdown);
151
- return findTitleFromMarkdownAst(markdownAst);
156
+ const markdownTitle = findTitleFromMarkdownAst(markdownAst);
157
+ if (!markdownTitle) {
158
+ return null;
159
+ }
160
+ return description.title ?? markdownTitle;
152
161
  }
@@ -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
- const prefix = '\n* ';
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))