chrome-devtools-frontend 1.0.1667564 → 1.0.1669021

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 (211) hide show
  1. package/.agents/skills/devtools-ci/SKILL.md +65 -0
  2. package/.agents/skills/devtools-setting-migration/SKILL.md +290 -0
  3. package/.agents/skills/gerrit-cli/SKILL.md +59 -0
  4. package/.agents/skills/gerrit-cli/references/commands.md +93 -0
  5. package/.agents/skills/gerrit-cli/references/workflows.md +56 -0
  6. package/.agents/skills/gerrit-cli/scripts/gerrit_client_wrapper.py +47 -0
  7. package/.agents/skills/gerrit-cli/scripts/import_upstream.py +230 -0
  8. package/.agents/skills/ui-eng-vision-widget-promoter/SKILL.md +11 -4
  9. package/.agents/skills/ui-widgets/SKILL.md +25 -1
  10. package/docs/contributing/infrastructure.md +7 -1
  11. package/docs/ui_engineering.md +44 -1
  12. package/front_end/core/common/Settings.ts +11 -4
  13. package/front_end/core/host/AidaClient.ts +66 -47
  14. package/front_end/core/host/GcaClient.ts +4 -6
  15. package/front_end/core/host/ResourceLoader.ts +4 -2
  16. package/front_end/core/root/Runtime.ts +5 -5
  17. package/front_end/core/sdk/CSSModel.test.api.ts +102 -0
  18. package/front_end/core/sdk/CSSModel.ts +3 -2
  19. package/front_end/core/sdk/ConsoleModel.ts +3 -2
  20. package/front_end/core/sdk/DebuggerModel.ts +16 -7
  21. package/front_end/core/sdk/SDKSettings.ts +38 -0
  22. package/front_end/core/sdk/sdk-meta.ts +0 -120
  23. package/front_end/core/sdk/sdk.ts +2 -0
  24. package/front_end/entrypoints/main/MainImpl.ts +10 -3
  25. package/front_end/foundation/Universe.ts +7 -0
  26. package/front_end/generated/InspectorBackendCommands.ts +2 -2
  27. package/front_end/generated/protocol.ts +11 -3
  28. package/front_end/models/ai_assistance/AiAgent2.ts +1 -1
  29. package/front_end/models/ai_assistance/AiUtils.ts +67 -4
  30. package/front_end/models/ai_assistance/BuiltInAi.ts +11 -6
  31. package/front_end/models/ai_assistance/ChangeManager.ts +17 -1
  32. package/front_end/models/ai_assistance/StorageItem.ts +26 -5
  33. package/front_end/models/ai_code_completion/AiCodeCompletion.ts +2 -19
  34. package/front_end/models/ai_code_generation/AiCodeGeneration.ts +2 -3
  35. package/front_end/models/javascript_metadata/NativeFunctions.js +22 -9
  36. package/front_end/models/source_map_scopes/NamesResolver.ts +23 -17
  37. package/front_end/models/trace/handlers/RendererHandler.ts +20 -4
  38. package/front_end/models/trace/types/File.ts +1 -0
  39. package/front_end/models/workspace/UISourceCode.ts +0 -10
  40. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +61 -182
  41. package/front_end/panels/ai_assistance/components/ChatInput.ts +25 -8
  42. package/front_end/panels/ai_assistance/components/ChatMessage.ts +13 -69
  43. package/front_end/panels/ai_assistance/components/ChatView.ts +14 -8
  44. package/front_end/panels/ai_assistance/components/chatMessage.css +2 -20
  45. package/front_end/panels/application/ApplicationPanelSidebar.ts +2 -2
  46. package/front_end/panels/application/ApplicationPanelTreeElement.ts +41 -3
  47. package/front_end/panels/application/ServiceWorkerUpdateCycleView.ts +131 -134
  48. package/front_end/panels/application/ServiceWorkersView.ts +8 -8
  49. package/front_end/panels/application/application.ts +2 -0
  50. package/front_end/panels/application/components/AdsView.ts +10 -6
  51. package/front_end/panels/application/preloading/PreloadingView.ts +53 -45
  52. package/front_end/panels/application/preloading/components/PreloadingDetailsReportView.ts +111 -119
  53. package/front_end/panels/application/preloading/components/preloadingDetailsReportView.css +22 -24
  54. package/front_end/panels/application/serviceWorkerUpdateCycleView.css +105 -102
  55. package/front_end/panels/common/AiCodeCompletionDisclaimer.ts +107 -96
  56. package/front_end/panels/common/AiCodeCompletionSummaryToolbar.ts +17 -19
  57. package/front_end/panels/common/AiCodeCompletionTeaser.ts +6 -6
  58. package/front_end/panels/common/AiCodeGenerationTeaser.ts +28 -26
  59. package/front_end/panels/common/common.ts +1 -1
  60. package/front_end/panels/console/ConsoleInsightTeaser.ts +6 -8
  61. package/front_end/panels/console/ConsoleView.ts +6 -4
  62. package/front_end/panels/console/ConsoleViewMessage.ts +1 -1
  63. package/front_end/panels/console/console-meta.ts +28 -0
  64. package/front_end/panels/elements/StylesAiCodeCompletionProvider.ts +1 -1
  65. package/front_end/panels/elements/StylesSidebarPane.ts +3 -2
  66. package/front_end/panels/emulation/DeviceModeToolbar.ts +2 -2
  67. package/front_end/panels/emulation/DeviceModeView.ts +63 -104
  68. package/front_end/panels/emulation/DeviceModeWrapper.ts +18 -4
  69. package/front_end/panels/explain/components/ConsoleInsight.ts +4 -6
  70. package/front_end/panels/explain/explain-meta.ts +7 -49
  71. package/front_end/panels/network/NetworkDataGridNode.ts +1 -1
  72. package/front_end/panels/network/NetworkLogView.ts +1 -0
  73. package/front_end/panels/network/OriginalResourceChunkView.ts +340 -0
  74. package/front_end/panels/network/ResourceChunkView.ts +72 -46
  75. package/front_end/panels/network/ResourceDirectSocketChunkView.ts +3 -2
  76. package/front_end/panels/network/ResourceWebSocketFrameView.ts +3 -3
  77. package/front_end/panels/network/originalResourceChunkView.css +49 -0
  78. package/front_end/panels/screencast/ScreencastView.ts +401 -260
  79. package/front_end/panels/screencast/screencastView.css +113 -100
  80. package/front_end/panels/sensors/LocationsSettingsTab.ts +329 -135
  81. package/front_end/panels/sensors/locationsSettingsTab.css +78 -4
  82. package/front_end/panels/settings/AISettingsTab.ts +17 -100
  83. package/front_end/panels/snippets/ScriptSnippetFileSystem.ts +2 -2
  84. package/front_end/panels/snippets/SnippetsQuickOpen.ts +4 -4
  85. package/front_end/panels/sources/AddSourceMapURLDialog.ts +3 -3
  86. package/front_end/panels/sources/AiCodeCompletionPlugin.ts +4 -3
  87. package/front_end/panels/sources/BreakpointEditDialog.ts +14 -14
  88. package/front_end/panels/sources/BreakpointsView.ts +14 -14
  89. package/front_end/panels/sources/CallStackSidebarPane.ts +6 -6
  90. package/front_end/panels/sources/CategorizedBreakpointL10n.ts +18 -18
  91. package/front_end/panels/sources/CoveragePlugin.ts +7 -7
  92. package/front_end/panels/sources/DebuggerPausedMessage.ts +23 -23
  93. package/front_end/panels/sources/DebuggerPlugin.ts +31 -31
  94. package/front_end/panels/sources/FilteredUISourceCodeListProvider.ts +2 -2
  95. package/front_end/panels/sources/GoToLineQuickOpen.ts +10 -10
  96. package/front_end/panels/sources/NavigatorView.ts +34 -27
  97. package/front_end/panels/sources/OutlineQuickOpen.ts +3 -3
  98. package/front_end/panels/sources/PersistenceActions.ts +9 -9
  99. package/front_end/panels/sources/ProfilePlugin.ts +1 -1
  100. package/front_end/panels/sources/ScopeChainSidebarPane.ts +5 -5
  101. package/front_end/panels/sources/SnippetsPlugin.ts +2 -2
  102. package/front_end/panels/sources/SourcesNavigator.ts +20 -18
  103. package/front_end/panels/sources/SourcesPanel.ts +26 -25
  104. package/front_end/panels/sources/SourcesView.ts +5 -6
  105. package/front_end/panels/sources/TabbedEditorContainer.ts +3 -3
  106. package/front_end/panels/sources/ThreadsSidebarPane.ts +1 -1
  107. package/front_end/panels/sources/UISourceCodeFrame.ts +0 -2
  108. package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +7 -7
  109. package/front_end/panels/sources/components/HeadersView.ts +2 -2
  110. package/front_end/panels/sources/sources-meta.ts +164 -71
  111. package/front_end/panels/sources/sources.ts +0 -2
  112. package/front_end/panels/timeline/TimelineHistoryManager.ts +14 -3
  113. package/front_end/panels/timeline/TimelineLoader.ts +72 -42
  114. package/front_end/panels/timeline/TimelinePanel.ts +29 -4
  115. package/front_end/panels/timeline/timelineHistoryManager.css +4 -0
  116. package/front_end/third_party/chromium/README.chromium +1 -1
  117. package/front_end/third_party/puppeteer/README.chromium +2 -2
  118. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.d.ts +187 -1
  119. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +91 -38
  120. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Browser.d.ts +145 -2
  121. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Browser.d.ts.map +1 -1
  122. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Browser.js +0 -2
  123. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Browser.js.map +1 -1
  124. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/BrowserContext.d.ts +0 -2
  125. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/BrowserContext.d.ts.map +1 -1
  126. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/BrowserContext.js +0 -2
  127. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/BrowserContext.js.map +1 -1
  128. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Dialog.d.ts +3 -2
  129. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Dialog.d.ts.map +1 -1
  130. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Dialog.js +8 -2
  131. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Dialog.js.map +1 -1
  132. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/JSHandle.d.ts +0 -2
  133. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/JSHandle.d.ts.map +1 -1
  134. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/JSHandle.js +0 -2
  135. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/JSHandle.js.map +1 -1
  136. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Page.d.ts +1 -3
  137. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Page.d.ts.map +1 -1
  138. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Page.js +0 -2
  139. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Page.js.map +1 -1
  140. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Realm.d.ts +0 -1
  141. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Realm.d.ts.map +1 -1
  142. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Realm.js +0 -1
  143. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Realm.js.map +1 -1
  144. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/BidiOverCdp.d.ts.map +1 -1
  145. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/BidiOverCdp.js +2 -2
  146. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/BidiOverCdp.js.map +1 -1
  147. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/Browser.d.ts +5 -1
  148. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/Browser.d.ts.map +1 -1
  149. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/Browser.js +12 -0
  150. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/Browser.js.map +1 -1
  151. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/HTTPResponse.d.ts.map +1 -1
  152. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/HTTPResponse.js +4 -1
  153. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/HTTPResponse.js.map +1 -1
  154. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.d.ts +5 -1
  155. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.d.ts.map +1 -1
  156. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.js +49 -0
  157. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.js.map +1 -1
  158. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/EventEmitter.d.ts +0 -6
  159. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/EventEmitter.d.ts.map +1 -1
  160. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/EventEmitter.js +0 -6
  161. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/EventEmitter.js.map +1 -1
  162. package/front_end/third_party/puppeteer/package/lib/puppeteer/generated/injected.d.ts +1 -1
  163. package/front_end/third_party/puppeteer/package/lib/puppeteer/generated/injected.d.ts.map +1 -1
  164. package/front_end/third_party/puppeteer/package/lib/puppeteer/generated/injected.js +1 -1
  165. package/front_end/third_party/puppeteer/package/lib/puppeteer/generated/injected.js.map +1 -1
  166. package/front_end/third_party/puppeteer/package/lib/puppeteer/injected/util.d.ts.map +1 -1
  167. package/front_end/third_party/puppeteer/package/lib/puppeteer/injected/util.js +3 -0
  168. package/front_end/third_party/puppeteer/package/lib/puppeteer/injected/util.js.map +1 -1
  169. package/front_end/third_party/puppeteer/package/lib/puppeteer/node/ScreenRecorder.d.ts +0 -3
  170. package/front_end/third_party/puppeteer/package/lib/puppeteer/node/ScreenRecorder.d.ts.map +1 -1
  171. package/front_end/third_party/puppeteer/package/lib/puppeteer/node/ScreenRecorder.js +0 -3
  172. package/front_end/third_party/puppeteer/package/lib/puppeteer/node/ScreenRecorder.js.map +1 -1
  173. package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.d.ts +3 -3
  174. package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.js +3 -3
  175. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/disposable.d.ts +2 -2
  176. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/disposable.js +2 -2
  177. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/version.d.ts +1 -1
  178. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/version.js +1 -1
  179. package/front_end/third_party/puppeteer/package/lib/types.d.ts +187 -1
  180. package/front_end/third_party/puppeteer/package/package.json +5 -5
  181. package/front_end/third_party/puppeteer/package/src/api/Browser.ts +155 -2
  182. package/front_end/third_party/puppeteer/package/src/api/BrowserContext.ts +0 -2
  183. package/front_end/third_party/puppeteer/package/src/api/Dialog.ts +10 -2
  184. package/front_end/third_party/puppeteer/package/src/api/JSHandle.ts +0 -2
  185. package/front_end/third_party/puppeteer/package/src/api/Page.ts +1 -3
  186. package/front_end/third_party/puppeteer/package/src/api/Realm.ts +0 -1
  187. package/front_end/third_party/puppeteer/package/src/bidi/BidiOverCdp.ts +4 -2
  188. package/front_end/third_party/puppeteer/package/src/bidi/Browser.ts +17 -0
  189. package/front_end/third_party/puppeteer/package/src/bidi/HTTPResponse.ts +5 -4
  190. package/front_end/third_party/puppeteer/package/src/cdp/Browser.ts +66 -0
  191. package/front_end/third_party/puppeteer/package/src/common/EventEmitter.ts +0 -6
  192. package/front_end/third_party/puppeteer/package/src/generated/injected.ts +1 -1
  193. package/front_end/third_party/puppeteer/package/src/injected/util.ts +4 -1
  194. package/front_end/third_party/puppeteer/package/src/node/ScreenRecorder.ts +0 -3
  195. package/front_end/third_party/puppeteer/package/src/revisions.ts +3 -3
  196. package/front_end/third_party/puppeteer/package/src/util/disposable.ts +2 -2
  197. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  198. package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +9 -7
  199. package/front_end/ui/components/text_editor/AiCodeGenerationProvider.ts +7 -8
  200. package/front_end/ui/legacy/Dialog.ts +103 -4
  201. package/front_end/ui/legacy/InspectorView.ts +1 -5
  202. package/front_end/ui/legacy/PopoverHelper.ts +5 -5
  203. package/front_end/ui/legacy/TabbedPane.ts +7 -2
  204. package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +40 -24
  205. package/front_end/ui/legacy/components/data_grid/DataGrid.ts +1 -0
  206. package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +8 -4
  207. package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +14 -7
  208. package/front_end/ui/legacy/components/quick_open/CommandMenu.ts +11 -1
  209. package/front_end/ui/visual_logging/KnownContextValues.ts +3 -0
  210. package/package.json +1 -1
  211. package/front_end/panels/sources/AiWarningInfobarPlugin.ts +0 -78
