chrome-devtools-frontend 1.0.999791 → 1.0.1000713
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/front_end/.eslintrc.js +4 -4
- package/front_end/core/common/ParsedURL.ts +3 -3
- package/front_end/core/i18n/locales/en-US.json +6 -0
- package/front_end/core/i18n/locales/en-XL.json +6 -0
- package/front_end/models/extensions/ExtensionAPI.ts +4 -4
- package/front_end/models/extensions/ExtensionTraceProvider.ts +2 -1
- package/front_end/models/issues_manager/DeprecationIssue.ts +7 -2
- package/front_end/models/workspace/WorkspaceImpl.ts +1 -1
- package/front_end/panels/lighthouse/LighthouseController.ts +15 -0
- package/front_end/panels/network/NetworkItemView.ts +1 -1
- package/front_end/panels/sources/AddSourceMapURLDialog.ts +5 -4
- package/front_end/panels/sources/DebuggerPausedMessage.ts +2 -1
- package/front_end/panels/sources/DebuggerPlugin.ts +2 -2
- package/front_end/panels/sources/EditingLocationHistoryManager.ts +2 -4
- package/front_end/panels/sources/NavigatorView.ts +4 -5
- package/front_end/panels/sources/ScriptFormatterEditorAction.ts +2 -1
- package/front_end/panels/sources/TabbedEditorContainer.ts +14 -13
- package/front_end/panels/timeline/ExtensionTracingSession.ts +2 -1
- package/front_end/panels/timeline/TimelineFlameChartView.ts +1 -1
- package/front_end/panels/timeline/TimelineLoader.ts +2 -1
- package/front_end/panels/timeline/TimelinePanel.ts +3 -3
- package/front_end/panels/timeline/TimelineTreeView.ts +4 -4
- package/front_end/panels/timeline/TimelineUIUtils.ts +3 -3
- package/front_end/panels/webauthn/WebauthnPane.ts +49 -15
- package/package.json +1 -1
- package/scripts/eslint_rules/lib/enforce_bound_render_for_schedule_render.js +110 -0
- package/scripts/eslint_rules/tests/enforce_bound_render_for_schedule_render_test.js +74 -0
package/front_end/.eslintrc.js
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
// Use of this source code is governed by a BSD-style license that can be
|
3
3
|
// found in the LICENSE file.
|
4
4
|
|
5
|
+
// clang-format off
|
5
6
|
const path = require('path');
|
6
7
|
const rulesDirPlugin = require('eslint-plugin-rulesdir');
|
7
8
|
rulesDirPlugin.RULES_DIR = path.join(__dirname, '..', 'scripts', 'eslint_rules', 'lib');
|
@@ -28,6 +29,7 @@ module.exports = {
|
|
28
29
|
'rules': {
|
29
30
|
'@typescript-eslint/explicit-function-return-type': 2,
|
30
31
|
'rulesdir/no_importing_images_from_src': 2,
|
32
|
+
'rulesdir/enforce_bound_render_for_schedule_render': 2,
|
31
33
|
'rulesdir/enforce_custom_event_names': 2,
|
32
34
|
'rulesdir/set_data_type_reference': 2,
|
33
35
|
'rulesdir/no_bound_component_methods': 2,
|
@@ -120,10 +122,7 @@ module.exports = {
|
|
120
122
|
},
|
121
123
|
{
|
122
124
|
// Ignore type properties that require quotes
|
123
|
-
'selector': [
|
124
|
-
'typeProperty',
|
125
|
-
'enumMember'
|
126
|
-
],
|
125
|
+
'selector': ['typeProperty', 'enumMember'],
|
127
126
|
'format': null,
|
128
127
|
'modifiers': ['requiresQuotes']
|
129
128
|
}
|
@@ -150,3 +149,4 @@ module.exports = {
|
|
150
149
|
}
|
151
150
|
]
|
152
151
|
};
|
152
|
+
// clang-format on
|
@@ -153,10 +153,10 @@ export class ParsedURL {
|
|
153
153
|
return null;
|
154
154
|
}
|
155
155
|
|
156
|
-
|
156
|
+
static preEncodeSpecialCharactersInPath(path: string): string {
|
157
157
|
// Based on net::FilePathToFileURL. Ideally we would handle
|
158
158
|
// '\\' as well on non-Windows file systems.
|
159
|
-
for (const specialChar of ['%', ';', '#', '?']) {
|
159
|
+
for (const specialChar of ['%', ';', '#', '?', ' ']) {
|
160
160
|
(path as string) = path.replaceAll(specialChar, encodeURIComponent(specialChar));
|
161
161
|
}
|
162
162
|
return path;
|
@@ -185,7 +185,7 @@ export class ParsedURL {
|
|
185
185
|
*/
|
186
186
|
static urlFromParentUrlAndName(parentUrl: Platform.DevToolsPath.UrlString, name: string):
|
187
187
|
Platform.DevToolsPath.UrlString {
|
188
|
-
return ParsedURL.concatenate(parentUrl, '/',
|
188
|
+
return ParsedURL.concatenate(parentUrl, '/', ParsedURL.preEncodeSpecialCharactersInPath(name));
|
189
189
|
}
|
190
190
|
|
191
191
|
static encodedPathToRawPathString(encPath: Platform.DevToolsPath.EncodedPathString):
|
@@ -5822,6 +5822,9 @@
|
|
5822
5822
|
"panels/lighthouse/LighthouseController.ts | isThisPageUsableByPeopleWith": {
|
5823
5823
|
"message": "Is this page usable by people with disabilities or impairments"
|
5824
5824
|
},
|
5825
|
+
"panels/lighthouse/LighthouseController.ts | javaScriptDisabled": {
|
5826
|
+
"message": "JavaScript is disabled. You need to enable JavaScript to audit this page. Open the Command Menu and run the Enable JavaScript command to enable JavaScript."
|
5827
|
+
},
|
5825
5828
|
"panels/lighthouse/LighthouseController.ts | legacyNavigation": {
|
5826
5829
|
"message": "Legacy navigation"
|
5827
5830
|
},
|
@@ -11528,6 +11531,9 @@
|
|
11528
11531
|
"panels/webauthn/WebauthnPane.ts | signCount": {
|
11529
11532
|
"message": "Signature Count"
|
11530
11533
|
},
|
11534
|
+
"panels/webauthn/WebauthnPane.ts | supportsLargeBlob": {
|
11535
|
+
"message": "Supports large blob"
|
11536
|
+
},
|
11531
11537
|
"panels/webauthn/WebauthnPane.ts | supportsResidentKeys": {
|
11532
11538
|
"message": "Supports resident keys"
|
11533
11539
|
},
|
@@ -5822,6 +5822,9 @@
|
|
5822
5822
|
"panels/lighthouse/LighthouseController.ts | isThisPageUsableByPeopleWith": {
|
5823
5823
|
"message": "Îś t̂h́îś p̂áĝé ûśâb́l̂é b̂ý p̂éôṕl̂é ŵít̂h́ d̂íŝáb̂íl̂ít̂íêś ôŕ îḿp̂áîŕm̂én̂t́ŝ"
|
5824
5824
|
},
|
5825
|
+
"panels/lighthouse/LighthouseController.ts | javaScriptDisabled": {
|
5826
|
+
"message": "Ĵáv̂áŜćr̂íp̂t́ îś d̂íŝáb̂ĺêd́. Ŷóû ńêéd̂ t́ô én̂áb̂ĺê J́âv́âŚĉŕîṕt̂ t́ô áûd́ît́ t̂h́îś p̂áĝé. Ôṕêń t̂h́ê Ćôḿm̂án̂d́ M̂én̂ú âńd̂ ŕûń t̂h́ê Én̂áb̂ĺê J́âv́âŚĉŕîṕt̂ ćôḿm̂án̂d́ t̂ó êńâb́l̂é Ĵáv̂áŜćr̂íp̂t́."
|
5827
|
+
},
|
5825
5828
|
"panels/lighthouse/LighthouseController.ts | legacyNavigation": {
|
5826
5829
|
"message": "L̂éĝáĉý n̂áv̂íĝát̂íôń"
|
5827
5830
|
},
|
@@ -11528,6 +11531,9 @@
|
|
11528
11531
|
"panels/webauthn/WebauthnPane.ts | signCount": {
|
11529
11532
|
"message": "Ŝíĝńât́ûŕê Ćôún̂t́"
|
11530
11533
|
},
|
11534
|
+
"panels/webauthn/WebauthnPane.ts | supportsLargeBlob": {
|
11535
|
+
"message": "Ŝúp̂ṕôŕt̂ś l̂ár̂ǵê b́l̂ób̂"
|
11536
|
+
},
|
11531
11537
|
"panels/webauthn/WebauthnPane.ts | supportsResidentKeys": {
|
11532
11538
|
"message": "Ŝúp̂ṕôŕt̂ś r̂éŝíd̂én̂t́ k̂éŷś"
|
11533
11539
|
},
|
@@ -28,7 +28,7 @@
|
|
28
28
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
29
29
|
*/
|
30
30
|
|
31
|
-
import
|
31
|
+
import * as Platform from '../../core/platform/platform.js';
|
32
32
|
import type * as PublicAPI from '../../../extension-api/ExtensionAPI'; // eslint-disable-line rulesdir/es_modules_import
|
33
33
|
import type * as HAR from '../har/har.js';
|
34
34
|
|
@@ -141,7 +141,7 @@ export namespace PrivateAPI {
|
|
141
141
|
type UpdateButtonRequest =
|
142
142
|
{command: Commands.UpdateButton, id: string, icon?: string, tooltip?: string, disabled?: boolean};
|
143
143
|
type CompleteTraceSessionRequest =
|
144
|
-
{command: Commands.CompleteTraceSession, id: string, url:
|
144
|
+
{command: Commands.CompleteTraceSession, id: string, url: Platform.DevToolsPath.UrlString, timeOffset: number};
|
145
145
|
type CreateSidebarPaneRequest = {command: Commands.CreateSidebarPane, id: string, panel: string, title: string};
|
146
146
|
type SetSidebarHeightRequest = {command: Commands.SetSidebarHeight, id: string, height: string};
|
147
147
|
type SetSidebarContentRequest = {
|
@@ -930,11 +930,11 @@ self.injectedExtensionAPI = function(
|
|
930
930
|
}
|
931
931
|
|
932
932
|
(TraceSessionImpl.prototype as Pick<APIImpl.TraceSession, 'complete'>) = {
|
933
|
-
complete: function(this: APIImpl.TraceSession, url?:
|
933
|
+
complete: function(this: APIImpl.TraceSession, url?: Platform.DevToolsPath.UrlString, timeOffset?: number): void {
|
934
934
|
extensionServer.sendRequest({
|
935
935
|
command: PrivateAPI.Commands.CompleteTraceSession,
|
936
936
|
id: this._id,
|
937
|
-
url: url ||
|
937
|
+
url: url || Platform.DevToolsPath.EmptyUrlString,
|
938
938
|
timeOffset: timeOffset || 0,
|
939
939
|
});
|
940
940
|
},
|
@@ -3,6 +3,7 @@
|
|
3
3
|
// found in the LICENSE file.
|
4
4
|
|
5
5
|
import {ExtensionServer} from './ExtensionServer.js';
|
6
|
+
import type * as Platform from '../../core/platform/platform.js';
|
6
7
|
|
7
8
|
export class ExtensionTraceProvider {
|
8
9
|
private readonly extensionOrigin: string;
|
@@ -43,5 +44,5 @@ export class ExtensionTraceProvider {
|
|
43
44
|
let _lastSessionId = 0;
|
44
45
|
|
45
46
|
export interface TracingSession {
|
46
|
-
complete(url:
|
47
|
+
complete(url: Platform.DevToolsPath.UrlString, timeOffsetMicroseconds: number): void;
|
47
48
|
}
|
@@ -327,8 +327,13 @@ const
|
|
327
327
|
xmlHttpRequestSynchronousInNonWorkerOutsideBeforeUnload:
|
328
328
|
'Synchronous `XMLHttpRequest` on the main thread is deprecated because of its detrimental effects to the end user\u2019s experience. For more help, check https://xhr.spec.whatwg.org/.',
|
329
329
|
/**
|
330
|
-
|
331
|
-
|
330
|
+
*@description Warning displayed to developers that instead of using
|
331
|
+
* `supportsSession()`, which returns a promise that resolves if
|
332
|
+
* the XR session can be supported and rejects if not, they should
|
333
|
+
* use `isSessionSupported()` which will return a promise which
|
334
|
+
* resolves to a boolean indicating if the XR session can be
|
335
|
+
* supported or not, but may reject to throw an exception.
|
336
|
+
*/
|
332
337
|
xrSupportsSession:
|
333
338
|
'`supportsSession()` is deprecated. Please use `isSessionSupported()` and check the resolved boolean value instead.',
|
334
339
|
};
|
@@ -183,7 +183,7 @@ export abstract class ProjectStore implements Project {
|
|
183
183
|
const oldPath = uiSourceCode.url();
|
184
184
|
const newPath = uiSourceCode.parentURL() ?
|
185
185
|
Common.ParsedURL.ParsedURL.urlFromParentUrlAndName(uiSourceCode.parentURL(), newName) :
|
186
|
-
|
186
|
+
Common.ParsedURL.ParsedURL.preEncodeSpecialCharactersInPath(newName) as Platform.DevToolsPath.UrlString;
|
187
187
|
const value = this.uiSourceCodesMap.get(oldPath) as {
|
188
188
|
uiSourceCode: UISourceCode,
|
189
189
|
index: number,
|
@@ -171,6 +171,11 @@ const UIStrings = {
|
|
171
171
|
*/
|
172
172
|
resetStorageLocalstorage:
|
173
173
|
'Reset storage (`cache`, `service workers`, etc) before auditing. (Good for performance & `PWA` testing)',
|
174
|
+
/**
|
175
|
+
*@description Explanation for user that Ligthhouse can only audit when JavaScript is enabled
|
176
|
+
*/
|
177
|
+
javaScriptDisabled:
|
178
|
+
'JavaScript is disabled. You need to enable JavaScript to audit this page. Open the Command Menu and run the Enable JavaScript command to enable JavaScript.',
|
174
179
|
};
|
175
180
|
const str_ = i18n.i18n.registerUIStrings('panels/lighthouse/LighthouseController.ts', UIStrings);
|
176
181
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
@@ -196,6 +201,9 @@ export class LighthouseController extends Common.ObjectWrapper.ObjectWrapper<Eve
|
|
196
201
|
runtimeSetting.setting.addChangeListener(this.recomputePageAuditability.bind(this));
|
197
202
|
}
|
198
203
|
|
204
|
+
const javaScriptDisabledSetting = Common.Settings.Settings.instance().moduleSetting('javaScriptDisabled');
|
205
|
+
javaScriptDisabledSetting.addChangeListener(this.recomputePageAuditability.bind(this));
|
206
|
+
|
199
207
|
SDK.TargetManager.TargetManager.instance().observeModels(SDK.ServiceWorkerManager.ServiceWorkerManager, this);
|
200
208
|
SDK.TargetManager.TargetManager.instance().addEventListener(
|
201
209
|
SDK.TargetManager.Events.InspectedURLChanged, this.recomputePageAuditability, this);
|
@@ -273,6 +281,10 @@ export class LighthouseController extends Common.ObjectWrapper.ObjectWrapper<Eve
|
|
273
281
|
return null;
|
274
282
|
}
|
275
283
|
|
284
|
+
private javaScriptDisabled(): boolean {
|
285
|
+
return Common.Settings.Settings.instance().moduleSetting('javaScriptDisabled').get();
|
286
|
+
}
|
287
|
+
|
276
288
|
private async hasImportantResourcesNotCleared(): Promise<string> {
|
277
289
|
const clearStorageSetting =
|
278
290
|
RuntimeSettings.find(runtimeSetting => runtimeSetting.setting.name === 'lighthouse.clear_storage');
|
@@ -363,6 +375,7 @@ export class LighthouseController extends Common.ObjectWrapper.ObjectWrapper<Eve
|
|
363
375
|
const hasActiveServiceWorker = this.hasActiveServiceWorker();
|
364
376
|
const hasAtLeastOneCategory = this.hasAtLeastOneCategory();
|
365
377
|
const unauditablePageMessage = this.unauditablePageMessage();
|
378
|
+
const javaScriptDisabled = this.javaScriptDisabled();
|
366
379
|
|
367
380
|
let helpText = '';
|
368
381
|
if (hasActiveServiceWorker) {
|
@@ -371,6 +384,8 @@ export class LighthouseController extends Common.ObjectWrapper.ObjectWrapper<Eve
|
|
371
384
|
helpText = i18nString(UIStrings.atLeastOneCategoryMustBeSelected);
|
372
385
|
} else if (unauditablePageMessage) {
|
373
386
|
helpText = unauditablePageMessage;
|
387
|
+
} else if (javaScriptDisabled) {
|
388
|
+
helpText = i18nString(UIStrings.javaScriptDisabled);
|
374
389
|
}
|
375
390
|
|
376
391
|
this.dispatchEventToListeners(Events.PageAuditabilityChanged, {helpText});
|
@@ -143,7 +143,7 @@ export class NetworkItemView extends UI.TabbedPane.TabbedPane {
|
|
143
143
|
this.element.classList.add('network-item-view');
|
144
144
|
|
145
145
|
this.resourceViewTabSetting = Common.Settings.Settings.instance().createSetting(
|
146
|
-
'resourceViewTab', NetworkForward.UIRequestLocation.UIRequestTabs.
|
146
|
+
'resourceViewTab', NetworkForward.UIRequestLocation.UIRequestTabs.Headers);
|
147
147
|
|
148
148
|
this.headersView = new RequestHeadersView(request);
|
149
149
|
this.appendTab(
|
@@ -3,6 +3,7 @@
|
|
3
3
|
// found in the LICENSE file.
|
4
4
|
|
5
5
|
import * as i18n from '../../core/i18n/i18n.js';
|
6
|
+
import type * as Platform from '../../core/platform/platform.js';
|
6
7
|
import * as UI from '../../ui/legacy/legacy.js';
|
7
8
|
|
8
9
|
import dialogStyles from './dialog.css.js';
|
@@ -22,8 +23,8 @@ const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
22
23
|
export class AddSourceMapURLDialog extends UI.Widget.HBox {
|
23
24
|
private readonly input: HTMLInputElement;
|
24
25
|
private readonly dialog: UI.Dialog.Dialog;
|
25
|
-
private readonly callback: (arg0:
|
26
|
-
constructor(callback: (arg0:
|
26
|
+
private readonly callback: (arg0: Platform.DevToolsPath.UrlString) => void;
|
27
|
+
constructor(callback: (arg0: Platform.DevToolsPath.UrlString) => void) {
|
27
28
|
super(/* isWebComponent */ true);
|
28
29
|
|
29
30
|
this.contentElement.createChild('label').textContent = i18nString(UIStrings.sourceMapUrl);
|
@@ -51,13 +52,13 @@ export class AddSourceMapURLDialog extends UI.Widget.HBox {
|
|
51
52
|
this.dialog.show();
|
52
53
|
}
|
53
54
|
|
54
|
-
private done(value:
|
55
|
+
private done(value: Platform.DevToolsPath.UrlString): void {
|
55
56
|
this.dialog.hide();
|
56
57
|
this.callback(value);
|
57
58
|
}
|
58
59
|
|
59
60
|
private apply(): void {
|
60
|
-
this.done(this.input.value);
|
61
|
+
this.done(this.input.value as Platform.DevToolsPath.UrlString);
|
61
62
|
}
|
62
63
|
|
63
64
|
private onKeyDown(event: KeyboardEvent): void {
|
@@ -4,6 +4,7 @@
|
|
4
4
|
|
5
5
|
import * as Common from '../../core/common/common.js';
|
6
6
|
import * as i18n from '../../core/i18n/i18n.js';
|
7
|
+
import type * as Platform from '../../core/platform/platform.js';
|
7
8
|
import * as SDK from '../../core/sdk/sdk.js';
|
8
9
|
|
9
10
|
import debuggerPausedMessageStyles from './debuggerPausedMessage.css.js';
|
@@ -276,6 +277,6 @@ export const BreakpointTypeNouns = new Map([
|
|
276
277
|
]);
|
277
278
|
interface PausedDetailsAuxData {
|
278
279
|
description?: string;
|
279
|
-
url?:
|
280
|
+
url?: Platform.DevToolsPath.UrlString;
|
280
281
|
value?: string;
|
281
282
|
}
|
@@ -472,11 +472,11 @@ export class DebuggerPlugin extends Plugin {
|
|
472
472
|
}
|
473
473
|
|
474
474
|
function addSourceMapURLDialogCallback(
|
475
|
-
scriptFile: Bindings.ResourceScriptMapping.ResourceScriptFile, url:
|
475
|
+
scriptFile: Bindings.ResourceScriptMapping.ResourceScriptFile, url: Platform.DevToolsPath.UrlString): void {
|
476
476
|
if (!url) {
|
477
477
|
return;
|
478
478
|
}
|
479
|
-
scriptFile.addSourceMapURL(url
|
479
|
+
scriptFile.addSourceMapURL(url);
|
480
480
|
}
|
481
481
|
|
482
482
|
if (this.uiSourceCode.project().type() === Workspace.Workspace.projectTypes.Network &&
|
@@ -93,9 +93,7 @@ export class EditingLocationHistoryManager {
|
|
93
93
|
}
|
94
94
|
|
95
95
|
private reveal(entry: EditingLocationHistoryEntry): void {
|
96
|
-
|
97
|
-
const uiSourceCode = Workspace.Workspace.WorkspaceImpl.instance().uiSourceCode(
|
98
|
-
entry.projectId, entry.url as Platform.DevToolsPath.UrlString);
|
96
|
+
const uiSourceCode = Workspace.Workspace.WorkspaceImpl.instance().uiSourceCode(entry.projectId, entry.url);
|
99
97
|
if (uiSourceCode) {
|
100
98
|
this.revealing = true;
|
101
99
|
this.sourcesView.showSourceLocation(uiSourceCode, entry.position, false, true);
|
@@ -131,7 +129,7 @@ export class EditingLocationHistoryManager {
|
|
131
129
|
|
132
130
|
class EditingLocationHistoryEntry {
|
133
131
|
readonly projectId: string;
|
134
|
-
readonly url:
|
132
|
+
readonly url: Platform.DevToolsPath.UrlString;
|
135
133
|
position: number;
|
136
134
|
|
137
135
|
constructor(uiSourceCode: Workspace.UISourceCode.UISourceCode, position: number) {
|
@@ -290,7 +290,7 @@ export class NavigatorView extends UI.Widget.VBox implements SDK.TargetManager.O
|
|
290
290
|
// Update folder titles.
|
291
291
|
const pathTokens =
|
292
292
|
Persistence.FileSystemWorkspaceBinding.FileSystemWorkspaceBinding.relativePath(binding.fileSystem);
|
293
|
-
let folderPath =
|
293
|
+
let folderPath = Platform.DevToolsPath.EmptyEncodedPathString;
|
294
294
|
for (let i = 0; i < pathTokens.length - 1; ++i) {
|
295
295
|
folderPath = Common.ParsedURL.ParsedURL.concatenate(folderPath, pathTokens[i]);
|
296
296
|
const folderId =
|
@@ -744,7 +744,7 @@ export class NavigatorView extends UI.Widget.VBox implements SDK.TargetManager.O
|
|
744
744
|
const folderId = this.folderNodeId(
|
745
745
|
project, target, frame, uiSourceCode.origin(),
|
746
746
|
currentNode instanceof NavigatorFolderTreeNode && currentNode.folderPath ||
|
747
|
-
|
747
|
+
Platform.DevToolsPath.EmptyEncodedPathString);
|
748
748
|
this.subfolderNodes.delete(folderId);
|
749
749
|
parentNode.removeChild(currentNode);
|
750
750
|
currentNode = (parentNode as NavigatorUISourceCodeTreeNode | null);
|
@@ -822,8 +822,7 @@ export class NavigatorView extends UI.Widget.VBox implements SDK.TargetManager.O
|
|
822
822
|
contextMenu.editSection().appendItem(i18nString(UIStrings.rename), this.handleContextMenuRename.bind(this, node));
|
823
823
|
contextMenu.editSection().appendItem(
|
824
824
|
i18nString(UIStrings.makeACopy),
|
825
|
-
this.handleContextMenuCreate.bind(
|
826
|
-
this, project, '' as Platform.DevToolsPath.EncodedPathString, uiSourceCode));
|
825
|
+
this.handleContextMenuCreate.bind(this, project, Platform.DevToolsPath.EmptyEncodedPathString, uiSourceCode));
|
827
826
|
contextMenu.editSection().appendItem(
|
828
827
|
i18nString(UIStrings.delete), this.handleContextMenuDelete.bind(this, uiSourceCode));
|
829
828
|
}
|
@@ -1472,7 +1471,7 @@ export class NavigatorFolderTreeNode extends NavigatorTreeNode {
|
|
1472
1471
|
folderPath: Platform.DevToolsPath.EncodedPathString, title: string) {
|
1473
1472
|
super(navigatorView, id, type);
|
1474
1473
|
this.project = project;
|
1475
|
-
this.folderPath = folderPath
|
1474
|
+
this.folderPath = folderPath;
|
1476
1475
|
this.title = title;
|
1477
1476
|
}
|
1478
1477
|
|
@@ -4,6 +4,7 @@
|
|
4
4
|
|
5
5
|
import type * as Common from '../../core/common/common.js';
|
6
6
|
import * as i18n from '../../core/i18n/i18n.js';
|
7
|
+
import type * as Platform from '../../core/platform/platform.js';
|
7
8
|
import * as FormatterModule from '../../models/formatter/formatter.js';
|
8
9
|
import * as Persistence from '../../models/persistence/persistence.js';
|
9
10
|
import * as Workspace from '../../models/workspace/workspace.js';
|
@@ -30,7 +31,7 @@ const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
30
31
|
let scriptFormatterEditorActionInstance: ScriptFormatterEditorAction;
|
31
32
|
|
32
33
|
export class ScriptFormatterEditorAction implements EditorAction {
|
33
|
-
private readonly pathsToFormatOnLoad: Set<
|
34
|
+
private readonly pathsToFormatOnLoad: Set<Platform.DevToolsPath.UrlString>;
|
34
35
|
private sourcesView!: SourcesView;
|
35
36
|
private button!: UI.Toolbar.ToolbarButton;
|
36
37
|
private constructor() {
|
@@ -436,10 +436,10 @@ export class TabbedEditorContainer extends Common.ObjectWrapper.ObjectWrapper<Ev
|
|
436
436
|
private updateHistory(): void {
|
437
437
|
const tabIds = this.tabbedPane.lastOpenedTabIds(maximalPreviouslyViewedFilesCount);
|
438
438
|
|
439
|
-
function tabIdToURI(this: TabbedEditorContainer, tabId: string):
|
439
|
+
function tabIdToURI(this: TabbedEditorContainer, tabId: string): Platform.DevToolsPath.UrlString {
|
440
440
|
const tab = this.files.get(tabId);
|
441
441
|
if (!tab) {
|
442
|
-
return
|
442
|
+
return Platform.DevToolsPath.EmptyUrlString;
|
443
443
|
}
|
444
444
|
return tab.url();
|
445
445
|
}
|
@@ -639,18 +639,19 @@ export let tabId = 0;
|
|
639
639
|
export const maximalPreviouslyViewedFilesCount = 30;
|
640
640
|
|
641
641
|
interface SerializedHistoryItem {
|
642
|
-
url:
|
642
|
+
url: Platform.DevToolsPath.UrlString;
|
643
643
|
selectionRange?: TextUtils.TextRange.SerializedTextRange;
|
644
644
|
scrollLineNumber?: number;
|
645
645
|
}
|
646
646
|
|
647
647
|
export class HistoryItem {
|
648
|
-
url:
|
648
|
+
url: Platform.DevToolsPath.UrlString;
|
649
649
|
private isSerializable: boolean;
|
650
650
|
selectionRange: TextUtils.TextRange.TextRange|undefined;
|
651
651
|
scrollLineNumber: number|undefined;
|
652
652
|
|
653
|
-
constructor(
|
653
|
+
constructor(
|
654
|
+
url: Platform.DevToolsPath.UrlString, selectionRange?: TextUtils.TextRange.TextRange, scrollLineNumber?: number) {
|
654
655
|
this.url = url;
|
655
656
|
this.isSerializable = url.length < HistoryItem.serializableUrlLengthLimit;
|
656
657
|
this.selectionRange = selectionRange;
|
@@ -700,7 +701,7 @@ export class History {
|
|
700
701
|
return new History(items);
|
701
702
|
}
|
702
703
|
|
703
|
-
index(url:
|
704
|
+
index(url: Platform.DevToolsPath.UrlString): number {
|
704
705
|
const index = this.itemsIndex.get(url);
|
705
706
|
if (index !== undefined) {
|
706
707
|
return index;
|
@@ -716,12 +717,12 @@ export class History {
|
|
716
717
|
}
|
717
718
|
}
|
718
719
|
|
719
|
-
selectionRange(url:
|
720
|
+
selectionRange(url: Platform.DevToolsPath.UrlString): TextUtils.TextRange.TextRange|undefined {
|
720
721
|
const index = this.index(url);
|
721
722
|
return index !== -1 ? this.items[index].selectionRange : undefined;
|
722
723
|
}
|
723
724
|
|
724
|
-
updateSelectionRange(url:
|
725
|
+
updateSelectionRange(url: Platform.DevToolsPath.UrlString, selectionRange?: TextUtils.TextRange.TextRange): void {
|
725
726
|
if (!selectionRange) {
|
726
727
|
return;
|
727
728
|
}
|
@@ -732,12 +733,12 @@ export class History {
|
|
732
733
|
this.items[index].selectionRange = selectionRange;
|
733
734
|
}
|
734
735
|
|
735
|
-
scrollLineNumber(url:
|
736
|
+
scrollLineNumber(url: Platform.DevToolsPath.UrlString): number|undefined {
|
736
737
|
const index = this.index(url);
|
737
738
|
return index !== -1 ? this.items[index].scrollLineNumber : undefined;
|
738
739
|
}
|
739
740
|
|
740
|
-
updateScrollLineNumber(url:
|
741
|
+
updateScrollLineNumber(url: Platform.DevToolsPath.UrlString, scrollLineNumber: number): void {
|
741
742
|
const index = this.index(url);
|
742
743
|
if (index === -1) {
|
743
744
|
return;
|
@@ -745,7 +746,7 @@ export class History {
|
|
745
746
|
this.items[index].scrollLineNumber = scrollLineNumber;
|
746
747
|
}
|
747
748
|
|
748
|
-
update(urls:
|
749
|
+
update(urls: Platform.DevToolsPath.UrlString[]): void {
|
749
750
|
for (let i = urls.length - 1; i >= 0; --i) {
|
750
751
|
const index = this.index(urls[i]);
|
751
752
|
let item;
|
@@ -760,7 +761,7 @@ export class History {
|
|
760
761
|
}
|
761
762
|
}
|
762
763
|
|
763
|
-
remove(url:
|
764
|
+
remove(url: Platform.DevToolsPath.UrlString): void {
|
764
765
|
const index = this.index(url);
|
765
766
|
if (index !== -1) {
|
766
767
|
this.items.splice(index, 1);
|
@@ -786,7 +787,7 @@ export class History {
|
|
786
787
|
return serializedHistory;
|
787
788
|
}
|
788
789
|
|
789
|
-
urls():
|
790
|
+
urls(): Platform.DevToolsPath.UrlString[] {
|
790
791
|
const result = [];
|
791
792
|
for (let i = 0; i < this.items.length; ++i) {
|
792
793
|
result.push(this.items[i].url);
|
@@ -2,6 +2,7 @@
|
|
2
2
|
// Use of this source code is governed by a BSD-style license that can be
|
3
3
|
// found in the LICENSE file.
|
4
4
|
|
5
|
+
import type * as Platform from '../../core/platform/platform.js';
|
5
6
|
import type * as SDK from '../../core/sdk/sdk.js';
|
6
7
|
import type * as Extensions from '../../models/extensions/extensions.js';
|
7
8
|
|
@@ -42,7 +43,7 @@ export class ExtensionTracingSession implements Extensions.ExtensionTraceProvide
|
|
42
43
|
this.completionCallback();
|
43
44
|
}
|
44
45
|
|
45
|
-
complete(url:
|
46
|
+
complete(url: Platform.DevToolsPath.UrlString, timeOffsetMicroseconds: number): void {
|
46
47
|
if (!url) {
|
47
48
|
this.completionCallback();
|
48
49
|
return;
|
@@ -161,7 +161,7 @@ export class TimelineFlameChartView extends UI.Widget.VBox implements PerfUI.Fla
|
|
161
161
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
162
162
|
private readonly groupBySetting: Common.Settings.Setting<any>;
|
163
163
|
private searchableView!: UI.SearchableView.SearchableView;
|
164
|
-
private urlToColorCache?: Map<
|
164
|
+
private urlToColorCache?: Map<Platform.DevToolsPath.UrlString, string>;
|
165
165
|
private needsResizeToPreferredHeights?: boolean;
|
166
166
|
private selectedSearchResult?: number;
|
167
167
|
private searchRegex?: RegExp;
|
@@ -5,6 +5,7 @@
|
|
5
5
|
import * as Common from '../../core/common/common.js';
|
6
6
|
import * as Host from '../../core/host/host.js';
|
7
7
|
import * as i18n from '../../core/i18n/i18n.js';
|
8
|
+
import type * as Platform from '../../core/platform/platform.js';
|
8
9
|
import * as SDK from '../../core/sdk/sdk.js';
|
9
10
|
import * as Bindings from '../../models/bindings/bindings.js';
|
10
11
|
import * as TextUtils from '../../models/text_utils/text_utils.js';
|
@@ -95,7 +96,7 @@ export class TimelineLoader implements Common.StringOutputStream.OutputStream {
|
|
95
96
|
return loader;
|
96
97
|
}
|
97
98
|
|
98
|
-
static loadFromURL(url:
|
99
|
+
static loadFromURL(url: Platform.DevToolsPath.UrlString, client: Client): TimelineLoader {
|
99
100
|
const loader = new TimelineLoader(client);
|
100
101
|
Host.ResourceLoader.loadAsStream(url, null, loader);
|
101
102
|
return loader;
|
@@ -695,7 +695,7 @@ export class TimelinePanel extends UI.Panel.Panel implements Client, TimelineMod
|
|
695
695
|
this.createFileSelector();
|
696
696
|
}
|
697
697
|
|
698
|
-
loadFromURL(url:
|
698
|
+
loadFromURL(url: Platform.DevToolsPath.UrlString): void {
|
699
699
|
if (this.state !== State.Idle) {
|
700
700
|
return;
|
701
701
|
}
|
@@ -1249,7 +1249,7 @@ export class TimelinePanel extends UI.Panel.Panel implements Client, TimelineMod
|
|
1249
1249
|
const item = items[0];
|
1250
1250
|
Host.userMetrics.actionTaken(Host.UserMetrics.Action.PerfPanelTraceImported);
|
1251
1251
|
if (item.kind === 'string') {
|
1252
|
-
const url = dataTransfer.getData('text/uri-list');
|
1252
|
+
const url = dataTransfer.getData('text/uri-list') as Platform.DevToolsPath.UrlString;
|
1253
1253
|
if (new Common.ParsedURL.ParsedURL(url).isValid) {
|
1254
1254
|
this.loadFromURL(url);
|
1255
1255
|
}
|
@@ -1492,7 +1492,7 @@ export class LoadTimelineHandler implements Common.QueryParamHandler.QueryParamH
|
|
1492
1492
|
|
1493
1493
|
handleQueryParam(value: string): void {
|
1494
1494
|
void UI.ViewManager.ViewManager.instance().showView('timeline').then(() => {
|
1495
|
-
TimelinePanel.instance().loadFromURL(window.decodeURIComponent(value));
|
1495
|
+
TimelinePanel.instance().loadFromURL(window.decodeURIComponent(value) as Platform.DevToolsPath.UrlString);
|
1496
1496
|
});
|
1497
1497
|
}
|
1498
1498
|
}
|
@@ -756,9 +756,9 @@ export class AggregatedTimelineTreeView extends TimelineTreeView {
|
|
756
756
|
}
|
757
757
|
|
758
758
|
private beautifyDomainName(this: AggregatedTimelineTreeView, name: string): string {
|
759
|
-
if (AggregatedTimelineTreeView.isExtensionInternalURL(name)) {
|
759
|
+
if (AggregatedTimelineTreeView.isExtensionInternalURL(name as Platform.DevToolsPath.UrlString)) {
|
760
760
|
name = i18nString(UIStrings.chromeExtensionsOverhead);
|
761
|
-
} else if (AggregatedTimelineTreeView.isV8NativeURL(name)) {
|
761
|
+
} else if (AggregatedTimelineTreeView.isV8NativeURL(name as Platform.DevToolsPath.UrlString)) {
|
762
762
|
name = i18nString(UIStrings.vRuntime);
|
763
763
|
} else if (name.startsWith('chrome-extension')) {
|
764
764
|
name = this.executionContextNamesByOrigin.get(name) || name;
|
@@ -949,11 +949,11 @@ export class AggregatedTimelineTreeView extends TimelineTreeView {
|
|
949
949
|
contextMenu.appendApplicableItems(frame.ownerNode);
|
950
950
|
}
|
951
951
|
|
952
|
-
private static isExtensionInternalURL(url:
|
952
|
+
private static isExtensionInternalURL(url: Platform.DevToolsPath.UrlString): boolean {
|
953
953
|
return url.startsWith(AggregatedTimelineTreeView.extensionInternalPrefix);
|
954
954
|
}
|
955
955
|
|
956
|
-
private static isV8NativeURL(url:
|
956
|
+
private static isV8NativeURL(url: Platform.DevToolsPath.UrlString): boolean {
|
957
957
|
return url.startsWith(AggregatedTimelineTreeView.v8NativePrefix);
|
958
958
|
}
|
959
959
|
|
@@ -1473,7 +1473,7 @@ export class TimelineUIUtils {
|
|
1473
1473
|
}
|
1474
1474
|
}
|
1475
1475
|
|
1476
|
-
static eventURL(event: SDK.TracingModel.Event):
|
1476
|
+
static eventURL(event: SDK.TracingModel.Event): Platform.DevToolsPath.UrlString|null {
|
1477
1477
|
const data = event.args['data'] || event.args['beginData'];
|
1478
1478
|
const url = data && data.url;
|
1479
1479
|
if (url) {
|
@@ -1482,7 +1482,7 @@ export class TimelineUIUtils {
|
|
1482
1482
|
const stackTrace = data && data['stackTrace'];
|
1483
1483
|
const frame = stackTrace && stackTrace.length && stackTrace[0] ||
|
1484
1484
|
TimelineModel.TimelineModel.TimelineData.forEvent(event).topFrame();
|
1485
|
-
return frame && frame.url || null;
|
1485
|
+
return frame && frame.url as Platform.DevToolsPath.UrlString || null;
|
1486
1486
|
}
|
1487
1487
|
|
1488
1488
|
static eventStyle(event: SDK.TracingModel.Event): TimelineRecordStyle {
|
@@ -1533,7 +1533,7 @@ export class TimelineUIUtils {
|
|
1533
1533
|
static eventColorByProduct(
|
1534
1534
|
model: TimelineModel.TimelineModel.TimelineModelImpl, urlToColorCache: Map<string, string>,
|
1535
1535
|
event: SDK.TracingModel.Event): string {
|
1536
|
-
const url = TimelineUIUtils.eventURL(event) ||
|
1536
|
+
const url = TimelineUIUtils.eventURL(event) || Platform.DevToolsPath.EmptyUrlString;
|
1537
1537
|
let color = urlToColorCache.get(url);
|
1538
1538
|
if (color) {
|
1539
1539
|
return color;
|
@@ -84,6 +84,12 @@ const UIStrings = {
|
|
84
84
|
*/
|
85
85
|
supportsResidentKeys: 'Supports resident keys',
|
86
86
|
/**
|
87
|
+
*@description Label for checkbox that toggles large blob support on virtual authenticators. Large blobs are opaque data associated
|
88
|
+
* with a WebAuthn credential that a website can store, like an SSH certificate or a symmetric encryption key.
|
89
|
+
* See https://w3c.github.io/webauthn/#sctn-large-blob-extension
|
90
|
+
*/
|
91
|
+
supportsLargeBlob: 'Supports large blob',
|
92
|
+
/**
|
87
93
|
*@description Text to add something
|
88
94
|
*/
|
89
95
|
add: 'Add',
|
@@ -244,10 +250,12 @@ export class WebauthnPaneImpl extends UI.Widget.VBox implements
|
|
244
250
|
#protocolSelect: HTMLSelectElement|undefined;
|
245
251
|
#transportSelect: HTMLSelectElement|undefined;
|
246
252
|
#residentKeyCheckboxLabel: UI.UIUtils.CheckboxLabel|undefined;
|
247
|
-
|
253
|
+
residentKeyCheckbox: HTMLInputElement|undefined;
|
248
254
|
#userVerificationCheckboxLabel: UI.UIUtils.CheckboxLabel|undefined;
|
249
255
|
#userVerificationCheckbox: HTMLInputElement|undefined;
|
250
|
-
#
|
256
|
+
#largeBlobCheckboxLabel: UI.UIUtils.CheckboxLabel|undefined;
|
257
|
+
largeBlobCheckbox: HTMLInputElement|undefined;
|
258
|
+
addAuthenticatorButton: HTMLButtonElement|undefined;
|
251
259
|
#isEnabling?: Promise<void>;
|
252
260
|
|
253
261
|
constructor() {
|
@@ -475,13 +483,18 @@ export class WebauthnPaneImpl extends UI.Widget.VBox implements
|
|
475
483
|
}
|
476
484
|
|
477
485
|
#updateNewAuthenticatorSectionOptions(): void {
|
478
|
-
if (!this.#protocolSelect || !this
|
486
|
+
if (!this.#protocolSelect || !this.residentKeyCheckbox || !this.#userVerificationCheckbox ||
|
487
|
+
!this.largeBlobCheckbox) {
|
479
488
|
return;
|
480
489
|
}
|
481
490
|
|
482
491
|
if (this.#protocolSelect.value === Protocol.WebAuthn.AuthenticatorProtocol.Ctap2) {
|
483
|
-
this
|
492
|
+
this.residentKeyCheckbox.disabled = false;
|
484
493
|
this.#userVerificationCheckbox.disabled = false;
|
494
|
+
this.largeBlobCheckbox.disabled = !this.residentKeyCheckbox.checked;
|
495
|
+
if (this.largeBlobCheckbox.disabled) {
|
496
|
+
this.largeBlobCheckbox.checked = false;
|
497
|
+
}
|
485
498
|
this.#updateEnabledTransportOptions([
|
486
499
|
Protocol.WebAuthn.AuthenticatorTransport.Usb,
|
487
500
|
Protocol.WebAuthn.AuthenticatorTransport.Ble,
|
@@ -491,10 +504,12 @@ export class WebauthnPaneImpl extends UI.Widget.VBox implements
|
|
491
504
|
Protocol.WebAuthn.AuthenticatorTransport.Internal,
|
492
505
|
]);
|
493
506
|
} else {
|
494
|
-
this
|
495
|
-
this
|
507
|
+
this.residentKeyCheckbox.checked = false;
|
508
|
+
this.residentKeyCheckbox.disabled = true;
|
496
509
|
this.#userVerificationCheckbox.checked = false;
|
497
510
|
this.#userVerificationCheckbox.disabled = true;
|
511
|
+
this.largeBlobCheckbox.checked = false;
|
512
|
+
this.largeBlobCheckbox.disabled = true;
|
498
513
|
this.#updateEnabledTransportOptions([
|
499
514
|
Protocol.WebAuthn.AuthenticatorTransport.Usb,
|
500
515
|
Protocol.WebAuthn.AuthenticatorTransport.Ble,
|
@@ -524,6 +539,7 @@ export class WebauthnPaneImpl extends UI.Widget.VBox implements
|
|
524
539
|
const transportGroup = this.#newAuthenticatorForm.createChild('div', 'authenticator-option');
|
525
540
|
const residentKeyGroup = this.#newAuthenticatorForm.createChild('div', 'authenticator-option');
|
526
541
|
const userVerificationGroup = this.#newAuthenticatorForm.createChild('div', 'authenticator-option');
|
542
|
+
const largeBlobGroup = this.#newAuthenticatorForm.createChild('div', 'authenticator-option');
|
527
543
|
const addButtonGroup = this.#newAuthenticatorForm.createChild('div', 'authenticator-option');
|
528
544
|
|
529
545
|
const protocolSelectTitle = UI.UIUtils.createLabel(i18nString(UIStrings.protocol), 'authenticator-option-label');
|
@@ -551,9 +567,9 @@ export class WebauthnPaneImpl extends UI.Widget.VBox implements
|
|
551
567
|
this.#residentKeyCheckboxLabel = UI.UIUtils.CheckboxLabel.create(i18nString(UIStrings.supportsResidentKeys), false);
|
552
568
|
this.#residentKeyCheckboxLabel.textElement.classList.add('authenticator-option-label');
|
553
569
|
residentKeyGroup.appendChild(this.#residentKeyCheckboxLabel.textElement);
|
554
|
-
this
|
555
|
-
this
|
556
|
-
this
|
570
|
+
this.residentKeyCheckbox = this.#residentKeyCheckboxLabel.checkboxElement;
|
571
|
+
this.residentKeyCheckbox.checked = false;
|
572
|
+
this.residentKeyCheckbox.classList.add('authenticator-option-checkbox');
|
557
573
|
residentKeyGroup.appendChild(this.#residentKeyCheckboxLabel);
|
558
574
|
|
559
575
|
this.#userVerificationCheckboxLabel = UI.UIUtils.CheckboxLabel.create('Supports user verification', false);
|
@@ -564,17 +580,29 @@ export class WebauthnPaneImpl extends UI.Widget.VBox implements
|
|
564
580
|
this.#userVerificationCheckbox.classList.add('authenticator-option-checkbox');
|
565
581
|
userVerificationGroup.appendChild(this.#userVerificationCheckboxLabel);
|
566
582
|
|
567
|
-
this.#
|
583
|
+
this.#largeBlobCheckboxLabel = UI.UIUtils.CheckboxLabel.create(i18nString(UIStrings.supportsLargeBlob), false);
|
584
|
+
this.#largeBlobCheckboxLabel.textElement.classList.add('authenticator-option-label');
|
585
|
+
largeBlobGroup.appendChild(this.#largeBlobCheckboxLabel.textElement);
|
586
|
+
this.largeBlobCheckbox = this.#largeBlobCheckboxLabel.checkboxElement;
|
587
|
+
this.largeBlobCheckbox.checked = false;
|
588
|
+
this.largeBlobCheckbox.classList.add('authenticator-option-checkbox');
|
589
|
+
this.largeBlobCheckbox.name = 'large-blob-checkbox';
|
590
|
+
largeBlobGroup.appendChild(this.#largeBlobCheckboxLabel);
|
591
|
+
|
592
|
+
this.addAuthenticatorButton =
|
568
593
|
UI.UIUtils.createTextButton(i18nString(UIStrings.add), this.#handleAddAuthenticatorButton.bind(this), '');
|
569
594
|
addButtonGroup.createChild('div', 'authenticator-option-label');
|
570
|
-
addButtonGroup.appendChild(this
|
595
|
+
addButtonGroup.appendChild(this.addAuthenticatorButton);
|
571
596
|
const addAuthenticatorTitle = UI.UIUtils.createLabel(i18nString(UIStrings.addAuthenticator), '');
|
572
|
-
UI.ARIAUtils.bindLabelToControl(addAuthenticatorTitle, this
|
597
|
+
UI.ARIAUtils.bindLabelToControl(addAuthenticatorTitle, this.addAuthenticatorButton);
|
573
598
|
|
574
599
|
this.#updateNewAuthenticatorSectionOptions();
|
575
600
|
if (this.#protocolSelect) {
|
576
601
|
this.#protocolSelect.addEventListener('change', this.#updateNewAuthenticatorSectionOptions.bind(this));
|
577
602
|
}
|
603
|
+
if (this.residentKeyCheckbox) {
|
604
|
+
this.residentKeyCheckbox.addEventListener('change', this.#updateNewAuthenticatorSectionOptions.bind(this));
|
605
|
+
}
|
578
606
|
}
|
579
607
|
|
580
608
|
async #handleAddAuthenticatorButton(): Promise<void> {
|
@@ -702,6 +730,7 @@ export class WebauthnPaneImpl extends UI.Widget.VBox implements
|
|
702
730
|
const protocolField = sectionFields.createChild('div', 'authenticator-field');
|
703
731
|
const transportField = sectionFields.createChild('div', 'authenticator-field');
|
704
732
|
const srkField = sectionFields.createChild('div', 'authenticator-field');
|
733
|
+
const slbField = sectionFields.createChild('div', 'authenticator-field');
|
705
734
|
const suvField = sectionFields.createChild('div', 'authenticator-field');
|
706
735
|
|
707
736
|
uuidField.appendChild(UI.UIUtils.createLabel(i18nString(UIStrings.uuid), 'authenticator-option-label'));
|
@@ -709,6 +738,7 @@ export class WebauthnPaneImpl extends UI.Widget.VBox implements
|
|
709
738
|
transportField.appendChild(UI.UIUtils.createLabel(i18nString(UIStrings.transport), 'authenticator-option-label'));
|
710
739
|
srkField.appendChild(
|
711
740
|
UI.UIUtils.createLabel(i18nString(UIStrings.supportsResidentKeys), 'authenticator-option-label'));
|
741
|
+
slbField.appendChild(UI.UIUtils.createLabel(i18nString(UIStrings.supportsLargeBlob), 'authenticator-option-label'));
|
712
742
|
suvField.appendChild(
|
713
743
|
UI.UIUtils.createLabel(i18nString(UIStrings.supportsUserVerification), 'authenticator-option-label'));
|
714
744
|
|
@@ -717,6 +747,8 @@ export class WebauthnPaneImpl extends UI.Widget.VBox implements
|
|
717
747
|
transportField.createChild('div', 'authenticator-field-value').textContent = options.transport;
|
718
748
|
srkField.createChild('div', 'authenticator-field-value').textContent =
|
719
749
|
options.hasResidentKey ? i18nString(UIStrings.yes) : i18nString(UIStrings.no);
|
750
|
+
slbField.createChild('div', 'authenticator-field-value').textContent =
|
751
|
+
options.hasLargeBlob ? i18nString(UIStrings.yes) : i18nString(UIStrings.no);
|
720
752
|
suvField.createChild('div', 'authenticator-field-value').textContent =
|
721
753
|
options.hasUserVerification ? i18nString(UIStrings.yes) : i18nString(UIStrings.no);
|
722
754
|
}
|
@@ -779,18 +811,20 @@ export class WebauthnPaneImpl extends UI.Widget.VBox implements
|
|
779
811
|
|
780
812
|
#createOptionsFromCurrentInputs(): Protocol.WebAuthn.VirtualAuthenticatorOptions {
|
781
813
|
// TODO(crbug.com/1034663): Add optionality for isUserVerified param.
|
782
|
-
if (!this.#protocolSelect || !this.#transportSelect || !this
|
783
|
-
!this.#userVerificationCheckbox) {
|
814
|
+
if (!this.#protocolSelect || !this.#transportSelect || !this.residentKeyCheckbox ||
|
815
|
+
!this.#userVerificationCheckbox || !this.largeBlobCheckbox) {
|
784
816
|
throw new Error('Unable to create options from current inputs');
|
785
817
|
}
|
786
818
|
|
787
819
|
return {
|
788
820
|
protocol: this.#protocolSelect.options[this.#protocolSelect.selectedIndex].value as
|
789
821
|
Protocol.WebAuthn.AuthenticatorProtocol,
|
822
|
+
ctap2Version: Protocol.WebAuthn.Ctap2Version.Ctap2_1,
|
790
823
|
transport: this.#transportSelect.options[this.#transportSelect.selectedIndex].value as
|
791
824
|
Protocol.WebAuthn.AuthenticatorTransport,
|
792
|
-
hasResidentKey: this
|
825
|
+
hasResidentKey: this.residentKeyCheckbox.checked,
|
793
826
|
hasUserVerification: this.#userVerificationCheckbox.checked,
|
827
|
+
hasLargeBlob: this.largeBlobCheckbox.checked,
|
794
828
|
automaticPresenceSimulation: true,
|
795
829
|
isUserVerified: true,
|
796
830
|
};
|
package/package.json
CHANGED
@@ -0,0 +1,110 @@
|
|
1
|
+
// Copyright 2020 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
|
+
'use strict';
|
5
|
+
|
6
|
+
function goToClassDeclaration(node) {
|
7
|
+
if (!node) {
|
8
|
+
return null;
|
9
|
+
}
|
10
|
+
|
11
|
+
if (node.type === 'ClassDeclaration') {
|
12
|
+
return node;
|
13
|
+
}
|
14
|
+
|
15
|
+
return goToClassDeclaration(node.parent);
|
16
|
+
}
|
17
|
+
|
18
|
+
function isMemberExpressionOnThis(memberExpression) {
|
19
|
+
if (!memberExpression) {
|
20
|
+
return false;
|
21
|
+
}
|
22
|
+
|
23
|
+
if (memberExpression.object.type === 'ThisExpression') {
|
24
|
+
// Take into `a.this.bind()` case into account
|
25
|
+
// `this` must be the last object in the `MemberExpression` chain
|
26
|
+
return !memberExpression.object.object;
|
27
|
+
}
|
28
|
+
|
29
|
+
return isMemberExpressionOnThis(memberExpression.object);
|
30
|
+
}
|
31
|
+
|
32
|
+
// Whether the right hand side of property definition is `this.xxx.yyy.bind(this);`
|
33
|
+
function isPropertyDefinitionViaBindCallToThis(propertyDefinition) {
|
34
|
+
if (propertyDefinition.value.type !== 'CallExpression' ||
|
35
|
+
propertyDefinition.value.callee.type !== 'MemberExpression') {
|
36
|
+
return false;
|
37
|
+
}
|
38
|
+
|
39
|
+
const isCalleeObjectThis = isMemberExpressionOnThis(propertyDefinition.value.callee);
|
40
|
+
// Whether the CallExpression is on a property of `this` (this.xxx.yyy.bind)
|
41
|
+
if (!isCalleeObjectThis) {
|
42
|
+
return false;
|
43
|
+
}
|
44
|
+
|
45
|
+
const isItBindCall = propertyDefinition.value.callee.property.name === 'bind';
|
46
|
+
// Whether the CallExpression is a `bind` call on a property of `this`
|
47
|
+
if (!isItBindCall) {
|
48
|
+
return false;
|
49
|
+
}
|
50
|
+
|
51
|
+
const callArgument = propertyDefinition.value.arguments[0];
|
52
|
+
// Call argument to `bind` is not `this`
|
53
|
+
if (!callArgument || callArgument.type !== 'ThisExpression') {
|
54
|
+
return false;
|
55
|
+
}
|
56
|
+
|
57
|
+
return true;
|
58
|
+
}
|
59
|
+
|
60
|
+
// Whether the property definition is arrow function like `#render = () => {}`
|
61
|
+
function isPropertyDefinitionViaArrowFunction(propertyDefinition) {
|
62
|
+
return propertyDefinition.value.type === 'ArrowFunctionExpression';
|
63
|
+
}
|
64
|
+
|
65
|
+
module.exports = {
|
66
|
+
meta: {
|
67
|
+
type: 'problem',
|
68
|
+
docs: {
|
69
|
+
description: 'Enforce render method to be bound while calling scheduleRender',
|
70
|
+
category: 'Possible Errors',
|
71
|
+
},
|
72
|
+
fixable: 'code',
|
73
|
+
schema: [] // no options
|
74
|
+
},
|
75
|
+
create: function(context) {
|
76
|
+
return {
|
77
|
+
CallExpression(node) {
|
78
|
+
// Calls in the form of `ScheduledRender.scheduleRender`
|
79
|
+
const isScheduleRenderCall = node.callee.type === 'MemberExpression' &&
|
80
|
+
node.callee.object?.property?.name === 'ScheduledRender' && node.callee.property?.name === 'scheduleRender';
|
81
|
+
if (!isScheduleRenderCall) {
|
82
|
+
return;
|
83
|
+
}
|
84
|
+
|
85
|
+
const callbackArgument = node.arguments[1];
|
86
|
+
// Whether the second argument points to a property of `this`
|
87
|
+
// like `ScheduledRender.scheduleRender(<any>, this.<any>)
|
88
|
+
if (callbackArgument.type !== 'MemberExpression' || callbackArgument.object.type !== 'ThisExpression') {
|
89
|
+
return;
|
90
|
+
}
|
91
|
+
|
92
|
+
const containingClassForTheCall = goToClassDeclaration(node);
|
93
|
+
// Only care about the calls in custom components
|
94
|
+
if (!containingClassForTheCall.superClass || containingClassForTheCall.superClass.name !== 'HTMLElement') {
|
95
|
+
return;
|
96
|
+
}
|
97
|
+
|
98
|
+
const calledMethod = callbackArgument.property;
|
99
|
+
// Check whether the called method is bound (it should be 'PropertyDefinition')
|
100
|
+
const propertyDefinition = containingClassForTheCall.body.body.find(
|
101
|
+
bodyNode => bodyNode.type === 'PropertyDefinition' && bodyNode.key.name === calledMethod.name);
|
102
|
+
if (!propertyDefinition ||
|
103
|
+
(!isPropertyDefinitionViaArrowFunction(propertyDefinition) &&
|
104
|
+
!isPropertyDefinitionViaBindCallToThis(propertyDefinition))) {
|
105
|
+
context.report({node, message: 'Bind `render` method of `scheduleRender` to `this` in components'});
|
106
|
+
}
|
107
|
+
}
|
108
|
+
};
|
109
|
+
}
|
110
|
+
};
|
@@ -0,0 +1,74 @@
|
|
1
|
+
// Copyright 2020 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
|
+
'use strict';
|
5
|
+
|
6
|
+
const rule = require('../lib/enforce_bound_render_for_schedule_render.js');
|
7
|
+
const ruleTester = new (require('eslint').RuleTester)({
|
8
|
+
parserOptions: {ecmaVersion: 9, sourceType: 'module'},
|
9
|
+
parser: require.resolve('@typescript-eslint/parser'),
|
10
|
+
});
|
11
|
+
|
12
|
+
ruleTester.run('enforce_bound_render_for_schedule_render', rule, {
|
13
|
+
valid: [
|
14
|
+
{
|
15
|
+
code: `
|
16
|
+
class Component extends HTMLElement {
|
17
|
+
#boundRender = this.#render.bind(this);
|
18
|
+
get data(data) {
|
19
|
+
this.data = data;
|
20
|
+
void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender);
|
21
|
+
}
|
22
|
+
|
23
|
+
#render() {}
|
24
|
+
}
|
25
|
+
`,
|
26
|
+
filename: 'front_end/ui/components/foo/Foo.ts',
|
27
|
+
},
|
28
|
+
{
|
29
|
+
code: `
|
30
|
+
class Component extends HTMLElement {
|
31
|
+
get data(data) {
|
32
|
+
this.data = data;
|
33
|
+
void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#render);
|
34
|
+
}
|
35
|
+
|
36
|
+
#render = () => {};
|
37
|
+
}
|
38
|
+
`,
|
39
|
+
filename: 'front_end/ui/components/foo/Foo.ts',
|
40
|
+
},
|
41
|
+
{
|
42
|
+
code: `
|
43
|
+
class Renderer {
|
44
|
+
render() {
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
class Component extends HTMLElement {
|
49
|
+
#renderer = new Renderer();
|
50
|
+
#boundRender = this.#renderer.render.bind(this);
|
51
|
+
get data(data) {
|
52
|
+
this.data = data;
|
53
|
+
void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender);
|
54
|
+
}
|
55
|
+
}
|
56
|
+
`,
|
57
|
+
filename: 'front_end/ui/components/foo/Foo.ts',
|
58
|
+
},
|
59
|
+
],
|
60
|
+
invalid: [
|
61
|
+
{
|
62
|
+
code: `class Component extends HTMLElement {
|
63
|
+
get data(data) {
|
64
|
+
this.data = data;
|
65
|
+
void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#render);
|
66
|
+
}
|
67
|
+
|
68
|
+
#render() {}
|
69
|
+
}`,
|
70
|
+
filename: 'front_end/components/test.ts',
|
71
|
+
errors: [{message: 'Bind `render` method of `scheduleRender` to `this` in components'}],
|
72
|
+
},
|
73
|
+
]
|
74
|
+
});
|