chrome-devtools-frontend 1.0.1571007 → 1.0.1571573

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 (65) hide show
  1. package/agents/prompts/README.md +18 -0
  2. package/agents/prompts/devtools-imports.md +47 -0
  3. package/agents/prompts/verification.md +27 -0
  4. package/front_end/core/host/UserMetrics.ts +1 -1
  5. package/front_end/core/root/Runtime.ts +48 -34
  6. package/front_end/core/sdk/CSSProperty.ts +1 -1
  7. package/front_end/core/sdk/CookieModel.ts +1 -1
  8. package/front_end/core/sdk/DOMModel.ts +1 -1
  9. package/front_end/core/sdk/OverlayModel.ts +3 -2
  10. package/front_end/entrypoints/main/MainImpl.ts +29 -18
  11. package/front_end/generated/SupportedCSSProperties.js +2 -6
  12. package/front_end/models/issues_manager/ContrastCheckTrigger.ts +2 -2
  13. package/front_end/models/javascript_metadata/NativeFunctions.js +8 -0
  14. package/front_end/models/trace/ModelImpl.ts +0 -4
  15. package/front_end/panels/accessibility/AccessibilitySidebarView.ts +1 -1
  16. package/front_end/panels/ai_assistance/PatchWidget.ts +10 -15
  17. package/front_end/panels/ai_assistance/SelectWorkspaceDialog.ts +5 -4
  18. package/front_end/panels/ai_assistance/components/ChatMessage.ts +3 -2
  19. package/front_end/panels/application/FrameDetailsView.ts +2 -1
  20. package/front_end/panels/application/components/ReportsGrid.ts +2 -1
  21. package/front_end/panels/common/AiCodeCompletionSummaryToolbar.ts +3 -5
  22. package/front_end/panels/common/AiCodeCompletionTeaser.ts +5 -5
  23. package/front_end/panels/console/ConsoleInsightTeaser.ts +10 -11
  24. package/front_end/panels/css_overview/CSSOverviewCompletedView.ts +2 -2
  25. package/front_end/panels/css_overview/CSSOverviewModel.ts +1 -1
  26. package/front_end/panels/elements/ComputedStyleModel.ts +11 -13
  27. package/front_end/panels/elements/ComputedStyleWidget.ts +3 -3
  28. package/front_end/panels/elements/ElementsPanel.ts +5 -2
  29. package/front_end/panels/elements/ElementsSidebarPane.ts +1 -1
  30. package/front_end/panels/elements/PlatformFontsWidget.ts +1 -1
  31. package/front_end/panels/elements/StylePropertiesSection.ts +1 -1
  32. package/front_end/panels/elements/StylePropertyTreeElement.ts +1 -1
  33. package/front_end/panels/emulation/DeviceModeWrapper.ts +101 -62
  34. package/front_end/panels/explain/components/ConsoleInsight.ts +27 -23
  35. package/front_end/panels/explain/components/consoleInsight.css +1 -1
  36. package/front_end/panels/network/RequestTimingView.ts +4 -3
  37. package/front_end/panels/network/components/RequestHeadersView.css +2 -2
  38. package/front_end/panels/network/components/RequestHeadersView.ts +7 -6
  39. package/front_end/panels/profiler/HeapProfileView.ts +3 -3
  40. package/front_end/panels/profiler/HeapSnapshotView.ts +2 -2
  41. package/front_end/panels/recorder/RecorderController.ts +6 -7
  42. package/front_end/panels/recorder/recorderController.css +1 -1
  43. package/front_end/panels/security/CookieControlsView.ts +2 -2
  44. package/front_end/panels/security/CookieReportView.ts +7 -7
  45. package/front_end/panels/settings/AISettingsTab.ts +5 -5
  46. package/front_end/panels/settings/components/SyncSection.ts +4 -3
  47. package/front_end/panels/sources/CallStackSidebarPane.ts +4 -1
  48. package/front_end/panels/sources/NavigatorView.ts +1 -1
  49. package/front_end/panels/sources/SourcesPanel.ts +3 -1
  50. package/front_end/panels/timeline/CompatibilityTracksAppender.ts +1 -1
  51. package/front_end/panels/timeline/ThreadAppender.ts +1 -1
  52. package/front_end/panels/timeline/TimelineController.ts +4 -3
  53. package/front_end/panels/timeline/TimelinePanel.ts +4 -3
  54. package/front_end/panels/whats_new/ReleaseNoteView.ts +4 -3
  55. package/front_end/third_party/chromium/README.chromium +1 -1
  56. package/front_end/ui/components/markdown_view/CodeBlock.ts +3 -6
  57. package/front_end/ui/components/markdown_view/MarkdownLink.ts +3 -4
  58. package/front_end/ui/components/panel_feedback/PanelFeedback.ts +3 -4
  59. package/front_end/ui/components/panel_feedback/PreviewToggle.ts +9 -6
  60. package/front_end/ui/components/panel_feedback/panelFeedback.css +2 -2
  61. package/front_end/ui/kit/link/Link.ts +1 -14
  62. package/front_end/ui/legacy/components/color_picker/ContrastDetails.ts +1 -1
  63. package/front_end/ui/legacy/components/color_picker/ContrastOverlay.ts +5 -4
  64. package/front_end/ui/visual_logging/KnownContextValues.ts +2 -1
  65. package/package.json +1 -1