@@ -2,20 +2,21 @@
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
- /* eslint-disable @devtools/no-imperative-dom-api */
6
-
7
5
  import * as Common from '../../core/common/common.js';
8
6
  import * as Host from '../../core/host/host.js';
9
7
  import * as i18n from '../../core/i18n/i18n.js';
10
8
  import type * as Platform from '../../core/platform/platform.js';
11
9
  import * as SDK from '../../core/sdk/sdk.js';
12
10
  import * as Protocol from '../../generated/protocol.js';
13
- import {createIcon, Icon} from '../../ui/kit/kit.js';
11
+ import * as Buttons from '../../ui/components/buttons/buttons.js';
14
12
  import * as UI from '../../ui/legacy/legacy.js';
13
+ import {Directives, html, nothing, render} from '../../ui/lit/lit.js';
15
14
 
16
15
  import {InputModel} from './InputModel.js';
17
16
  import screencastViewStyles from './screencastView.css.js';
18
17
 
18
+ const {ref, styleMap, classMap} = Directives;
19
+
19
20
  const UIStrings = {
20
21
  /**
21
22
  * @description Accessible alt text for the Screencast canvas rendering of the debug target webpage.
@@ -62,7 +63,186 @@ interface Point {
62
63
  y: number;
63
64
  }
64
65
 
65
- export class ScreencastView extends UI.Widget.VBox implements SDK.OverlayModel.Highlighter {
66
+ export interface CanvasRenderingInput {
67
+ screencastImage?: HTMLImageElement;
68
+ screenOffsetTop: number;
69
+ screenZoom: number;
70
+ imageZoom: number;
71
+ highlightModel?: Protocol.DOM.BoxModel|null;
72
+ highlightConfig?: Protocol.Overlay.HighlightConfig|null;
73
+ }
74
+
75
+ export interface ViewInput extends CanvasRenderingInput {
76
+ navigationUrl: string;
77
+ canGoBack: boolean;
78
+ canGoForward: boolean;
79
+ isTouchEmulated: boolean;
80
+ glassPaneText: string;
81
+ glassPaneHidden: boolean;
82
+ isViewportHidden: boolean;
83
+ viewportWidth?: string;
84
+ viewportHeight?: string;
85
+ progressPercent: number;
86
+ elementTitleData?: ElementTitleData;
87
+ onBackClick: () => void;
88
+ onForwardClick: () => void;
89
+ onReloadClick: () => void;
90
+ onUrlInputKeyUp: (event: KeyboardEvent) => void;
91
+ onToggleTouch: (emulateTouch: boolean) => void;
92
+ onCanvasMouseEvent: (event: MouseEvent) => void;
93
+ onCanvasWheel: (event: WheelEvent) => void;
94
+ onCanvasKeyEvent: (event: KeyboardEvent) => void;
95
+ onCanvasContextMenu: (event: Event) => void;
96
+ onCanvasBlur: () => void;
97
+ }
98
+
99
+ export interface ElementTitleData {
100
+ visible: boolean;
101
+ tagName?: string;
102
+ attribute?: string;
103
+ width?: string;
104
+ height?: string;
105
+ }
106
+
107
+ export interface ViewOutput {
108
+ focusUrlInput?: () => void;
109
+ focusCanvas?: () => void;
110
+ }
111
+
112
+ export type View = (input: ViewInput, output: ViewOutput, target: HTMLElement) => void;
113
+
114
+ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLElement): void => {
115
+ let canvasElement: HTMLCanvasElement|null = null;
116
+ // clang-format off
117
+ render(html`
118
+ <style>${screencastViewStyles}</style>
119
+ <div class="screencast-navigation">
120
+ <devtools-button class="navigation"
121
+ .data=${{
122
+ variant: Buttons.Button.Variant.TOOLBAR,
123
+ iconName: 'arrow-back',
124
+ disabled: !input.canGoBack,
125
+ title: i18nString(UIStrings.back),
126
+ accessibleLabel: i18nString(UIStrings.back),
127
+ } as Buttons.Button.ButtonData}
128
+ @click=${input.onBackClick}></devtools-button>
129
+ <devtools-button class="navigation"
130
+ .data=${{
131
+ variant: Buttons.Button.Variant.TOOLBAR,
132
+ iconName: 'arrow-forward',
133
+ disabled: !input.canGoForward,
134
+ title: i18nString(UIStrings.forward),
135
+ accessibleLabel: i18nString(UIStrings.forward),
136
+ } as Buttons.Button.ButtonData}
137
+ @click=${input.onForwardClick}></devtools-button>
138
+ <devtools-button class="navigation"
139
+ .data=${{
140
+ variant: Buttons.Button.Variant.TOOLBAR,
141
+ iconName: 'refresh',
142
+ title: i18nString(UIStrings.reload),
143
+ accessibleLabel: i18nString(UIStrings.reload),
144
+ } as Buttons.Button.ButtonData}
145
+ @click=${input.onReloadClick}></devtools-button>
146
+ <input type="text" class="harmony-input" aria-label=${i18nString(UIStrings.addressBar)}
147
+ .value=${input.navigationUrl}
148
+ @keyup=${input.onUrlInputKeyUp}
149
+ ${ref(el => {
150
+ if (el instanceof HTMLInputElement) {
151
+ output.focusUrlInput = (): void => {
152
+ el.focus();
153
+ el.select();
154
+ };
155
+ }
156
+ })}
157
+ />
158
+ <devtools-button
159
+ .data=${{
160
+ variant: Buttons.Button.Variant.TOOLBAR,
161
+ iconName: 'mouse',
162
+ toggledIconName: 'mouse',
163
+ disabled: !input.isTouchEmulated,
164
+ toggled: !input.isTouchEmulated,
165
+ toggleType: Buttons.Button.ToggleType.PRIMARY,
166
+ title: i18nString(UIStrings.mouseInput),
167
+ accessibleLabel: i18nString(UIStrings.mouseInput),
168
+ } as Buttons.Button.ButtonData}
169
+ @click=${() => input.onToggleTouch(false)}></devtools-button>
170
+ <devtools-button
171
+ .data=${{
172
+ variant: Buttons.Button.Variant.TOOLBAR,
173
+ iconName: 'touch-app',
174
+ toggledIconName: 'touch-app',
175
+ disabled: input.isTouchEmulated,
176
+ toggled: input.isTouchEmulated,
177
+ toggleType: Buttons.Button.ToggleType.PRIMARY,
178
+ title: i18nString(UIStrings.touchInput),
179
+ accessibleLabel: i18nString(UIStrings.touchInput),
180
+ } as Buttons.Button.ButtonData}
181
+ @click=${() => input.onToggleTouch(true)}></devtools-button>
182
+ <div class="progress" style=${styleMap({width: `${(input.progressPercent ?? 0) * 100}%`})}></div>
183
+ </div>
184
+ <div
185
+ class=${classMap({
186
+ 'screencast-viewport': true,
187
+ hidden: input.isViewportHidden,
188
+ })}
189
+ style=${styleMap({
190
+ width: input.viewportWidth,
191
+ height: input.viewportHeight,
192
+ })}>
193
+ <div class=${classMap({
194
+ 'screencast-canvas-container': true,
195
+ touchable: input.isTouchEmulated,
196
+ })}>
197
+ <div class=${classMap({
198
+ 'screencast-glasspane': true,
199
+ fill: true,
200
+ hidden: input.glassPaneHidden,
201
+ })}>${input.glassPaneText}</div>
202
+ <canvas aria-label=${i18nString(UIStrings.screencastViewOfDebugTarget)} tabindex="0"
203
+ @mousedown=${input.onCanvasMouseEvent}
204
+ @mouseup=${input.onCanvasMouseEvent}
205
+ @mousemove=${input.onCanvasMouseEvent}
206
+ @wheel=${input.onCanvasWheel}
207
+ @click=${input.onCanvasMouseEvent}
208
+ @contextmenu=${input.onCanvasContextMenu}
209
+ @keydown=${input.onCanvasKeyEvent}
210
+ @keyup=${input.onCanvasKeyEvent}
211
+ @keypress=${input.onCanvasKeyEvent}
212
+ @blur=${input.onCanvasBlur}
213
+ ${ref(el => {
214
+ if (el instanceof HTMLCanvasElement) {
215
+ output.focusCanvas = (): void => el.focus();
216
+ canvasElement = el;
217
+ ScreencastView.repaintScreencastCanvas(el, input);
218
+ }
219
+ })}></canvas>
220
+ <div
221
+ class=${classMap({
222
+ 'screencast-element-title': true,
223
+ monospace: true,
224
+ hidden: !input.elementTitleData?.visible,
225
+ })}
226
+ ${ref(el => {
227
+ if (el instanceof HTMLElement && canvasElement && input.elementTitleData?.visible && !el.classList.contains('hidden')) {
228
+ ScreencastView.clampTooltipPosition(canvasElement, el, input.highlightModel);
229
+ }
230
+ })}>
231
+ <span class="screencast-tag-name">${input.elementTitleData?.tagName ?? nothing}</span>
232
+ <span class="screencast-attribute">${input.elementTitleData?.attribute ?? nothing}</span>
233
+ <span class="screencast-dimension"> <span>${input.elementTitleData?.width ?? nothing}</span> × <span>${input.elementTitleData?.height ?? nothing}</span>
234
+ </span>
235
+ </div>
236
+ </div>
237
+ </div>
238
+ `, target);
239
+ // clang-format on
240
+ };
241
+
242
+ export class ScreencastView extends UI.Widget.Widget implements SDK.OverlayModel.Highlighter {
243
+ readonly #view: View;
244
+ #viewOutput: ViewOutput;
245
+
66
246
  private readonly screenCaptureModel: SDK.ScreenCaptureModel.ScreenCaptureModel;
67
247
  private domModel: SDK.DOMModel.DOMModel|null;
68
248
  private readonly overlayModel: SDK.OverlayModel.OverlayModel|null;
@@ -76,43 +256,38 @@ export class ScreencastView extends UI.Widget.VBox implements SDK.OverlayModel.H
76
256
  private screenOffsetTop: number;
77
257
  private pageScaleFactor: number;
78
258
  private imageElement!: HTMLImageElement;
79
- private viewportElement!: HTMLElement;
80
- private glassPaneElement!: HTMLElement;
81
- private canvasElement!: HTMLCanvasElement;
82
- private titleElement!: HTMLElement;
83
- private context!: CanvasRenderingContext2D;
84
259
  private imageZoom: number;
85
- private tagNameElement!: HTMLElement;
86
- private attributeElement!: HTMLElement;
87
- private nodeWidthElement!: HTMLElement;
88
- private nodeHeightElement!: HTMLElement;
89
260
  private model!: Protocol.DOM.BoxModel|null;
90
261
  private highlightConfig!: Protocol.Overlay.HighlightConfig|null;
91
- private navigationUrl!: HTMLInputElement;
92
- private navigationBack!: HTMLButtonElement;
93
- private navigationForward!: HTMLButtonElement;
94
- private canvasContainerElement?: HTMLElement;
95
- private checkerboardPattern?: CanvasPattern|null;
96
262
  private targetInactive?: boolean;
97
263
  private deferredCasting?: number;
98
264
  private highlightNode?: SDK.DOMModel.DOMNode|null;
99
265
  private config?: Protocol.Overlay.HighlightConfig|null;
100
266
  private node?: SDK.DOMModel.DOMNode|null;
101
267
  private inspectModeConfig?: Protocol.Overlay.HighlightConfig|null;
102
- private navigationBar?: HTMLElement;
103
- private navigationReload?: HTMLElement;
104
268
  private navigationProgressBar?: ProgressTracker;
105
- private touchInputToggle?: HTMLButtonElement;
106
- private mouseInputToggle?: HTMLButtonElement;
107
- private touchInputToggleIcon?: Icon;
108
- private mouseInputToggleIcon?: Icon;
109
269
  private historyIndex?: number;
110
270
  private historyEntries?: Protocol.Page.NavigationEntry[];
111
271
  private isCasting = false;
112
272
  private screencastOperationId?: number;
113
- constructor(screenCaptureModel: SDK.ScreenCaptureModel.ScreenCaptureModel) {
114
- super();
115
- this.registerRequiredCSS(screencastViewStyles);
273
+
274
+ #navigationUrlText = '';
275
+ #canGoBack = false;
276
+ #canGoForward = false;
277
+ #isTouchEmulated = false;
278
+ #glassPaneText = '';
279
+ #glassPaneHidden = true;
280
+ #isViewportHidden = true;
281
+ #viewportWidth = '';
282
+ #viewportHeight = '';
283
+ #progressPercent = 0;
284
+ #elementTitleData: ElementTitleData = {visible: false};
285
+
286
+ constructor(screenCaptureModel: SDK.ScreenCaptureModel.ScreenCaptureModel, element?: HTMLElement,
287
+ view: View = DEFAULT_VIEW) {
288
+ super(element);
289
+ this.#view = view;
290
+ this.#viewOutput = {};
116
291
  this.screenCaptureModel = screenCaptureModel;
117
292
  this.domModel = screenCaptureModel.target().model(SDK.DOMModel.DOMModel);
118
293
  this.overlayModel = screenCaptureModel.target().model(SDK.OverlayModel.OverlayModel);
@@ -132,48 +307,58 @@ export class ScreencastView extends UI.Widget.VBox implements SDK.OverlayModel.H
132
307
  }
133
308
 
134
309
  initialize(): void {
135
- this.element.classList.add('screencast');
136
-
137
310
  this.createNavigationBar();
138
- this.viewportElement = this.element.createChild('div', 'screencast-viewport hidden');
139
- this.canvasContainerElement = this.viewportElement.createChild('div', 'screencast-canvas-container');
140
- this.glassPaneElement = this.canvasContainerElement.createChild('div', 'screencast-glasspane fill hidden');
141
- this.canvasElement = this.canvasContainerElement.createChild('canvas');
142
- UI.ARIAUtils.setLabel(this.canvasElement, i18nString(UIStrings.screencastViewOfDebugTarget));
143
- this.canvasElement.tabIndex = 0;
144
- this.canvasElement.addEventListener('mousedown', this.handleMouseEvent.bind(this), false);
145
- this.canvasElement.addEventListener('mouseup', this.handleMouseEvent.bind(this), false);
146
- this.canvasElement.addEventListener('mousemove', this.handleMouseEvent.bind(this), false);
147
- this.canvasElement.addEventListener('wheel', this.handleWheelEvent.bind(this), false);
148
- this.canvasElement.addEventListener('click', this.handleMouseEvent.bind(this), false);
149
- this.canvasElement.addEventListener('contextmenu', this.handleContextMenuEvent.bind(this), false);
150
- this.canvasElement.addEventListener('keydown', this.handleKeyEvent.bind(this), false);
151
- this.canvasElement.addEventListener('keyup', this.handleKeyEvent.bind(this), false);
152
- this.canvasElement.addEventListener('keypress', this.handleKeyEvent.bind(this), false);
153
- this.canvasElement.addEventListener('blur', this.handleBlurEvent.bind(this), false);
154
- this.titleElement = this.canvasContainerElement.createChild('div', 'screencast-element-title monospace hidden');
155
- this.tagNameElement = this.titleElement.createChild('span', 'screencast-tag-name');
156
- this.attributeElement = this.titleElement.createChild('span', 'screencast-attribute');
157
- UI.UIUtils.createTextChild(this.titleElement, ' ');
158
- const dimension = this.titleElement.createChild('span', 'screencast-dimension');
159
- this.nodeWidthElement = dimension.createChild('span');
160
- UI.UIUtils.createTextChild(dimension, ' × ');
161
- this.nodeHeightElement = dimension.createChild('span');
162
- this.titleElement.style.top = '0';
163
- this.titleElement.style.left = '0';
164
-
165
311
  this.imageElement = new Image();
166
- this.context = this.canvasElement.getContext('2d') as CanvasRenderingContext2D;
167
- this.checkerboardPattern = this.createCheckerboardPattern(this.context);
168
-
169
312
  this.shortcuts[UI.KeyboardShortcut.KeyboardShortcut.makeKey('l', UI.KeyboardShortcut.Modifiers.Ctrl.value)] =
170
313
  this.focusNavigationBar.bind(this);
171
-
172
314
  SDK.TargetManager.TargetManager.instance().addEventListener(
173
315
  SDK.TargetManager.Events.SUSPEND_STATE_CHANGED, this.onSuspendStateChange, this);
174
316
  this.updateGlasspane();
175
317
  }
176
318
 
319
+ override wasShown(): void {
320
+ super.wasShown();
321
+ if (this.deferredCasting) {
322
+ clearTimeout(this.deferredCasting);
323
+ delete this.deferredCasting;
324
+ }
325
+ this.deferredCasting = window.setTimeout(this.startCasting.bind(this), 100);
326
+ this.requestUpdate();
327
+ }
328
+
329
+ override performUpdate(): void {
330
+ const input: ViewInput = {
331
+ navigationUrl: this.#navigationUrlText,
332
+ canGoBack: this.#canGoBack,
333
+ canGoForward: this.#canGoForward,
334
+ isTouchEmulated: this.#isTouchEmulated,
335
+ glassPaneText: this.#glassPaneText,
336
+ glassPaneHidden: this.#glassPaneHidden,
337
+ isViewportHidden: this.#isViewportHidden,
338
+ viewportWidth: this.#viewportWidth,
339
+ viewportHeight: this.#viewportHeight,
340
+ progressPercent: this.#progressPercent,
341
+ elementTitleData: this.#elementTitleData,
342
+ screencastImage: this.imageElement,
343
+ screenOffsetTop: this.screenOffsetTop,
344
+ screenZoom: this.screenZoom,
345
+ imageZoom: this.imageZoom,
346
+ highlightModel: this.model,
347
+ highlightConfig: this.config,
348
+ onBackClick: () => this.navigateToHistoryEntry(-1),
349
+ onForwardClick: () => this.navigateToHistoryEntry(1),
350
+ onReloadClick: () => this.navigateReload(),
351
+ onUrlInputKeyUp: (e: KeyboardEvent) => this.navigationUrlKeyUp(e),
352
+ onToggleTouch: (emulateTouch: boolean) => this.#toggleTouchEmulation(emulateTouch),
353
+ onCanvasMouseEvent: (e: MouseEvent) => void this.handleMouseEvent(e),
354
+ onCanvasWheel: (e: WheelEvent) => void this.handleWheelEvent(e),
355
+ onCanvasKeyEvent: (e: KeyboardEvent) => this.handleKeyEvent(e),
356
+ onCanvasContextMenu: (e: Event) => this.handleContextMenuEvent(e),
357
+ onCanvasBlur: () => this.handleBlurEvent(),
358
+ };
359
+ this.#view(input, this.#viewOutput, this.contentElement);
360
+ }
361
+
177
362
  override willHide(): void {
178
363
  super.willHide();
179
364
  this.stopCasting();
@@ -229,6 +414,7 @@ export class ScreencastView extends UI.Widget.VBox implements SDK.OverlayModel.H
229
414
  for (const emulationModel of SDK.TargetManager.TargetManager.instance().models(SDK.EmulationModel.EmulationModel)) {
230
415
  void emulationModel.overrideEmulateTouch(false);
231
416
  }
417
+ this.#isTouchEmulated = false;
232
418
  if (this.overlayModel) {
233
419
  this.overlayModel.setHighlighter(null);
234
420
  }
@@ -243,18 +429,26 @@ export class ScreencastView extends UI.Widget.VBox implements SDK.OverlayModel.H
243
429
 
244
430
  const deviceSizeRatio = metadata.deviceHeight / metadata.deviceWidth;
245
431
  const dimensionsCSS = this.viewportDimensions();
432
+ if (dimensionsCSS.width <= 0 || dimensionsCSS.height <= 0) {
433
+ return;
434
+ }
246
435
 
247
436
  this.imageZoom = Math.min(
248
437
  dimensionsCSS.width / this.imageElement.naturalWidth,
249
438
  dimensionsCSS.height / (this.imageElement.naturalWidth * deviceSizeRatio));
250
- this.viewportElement.classList.remove('hidden');
251
439
  const bordersSize = BORDERS_SIZE;
252
440
  if (this.imageZoom < 1.01 / window.devicePixelRatio) {
253
441
  this.imageZoom = 1 / window.devicePixelRatio;
254
442
  }
255
443
  this.screenZoom = this.imageElement.naturalWidth * this.imageZoom / metadata.deviceWidth;
256
- this.viewportElement.style.width = metadata.deviceWidth * this.screenZoom + bordersSize + 'px';
257
- this.viewportElement.style.height = metadata.deviceHeight * this.screenZoom + bordersSize + 'px';
444
+ const newWidth = metadata.deviceWidth * this.screenZoom + bordersSize + 'px';
445
+ const newHeight = metadata.deviceHeight * this.screenZoom + bordersSize + 'px';
446
+ if (this.#viewportWidth !== newWidth || this.#viewportHeight !== newHeight || this.#isViewportHidden) {
447
+ this.#viewportWidth = newWidth;
448
+ this.#viewportHeight = newHeight;
449
+ this.#isViewportHidden = false;
450
+ this.requestUpdate();
451
+ }
258
452
 
259
453
  const data = this.highlightNode ? {node: this.highlightNode} : {clear: true};
260
454
  void this.updateHighlightInOverlayAndRepaint(data, this.highlightConfig);
@@ -263,7 +457,7 @@ export class ScreencastView extends UI.Widget.VBox implements SDK.OverlayModel.H
263
457
  }
264
458
 
265
459
  private isGlassPaneActive(): boolean {
266
- return !this.glassPaneElement.classList.contains('hidden');
460
+ return !this.#glassPaneHidden;
267
461
  }
268
462
 
269
463
  private screencastVisibilityChanged(visible: boolean): void {
@@ -281,14 +475,20 @@ export class ScreencastView extends UI.Widget.VBox implements SDK.OverlayModel.H
281
475
  }
282
476
 
283
477
  private updateGlasspane(): void {
478
+ let newText = '';
479
+ let newHidden = true;
284
480
  if (this.targetInactive) {
285
- this.glassPaneElement.textContent = i18nString(UIStrings.theTabIsInactive);
286
- this.glassPaneElement.classList.remove('hidden');
481
+ newText = i18nString(UIStrings.theTabIsInactive);
482
+ newHidden = false;
287
483
  } else if (SDK.TargetManager.TargetManager.instance().allTargetsSuspended()) {
288
- this.glassPaneElement.textContent = i18nString(UIStrings.profilingInProgress);
289
- this.glassPaneElement.classList.remove('hidden');
290
- } else {
291
- this.glassPaneElement.classList.add('hidden');
484
+ newText = i18nString(UIStrings.profilingInProgress);
485
+ newHidden = false;
486
+ }
487
+
488
+ if (this.#glassPaneText !== newText || this.#glassPaneHidden !== newHidden) {
489
+ this.#glassPaneText = newText;
490
+ this.#glassPaneHidden = newHidden;
491
+ this.requestUpdate();
292
492
  }
293
493
  }
294
494
 
@@ -306,7 +506,7 @@ export class ScreencastView extends UI.Widget.VBox implements SDK.OverlayModel.H
306
506
  }
307
507
  event.preventDefault();
308
508
  if (event.type === 'mousedown') {
309
- this.canvasElement.focus();
509
+ this.#viewOutput.focusCanvas?.();
310
510
  }
311
511
  return;
312
512
  }
@@ -359,11 +559,11 @@ export class ScreencastView extends UI.Widget.VBox implements SDK.OverlayModel.H
359
559
  this.inputModel.emitKeyEvent(event);
360
560
  }
361
561
  event.consume();
362
- this.canvasElement.focus();
562
+ this.#viewOutput.focusCanvas?.();
363
563
  }
364
564
 
365
565
  private handleBlurEvent(): void {
366
- if (this.inputModel && this.mouseInputToggle?.disabled) {
566
+ if (this.inputModel && !this.#isTouchEmulated) {
367
567
  const event = new MouseEvent('mouseup');
368
568
  this.inputModel.emitMouseEvent(event, this.screenOffsetTop, this.screenZoom);
369
569
  }
@@ -416,20 +616,25 @@ export class ScreencastView extends UI.Widget.VBox implements SDK.OverlayModel.H
416
616
  this.model = null;
417
617
  this.config = null;
418
618
  this.node = null;
419
- this.titleElement.classList.add('hidden');
420
- this.repaint();
619
+ if (this.#elementTitleData.visible) {
620
+ this.#elementTitleData = {visible: false};
621
+ }
622
+ this.drawElementTitle();
623
+ this.requestUpdate();
421
624
  return;
422
625
  }
423
626
 
424
627
  this.node = node;
425
628
  void node.boxModel().then(model => {
426
629
  if (!model || !this.pageScaleFactor) {
427
- this.repaint();
630
+ this.drawElementTitle();
631
+ this.requestUpdate();
428
632
  return;
429
633
  }
430
634
  this.model = this.scaleModel(model);
431
635
  this.config = config;
432
- this.repaint();
636
+ this.drawElementTitle();
637
+ this.requestUpdate();
433
638
  });
434
639
  }
435
640
 
@@ -448,31 +653,25 @@ export class ScreencastView extends UI.Widget.VBox implements SDK.OverlayModel.H
448
653
  return model;
449
654
  }
450
655
 
451
- private repaint(): void {
452
- const model = this.model;
453
- const config = this.config;
656
+ static repaintScreencastCanvas(el: HTMLCanvasElement, input: CanvasRenderingInput): void {
657
+ const context = el.getContext('2d');
658
+ if (!context) {
659
+ return;
660
+ }
454
661
 
455
- const canvasWidth = this.canvasElement.getBoundingClientRect().width;
456
- const canvasHeight = this.canvasElement.getBoundingClientRect().height;
457
- this.canvasElement.width = window.devicePixelRatio * canvasWidth;
458
- this.canvasElement.height = window.devicePixelRatio * canvasHeight;
662
+ const model = input.highlightModel;
663
+ const config = input.highlightConfig;
459
664
 
460
- this.context.save();
461
- this.context.scale(window.devicePixelRatio, window.devicePixelRatio);
665
+ const canvasWidth = el.getBoundingClientRect().width;
666
+ const canvasHeight = el.getBoundingClientRect().height;
667
+ el.width = window.devicePixelRatio * canvasWidth;
668
+ el.height = window.devicePixelRatio * canvasHeight;
462
669
 
463
- // Paint top and bottom gutter.
464
- this.context.save();
465
- if (this.checkerboardPattern) {
466
- this.context.fillStyle = this.checkerboardPattern;
467
- }
468
- this.context.fillRect(0, 0, canvasWidth, this.screenOffsetTop * this.screenZoom);
469
- this.context.fillRect(
470
- 0, this.screenOffsetTop * this.screenZoom + this.imageElement.naturalHeight * this.imageZoom, canvasWidth,
471
- canvasHeight);
472
- this.context.restore();
670
+ context.save();
671
+ context.scale(window.devicePixelRatio, window.devicePixelRatio);
473
672
 
474
673
  if (model && config) {
475
- this.context.save();
674
+ context.save();
476
675
  const quads = [];
477
676
  const isTransparent = (color: Protocol.DOM.RGBA): boolean => Boolean(color.a && color.a === 0);
478
677
  if (model.content && config.contentColor && !isTransparent(config.contentColor)) {
@@ -489,25 +688,23 @@ export class ScreencastView extends UI.Widget.VBox implements SDK.OverlayModel.H
489
688
  }
490
689
 
491
690
  for (let i = quads.length - 1; i > 0; --i) {
492
- this.drawOutlinedQuadWithClip(quads[i].quad, quads[i - 1].quad, quads[i].color);
691
+ ScreencastView.drawOutlinedQuadWithClip(context, quads[i].quad, quads[i - 1].quad, quads[i].color);
493
692
  }
494
693
  if (quads.length > 0) {
495
- this.drawOutlinedQuad(quads[0].quad, quads[0].color);
694
+ ScreencastView.drawOutlinedQuad(context, quads[0].quad, quads[0].color);
496
695
  }
497
- this.context.restore();
498
-
499
- this.drawElementTitle();
500
-
501
- this.context.globalCompositeOperation = 'destination-over';
696
+ context.globalCompositeOperation = 'destination-over';
502
697
  }
503
698
 
504
- this.context.drawImage(
505
- this.imageElement, 0, this.screenOffsetTop * this.screenZoom, this.imageElement.naturalWidth * this.imageZoom,
506
- this.imageElement.naturalHeight * this.imageZoom);
507
- this.context.restore();
699
+ if (input.screencastImage) {
700
+ context.drawImage(input.screencastImage, 0, input.screenOffsetTop * input.screenZoom,
701
+ input.screencastImage.naturalWidth * input.imageZoom,
702
+ input.screencastImage.naturalHeight * input.imageZoom);
703
+ }
704
+ context.restore();
508
705
  }
509
706
 
510
- private cssColor(color: Protocol.DOM.RGBA): string {
707
+ private static cssColor(color: Protocol.DOM.RGBA): string {
511
708
  if (!color) {
512
709
  return 'transparent';
513
710
  }
@@ -516,64 +713,83 @@ export class ScreencastView extends UI.Widget.VBox implements SDK.OverlayModel.H
516
713
  '';
517
714
  }
518
715
 
519
- private quadToPath(quad: Protocol.DOM.Quad): CanvasRenderingContext2D {
520
- this.context.beginPath();
521
- this.context.moveTo(quad[0], quad[1]);
522
- this.context.lineTo(quad[2], quad[3]);
523
- this.context.lineTo(quad[4], quad[5]);
524
- this.context.lineTo(quad[6], quad[7]);
525
- this.context.closePath();
526
- return this.context;
716
+ private static quadToPath(context: CanvasRenderingContext2D, quad: Protocol.DOM.Quad): CanvasRenderingContext2D {
717
+ context.beginPath();
718
+ context.moveTo(quad[0], quad[1]);
719
+ context.lineTo(quad[2], quad[3]);
720
+ context.lineTo(quad[4], quad[5]);
721
+ context.lineTo(quad[6], quad[7]);
722
+ context.closePath();
723
+ return context;
527
724
  }
528
725
 
529
- private drawOutlinedQuad(quad: Protocol.DOM.Quad, fillColor: Protocol.DOM.RGBA): void {
530
- this.context.save();
531
- this.context.lineWidth = 2;
532
- this.quadToPath(quad).clip();
533
- this.context.fillStyle = this.cssColor(fillColor);
534
- this.context.fill();
535
- this.context.restore();
726
+ private static drawOutlinedQuad(context: CanvasRenderingContext2D, quad: Protocol.DOM.Quad,
727
+ fillColor: Protocol.DOM.RGBA): void {
728
+ context.save();
729
+ context.lineWidth = 2;
730
+ ScreencastView.quadToPath(context, quad).clip();
731
+ context.fillStyle = ScreencastView.cssColor(fillColor);
732
+ context.fill();
733
+ context.restore();
536
734
  }
537
735
 
538
- private drawOutlinedQuadWithClip(quad: Protocol.DOM.Quad, clipQuad: Protocol.DOM.Quad, fillColor: Protocol.DOM.RGBA):
539
- void {
540
- this.context.fillStyle = this.cssColor(fillColor);
541
- this.context.save();
542
- this.context.lineWidth = 0;
543
- this.quadToPath(quad).fill();
544
- this.context.globalCompositeOperation = 'destination-out';
545
- this.context.fillStyle = 'red';
546
- this.quadToPath(clipQuad).fill();
547
- this.context.restore();
736
+ private static drawOutlinedQuadWithClip(context: CanvasRenderingContext2D, quad: Protocol.DOM.Quad,
737
+ clipQuad: Protocol.DOM.Quad, fillColor: Protocol.DOM.RGBA): void {
738
+ context.fillStyle = ScreencastView.cssColor(fillColor);
739
+ context.save();
740
+ context.lineWidth = 0;
741
+ ScreencastView.quadToPath(context, quad).fill();
742
+ context.globalCompositeOperation = 'destination-out';
743
+ context.fillStyle = 'red';
744
+ ScreencastView.quadToPath(context, clipQuad).fill();
745
+ context.restore();
548
746
  }
549
747
 
550
748
  private drawElementTitle(): void {
551
749
  if (!this.node) {
750
+ if (this.#elementTitleData.visible) {
751
+ this.#elementTitleData = {visible: false};
752
+ this.requestUpdate();
753
+ }
552
754
  return;
553
755
  }
554
756
 
555
- const canvasWidth = this.canvasElement.getBoundingClientRect().width;
556
- const canvasHeight = this.canvasElement.getBoundingClientRect().height;
557
-
558
757
  const lowerCaseName = this.node.localName() || this.node.nodeName().toLowerCase();
559
- this.tagNameElement.textContent = lowerCaseName;
560
-
561
- this.attributeElement.textContent = getAttributesForElementTitle(this.node);
562
- this.nodeWidthElement.textContent = String(this.model ? this.model.width : 0);
563
- this.nodeHeightElement.textContent = String(this.model ? this.model.height : 0);
758
+ const attribute = getAttributesForElementTitle(this.node);
759
+ const width = String(this.model ? this.model.width : 0);
760
+ const height = String(this.model ? this.model.height : 0);
761
+
762
+ if (!this.#elementTitleData.visible || this.#elementTitleData.tagName !== lowerCaseName ||
763
+ this.#elementTitleData.attribute !== attribute || this.#elementTitleData.width !== width ||
764
+ this.#elementTitleData.height !== height) {
765
+ this.#elementTitleData = {
766
+ visible: true,
767
+ tagName: lowerCaseName,
768
+ attribute,
769
+ width,
770
+ height,
771
+ };
772
+ this.requestUpdate();
773
+ }
774
+ }
564
775
 
565
- this.titleElement.classList.remove('hidden');
566
- const titleWidth = this.titleElement.offsetWidth + 6;
567
- const titleHeight = this.titleElement.offsetHeight + 4;
776
+ static clampTooltipPosition(canvas: HTMLCanvasElement, titleElement: HTMLElement,
777
+ model?: Protocol.DOM.BoxModel|null): void {
778
+ if (!model) {
779
+ return;
780
+ }
781
+ const canvasWidth = canvas.getBoundingClientRect().width;
782
+ const canvasHeight = canvas.getBoundingClientRect().height;
783
+ const titleWidth = titleElement.offsetWidth;
784
+ const titleHeight = titleElement.offsetHeight;
568
785
 
569
- const anchorTop = this.model ? this.model.margin[1] : 0;
570
- const anchorBottom = this.model ? this.model.margin[7] : 0;
786
+ const anchorTop = model.margin[1];
787
+ const anchorBottom = model.margin[7];
571
788
 
572
789
  const arrowHeight = 7;
573
- let renderArrowUp = false;
574
- let renderArrowDown = false;
790
+ let arrowDirection: 'up'|'down'|undefined;
575
791
 
576
- let boxX = Math.max(2, this.model ? this.model.margin[0] : 0);
792
+ let boxX = Math.max(2, model.margin[0]);
577
793
  if (boxX + titleWidth > canvasWidth) {
578
794
  boxX = canvasWidth - titleWidth - 2;
579
795
  }
@@ -581,47 +797,24 @@ export class ScreencastView extends UI.Widget.VBox implements SDK.OverlayModel.H
581
797
  let boxY;
582
798
  if (anchorTop > canvasHeight) {
583
799
  boxY = canvasHeight - titleHeight - arrowHeight;
584
- renderArrowDown = true;
800
+ arrowDirection = 'down';
585
801
  } else if (anchorBottom < 0) {
586
802
  boxY = arrowHeight;
587
- renderArrowUp = true;
803
+ arrowDirection = 'up';
588
804
  } else if (anchorBottom + titleHeight + arrowHeight < canvasHeight) {
589
805
  boxY = anchorBottom + arrowHeight - 4;
590
- renderArrowUp = true;
806
+ arrowDirection = 'up';
591
807
  } else if (anchorTop - titleHeight - arrowHeight > 0) {
592
808
  boxY = anchorTop - titleHeight - arrowHeight + 3;
593
- renderArrowDown = true;
809
+ arrowDirection = 'down';
594
810
  } else {
595
811
  boxY = arrowHeight;
596
812
  }
597
813
 
598
- this.context.save();
599
- this.context.translate(0.5, 0.5);
600
- this.context.beginPath();
601
- this.context.moveTo(boxX, boxY);
602
- if (renderArrowUp) {
603
- this.context.lineTo(boxX + 2 * arrowHeight, boxY);
604
- this.context.lineTo(boxX + 3 * arrowHeight, boxY - arrowHeight);
605
- this.context.lineTo(boxX + 4 * arrowHeight, boxY);
606
- }
607
- this.context.lineTo(boxX + titleWidth, boxY);
608
- this.context.lineTo(boxX + titleWidth, boxY + titleHeight);
609
- if (renderArrowDown) {
610
- this.context.lineTo(boxX + 4 * arrowHeight, boxY + titleHeight);
611
- this.context.lineTo(boxX + 3 * arrowHeight, boxY + titleHeight + arrowHeight);
612
- this.context.lineTo(boxX + 2 * arrowHeight, boxY + titleHeight);
613
- }
614
- this.context.lineTo(boxX, boxY + titleHeight);
615
- this.context.closePath();
616
- this.context.fillStyle = 'var(--sys-color-yellow-container)';
617
- this.context.fill();
618
- this.context.strokeStyle = 'var(--sys-color-outline)';
619
- this.context.stroke();
620
-
621
- this.context.restore();
622
-
623
- this.titleElement.style.top = (boxY + 3) + 'px';
624
- this.titleElement.style.left = (boxX + 3) + 'px';
814
+ titleElement.style.top = `${boxY}px`;
815
+ titleElement.style.left = `${boxX}px`;
816
+ titleElement.classList.toggle('arrow-up', arrowDirection === 'up');
817
+ titleElement.classList.toggle('arrow-down', arrowDirection === 'down');
625
818
  }
626
819
 
627
820
  private viewportDimensions(): {width: number, height: number} {
@@ -640,69 +833,21 @@ export class ScreencastView extends UI.Widget.VBox implements SDK.OverlayModel.H
640
833
  highlightFrame(_frameId: string): void {
641
834
  }
642
835
 
643
- private createCheckerboardPattern(context: CanvasRenderingContext2D): CanvasPattern|null {
644
- const size = 32;
645
- const pattern = new OffscreenCanvas(size * 2, size * 2);
646
- const pctx = pattern.getContext('2d', {willReadFrequently: true}) as OffscreenCanvasRenderingContext2D;
647
-
648
- pctx.fillStyle = 'var(--sys-color-neutral-outline)';
649
- pctx.fillRect(0, 0, size * 2, size * 2);
650
-
651
- pctx.fillStyle = 'var(--sys-color-surface-variant)';
652
- pctx.fillRect(0, 0, size, size);
653
- pctx.fillRect(size, size, size, size);
654
- return context.createPattern(pattern, 'repeat');
655
- }
656
-
657
836
  private createNavigationBar(): void {
658
- this.navigationBar = this.element.createChild('div', 'screencast-navigation');
659
-
660
- this.navigationBack = this.navigationBar.createChild('button', 'navigation');
661
- this.navigationBack.appendChild(createIcon('arrow-back'));
662
- this.navigationBack.disabled = true;
663
- UI.ARIAUtils.setLabel(this.navigationBack, i18nString(UIStrings.back));
664
-
665
- this.navigationForward = this.navigationBar.createChild('button', 'navigation');
666
- this.navigationForward.appendChild(createIcon('arrow-forward'));
667
- this.navigationForward.disabled = true;
668
- UI.ARIAUtils.setLabel(this.navigationForward, i18nString(UIStrings.forward));
669
-
670
- this.navigationReload = this.navigationBar.createChild('button', 'navigation');
671
- this.navigationReload.appendChild(createIcon('refresh'));
672
- UI.ARIAUtils.setLabel(this.navigationReload, i18nString(UIStrings.reload));
673
-
674
- this.navigationUrl = this.navigationBar.appendChild(UI.UIUtils.createInput());
675
- this.navigationUrl.type = 'text';
676
- UI.ARIAUtils.setLabel(this.navigationUrl, i18nString(UIStrings.addressBar));
677
-
678
- this.mouseInputToggle = this.navigationBar.createChild('button');
679
- this.mouseInputToggle.disabled = true;
680
- {
681
- this.mouseInputToggleIcon = this.mouseInputToggle.appendChild(new Icon());
682
- this.mouseInputToggleIcon.name = 'mouse';
683
- this.mouseInputToggleIcon.classList.toggle('toggled', true);
684
- }
685
- UI.ARIAUtils.setLabel(this.mouseInputToggle, i18nString(UIStrings.mouseInput));
686
-
687
- this.touchInputToggle = this.navigationBar.createChild('button');
688
- this.touchInputToggleIcon = this.touchInputToggle.appendChild(createIcon('touch-app'));
689
- UI.ARIAUtils.setLabel(this.touchInputToggle, i18nString(UIStrings.touchInput));
690
-
691
- this.navigationProgressBar = new ProgressTracker(
692
- this.resourceTreeModel, this.networkManager, this.navigationBar.createChild('div', 'progress'));
837
+ this.navigationProgressBar =
838
+ new ProgressTracker(this.resourceTreeModel, this.networkManager, (progress: number) => {
839
+ if (this.#progressPercent !== progress) {
840
+ this.#progressPercent = progress;
841
+ this.requestUpdate();
842
+ }
843
+ });
693
844
 
694
845
  if (this.resourceTreeModel) {
695
- this.navigationBack.addEventListener('click', this.navigateToHistoryEntry.bind(this, -1), false);
696
- this.navigationForward.addEventListener('click', this.navigateToHistoryEntry.bind(this, 1), false);
697
- this.navigationReload.addEventListener('click', this.navigateReload.bind(this), false);
698
- this.navigationUrl.addEventListener('keyup', this.navigationUrlKeyUp.bind(this), true);
699
- this.touchInputToggle.addEventListener('click', this.#toggleTouchEmulation.bind(this, true), false);
700
- this.mouseInputToggle.addEventListener('click', this.#toggleTouchEmulation.bind(this, false), false);
701
846
  void this.requestNavigationHistory();
702
- this.resourceTreeModel.addEventListener(
703
- SDK.ResourceTreeModel.Events.PrimaryPageChanged, this.requestNavigationHistoryEvent, this);
704
- this.resourceTreeModel.addEventListener(
705
- SDK.ResourceTreeModel.Events.CachedResourcesLoaded, this.requestNavigationHistoryEvent, this);
847
+ this.resourceTreeModel.addEventListener(SDK.ResourceTreeModel.Events.PrimaryPageChanged,
848
+ this.requestNavigationHistoryEvent, this);
849
+ this.resourceTreeModel.addEventListener(SDK.ResourceTreeModel.Events.CachedResourcesLoaded,
850
+ this.requestNavigationHistoryEvent, this);
706
851
  }
707
852
  }
708
853
 
@@ -729,7 +874,8 @@ export class ScreencastView extends UI.Widget.VBox implements SDK.OverlayModel.H
729
874
  if (event.key !== 'Enter') {
730
875
  return;
731
876
  }
732
- let url: string = this.navigationUrl.value;
877
+ const inputElement = event.target as HTMLInputElement;
878
+ let url: string = inputElement.value;
733
879
  if (!url) {
734
880
  return;
735
881
  }
@@ -739,23 +885,19 @@ export class ScreencastView extends UI.Widget.VBox implements SDK.OverlayModel.H
739
885
  if (this.resourceTreeModel) {
740
886
  void this.resourceTreeModel.navigate(url as Platform.DevToolsPath.UrlString);
741
887
  }
742
- this.canvasElement.focus();
888
+ this.#viewOutput.focusCanvas?.();
743
889
  }
744
890
 
745
891
  #toggleTouchEmulation(value: boolean): void {
746
- if (!this.canvasContainerElement || !this.isCasting || !this.mouseInputToggle || !this.touchInputToggle ||
747
- !this.mouseInputToggleIcon || !this.touchInputToggleIcon) {
892
+ if (!this.isCasting) {
748
893
  return;
749
894
  }
750
895
  const models = SDK.TargetManager.TargetManager.instance().models(SDK.EmulationModel.EmulationModel);
751
896
  for (const model of models) {
752
897
  void model.overrideEmulateTouch(value);
753
898
  }
754
- this.mouseInputToggle.disabled = !value;
755
- this.touchInputToggle.disabled = value;
756
- this.mouseInputToggleIcon.classList.toggle('toggled', this.mouseInputToggle.disabled);
757
- this.touchInputToggleIcon.classList.toggle('toggled', this.touchInputToggle.disabled);
758
- this.canvasContainerElement.classList.toggle('touchable', value);
899
+ this.#isTouchEmulated = value;
900
+ this.requestUpdate();
759
901
  }
760
902
 
761
903
  private requestNavigationHistoryEvent(): void {
@@ -771,8 +913,8 @@ export class ScreencastView extends UI.Widget.VBox implements SDK.OverlayModel.H
771
913
  this.historyIndex = history.currentIndex;
772
914
  this.historyEntries = history.entries;
773
915
 
774
- this.navigationBack.disabled = this.historyIndex === 0;
775
- this.navigationForward.disabled = this.historyIndex === (this.historyEntries.length - 1);
916
+ this.#canGoBack = this.historyIndex > 0;
917
+ this.#canGoForward = this.historyIndex < (this.historyEntries.length - 1);
776
918
 
777
919
  let url: string = this.historyEntries[this.historyIndex].url;
778
920
  const match = url.match(HTTP_REGEX);
@@ -781,12 +923,12 @@ export class ScreencastView extends UI.Widget.VBox implements SDK.OverlayModel.H
781
923
  }
782
924
  Host.InspectorFrontendHost.InspectorFrontendHostInstance.inspectedURLChanged(
783
925
  url as Platform.DevToolsPath.UrlString);
784
- this.navigationUrl.value = decodeURI(url);
926
+ this.#navigationUrlText = decodeURI(url);
927
+ this.requestUpdate();
785
928
  }
786
929
 
787
930
  private focusNavigationBar(): boolean {
788
- this.navigationUrl.focus();
789
- this.navigationUrl.select();
931
+ this.#viewOutput.focusUrlInput?.();
790
932
  return true;
791
933
  }
792
934
  }
@@ -799,18 +941,17 @@ export const SCHEME_REGEX = /^(https?|about|chrome):/;
799
941
  export class ProgressTracker {
800
942
  private readonly resourceTreeModel: SDK.ResourceTreeModel.ResourceTreeModel|null;
801
943
  private readonly networkManager: SDK.NetworkManager.NetworkManager|null;
802
- private element: HTMLElement;
803
944
  private requestIds: Map<string, SDK.NetworkRequest.NetworkRequest>|null;
804
945
  private startedRequests: number;
805
946
  private finishedRequests: number;
806
947
  private maxDisplayedProgress: number;
948
+ private readonly onProgressUpdate?: (progress: number) => void;
807
949
 
808
- constructor(
809
- resourceTreeModel: SDK.ResourceTreeModel.ResourceTreeModel|null,
810
- networkManager: SDK.NetworkManager.NetworkManager|null, element: HTMLElement) {
811
- this.element = element;
950
+ constructor(resourceTreeModel: SDK.ResourceTreeModel.ResourceTreeModel|null,
951
+ networkManager: SDK.NetworkManager.NetworkManager|null, onProgressUpdate?: (progress: number) => void) {
812
952
  this.resourceTreeModel = resourceTreeModel;
813
953
  this.networkManager = networkManager;
954
+ this.onProgressUpdate = onProgressUpdate;
814
955
  if (this.resourceTreeModel) {
815
956
  this.resourceTreeModel.addEventListener(SDK.ResourceTreeModel.Events.PrimaryPageChanged,
816
957
  this.onPrimaryPageChanged, this);
@@ -902,7 +1043,7 @@ export class ProgressTracker {
902
1043
  }
903
1044
 
904
1045
  private displayProgress(progress: number): void {
905
- this.element.style.width = (100 * progress) + '%';
1046
+ this.onProgressUpdate?.(progress);
906
1047
  }
907
1048
  }
908
1049