chrome-devtools-frontend 1.0.1007307 → 1.0.1007778
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/config/gni/devtools_grd_files.gni +3 -0
- package/extension-api/ExtensionAPI.d.ts +10 -0
- package/front_end/core/i18n/locales/en-US.json +14 -2
- package/front_end/core/i18n/locales/en-XL.json +14 -2
- package/front_end/entrypoints/lighthouse_worker/LighthouseWorkerService.ts +1 -4
- package/front_end/legacy_test_runner/lighthouse_test_runner/lighthouse_test_runner.js +16 -0
- package/front_end/models/extensions/ExtensionAPI.ts +95 -12
- package/front_end/models/extensions/ExtensionEndpoint.ts +69 -0
- package/front_end/models/extensions/ExtensionServer.ts +21 -0
- package/front_end/models/extensions/LanguageExtensionEndpoint.ts +46 -78
- package/front_end/models/extensions/RecorderExtensionEndpoint.ts +43 -0
- package/front_end/models/extensions/RecorderPluginManager.ts +30 -0
- package/front_end/models/extensions/extensions.ts +2 -0
- package/front_end/panels/application/AppManifestView.ts +2 -1
- package/front_end/panels/browser_debugger/DOMBreakpointsSidebarPane.ts +15 -1
- package/front_end/panels/lighthouse/LighthouseController.ts +25 -10
- package/front_end/panels/lighthouse/LighthouseStartView.ts +25 -1
- package/front_end/panels/lighthouse/LighthouseStartViewFR.ts +1 -1
- package/front_end/panels/network/components/RequestHeadersView.css +5 -0
- package/front_end/panels/network/components/RequestHeadersView.ts +56 -6
- package/package.json +1 -1
@@ -714,11 +714,14 @@ grd_files_debug_sources = [
|
|
714
714
|
"front_end/models/emulation/DeviceModeModel.js",
|
715
715
|
"front_end/models/emulation/EmulatedDevices.js",
|
716
716
|
"front_end/models/extensions/ExtensionAPI.js",
|
717
|
+
"front_end/models/extensions/ExtensionEndpoint.js",
|
717
718
|
"front_end/models/extensions/ExtensionPanel.js",
|
718
719
|
"front_end/models/extensions/ExtensionServer.js",
|
719
720
|
"front_end/models/extensions/ExtensionTraceProvider.js",
|
720
721
|
"front_end/models/extensions/ExtensionView.js",
|
721
722
|
"front_end/models/extensions/LanguageExtensionEndpoint.js",
|
723
|
+
"front_end/models/extensions/RecorderExtensionEndpoint.js",
|
724
|
+
"front_end/models/extensions/RecorderPluginManager.js",
|
722
725
|
"front_end/models/formatter/FormatterWorkerPool.js",
|
723
726
|
"front_end/models/formatter/ScriptFormatter.js",
|
724
727
|
"front_end/models/formatter/SourceFormatter.js",
|
@@ -94,6 +94,7 @@ export namespace Chrome {
|
|
94
94
|
panels: Panels;
|
95
95
|
inspectedWindow: InspectedWindow;
|
96
96
|
languageServices: LanguageExtensions;
|
97
|
+
recorder: RecorderExtensions;
|
97
98
|
}
|
98
99
|
|
99
100
|
export interface ExperimentalDevToolsAPI {
|
@@ -170,6 +171,10 @@ export namespace Chrome {
|
|
170
171
|
payload: unknown;
|
171
172
|
}
|
172
173
|
|
174
|
+
export interface RecorderExtensionPlugin {
|
175
|
+
stringify(obj: Record<string, any>): Promise<string>;
|
176
|
+
}
|
177
|
+
|
173
178
|
export interface LanguageExtensionPlugin {
|
174
179
|
/**
|
175
180
|
* A new raw module has been loaded. If the raw wasm module references an external debug info module, its URL will be
|
@@ -272,6 +277,11 @@ export namespace Chrome {
|
|
272
277
|
unregisterLanguageExtensionPlugin(plugin: LanguageExtensionPlugin): Promise<void>;
|
273
278
|
}
|
274
279
|
|
280
|
+
export interface RecorderExtensions {
|
281
|
+
registerRecorderExtensionPlugin(plugin: RecorderExtensionPlugin, pluginName: string): Promise<void>;
|
282
|
+
unregisterRecorderExtensionPlugin(plugin: RecorderExtensionPlugin): Promise<void>;
|
283
|
+
}
|
284
|
+
|
275
285
|
export interface Chrome {
|
276
286
|
devtools: DevToolsAPI;
|
277
287
|
experimental: {devtools: ExperimentalDevToolsAPI};
|
@@ -3803,6 +3803,9 @@
|
|
3803
3803
|
"panels/browser_debugger/DOMBreakpointsSidebarPane.ts | sS": {
|
3804
3804
|
"message": "{PH1}: {PH2}"
|
3805
3805
|
},
|
3806
|
+
"panels/browser_debugger/DOMBreakpointsSidebarPane.ts | sSS": {
|
3807
|
+
"message": "{PH1}: {PH2}, {PH3}"
|
3808
|
+
},
|
3806
3809
|
"panels/browser_debugger/DOMBreakpointsSidebarPane.ts | subtreeModified": {
|
3807
3810
|
"message": "Subtree modified"
|
3808
3811
|
},
|
@@ -5951,6 +5954,9 @@
|
|
5951
5954
|
"panels/lighthouse/LighthouseController.ts | desktop": {
|
5952
5955
|
"message": "Desktop"
|
5953
5956
|
},
|
5957
|
+
"panels/lighthouse/LighthouseController.ts | devtoolsThrottling": {
|
5958
|
+
"message": "DevTools throttling (advanced)"
|
5959
|
+
},
|
5954
5960
|
"panels/lighthouse/LighthouseController.ts | doesThisPageFollowBestPractices": {
|
5955
5961
|
"message": "Does this page follow best practices for modern web development"
|
5956
5962
|
},
|
@@ -6015,10 +6021,10 @@
|
|
6015
6021
|
"message": "SEO"
|
6016
6022
|
},
|
6017
6023
|
"panels/lighthouse/LighthouseController.ts | simulateASlowerPageLoadBasedOn": {
|
6018
|
-
"message": "
|
6024
|
+
"message": "Simulated throttling simulates a slower page load based on data from an initial unthrottled load. DevTools throttling actually slows down the page."
|
6019
6025
|
},
|
6020
6026
|
"panels/lighthouse/LighthouseController.ts | simulatedThrottling": {
|
6021
|
-
"message": "Simulated throttling"
|
6027
|
+
"message": "Simulated throttling (default)"
|
6022
6028
|
},
|
6023
6029
|
"panels/lighthouse/LighthouseController.ts | snapshot": {
|
6024
6030
|
"message": "Snapshot"
|
@@ -6032,6 +6038,9 @@
|
|
6032
6038
|
"panels/lighthouse/LighthouseController.ts | thereMayBeStoredDataAffectingSingular": {
|
6033
6039
|
"message": "There may be stored data affecting loading performance in this location: {PH1}. Audit this page in an incognito window to prevent those resources from affecting your scores."
|
6034
6040
|
},
|
6041
|
+
"panels/lighthouse/LighthouseController.ts | throttlingMethod": {
|
6042
|
+
"message": "Throttling method"
|
6043
|
+
},
|
6035
6044
|
"panels/lighthouse/LighthouseController.ts | timespan": {
|
6036
6045
|
"message": "Timespan"
|
6037
6046
|
},
|
@@ -6653,6 +6662,9 @@
|
|
6653
6662
|
"panels/network/components/RequestHeadersView.ts | responseHeaders": {
|
6654
6663
|
"message": "Response Headers"
|
6655
6664
|
},
|
6665
|
+
"panels/network/components/RequestHeadersView.ts | showMore": {
|
6666
|
+
"message": "Show more"
|
6667
|
+
},
|
6656
6668
|
"panels/network/components/RequestHeadersView.ts | statusCode": {
|
6657
6669
|
"message": "Status Code"
|
6658
6670
|
},
|
@@ -3803,6 +3803,9 @@
|
|
3803
3803
|
"panels/browser_debugger/DOMBreakpointsSidebarPane.ts | sS": {
|
3804
3804
|
"message": "{PH1}: {PH2}"
|
3805
3805
|
},
|
3806
|
+
"panels/browser_debugger/DOMBreakpointsSidebarPane.ts | sSS": {
|
3807
|
+
"message": "{PH1}: {PH2}, {PH3}"
|
3808
|
+
},
|
3806
3809
|
"panels/browser_debugger/DOMBreakpointsSidebarPane.ts | subtreeModified": {
|
3807
3810
|
"message": "Ŝúb̂t́r̂éê ḿôd́îf́îéd̂"
|
3808
3811
|
},
|
@@ -5951,6 +5954,9 @@
|
|
5951
5954
|
"panels/lighthouse/LighthouseController.ts | desktop": {
|
5952
5955
|
"message": "D̂éŝḱt̂óp̂"
|
5953
5956
|
},
|
5957
|
+
"panels/lighthouse/LighthouseController.ts | devtoolsThrottling": {
|
5958
|
+
"message": "D̂év̂T́ôól̂ś t̂h́r̂ót̂t́l̂ín̂ǵ (âd́v̂án̂ćêd́)"
|
5959
|
+
},
|
5954
5960
|
"panels/lighthouse/LighthouseController.ts | doesThisPageFollowBestPractices": {
|
5955
5961
|
"message": "D̂óêś t̂h́îś p̂áĝé f̂ól̂ĺôẃ b̂éŝt́ p̂ŕâćt̂íĉéŝ f́ôŕ m̂ód̂ér̂ń ŵéb̂ d́êv́êĺôṕm̂én̂t́"
|
5956
5962
|
},
|
@@ -6015,10 +6021,10 @@
|
|
6015
6021
|
"message": "ŜÉÔ"
|
6016
6022
|
},
|
6017
6023
|
"panels/lighthouse/LighthouseController.ts | simulateASlowerPageLoadBasedOn": {
|
6018
|
-
"message": "Ŝím̂úl̂át̂é â śl̂óŵér̂ ṕâǵê ĺôád
|
6024
|
+
"message": "Ŝím̂úl̂át̂éd̂ t́ĥŕôt́t̂ĺîńĝ śîḿûĺât́êś â śl̂óŵér̂ ṕâǵê ĺôád̂ b́âśêd́ ôń d̂át̂á f̂ŕôḿ âń îńît́îál̂ ún̂t́ĥŕôt́t̂ĺêd́ l̂óâd́. D̂év̂T́ôól̂ś t̂h́r̂ót̂t́l̂ín̂ǵ âćt̂úâĺl̂ý ŝĺôẃŝ d́ôẃn̂ t́ĥé p̂áĝé."
|
6019
6025
|
},
|
6020
6026
|
"panels/lighthouse/LighthouseController.ts | simulatedThrottling": {
|
6021
|
-
"message": "Ŝím̂úl̂át̂éd̂ t́ĥŕôt́t̂ĺîńĝ"
|
6027
|
+
"message": "Ŝím̂úl̂át̂éd̂ t́ĥŕôt́t̂ĺîńĝ (d́êf́âúl̂t́)"
|
6022
6028
|
},
|
6023
6029
|
"panels/lighthouse/LighthouseController.ts | snapshot": {
|
6024
6030
|
"message": "Ŝńâṕŝh́ôt́"
|
@@ -6032,6 +6038,9 @@
|
|
6032
6038
|
"panels/lighthouse/LighthouseController.ts | thereMayBeStoredDataAffectingSingular": {
|
6033
6039
|
"message": "T̂h́êŕê ḿâý b̂é ŝt́ôŕêd́ d̂át̂á âf́f̂éĉt́îńĝ ĺôád̂ín̂ǵ p̂ér̂f́ôŕm̂án̂ćê ín̂ t́ĥíŝ ĺôćât́îón̂: {PH1}. Áûd́ît́ t̂h́îś p̂áĝé îń âń îńĉóĝńît́ô ẃîńd̂óŵ t́ô ṕr̂év̂én̂t́ t̂h́ôśê ŕêśôúr̂ćêś f̂ŕôḿ âf́f̂éĉt́îńĝ ýôúr̂ śĉór̂éŝ."
|
6034
6040
|
},
|
6041
|
+
"panels/lighthouse/LighthouseController.ts | throttlingMethod": {
|
6042
|
+
"message": "T̂h́r̂ót̂t́l̂ín̂ǵ m̂ét̂h́ôd́"
|
6043
|
+
},
|
6035
6044
|
"panels/lighthouse/LighthouseController.ts | timespan": {
|
6036
6045
|
"message": "T̂ím̂éŝṕâń"
|
6037
6046
|
},
|
@@ -6653,6 +6662,9 @@
|
|
6653
6662
|
"panels/network/components/RequestHeadersView.ts | responseHeaders": {
|
6654
6663
|
"message": "R̂éŝṕôńŝé Ĥéâd́êŕŝ"
|
6655
6664
|
},
|
6665
|
+
"panels/network/components/RequestHeadersView.ts | showMore": {
|
6666
|
+
"message": "Ŝh́ôẃ m̂ór̂é"
|
6667
|
+
},
|
6656
6668
|
"panels/network/components/RequestHeadersView.ts | statusCode": {
|
6657
6669
|
"message": "Ŝt́ât́ûś Ĉód̂é"
|
6658
6670
|
},
|
@@ -143,10 +143,7 @@ async function invokeLH(action: string, args: any): Promise<unknown> {
|
|
143
143
|
const {page} = puppeteerConnection;
|
144
144
|
const configContext = {
|
145
145
|
logLevel: flags.logLevel,
|
146
|
-
settingsOverrides:
|
147
|
-
channel: flags.channel,
|
148
|
-
locale: flags.locale,
|
149
|
-
},
|
146
|
+
settingsOverrides: flags,
|
150
147
|
};
|
151
148
|
|
152
149
|
if (action === 'snapshot') {
|
@@ -100,6 +100,18 @@ LighthouseTestRunner._checkboxStateLabel = function(checkboxContainer) {
|
|
100
100
|
return `[${checkedLabel}] ${label}`;
|
101
101
|
};
|
102
102
|
|
103
|
+
/**
|
104
|
+
* @param {?Element} combobox
|
105
|
+
* @return {string}
|
106
|
+
*/
|
107
|
+
LighthouseTestRunner._comboboxStateLabel = function(combobox) {
|
108
|
+
if (!combobox) {
|
109
|
+
return 'missing';
|
110
|
+
}
|
111
|
+
|
112
|
+
return `${combobox.ariaLabel}: ${combobox.value}`;
|
113
|
+
};
|
114
|
+
|
103
115
|
/**
|
104
116
|
* @param {?Element} button
|
105
117
|
* @return {string}
|
@@ -130,6 +142,10 @@ LighthouseTestRunner.dumpStartAuditState = function() {
|
|
130
142
|
TestRunner.addResult(LighthouseTestRunner._checkboxStateLabel(element));
|
131
143
|
});
|
132
144
|
|
145
|
+
for (const combobox of toolbarShadowRoot.querySelectorAll('select')) {
|
146
|
+
TestRunner.addResult(LighthouseTestRunner._comboboxStateLabel(combobox));
|
147
|
+
}
|
148
|
+
|
133
149
|
const helpText = containerElement.querySelector('.lighthouse-help-text');
|
134
150
|
if (!helpText.classList.contains('hidden')) {
|
135
151
|
TestRunner.addResult(`Help text: ${helpText.textContent}`);
|
@@ -86,6 +86,7 @@ export namespace PrivateAPI {
|
|
86
86
|
Unsubscribe = 'unsubscribe',
|
87
87
|
UpdateButton = 'updateButton',
|
88
88
|
RegisterLanguageExtensionPlugin = 'registerLanguageExtensionPlugin',
|
89
|
+
RegisterRecorderExtensionPlugin = 'registerRecorderExtensionPlugin',
|
89
90
|
}
|
90
91
|
|
91
92
|
export const enum LanguageExtensionPluginCommands {
|
@@ -108,6 +109,14 @@ export namespace PrivateAPI {
|
|
108
109
|
UnregisteredLanguageExtensionPlugin = 'unregisteredLanguageExtensionPlugin',
|
109
110
|
}
|
110
111
|
|
112
|
+
export const enum RecorderExtensionPluginCommands {
|
113
|
+
Stringify = 'stringify',
|
114
|
+
}
|
115
|
+
|
116
|
+
export const enum RecorderExtensionPluginEvents {
|
117
|
+
UnregisteredRecorderExtensionPlugin = 'unregisteredRecorderExtensionPlugin',
|
118
|
+
}
|
119
|
+
|
111
120
|
export interface EvaluateOptions {
|
112
121
|
frameURL?: string;
|
113
122
|
useContentScriptContext?: boolean;
|
@@ -120,6 +129,11 @@ export namespace PrivateAPI {
|
|
120
129
|
port: MessagePort,
|
121
130
|
supportedScriptTypes: PublicAPI.Chrome.DevTools.SupportedScriptTypes,
|
122
131
|
};
|
132
|
+
type RegisterRecorderExtensionPluginRequest = {
|
133
|
+
command: Commands.RegisterRecorderExtensionPlugin,
|
134
|
+
pluginName: string,
|
135
|
+
port: MessagePort,
|
136
|
+
};
|
123
137
|
type SubscribeRequest = {command: Commands.Subscribe, type: string};
|
124
138
|
type UnsubscribeRequest = {command: Commands.Unsubscribe, type: string};
|
125
139
|
type AddRequestHeadersRequest = {
|
@@ -182,13 +196,13 @@ export namespace PrivateAPI {
|
|
182
196
|
type GetHARRequest = {command: Commands.GetHAR};
|
183
197
|
type GetPageResourcesRequest = {command: Commands.GetPageResources};
|
184
198
|
|
185
|
-
export type ServerRequests = RegisterLanguageExtensionPluginRequest|
|
186
|
-
AddRequestHeadersRequest|ApplyStyleSheetRequest|CreatePanelRequest|
|
187
|
-
UpdateButtonRequest|CompleteTraceSessionRequest|
|
188
|
-
SetSidebarContentRequest|SetSidebarPageRequest|
|
189
|
-
SetThemeChangeHandlerRequest|ReloadRequest|
|
190
|
-
GetResourceContentRequest|SetResourceContentRequest|
|
191
|
-
GetHARRequest|GetPageResourcesRequest;
|
199
|
+
export type ServerRequests = RegisterRecorderExtensionPluginRequest|RegisterLanguageExtensionPluginRequest|
|
200
|
+
SubscribeRequest|UnsubscribeRequest|AddRequestHeadersRequest|ApplyStyleSheetRequest|CreatePanelRequest|
|
201
|
+
ShowPanelRequest|CreateToolbarButtonRequest|UpdateButtonRequest|CompleteTraceSessionRequest|
|
202
|
+
CreateSidebarPaneRequest|SetSidebarHeightRequest|SetSidebarContentRequest|SetSidebarPageRequest|
|
203
|
+
OpenResourceRequest|SetOpenResourceHandlerRequest|SetThemeChangeHandlerRequest|ReloadRequest|
|
204
|
+
EvaluateOnInspectedPageRequest|GetRequestContentRequest|GetResourceContentRequest|SetResourceContentRequest|
|
205
|
+
AddTraceProviderRequest|ForwardKeyboardEventRequest|GetHARRequest|GetPageResourcesRequest;
|
192
206
|
export type ExtensionServerRequestMessage = PrivateAPI.ServerRequests&{requestId?: number};
|
193
207
|
|
194
208
|
type AddRawModuleRequest = {
|
@@ -256,6 +270,13 @@ export namespace PrivateAPI {
|
|
256
270
|
RawLocationToSourceLocationRequest|GetScopeInfoRequest|ListVariablesInScopeRequest|RemoveRawModuleRequest|
|
257
271
|
GetTypeInfoRequest|GetFormatterRequest|GetInspectableAddressRequest|GetFunctionInfoRequest|
|
258
272
|
GetInlinedFunctionRangesRequest|GetInlinedCalleesRangesRequest|GetMappedLinesRequest;
|
273
|
+
|
274
|
+
type StringifyRequest = {
|
275
|
+
method: RecorderExtensionPluginCommands.Stringify,
|
276
|
+
parameters: {recording: Record<string, unknown>},
|
277
|
+
};
|
278
|
+
|
279
|
+
export type RecorderExtensionRequests = StringifyRequest;
|
259
280
|
}
|
260
281
|
|
261
282
|
declare global {
|
@@ -264,7 +285,7 @@ declare global {
|
|
264
285
|
(extensionInfo: ExtensionDescriptor, inspectedTabId: string, themeName: string, keysToForward: number[],
|
265
286
|
testHook:
|
266
287
|
(extensionServer: APIImpl.ExtensionServerClient, extensionAPI: APIImpl.InspectorExtensionAPI) => unknown,
|
267
|
-
injectedScriptId: number) => void;
|
288
|
+
injectedScriptId: number, targetWindow?: Window) => void;
|
268
289
|
buildExtensionAPIInjectedScript(
|
269
290
|
extensionInfo: ExtensionDescriptor, inspectedTabId: string, themeName: string, keysToForward: number[],
|
270
291
|
testHook: undefined|((extensionServer: unknown, extensionAPI: unknown) => unknown)): string;
|
@@ -283,6 +304,7 @@ export type ExtensionDescriptor = {
|
|
283
304
|
namespace APIImpl {
|
284
305
|
export interface InspectorExtensionAPI {
|
285
306
|
languageServices: PublicAPI.Chrome.DevTools.LanguageExtensions;
|
307
|
+
recorder: PublicAPI.Chrome.DevTools.RecorderExtensions;
|
286
308
|
timeline: Timeline;
|
287
309
|
network: PublicAPI.Chrome.DevTools.Network;
|
288
310
|
panels: PublicAPI.Chrome.DevTools.Panels;
|
@@ -357,6 +379,10 @@ namespace APIImpl {
|
|
357
379
|
_plugins: Map<PublicAPI.Chrome.DevTools.LanguageExtensionPlugin, MessagePort>;
|
358
380
|
}
|
359
381
|
|
382
|
+
export interface RecorderExtensions extends PublicAPI.Chrome.DevTools.RecorderExtensions {
|
383
|
+
_plugins: Map<PublicAPI.Chrome.DevTools.RecorderExtensionPlugin, MessagePort>;
|
384
|
+
}
|
385
|
+
|
360
386
|
export interface ExtensionPanel extends ExtensionView, PublicAPI.Chrome.DevTools.ExtensionPanel {
|
361
387
|
show(): void;
|
362
388
|
}
|
@@ -392,7 +418,7 @@ namespace APIImpl {
|
|
392
418
|
self.injectedExtensionAPI = function(
|
393
419
|
extensionInfo: ExtensionDescriptor, inspectedTabId: string, themeName: string, keysToForward: number[],
|
394
420
|
testHook: (extensionServer: APIImpl.ExtensionServerClient, extensionAPI: APIImpl.InspectorExtensionAPI) => unknown,
|
395
|
-
injectedScriptId: number): void {
|
421
|
+
injectedScriptId: number, targetWindowForTest?: Window): void {
|
396
422
|
const keysToForwardSet = new Set<number>(keysToForward);
|
397
423
|
const chrome = window.chrome || {};
|
398
424
|
|
@@ -473,6 +499,7 @@ self.injectedExtensionAPI = function(
|
|
473
499
|
this.network = new (Constructor(Network))();
|
474
500
|
this.timeline = new (Constructor(Timeline))();
|
475
501
|
this.languageServices = new (Constructor(LanguageServicesAPI))();
|
502
|
+
this.recorder = new (Constructor(RecorderServicesAPI))();
|
476
503
|
defineDeprecatedProperty(this, 'webInspector', 'resources', 'network');
|
477
504
|
}
|
478
505
|
|
@@ -671,6 +698,60 @@ self.injectedExtensionAPI = function(
|
|
671
698
|
__proto__: ExtensionViewImpl.prototype,
|
672
699
|
};
|
673
700
|
|
701
|
+
function RecorderServicesAPIImpl(this: APIImpl.RecorderExtensions): void {
|
702
|
+
this._plugins = new Map();
|
703
|
+
}
|
704
|
+
|
705
|
+
(RecorderServicesAPIImpl.prototype as
|
706
|
+
Pick<APIImpl.RecorderExtensions, 'registerRecorderExtensionPlugin'|'unregisterRecorderExtensionPlugin'>) = {
|
707
|
+
registerRecorderExtensionPlugin: async function(
|
708
|
+
this: APIImpl.RecorderExtensions, plugin: PublicAPI.Chrome.DevTools.RecorderExtensionPlugin,
|
709
|
+
pluginName: string): Promise<void> {
|
710
|
+
if (this._plugins.has(plugin)) {
|
711
|
+
throw new Error(`Tried to register plugin '${pluginName}' twice`);
|
712
|
+
}
|
713
|
+
const channel = new MessageChannel();
|
714
|
+
const port = channel.port1;
|
715
|
+
this._plugins.set(plugin, port);
|
716
|
+
port.onmessage = ({data}: MessageEvent<{requestId: number}&PrivateAPI.RecorderExtensionRequests>): void => {
|
717
|
+
const {requestId} = data;
|
718
|
+
dispatchMethodCall(data)
|
719
|
+
.then(result => port.postMessage({requestId, result}))
|
720
|
+
.catch(error => port.postMessage({requestId, error: {message: error.message}}));
|
721
|
+
};
|
722
|
+
|
723
|
+
function dispatchMethodCall(request: PrivateAPI.RecorderExtensionRequests): Promise<unknown> {
|
724
|
+
switch (request.method) {
|
725
|
+
case PrivateAPI.RecorderExtensionPluginCommands.Stringify:
|
726
|
+
return plugin.stringify(request.parameters.recording);
|
727
|
+
default:
|
728
|
+
throw new Error(`'${request.method}' is not recognized`);
|
729
|
+
}
|
730
|
+
}
|
731
|
+
|
732
|
+
await new Promise<void>(resolve => {
|
733
|
+
extensionServer.sendRequest(
|
734
|
+
{
|
735
|
+
command: PrivateAPI.Commands.RegisterRecorderExtensionPlugin,
|
736
|
+
pluginName,
|
737
|
+
port: channel.port2,
|
738
|
+
},
|
739
|
+
() => resolve(), [channel.port2]);
|
740
|
+
});
|
741
|
+
},
|
742
|
+
|
743
|
+
unregisterRecorderExtensionPlugin: async function(
|
744
|
+
this: APIImpl.RecorderExtensions, plugin: PublicAPI.Chrome.DevTools.RecorderExtensionPlugin): Promise<void> {
|
745
|
+
const port = this._plugins.get(plugin);
|
746
|
+
if (!port) {
|
747
|
+
throw new Error('Tried to unregister a plugin that was not previously registered');
|
748
|
+
}
|
749
|
+
this._plugins.delete(plugin);
|
750
|
+
port.postMessage({event: PrivateAPI.RecorderExtensionPluginEvents.UnregisteredRecorderExtensionPlugin});
|
751
|
+
port.close();
|
752
|
+
},
|
753
|
+
};
|
754
|
+
|
674
755
|
function LanguageServicesAPIImpl(this: APIImpl.LanguageExtensions): void {
|
675
756
|
this._plugins = new Map();
|
676
757
|
}
|
@@ -787,6 +868,7 @@ self.injectedExtensionAPI = function(
|
|
787
868
|
}
|
788
869
|
|
789
870
|
const LanguageServicesAPI = declareInterfaceClass(LanguageServicesAPIImpl);
|
871
|
+
const RecorderServicesAPI = declareInterfaceClass(RecorderServicesAPIImpl);
|
790
872
|
const Button = declareInterfaceClass(ButtonImpl);
|
791
873
|
const EventSink = declareInterfaceClass(EventSinkImpl);
|
792
874
|
const ExtensionPanel = declareInterfaceClass(ExtensionPanelImpl);
|
@@ -1128,7 +1210,7 @@ self.injectedExtensionAPI = function(
|
|
1128
1210
|
|
1129
1211
|
document.addEventListener('keydown', forwardKeyboardEvent, false);
|
1130
1212
|
|
1131
|
-
function ExtensionServerClient(this: APIImpl.ExtensionServerClient): void {
|
1213
|
+
function ExtensionServerClient(this: APIImpl.ExtensionServerClient, targetWindow: Window): void {
|
1132
1214
|
this._callbacks = {};
|
1133
1215
|
this._handlers = {};
|
1134
1216
|
this._lastRequestId = 0;
|
@@ -1141,7 +1223,7 @@ self.injectedExtensionAPI = function(
|
|
1141
1223
|
this._port.addEventListener('message', this._onMessage.bind(this), false);
|
1142
1224
|
this._port.start();
|
1143
1225
|
|
1144
|
-
|
1226
|
+
targetWindow.postMessage('registerExtension', '*', [channel.port2]);
|
1145
1227
|
}
|
1146
1228
|
|
1147
1229
|
(ExtensionServerClient.prototype as Pick<
|
@@ -1225,7 +1307,7 @@ self.injectedExtensionAPI = function(
|
|
1225
1307
|
}
|
1226
1308
|
}
|
1227
1309
|
|
1228
|
-
const extensionServer = new (Constructor(ExtensionServerClient))();
|
1310
|
+
const extensionServer = new (Constructor(ExtensionServerClient))(targetWindowForTest || window.parent);
|
1229
1311
|
|
1230
1312
|
const coreAPI = new (Constructor(InspectorExtensionAPI))();
|
1231
1313
|
|
@@ -1241,6 +1323,7 @@ self.injectedExtensionAPI = function(
|
|
1241
1323
|
chrome.devtools!.panels = coreAPI.panels;
|
1242
1324
|
chrome.devtools!.panels.themeName = themeName;
|
1243
1325
|
chrome.devtools!.languageServices = coreAPI.languageServices;
|
1326
|
+
chrome.devtools!.recorder = coreAPI.recorder;
|
1244
1327
|
|
1245
1328
|
// default to expose experimental APIs for now.
|
1246
1329
|
if (extensionInfo.exposeExperimentalAPIs !== false) {
|
@@ -0,0 +1,69 @@
|
|
1
|
+
// Copyright 2022 The Chromium Authors. All rights reserved.
|
2
|
+
// Use of this source code is governed by a BSD-style license that can be
|
3
|
+
// found in the LICENSE file.
|
4
|
+
|
5
|
+
type Response = {
|
6
|
+
requestId: number,
|
7
|
+
result: unknown,
|
8
|
+
error: Error|null,
|
9
|
+
};
|
10
|
+
|
11
|
+
type Event = {
|
12
|
+
event: string,
|
13
|
+
};
|
14
|
+
|
15
|
+
type Message = MessageEvent<Response|Event>;
|
16
|
+
|
17
|
+
export class ExtensionEndpoint {
|
18
|
+
private readonly port: MessagePort;
|
19
|
+
private nextRequestId: number = 0;
|
20
|
+
private pendingRequests: Map<number, {
|
21
|
+
resolve: (arg: unknown) => void,
|
22
|
+
reject: (error: Error) => void,
|
23
|
+
}>;
|
24
|
+
|
25
|
+
constructor(port: MessagePort) {
|
26
|
+
this.port = port;
|
27
|
+
this.port.onmessage = this.onResponse.bind(this);
|
28
|
+
this.pendingRequests = new Map();
|
29
|
+
}
|
30
|
+
|
31
|
+
sendRequest<ReturnType>(method: string, parameters: unknown): Promise<ReturnType> {
|
32
|
+
return new Promise((resolve, reject) => {
|
33
|
+
const requestId = this.nextRequestId++;
|
34
|
+
this.pendingRequests.set(requestId, {resolve: resolve as (arg: unknown) => void, reject});
|
35
|
+
this.port.postMessage({requestId, method, parameters});
|
36
|
+
});
|
37
|
+
}
|
38
|
+
|
39
|
+
protected disconnect(): void {
|
40
|
+
for (const {reject} of this.pendingRequests.values()) {
|
41
|
+
reject(new Error('Extension endpoint disconnected'));
|
42
|
+
}
|
43
|
+
this.pendingRequests.clear();
|
44
|
+
this.port.close();
|
45
|
+
}
|
46
|
+
|
47
|
+
private onResponse({data}: Message): void {
|
48
|
+
if ('event' in data) {
|
49
|
+
this.handleEvent(data);
|
50
|
+
return;
|
51
|
+
}
|
52
|
+
const {requestId, result, error} = data;
|
53
|
+
const pendingRequest = this.pendingRequests.get(requestId);
|
54
|
+
if (!pendingRequest) {
|
55
|
+
console.error(`No pending request ${requestId}`);
|
56
|
+
return;
|
57
|
+
}
|
58
|
+
this.pendingRequests.delete(requestId);
|
59
|
+
if (error) {
|
60
|
+
pendingRequest.reject(new Error(error.message));
|
61
|
+
} else {
|
62
|
+
pendingRequest.resolve(result);
|
63
|
+
}
|
64
|
+
}
|
65
|
+
|
66
|
+
protected handleEvent(_event: Event): void {
|
67
|
+
throw new Error('handleEvent is not implemented');
|
68
|
+
}
|
69
|
+
}
|
@@ -52,7 +52,9 @@ import {ExtensionButton, ExtensionPanel, ExtensionSidebarPane} from './Extension
|
|
52
52
|
import type {TracingSession} from './ExtensionTraceProvider.js';
|
53
53
|
import {ExtensionTraceProvider} from './ExtensionTraceProvider.js';
|
54
54
|
import {LanguageExtensionEndpoint} from './LanguageExtensionEndpoint.js';
|
55
|
+
import {RecorderExtensionEndpoint} from './RecorderExtensionEndpoint.js';
|
55
56
|
import {PrivateAPI} from './ExtensionAPI.js';
|
57
|
+
import {RecorderPluginManager} from './RecorderPluginManager.js';
|
56
58
|
|
57
59
|
const extensionOrigins: WeakMap<MessagePort, Platform.DevToolsPath.UrlString> = new WeakMap();
|
58
60
|
|
@@ -138,6 +140,8 @@ export class ExtensionServer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
|
|
138
140
|
this.registerHandler(PrivateAPI.Commands.UpdateButton, this.onUpdateButton.bind(this));
|
139
141
|
this.registerHandler(
|
140
142
|
PrivateAPI.Commands.RegisterLanguageExtensionPlugin, this.registerLanguageExtensionEndpoint.bind(this));
|
143
|
+
this.registerHandler(
|
144
|
+
PrivateAPI.Commands.RegisterRecorderExtensionPlugin, this.registerRecorderExtensionEndpoint.bind(this));
|
141
145
|
window.addEventListener('message', this.onWindowMessage.bind(this), false); // Only for main window.
|
142
146
|
|
143
147
|
const existingTabId =
|
@@ -215,6 +219,16 @@ export class ExtensionServer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
|
|
215
219
|
return this.status.OK();
|
216
220
|
}
|
217
221
|
|
222
|
+
private registerRecorderExtensionEndpoint(
|
223
|
+
message: PrivateAPI.ExtensionServerRequestMessage, _shared_port: MessagePort): Record {
|
224
|
+
if (message.command !== PrivateAPI.Commands.RegisterRecorderExtensionPlugin) {
|
225
|
+
return this.status.E_BADARG('command', `expected ${PrivateAPI.Commands.Subscribe}`);
|
226
|
+
}
|
227
|
+
const {pluginName, port} = message;
|
228
|
+
RecorderPluginManager.instance().addPlugin(new RecorderExtensionEndpoint(pluginName, port));
|
229
|
+
return this.status.OK();
|
230
|
+
}
|
231
|
+
|
218
232
|
private inspectedURLChanged(event: Common.EventTarget.EventTargetEvent<SDK.Target.Target>): void {
|
219
233
|
if (!this.canInspectURL(event.data.inspectedURL())) {
|
220
234
|
this.disableExtensions();
|
@@ -856,6 +870,13 @@ export class ExtensionServer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
|
|
856
870
|
}
|
857
871
|
}
|
858
872
|
|
873
|
+
addExtensionForTest(extensionInfo: Host.InspectorFrontendHostAPI.ExtensionDescriptor, origin: string): boolean
|
874
|
+
|undefined {
|
875
|
+
const name = extensionInfo.name || `Extension ${origin}`;
|
876
|
+
this.registeredExtensions.set(origin, {name});
|
877
|
+
return true;
|
878
|
+
}
|
879
|
+
|
859
880
|
private addExtension(extensionInfo: Host.InspectorFrontendHostAPI.ExtensionDescriptor): boolean|undefined {
|
860
881
|
const startPage = extensionInfo.startPage;
|
861
882
|
|
@@ -5,9 +5,30 @@
|
|
5
5
|
import type * as SDK from '../../core/sdk/sdk.js';
|
6
6
|
import * as Bindings from '../bindings/bindings.js';
|
7
7
|
import type {Chrome} from '../../../extension-api/ExtensionAPI.js'; // eslint-disable-line rulesdir/es_modules_import
|
8
|
+
import {ExtensionEndpoint} from './ExtensionEndpoint.js';
|
8
9
|
|
9
10
|
import {PrivateAPI} from './ExtensionAPI.js';
|
10
11
|
|
12
|
+
class LanguageExtensionEndpointImpl extends ExtensionEndpoint {
|
13
|
+
private plugin: LanguageExtensionEndpoint;
|
14
|
+
constructor(plugin: LanguageExtensionEndpoint, port: MessagePort) {
|
15
|
+
super(port);
|
16
|
+
this.plugin = plugin;
|
17
|
+
}
|
18
|
+
protected handleEvent({event}: {event: string}): void {
|
19
|
+
switch (event) {
|
20
|
+
case PrivateAPI.LanguageExtensionPluginEvents.UnregisteredLanguageExtensionPlugin: {
|
21
|
+
this.disconnect();
|
22
|
+
const {pluginManager} = Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance();
|
23
|
+
if (pluginManager) {
|
24
|
+
pluginManager.removePlugin(this.plugin);
|
25
|
+
}
|
26
|
+
break;
|
27
|
+
}
|
28
|
+
}
|
29
|
+
}
|
30
|
+
}
|
31
|
+
|
11
32
|
export class LanguageExtensionEndpoint extends Bindings.DebuggerLanguagePlugins.DebuggerLanguagePlugin {
|
12
33
|
private readonly supportedScriptTypes: {
|
13
34
|
language: string,
|
@@ -15,13 +36,7 @@ export class LanguageExtensionEndpoint extends Bindings.DebuggerLanguagePlugins.
|
|
15
36
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
16
37
|
symbol_types: Array<string>,
|
17
38
|
};
|
18
|
-
private
|
19
|
-
private nextRequestId: number;
|
20
|
-
// TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
|
21
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
22
|
-
// TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
|
23
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
24
|
-
private pendingRequests: Map<any, any>;
|
39
|
+
private endpoint: LanguageExtensionEndpointImpl;
|
25
40
|
constructor(
|
26
41
|
name: string, supportedScriptTypes: {
|
27
42
|
language: string,
|
@@ -32,63 +47,7 @@ export class LanguageExtensionEndpoint extends Bindings.DebuggerLanguagePlugins.
|
|
32
47
|
port: MessagePort) {
|
33
48
|
super(name);
|
34
49
|
this.supportedScriptTypes = supportedScriptTypes;
|
35
|
-
this.
|
36
|
-
this.port.onmessage = this.onResponse.bind(this);
|
37
|
-
this.nextRequestId = 0;
|
38
|
-
this.pendingRequests = new Map();
|
39
|
-
}
|
40
|
-
|
41
|
-
// TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
|
42
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
43
|
-
// TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
|
44
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
45
|
-
private sendRequest(method: string, parameters: any): Promise<any> {
|
46
|
-
return new Promise((resolve, reject) => {
|
47
|
-
const requestId = this.nextRequestId++;
|
48
|
-
this.pendingRequests.set(requestId, {resolve, reject});
|
49
|
-
this.port.postMessage({requestId, method, parameters});
|
50
|
-
});
|
51
|
-
}
|
52
|
-
|
53
|
-
private onResponse({data}: MessageEvent<{
|
54
|
-
requestId: number,
|
55
|
-
// TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
|
56
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
57
|
-
result: any,
|
58
|
-
error: Error|null,
|
59
|
-
}|{
|
60
|
-
event: string,
|
61
|
-
}>): void {
|
62
|
-
if ('event' in data) {
|
63
|
-
const {event} = data;
|
64
|
-
switch (event) {
|
65
|
-
case PrivateAPI.LanguageExtensionPluginEvents.UnregisteredLanguageExtensionPlugin: {
|
66
|
-
for (const {reject} of this.pendingRequests.values()) {
|
67
|
-
reject(new Error('Language extension endpoint disconnected'));
|
68
|
-
}
|
69
|
-
this.pendingRequests.clear();
|
70
|
-
this.port.close();
|
71
|
-
const {pluginManager} = Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance();
|
72
|
-
if (pluginManager) {
|
73
|
-
pluginManager.removePlugin(this);
|
74
|
-
}
|
75
|
-
break;
|
76
|
-
}
|
77
|
-
}
|
78
|
-
return;
|
79
|
-
}
|
80
|
-
const {requestId, result, error} = data;
|
81
|
-
if (!this.pendingRequests.has(requestId)) {
|
82
|
-
console.error(`No pending request ${requestId}`);
|
83
|
-
return;
|
84
|
-
}
|
85
|
-
const {resolve, reject} = this.pendingRequests.get(requestId);
|
86
|
-
this.pendingRequests.delete(requestId);
|
87
|
-
if (error) {
|
88
|
-
reject(new Error(error.message));
|
89
|
-
} else {
|
90
|
-
resolve(result);
|
91
|
-
}
|
50
|
+
this.endpoint = new LanguageExtensionEndpointImpl(this, port);
|
92
51
|
}
|
93
52
|
|
94
53
|
handleScript(script: SDK.Script.Script): boolean {
|
@@ -100,7 +59,7 @@ export class LanguageExtensionEndpoint extends Bindings.DebuggerLanguagePlugins.
|
|
100
59
|
/** Notify the plugin about a new script
|
101
60
|
*/
|
102
61
|
addRawModule(rawModuleId: string, symbolsURL: string, rawModule: Chrome.DevTools.RawModule): Promise<string[]> {
|
103
|
-
return this.sendRequest(
|
62
|
+
return this.endpoint.sendRequest(
|
104
63
|
PrivateAPI.LanguageExtensionPluginCommands.AddRawModule, {rawModuleId, symbolsURL, rawModule}) as
|
105
64
|
Promise<string[]>;
|
106
65
|
}
|
@@ -109,33 +68,36 @@ export class LanguageExtensionEndpoint extends Bindings.DebuggerLanguagePlugins.
|
|
109
68
|
* Notifies the plugin that a script is removed.
|
110
69
|
*/
|
111
70
|
removeRawModule(rawModuleId: string): Promise<void> {
|
112
|
-
return this.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.RemoveRawModule, {rawModuleId}) as
|
71
|
+
return this.endpoint.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.RemoveRawModule, {rawModuleId}) as
|
72
|
+
Promise<void>;
|
113
73
|
}
|
114
74
|
|
115
75
|
/** Find locations in raw modules from a location in a source file
|
116
76
|
*/
|
117
77
|
sourceLocationToRawLocation(sourceLocation: Chrome.DevTools.SourceLocation):
|
118
78
|
Promise<Chrome.DevTools.RawLocationRange[]> {
|
119
|
-
return this.sendRequest(
|
79
|
+
return this.endpoint.sendRequest(
|
80
|
+
PrivateAPI.LanguageExtensionPluginCommands.SourceLocationToRawLocation, {sourceLocation}) as
|
120
81
|
Promise<Chrome.DevTools.RawLocationRange[]>;
|
121
82
|
}
|
122
83
|
|
123
84
|
/** Find locations in source files from a location in a raw module
|
124
85
|
*/
|
125
86
|
rawLocationToSourceLocation(rawLocation: Chrome.DevTools.RawLocation): Promise<Chrome.DevTools.SourceLocation[]> {
|
126
|
-
return this.sendRequest(
|
87
|
+
return this.endpoint.sendRequest(
|
88
|
+
PrivateAPI.LanguageExtensionPluginCommands.RawLocationToSourceLocation, {rawLocation}) as
|
127
89
|
Promise<Chrome.DevTools.SourceLocation[]>;
|
128
90
|
}
|
129
91
|
|
130
92
|
getScopeInfo(type: string): Promise<Chrome.DevTools.ScopeInfo> {
|
131
|
-
return this.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.GetScopeInfo, {type}) as
|
93
|
+
return this.endpoint.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.GetScopeInfo, {type}) as
|
132
94
|
Promise<Chrome.DevTools.ScopeInfo>;
|
133
95
|
}
|
134
96
|
|
135
97
|
/** List all variables in lexical scope at a given location in a raw module
|
136
98
|
*/
|
137
99
|
listVariablesInScope(rawLocation: Chrome.DevTools.RawLocation): Promise<Chrome.DevTools.Variable[]> {
|
138
|
-
return this.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.ListVariablesInScope, {rawLocation}) as
|
100
|
+
return this.endpoint.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.ListVariablesInScope, {rawLocation}) as
|
139
101
|
Promise<Chrome.DevTools.Variable[]>;
|
140
102
|
}
|
141
103
|
|
@@ -144,7 +106,8 @@ export class LanguageExtensionEndpoint extends Bindings.DebuggerLanguagePlugins.
|
|
144
106
|
getFunctionInfo(rawLocation: Chrome.DevTools.RawLocation): Promise<{
|
145
107
|
frames: Array<Chrome.DevTools.FunctionInfo>,
|
146
108
|
}> {
|
147
|
-
return this.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.GetFunctionInfo, {rawLocation}) as
|
109
|
+
return this.endpoint.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.GetFunctionInfo, {rawLocation}) as
|
110
|
+
Promise<{
|
148
111
|
frames: Array<Chrome.DevTools.FunctionInfo>,
|
149
112
|
}>;
|
150
113
|
}
|
@@ -153,7 +116,8 @@ export class LanguageExtensionEndpoint extends Bindings.DebuggerLanguagePlugins.
|
|
153
116
|
* that rawLocation is in.
|
154
117
|
*/
|
155
118
|
getInlinedFunctionRanges(rawLocation: Chrome.DevTools.RawLocation): Promise<Chrome.DevTools.RawLocationRange[]> {
|
156
|
-
return this.sendRequest(
|
119
|
+
return this.endpoint.sendRequest(
|
120
|
+
PrivateAPI.LanguageExtensionPluginCommands.GetInlinedFunctionRanges, {rawLocation}) as
|
157
121
|
Promise<Chrome.DevTools.RawLocationRange[]>;
|
158
122
|
}
|
159
123
|
|
@@ -161,7 +125,8 @@ export class LanguageExtensionEndpoint extends Bindings.DebuggerLanguagePlugins.
|
|
161
125
|
* called by the function or inline frame that rawLocation is in.
|
162
126
|
*/
|
163
127
|
getInlinedCalleesRanges(rawLocation: Chrome.DevTools.RawLocation): Promise<Chrome.DevTools.RawLocationRange[]> {
|
164
|
-
return this.sendRequest(
|
128
|
+
return this.endpoint.sendRequest(
|
129
|
+
PrivateAPI.LanguageExtensionPluginCommands.GetInlinedCalleesRanges, {rawLocation}) as
|
165
130
|
Promise<Chrome.DevTools.RawLocationRange[]>;
|
166
131
|
}
|
167
132
|
|
@@ -169,7 +134,8 @@ export class LanguageExtensionEndpoint extends Bindings.DebuggerLanguagePlugins.
|
|
169
134
|
typeInfos: Array<Chrome.DevTools.TypeInfo>,
|
170
135
|
base: Chrome.DevTools.EvalBase,
|
171
136
|
}|null> {
|
172
|
-
return this.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.GetTypeInfo, {expression, context}) as
|
137
|
+
return this.endpoint.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.GetTypeInfo, {expression, context}) as
|
138
|
+
Promise<{
|
173
139
|
typeInfos: Array<Chrome.DevTools.TypeInfo>,
|
174
140
|
base: Chrome.DevTools.EvalBase,
|
175
141
|
}|null>;
|
@@ -183,8 +149,8 @@ export class LanguageExtensionEndpoint extends Bindings.DebuggerLanguagePlugins.
|
|
183
149
|
context: Chrome.DevTools.RawLocation): Promise<{
|
184
150
|
js: string,
|
185
151
|
}> {
|
186
|
-
return this.sendRequest(
|
187
|
-
|
152
|
+
return this.endpoint.sendRequest(
|
153
|
+
PrivateAPI.LanguageExtensionPluginCommands.GetFormatter, {expressionOrField, context}) as Promise<{
|
188
154
|
js: string,
|
189
155
|
}>;
|
190
156
|
}
|
@@ -195,13 +161,15 @@ export class LanguageExtensionEndpoint extends Bindings.DebuggerLanguagePlugins.
|
|
195
161
|
}): Promise<{
|
196
162
|
js: string,
|
197
163
|
}> {
|
198
|
-
return this.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.GetInspectableAddress, {field}) as
|
164
|
+
return this.endpoint.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.GetInspectableAddress, {field}) as
|
165
|
+
Promise<{
|
199
166
|
js: string,
|
200
167
|
}>;
|
201
168
|
}
|
202
169
|
|
203
170
|
async getMappedLines(rawModuleId: string, sourceFileURL: string): Promise<number[]|undefined> {
|
204
|
-
return this.sendRequest(
|
171
|
+
return this.endpoint.sendRequest(
|
172
|
+
PrivateAPI.LanguageExtensionPluginCommands.GetMappedLines, {rawModuleId, sourceFileURL});
|
205
173
|
}
|
206
174
|
|
207
175
|
dispose(): void {
|
@@ -0,0 +1,43 @@
|
|
1
|
+
// Copyright 2022 The Chromium Authors. All rights reserved.
|
2
|
+
// Use of this source code is governed by a BSD-style license that can be
|
3
|
+
// found in the LICENSE file.
|
4
|
+
|
5
|
+
import {PrivateAPI} from './ExtensionAPI.js';
|
6
|
+
import {ExtensionEndpoint} from './ExtensionEndpoint.js';
|
7
|
+
import {RecorderPluginManager} from './RecorderPluginManager.js';
|
8
|
+
|
9
|
+
export class RecorderExtensionEndpoint extends ExtensionEndpoint {
|
10
|
+
private readonly name: string;
|
11
|
+
|
12
|
+
constructor(name: string, port: MessagePort) {
|
13
|
+
super(port);
|
14
|
+
this.name = name;
|
15
|
+
}
|
16
|
+
|
17
|
+
getName(): string {
|
18
|
+
return this.name;
|
19
|
+
}
|
20
|
+
|
21
|
+
protected handleEvent({event}: {event: string}): void {
|
22
|
+
switch (event) {
|
23
|
+
case PrivateAPI.RecorderExtensionPluginEvents.UnregisteredRecorderExtensionPlugin: {
|
24
|
+
this.disconnect();
|
25
|
+
RecorderPluginManager.instance().removePlugin(this);
|
26
|
+
break;
|
27
|
+
}
|
28
|
+
default:
|
29
|
+
throw new Error(`Unrecognized Recorder extension endpoint event: ${event}`);
|
30
|
+
}
|
31
|
+
}
|
32
|
+
|
33
|
+
/**
|
34
|
+
* In practice, `recording` is a UserFlow[1], but we avoid defining this type on the
|
35
|
+
* API in order to prevent dependencies between Chrome and puppeteer. Extensions
|
36
|
+
* are responsible for working out potential compatibility issues.
|
37
|
+
*
|
38
|
+
* [1]: https://github.com/puppeteer/replay/blob/main/src/Schema.ts#L245
|
39
|
+
*/
|
40
|
+
stringify(recording: Object): Promise<string> {
|
41
|
+
return this.sendRequest(PrivateAPI.RecorderExtensionPluginCommands.Stringify, {recording});
|
42
|
+
}
|
43
|
+
}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
// Copyright 2022 The Chromium Authors. All rights reserved.
|
2
|
+
// Use of this source code is governed by a BSD-style license that can be
|
3
|
+
// found in the LICENSE file.
|
4
|
+
|
5
|
+
import type {RecorderExtensionEndpoint} from './RecorderExtensionEndpoint.js';
|
6
|
+
|
7
|
+
let instance: RecorderPluginManager|null = null;
|
8
|
+
|
9
|
+
export class RecorderPluginManager {
|
10
|
+
#plugins: Set<RecorderExtensionEndpoint> = new Set();
|
11
|
+
|
12
|
+
static instance(): RecorderPluginManager {
|
13
|
+
if (!instance) {
|
14
|
+
instance = new RecorderPluginManager();
|
15
|
+
}
|
16
|
+
return instance;
|
17
|
+
}
|
18
|
+
|
19
|
+
addPlugin(plugin: RecorderExtensionEndpoint): void {
|
20
|
+
this.#plugins.add(plugin);
|
21
|
+
}
|
22
|
+
|
23
|
+
removePlugin(plugin: RecorderExtensionEndpoint): void {
|
24
|
+
this.#plugins.delete(plugin);
|
25
|
+
}
|
26
|
+
|
27
|
+
plugins(): RecorderExtensionEndpoint[] {
|
28
|
+
return Array.from(this.#plugins.values());
|
29
|
+
}
|
30
|
+
}
|
@@ -7,6 +7,7 @@ import * as ExtensionPanel from './ExtensionPanel.js';
|
|
7
7
|
import * as ExtensionServer from './ExtensionServer.js';
|
8
8
|
import * as ExtensionTraceProvider from './ExtensionTraceProvider.js';
|
9
9
|
import * as ExtensionView from './ExtensionView.js';
|
10
|
+
import * as RecorderPluginManager from './RecorderPluginManager.js';
|
10
11
|
|
11
12
|
export {
|
12
13
|
ExtensionAPI,
|
@@ -14,4 +15,5 @@ export {
|
|
14
15
|
ExtensionServer,
|
15
16
|
ExtensionTraceProvider,
|
16
17
|
ExtensionView,
|
18
|
+
RecorderPluginManager,
|
17
19
|
};
|
@@ -663,7 +663,8 @@ export class AppManifestView extends UI.Widget.VBox implements SDK.TargetManager
|
|
663
663
|
}
|
664
664
|
|
665
665
|
const userPreferences = parsedManifest['user_preferences'] || {};
|
666
|
-
const
|
666
|
+
const colorScheme = userPreferences['color_scheme'] || {};
|
667
|
+
const colorSchemeDark = colorScheme['dark'] || {};
|
667
668
|
const darkThemeColorString = colorSchemeDark['theme_color'];
|
668
669
|
const hasDarkThemeColor = typeof darkThemeColorString === 'string';
|
669
670
|
this.darkThemeColorField.parentElement?.classList.toggle('hidden', !hasDarkThemeColor);
|
@@ -53,6 +53,13 @@ const UIStrings = {
|
|
53
53
|
*/
|
54
54
|
sS: '{PH1}: {PH2}',
|
55
55
|
/**
|
56
|
+
*@description Text with three placeholders separated by a colon and a comma
|
57
|
+
*@example {Node removed} PH1
|
58
|
+
*@example {div#id1} PH2
|
59
|
+
*@example {checked} PH3
|
60
|
+
*/
|
61
|
+
sSS: '{PH1}: {PH2}, {PH3}',
|
62
|
+
/**
|
56
63
|
*@description Text exposed to screen readers on checked items.
|
57
64
|
*/
|
58
65
|
checked: 'checked',
|
@@ -196,19 +203,26 @@ export class DOMBreakpointsSidebarPane extends UI.Widget.VBox implements
|
|
196
203
|
description.textContent = breakpointTypeLabel ? breakpointTypeLabel() : null;
|
197
204
|
const breakpointTypeText = breakpointTypeLabel ? breakpointTypeLabel() : '';
|
198
205
|
UI.ARIAUtils.setAccessibleName(checkboxElement, breakpointTypeText);
|
206
|
+
const checkedStateText = item.enabled ? i18nString(UIStrings.checked) : i18nString(UIStrings.unchecked);
|
199
207
|
const linkifiedNode = document.createElement('monospace');
|
200
208
|
linkifiedNode.style.display = 'block';
|
201
209
|
labelElement.appendChild(linkifiedNode);
|
202
210
|
void Common.Linkifier.Linkifier.linkify(item.node, {preventKeyboardFocus: true, tooltip: undefined})
|
203
211
|
.then(linkified => {
|
204
212
|
linkifiedNode.appendChild(linkified);
|
213
|
+
// Give the checkbox an aria-label as it is required for all form element
|
205
214
|
UI.ARIAUtils.setAccessibleName(
|
206
215
|
checkboxElement, i18nString(UIStrings.sS, {PH1: breakpointTypeText, PH2: linkified.deepTextContent()}));
|
216
|
+
// The parent list element is the one that actually gets focused.
|
217
|
+
// Assign it an aria-label with complete information for the screen reader to read out properly
|
218
|
+
UI.ARIAUtils.setAccessibleName(
|
219
|
+
element,
|
220
|
+
i18nString(
|
221
|
+
UIStrings.sSS, {PH1: breakpointTypeText, PH2: linkified.deepTextContent(), PH3: checkedStateText}));
|
207
222
|
});
|
208
223
|
|
209
224
|
labelElement.appendChild(description);
|
210
225
|
|
211
|
-
const checkedStateText = item.enabled ? i18nString(UIStrings.checked) : i18nString(UIStrings.unchecked);
|
212
226
|
if (item === this.#highlightedBreakpoint) {
|
213
227
|
element.classList.add('breakpoint-hit');
|
214
228
|
UI.ARIAUtils.setDescription(element, i18nString(UIStrings.sBreakpointHit, {PH1: checkedStateText}));
|
@@ -146,14 +146,22 @@ const UIStrings = {
|
|
146
146
|
*/
|
147
147
|
desktop: 'Desktop',
|
148
148
|
/**
|
149
|
-
|
150
|
-
|
151
|
-
|
149
|
+
* @description Text for an option to select a throttling method.
|
150
|
+
*/
|
151
|
+
throttlingMethod: 'Throttling method',
|
152
152
|
/**
|
153
|
-
|
154
|
-
|
153
|
+
* @description Text for an option in a dropdown to use simulated throttling. This is the default setting.
|
154
|
+
*/
|
155
|
+
simulatedThrottling: 'Simulated throttling (default)',
|
156
|
+
/**
|
157
|
+
* @description Text for an option in a dropdown to use DevTools throttling. This option should only be used by advanced users.
|
158
|
+
*/
|
159
|
+
devtoolsThrottling: 'DevTools throttling (advanced)',
|
160
|
+
/**
|
161
|
+
* @description Tooltip text that appears when hovering over the 'Simulated Throttling' checkbox in the settings pane opened by clicking the setting cog in the start view of the audits panel
|
162
|
+
*/
|
155
163
|
simulateASlowerPageLoadBasedOn:
|
156
|
-
'
|
164
|
+
'Simulated throttling simulates a slower page load based on data from an initial unthrottled load. DevTools throttling actually slows down the page.',
|
157
165
|
/**
|
158
166
|
*@description Text of checkbox to reset storage features prior to running audits in Lighthouse
|
159
167
|
*/
|
@@ -515,17 +523,24 @@ export const RuntimeSettings: RuntimeSetting[] = [
|
|
515
523
|
{
|
516
524
|
// This setting is disabled, but we keep it around to show in the UI.
|
517
525
|
setting: Common.Settings.Settings.instance().createSetting(
|
518
|
-
'lighthouse.throttling',
|
519
|
-
title: i18nLazyString(UIStrings.
|
526
|
+
'lighthouse.throttling', 'simulated', Common.Settings.SettingStorageType.Synced),
|
527
|
+
title: i18nLazyString(UIStrings.throttlingMethod),
|
520
528
|
// We will disable this when we have a Lantern trace viewer within DevTools.
|
521
529
|
learnMore:
|
522
530
|
'https://github.com/GoogleChrome/lighthouse/blob/master/docs/throttling.md#devtools-lighthouse-panel-throttling' as
|
523
531
|
Platform.DevToolsPath.UrlString,
|
524
532
|
description: i18nLazyString(UIStrings.simulateASlowerPageLoadBasedOn),
|
525
533
|
setFlags: (flags: Flags, value: string|boolean): void => {
|
526
|
-
|
534
|
+
if (typeof value === 'string') {
|
535
|
+
flags.throttlingMethod = value;
|
536
|
+
} else {
|
537
|
+
flags.throttlingMethod = value ? 'simulate' : 'devtools';
|
538
|
+
}
|
527
539
|
},
|
528
|
-
options:
|
540
|
+
options: [
|
541
|
+
{label: i18nLazyString(UIStrings.simulatedThrottling), value: 'simulate'},
|
542
|
+
{label: i18nLazyString(UIStrings.devtoolsThrottling), value: 'devtools'},
|
543
|
+
],
|
529
544
|
},
|
530
545
|
{
|
531
546
|
setting: Common.Settings.Settings.instance().createSetting(
|
@@ -103,6 +103,30 @@ export class StartView extends UI.Widget.Widget {
|
|
103
103
|
}
|
104
104
|
}
|
105
105
|
|
106
|
+
protected populateRuntimeSettingAsToolbarDropdown(settingName: string, toolbar: UI.Toolbar.Toolbar): void {
|
107
|
+
const runtimeSetting = RuntimeSettings.find(item => item.setting.name === settingName);
|
108
|
+
if (!runtimeSetting || !runtimeSetting.title) {
|
109
|
+
throw new Error(`${settingName} is not a setting with a title`);
|
110
|
+
}
|
111
|
+
|
112
|
+
const options = runtimeSetting.options?.map(option => ({label: option.label(), value: option.value})) || [];
|
113
|
+
|
114
|
+
runtimeSetting.setting.setTitle(runtimeSetting.title());
|
115
|
+
const control = new UI.Toolbar.ToolbarSettingComboBox(
|
116
|
+
options,
|
117
|
+
runtimeSetting.setting as Common.Settings.Setting<string>,
|
118
|
+
runtimeSetting.title(),
|
119
|
+
);
|
120
|
+
control.setTitle(runtimeSetting.description());
|
121
|
+
toolbar.appendToolbarItem(control);
|
122
|
+
if (runtimeSetting.learnMore) {
|
123
|
+
const link =
|
124
|
+
UI.XLink.XLink.create(runtimeSetting.learnMore, i18nString(UIStrings.learnMore), 'lighthouse-learn-more');
|
125
|
+
link.style.padding = '5px';
|
126
|
+
control.element.appendChild(link);
|
127
|
+
}
|
128
|
+
}
|
129
|
+
|
106
130
|
protected populateFormControls(fragment: UI.Fragment.Fragment, mode?: string): void {
|
107
131
|
// Populate the device type
|
108
132
|
const deviceTypeFormElements = fragment.$('device-type-form-elements');
|
@@ -134,7 +158,7 @@ export class StartView extends UI.Widget.Widget {
|
|
134
158
|
protected render(): void {
|
135
159
|
this.populateRuntimeSettingAsToolbarCheckbox('lighthouse.legacy_navigation', this.settingsToolbarInternal);
|
136
160
|
this.populateRuntimeSettingAsToolbarCheckbox('lighthouse.clear_storage', this.settingsToolbarInternal);
|
137
|
-
this.
|
161
|
+
this.populateRuntimeSettingAsToolbarDropdown('lighthouse.throttling', this.settingsToolbarInternal);
|
138
162
|
|
139
163
|
this.startButton = UI.UIUtils.createTextButton(
|
140
164
|
i18nString(UIStrings.generateReport),
|
@@ -49,7 +49,7 @@ export class StartViewFR extends StartView {
|
|
49
49
|
protected render(): void {
|
50
50
|
this.populateRuntimeSettingAsToolbarCheckbox('lighthouse.legacy_navigation', this.settingsToolbarInternal);
|
51
51
|
this.populateRuntimeSettingAsToolbarCheckbox('lighthouse.clear_storage', this.settingsToolbarInternal);
|
52
|
-
this.
|
52
|
+
this.populateRuntimeSettingAsToolbarDropdown('lighthouse.throttling', this.settingsToolbarInternal);
|
53
53
|
|
54
54
|
const {mode} = this.controller.getFlags();
|
55
55
|
this.populateStartButton(mode);
|
@@ -52,6 +52,10 @@ details summary input {
|
|
52
52
|
user-select: text;
|
53
53
|
}
|
54
54
|
|
55
|
+
div.raw-headers-row {
|
56
|
+
display: block;
|
57
|
+
}
|
58
|
+
|
55
59
|
.row:first-of-type {
|
56
60
|
margin-top: 2px;
|
57
61
|
}
|
@@ -103,4 +107,5 @@ details summary input {
|
|
103
107
|
font-family: var(--source-code-font-family);
|
104
108
|
font-size: var(--source-code-font-size);
|
105
109
|
white-space: pre-wrap;
|
110
|
+
word-break: break-all;
|
106
111
|
}
|
@@ -6,12 +6,14 @@ import * as Common from '../../../core/common/common.js';
|
|
6
6
|
import * as i18n from '../../../core/i18n/i18n.js';
|
7
7
|
import {assertNotNullOrUndefined} from '../../../core/platform/platform.js';
|
8
8
|
import * as SDK from '../../../core/sdk/sdk.js';
|
9
|
+
import * as Buttons from '../../../ui/components/buttons/buttons.js';
|
9
10
|
import * as ComponentHelpers from '../../../ui/components/helpers/helpers.js';
|
10
11
|
import * as UI from '../../../ui/legacy/legacy.js';
|
11
12
|
import * as LitHtml from '../../../ui/lit-html/lit-html.js';
|
12
13
|
|
13
14
|
import requestHeadersViewStyles from './RequestHeadersView.css.js';
|
14
15
|
|
16
|
+
const RAW_HEADER_CUTOFF = 3000;
|
15
17
|
const {render, html} = LitHtml;
|
16
18
|
|
17
19
|
const UIStrings = {
|
@@ -64,6 +66,10 @@ const UIStrings = {
|
|
64
66
|
*/
|
65
67
|
responseHeaders: 'Response Headers',
|
66
68
|
/**
|
69
|
+
*@description Text to show more content
|
70
|
+
*/
|
71
|
+
showMore: 'Show more',
|
72
|
+
/**
|
67
73
|
*@description HTTP response code
|
68
74
|
*/
|
69
75
|
statusCode: 'Status Code',
|
@@ -117,6 +123,8 @@ export class RequestHeadersComponent extends HTMLElement {
|
|
117
123
|
#request?: Readonly<SDK.NetworkRequest.NetworkRequest>;
|
118
124
|
#showResponseHeadersText = false;
|
119
125
|
#showRequestHeadersText = false;
|
126
|
+
#showResponseHeadersTextFull = false;
|
127
|
+
#showRequestHeadersTextFull = false;
|
120
128
|
|
121
129
|
set data(data: RequestHeadersComponentData) {
|
122
130
|
this.#request = data.request;
|
@@ -161,9 +169,8 @@ export class RequestHeadersComponent extends HTMLElement {
|
|
161
169
|
} as CategoryData}
|
162
170
|
aria-label=${i18nString(UIStrings.responseHeaders)}
|
163
171
|
>
|
164
|
-
${this.#showResponseHeadersText ?
|
165
|
-
|
166
|
-
` : html`
|
172
|
+
${this.#showResponseHeadersText ?
|
173
|
+
this.#renderRawHeaders(this.#request.responseHeadersText, true) : html`
|
167
174
|
${this.#request.sortedResponseHeaders.map(header => html`
|
168
175
|
<div class="row">
|
169
176
|
<div class="header-name">${header.name}:</div>
|
@@ -198,9 +205,8 @@ export class RequestHeadersComponent extends HTMLElement {
|
|
198
205
|
} as CategoryData}
|
199
206
|
aria-label=${i18nString(UIStrings.requestHeaders)}
|
200
207
|
>
|
201
|
-
${(this.#showRequestHeadersText && requestHeadersText) ?
|
202
|
-
|
203
|
-
` : html`
|
208
|
+
${(this.#showRequestHeadersText && requestHeadersText) ?
|
209
|
+
this.#renderRawHeaders(requestHeadersText, false) : html`
|
204
210
|
${this.#request.requestHeaders().map(header => html`
|
205
211
|
<div class="row">
|
206
212
|
<div class="header-name">${header.name}:</div>
|
@@ -212,6 +218,50 @@ export class RequestHeadersComponent extends HTMLElement {
|
|
212
218
|
`;
|
213
219
|
}
|
214
220
|
|
221
|
+
#renderRawHeaders(rawHeadersText: string, forResponseHeaders: boolean): LitHtml.TemplateResult {
|
222
|
+
const trimmed = rawHeadersText.trim();
|
223
|
+
const showFull = forResponseHeaders ? this.#showResponseHeadersTextFull : this.#showRequestHeadersTextFull;
|
224
|
+
const isShortened = !showFull && trimmed.length > RAW_HEADER_CUTOFF;
|
225
|
+
|
226
|
+
const showMore = ():void => {
|
227
|
+
if (forResponseHeaders) {
|
228
|
+
this.#showResponseHeadersTextFull = true;
|
229
|
+
} else {
|
230
|
+
this.#showRequestHeadersTextFull = true;
|
231
|
+
}
|
232
|
+
this.#render();
|
233
|
+
};
|
234
|
+
|
235
|
+
const onContextMenuOpen = (event: Event): void => {
|
236
|
+
const showFull = forResponseHeaders ? this.#showResponseHeadersTextFull : this.#showRequestHeadersTextFull;
|
237
|
+
if (!showFull) {
|
238
|
+
const contextMenu = new UI.ContextMenu.ContextMenu(event);
|
239
|
+
const section = contextMenu.newSection();
|
240
|
+
section.appendItem(i18nString(UIStrings.showMore), showMore);
|
241
|
+
void contextMenu.show();
|
242
|
+
}
|
243
|
+
};
|
244
|
+
|
245
|
+
const addContextMenuListener = (el: Element):void => {
|
246
|
+
if (isShortened) {
|
247
|
+
el.addEventListener('contextmenu', onContextMenuOpen);
|
248
|
+
}
|
249
|
+
};
|
250
|
+
|
251
|
+
return html`
|
252
|
+
<div class="row raw-headers-row" on-render=${ComponentHelpers.Directives.nodeRenderedCallback(addContextMenuListener)}>
|
253
|
+
<div class="raw-headers">${isShortened ? trimmed.substring(0, RAW_HEADER_CUTOFF) : trimmed}</div>
|
254
|
+
${isShortened ? html`
|
255
|
+
<${Buttons.Button.Button.litTagName}
|
256
|
+
.size=${Buttons.Button.Size.SMALL}
|
257
|
+
.variant=${Buttons.Button.Variant.SECONDARY}
|
258
|
+
@click=${showMore}
|
259
|
+
>${i18nString(UIStrings.showMore)}</${Buttons.Button.Button.litTagName}>
|
260
|
+
` : LitHtml.nothing}
|
261
|
+
</div>
|
262
|
+
`;
|
263
|
+
}
|
264
|
+
|
215
265
|
#renderGeneralSection(): LitHtml.TemplateResult {
|
216
266
|
assertNotNullOrUndefined(this.#request);
|
217
267
|
|
package/package.json
CHANGED