@@ -856,7 +856,8 @@ export class FrameDetailsReportView extends UI.Widget.Widget {
856
856
 
857
857
  constructor(element?: HTMLElement, view = DEFAULT_VIEW) {
858
858
  super(element, {useShadowDom: true});
859
- this.#protocolMonitorExperimentEnabled = Root.Runtime.experiments.isEnabled('protocol-monitor');
859
+ this.#protocolMonitorExperimentEnabled =
860
+ Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.PROTOCOL_MONITOR);
860
861
  this.#view = view;
861
862
  }
862
863
 
@@ -134,7 +134,8 @@ export class ReportsGrid extends UI.Widget.Widget {
134
134
  constructor(element?: HTMLElement, view: View = DEFAULT_VIEW) {
135
135
  super(element);
136
136
  this.#view = view;
137
- this.#protocolMonitorExperimentEnabled = Root.Runtime.experiments.isEnabled('protocol-monitor');
137
+ this.#protocolMonitorExperimentEnabled =
138
+ Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.PROTOCOL_MONITOR);
138
139
  this.requestUpdate();
139
140
  }
140
141
 
@@ -4,13 +4,13 @@
4
4
 
5
5
  import '../../ui/components/spinners/spinners.js';
6
6
  import '../../ui/components/tooltips/tooltips.js';
7
+ import '../../ui/kit/kit.js';
7
8
 
8
9
  import * as Host from '../../core/host/host.js';
9
10
  import * as i18n from '../../core/i18n/i18n.js';
10
11
  import type * as AiCodeCompletion from '../../models/ai_code_completion/ai_code_completion.js';
11
12
  import * as UI from '../../ui/legacy/legacy.js';
12
13
  import {Directives, html, nothing, render} from '../../ui/lit/lit.js';
13
- import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
14
14
 
15
15
  import {AiCodeCompletionDisclaimer} from './AiCodeCompletionDisclaimer.js';
16
16
  import styles from './aiCodeCompletionSummaryToolbar.css.js';
