chrome-ai-bridge 1.0.3 → 1.0.4

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 (69) hide show
  1. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Base64.js +20 -2
  2. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Gzip.js +11 -0
  3. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Object.js +6 -1
  4. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ParsedURL.js +3 -0
  5. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ResourceType.js +6 -0
  6. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Settings.js +18 -8
  7. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/InspectorFrontendHostStub.js +3 -3
  8. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/ResourceLoader.js +1 -1
  9. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/UserMetrics.js +17 -1
  10. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/ArrayUtilities.js +10 -0
  11. package/build/node_modules/chrome-devtools-frontend/front_end/core/platform/StringUtilities.js +63 -12
  12. package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/CDPConnection.js +1 -0
  13. package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/InspectorBackend.js +4 -1
  14. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSMatchedStyles.js +44 -9
  15. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSMetadata.js +6 -6
  16. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSModel.js +1 -1
  17. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/DOMModel.js +169 -12
  18. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/DebuggerModel.js +2 -1
  19. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/IsolateManager.js +6 -0
  20. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/NetworkManager.js +18 -4
  21. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/NetworkRequest.js +7 -21
  22. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/OverlayModel.js +17 -5
  23. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/RehydratingConnection.js +5 -1
  24. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ResourceTreeModel.js +8 -5
  25. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/SourceMap.js +14 -2
  26. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/SourceMapManager.js +1 -1
  27. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/SourceMapScopesInfo.js +11 -4
  28. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/Target.js +3 -1
  29. package/build/node_modules/chrome-devtools-frontend/front_end/generated/ARIAProperties.js +1 -1
  30. package/build/node_modules/chrome-devtools-frontend/front_end/generated/Deprecation.js +1 -16
  31. package/build/node_modules/chrome-devtools-frontend/front_end/generated/InspectorBackendCommands.js +35 -14
  32. package/build/node_modules/chrome-devtools-frontend/front_end/generated/SupportedCSSProperties.js +197 -101
  33. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.js +2 -1
  34. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.js +10 -16
  35. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.js +97 -26
  36. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/performance/AICallTree.js +35 -0
  37. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/CompilerScriptMapping.js +5 -3
  38. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/DebuggerWorkspaceBinding.js +7 -3
  39. package/build/node_modules/chrome-devtools-frontend/front_end/models/emulation/DeviceModeModel.js +1 -1
  40. package/build/node_modules/chrome-devtools-frontend/front_end/models/emulation/EmulatedDevices.js +14 -0
  41. package/build/node_modules/chrome-devtools-frontend/front_end/models/formatter/FormatterWorkerPool.js +8 -5
  42. package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/StackTraceImpl.js +70 -1
  43. package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/StackTraceModel.js +82 -30
  44. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/EventsSerializer.js +7 -2
  45. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/LanternComputationData.js +2 -2
  46. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/Processor.js +18 -19
  47. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/Styles.js +12 -4
  48. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/extras/Initiators.js +46 -0
  49. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/extras/TraceTree.js +4 -3
  50. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/extras/extras.js +1 -0
  51. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/LargestImagePaintHandler.js +2 -2
  52. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/LayoutShiftsHandler.js +1 -1
  53. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/MetaHandler.js +6 -0
  54. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/NetworkRequestsHandler.js +10 -1
  55. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/PageLoadMetricsHandler.js +44 -27
  56. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/helpers/Timing.js +9 -2
  57. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/Common.js +1 -6
  58. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/LCPBreakdown.js +2 -2
  59. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/LCPDiscovery.js +2 -4
  60. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/NetworkDependencyTree.js +3 -2
  61. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/insights/RenderBlocking.js +1 -1
  62. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/types/TraceEvents.js +30 -11
  63. package/build/node_modules/chrome-devtools-frontend/front_end/third_party/source-map-scopes-codec/package/src/decode/decode.js +28 -13
  64. package/build/node_modules/chrome-devtools-frontend/front_end/third_party/source-map-scopes-codec/package/src/encode/encoder.js +1 -1
  65. package/build/node_modules/chrome-devtools-frontend/front_end/third_party/source-map-scopes-codec/package/src/scopes.js +4 -0
  66. package/build/src/tools/chatgpt-web.js +68 -49
  67. package/build/src/tools/gemini-web.js +43 -16
  68. package/build/src/tools/pages.js +0 -1
  69. package/package.json +1 -1
@@ -4,7 +4,9 @@
4
4
  /**
5
5
  * This handler stores page load metrics, including web vitals,
6
6
  * and exports them in the shape of a map with the following shape:
7
- * Map(FrameId -> Map(navigationID -> metrics) )
7
+ * Map(FrameId -> Map(navigation -> metrics) )
8
+ *
9
+ * Includes soft navigations.
8
10
  *
9
11
  * It also exports all markers in a trace in an array.
10
12
  *
@@ -50,10 +52,6 @@ export function handleEvent(event) {
50
52
  pageLoadEventsArray.push(event);
51
53
  }
52
54
  function storePageLoadMetricAgainstNavigationId(navigation, event) {
53
- const navigationId = navigation.args.data?.navigationId;
54
- if (!navigationId) {
55
- throw new Error('Navigation event unexpectedly had no navigation ID.');
56
- }
57
55
  const frameId = getFrameIdForPageLoadEvent(event);
58
56
  const { rendererProcessesByFrame } = metaHandlerData();
59
57
  // If either of these pieces of data do not exist, the most likely
@@ -77,14 +75,14 @@ function storePageLoadMetricAgainstNavigationId(navigation, event) {
77
75
  const fcpTime = Types.Timing.Micro(event.ts - navigation.ts);
78
76
  const classification = scoreClassificationForFirstContentfulPaint(fcpTime);
79
77
  const metricScore = { event, metricName: "FCP" /* MetricName.FCP */, classification, navigation, timing: fcpTime };
