chrome-devtools-frontend 1.0.1593518 → 1.0.1595090
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/docs/contributing/settings-experiments-features.md +93 -98
- package/front_end/core/common/README.md +126 -0
- package/front_end/core/host/UserMetrics.ts +1 -2
- package/front_end/core/protocol_client/InspectorBackend.ts +4 -0
- package/front_end/core/root/ExperimentNames.ts +0 -1
- package/front_end/core/sdk/CSSModel.ts +22 -0
- package/front_end/core/sdk/CSSNavigation.ts +33 -0
- package/front_end/core/sdk/CSSRule.ts +12 -2
- package/front_end/core/sdk/EmulationModel.ts +41 -1
- package/front_end/core/sdk/sdk-meta.ts +22 -18
- package/front_end/core/sdk/sdk.ts +2 -0
- package/front_end/design_system_tokens.css +538 -259
- package/front_end/entrypoints/main/MainImpl.ts +0 -6
- package/front_end/entrypoints/main/main-meta.ts +2 -2
- package/front_end/generated/InspectorBackendCommands.ts +6 -4
- package/front_end/generated/SupportedCSSProperties.js +12 -0
- package/front_end/generated/protocol-mapping.d.ts +7 -0
- package/front_end/generated/protocol-proxy-api.d.ts +5 -0
- package/front_end/generated/protocol.ts +47 -0
- package/front_end/models/ai_code_completion/AiCodeCompletion.ts +1 -0
- package/front_end/models/emulation/DeviceModeModel.ts +47 -0
- package/front_end/models/issues_manager/Issue.ts +1 -0
- package/front_end/models/issues_manager/IssueAggregator.ts +9 -9
- package/front_end/models/issues_manager/IssuesManager.ts +5 -5
- package/front_end/models/issues_manager/SelectivePermissionsInterventionIssue.ts +65 -0
- package/front_end/models/issues_manager/descriptions/selectivePermissionsIntervention.md +7 -0
- package/front_end/models/issues_manager/issues_manager.ts +2 -4
- package/front_end/models/javascript_metadata/NativeFunctions.js +48 -9
- package/front_end/models/lighthouse/lighthouse.ts +9 -0
- package/front_end/models/persistence/persistence-meta.ts +4 -4
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +12 -1
- package/front_end/panels/ai_assistance/components/ChatInput.ts +37 -30
- package/front_end/panels/ai_assistance/components/ChatView.ts +4 -2
- package/front_end/panels/ai_assistance/components/chatInput.css +26 -1
- package/front_end/panels/application/StorageView.ts +8 -2
- package/front_end/panels/application/preloading/PreloadingView.ts +105 -7
- package/front_end/panels/application/preloading/preloadingView.css +4 -0
- package/front_end/panels/common/AiCodeCompletionDisclaimer.ts +3 -0
- package/front_end/panels/common/AiCodeGenerationTeaser.ts +3 -0
- package/front_end/panels/console/ConsoleView.ts +2 -2
- package/front_end/panels/console/console-meta.ts +18 -14
- package/front_end/panels/elements/ComputedStyleWidget.ts +12 -7
- package/front_end/panels/elements/ElementsPanel.ts +1 -1
- package/front_end/panels/elements/ElementsTreeOutline.ts +4 -6
- package/front_end/panels/elements/ImagePreviewPopover.ts +6 -9
- package/front_end/panels/elements/StylePropertiesSection.ts +30 -0
- package/front_end/panels/elements/StylesAiCodeCompletionProvider.ts +1 -6
- package/front_end/panels/elements/StylesSidebarPane.ts +92 -7
- package/front_end/panels/elements/elements-meta.ts +12 -8
- package/front_end/panels/emulation/DeviceModeToolbar.ts +206 -66
- package/front_end/panels/issues/AffectedSelectivePermissionsInterventionView.ts +120 -0
- package/front_end/panels/issues/HiddenIssuesRow.ts +1 -1
- package/front_end/panels/issues/IssueKindView.ts +2 -1
- package/front_end/panels/issues/IssueView.ts +4 -4
- package/front_end/panels/issues/IssuesPane.ts +8 -2
- package/front_end/panels/issues/issues.ts +2 -0
- package/front_end/panels/lighthouse/LighthouseController.ts +3 -3
- package/front_end/panels/lighthouse/LighthousePanel.ts +9 -5
- package/front_end/panels/lighthouse/LighthouseProtocolService.ts +5 -5
- package/front_end/panels/lighthouse/LighthouseReportRenderer.ts +6 -7
- package/front_end/panels/lighthouse/LighthouseReportSelector.ts +4 -3
- package/front_end/panels/lighthouse/LighthouseStartView.ts +99 -38
- package/front_end/panels/lighthouse/LighthouseTimespanView.ts +53 -14
- package/front_end/panels/lighthouse/RadioSetting.ts +33 -19
- package/front_end/panels/lighthouse/lighthouse.ts +2 -2
- package/front_end/panels/media/PlayerMessagesView.ts +62 -49
- package/front_end/panels/media/media.ts +2 -0
- package/front_end/panels/media/playerMessagesView.css +1 -1
- package/front_end/panels/profiler/HeapSnapshotGridNodes.ts +49 -24
- package/front_end/panels/settings/FrameworkIgnoreListSettingsTab.ts +2 -2
- package/front_end/panels/sources/sources-meta.ts +8 -4
- package/front_end/panels/utils/utils.ts +11 -5
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/third_party/lit/lib/async-directive.d.ts +465 -0
- package/front_end/third_party/lit/lib/async-directive.js +23 -0
- package/front_end/third_party/lit/lib/async-directive.js.map +1 -0
- package/front_end/third_party/lit/lib/decorators.d.ts +7 -1
- package/front_end/third_party/lit/lib/decorators.js +2 -2
- package/front_end/third_party/lit/lib/decorators.js.map +1 -1
- package/front_end/third_party/lit/lib/directive.js.map +1 -1
- package/front_end/third_party/lit/lib/directives.d.ts +27 -8
- package/front_end/third_party/lit/lib/directives.js +8 -8
- package/front_end/third_party/lit/lib/directives.js.map +1 -1
- package/front_end/third_party/lit/lib/lit.d.ts +15 -5
- package/front_end/third_party/lit/lib/lit.js +4 -4
- package/front_end/third_party/lit/lib/lit.js.map +1 -1
- package/front_end/third_party/lit/lib/static-html.js +2 -2
- package/front_end/third_party/lit/lib/static-html.js.map +1 -1
- package/front_end/third_party/lit/lit.ts +2 -1
- package/front_end/third_party/lit/rollup.config.mjs +1 -1
- package/front_end/third_party/source-map-scopes-codec/package/deno.json +1 -1
- package/front_end/third_party/source-map-scopes-codec/package/package.json +1 -1
- package/front_end/third_party/source-map-scopes-codec/package/src/decode/decode.js +1 -1
- package/front_end/third_party/source-map-scopes-codec/package/src/decode/decode.js.map +1 -1
- package/front_end/third_party/source-map-scopes-codec/package/src/decode/decode.ts +1 -1
- package/front_end/third_party/source-map-scopes-codec/package/src/encode/encoder.js +1 -1
- package/front_end/third_party/source-map-scopes-codec/package/src/encode/encoder.js.map +1 -1
- package/front_end/third_party/source-map-scopes-codec/package/src/encode/encoder.ts +2 -2
- package/front_end/ui/components/markdown_view/MarkdownLinksMap.ts +5 -0
- package/front_end/ui/legacy/SuggestBox.ts +4 -0
- package/front_end/ui/legacy/TextPrompt.ts +1 -1
- package/front_end/ui/legacy/components/utils/ImagePreview.ts +27 -23
- package/front_end/ui/lit/lit.ts +1 -0
- package/front_end/ui/visual_logging/Debugging.ts +1 -1
- package/front_end/ui/visual_logging/KnownContextValues.ts +19 -0
- package/inspector_overlay/highlight_grid_common.ts +11 -8
- package/package.json +1 -1
- package/front_end/models/issues_manager/ContrastCheckTrigger.ts +0 -78
- package/front_end/models/issues_manager/LowTextContrastIssue.ts +0 -63
- package/front_end/panels/issues/AffectedElementsWithLowContrastView.ts +0 -91
- /package/front_end/{panels → models}/lighthouse/LighthouseReporterTypes.ts +0 -0
|
@@ -1,44 +1,35 @@
|
|
|
1
1
|
# Settings, Experiments, and Features in Chromium DevTools
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
customized in multiple ways:
|
|
3
|
+
Chromium DevTools can be customized in several ways:
|
|
5
4
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
Adding new DevTools experiments is deprecated, the preferred way for adding new
|
|
12
|
-
features / exposing experimental features is via `base::Feature`s. These are
|
|
13
|
-
controllable via Chromium command line parameters or optionally via `chrome://flags`.
|
|
14
|
-
|
|
15
|
-
Note: We are currently in the process of migrating away from DevTools experiments,
|
|
16
|
-
this documentation is partly outdated and will be updated ASAP.
|
|
5
|
+
**Preferences** (found in Settings > Preferences) control the general appearance and behavior of
|
|
6
|
+
DevTools, such as language or UI options. In the codebase, these are represented as "Settings"
|
|
7
|
+
([documentation](https://chromium.googlesource.com/devtools/devtools-frontend/+/main/front_end/core/common/README.md)).
|
|
8
|
+
They are intended as permanent options that remain relatively stable over time.
|
|
17
9
|
|
|
10
|
+
**Experiments** are features under active development or evaluation. These can change frequently
|
|
11
|
+
and are managed through DevTools > Settings > Experiments, `chrome://flags`, or command-line
|
|
12
|
+
switches. They are implemented using Chromium's `base::Feature` system, which allows for granular
|
|
13
|
+
control and testing.
|
|
18
14
|
|
|
19
15
|
[TOC]
|
|
20
16
|
|
|
21
|
-
## How to add
|
|
22
|
-
|
|
23
|
-
[go/chrome-devtools:command-line-config](http://go/chrome-devtools:command-line-config)
|
|
17
|
+
## How to add base::Feature feature flags
|
|
24
18
|
|
|
25
|
-
`base::Feature`
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
19
|
+
`base::Feature` flags are defined in the Chromium repository and exposed to DevTools through [host
|
|
20
|
+
bindings](https://crsrc.org/c/chrome/browser/devtools/devtools_ui_bindings.cc). This architecture
|
|
21
|
+
ensures a single source of truth for features that span both the front-end (DevTools) and back-end
|
|
22
|
+
(Chromium). Even features that only affect the front-end should use this system for consistency.
|
|
23
|
+
By default, `base::Features` are configurable via command line parameters when launching Chromium.
|
|
24
|
+
Optionally, they can be made available via the `chrome://flags` UI and via
|
|
25
|
+
DevTools > Settings > Preferences as well.
|
|
30
26
|
|
|
31
|
-
|
|
32
|
-
launching Chromium. Optionally, they can be made available via the `chrome://flags`
|
|
33
|
-
UI as well.
|
|
34
|
-
|
|
35
|
-
### Define a new `base::Feature`
|
|
27
|
+
### Define a new base::Feature
|
|
36
28
|
|
|
37
29
|
Add a new `base::Feature` to DevTools' [`features.cc`](https://crsrc.org/c/chrome/browser/devtools/features.cc). This feature will automatically be available as a Chrome command line parameter:
|
|
38
30
|
|
|
39
31
|
```cxx
|
|
40
|
-
// in
|
|
41
|
-
|
|
32
|
+
// in features.cc
|
|
42
33
|
BASE_FEATURE(kDevToolsNewFeature, "DevToolsNewFeature",
|
|
43
34
|
base::FEATURE_DISABLED_BY_DEFAULT);
|
|
44
35
|
|
|
@@ -47,25 +38,20 @@ const base::FeatureParam<std::string> kDevToolsNewFeatureStringParam{
|
|
|
47
38
|
&kDevToolsNewFeature, "string_param", /*default_value=*/""};
|
|
48
39
|
const base::FeatureParam<double> kDevToolsNewFeatureDoubleParam{
|
|
49
40
|
&kDevToolsNewFeature, "double_param", /*default_value=*/0};
|
|
50
|
-
|
|
51
41
|
```
|
|
52
42
|
|
|
53
43
|
Start Chrome via command line including flags:
|
|
54
44
|
|
|
55
45
|
```bash
|
|
56
|
-
|
|
46
|
+
[path to chrome]/chrome --enable-features=DevToolsNewFeature
|
|
57
47
|
```
|
|
58
48
|
|
|
59
|
-
|
|
49
|
+
Command for starting Chrome with feature and additional feature parameters:
|
|
60
50
|
|
|
61
51
|
```bash
|
|
62
|
-
|
|
52
|
+
[path to chrome]/chrome --enable-features="DevToolsNewFeature:string_param/foo/double_param/0.5"
|
|
63
53
|
```
|
|
64
54
|
|
|
65
|
-
### Make the `base::Feature` available via `chrome://flags`
|
|
66
|
-
|
|
67
|
-
This step is optional. If you want the `base::Feature` to be controllable via the `chrome://flags` UI and not only via the command line, follow [this documentation](https://chromium.googlesource.com/chromium/src/+/main/docs/how_to_add_your_feature_flag.md#step-2_adding-the-feature-flag-to-the-chrome_flags-ui).
|
|
68
|
-
|
|
69
55
|
### Add the new feature to the host configuration being sent to DevTools
|
|
70
56
|
|
|
71
57
|
Add the new feature to `DevToolsUIBindings::GetHostConfig` ([link to code](https://crsrc.org/c/chrome/browser/devtools/devtools_ui_bindings.cc;l=1506;drc=dd0b2a0bee86088ec0d481bd8722c06edaaba4a4), [example CL](https://crrev.com/c/5625935)).
|
|
@@ -73,92 +59,101 @@ This step is optional. If you want the `base::Feature` to be controllable via th
|
|
|
73
59
|
### In DevTools, use the new property added to HostConfig
|
|
74
60
|
|
|
75
61
|
* Update the type definition in [`Runtime.ts`](https://crsrc.org/c/third_party/devtools-frontend/src/front_end/core/root/Runtime.ts).
|
|
76
|
-
*
|
|
77
|
-
|
|
78
|
-
* In unit tests, make sure to assign the expected configuration using
|
|
62
|
+
* Check if the `base::Feature` is enabled and use this to run certain code blocks conditionally by
|
|
63
|
+
accessing the host config via `Root.Runtime.hostConfig`.
|
|
64
|
+
* In unit tests, make sure to assign the expected configuration using
|
|
65
|
+
`updateHostConfig({ someFeature: {enabled: true} })`.
|
|
79
66
|
|
|
80
|
-
Please refer to this [example CL](https://crrev.com/c/
|
|
67
|
+
Please refer to this [example CL](https://crrev.com/c/6820841).
|
|
81
68
|
|
|
82
|
-
|
|
69
|
+
### Optional: Make the base::Feature available via chrome://flags
|
|
83
70
|
|
|
84
|
-
|
|
71
|
+
This step is optional. If you want the `base::Feature` to be controllable via the
|
|
72
|
+
`chrome://flags` UI and not only via the command line, follow
|
|
73
|
+
[this documentation](https://chromium.googlesource.com/chromium/src/+/main/docs/how_to_add_your_feature_flag.md#step-2_adding-the-feature-flag-to-the-chrome_flags-ui).
|
|
85
74
|
|
|
86
|
-
|
|
87
|
-
will need to do two things:
|
|
75
|
+
[Example CL](https://crrev.com/c/7588357)
|
|
88
76
|
|
|
89
|
-
|
|
90
|
-
enabling/disabling the experiment.
|
|
91
|
-
2. Register the experiment in DevTools to have it added to the Settings UI.
|
|
77
|
+
### Optional: Add the base::Feature to DevTools > Settings > Experiments
|
|
92
78
|
|
|
93
|
-
|
|
79
|
+
Prerequisite: The `base::Feature` needs to be have been added to `chrome://flags`.
|
|
94
80
|
|
|
95
|
-
|
|
96
|
-
`DevToolsExperiments` (your best bet is to search for this text in your editor,
|
|
97
|
-
as `enums.xml` is very large).
|
|
81
|
+
#### Step 1: Create a HostExperiment in the DevTools repository
|
|
98
82
|
|
|
99
|
-
|
|
100
|
-
increased by one from the previous entry. The label can be anything you like but
|
|
101
|
-
make sure it is easily identifiable.
|
|
83
|
+
Register the experiment in [`MainImpl.ts`](https://crsrc.org/c/third_party/devtools-frontend/src/front_end/entrypoints/main/MainImpl.ts;l=343)
|
|
102
84
|
|
|
103
|
-
```
|
|
104
|
-
|
|
85
|
+
```ts
|
|
86
|
+
Root.Runtime.experiments.registerHostExperiment({
|
|
87
|
+
name: Root.ExperimentNames.ExperimentName.DURABLE_MESSAGES,
|
|
88
|
+
// Short description of the experiment, shown to users
|
|
89
|
+
title: 'Durable Messages',
|
|
90
|
+
// This must match the name of the Chrome flag as seen in `chrome://flags` or in `chrome/browser/about_flags.cc`
|
|
91
|
+
aboutFlag: 'devtools-enable-durable-messages',
|
|
92
|
+
isEnabled: Root.Runtime.hostConfig.devToolsEnableDurableMessages?.enabled ?? false,
|
|
93
|
+
// Whether the experiment requires a Chrome restart or only a reload of DevTools
|
|
94
|
+
requiresChromeRestart: false,
|
|
95
|
+
// optional
|
|
96
|
+
docLink: 'https://goo.gle/related-documentation',
|
|
97
|
+
// optional
|
|
98
|
+
feedbackLink: 'https://crbug.com/[bug number]',
|
|
99
|
+
});
|
|
105
100
|
```
|
|
106
101
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
### DevTools front-end
|
|
102
|
+
[Example CL](https://crrev.com/c/7588114)
|
|
110
103
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
`
|
|
104
|
+
`requiresChromeRestart` should be set to `true` if the `base::Feature` is used for conditionally
|
|
105
|
+
running code in the Chromium repository as well. If it only affects code paths in the DevTools
|
|
106
|
+
repository, `requiresChromeRestart` should be set to `false`. This means that toggling the
|
|
107
|
+
experimental feature requires only reloading DevTools and not restarting Chrome.
|
|
114
108
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
'yourExperimentNameHere',
|
|
118
|
-
'User facing short description of experiment here',
|
|
119
|
-
false,
|
|
120
|
-
);
|
|
121
|
-
```
|
|
109
|
+
You may also pass in two additional arguments which can be used to link users to documentation and
|
|
110
|
+
to a place for providing feedback.
|
|
122
111
|
|
|
123
|
-
|
|
124
|
-
|
|
112
|
+
You must also add the experiment to [UserMetrics.ts](https://crsrc.org/c/third_party/devtools-frontend/src/front_end/core/host/UserMetrics.ts;l=807).
|
|
113
|
+
Add an entry to the `DevToolsExperiments` enum, incrementing the `MAX_VALUE` by 1 and assigning the
|
|
114
|
+
un-incremented value to your experiment (the gaps in the values assigned to experiments are caused
|
|
115
|
+
by expired experiments which are no longer part of the codebase).
|
|
125
116
|
|
|
126
|
-
|
|
127
|
-
be shown to users.
|
|
117
|
+
#### Step 2: Update UI bindings to allow toggling the experiment from the DevTools UI
|
|
128
118
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
may cause issues.
|
|
119
|
+
In the scenario of allowing users to toggle the experiment from the DevTools UI, when assembling
|
|
120
|
+
the host config to be sent to DevTools, use the `GetFeatureStateForDevTools` helper to determine
|
|
121
|
+
the enabled/disabled state of the experiment.
|
|
133
122
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
123
|
+
```cxx
|
|
124
|
+
response_dict.Set(
|
|
125
|
+
"devToolsNewFeature",
|
|
126
|
+
base::DictValue().Set(
|
|
127
|
+
"enabled",
|
|
128
|
+
GetFeatureStateForDevTools(
|
|
129
|
+
::features::kDevToolsNewFeature,
|
|
130
|
+
enabled_by_flags,
|
|
131
|
+
disabled_by_flags
|
|
132
|
+
)
|
|
133
|
+
)
|
|
144
134
|
);
|
|
145
135
|
```
|
|
146
136
|
|
|
147
|
-
|
|
148
|
-
entry to the `DevToolsExperiments` enum, using **the same label and integer
|
|
149
|
-
value** that you used in the Chromium CL. You should also increase the
|
|
150
|
-
`MaxValue` entry by one.
|
|
137
|
+
#### Step 3: Add the experiment to enums.xml in the Chromium repository
|
|
151
138
|
|
|
152
|
-
|
|
153
|
-
|
|
139
|
+
In Chromium, edit [tools/metrics/histograms/metadata/dev/enums.xml](https://crsrc.org/c/tools/metrics/histograms/metadata/dev/enums.xml;l=898).
|
|
140
|
+
Find the enum titled `DevToolsExperiments`, and add a new entry. Make sure that the value matches the one from
|
|
141
|
+
[UserMetrics.ts](https://crsrc.org/c/third_party/devtools-frontend/src/front_end/core/host/UserMetrics.ts;l=807)
|
|
142
|
+
in the DevTools repository. The label can be anything you like but make sure it is easily identifiable.
|
|
154
143
|
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
// Experiment code here
|
|
158
|
-
}
|
|
144
|
+
```xml
|
|
145
|
+
<int value="95" label="yourExperimentNameHere"/>
|
|
159
146
|
```
|
|
160
147
|
|
|
161
|
-
|
|
148
|
+
[Example CL](https://crrev.com/c/4915777)
|
|
149
|
+
|
|
150
|
+
## Deprecated: DevTools experiments
|
|
151
|
+
|
|
152
|
+
Previously, DevTools experiments that lacked a Chrome component were managed separately. This
|
|
153
|
+
system is now deprecated. All legacy experiments are being migrated to the `base::Feature`
|
|
154
|
+
framework to streamline development.
|
|
155
|
+
|
|
156
|
+
## A/B Testing with Finch and base::Feature
|
|
162
157
|
|
|
163
158
|
`base::Feature` flags can be used with Finch to conduct A/B testing by toggling feature flags for a defined percentage of users. A/B testing can be a good way of testing the waters. However, since the time to get meaningful metrics may take a long time, it shouldn't be used to get feedback on options quickly.
|
|
164
159
|
|
|
@@ -31,3 +31,129 @@ Common.Settings.registerSettingExtension({
|
|
|
31
31
|
|
|
32
32
|
A controller for the each setting is added to the 'preferences' tab if they are visible, that is, they have a `title` and a `category`.
|
|
33
33
|
They can also be set with the command menu if they have `options` and a `category` (options’ names are added as commands).
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
# The DevTools Revealer System
|
|
38
|
+
|
|
39
|
+
The `Revealer.ts` module provides a centralized system that allows different parts of the DevTools UI to "reveal" or display various types of objects without being tightly coupled to one another. It's a powerful decoupling mechanism that makes the codebase more modular, extensible, and performant.
|
|
40
|
+
|
|
41
|
+
For example, if you click on a CSS file link in the **Console**, this system is responsible for telling the **Sources** panel to open and display that file, without the Console needing a direct reference to the Sources panel.
|
|
42
|
+
|
|
43
|
+
## Core Concepts
|
|
44
|
+
|
|
45
|
+
1. **`Revealer<T>` Interface**: This is the fundamental contract. A "Revealer" is any object (typically a UI panel or view) that knows how to display a specific type of data (`T`). It must implement a single method: `reveal(revealable: T): Promise<void>`.
|
|
46
|
+
|
|
47
|
+
2. **"Revealable" Object**: This is any object you want to show to the user. It can be a source code file (`SDK.SourceCode.UISourceCode`), a network request (`SDK.NetworkRequest.NetworkRequest`), a DOM node (`SDK.DOMModel.DOMNode`), or any other custom data type.
|
|
48
|
+
|
|
49
|
+
3. **`RevealerRegistry`**: This is a singleton class that acts as a central directory. It holds a list of all available `Revealer`s and maps them to the data types they can handle.
|
|
50
|
+
|
|
51
|
+
4. **`RevealerRegistration<T>`**: This is a configuration object used to register a `Revealer` with the `RevealerRegistry`. It contains three key pieces of information:
|
|
52
|
+
* `contextTypes`: A function that returns an array of classes (constructors) that the `Revealer` can handle.
|
|
53
|
+
* `loadRevealer`: An asynchronous function that returns a promise, which resolves to an instance of the `Revealer`. This allows for the lazy-loading of UI panels, improving initial application performance.
|
|
54
|
+
* `destination` (optional): A user-friendly, localized string that describes where the object will be revealed (e.g., "Network panel", "Elements panel"). This is used for UI text, such as in context menus ("Reveal in...").
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## How to Create a New Revealer
|
|
59
|
+
|
|
60
|
+
Here are the steps to implement a new revealer that can take the user to a specific place in the DevTools UI.
|
|
61
|
+
|
|
62
|
+
### Step 1: Define the Object to be Revealed
|
|
63
|
+
|
|
64
|
+
First, ensure you have a class or data type that you want to make "revealable."
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
// front_end/models/my_app/MyApp.ts
|
|
68
|
+
export class MyDataObject {
|
|
69
|
+
id: string;
|
|
70
|
+
constructor(id: string) {
|
|
71
|
+
this.id = id;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Step 2: Implement the `Revealer` Interface in Your UI Panel
|
|
77
|
+
|
|
78
|
+
The UI component that will display the object (e.g., your panel or widget) must implement the `Common.Revealer.Revealer<T>` interface for your specific data type.
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
// front_end/panels/my_panel/MyPanel.ts
|
|
82
|
+
import * as Common from '../../core/common/common.js';
|
|
83
|
+
import * as UI from '../../ui/legacy/legacy.js';
|
|
84
|
+
import * as MyApp from '../../models/my_app/my_app.js';
|
|
85
|
+
|
|
86
|
+
// 1. Implement the interface for your specific data type.
|
|
87
|
+
export class MyPanel extends UI.Panel.Panel implements Common.Revealer.Revealer<MyApp.MyDataObject> {
|
|
88
|
+
// ... other panel implementation ...
|
|
89
|
+
|
|
90
|
+
// 2. This is the required method from the interface.
|
|
91
|
+
async reveal(dataObject: MyApp.MyDataObject): Promise<void> {
|
|
92
|
+
// This is where you put your panel's custom logic to show the object.
|
|
93
|
+
|
|
94
|
+
// First, ensure this panel is visible to the user.
|
|
95
|
+
await UI.ViewManager.ViewManager.instance().showView('my-panel-view-id');
|
|
96
|
+
|
|
97
|
+
// Now, highlight the specific item within your panel.
|
|
98
|
+
console.log(`Revealing data object with ID: ${dataObject.id}`);
|
|
99
|
+
// e.g., this.selectItemInUI(dataObject.id);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Step 3: Register Your Panel as a `Revealer`
|
|
105
|
+
|
|
106
|
+
This is the most important step. You must tell the central `RevealerRegistry` that `MyPanel` knows how to handle `MyDataObject`. This registration is typically done in a module's `-meta.ts` or `-legacy.ts` configuration file, which is executed at startup.
|
|
107
|
+
|
|
108
|
+
```ts
|
|
109
|
+
// front_end/panels/my_panel/my_panel-meta.ts
|
|
110
|
+
import * as Common from '../../core/common/common.js';
|
|
111
|
+
import * as i18n from '../../core/i18n/i18n.js';
|
|
112
|
+
|
|
113
|
+
const UIStrings = {
|
|
114
|
+
/**
|
|
115
|
+
* @description The name of the panel that reveals our custom data objects.
|
|
116
|
+
*/
|
|
117
|
+
myPanel: 'My Awesome Panel',
|
|
118
|
+
};
|
|
119
|
+
const str_ = i18n.i18n.registerUIStrings('panels/my_panel/my_panel-meta.ts', UIStrings);
|
|
120
|
+
const i18nLazyString = i18n.i18n.getLazilyComputedLocalizedString.bind(undefined, str_);
|
|
121
|
+
|
|
122
|
+
Common.Revealer.registerRevealer({
|
|
123
|
+
// 1. Specify the data type(s) this revealer can handle.
|
|
124
|
+
contextTypes() {
|
|
125
|
+
// Use a dynamic import to avoid circular dependencies at module load time.
|
|
126
|
+
const {MyApp} = await import('../../models/my_app/my_app.js');
|
|
127
|
+
return [
|
|
128
|
+
MyApp.MyDataObject,
|
|
129
|
+
];
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
// 2. Provide a function to load and return an instance of your panel.
|
|
133
|
+
// This is what makes lazy loading possible.
|
|
134
|
+
async loadRevealer() {
|
|
135
|
+
const {MyPanel} = await import('./MyPanel.js');
|
|
136
|
+
// If your panel is a singleton, return its instance, otherwise create a new one.
|
|
137
|
+
return MyPanel.instance();
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
// 3. (Optional) Provide a user-facing destination name for context menus.
|
|
141
|
+
destination: i18nLazyString(UIStrings.myPanel),
|
|
142
|
+
});
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Step 4: Trigger the Reveal from Anywhere
|
|
146
|
+
|
|
147
|
+
Now that your revealer is registered, any other part of the DevTools codebase can ask to reveal an instance of `MyDataObject` without needing to know anything about `MyPanel`.
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
// In some other file, e.g., a context menu action.
|
|
151
|
+
import * as Common from '../../core/common/common.js';
|
|
152
|
+
import * as MyApp from '../../models/my_app/my_app.js';
|
|
153
|
+
|
|
154
|
+
// Create an instance of the object you want to reveal.
|
|
155
|
+
const myObject = new MyApp.MyDataObject('abc-123');
|
|
156
|
+
|
|
157
|
+
// Call the global reveal function.
|
|
158
|
+
await Common.Revealer.reveal(myObject);
|
|
159
|
+
```
|
|
@@ -817,7 +817,6 @@ export enum DevtoolsExperiments {
|
|
|
817
817
|
apca = 39,
|
|
818
818
|
'font-editor' = 41,
|
|
819
819
|
'full-accessibility-tree' = 42,
|
|
820
|
-
'contrast-issues' = 44,
|
|
821
820
|
'experimental-cookie-features' = 45,
|
|
822
821
|
'instrumentation-breakpoints' = 61,
|
|
823
822
|
'authored-deployed-grouping' = 63,
|
|
@@ -907,7 +906,7 @@ export enum IssueCreated {
|
|
|
907
906
|
'CookieIssue::WarnSameSiteUnspecifiedCrossSiteContext::SetCookie' = 35,
|
|
908
907
|
'SharedArrayBufferIssue::TransferIssue' = 36,
|
|
909
908
|
'SharedArrayBufferIssue::CreationIssue' = 37,
|
|
910
|
-
|
|
909
|
+
|
|
911
910
|
'CorsIssue::InsecureLocalNetwork' = 42,
|
|
912
911
|
'CorsIssue::InvalidHeaders' = 44,
|
|
913
912
|
'CorsIssue::WildcardOriginWithCredentials' = 45,
|
|
@@ -559,6 +559,10 @@ export class TargetBase {
|
|
|
559
559
|
this.registerDispatcher('DOMStorage', dispatcher);
|
|
560
560
|
}
|
|
561
561
|
|
|
562
|
+
registerEmulationDispatcher(dispatcher: ProtocolProxyApi.EmulationDispatcher): void {
|
|
563
|
+
this.registerDispatcher('Emulation', dispatcher);
|
|
564
|
+
}
|
|
565
|
+
|
|
562
566
|
registerFetchDispatcher(dispatcher: ProtocolProxyApi.FetchDispatcher): void {
|
|
563
567
|
this.registerDispatcher('Fetch', dispatcher);
|
|
564
568
|
}
|
|
@@ -15,7 +15,6 @@ export enum ExperimentName {
|
|
|
15
15
|
APCA = 'apca',
|
|
16
16
|
FONT_EDITOR = 'font-editor',
|
|
17
17
|
FULL_ACCESSIBILITY_TREE = 'full-accessibility-tree',
|
|
18
|
-
CONTRAST_ISSUES = 'contrast-issues',
|
|
19
18
|
EXPERIMENTAL_COOKIE_FEATURES = 'experimental-cookie-features',
|
|
20
19
|
INSTRUMENTATION_BREAKPOINTS = 'instrumentation-breakpoints',
|
|
21
20
|
AUTHORED_DEPLOYED_GROUPING = 'authored-deployed-grouping',
|
|
@@ -575,6 +575,28 @@ export class CSSModel extends SDKModel<EventTypes> {
|
|
|
575
575
|
}
|
|
576
576
|
}
|
|
577
577
|
|
|
578
|
+
async setNavigationText(
|
|
579
|
+
styleSheetId: Protocol.DOM.StyleSheetId, range: TextUtils.TextRange.TextRange,
|
|
580
|
+
newNavigationText: string): Promise<boolean> {
|
|
581
|
+
Host.userMetrics.actionTaken(Host.UserMetrics.Action.StyleRuleEdited);
|
|
582
|
+
|
|
583
|
+
try {
|
|
584
|
+
await this.ensureOriginalStyleSheetText(styleSheetId);
|
|
585
|
+
const {navigation} = await this.agent.invoke_setNavigationText({styleSheetId, range, text: newNavigationText});
|
|
586
|
+
|
|
587
|
+
if (!navigation) {
|
|
588
|
+
return false;
|
|
589
|
+
}
|
|
590
|
+
this.#domModel.markUndoableState();
|
|
591
|
+
const edit = new Edit(styleSheetId, range, newNavigationText, navigation);
|
|
592
|
+
this.fireStyleSheetChanged(styleSheetId, edit);
|
|
593
|
+
return true;
|
|
594
|
+
} catch (e) {
|
|
595
|
+
console.error(e);
|
|
596
|
+
return false;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
578
600
|
async setScopeText(
|
|
579
601
|
styleSheetId: Protocol.DOM.StyleSheetId, range: TextUtils.TextRange.TextRange,
|
|
580
602
|
newScopeText: string): Promise<boolean> {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
// Copyright 2026 The Chromium Authors
|
|
2
|
+
// Use of this source code is governed by a BSD-style license that can be
|
|
3
|
+
// found in the LICENSE file.
|
|
4
|
+
|
|
5
|
+
import type * as Protocol from '../../generated/protocol.js';
|
|
6
|
+
import * as TextUtils from '../../models/text_utils/text_utils.js';
|
|
7
|
+
|
|
8
|
+
import type {CSSModel} from './CSSModel.js';
|
|
9
|
+
import {CSSQuery} from './CSSQuery.js';
|
|
10
|
+
|
|
11
|
+
export class CSSNavigation extends CSSQuery {
|
|
12
|
+
static parseNavigationPayload(cssModel: CSSModel, payload: Protocol.CSS.CSSNavigation[]): CSSNavigation[] {
|
|
13
|
+
return payload.map(navigation => new CSSNavigation(cssModel, navigation));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
#active = true;
|
|
17
|
+
|
|
18
|
+
constructor(cssModel: CSSModel, payload: Protocol.CSS.CSSNavigation) {
|
|
19
|
+
super(cssModel);
|
|
20
|
+
this.reinitialize(payload);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
reinitialize(payload: Protocol.CSS.CSSNavigation): void {
|
|
24
|
+
this.text = payload.text;
|
|
25
|
+
this.range = payload.range ? TextUtils.TextRange.TextRange.fromObject(payload.range) : null;
|
|
26
|
+
this.styleSheetId = payload.styleSheetId;
|
|
27
|
+
this.#active = payload.active ?? true;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
active(): boolean {
|
|
31
|
+
return this.#active;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -10,6 +10,7 @@ import {CSSContainerQuery} from './CSSContainerQuery.js';
|
|
|
10
10
|
import {CSSLayer} from './CSSLayer.js';
|
|
11
11
|
import {CSSMedia} from './CSSMedia.js';
|
|
12
12
|
import type {CSSModel, Edit} from './CSSModel.js';
|
|
13
|
+
import {CSSNavigation} from './CSSNavigation.js';
|
|
13
14
|
import {CSSScope} from './CSSScope.js';
|
|
14
15
|
import {CSSStartingStyle} from './CSSStartingStyle.js';
|
|
15
16
|
import {CSSStyleDeclaration, Type} from './CSSStyleDeclaration.js';
|
|
@@ -113,6 +114,7 @@ export class CSSStyleRule extends CSSRule {
|
|
|
113
114
|
layers: CSSLayer[];
|
|
114
115
|
ruleTypes: Protocol.CSS.CSSRuleType[];
|
|
115
116
|
startingStyles: CSSStartingStyle[];
|
|
117
|
+
navigations: CSSNavigation[];
|
|
116
118
|
wasUsed: boolean;
|
|
117
119
|
constructor(cssModel: CSSModel, payload: Protocol.CSS.CSSRule, wasUsed?: boolean) {
|
|
118
120
|
super(cssModel, {
|
|
@@ -132,6 +134,7 @@ export class CSSStyleRule extends CSSRule {
|
|
|
132
134
|
this.layers = payload.layers ? CSSLayer.parseLayerPayload(cssModel, payload.layers) : [];
|
|
133
135
|
this.startingStyles =
|
|
134
136
|
payload.startingStyles ? CSSStartingStyle.parseStartingStylePayload(cssModel, payload.startingStyles) : [];
|
|
137
|
+
this.navigations = payload.navigations ? CSSNavigation.parseNavigationPayload(cssModel, payload.navigations) : [];
|
|
135
138
|
this.ruleTypes = payload.ruleTypes || [];
|
|
136
139
|
this.wasUsed = wasUsed || false;
|
|
137
140
|
}
|
|
@@ -224,6 +227,7 @@ export class CSSStyleRule extends CSSRule {
|
|
|
224
227
|
this.containerQueries.forEach(cq => cq.rebase(edit));
|
|
225
228
|
this.scopes.forEach(scope => scope.rebase(edit));
|
|
226
229
|
this.supports.forEach(supports => supports.rebase(edit));
|
|
230
|
+
this.navigations.forEach(navigation => navigation.rebase(edit));
|
|
227
231
|
|
|
228
232
|
super.rebase(edit);
|
|
229
233
|
}
|
|
@@ -396,7 +400,7 @@ export interface CSSNestedStyleLeaf {
|
|
|
396
400
|
|
|
397
401
|
export type CSSNestedStyleCondition = {
|
|
398
402
|
children: CSSNestedStyle[],
|
|
399
|
-
}&({media: CSSMedia}|{container: CSSContainerQuery}|{supports: CSSSupports});
|
|
403
|
+
}&({media: CSSMedia}|{container: CSSContainerQuery}|{supports: CSSSupports}|{navigation: CSSNavigation});
|
|
400
404
|
|
|
401
405
|
export type CSSNestedStyle = CSSNestedStyleLeaf|CSSNestedStyleCondition;
|
|
402
406
|
|
|
@@ -463,7 +467,13 @@ export class CSSFunctionRule extends CSSRule {
|
|
|
463
467
|
supports: new CSSSupports(this.cssModelInternal, node.condition.supports),
|
|
464
468
|
};
|
|
465
469
|
}
|
|
466
|
-
|
|
470
|
+
if (node.condition.navigation) {
|
|
471
|
+
return {
|
|
472
|
+
children,
|
|
473
|
+
navigation: new CSSNavigation(this.cssModelInternal, node.condition.navigation),
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
console.error('A function rule condition must have a media, container, supports, or navigation');
|
|
467
477
|
return;
|
|
468
478
|
}
|
|
469
479
|
console.error('A function rule node must have a style or condition');
|
|
@@ -18,7 +18,7 @@ export const enum DataSaverOverride {
|
|
|
18
18
|
DISABLED = 'disabled',
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
export class EmulationModel extends SDKModel<
|
|
21
|
+
export class EmulationModel extends SDKModel<EmulationModelEventTypes> implements ProtocolProxyApi.EmulationDispatcher {
|
|
22
22
|
readonly #emulationAgent: ProtocolProxyApi.EmulationApi;
|
|
23
23
|
readonly #deviceOrientationAgent: ProtocolProxyApi.DeviceOrientationApi;
|
|
24
24
|
#cssModel: CSSModel|null;
|
|
@@ -33,11 +33,15 @@ export class EmulationModel extends SDKModel<void> {
|
|
|
33
33
|
enabled: boolean,
|
|
34
34
|
configuration: Protocol.Emulation.SetEmitTouchEventsForMouseRequestConfiguration,
|
|
35
35
|
};
|
|
36
|
+
#screenOrientationLocked: boolean;
|
|
37
|
+
#lockedOrientation: Protocol.Emulation.ScreenOrientation|null;
|
|
36
38
|
|
|
37
39
|
constructor(target: Target) {
|
|
38
40
|
super(target);
|
|
39
41
|
this.#emulationAgent = target.emulationAgent();
|
|
40
42
|
this.#deviceOrientationAgent = target.deviceOrientationAgent();
|
|
43
|
+
this.#screenOrientationLocked = false;
|
|
44
|
+
this.#lockedOrientation = null;
|
|
41
45
|
this.#cssModel = target.model(CSSModel);
|
|
42
46
|
this.#overlayModel = target.model(OverlayModel);
|
|
43
47
|
if (this.#overlayModel) {
|
|
@@ -225,6 +229,7 @@ export class EmulationModel extends SDKModel<void> {
|
|
|
225
229
|
enabled: false,
|
|
226
230
|
configuration: Protocol.Emulation.SetEmitTouchEventsForMouseRequestConfiguration.Mobile,
|
|
227
231
|
};
|
|
232
|
+
target.registerEmulationDispatcher(this);
|
|
228
233
|
}
|
|
229
234
|
|
|
230
235
|
setTouchEmulationAllowed(touchEmulationAllowed: boolean): void {
|
|
@@ -478,6 +483,41 @@ export class EmulationModel extends SDKModel<void> {
|
|
|
478
483
|
];
|
|
479
484
|
return await this.emulateCSSMedia(type, features);
|
|
480
485
|
}
|
|
486
|
+
|
|
487
|
+
// ProtocolProxyApi.EmulationDispatcher implementation
|
|
488
|
+
|
|
489
|
+
virtualTimeBudgetExpired(): void {
|
|
490
|
+
// No-op for now; not used by the frontend.
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
screenOrientationLockChanged(event: Protocol.Emulation.ScreenOrientationLockChangedEvent): void {
|
|
494
|
+
this.#screenOrientationLocked = event.locked;
|
|
495
|
+
this.#lockedOrientation = event.orientation ?? null;
|
|
496
|
+
this.dispatchEventToListeners(
|
|
497
|
+
EmulationModelEvents.SCREEN_ORIENTATION_LOCK_CHANGED,
|
|
498
|
+
{locked: event.locked, orientation: event.orientation ?? null});
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
isScreenOrientationLocked(): boolean {
|
|
502
|
+
return this.#screenOrientationLocked;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
lockedOrientation(): Protocol.Emulation.ScreenOrientation|null {
|
|
506
|
+
return this.#lockedOrientation;
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
export const enum EmulationModelEvents {
|
|
511
|
+
SCREEN_ORIENTATION_LOCK_CHANGED = 'ScreenOrientationLockChanged',
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
export interface ScreenOrientationLockChangedEvent {
|
|
515
|
+
locked: boolean;
|
|
516
|
+
orientation: Protocol.Emulation.ScreenOrientation|null;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
export interface EmulationModelEventTypes {
|
|
520
|
+
[EmulationModelEvents.SCREEN_ORIENTATION_LOCK_CHANGED]: ScreenOrientationLockChangedEvent;
|
|
481
521
|
}
|
|
482
522
|
|
|
483
523
|
export class Location {
|