@@ -86,12 +86,10 @@ export const DEFAULT_SUMMARY_TOOLBAR_VIEW: View = (input, _output, target) => {
86
86
  variant="rich"
87
87
  jslogContext="ai-code-completion-citations"
88
88
  ><div class="citations-tooltip-container">
89
- ${Directives.repeat(input.citations, citation => html`<x-link
89
+ ${Directives.repeat(input.citations, citation => html`<devtools-link
90
90
  tabIndex="0"
91
91
  href=${citation}
92
- jslog=${VisualLogging.link('ai-code-completion-citations.citation-link').track({
93
- click: true
94
- })}>${citation}</x-link>`)}</div></devtools-tooltip>
92
+ .jslogContext=${'ai-code-completion-citations.citation-link'}>${citation}</devtools-link>`)}</div></devtools-tooltip>
95
93
  </div>` : nothing;
96
94
 
97
95
  render(
@@ -2,6 +2,8 @@
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
4
 
5
+ import '../../ui/kit/kit.js';
6
+
5
7
  import * as Common from '../../core/common/common.js';
6
8
  import * as Host from '../../core/host/host.js';
7
9
  import * as i18n from '../../core/i18n/i18n.js';
@@ -244,13 +246,11 @@ export class AiCodeCompletionTeaser extends UI.Widget.Widget {
244
246
  {
245
247
  iconName: 'warning',
246
248
  // clang-format off
247
- content: html`<x-link
249
+ content: html`<devtools-link
248
250
  href=${CODE_SNIPPET_WARNING_URL}
249
251
  class="link devtools-link"
250
- jslog=${VisualLogging.link('code-snippets-explainer.ai-code-completion-teaser').track({
251
- click: true
252
- })}
253
- >${lockedString(UIStringsNotTranslate.freDisclaimerTextUseWithCaution)}</x-link>`,
252
+ .jslogContext=${'code-snippets-explainer.ai-code-completion-teaser'}
253
+ >${lockedString(UIStringsNotTranslate.freDisclaimerTextUseWithCaution)}</devtools-link>`,
254
254
  // clang-format on
255
255
  });
256
256
  return reminderItems;
@@ -3,6 +3,7 @@
3
3
  // found in the LICENSE file.
4
4
 
5
5
  import '../../ui/components/tooltips/tooltips.js';
6
+ import '../../ui/kit/kit.js';
6
7
 
7
8
  import * as Common from '../../core/common/common.js';
8
9
  import * as Host from '../../core/host/host.js';
@@ -167,13 +168,13 @@ function renderNoModel(input: ViewInput): Lit.TemplateResult {
167
168
  lockedString(UIStringsNotTranslate.getHelpForWarning) :
168
169
  lockedString(UIStringsNotTranslate.getHelpForError)}
169
170
  </h2>
170
- <div>You can get quick answers from <x-link
171
- .jslog=${VisualLogging.link().track({click: true, keydown: 'Enter|Space'}).context('insights-teaser-built-in-ai-documentation')}
171
+ <div>You can get quick answers from <devtools-link
172
+ .jslogContext=${'insights-teaser-built-in-ai-documentation'}
172
173
  class="link"
173
174
  href=${BUILT_IN_AI_DOCUMENTATION}
174
175
  >
175
176
  Chrome’s Built-in AI
176
- </x-link>
177
+ </devtools-link>
177
178
  , without any data leaving your device.
178
179
  </div>
179
180
  <div>${lockedString(UIStringsNotTranslate.toUseDownload)}</div>
@@ -324,12 +325,12 @@ function renderFooter(input: ViewInput): Lit.TemplateResult {
324
325
  >
325
326
  <div class="info-tooltip-text">${lockedString(UIStringsNotTranslate.infoTooltipText)}</div>
326
327
  <div class="learn-more">
327
- <x-link
328
+ <devtools-link
328
329
  class="devtools-link"
329
330
  title=${lockedString(UIStringsNotTranslate.learnMoreAboutAiSummaries)}
330
331
  href=${DATA_USAGE_URL}
331
- jslog=${VisualLogging.link().track({click: true, keydown:'Enter|Space'}).context('explain.teaser.learn-more')}
332
- >${lockedString(UIStringsNotTranslate.learnMoreAboutAiSummaries)}</x-link>
332
+ .jslogContext=${'explain.teaser.learn-more'}
333
+ >${lockedString(UIStringsNotTranslate.learnMoreAboutAiSummaries)}</devtools-link>
333
334
  </div>
334
335
  </devtools-tooltip>
335
336
  ${renderDontShowCheckbox(input)}
@@ -509,13 +510,11 @@ export class ConsoleInsightTeaser extends UI.Widget.Widget {
509
510
  {
510
511
  iconName: 'warning',
511
512
  // clang-format off
512
- content: html`<x-link
513
+ content: html`<devtools-link
513
514
  href=${CODE_SNIPPET_WARNING_URL}
514
515
  class="link devtools-link"
515
- jslog=${VisualLogging.link('explain.teaser.code-snippets-explainer').track({
516
- click: true
517
- })}
518
- >${lockedString(UIStringsNotTranslate.freDisclaimerTextUseWithCaution)}</x-link>`,
516
+ .jslogContext=${'explain.teaser.code-snippets-explainer'}
517
+ >${lockedString(UIStringsNotTranslate.freDisclaimerTextUseWithCaution)}</devtools-link>`,
519
518
  // clang-format on
520
519
  }
521
520
  ],
@@ -483,7 +483,7 @@ function renderContrastIssue(key: string, issues: ContrastIssue[]): TemplateResu
483
483
  const color = (minContrastIssue.textColor.asString(Common.Color.Format.HEXA));
484
484
  const backgroundColor = (minContrastIssue.backgroundColor.asString(Common.Color.Format.HEXA));
485
485
 
486
- const showAPCA = Root.Runtime.experiments.isEnabled('apca');
486
+ const showAPCA = Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.APCA);
487
487
 
488
488
  const title = i18nString(UIStrings.textColorSOverSBackgroundResults, {
489
489
  PH1: color,
@@ -1041,7 +1041,7 @@ function renderContrastRatio(data: PopulateNodesEventNodeTypes): TemplateResult
1041
1041
  if (!('contrastRatio' in data)) {
1042
1042
  throw new Error('Contrast ratio entry is missing a contrast ratio.');
1043
1043
  }
1044
- const showAPCA = Root.Runtime.experiments.isEnabled('apca');
1044
+ const showAPCA = Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.APCA);
1045
1045
  const contrastRatio = Platform.NumberUtilities.floor(data.contrastRatio, 2);
1046
1046
  const contrastRatioString = showAPCA ? contrastRatio + '%' : contrastRatio;
1047
1047
  const border = getBorderString(data.backgroundColor);
@@ -267,7 +267,7 @@ export class CSSOverviewModel extends SDK.SDKModel.SDKModel<void> {
267
267
  const formattedTextColor = formatColor(blendedTextColor);
268
268
  const formattedBackgroundColor = formatColor(blendedBackgroundColor.asLegacyColor());
269
269
  const key = `${formattedTextColor}_${formattedBackgroundColor}`;
270
- if (Root.Runtime.experiments.isEnabled('apca')) {
270
+ if (Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.APCA)) {
271
271
  const contrastRatio = contrastInfo.contrastRatioAPCA();
272
272
  const threshold = contrastInfo.contrastRatioAPCAThreshold();
273
273
  const passes = contrastRatio && threshold ? Math.abs(contrastRatio) >= threshold : false;
@@ -18,13 +18,12 @@ export class ComputedStyleModel extends Common.ObjectWrapper.ObjectWrapper<Event
18
18
  private computedStylePromise?: Promise<ComputedStyle|null>;
19
19
  private currentTrackedNodeId?: number;
20
20
 
21
- constructor() {
21
+ constructor(node?: SDK.DOMModel.DOMNode|null) {
22
22
  super();
23
23
  this.#cssModel = null;
24
24
  this.eventListeners = [];
25
- this.#node = UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode);
25
+ this.#node = node ?? null;
26
26
 
27
- UI.Context.Context.instance().addFlavorChangeListener(SDK.DOMModel.DOMNode, this.onNodeChanged, this);
28
27
  UI.Context.Context.instance().addFlavorChangeListener(
29
28
  StylesSidebarPane, this.evaluateTrackingComputedStyleUpdatesForNode, this);
30
29
  UI.Context.Context.instance().addFlavorChangeListener(
@@ -32,17 +31,23 @@ export class ComputedStyleModel extends Common.ObjectWrapper.ObjectWrapper<Event
32
31
  }
33
32
 
34
33
  dispose(): void {
35
- UI.Context.Context.instance().removeFlavorChangeListener(SDK.DOMModel.DOMNode, this.onNodeChanged, this);
36
34
  UI.Context.Context.instance().removeFlavorChangeListener(
37
35
  StylesSidebarPane, this.evaluateTrackingComputedStyleUpdatesForNode, this);
38
36
  UI.Context.Context.instance().removeFlavorChangeListener(
39
37
  ComputedStyleWidget, this.evaluateTrackingComputedStyleUpdatesForNode, this);
40
38
  }
41
39
 
42
- node(): SDK.DOMModel.DOMNode|null {
40
+ get node(): SDK.DOMModel.DOMNode|null {
43
41
  return this.#node;
44
42
  }
45
43
 
44
+ set node(node: SDK.DOMModel.DOMNode|null) {
45
+ this.#node = node;
46
+ this.updateModel(this.#node ? this.#node.domModel().cssModel() : null);
47
+ this.onCSSModelChanged(null);
48
+ this.evaluateTrackingComputedStyleUpdatesForNode();
49
+ }
50
+
46
51
  cssModel(): SDK.CSSModel.CSSModel|null {
47
52
  return this.#cssModel?.isEnabled() ? this.#cssModel : null;
48
53
  }
@@ -82,13 +87,6 @@ export class ComputedStyleModel extends Common.ObjectWrapper.ObjectWrapper<Event
82
87
  }
83
88
  }, 100);
84
89
 
85
- private onNodeChanged(event: Common.EventTarget.EventTargetEvent<SDK.DOMModel.DOMNode|null>): void {
86
- this.#node = event.data;
87
- this.updateModel(this.#node ? this.#node.domModel().cssModel() : null);
88
- this.onCSSModelChanged(null);
89
- this.evaluateTrackingComputedStyleUpdatesForNode();
90
- }
91
-
92
90
  private updateModel(cssModel: SDK.CSSModel.CSSModel|null): void {
93
91
  if (this.#cssModel === cssModel) {
94
92
  return;
@@ -155,7 +153,7 @@ export class ComputedStyleModel extends Common.ObjectWrapper.ObjectWrapper<Event
155
153
  }
156
154
 
157
155
  private elementNode(): SDK.DOMModel.DOMNode|null {
158
- const node = this.node();
156
+ const node = this.node;
159
157
  if (!node) {
160
158
  return null;
161
159
  }
@@ -319,7 +319,7 @@ export class ComputedStyleWidget extends UI.Widget.VBox {
319
319
  return link;
320
320
  }
321
321
  return null;
322
- }, () => this.computedStyleModel.node());
322
+ }, () => this.computedStyleModel.node);
323
323
 
324
324
  const fontsWidget = new PlatformFontsWidget(this.computedStyleModel);
325
325
  fontsWidget.show(this.contentElement);
@@ -356,7 +356,7 @@ export class ComputedStyleWidget extends UI.Widget.VBox {
356
356
  }
357
357
 
358
358
  private async fetchMatchedCascade(): Promise<SDK.CSSMatchedStyles.CSSMatchedStyles|null> {
359
- const node = this.computedStyleModel.node();
359
+ const node = this.computedStyleModel.node;
360
360
  if (!node || !this.computedStyleModel.cssModel()) {
361
361
  return null;
362
362
  }
@@ -370,7 +370,7 @@ export class ComputedStyleWidget extends UI.Widget.VBox {
370
370
 
371
371
  function validateStyles(this: ComputedStyleWidget, matchedStyles: SDK.CSSMatchedStyles.CSSMatchedStyles|null):
372
372
  SDK.CSSMatchedStyles.CSSMatchedStyles|null {
373
- return matchedStyles && matchedStyles.node() === this.computedStyleModel.node() ? matchedStyles : null;
373
+ return matchedStyles && matchedStyles.node() === this.computedStyleModel.node ? matchedStyles : null;
374
374
  }
375
375
  }
376
376
 
@@ -263,7 +263,7 @@ export class ElementsPanel extends UI.Panel.Panel implements UI.SearchableView.S
263
263
  this.mainContainer = document.createElement('div');
264
264
  this.domTreeContainer = document.createElement('div');
265
265
  const crumbsContainer = document.createElement('div');
266
- if (Root.Runtime.experiments.isEnabled('full-accessibility-tree')) {
266
+ if (Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.FULL_ACCESSIBILITY_TREE)) {
267
267
  this.initializeFullAccessibilityTreeView();
268
268
  }
269
269
  this.mainContainer.appendChild(this.domTreeContainer);
@@ -299,7 +299,10 @@ export class ElementsPanel extends UI.Panel.Panel implements UI.SearchableView.S
299
299
 
300
300
  crumbsContainer.appendChild(this.breadcrumbs);
301
301
 
302
- const computedStyleModel = new ComputedStyleModel();
302
+ const computedStyleModel = new ComputedStyleModel(UI.Context.Context.instance().flavor(SDK.DOMModel.DOMNode));
303
+ UI.Context.Context.instance().addFlavorChangeListener(SDK.DOMModel.DOMNode, event => {
304
+ computedStyleModel.node = event.data;
305
+ });
303
306
  this.stylesWidget = new StylesSidebarPane(computedStyleModel);
304
307
  this.computedStyleWidget = new ComputedStyleWidget(computedStyleModel);
305
308
  this.metricsWidget = new MetricsSidebarPane(computedStyleModel);
@@ -21,7 +21,7 @@ export class ElementsSidebarPane extends UI.Widget.VBox {
21
21
  }
22
22
 
23
23
  node(): SDK.DOMModel.DOMNode|null {
24
- return this.computedStyleModelInternal.node();
24
+ return this.computedStyleModelInternal.node;
25
25
  }
26
26
 
27
27
  cssModel(): SDK.CSSModel.CSSModel|null {
@@ -93,7 +93,7 @@ export class PlatformFontsWidget extends UI.Widget.VBox {
93
93
 
94
94
  override async performUpdate(): Promise<void> {
95
95
  const cssModel = this.sharedModel.cssModel();
96
- const node = this.sharedModel.node();
96
+ const node = this.sharedModel.node;
97
97
  if (!node || !cssModel) {
98
98
  this.#view({platformFonts: null}, {}, this.contentElement);
99
99
  return;
@@ -257,7 +257,7 @@ export class StylePropertiesSection {
257
257
  UI.ARIAUtils.setHidden(this.newStyleRuleToolbar, true);
258
258
  }
259
259
 
260
- if (Root.Runtime.experiments.isEnabled('font-editor') && this.editable) {
260
+ if (Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.FONT_EDITOR) && this.editable) {
261
261
  this.fontEditorToolbar = this.#styleRuleElement.createChild('devtools-toolbar', 'sidebar-pane-section-toolbar');
262
262
  this.fontEditorSectionManager = new FontEditorSectionManager(this.parentPane.swatchPopoverHelper(), this);
263
263
  this.fontEditorButton =
@@ -2348,7 +2348,7 @@ export class StylePropertyTreeElement extends UI.TreeOutline.TreeElement {
2348
2348
  this.name, this.style, this.#parentPane, this.#matchedStyles, this, this.getComputedStyles() ?? new Map()) :
2349
2349
  [];
2350
2350
 
2351
- if (Root.Runtime.experiments.isEnabled('font-editor') && this.property.parsedOk) {
2351
+ if (Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.FONT_EDITOR) && this.property.parsedOk) {
2352
2352
  renderers.push(new FontRenderer(this));
2353
2353
  }
2354
2354
  this.listItemElement.removeChildren();
@@ -123,73 +123,47 @@ export class ActionDelegate implements UI.ActionRegistration.ActionDelegate {
123
123
  return;
124
124
  }
125
125
 
126
+ // Resolve to a remote object to ensure the node is alive in the context.
126
127
  const object = await node.resolveToObject();
127
128
  if (!object) {
128
129
  return;
129
130
  }
130
- const result = await object.callFunction(function(this: Element) {
131
- function getFrameOffset(frame: Element|null): {x: number, y: number} {
132
- if (!frame) {
133
- return {x: 0, y: 0};
134
- }
135
-
136
- // The offset of the frame's content relative to the frame element
137
- // contains the border width and the padding.
138
- // The border width.
139
- const borderTop = frame.clientTop;
140
- const borderLeft = frame.clientLeft;
141
-
142
- // The padding can be retrieved via computed styles.
143
- const styles = window.getComputedStyle(frame);
144
- const paddingTop = parseFloat(styles.paddingTop);
145
- const paddingLeft = parseFloat(styles.paddingLeft);
146
-
147
- // The position of the frame in it's parent.
148
- const rect = frame.getBoundingClientRect();
149
-
150
- // The offset of the parent frame's content relative to the
151
- // document. If there is no parent frame, the offset is 0.
152
- // In case of OOPiF, there is no access to the parent frame's
153
- // offset.
154
- const parentFrameOffset = getFrameOffset(frame.ownerDocument.defaultView?.frameElement ?? null);
155
-
156
- // The scroll position of the frame.
157
- const scrollX = frame.ownerDocument.defaultView?.scrollX ?? 0;
158
- const scrollY = frame.ownerDocument.defaultView?.scrollY ?? 0;
159
-
160
- return {
161
- x: parentFrameOffset.x + rect.left + borderLeft + paddingLeft + scrollX,
162
- y: parentFrameOffset.y + rect.top + borderTop + paddingTop + scrollY,
163
- };
164
- }
165
-
166
- // The bounding client rect of the node relative to the viewport.
167
- const rect = this.getBoundingClientRect();
168
- const frameOffset = getFrameOffset(this.ownerDocument.defaultView?.frameElement ?? null);
169
-
170
- // The scroll position of the frame.
171
- const scrollX = this.ownerDocument.defaultView?.scrollX ?? 0;
172
- const scrollY = this.ownerDocument.defaultView?.scrollY ?? 0;
173
-
174
- // The offset of the node's content relative to the top-level
175
- // document is the sum of the element offset relative to the
176
- // document's viewport, the document's scroll position, and the
177
- // parent's offset relative to the top-level document.
178
- return JSON.stringify({
179
- x: rect.left + frameOffset.x + scrollX,
180
- y: rect.top + frameOffset.y + scrollY,
181
- width: rect.width,
182
- height: rect.height,
183
- scale: 1,
184
- });
185
- });
186
- if (!result.object) {
187
- throw new Error('Clipping error: could not get object data.');
131
+
132
+ // Get the Box Model via CDP.
133
+ // This returns the quads relative to the target's viewport.
134
+ // We use the 'border' quad to include the border and padding in the screenshot,
135
+ // matching the 'width' and 'height' properties which are also Border Box dimensions.
136
+ const nodeBoxModel = await node.boxModel();
137
+ if (!nodeBoxModel) {
138
+ throw new Error(`Unable to get box model of the node: ${new Error().stack}`);
139
+ }
140
+ const nodeBorderQuad = nodeBoxModel.border;
141
+
142
+ // Get Layout Metrics to account for the Visual Viewport scroll and zoom.
143
+ const metrics = await node.domModel().target().pageAgent().invoke_getLayoutMetrics();
144
+ if (metrics.getError()) {
145
+ throw new Error(`Unable to get metrics: ${new Error().stack}`);
188
146
  }
189
- const clip = (JSON.parse((result.object.value as string)));
190
- const response = await node.domModel().target().pageAgent().invoke_getLayoutMetrics();
191
- const error = response.getError();
192
- const zoom = !error && response.visualViewport.zoom || 1;
147
+
148
+ const scrollX = metrics.cssVisualViewport.pageX;
149
+ const scrollY = metrics.cssVisualViewport.pageY;
150
+
151
+ // Calculate the global offset for OOPiFs (Out-of-Process iframes).
152
+ // This accounts for the position of the target's frame within the main page.
153
+ const {x: oopifOffsetX, y: oopifOffsetY} = await getOopifOffset(node.domModel().target());
154
+
155
+ // Assemble the final Clip.
156
+ // The absolute coordinates are: Global (OOPiF) + Viewport Scroll + Local Node Position (Border Box).
157
+ const clip = {
158
+ x: oopifOffsetX + scrollX + nodeBorderQuad[0],
159
+ y: oopifOffsetY + scrollY + nodeBorderQuad[1],
160
+ width: nodeBoxModel.width,
161
+ height: nodeBoxModel.height,
162
+ scale: 1,
163
+ };
164
+
165
+ // Apply Zoom factor.
166
+ const zoom = metrics.cssVisualViewport.zoom ?? 1;
193
167
  clip.x *= zoom;
194
168
  clip.y *= zoom;
195
169
  clip.width *= zoom;
@@ -210,3 +184,68 @@ export class ActionDelegate implements UI.ActionRegistration.ActionDelegate {
210
184
  return false;
211
185
  }
212
186
  }
187
+
188
+ /**
189
+ * Calculate the offset of the "Local Root" frame relative to the "Global Root" (the main frame).
190
+ * This involves traversing the CDP Targets for OOPiFs.
191
+ */
192
+ async function getOopifOffset(target: SDK.Target.Target|null): Promise<{x: number, y: number}> {
193
+ if (!target) {
194
+ return {x: 0, y: 0};
195
+ }
196
+
197
+ // Get the parent target. If there's no parent (we are at root) or it's not a frame, we are done.
198
+ const parentTarget = target.parentTarget();
199
+ if (!parentTarget || parentTarget.type() !== SDK.Target.Type.FRAME) {
200
+ return {x: 0, y: 0};
201
+ }
202
+
203
+ // Identify the current frame's ID to find its owner in the parent.
204
+ const frameId = target.model(SDK.ResourceTreeModel.ResourceTreeModel)?.mainFrame?.id;
205
+ if (!frameId) {
206
+ return {x: 0, y: 0};
207
+ }
208
+
209
+ // Get the DOMModel of the parent to query the frame owner element.
210
+ const parentDOMModel = parentTarget.model(SDK.DOMModel.DOMModel);
211
+ if (!parentDOMModel) {
212
+ return {x: 0, y: 0};
213
+ }
214
+
215
+ // Retrieve the frame owner node (e.g. the <iframe> element) in the parent's document.
216
+ const frameOwnerDeferred = await parentDOMModel.getOwnerNodeForFrame(frameId);
217
+ const frameOwner = await frameOwnerDeferred?.resolvePromise();
218
+ if (!frameOwner) {
219
+ return {x: 0, y: 0};
220
+ }
221
+
222
+ // Get the content box of the iframe element.
223
+ // This is relative to the parent target's viewport.
224
+ const boxModel = await frameOwner.boxModel();
225
+ if (!boxModel) {
226
+ return {x: 0, y: 0};
227
+ }
228
+
229
+ // content is a Quad [x1, y1, x2, y2, x3, y3, x4, y4]
230
+ const contentQuad = boxModel.content;
231
+ const iframeContentX = contentQuad[0];
232
+ const iframeContentY = contentQuad[1];
233
+
234
+ // Get the scroll position of the parent target to convert viewport-relative coordinates
235
+ // to document-relative coordinates.
236
+ const parentMetrics = await parentTarget.pageAgent().invoke_getLayoutMetrics();
237
+ if (parentMetrics.getError()) {
238
+ return {x: 0, y: 0};
239
+ }
240
+
241
+ const scrollX = parentMetrics.cssVisualViewport.pageX;
242
+ const scrollY = parentMetrics.cssVisualViewport.pageY;
243
+
244
+ // Recursively add the offset of the parent target itself (if it is also an OOPiF).
245
+ const parentOffset = await getOopifOffset(parentTarget);
246
+
247
+ return {
248
+ x: iframeContentX + scrollX + parentOffset.x,
249
+ y: iframeContentY + scrollY + parentOffset.y,
250
+ };
251
+ }
@@ -3,6 +3,7 @@
3
3
  // found in the LICENSE file.
4
4
 
5
5
  import '../../../ui/components/spinners/spinners.js';
6
+ import '../../../ui/kit/kit.js';
6
7
 
7
8
  import * as Common from '../../../core/common/common.js';
8
9
  import * as Host from '../../../core/host/host.js';
@@ -299,9 +300,9 @@ function renderSearchButton(onSearch: ViewInput['callbacks']['onSearch']): Lit.T
299
300
 
300
301
  function renderLearnMoreAboutInsights(): Lit.TemplateResult {
301
302
  // clang-format off
302
- return html`<x-link href=${LEARN_MORE_URL} class="link" jslog=${VisualLogging.link('learn-more').track({click: true})}>
303
+ return html`<devtools-link href=${LEARN_MORE_URL} class="link" .jslogContext=${'learn-more'}>
303
304
  ${i18nString(UIStrings.learnMore)}
304
- </x-link>`;
305
+ </devtools-link>`;
305
306
  // clang-format on
306
307
  }
307
308
 
@@ -317,15 +318,15 @@ function maybeRenderSources(
317
318
  <ol class="sources-list">
318
319
  ${directCitationUrls.map((url, index) => html`
319
320
  <li>
320
- <x-link
321
+ <devtools-link
321
322
  href=${url}
322
323
  class=${Directives.classMap({link: true, highlighted: index === highlightedCitationIndex})}
323
- jslog=${VisualLogging.link('references.console-insights').track({click: true})}
324
+ .jslogContext=${'references.console-insights'}
324
325
  ${Directives.ref(e => { output.citationLinks[index] = e as HTMLElement; })}
325
326
  @animationend=${onCitationAnimationEnd}
326
327
  >
327
328
  ${url}
328
- </x-link>
329
+ </devtools-link>
329
330
  </li>
330
331
  `)}
331
332
  </ol>
@@ -343,13 +344,13 @@ function maybeRenderRelatedContent(relatedUrls: string[], directCitationUrls: st
343
344
  <ul class="references-list">
344
345
  ${relatedUrls.map(relatedUrl => html`
345
346
  <li>
346
- <x-link
347
+ <devtools-link
347
348
  href=${relatedUrl}
348
349
  class="link"
349
- jslog=${VisualLogging.link('references.console-insights').track({click: true})}
350
+ .jslogContext=${'references.console-insights'}
350
351
  >
351
352
  ${relatedUrl}
352
- </x-link>
353
+ </devtools-link>
353
354
  </li>
354
355
  `)}
355
356
  </ul>
@@ -379,10 +380,10 @@ function renderInsightSourcesList(
379
380
  <div class="insight-sources">
380
381
  <ul>
381
382
  ${Directives.repeat(sources, item => item.value, item => {
382
- return html`<li><x-link class="link" title="${localizeType(item.type)} ${i18nString(UIStrings.opensInNewTab)}" href="data:text/plain;charset=utf-8,${encodeURIComponent(item.value)}" jslog=${VisualLogging.link('source-' + item.type).track({click: true})}>
383
+ return html`<li><devtools-link class="link" title="${localizeType(item.type)} ${i18nString(UIStrings.opensInNewTab)}" href="data:text/plain;charset=utf-8,${encodeURIComponent(item.value)}" .jslogContext=${'source-' + item.type}>
383
384
  <devtools-icon name="open-externally"></devtools-icon>
384
385
  ${localizeType(item.type)}
385
- </x-link></li>`;
386
+ </devtools-link></li>`;
386
387
  })}
387
388
  ${isPageReloadRecommended ? html`<li class="source-disclaimer">
388
389
  <devtools-icon name="warning"></devtools-icon>
@@ -450,28 +451,28 @@ function renderConsentReminder(noLogging: boolean): Lit.TemplateResult {
450
451
  <devtools-icon name="policy" class="medium">
451
452
  </devtools-icon>
452
453
  </div>
453
- <div>Use of this feature is subject to the <x-link
454
+ <div>Use of this feature is subject to the <devtools-link
454
455
  href=${TERMS_OF_SERVICE_URL}
455
456
  class="link"
456
- jslog=${VisualLogging.link('terms-of-service.console-insights').track({click: true})}>
457
+ .jslogContext=${'terms-of-service.console-insights'}>
457
458
  Google Terms of Service
458
- </x-link> and <x-link
459
+ </devtools-link> and <devtools-link
459
460
  href=${PRIVACY_POLICY_URL}
460
461
  class="link"
461
- jslog=${VisualLogging.link('privacy-policy.console-insights').track({click: true})}>
462
+ .jslogContext=${'privacy-policy.console-insights'}>
462
463
  Google Privacy Policy
463
- </x-link>
464
+ </devtools-link>
464
465
  </div>
465
466
  <div>
466
467
  <devtools-icon name="warning" class="medium">
467
468
  </devtools-icon>
468
469
  </div>
469
470
  <div>
470
- <x-link
471
+ <devtools-link
471
472
  href=${CODE_SNIPPET_WARNING_URL}
472
473
  class="link"
473
- jslog=${VisualLogging.link('code-snippets-explainer.console-insights').track({click: true})}
474
- >Use generated code snippets with caution</x-link>
474
+ .jslogContext=${'code-snippets-explainer.console-insights'}
475
+ >Use generated code snippets with caution</devtools-link>
475
476
  </div>
476
477
  </div>`;
477
478
  // clang-format on
@@ -513,10 +514,10 @@ function renderDisclaimer(noLogging: boolean, onDisclaimerSettingsLink: () => vo
513
514
  } <button class="link" role="link" @click=${onDisclaimerSettingsLink}
514
515
  jslog=${VisualLogging.action('open-ai-settings').track({click: true})}>
515
516
  Open settings
516
- </button> or <x-link href=${LEARN_MORE_URL}
517
- class="link" jslog=${VisualLogging.link('learn-more').track({click: true})}>
517
+ </button> or <devtools-link href=${LEARN_MORE_URL}
518
+ class="link" .jslogContext=${'learn-more'}>
518
519
  learn more
519
- </x-link>
520
+ </devtools-link>
520
521
  </span>`;
521
522
  // clang-format on
522
523
  }
@@ -549,7 +550,8 @@ function renderSignInFooter(onGoToSignIn: () => void): Lit.LitTemplate {
549
550
  // clang-format on
550
551
  }
551
552
 
552
- function renderConsentReminderFooter(onReminderSettingsLink: () => void, onConsentReminderConfirmed: () => void): Lit.LitTemplate {
553
+ function renderConsentReminderFooter(
554
+ onReminderSettingsLink: () => void, onConsentReminderConfirmed: () => void): Lit.LitTemplate {
553
555
  // clang-format off
554
556
  return html`
555
557
  <div class="filler"></div>
@@ -575,7 +577,9 @@ function renderConsentReminderFooter(onReminderSettingsLink: () => void, onConse
575
577
  // clang-format on
576
578
  }
577
579
 
578
- function renderInsightFooter(noLogging: ViewInput['noLogging'], selectedRating: ViewInput['selectedRating'], callbacks: ViewInput['callbacks']): Lit.LitTemplate {
580
+ function renderInsightFooter(
581
+ noLogging: ViewInput['noLogging'], selectedRating: ViewInput['selectedRating'],
582
+ callbacks: ViewInput['callbacks']): Lit.LitTemplate {
579
583
  // clang-format off
580
584
  return html`
581
585
  <div class="disclaimer">
@@ -360,7 +360,7 @@
360
360
  display: table-cell;
361
361
  }
362
362
 
363
- .sources-list x-link.highlighted {
363
+ .sources-list devtools-link.highlighted {
364
364
  animation: highlight-fadeout 2s;
365
365
  }
366
366