80
- storeMetricScore(frameId, navigationId, metricScore);
78
+ storeMetricScore(frameId, navigation, metricScore);
81
79
  return;
82
80
  }
83
81
  if (Types.Events.isFirstPaint(event)) {
84
82
  const paintTime = Types.Timing.Micro(event.ts - navigation.ts);
85
83
  const classification = "unclassified" /* ScoreClassification.UNCLASSIFIED */;
86
84
  const metricScore = { event, metricName: "FP" /* MetricName.FP */, classification, navigation, timing: paintTime };
87
- storeMetricScore(frameId, navigationId, metricScore);
85
+ storeMetricScore(frameId, navigation, metricScore);
88
86
  return;
89
87
  }
90
88
  if (Types.Events.isMarkDOMContent(event)) {
@@ -96,7 +94,7 @@ function storePageLoadMetricAgainstNavigationId(navigation, event) {
96
94
  navigation,
97
95
  timing: dclTime,
98
96
  };
99
- storeMetricScore(frameId, navigationId, metricScore);
97
+ storeMetricScore(frameId, navigation, metricScore);
100
98
  return;
101
99
  }
102
100
  if (Types.Events.isInteractiveTime(event)) {
@@ -108,7 +106,7 @@ function storePageLoadMetricAgainstNavigationId(navigation, event) {
108
106
  navigation,
109
107
  timing: ttiValue,
110
108
  };
111
- storeMetricScore(frameId, navigationId, tti);
109
+ storeMetricScore(frameId, navigation, tti);
112
110
  const tbtValue = Helpers.Timing.milliToMicro(Types.Timing.Milli(event.args.args.total_blocking_time_ms));
113
111
  const tbt = {
114
112
  event,
@@ -117,7 +115,7 @@ function storePageLoadMetricAgainstNavigationId(navigation, event) {
117
115
  navigation,
118
116
  timing: tbtValue,
119
117
  };
120
- storeMetricScore(frameId, navigationId, tbt);
118
+ storeMetricScore(frameId, navigation, tbt);
121
119
  return;
122
120
  }
123
121
  if (Types.Events.isMarkLoad(event)) {
@@ -129,10 +127,10 @@ function storePageLoadMetricAgainstNavigationId(navigation, event) {
129
127
  navigation,
130
128
  timing: loadTime,
131
129
  };
132
- storeMetricScore(frameId, navigationId, metricScore);
130
+ storeMetricScore(frameId, navigation, metricScore);
133
131
  return;
134
132
  }
135
- if (Types.Events.isLargestContentfulPaintCandidate(event)) {
133
+ if (Types.Events.isAnyLargestContentfulPaintCandidate(event)) {
136
134
  const candidateIndex = event.args.data?.candidateIndex;
137
135
  if (!candidateIndex) {
138
136
  throw new Error('Largest Contentful Paint unexpectedly had no candidateIndex.');
@@ -146,15 +144,15 @@ function storePageLoadMetricAgainstNavigationId(navigation, event) {
146
144
  timing: lcpTime,
147
145
  };
148
146
  const metricsByNavigation = Platform.MapUtilities.getWithDefault(metricScoresByFrameId, frameId, () => new Map());
149
- const metrics = Platform.MapUtilities.getWithDefault(metricsByNavigation, navigationId, () => new Map());
147
+ const metrics = Platform.MapUtilities.getWithDefault(metricsByNavigation, navigation, () => new Map());
150
148
  const lastLCPCandidate = metrics.get("LCP" /* MetricName.LCP */);
151
149
  if (lastLCPCandidate === undefined) {
152
150
  selectedLCPCandidateEvents.add(lcp.event);
153
- storeMetricScore(frameId, navigationId, lcp);
151
+ storeMetricScore(frameId, navigation, lcp);
154
152
  return;
155
153
  }
156
154
  const lastLCPCandidateEvent = lastLCPCandidate.event;
157
- if (!Types.Events.isLargestContentfulPaintCandidate(lastLCPCandidateEvent)) {
155
+ if (!Types.Events.isAnyLargestContentfulPaintCandidate(lastLCPCandidateEvent)) {
158
156
  return;
159
157
  }
160
158
  const lastCandidateIndex = lastLCPCandidateEvent.args.data?.candidateIndex;
@@ -167,18 +165,21 @@ function storePageLoadMetricAgainstNavigationId(navigation, event) {
167
165
  if (lastCandidateIndex < candidateIndex) {
168
166
  selectedLCPCandidateEvents.delete(lastLCPCandidateEvent);
169
167
  selectedLCPCandidateEvents.add(lcp.event);
170
- storeMetricScore(frameId, navigationId, lcp);
168
+ storeMetricScore(frameId, navigation, lcp);
171
169
  }
172
170
  return;
173
171
  }
174
172
  if (Types.Events.isLayoutShift(event)) {
175
173
  return;
176
174
  }
175
+ if (Types.Events.isSoftNavigationStart(event)) {
176
+ return;
177
+ }
177
178
  return Platform.assertNever(event, `Unexpected event type: ${event}`);
178
179
  }
179
- function storeMetricScore(frameId, navigationId, metricScore) {
180
+ function storeMetricScore(frameId, navigation, metricScore) {
180
181
  const metricsByNavigation = Platform.MapUtilities.getWithDefault(metricScoresByFrameId, frameId, () => new Map());
181
- const metrics = Platform.MapUtilities.getWithDefault(metricsByNavigation, navigationId, () => new Map());
182
+ const metrics = Platform.MapUtilities.getWithDefault(metricsByNavigation, navigation, () => new Map());
182
183
  // If an entry with that metric name is present, delete it so that the new entry that
183
184
  // will replace it is added at the end of the map. This way we guarantee the map entries
184
185
  // are ordered in ASC manner by timestamp.
@@ -187,8 +188,9 @@ function storeMetricScore(frameId, navigationId, metricScore) {
187
188
  }
188
189
  export function getFrameIdForPageLoadEvent(event) {
189
190
  if (Types.Events.isFirstContentfulPaint(event) || Types.Events.isInteractiveTime(event) ||
190
- Types.Events.isLargestContentfulPaintCandidate(event) || Types.Events.isNavigationStart(event) ||
191
- Types.Events.isLayoutShift(event) || Types.Events.isFirstPaint(event)) {
191
+ Types.Events.isAnyLargestContentfulPaintCandidate(event) || Types.Events.isNavigationStart(event) ||
192
+ Types.Events.isSoftNavigationStart(event) || Types.Events.isLayoutShift(event) ||
193
+ Types.Events.isFirstPaint(event)) {
192
194
  return event.args.frame;
193
195
  }
194
196
  if (Types.Events.isMarkDOMContent(event) || Types.Events.isMarkLoad(event)) {
@@ -201,20 +203,35 @@ export function getFrameIdForPageLoadEvent(event) {
201
203
  Platform.assertNever(event, `Unexpected event type: ${event}`);
202
204
  }
203
205
  function getNavigationForPageLoadEvent(event) {
204
- if (Types.Events.isFirstContentfulPaint(event) || Types.Events.isLargestContentfulPaintCandidate(event) ||
206
+ if (Types.Events.isFirstContentfulPaint(event) || Types.Events.isAnyLargestContentfulPaintCandidate(event) ||
205
207
  Types.Events.isFirstPaint(event)) {
206
- const navigationId = event.args.data?.navigationId;
207
- if (!navigationId) {
208
- throw new Error('Trace event unexpectedly had no navigation ID.');
208
+ const { navigationsByNavigationId, softNavigationsById } = metaHandlerData();
209
+ let navigation;
210
+ if (event.name === "largestContentfulPaint::CandidateForSoftNavigation" /* Types.Events.Name.MARK_LCP_CANDIDATE_FOR_SOFT_NAVIGATION */ &&
211
+ event.args.data?.performanceTimelineNavigationId) {
212
+ navigation = softNavigationsById.get(event.args.data.performanceTimelineNavigationId);
213
+ if (!navigation) {
214
+ // The most recent soft navigation must have been before the trace started.
215
+ return null;
216
+ }
217
+ }
218
+ else {
219
+ const navigationId = event.args.data?.navigationId;
220
+ if (!navigationId) {
221
+ throw new Error(`Trace event unexpectedly had no navigation ID: ${JSON.stringify(event, null, 2)}`);
222
+ }
223
+ navigation = navigationsByNavigationId.get(navigationId);
209
224
  }
210
- const { navigationsByNavigationId } = metaHandlerData();
211
- const navigation = navigationsByNavigationId.get(navigationId);
212
225
  if (!navigation) {
213
226
  // This event's navigation has been filtered out by the meta handler as a noise event.
214
227
  return null;
215
228
  }
216
229
  return navigation;
217
230
  }
231
+ if (Types.Events.isSoftNavigationStart(event)) {
232
+ const { softNavigationsById } = metaHandlerData();
233
+ return softNavigationsById.get(event.args.context.performanceTimelineNavigationId) ?? null;
234
+ }
218
235
  if (Types.Events.isMarkDOMContent(event) || Types.Events.isInteractiveTime(event) ||
219
236
  Types.Events.isLayoutShift(event) || Types.Events.isMarkLoad(event)) {
220
237
  const frameId = getFrameIdForPageLoadEvent(event);
@@ -329,7 +346,7 @@ export async function finalize() {
329
346
  const allFinalLCPEvents = gatherFinalLCPEvents();
330
347
  const mainFrame = metaHandlerData().mainFrameId;
331
348
  // Filter out LCP candidates to use only definitive LCP values
332
- const allEventsButLCP = pageLoadEventsArray.filter(event => !Types.Events.isLargestContentfulPaintCandidate(event));
349
+ const allEventsButLCP = pageLoadEventsArray.filter(event => !Types.Events.isAnyLargestContentfulPaintCandidate(event));
333
350
  const markerEvents = [...allFinalLCPEvents, ...allEventsButLCP].filter(Types.Events.isMarkerEvent);
334
351
  // Filter by main frame and sort.
335
352
  allMarkerEvents =
@@ -9,9 +9,16 @@ export const secondsToMilli = (value) => Types.Timing.Milli(value * 1000);
9
9
  export const secondsToMicro = (value) => milliToMicro(secondsToMilli(value));
10
10
  export const microToMilli = (value) => Types.Timing.Milli(value / 1000);
11
11
  export const microToSeconds = (value) => Types.Timing.Seconds(value / 1000 / 1000);
12
- export function timeStampForEventAdjustedByClosestNavigation(event, traceBounds, navigationsByNavigationId, navigationsByFrameId) {
12
+ export function timeStampForEventAdjustedByClosestNavigation(event, traceBounds, navigationsByNavigationId, softNavigationsById, navigationsByFrameId) {
13
13
  let eventTimeStamp = event.ts - traceBounds.min;
14
- if (event.args?.data?.navigationId) {
14
+ if (event.name === "largestContentfulPaint::CandidateForSoftNavigation" /* Types.Events.Name.MARK_LCP_CANDIDATE_FOR_SOFT_NAVIGATION */ &&
15
+ event.args?.data?.performanceTimelineNavigationId) {
16
+ const navigationForEvent = softNavigationsById.get(event.args.data.performanceTimelineNavigationId);
17
+ if (navigationForEvent) {
18
+ eventTimeStamp = event.ts - navigationForEvent.ts;
19
+ }
20
+ }
21
+ else if (event.args?.data?.navigationId) {
15
22
  const navigationForEvent = navigationsByNavigationId.get(event.args.data.navigationId);
16
23
  if (navigationForEvent) {
17
24
  eventTimeStamp = event.ts - navigationForEvent.ts;
@@ -6,12 +6,7 @@ import * as Types from '../types/types.js';
6
6
  import { getLogNormalScore } from './Statistics.js';
7
7
  const GRAPH_SAVINGS_PRECISION = 50;
8
8
  export function getInsight(insightName, insightSet) {
9
- const insight = insightSet.model[insightName];
10
- if (insight instanceof Error) {
11
- return null;
12
- }
13
- // For some reason typescript won't narrow the type by removing Error, so do it manually.
14
- return insight;
9
+ return insightSet.model[insightName];
15
10
  }
16
11
  export function getLCP(insightSet) {
17
12
  const insight = getInsight("LCPBreakdown" /* InsightKeys.LCP_BREAKDOWN */, insightSet);
@@ -155,13 +155,13 @@ export function generateInsight(data, context) {
155
155
  if (!frameMetrics) {
156
156
  throw new Error('no frame metrics');
157
157
  }
158
- const navMetrics = frameMetrics.get(context.navigationId);
158
+ const navMetrics = frameMetrics.get(context.navigation);
159
159
  if (!navMetrics) {
160
160
  throw new Error('no navigation metrics');
161
161
  }
162
162
  const metricScore = navMetrics.get("LCP" /* Handlers.ModelHandlers.PageLoadMetrics.MetricName.LCP */);
163
163
  const lcpEvent = metricScore?.event;
164
- if (!lcpEvent || !Types.Events.isLargestContentfulPaintCandidate(lcpEvent)) {
164
+ if (!lcpEvent || !Types.Events.isAnyLargestContentfulPaintCandidate(lcpEvent)) {
165
165
  return finalize({ warnings: [InsightWarning.NO_LCP] });
166
166
  }
167
167
  // This helps calculate the subparts.
@@ -81,13 +81,13 @@ export function generateInsight(data, context) {
81
81
  if (!frameMetrics) {
82
82
  throw new Error('no frame metrics');
83
83
  }
84
- const navMetrics = frameMetrics.get(context.navigationId);
84
+ const navMetrics = frameMetrics.get(context.navigation);
85
85
  if (!navMetrics) {
86
86
  throw new Error('no navigation metrics');
87
87
  }
88
88
  const metricScore = navMetrics.get("LCP" /* Handlers.ModelHandlers.PageLoadMetrics.MetricName.LCP */);
89
89
  const lcpEvent = metricScore?.event;
90
- if (!lcpEvent || !Types.Events.isLargestContentfulPaintCandidate(lcpEvent)) {
90
+ if (!lcpEvent || !Types.Events.isAnyLargestContentfulPaintCandidate(lcpEvent)) {
91
91
  return finalize({ warnings: [InsightWarning.NO_LCP] });
92
92
  }
93
93
  const docRequest = networkRequests.byId.get(context.navigationId);
@@ -99,8 +99,6 @@ export function generateInsight(data, context) {
99
99
  return finalize({ lcpEvent });
100
100
  }
101
101
  const initiatorUrl = lcpRequest.args.data.initiator?.url;
102
- // TODO(b/372319476): Explore using trace event HTMLDocumentParser::FetchQueuedPreloads to determine if the request
103
- // is discovered by the preload scanner.
104
102
  const initiatedByMainDoc = lcpRequest?.args.data.initiator?.type === 'parser' && docRequest.args.data.url === initiatorUrl;
105
103
  const imgPreloadedOrFoundInHTML = lcpRequest?.args.data.isLinkPreload || initiatedByMainDoc;
106
104
  const imageLoadingAttr = lcpEvent.args.data?.loadingAttr;
@@ -4,6 +4,7 @@
4
4
  import * as Common from '../../../core/common/common.js';
5
5
  import * as i18n from '../../../core/i18n/i18n.js';
6
6
  import * as Platform from '../../../core/platform/platform.js';
7
+ import * as Extras from '../extras/extras.js';
7
8
  import * as Helpers from '../helpers/helpers.js';
8
9
  import * as Types from '../types/types.js';
9
10
  import { InsightCategory, } from './types.js';
@@ -406,8 +407,8 @@ function candidateRequestsByOrigin(data, mainResource, contextRequests, lcpGraph
406
407
  if (!hasValidTiming(request)) {
407
408
  return;
408
409
  }
409
- // Filter out all resources that are loaded by the document. Connections are already early.
410
- if (data.NetworkRequests.eventToInitiator.get(request) === mainResource) {
410
+ const initiator = Extras.Initiators.getNetworkInitiator(data, request);
411
+ if (initiator === mainResource) {
411
412
  return;
412
413
  }
413
414
  const url = new URL(request.args.data.url);
@@ -130,7 +130,7 @@ export function generateInsight(data, context) {
130
130
  });
131
131
  }
132
132
  const firstPaintTs = data.PageLoadMetrics.metricScoresByFrameId.get(context.frameId)
133
- ?.get(context.navigationId)
133
+ ?.get(context.navigation)
134
134
  ?.get("FP" /* Handlers.ModelHandlers.PageLoadMetrics.MetricName.FP */)
135
135
  ?.event?.ts;
136
136
  if (!firstPaintTs) {
@@ -12,6 +12,9 @@ export function isPhaseAsync(phase) {
12
12
  export function isFlowPhase(phase) {
13
13
  return phase === "s" /* Phase.FLOW_START */ || phase === "t" /* Phase.FLOW_STEP */ || phase === "f" /* Phase.FLOW_END */;
14
14
  }
15
+ export function objectIsEvent(obj) {
16
+ return 'cat' in obj && 'name' in obj && 'ts' in obj;
17
+ }
15
18
  export function objectIsCallFrame(object) {
16
19
  return ('functionName' in object && typeof object.functionName === 'string') &&
17
20
  ('scriptId' in object && (typeof object.scriptId === 'string' || typeof object.scriptId === 'number')) &&
@@ -38,17 +41,30 @@ export function isLegacySyntheticScreenshot(event) {
38
41
  export function isScreenshot(event) {
39
42
  return event.name === "Screenshot" /* Name.SCREENSHOT */ && 'source_id' in (event.args ?? {});
40
43
  }
44
+ export function isSoftNavigationStart(event) {
45
+ return event.name === "SoftNavigationStart" /* Name.SOFT_NAVIGATION_START */;
46
+ }
41
47
  const markerTypeGuards = [
42
48
  isMarkDOMContent,
43
49
  isMarkLoad,
44
50
  isFirstPaint,
45
51
  isFirstContentfulPaint,
46
- isLargestContentfulPaintCandidate,
52
+ isAnyLargestContentfulPaintCandidate,
47
53
  isNavigationStart,
54
+ isSoftNavigationStart,
55
+ ];
56
+ export const MarkerName = [
57
+ "MarkDOMContent" /* Name.MARK_DOM_CONTENT */,
58
+ "MarkLoad" /* Name.MARK_LOAD */,
59
+ "firstPaint" /* Name.MARK_FIRST_PAINT */,
60
+ "firstContentfulPaint" /* Name.MARK_FCP */,
61
+ "largestContentfulPaint::Candidate" /* Name.MARK_LCP_CANDIDATE */,
62
+ "largestContentfulPaint::CandidateForSoftNavigation" /* Name.MARK_LCP_CANDIDATE_FOR_SOFT_NAVIGATION */,
63
+ "navigationStart" /* Name.NAVIGATION_START */,
64
+ "SoftNavigationStart" /* Name.SOFT_NAVIGATION_START */,
48
65
  ];
49
- export const MarkerName = ['MarkDOMContent', 'MarkLoad', 'firstPaint', 'firstContentfulPaint', 'largestContentfulPaint::Candidate'];
50
66
  export function isMarkerEvent(event) {
51
- if (event.ph === "I" /* Phase.INSTANT */ || event.ph === "R" /* Phase.MARK */) {
67
+ if (event.ph === "I" /* Phase.INSTANT */ || "n" /* Phase.ASYNC_NESTABLE_INSTANT */ || event.ph === "R" /* Phase.MARK */) {
52
68
  return markerTypeGuards.some(fn => fn(event));
53
69
  }
54
70
  return false;
@@ -58,7 +74,7 @@ const pageLoadEventTypeGuards = [
58
74
  isInteractiveTime,
59
75
  ];
60
76
  export function eventIsPageLoadEvent(event) {
61
- if (event.ph === "I" /* Phase.INSTANT */ || event.ph === "R" /* Phase.MARK */) {
77
+ if (event.ph === "I" /* Phase.INSTANT */ || "n" /* Phase.ASYNC_NESTABLE_INSTANT */ || event.ph === "R" /* Phase.MARK */) {
62
78
  return pageLoadEventTypeGuards.some(fn => fn(event));
63
79
  }
64
80
  return false;
@@ -285,10 +301,13 @@ export function isLayoutInvalidationTracking(event) {
285
301
  return event.name === "LayoutInvalidationTracking" /* Name.LAYOUT_INVALIDATION_TRACKING */;
286
302
  }
287
303
  export function isFirstContentfulPaint(event) {
288
- return event.name === 'firstContentfulPaint';
304
+ return event.name === "firstContentfulPaint" /* Name.MARK_FCP */;
305
+ }
306
+ export function isAnyLargestContentfulPaintCandidate(event) {
307
+ return event.name === "largestContentfulPaint::Candidate" /* Name.MARK_LCP_CANDIDATE */ || event.name === "largestContentfulPaint::CandidateForSoftNavigation" /* Name.MARK_LCP_CANDIDATE_FOR_SOFT_NAVIGATION */;
289
308
  }
290
- export function isLargestContentfulPaintCandidate(event) {
291
- return event.name === "largestContentfulPaint::Candidate" /* Name.MARK_LCP_CANDIDATE */;
309
+ export function isSoftLargestContentfulPaintCandidate(event) {
310
+ return event.name === "largestContentfulPaint::CandidateForSoftNavigation" /* Name.MARK_LCP_CANDIDATE_FOR_SOFT_NAVIGATION */;
292
311
  }
293
312
  export function isLargestImagePaintCandidate(event) {
294
313
  return event.name === 'LargestImagePaint::Candidate';
@@ -297,13 +316,13 @@ export function isLargestTextPaintCandidate(event) {
297
316
  return event.name === 'LargestTextPaint::Candidate';
298
317
  }
299
318
  export function isMarkLoad(event) {
300
- return event.name === 'MarkLoad';
319
+ return event.name === "MarkLoad" /* Name.MARK_LOAD */;
301
320
  }
302
321
  export function isFirstPaint(event) {
303
- return event.name === 'firstPaint';
322
+ return event.name === "firstPaint" /* Name.MARK_FIRST_PAINT */;
304
323
  }
305
324
  export function isMarkDOMContent(event) {
306
- return event.name === 'MarkDOMContent';
325
+ return event.name === "MarkDOMContent" /* Name.MARK_DOM_CONTENT */;
307
326
  }
308
327
  export function isInteractiveTime(event) {
309
328
  return event.name === 'InteractiveTime';
@@ -369,7 +388,7 @@ export function isPrePaint(event) {
369
388
  }
370
389
  /** A VALID navigation start (as it has a populated documentLoaderURL) */
371
390
  export function isNavigationStart(event) {
372
- return event.name === 'navigationStart' && event.args?.data?.documentLoaderURL !== '';
391
+ return event.name === "navigationStart" /* Name.NAVIGATION_START */ && event.args?.data?.documentLoaderURL !== '';
373
392
  }
374
393
  export function isDidCommitSameDocumentNavigation(event) {
375
394
  return event.name === 'RenderFrameHostImpl::DidCommitSameDocumentNavigation' && event.ph === "X" /* Phase.COMPLETE */;
@@ -69,13 +69,12 @@ class Decoder {
69
69
  decode() {
70
70
  const iter = new TokenIterator(this.#encodedScopes);
71
71
  while (iter.hasNext()) {
72
- if (iter.peek() === ",") {
73
- iter.nextChar(); // Consume ",".
74
- this.#scopes.push(null); // Add an EmptyItem;
75
- continue;
76
- }
77
72
  const tag = iter.nextUnsignedVLQ();
78
73
  switch (tag) {
74
+ case 0 /* Tag.EMPTY */: {
75
+ this.#scopes.push(null);
76
+ break;
77
+ }
79
78
  case 1 /* Tag.ORIGINAL_SCOPE_START */: {
80
79
  const item = {
81
80
  flags: iter.nextUnsignedVLQ(),
@@ -158,6 +157,14 @@ class Decoder {
158
157
  this.#handleGeneratedRangeCallSite(iter.nextUnsignedVLQ(), iter.nextUnsignedVLQ(), iter.nextUnsignedVLQ());
159
158
  break;
160
159
  }
160
+ case 99 /* Tag.VENDOR_EXTENSION */: {
161
+ const _extensionNameIdx = iter.nextUnsignedVLQ();
162
+ break;
163
+ }
164
+ default: {
165
+ this.#throwInStrictMode(`Encountered illegal item tag ${tag}`);
166
+ break;
167
+ }
161
168
  }
162
169
  // Consume any trailing VLQ and the the ","
163
170
  while (iter.hasNext() && iter.peek() !== ",")
@@ -165,10 +172,6 @@ class Decoder {
165
172
  if (iter.hasNext())
166
173
  iter.nextChar();
167
174
  }
168
- if (iter.currentChar() === ",") {
169
- // Handle trailing EmptyItem.
170
- this.#scopes.push(null);
171
- }
172
175
  if (this.#scopeStack.length > 0) {
173
176
  this.#throwInStrictMode("Encountered ORIGINAL_SCOPE_START without matching END!");
174
177
  }
@@ -285,7 +288,6 @@ class Decoder {
285
288
  }
286
289
  }
287
290
  this.#rangeStack.push(range);
288
- this.#subRangeBindingsForRange.clear();
289
291
  }
290
292
  #handleGeneratedRangeBindingsItem(valueIdxs) {
291
293
  const range = this.#rangeStack.at(-1);
@@ -303,11 +305,21 @@ class Decoder {
303
305
  }
304
306
  }
305
307
  #recordGeneratedSubRangeBindingItem(variableIndex, bindings) {
306
- if (this.#subRangeBindingsForRange.has(variableIndex)) {
308
+ const range = this.#rangeStack.at(-1);
309
+ if (!range) {
310
+ this.#throwInStrictMode("Encountered GENERATED_RANGE_SUBRANGE_BINDING without surrounding GENERATED_RANGE_START");
311
+ return;
312
+ }
313
+ let subRangeBindings = this.#subRangeBindingsForRange.get(range);
314
+ if (!subRangeBindings) {
315
+ subRangeBindings = new Map();
316
+ this.#subRangeBindingsForRange.set(range, subRangeBindings);
317
+ }
318
+ if (subRangeBindings.has(variableIndex)) {
307
319
  this.#throwInStrictMode("Encountered multiple GENERATED_RANGE_SUBRANGE_BINDING items for the same variable");
308
320
  return;
309
321
  }
310
- this.#subRangeBindingsForRange.set(variableIndex, bindings);
322
+ subRangeBindings.set(variableIndex, bindings);
311
323
  }
312
324
  #handleGeneratedRangeCallSite(sourceIndex, line, column) {
313
325
  const range = this.#rangeStack.at(-1);
@@ -349,7 +361,10 @@ class Decoder {
349
361
  }
350
362
  }
351
363
  #handleGeneratedRangeSubRangeBindings(range) {
352
- for (const [variableIndex, bindings] of this.#subRangeBindingsForRange) {
364
+ const subRangeBindings = this.#subRangeBindingsForRange.get(range);
365
+ if (!subRangeBindings)
366
+ return;
367
+ for (const [variableIndex, bindings] of subRangeBindings) {
353
368
  const value = range.values[variableIndex];
354
369
  const subRanges = [];
355
370
  range.values[variableIndex] = subRanges;
@@ -48,7 +48,7 @@ export class Encoder {
48
48
  }
49
49
  #encodeOriginalScope(scope) {
50
50
  if (scope === null) {
51
- this.#encodedItems.push("");
51
+ this.#encodedItems.push("A" /* EncodedTag.EMPTY */);
52
52
  return;
53
53
  }
54
54
  this.#encodeOriginalScopeStart(scope);
@@ -0,0 +1,4 @@
1
+ // Copyright 2025 The Chromium Authors. All rights reserved.
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+ export {};
@@ -238,19 +238,47 @@ export const askChatGPTWeb = defineTool({
238
238
  // Get or create a dedicated ChatGPT tab
239
239
  const { page, needsNavigation } = await getOrCreateChatGPTPage(context);
240
240
  try {
241
- // Step 1: Navigate to ChatGPT (only if not already there)
241
+ // Step 1: Determine target URL (existing session or top page)
242
242
  response.appendResponseLine('ChatGPTに接続中...');
243
- if (needsNavigation) {
244
- await navigateWithRetry(page, CHATGPT_CONFIG.DEFAULT_URL, {
243
+ let isNewChat = false;
244
+ let sessionChatId;
245
+ let targetUrl = CHATGPT_CONFIG.DEFAULT_URL;
246
+ if (!createNewChat) {
247
+ // Check for existing session first
248
+ const sessions = await loadChatSessions();
249
+ const projectSessions = sessions[project] || [];
250
+ if (projectSessions.length > 0) {
251
+ // Get the most recently used session
252
+ const sortedSessions = [...projectSessions].sort((a, b) => new Date(b.lastUsed).getTime() - new Date(a.lastUsed).getTime());
253
+ const latestSession = sortedSessions[0];
254
+ sessionChatId = latestSession.chatId;
255
+ targetUrl = latestSession.url;
256
+ response.appendResponseLine(`既存のプロジェクトチャットを使用: ${latestSession.url}`);
257
+ }
258
+ else {
259
+ response.appendResponseLine('既存チャットが見つかりませんでした。新規作成します。');
260
+ isNewChat = true;
261
+ }
262
+ }
263
+ else {
264
+ isNewChat = true;
265
+ }
266
+ // Step 2: Navigate to target URL (skip if already there)
267
+ const currentUrl = page.url();
268
+ const isAlreadyOnTarget = sessionChatId
269
+ ? currentUrl.includes(sessionChatId)
270
+ : currentUrl.includes('chatgpt.com') && !needsNavigation;
271
+ if (!isAlreadyOnTarget) {
272
+ await navigateWithRetry(page, targetUrl, {
245
273
  waitUntil: 'networkidle2',
246
274
  });
247
- // Wait for page to fully render (ChatGPT takes time to load UI)
275
+ // Wait for page to fully render
248
276
  await new Promise(resolve => setTimeout(resolve, 2000));
249
277
  }
250
278
  else {
251
279
  response.appendResponseLine('✅ 既存のChatGPTタブを再利用');
252
280
  }
253
- // Step 2: Check login status using session probe (most reliable)
281
+ // Step 3: Check login status
254
282
  const loginStatus = await getLoginStatus(page, 'chatgpt');
255
283
  if (loginStatus === LoginStatus.NEEDS_LOGIN) {
256
284
  response.appendResponseLine('\n❌ ChatGPTへのログインが必要です');
@@ -287,54 +315,27 @@ export const askChatGPTWeb = defineTool({
287
315
  }
288
316
  }
289
317
  response.appendResponseLine('✅ ログイン確認完了');
290
- // Step 2: Load existing session or create new chat
291
- let isNewChat = false;
292
- let sessionChatId;
293
- if (!createNewChat) {
294
- // Try to load existing session for this project
295
- const sessions = await loadChatSessions();
296
- const projectSessions = sessions[project] || [];
297
- if (projectSessions.length > 0) {
298
- // Get the most recently used session
299
- const sortedSessions = [...projectSessions].sort((a, b) => new Date(b.lastUsed).getTime() - new Date(a.lastUsed).getTime());
300
- const latestSession = sortedSessions[0];
301
- response.appendResponseLine(`既存のプロジェクトチャットを使用: ${latestSession.url}`);
302
- sessionChatId = latestSession.chatId;
303
- // Skip navigation if already on the same chat
304
- const currentUrl = page.url();
305
- if (!currentUrl.includes(latestSession.chatId)) {
306
- await navigateWithRetry(page, latestSession.url, {
307
- waitUntil: 'networkidle2', // Wait for JS to finish loading
308
- });
318
+ // Step 4: Wait for input field (for existing sessions)
319
+ if (sessionChatId) {
320
+ let inputFieldReady = false;
321
+ for (let attempt = 0; attempt < 3; attempt++) {
322
+ try {
323
+ await page.waitForSelector('.ProseMirror[contenteditable="true"]', { timeout: 5000 });
324
+ inputFieldReady = true;
325
+ break;
309
326
  }
310
- // Wait for input field to be ready with retry
311
- let inputFieldReady = false;
312
- for (let attempt = 0; attempt < 3; attempt++) {
313
- try {
314
- await page.waitForSelector('.ProseMirror[contenteditable="true"]', { timeout: 5000 });
315
- inputFieldReady = true;
316
- break;
327
+ catch {
328
+ if (attempt < 2) {
329
+ response.appendResponseLine(`⏳ 入力欄を待機中... (${attempt + 1}/3)`);
330
+ await new Promise(r => setTimeout(r, 2000));
317
331
  }
318
- catch {
319
- if (attempt < 2) {
320
- response.appendResponseLine(`⏳ 入力欄を待機中... (${attempt + 1}/3)`);
321
- await new Promise(r => setTimeout(r, 2000));
322
- }
323
- }
324
- }
325
- if (!inputFieldReady) {
326
- response.appendResponseLine('⚠️ 入力欄の準備に時間がかかっています。続行を試みます...');
327
332
  }
328
333
  }
329
- else {
330
- response.appendResponseLine('既存チャットが見つかりませんでした。新規作成します。');
331
- isNewChat = true;
334
+ if (!inputFieldReady) {
335
+ response.appendResponseLine('⚠️ 入力欄の準備に時間がかかっています。続行を試みます...');
332
336
  }
333
337
  }
334
- else {
335
- isNewChat = true;
336
- }
337
- // Step 3: Create new chat if needed
338
+ // Step 5: Create new chat if needed
338
339
  if (isNewChat) {
339
340
  response.appendResponseLine('新規チャットを作成中...');
340
341
  // Click "新しいチャット"
@@ -549,8 +550,26 @@ export const askChatGPTWeb = defineTool({
549
550
  }
550
551
  }
551
552
  catch (error) {
552
- const errorMessage = error instanceof Error ? error.message : String(error);
553
- response.appendResponseLine(`❌ エラー: ${errorMessage}`);
553
+ const msg = error instanceof Error ? error.message : String(error);
554
+ // ケース分類:致命的エラーには明確なメッセージを表示
555
+ if (msg.includes('No page selected') || msg.includes('page is null')) {
556
+ response.appendResponseLine('❌ ブラウザタブがありません');
557
+ response.appendResponseLine('→ MCPサーバーを再起動してブラウザを開いてください');
558
+ }
559
+ else if (msg.includes('Target closed') ||
560
+ msg.includes('Session closed') ||
561
+ msg.includes('Connection closed')) {
562
+ response.appendResponseLine('❌ ブラウザ接続が切れました');
563
+ response.appendResponseLine('→ MCPサーバーを再起動してください');
564
+ }
565
+ else if (msg.includes('Protocol error') ||
566
+ msg.includes('Browser disconnected')) {
567
+ response.appendResponseLine('❌ ブラウザとの通信エラー');
568
+ response.appendResponseLine('→ MCPサーバーを再起動してください');
569
+ }
570
+ else {
571
+ response.appendResponseLine(`❌ エラー: ${msg}`);
572
+ }
554
573
  }
555
574
  },
556
575
  });