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.
- package/.agents/skills/devtools-ci/SKILL.md +65 -0
- package/.agents/skills/devtools-setting-migration/SKILL.md +290 -0
- package/.agents/skills/gerrit-cli/SKILL.md +59 -0
- package/.agents/skills/gerrit-cli/references/commands.md +93 -0
- package/.agents/skills/gerrit-cli/references/workflows.md +56 -0
- package/.agents/skills/gerrit-cli/scripts/gerrit_client_wrapper.py +47 -0
- package/.agents/skills/gerrit-cli/scripts/import_upstream.py +230 -0
- package/.agents/skills/ui-eng-vision-widget-promoter/SKILL.md +11 -4
- package/.agents/skills/ui-widgets/SKILL.md +25 -1
- package/docs/contributing/infrastructure.md +7 -1
- package/docs/ui_engineering.md +44 -1
- package/front_end/core/common/Settings.ts +11 -4
- package/front_end/core/host/AidaClient.ts +66 -47
- package/front_end/core/host/GcaClient.ts +4 -6
- package/front_end/core/host/ResourceLoader.ts +4 -2
- package/front_end/core/root/Runtime.ts +5 -5
- package/front_end/core/sdk/CSSModel.test.api.ts +102 -0
- package/front_end/core/sdk/CSSModel.ts +3 -2
- package/front_end/core/sdk/ConsoleModel.ts +3 -2
- package/front_end/core/sdk/DebuggerModel.ts +16 -7
- package/front_end/core/sdk/SDKSettings.ts +38 -0
- package/front_end/core/sdk/sdk-meta.ts +0 -120
- package/front_end/core/sdk/sdk.ts +2 -0
- package/front_end/entrypoints/main/MainImpl.ts +10 -3
- package/front_end/foundation/Universe.ts +7 -0
- package/front_end/generated/InspectorBackendCommands.ts +2 -2
- package/front_end/generated/protocol.ts +11 -3
- package/front_end/models/ai_assistance/AiAgent2.ts +1 -1
- package/front_end/models/ai_assistance/AiUtils.ts +67 -4
- package/front_end/models/ai_assistance/BuiltInAi.ts +11 -6
- package/front_end/models/ai_assistance/ChangeManager.ts +17 -1
- package/front_end/models/ai_assistance/StorageItem.ts +26 -5
- package/front_end/models/ai_code_completion/AiCodeCompletion.ts +2 -19
- package/front_end/models/ai_code_generation/AiCodeGeneration.ts +2 -3
- package/front_end/models/javascript_metadata/NativeFunctions.js +22 -9
- package/front_end/models/source_map_scopes/NamesResolver.ts +23 -17
- package/front_end/models/trace/handlers/RendererHandler.ts +20 -4
- package/front_end/models/trace/types/File.ts +1 -0
- package/front_end/models/workspace/UISourceCode.ts +0 -10
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +61 -182
- package/front_end/panels/ai_assistance/components/ChatInput.ts +25 -8
- package/front_end/panels/ai_assistance/components/ChatMessage.ts +13 -69
- package/front_end/panels/ai_assistance/components/ChatView.ts +14 -8
- package/front_end/panels/ai_assistance/components/chatMessage.css +2 -20
- package/front_end/panels/application/ApplicationPanelSidebar.ts +2 -2
- package/front_end/panels/application/ApplicationPanelTreeElement.ts +41 -3
- package/front_end/panels/application/ServiceWorkerUpdateCycleView.ts +131 -134
- package/front_end/panels/application/ServiceWorkersView.ts +8 -8
- package/front_end/panels/application/application.ts +2 -0
- package/front_end/panels/application/components/AdsView.ts +10 -6
- package/front_end/panels/application/preloading/PreloadingView.ts +53 -45
- package/front_end/panels/application/preloading/components/PreloadingDetailsReportView.ts +111 -119
- package/front_end/panels/application/preloading/components/preloadingDetailsReportView.css +22 -24
- package/front_end/panels/application/serviceWorkerUpdateCycleView.css +105 -102
- package/front_end/panels/common/AiCodeCompletionDisclaimer.ts +107 -96
- package/front_end/panels/common/AiCodeCompletionSummaryToolbar.ts +17 -19
- package/front_end/panels/common/AiCodeCompletionTeaser.ts +6 -6
- package/front_end/panels/common/AiCodeGenerationTeaser.ts +28 -26
- package/front_end/panels/common/common.ts +1 -1
- package/front_end/panels/console/ConsoleInsightTeaser.ts +6 -8
- package/front_end/panels/console/ConsoleView.ts +6 -4
- package/front_end/panels/console/ConsoleViewMessage.ts +1 -1
- package/front_end/panels/console/console-meta.ts +28 -0
- package/front_end/panels/elements/StylesAiCodeCompletionProvider.ts +1 -1
- package/front_end/panels/elements/StylesSidebarPane.ts +3 -2
- package/front_end/panels/emulation/DeviceModeToolbar.ts +2 -2
- package/front_end/panels/emulation/DeviceModeView.ts +63 -104
- package/front_end/panels/emulation/DeviceModeWrapper.ts +18 -4
- package/front_end/panels/explain/components/ConsoleInsight.ts +4 -6
- package/front_end/panels/explain/explain-meta.ts +7 -49
- package/front_end/panels/network/NetworkDataGridNode.ts +1 -1
- package/front_end/panels/network/NetworkLogView.ts +1 -0
- package/front_end/panels/network/OriginalResourceChunkView.ts +340 -0
- package/front_end/panels/network/ResourceChunkView.ts +72 -46
- package/front_end/panels/network/ResourceDirectSocketChunkView.ts +3 -2
- package/front_end/panels/network/ResourceWebSocketFrameView.ts +3 -3
- package/front_end/panels/network/originalResourceChunkView.css +49 -0
- package/front_end/panels/screencast/ScreencastView.ts +401 -260
- package/front_end/panels/screencast/screencastView.css +113 -100
- package/front_end/panels/sensors/LocationsSettingsTab.ts +329 -135
- package/front_end/panels/sensors/locationsSettingsTab.css +78 -4
- package/front_end/panels/settings/AISettingsTab.ts +17 -100
- package/front_end/panels/snippets/ScriptSnippetFileSystem.ts +2 -2
- package/front_end/panels/snippets/SnippetsQuickOpen.ts +4 -4
- package/front_end/panels/sources/AddSourceMapURLDialog.ts +3 -3
- package/front_end/panels/sources/AiCodeCompletionPlugin.ts +4 -3
- package/front_end/panels/sources/BreakpointEditDialog.ts +14 -14
- package/front_end/panels/sources/BreakpointsView.ts +14 -14
- package/front_end/panels/sources/CallStackSidebarPane.ts +6 -6
- package/front_end/panels/sources/CategorizedBreakpointL10n.ts +18 -18
- package/front_end/panels/sources/CoveragePlugin.ts +7 -7
- package/front_end/panels/sources/DebuggerPausedMessage.ts +23 -23
- package/front_end/panels/sources/DebuggerPlugin.ts +31 -31
- package/front_end/panels/sources/FilteredUISourceCodeListProvider.ts +2 -2
- package/front_end/panels/sources/GoToLineQuickOpen.ts +10 -10
- package/front_end/panels/sources/NavigatorView.ts +34 -27
- package/front_end/panels/sources/OutlineQuickOpen.ts +3 -3
- package/front_end/panels/sources/PersistenceActions.ts +9 -9
- package/front_end/panels/sources/ProfilePlugin.ts +1 -1
- package/front_end/panels/sources/ScopeChainSidebarPane.ts +5 -5
- package/front_end/panels/sources/SnippetsPlugin.ts +2 -2
- package/front_end/panels/sources/SourcesNavigator.ts +20 -18
- package/front_end/panels/sources/SourcesPanel.ts +26 -25
- package/front_end/panels/sources/SourcesView.ts +5 -6
- package/front_end/panels/sources/TabbedEditorContainer.ts +3 -3
- package/front_end/panels/sources/ThreadsSidebarPane.ts +1 -1
- package/front_end/panels/sources/UISourceCodeFrame.ts +0 -2
- package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +7 -7
- package/front_end/panels/sources/components/HeadersView.ts +2 -2
- package/front_end/panels/sources/sources-meta.ts +164 -71
- package/front_end/panels/sources/sources.ts +0 -2
- package/front_end/panels/timeline/TimelineHistoryManager.ts +14 -3
- package/front_end/panels/timeline/TimelineLoader.ts +72 -42
- package/front_end/panels/timeline/TimelinePanel.ts +29 -4
- package/front_end/panels/timeline/timelineHistoryManager.css +4 -0
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/third_party/puppeteer/README.chromium +2 -2
- package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.d.ts +187 -1
- package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +91 -38
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Browser.d.ts +145 -2
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Browser.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Browser.js +0 -2
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Browser.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/BrowserContext.d.ts +0 -2
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/BrowserContext.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/BrowserContext.js +0 -2
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/BrowserContext.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Dialog.d.ts +3 -2
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Dialog.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Dialog.js +8 -2
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Dialog.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/JSHandle.d.ts +0 -2
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/JSHandle.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/JSHandle.js +0 -2
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/JSHandle.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Page.d.ts +1 -3
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Page.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Page.js +0 -2
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Page.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Realm.d.ts +0 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Realm.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Realm.js +0 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Realm.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/BidiOverCdp.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/BidiOverCdp.js +2 -2
- package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/BidiOverCdp.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/Browser.d.ts +5 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/Browser.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/Browser.js +12 -0
- package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/Browser.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/HTTPResponse.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/HTTPResponse.js +4 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/HTTPResponse.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.d.ts +5 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.js +49 -0
- package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Browser.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/common/EventEmitter.d.ts +0 -6
- package/front_end/third_party/puppeteer/package/lib/puppeteer/common/EventEmitter.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/common/EventEmitter.js +0 -6
- package/front_end/third_party/puppeteer/package/lib/puppeteer/common/EventEmitter.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/generated/injected.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/generated/injected.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/generated/injected.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/generated/injected.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/injected/util.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/injected/util.js +3 -0
- package/front_end/third_party/puppeteer/package/lib/puppeteer/injected/util.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/node/ScreenRecorder.d.ts +0 -3
- package/front_end/third_party/puppeteer/package/lib/puppeteer/node/ScreenRecorder.d.ts.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/node/ScreenRecorder.js +0 -3
- package/front_end/third_party/puppeteer/package/lib/puppeteer/node/ScreenRecorder.js.map +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.d.ts +3 -3
- package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.js +3 -3
- package/front_end/third_party/puppeteer/package/lib/puppeteer/util/disposable.d.ts +2 -2
- package/front_end/third_party/puppeteer/package/lib/puppeteer/util/disposable.js +2 -2
- package/front_end/third_party/puppeteer/package/lib/puppeteer/util/version.d.ts +1 -1
- package/front_end/third_party/puppeteer/package/lib/puppeteer/util/version.js +1 -1
- package/front_end/third_party/puppeteer/package/lib/types.d.ts +187 -1
- package/front_end/third_party/puppeteer/package/package.json +5 -5
- package/front_end/third_party/puppeteer/package/src/api/Browser.ts +155 -2
- package/front_end/third_party/puppeteer/package/src/api/BrowserContext.ts +0 -2
- package/front_end/third_party/puppeteer/package/src/api/Dialog.ts +10 -2
- package/front_end/third_party/puppeteer/package/src/api/JSHandle.ts +0 -2
- package/front_end/third_party/puppeteer/package/src/api/Page.ts +1 -3
- package/front_end/third_party/puppeteer/package/src/api/Realm.ts +0 -1
- package/front_end/third_party/puppeteer/package/src/bidi/BidiOverCdp.ts +4 -2
- package/front_end/third_party/puppeteer/package/src/bidi/Browser.ts +17 -0
- package/front_end/third_party/puppeteer/package/src/bidi/HTTPResponse.ts +5 -4
- package/front_end/third_party/puppeteer/package/src/cdp/Browser.ts +66 -0
- package/front_end/third_party/puppeteer/package/src/common/EventEmitter.ts +0 -6
- package/front_end/third_party/puppeteer/package/src/generated/injected.ts +1 -1
- package/front_end/third_party/puppeteer/package/src/injected/util.ts +4 -1
- package/front_end/third_party/puppeteer/package/src/node/ScreenRecorder.ts +0 -3
- package/front_end/third_party/puppeteer/package/src/revisions.ts +3 -3
- package/front_end/third_party/puppeteer/package/src/util/disposable.ts +2 -2
- package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
- package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +9 -7
- package/front_end/ui/components/text_editor/AiCodeGenerationProvider.ts +7 -8
- package/front_end/ui/legacy/Dialog.ts +103 -4
- package/front_end/ui/legacy/InspectorView.ts +1 -5
- package/front_end/ui/legacy/PopoverHelper.ts +5 -5
- package/front_end/ui/legacy/TabbedPane.ts +7 -2
- package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +40 -24
- package/front_end/ui/legacy/components/data_grid/DataGrid.ts +1 -0
- package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +8 -4
- package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +14 -7
- package/front_end/ui/legacy/components/quick_open/CommandMenu.ts +11 -1
- package/front_end/ui/visual_logging/KnownContextValues.ts +3 -0
- package/package.json +1 -1
- package/front_end/panels/sources/AiWarningInfobarPlugin.ts +0 -78
|
@@ -4,11 +4,21 @@
|
|
|
4
4
|
|
|
5
5
|
import * as Common from '../../core/common/common.js';
|
|
6
6
|
import * as i18n from '../../core/i18n/i18n.js';
|
|
7
|
+
import * as SDK from '../../core/sdk/sdk.js';
|
|
7
8
|
import * as UI from '../../ui/legacy/legacy.js';
|
|
9
|
+
import * as SettingsUI from '../../ui/settings/settings.js';
|
|
8
10
|
|
|
9
11
|
import type * as Console from './console.js';
|
|
10
12
|
|
|
11
13
|
const UIStrings = {
|
|
14
|
+
/**
|
|
15
|
+
* @description Title of a setting under the Console category that can be invoked through the Command Menu.
|
|
16
|
+
*/
|
|
17
|
+
preserveLogUponNavigation: 'Keep log on navigation',
|
|
18
|
+
/**
|
|
19
|
+
* @description Title of a setting under the Console category that can be invoked through the Command Menu.
|
|
20
|
+
*/
|
|
21
|
+
doNotPreserveLogUponNavigation: 'Don’t keep log on navigation',
|
|
12
22
|
/**
|
|
13
23
|
* @description Title of the Console tool.
|
|
14
24
|
*/
|
|
@@ -201,6 +211,9 @@ UI.ActionRegistration.registerActionExtension({
|
|
|
201
211
|
UI.ActionRegistration.KeybindSet.DEVTOOLS_DEFAULT,
|
|
202
212
|
UI.ActionRegistration.KeybindSet.VS_CODE,
|
|
203
213
|
],
|
|
214
|
+
// The Cmd+` combination is used in macOS to activate the next
|
|
215
|
+
// open window in the front app. Therefore it was not implemented
|
|
216
|
+
// below.
|
|
204
217
|
},
|
|
205
218
|
],
|
|
206
219
|
});
|
|
@@ -446,6 +459,21 @@ Common.Settings.registerSettingExtension({
|
|
|
446
459
|
defaultValue: true,
|
|
447
460
|
});
|
|
448
461
|
|
|
462
|
+
SettingsUI.SettingUIRegistration.register(SDK.SDKSettings.preserveConsoleLogSettingDescriptor, {
|
|
463
|
+
category: Common.Settings.SettingCategory.CONSOLE,
|
|
464
|
+
title: i18nLazyString(UIStrings.preserveLogUponNavigation),
|
|
465
|
+
options: [
|
|
466
|
+
{
|
|
467
|
+
value: true,
|
|
468
|
+
title: i18nLazyString(UIStrings.preserveLogUponNavigation),
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
value: false,
|
|
472
|
+
title: i18nLazyString(UIStrings.doNotPreserveLogUponNavigation),
|
|
473
|
+
},
|
|
474
|
+
],
|
|
475
|
+
});
|
|
476
|
+
|
|
449
477
|
Common.Revealer.registerRevealer({
|
|
450
478
|
contextTypes() {
|
|
451
479
|
return [
|
|
@@ -76,7 +76,7 @@ export class StylesAiCodeCompletionProvider {
|
|
|
76
76
|
aidaClient: this.#aidaClient,
|
|
77
77
|
serverSideLoggingEnabled: !Root.Runtime.hostConfig.aidaAvailability?.disallowLogging,
|
|
78
78
|
},
|
|
79
|
-
|
|
79
|
+
|
|
80
80
|
undefined, stopSequences);
|
|
81
81
|
this.#aiCodeCompletionConfig.onFeatureEnabled();
|
|
82
82
|
}
|
|
@@ -300,7 +300,8 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
|
|
|
300
300
|
onSuggestionAccepted: this.#onAiCodeCompletionSuggestionAccepted.bind(this),
|
|
301
301
|
onRequestTriggered: this.#onAiCodeCompletionRequestTriggered.bind(this),
|
|
302
302
|
onResponseReceived: this.#onAiCodeCompletionResponseReceived.bind(this),
|
|
303
|
-
|
|
303
|
+
disclaimerTooltipId: DISCLAIMER_TOOLTIP_ID,
|
|
304
|
+
disclaimerTextVariant: 'styles',
|
|
304
305
|
};
|
|
305
306
|
this.aiCodeCompletionProvider =
|
|
306
307
|
StylesAiCodeCompletionProvider.StylesAiCodeCompletionProvider.createInstance(this.aiCodeCompletionConfig);
|
|
@@ -1618,7 +1619,7 @@ export class StylesSidebarPane extends Common.ObjectWrapper.eventMixin<EventType
|
|
|
1618
1619
|
citationsTooltipId: CITATIONS_TOOLTIP_ID,
|
|
1619
1620
|
disclaimerTooltipId: DISCLAIMER_TOOLTIP_ID,
|
|
1620
1621
|
spinnerTooltipId: SPINNER_TOOLTIP_ID,
|
|
1621
|
-
|
|
1622
|
+
disclaimerTextVariant: 'styles',
|
|
1622
1623
|
});
|
|
1623
1624
|
const containingPane = this.contentElement.enclosingNodeOrSelfWithClass('style-panes-wrapper') as HTMLElement;
|
|
1624
1625
|
this.#aiCodeCompletionSummaryToolbarContainer =
|
|
@@ -512,8 +512,8 @@ export class DeviceModeToolbar extends UI.Widget.Widget {
|
|
|
512
512
|
super.wasShown();
|
|
513
513
|
// TODO(crbug.com/407750803): Revisit once DeviceModeView is migrated.
|
|
514
514
|
this.performUpdate(); // Trigger a manual update eagerly, DeviceModeView needs to measure our height.
|
|
515
|
+
this.restore();
|
|
515
516
|
}
|
|
516
|
-
|
|
517
517
|
override performUpdate(): void {
|
|
518
518
|
if (!this.model) {
|
|
519
519
|
return;
|
|
@@ -1078,7 +1078,7 @@ export class DeviceModeToolbar extends UI.Widget.Widget {
|
|
|
1078
1078
|
return !this.model ? '' : `${(this.model.scale() * 100).toFixed(0)}`;
|
|
1079
1079
|
}
|
|
1080
1080
|
|
|
1081
|
-
restore(): void {
|
|
1081
|
+
private restore(): void {
|
|
1082
1082
|
if (!this.model) {
|
|
1083
1083
|
return;
|
|
1084
1084
|
}
|
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
// Copyright 2015 The Chromium Authors
|
|
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
|
-
/* eslint-disable @devtools/no-imperative-dom-api */
|
|
5
4
|
|
|
6
5
|
import * as Common from '../../core/common/common.js';
|
|
7
6
|
import * as Host from '../../core/host/host.js';
|
|
8
7
|
import * as i18n from '../../core/i18n/i18n.js';
|
|
9
8
|
import * as Platform from '../../core/platform/platform.js';
|
|
9
|
+
import * as TextUtils from '../../core/text_utils/text_utils.js';
|
|
10
10
|
import type * as Protocol from '../../generated/protocol.js';
|
|
11
11
|
import * as EmulationModel from '../../models/emulation/emulation.js';
|
|
12
12
|
import * as Geometry from '../../models/geometry/geometry.js';
|
|
13
|
+
import * as Workspace from '../../models/workspace/workspace.js';
|
|
13
14
|
import * as UI from '../../ui/legacy/legacy.js';
|
|
14
15
|
import {Directives, html, nothing, render} from '../../ui/lit/lit.js';
|
|
15
16
|
import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
|
|
@@ -60,16 +61,6 @@ const UIStrings = {
|
|
|
60
61
|
const str_ = i18n.i18n.registerUIStrings('panels/emulation/DeviceModeView.ts', UIStrings);
|
|
61
62
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
62
63
|
|
|
63
|
-
export interface DeviceModeViewRefs {
|
|
64
|
-
toolbar: DeviceModeToolbar;
|
|
65
|
-
bottomRightResizerElement: HTMLElement;
|
|
66
|
-
bottomLeftResizerElement: HTMLElement;
|
|
67
|
-
rightResizerElement: HTMLElement;
|
|
68
|
-
leftResizerElement: HTMLElement;
|
|
69
|
-
bottomResizerElement: HTMLElement;
|
|
70
|
-
pageArea: HTMLElement;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
64
|
export interface DeviceModeViewInput {
|
|
74
65
|
model: EmulationModel.DeviceModeModel.DeviceModeModel;
|
|
75
66
|
showMediaInspectorSetting: Common.Settings.Setting<boolean>;
|
|
@@ -78,10 +69,12 @@ export interface DeviceModeViewInput {
|
|
|
78
69
|
outlineImageLoaded: boolean;
|
|
79
70
|
screenImage: string;
|
|
80
71
|
screenImageLoaded: boolean;
|
|
72
|
+
resizable: boolean;
|
|
81
73
|
showRulers: boolean;
|
|
82
74
|
showMediaInspector: boolean;
|
|
83
75
|
scale: number;
|
|
84
76
|
cachedCssScreenRect?: EmulationModel.DeviceModeModel.Rect;
|
|
77
|
+
cachedCssVisiblePageRect?: EmulationModel.DeviceModeModel.Rect;
|
|
85
78
|
cachedOutlineRect?: EmulationModel.DeviceModeModel.Rect;
|
|
86
79
|
onApplyPresetSize: (size: number, e: Event) => void;
|
|
87
80
|
bottomRightResizer: UI.ResizerWidget.ResizerWidget;
|
|
@@ -101,7 +94,7 @@ export interface DeviceModeViewInput {
|
|
|
101
94
|
|
|
102
95
|
export type DeviceModeViewView = (
|
|
103
96
|
input: DeviceModeViewInput,
|
|
104
|
-
output:
|
|
97
|
+
output: undefined,
|
|
105
98
|
target: HTMLElement,
|
|
106
99
|
) => void;
|
|
107
100
|
|
|
@@ -130,7 +123,7 @@ function resizerRef(
|
|
|
130
123
|
|
|
131
124
|
export const DEFAULT_DEVICE_MODE_VIEW: DeviceModeViewView = (
|
|
132
125
|
input: DeviceModeViewInput,
|
|
133
|
-
|
|
126
|
+
_output: undefined,
|
|
134
127
|
target: HTMLElement,
|
|
135
128
|
): void => {
|
|
136
129
|
const sizes = [320, 375, 425, 768, 1024, 1440, 2560];
|
|
@@ -145,8 +138,7 @@ export const DEFAULT_DEVICE_MODE_VIEW: DeviceModeViewView = (
|
|
|
145
138
|
];
|
|
146
139
|
// clang-format off
|
|
147
140
|
render(html`
|
|
148
|
-
|
|
149
|
-
${UI.Widget.widgetRef(DeviceModeToolbar, t => { output.toolbar = t; })}></devtools-widget>
|
|
141
|
+
${UI.Widget.widget(DeviceModeToolbar, {model: input.model})}
|
|
150
142
|
<div class=${classMap({
|
|
151
143
|
'device-mode-content-clip': true,
|
|
152
144
|
vbox: true,
|
|
@@ -199,33 +191,44 @@ export const DEFAULT_DEVICE_MODE_VIEW: DeviceModeViewView = (
|
|
|
199
191
|
@load=${(): void => input.onScreenImageLoaded(true)}
|
|
200
192
|
@error=${(): void => input.onScreenImageLoaded(false)}>
|
|
201
193
|
<div class="device-mode-resizer device-mode-bottom-right-resizer"
|
|
194
|
+
?hidden=${!input.resizable}
|
|
202
195
|
jslog=${VisualLogging.slider('device-mode-resizer').track({drag: true})}
|
|
203
196
|
${ref(input.bottomRightResizerRef)}>
|
|
204
197
|
<div></div>
|
|
205
198
|
</div>
|
|
206
199
|
<div class="device-mode-resizer device-mode-bottom-left-resizer"
|
|
200
|
+
?hidden=${!input.resizable}
|
|
207
201
|
jslog=${VisualLogging.slider('device-mode-resizer').track({drag: true})}
|
|
208
202
|
${ref(input.bottomLeftResizerRef)}>
|
|
209
203
|
<div></div>
|
|
210
204
|
</div>
|
|
211
205
|
<div class="device-mode-resizer device-mode-right-resizer"
|
|
206
|
+
?hidden=${!input.resizable}
|
|
212
207
|
jslog=${VisualLogging.slider('device-mode-resizer').track({drag: true})}
|
|
213
208
|
${ref(input.rightResizerRef)}>
|
|
214
209
|
<div></div>
|
|
215
210
|
</div>
|
|
216
211
|
<div class="device-mode-resizer device-mode-left-resizer"
|
|
212
|
+
?hidden=${!input.resizable}
|
|
217
213
|
jslog=${VisualLogging.slider('device-mode-resizer').track({drag: true})}
|
|
218
214
|
${ref(input.leftResizerRef)}>
|
|
219
215
|
<div></div>
|
|
220
216
|
</div>
|
|
221
217
|
<div class="device-mode-resizer device-mode-bottom-resizer"
|
|
218
|
+
?hidden=${!input.resizable}
|
|
222
219
|
jslog=${VisualLogging.slider('device-mode-resizer').track({drag: true})}
|
|
223
220
|
title=${i18nString(UIStrings.doubleclickForFullHeight)}
|
|
224
221
|
${ref(input.bottomResizerRef)}
|
|
225
222
|
@dblclick=${input.onDoubleclickBottomResizer}>
|
|
226
223
|
<div></div>
|
|
227
224
|
</div>
|
|
228
|
-
<div class="device-mode-page-area"
|
|
225
|
+
<div class="device-mode-page-area"
|
|
226
|
+
style=${styleMap(input.cachedCssVisiblePageRect ? {
|
|
227
|
+
left: `${input.cachedCssVisiblePageRect.left}px`,
|
|
228
|
+
top: `${input.cachedCssVisiblePageRect.top}px`,
|
|
229
|
+
width: `${input.cachedCssVisiblePageRect.width}px`,
|
|
230
|
+
height: `${input.cachedCssVisiblePageRect.height}px`,
|
|
231
|
+
} : {})}><slot></slot></div>
|
|
229
232
|
</div>
|
|
230
233
|
${input.showRulers ? html`
|
|
231
234
|
<devtools-widget class="device-mode-ruler-top device-mode-ruler"
|
|
@@ -247,7 +250,11 @@ export const DEFAULT_DEVICE_MODE_VIEW: DeviceModeViewView = (
|
|
|
247
250
|
` : nothing}
|
|
248
251
|
</div>
|
|
249
252
|
</div>
|
|
250
|
-
`, target
|
|
253
|
+
`, target, {
|
|
254
|
+
container: {
|
|
255
|
+
classes: ['device-mode-view'],
|
|
256
|
+
},
|
|
257
|
+
});
|
|
251
258
|
// clang-format on
|
|
252
259
|
};
|
|
253
260
|
|
|
@@ -256,34 +263,16 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
256
263
|
private model: EmulationModel.DeviceModeModel.DeviceModeModel;
|
|
257
264
|
private showMediaInspectorSetting: Common.Settings.Setting<boolean>;
|
|
258
265
|
private showRulersSetting: Common.Settings.Setting<boolean>;
|
|
259
|
-
pageArea!: HTMLElement;
|
|
260
|
-
rightResizerElement!: HTMLElement;
|
|
261
|
-
leftResizerElement!: HTMLElement;
|
|
262
|
-
bottomResizerElement!: HTMLElement;
|
|
263
|
-
bottomRightResizerElement!: HTMLElement;
|
|
264
|
-
bottomLeftResizerElement!: HTMLElement;
|
|
265
266
|
private readonly bottomRightResizer = this.createResizer(2, 1);
|
|
266
267
|
private readonly bottomLeftResizer = this.createResizer(-2, 1);
|
|
267
268
|
private readonly rightResizer = this.createResizer(2, 0);
|
|
268
269
|
private readonly leftResizer = this.createResizer(-2, 0);
|
|
269
270
|
private readonly bottomResizer = this.createResizer(0, 1);
|
|
270
|
-
private readonly bottomRightResizerRef = resizerRef(this.bottomRightResizer
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
private readonly
|
|
274
|
-
|
|
275
|
-
});
|
|
276
|
-
private readonly rightResizerRef = resizerRef(this.rightResizer, el => {
|
|
277
|
-
this.rightResizerElement = el;
|
|
278
|
-
});
|
|
279
|
-
private readonly leftResizerRef = resizerRef(this.leftResizer, el => {
|
|
280
|
-
this.leftResizerElement = el;
|
|
281
|
-
});
|
|
282
|
-
private readonly bottomResizerRef = resizerRef(this.bottomResizer, el => {
|
|
283
|
-
this.bottomResizerElement = el;
|
|
284
|
-
});
|
|
285
|
-
private cachedResizable!: boolean|undefined;
|
|
286
|
-
toolbar!: DeviceModeToolbar;
|
|
271
|
+
private readonly bottomRightResizerRef = resizerRef(this.bottomRightResizer);
|
|
272
|
+
private readonly bottomLeftResizerRef = resizerRef(this.bottomLeftResizer);
|
|
273
|
+
private readonly rightResizerRef = resizerRef(this.rightResizer);
|
|
274
|
+
private readonly leftResizerRef = resizerRef(this.leftResizer);
|
|
275
|
+
private readonly bottomResizerRef = resizerRef(this.bottomResizer);
|
|
287
276
|
private slowPositionStart?: {
|
|
288
277
|
x: number,
|
|
289
278
|
y: number,
|
|
@@ -295,8 +284,6 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
295
284
|
private cachedMediaInspectorVisible?: boolean;
|
|
296
285
|
private cachedShowRulers?: boolean;
|
|
297
286
|
private cachedScale?: number;
|
|
298
|
-
private handleWidth?: number;
|
|
299
|
-
private handleHeight?: number;
|
|
300
287
|
#outlineImageLoaded = false;
|
|
301
288
|
#lastOutlineImageSrc?: string;
|
|
302
289
|
#screenImageLoaded = false;
|
|
@@ -308,7 +295,6 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
308
295
|
this.#view = view;
|
|
309
296
|
|
|
310
297
|
this.setMinimumSize(150, 150);
|
|
311
|
-
this.element.classList.add('device-mode-view');
|
|
312
298
|
this.registerRequiredCSS(deviceModeViewStyles);
|
|
313
299
|
|
|
314
300
|
this.model = EmulationModel.DeviceModeModel.DeviceModeModel.instance();
|
|
@@ -339,11 +325,13 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
339
325
|
outlineImageLoaded: this.#outlineImageLoaded,
|
|
340
326
|
screenImage: this.model.screenImage(),
|
|
341
327
|
screenImageLoaded: this.#screenImageLoaded,
|
|
328
|
+
resizable: this.model.type() === EmulationModel.DeviceModeModel.Type.Responsive,
|
|
342
329
|
showRulers: this.showRulersSetting.get() && this.model.type() !== EmulationModel.DeviceModeModel.Type.None,
|
|
343
330
|
showMediaInspector:
|
|
344
331
|
this.showMediaInspectorSetting.get() && this.model.type() !== EmulationModel.DeviceModeModel.Type.None,
|
|
345
332
|
scale: this.model.scale(),
|
|
346
333
|
cachedCssScreenRect: this.cachedCssScreenRect,
|
|
334
|
+
cachedCssVisiblePageRect: this.cachedCssVisiblePageRect,
|
|
347
335
|
cachedOutlineRect: this.cachedOutlineRect,
|
|
348
336
|
onApplyPresetSize: (width: number, e: Event): void => {
|
|
349
337
|
this.model.emulate(EmulationModel.DeviceModeModel.Type.Responsive, null, null);
|
|
@@ -364,7 +352,7 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
364
352
|
onOutlineImageLoaded: (success: boolean): void => this.onOutlineImageLoaded(success),
|
|
365
353
|
onScreenImageLoaded: (success: boolean): void => this.onScreenImageLoaded(success),
|
|
366
354
|
};
|
|
367
|
-
this.#view(input,
|
|
355
|
+
this.#view(input, undefined, this.contentElement);
|
|
368
356
|
}
|
|
369
357
|
|
|
370
358
|
private onOutlineImageLoaded(success: boolean): void {
|
|
@@ -380,6 +368,7 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
380
368
|
this.requestUpdate();
|
|
381
369
|
}
|
|
382
370
|
}
|
|
371
|
+
|
|
383
372
|
private createResizer(widthFactor: number, heightFactor: number): UI.ResizerWidget.ResizerWidget {
|
|
384
373
|
const resizer = new UI.ResizerWidget.ResizerWidget();
|
|
385
374
|
let cursor: 'nwse-resize'|'nesw-resize'|('ew-resize' | 'ns-resize') = widthFactor ? 'ew-resize' : 'ns-resize';
|
|
@@ -450,13 +439,6 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
450
439
|
}
|
|
451
440
|
|
|
452
441
|
private updateUI(): void {
|
|
453
|
-
function applyRect(element: HTMLElement, rect: EmulationModel.DeviceModeModel.Rect): void {
|
|
454
|
-
element.style.left = rect.left + 'px';
|
|
455
|
-
element.style.top = rect.top + 'px';
|
|
456
|
-
element.style.width = rect.width + 'px';
|
|
457
|
-
element.style.height = rect.height + 'px';
|
|
458
|
-
}
|
|
459
|
-
|
|
460
442
|
if (!this.isShowing()) {
|
|
461
443
|
return;
|
|
462
444
|
}
|
|
@@ -474,7 +456,6 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
474
456
|
|
|
475
457
|
const cssVisiblePageRect = this.model.visiblePageRect().scale(1 / zoomFactor);
|
|
476
458
|
if (!this.cachedCssVisiblePageRect || !cssVisiblePageRect.isEqual(this.cachedCssVisiblePageRect)) {
|
|
477
|
-
applyRect(this.pageArea, cssVisiblePageRect);
|
|
478
459
|
callDoResize = true;
|
|
479
460
|
this.cachedCssVisiblePageRect = cssVisiblePageRect;
|
|
480
461
|
}
|
|
@@ -488,16 +469,6 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
488
469
|
}
|
|
489
470
|
}
|
|
490
471
|
|
|
491
|
-
const resizable = this.model.type() === EmulationModel.DeviceModeModel.Type.Responsive;
|
|
492
|
-
if (resizable !== this.cachedResizable) {
|
|
493
|
-
this.rightResizerElement.classList.toggle('hidden', !resizable);
|
|
494
|
-
this.leftResizerElement.classList.toggle('hidden', !resizable);
|
|
495
|
-
this.bottomResizerElement.classList.toggle('hidden', !resizable);
|
|
496
|
-
this.bottomRightResizerElement.classList.toggle('hidden', !resizable);
|
|
497
|
-
this.bottomLeftResizerElement.classList.toggle('hidden', !resizable);
|
|
498
|
-
this.cachedResizable = resizable;
|
|
499
|
-
}
|
|
500
|
-
|
|
501
472
|
const mediaInspectorVisible =
|
|
502
473
|
this.showMediaInspectorSetting.get() && this.model.type() !== EmulationModel.DeviceModeModel.Type.None;
|
|
503
474
|
if (mediaInspectorVisible !== this.cachedMediaInspectorVisible) {
|
|
@@ -518,7 +489,6 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
518
489
|
}
|
|
519
490
|
|
|
520
491
|
this.requestUpdate();
|
|
521
|
-
this.toolbar.requestUpdate();
|
|
522
492
|
if (callDoResize) {
|
|
523
493
|
this.doResize();
|
|
524
494
|
}
|
|
@@ -545,29 +515,18 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
545
515
|
}
|
|
546
516
|
const zoomFactor = UI.ZoomManager.ZoomManager.instance().zoomFactor();
|
|
547
517
|
const rect = contentArea.getBoundingClientRect();
|
|
518
|
+
const handleWidth = this.contentElement.querySelector<HTMLElement>('.device-mode-right-resizer')?.offsetWidth || 20;
|
|
519
|
+
const handleHeight =
|
|
520
|
+
this.contentElement.querySelector<HTMLElement>('.device-mode-bottom-resizer')?.offsetHeight || 20;
|
|
548
521
|
const availableSize =
|
|
549
522
|
new Geometry.Size(Math.max(rect.width * zoomFactor, 1), Math.max(rect.height * zoomFactor, 1));
|
|
550
|
-
const preferredSize = new Geometry.Size(
|
|
551
|
-
|
|
552
|
-
Math.max((rect.height - (this.handleHeight || 0)) * zoomFactor, 1));
|
|
523
|
+
const preferredSize = new Geometry.Size(Math.max((rect.width - 2 * handleWidth) * zoomFactor, 1),
|
|
524
|
+
Math.max((rect.height - handleHeight) * zoomFactor, 1));
|
|
553
525
|
this.model.setAvailableSize(availableSize, preferredSize);
|
|
554
526
|
}
|
|
555
527
|
|
|
556
|
-
private measureHandles(): void {
|
|
557
|
-
const hidden = this.rightResizerElement.classList.contains('hidden');
|
|
558
|
-
this.rightResizerElement.classList.toggle('hidden', false);
|
|
559
|
-
this.bottomResizerElement.classList.toggle('hidden', false);
|
|
560
|
-
this.handleWidth = this.rightResizerElement.offsetWidth;
|
|
561
|
-
this.handleHeight = this.bottomResizerElement.offsetHeight;
|
|
562
|
-
this.rightResizerElement.classList.toggle('hidden', hidden);
|
|
563
|
-
this.bottomResizerElement.classList.toggle('hidden', hidden);
|
|
564
|
-
}
|
|
565
|
-
|
|
566
528
|
private zoomChanged(): void {
|
|
567
|
-
delete this.handleWidth;
|
|
568
|
-
delete this.handleHeight;
|
|
569
529
|
if (this.isShowing()) {
|
|
570
|
-
this.measureHandles();
|
|
571
530
|
this.contentAreaResized();
|
|
572
531
|
}
|
|
573
532
|
}
|
|
@@ -580,8 +539,6 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
580
539
|
|
|
581
540
|
override wasShown(): void {
|
|
582
541
|
super.wasShown();
|
|
583
|
-
this.measureHandles();
|
|
584
|
-
this.toolbar.restore();
|
|
585
542
|
}
|
|
586
543
|
|
|
587
544
|
override willHide(): void {
|
|
@@ -609,11 +566,10 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
609
566
|
const contentLeft = screenRect.left + visiblePageRect.left - outlineRect.left;
|
|
610
567
|
const contentTop = screenRect.top + visiblePageRect.top - outlineRect.top;
|
|
611
568
|
|
|
612
|
-
const canvas =
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
canvas.height = Math.min((1 << 14), Math.floor(outlineRect.height));
|
|
569
|
+
const canvas = new OffscreenCanvas(Math.floor(outlineRect.width),
|
|
570
|
+
// Cap the height to not hit the GPU limit.
|
|
571
|
+
// https://crbug.com/1260828
|
|
572
|
+
Math.min((1 << 14), Math.floor(outlineRect.height)));
|
|
617
573
|
const ctx = canvas.getContext('2d', {willReadFrequently: true});
|
|
618
574
|
if (!ctx) {
|
|
619
575
|
throw new Error('Could not get 2d context from canvas.');
|
|
@@ -627,7 +583,7 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
627
583
|
await this.paintImage(ctx, this.model.screenImage(), screenRect.relativeTo(outlineRect));
|
|
628
584
|
}
|
|
629
585
|
ctx.drawImage(pageImage, Math.floor(contentLeft), Math.floor(contentTop));
|
|
630
|
-
this.saveScreenshot(
|
|
586
|
+
void this.saveScreenshot(canvas);
|
|
631
587
|
};
|
|
632
588
|
}
|
|
633
589
|
|
|
@@ -651,23 +607,22 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
651
607
|
const pageImage = new Image();
|
|
652
608
|
pageImage.src = 'data:image/png;base64,' + screenshot;
|
|
653
609
|
pageImage.onload = () => {
|
|
654
|
-
const canvas =
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
canvas.height = Math.min((1 << 14), Math.floor(pageImage.naturalHeight));
|
|
610
|
+
const canvas = new OffscreenCanvas(pageImage.naturalWidth,
|
|
611
|
+
// Cap the height to not hit the GPU limit.
|
|
612
|
+
// https://crbug.com/1260828
|
|
613
|
+
Math.min((1 << 14), Math.floor(pageImage.naturalHeight)));
|
|
659
614
|
const ctx = canvas.getContext('2d', {willReadFrequently: true});
|
|
660
615
|
if (!ctx) {
|
|
661
616
|
throw new Error('Could not get 2d context for base64 screenshot.');
|
|
662
617
|
}
|
|
663
618
|
ctx.imageSmoothingEnabled = false;
|
|
664
619
|
ctx.drawImage(pageImage, 0, 0);
|
|
665
|
-
this.saveScreenshot(
|
|
620
|
+
void this.saveScreenshot(canvas);
|
|
666
621
|
};
|
|
667
622
|
}
|
|
668
623
|
|
|
669
|
-
private paintImage(ctx: CanvasRenderingContext2D, src: string,
|
|
670
|
-
|
|
624
|
+
private paintImage(ctx: OffscreenCanvasRenderingContext2D|CanvasRenderingContext2D, src: string,
|
|
625
|
+
rect: EmulationModel.DeviceModeModel.Rect): Promise<void> {
|
|
671
626
|
return new Promise(resolve => {
|
|
672
627
|
const image = new Image();
|
|
673
628
|
image.crossOrigin = 'Anonymous';
|
|
@@ -680,7 +635,7 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
680
635
|
});
|
|
681
636
|
}
|
|
682
637
|
|
|
683
|
-
private saveScreenshot(canvas:
|
|
638
|
+
private async saveScreenshot(canvas: OffscreenCanvas): Promise<void> {
|
|
684
639
|
const url = this.model.inspectedURL();
|
|
685
640
|
let fileName = '';
|
|
686
641
|
if (url) {
|
|
@@ -692,15 +647,19 @@ export class DeviceModeView extends UI.Widget.VBox {
|
|
|
692
647
|
if (device && this.model.type() === EmulationModel.DeviceModeModel.Type.Device) {
|
|
693
648
|
fileName += `(${device.title})`;
|
|
694
649
|
}
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
link.click();
|
|
650
|
+
fileName += '.png';
|
|
651
|
+
const blob = await canvas.convertToBlob({type: 'image/png'});
|
|
652
|
+
const dataUrl = await new Promise<string>((resolve, reject) => {
|
|
653
|
+
const reader = new FileReader();
|
|
654
|
+
reader.onloadend = () => resolve(reader.result as string);
|
|
655
|
+
reader.onerror = reject;
|
|
656
|
+
reader.readAsDataURL(blob);
|
|
703
657
|
});
|
|
658
|
+
const base64 = dataUrl.slice(dataUrl.indexOf(',') + 1);
|
|
659
|
+
const contentData = new TextUtils.ContentData.ContentData(base64, /* isBase64=*/ true, 'image/png');
|
|
660
|
+
await Workspace.FileManager.FileManager.instance().save(fileName as Platform.DevToolsPath.RawPathString,
|
|
661
|
+
contentData, /* forceSaveAs=*/ true);
|
|
662
|
+
Workspace.FileManager.FileManager.instance().close(fileName as Platform.DevToolsPath.RawPathString);
|
|
704
663
|
}
|
|
705
664
|
}
|
|
706
665
|
|
|
@@ -138,6 +138,8 @@ export class ActionDelegate implements UI.ActionRegistration.ActionDelegate {
|
|
|
138
138
|
throw new Error(`Unable to get box model of the node: ${new Error().stack}`);
|
|
139
139
|
}
|
|
140
140
|
const nodeBorderQuad = nodeBoxModel.border;
|
|
141
|
+
// Calculate the rendered bounding box from the border quad to account for CSS scaling and transforms (e.g. zoom or transform: scale).
|
|
142
|
+
const {minX, maxX, minY, maxY} = getQuadBoundingBox(nodeBorderQuad);
|
|
141
143
|
|
|
142
144
|
// Get Layout Metrics to account for the Visual Viewport scroll and zoom.
|
|
143
145
|
const metrics = await node.domModel().target().pageAgent().invoke_getLayoutMetrics();
|
|
@@ -155,10 +157,10 @@ export class ActionDelegate implements UI.ActionRegistration.ActionDelegate {
|
|
|
155
157
|
// Assemble the final Clip.
|
|
156
158
|
// The absolute coordinates are: Global (OOPiF) + Viewport Scroll + Local Node Position (Border Box).
|
|
157
159
|
const clip = {
|
|
158
|
-
x: oopifOffsetX + scrollX +
|
|
159
|
-
y: oopifOffsetY + scrollY +
|
|
160
|
-
width:
|
|
161
|
-
height:
|
|
160
|
+
x: oopifOffsetX + scrollX + minX,
|
|
161
|
+
y: oopifOffsetY + scrollY + minY,
|
|
162
|
+
width: maxX - minX,
|
|
163
|
+
height: maxY - minY,
|
|
162
164
|
scale: 1,
|
|
163
165
|
};
|
|
164
166
|
|
|
@@ -249,3 +251,15 @@ async function getOopifOffset(target: SDK.Target.Target|null): Promise<{x: numbe
|
|
|
249
251
|
y: iframeContentY + scrollY + parentOffset.y,
|
|
250
252
|
};
|
|
251
253
|
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Calculate the axis-aligned bounding box for a Quad [x1, y1, x2, y2, x3, y3, x4, y4].
|
|
257
|
+
* This accounts for CSS scaling and transforms (e.g. zoom, transform: scale).
|
|
258
|
+
*/
|
|
259
|
+
function getQuadBoundingBox(quad: Protocol.DOM.Quad): {minX: number, maxX: number, minY: number, maxY: number} {
|
|
260
|
+
const minX = Math.min(quad[0], quad[2], quad[4], quad[6]);
|
|
261
|
+
const maxX = Math.max(quad[0], quad[2], quad[4], quad[6]);
|
|
262
|
+
const minY = Math.min(quad[1], quad[3], quad[5], quad[7]);
|
|
263
|
+
const maxY = Math.max(quad[1], quad[3], quad[5], quad[7]);
|
|
264
|
+
return {minX, maxX, minY, maxY};
|
|
265
|
+
}
|
|
@@ -10,6 +10,7 @@ import * as Host from '../../../core/host/host.js';
|
|
|
10
10
|
import * as i18n from '../../../core/i18n/i18n.js';
|
|
11
11
|
import type * as Platform from '../../../core/platform/platform.js';
|
|
12
12
|
import * as Root from '../../../core/root/root.js';
|
|
13
|
+
import * as AiAssistanceModel from '../../../models/ai_assistance/ai_assistance.js';
|
|
13
14
|
import * as Marked from '../../../third_party/marked/marked.js';
|
|
14
15
|
import * as Buttons from '../../../ui/components/buttons/buttons.js';
|
|
15
16
|
import * as Input from '../../../ui/components/input/input.js';
|
|
@@ -861,12 +862,9 @@ export class ConsoleInsight extends UI.Widget.Widget {
|
|
|
861
862
|
// off -> entrypoints are shown, and point to the AI setting panel where the setting can be turned on
|
|
862
863
|
// on -> entrypoints are shown, and console insights can be generated
|
|
863
864
|
#getConsoleInsightsEnabledSetting(): Common.Settings.Setting<boolean>|undefined {
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
} catch {
|
|
868
|
-
return;
|
|
869
|
-
}
|
|
865
|
+
const result = Common.Settings.Settings.instance().maybeResolve(
|
|
866
|
+
AiAssistanceModel.AiUtils.consoleInsightsEnabledSettingDescriptor);
|
|
867
|
+
return 'setting' in result ? (result.setting as Common.Settings.Setting<boolean>) : undefined;
|
|
870
868
|
}
|
|
871
869
|
|
|
872
870
|
// off -> consent reminder is shown, unless the 'console-insights-enabled'-setting has been enabled in the current DevTools session
|
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
import * as Common from '../../core/common/common.js';
|
|
6
6
|
import * as i18n from '../../core/i18n/i18n.js';
|
|
7
|
-
import type * as Platform from '../../core/platform/platform.js';
|
|
8
7
|
import type * as Root from '../../core/root/root.js';
|
|
8
|
+
import * as AiAssistanceModel from '../../models/ai_assistance/ai_assistance.js';
|
|
9
9
|
import * as Console from '../../panels/console/console.js';
|
|
10
10
|
import * as UI from '../../ui/legacy/legacy.js';
|
|
11
|
+
import * as SettingUIRegistration from '../../ui/settings/settings.js';
|
|
11
12
|
|
|
12
13
|
const UIStrings = {
|
|
13
14
|
/**
|
|
@@ -27,27 +28,9 @@ const UIStrings = {
|
|
|
27
28
|
* the settings tab.
|
|
28
29
|
*/
|
|
29
30
|
enableConsoleInsights: 'Understand console messages with AI',
|
|
30
|
-
/**
|
|
31
|
-
* @description Message shown to the user if the DevTools locale is not
|
|
32
|
-
* supported.
|
|
33
|
-
*/
|
|
34
|
-
wrongLocale: 'To use this feature, set your language preference to English in DevTools settings.',
|
|
35
|
-
/**
|
|
36
|
-
* @description Message shown to the user if the user's region is not
|
|
37
|
-
* supported.
|
|
38
|
-
*/
|
|
39
|
-
geoRestricted: 'This feature is unavailable in your region.',
|
|
40
|
-
/**
|
|
41
|
-
* @description Message shown to the user if the enterprise policy does
|
|
42
|
-
* not allow this feature.
|
|
43
|
-
*/
|
|
44
|
-
policyRestricted: 'This setting is managed by your administrator.',
|
|
45
31
|
} as const;
|
|
46
32
|
const str_ = i18n.i18n.registerUIStrings('panels/explain/explain-meta.ts', UIStrings);
|
|
47
33
|
const i18nLazyString = i18n.i18n.getLazilyComputedLocalizedString.bind(undefined, str_);
|
|
48
|
-
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
49
|
-
|
|
50
|
-
const setting = 'console-insights-enabled';
|
|
51
34
|
|
|
52
35
|
const actions = [
|
|
53
36
|
{
|
|
@@ -92,11 +75,6 @@ const actions = [
|
|
|
92
75
|
},
|
|
93
76
|
];
|
|
94
77
|
|
|
95
|
-
function isLocaleRestricted(): boolean {
|
|
96
|
-
const devtoolsLocale = i18n.DevToolsLocale.DevToolsLocale.instance();
|
|
97
|
-
return !devtoolsLocale.locale.startsWith('en-');
|
|
98
|
-
}
|
|
99
|
-
|
|
100
78
|
function isGeoRestricted(config?: Root.Runtime.HostConfig): boolean {
|
|
101
79
|
return config?.aidaAvailability?.blockedByGeo === true;
|
|
102
80
|
}
|
|
@@ -109,31 +87,11 @@ function isFeatureEnabled(config?: Root.Runtime.HostConfig): boolean {
|
|
|
109
87
|
return (config?.aidaAvailability?.enabled && config?.devToolsConsoleInsights?.enabled) === true;
|
|
110
88
|
}
|
|
111
89
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
defaultValue: false,
|
|
118
|
-
reloadRequired: false,
|
|
119
|
-
condition: config => isFeatureEnabled(config),
|
|
120
|
-
disabledCondition: config => {
|
|
121
|
-
const reasons: Platform.UIString.LocalizedString[] = [];
|
|
122
|
-
if (isGeoRestricted(config)) {
|
|
123
|
-
reasons.push(i18nString(UIStrings.geoRestricted));
|
|
124
|
-
}
|
|
125
|
-
if (isPolicyRestricted(config)) {
|
|
126
|
-
reasons.push(i18nString(UIStrings.policyRestricted));
|
|
127
|
-
}
|
|
128
|
-
if (isLocaleRestricted()) {
|
|
129
|
-
reasons.push(i18nString(UIStrings.wrongLocale));
|
|
130
|
-
}
|
|
131
|
-
if (reasons.length > 0) {
|
|
132
|
-
return {disabled: true, reasons};
|
|
133
|
-
}
|
|
134
|
-
return {disabled: false};
|
|
135
|
-
},
|
|
136
|
-
});
|
|
90
|
+
SettingUIRegistration.SettingUIRegistration.register(AiAssistanceModel.AiUtils.consoleInsightsEnabledSettingDescriptor,
|
|
91
|
+
{
|
|
92
|
+
category: Common.Settings.SettingCategory.AI,
|
|
93
|
+
title: i18nLazyString(UIStrings.enableConsoleInsights),
|
|
94
|
+
});
|
|
137
95
|
|
|
138
96
|
for (const action of actions) {
|
|
139
97
|
UI.ActionRegistration.registerActionExtension({
|
|
@@ -378,7 +378,7 @@ export interface RequestActivatedEvent {
|
|
|
378
378
|
}
|
|
379
379
|
|
|
380
380
|
export interface EventTypes {
|
|
381
|
-
[Events.RequestSelected]: SDK.NetworkRequest.NetworkRequest;
|
|
381
|
+
[Events.RequestSelected]: SDK.NetworkRequest.NetworkRequest|null;
|
|
382
382
|
[Events.RequestActivated]: RequestActivatedEvent;
|
|
383
383
|
}
|
|
384
384
|
|