chrome-devtools-frontend 1.0.975699 → 1.0.976703
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/AUTHORS +1 -0
- package/config/gni/devtools_grd_files.gni +8 -0
- package/config/gni/devtools_image_files.gni +2 -0
- package/front_end/Images/src/minus_icon.svg +3 -0
- package/front_end/Images/src/plus_icon.svg +3 -0
- package/front_end/core/host/UserMetrics.ts +23 -23
- 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/core/sdk/CSSContainerQuery.ts +1 -1
- package/front_end/core/sdk/CSSModel.ts +13 -9
- package/front_end/generated/InspectorBackendCommands.js +7 -6
- package/front_end/generated/protocol.ts +11 -9
- package/front_end/models/issues_manager/Issue.ts +1 -1
- package/front_end/models/issues_manager/IssuesManager.ts +3 -9
- package/front_end/models/issues_manager/SameSiteCookieIssue.ts +74 -78
- package/front_end/models/issues_manager/issues_manager.ts +2 -2
- package/front_end/models/persistence/NetworkPersistenceManager.ts +1 -1
- package/front_end/panels/elements/ClassesPaneWidget.ts +1 -1
- package/front_end/panels/elements/ComputedStyleModel.ts +1 -1
- package/front_end/panels/elements/ElementsTreeElement.ts +1 -1
- package/front_end/panels/elements/LayersWidget.ts +4 -3
- package/front_end/panels/elements/MetricsSidebarPane.ts +2 -2
- package/front_end/panels/elements/PlatformFontsWidget.ts +1 -1
- package/front_end/panels/elements/StylePropertyTreeElement.ts +1 -1
- package/front_end/panels/elements/StylesSidebarPane.ts +10 -8
- package/front_end/panels/emulation/MediaQueryInspector.ts +1 -1
- package/front_end/panels/issues/IssueView.ts +1 -1
- package/front_end/panels/issues/IssuesPane.ts +1 -1
- package/front_end/panels/network/NetworkLogView.ts +1 -1
- package/front_end/panels/settings/components/SyncSection.ts +5 -17
- package/front_end/panels/sources/CSSPlugin.ts +77 -22
- package/front_end/panels/sources/SourcesView.ts +23 -10
- package/front_end/panels/sources/components/HeadersView.css +32 -0
- package/front_end/panels/sources/components/HeadersView.ts +89 -0
- package/front_end/panels/sources/components/components.ts +9 -0
- package/front_end/ui/components/chrome_link/ChromeLink.ts +62 -0
- package/front_end/ui/components/chrome_link/chromeLink.css +12 -0
- package/front_end/ui/components/chrome_link/chrome_link.ts +9 -0
- package/package.json +1 -1
@@ -4,13 +4,17 @@
|
|
4
4
|
|
5
5
|
import * as Common from '../../core/common/common.js';
|
6
6
|
import * as i18n from '../../core/i18n/i18n.js';
|
7
|
+
import * as Platform from '../../core/platform/platform.js';
|
7
8
|
import * as SDK from '../../core/sdk/sdk.js';
|
9
|
+
import type * as Protocol from '../../generated/protocol.js';
|
8
10
|
import type * as Workspace from '../../models/workspace/workspace.js';
|
9
11
|
import * as ColorPicker from '../../ui/legacy/components/color_picker/color_picker.js';
|
10
12
|
import * as InlineEditor from '../../ui/legacy/components/inline_editor/inline_editor.js';
|
11
13
|
import * as CodeMirror from '../../third_party/codemirror.next/codemirror.next.js';
|
14
|
+
import type * as SourceFrame from '../../ui/legacy/components/source_frame/source_frame.js';
|
12
15
|
import * as UI from '../../ui/legacy/legacy.js';
|
13
16
|
|
17
|
+
import {assertNotNullOrUndefined} from '../../core/platform/platform.js';
|
14
18
|
import {Plugin} from './Plugin.js';
|
15
19
|
|
16
20
|
// Plugin to add CSS completion, shortcuts, and color/curve swatches
|
@@ -29,17 +33,6 @@ const UIStrings = {
|
|
29
33
|
const str_ = i18n.i18n.registerUIStrings('panels/sources/CSSPlugin.ts', UIStrings);
|
30
34
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
31
35
|
|
32
|
-
export function completion(): CodeMirror.Extension {
|
33
|
-
const {cssCompletionSource} = CodeMirror.css;
|
34
|
-
return CodeMirror.autocompletion({
|
35
|
-
override:
|
36
|
-
[async(cx: CodeMirror.CompletionContext):
|
37
|
-
Promise<CodeMirror.CompletionResult|null> => {
|
38
|
-
return (await specificCssCompletion(cx)) || cssCompletionSource(cx);
|
39
|
-
}],
|
40
|
-
});
|
41
|
-
}
|
42
|
-
|
43
36
|
const dontCompleteIn = new Set(['ColorLiteral', 'NumberLiteral', 'StringLiteral', 'Comment', 'Important']);
|
44
37
|
|
45
38
|
function findPropertyAt(node: CodeMirror.SyntaxNode, pos: number): CodeMirror.SyntaxNode|null {
|
@@ -57,18 +50,43 @@ function findPropertyAt(node: CodeMirror.SyntaxNode, pos: number): CodeMirror.Sy
|
|
57
50
|
return null;
|
58
51
|
}
|
59
52
|
|
60
|
-
function
|
53
|
+
function getCurrentStyleSheet(
|
54
|
+
url: Platform.DevToolsPath.UrlString, cssModel: SDK.CSSModel.CSSModel): Protocol.CSS.StyleSheetId {
|
55
|
+
const currentStyleSheet = cssModel.getStyleSheetIdsForURL(url);
|
56
|
+
if (currentStyleSheet.length === 0) {
|
57
|
+
Platform.DCHECK(() => currentStyleSheet.length !== 0, 'Can\'t find style sheet ID for current URL');
|
58
|
+
}
|
59
|
+
|
60
|
+
return currentStyleSheet[0];
|
61
|
+
}
|
62
|
+
|
63
|
+
async function specificCssCompletion(
|
64
|
+
cx: CodeMirror.CompletionContext, uiSourceCode: Workspace.UISourceCode.UISourceCode,
|
65
|
+
cssModel: SDK.CSSModel.CSSModel|undefined): Promise<CodeMirror.CompletionResult|null> {
|
61
66
|
const node = CodeMirror.syntaxTree(cx.state).resolveInner(cx.pos, -1);
|
67
|
+
if (node.name === 'ClassName') {
|
68
|
+
// Should never happen, but let's code defensively here
|
69
|
+
assertNotNullOrUndefined(cssModel);
|
70
|
+
|
71
|
+
const currentStyleSheet = getCurrentStyleSheet(uiSourceCode.url(), cssModel);
|
72
|
+
const existingClassNames = await cssModel.getClassNames(currentStyleSheet);
|
73
|
+
|
74
|
+
return {
|
75
|
+
from: node.from,
|
76
|
+
options: existingClassNames.map(value => ({type: 'constant', label: value})),
|
77
|
+
};
|
78
|
+
}
|
62
79
|
const property = findPropertyAt(node, cx.pos);
|
63
|
-
if (
|
64
|
-
|
80
|
+
if (property) {
|
81
|
+
const propertyValues =
|
82
|
+
SDK.CSSMetadata.cssMetadata().getPropertyValues(cx.state.sliceDoc(property.from, property.to));
|
83
|
+
return {
|
84
|
+
from: node.name === 'ValueName' ? node.from : cx.pos,
|
85
|
+
options: propertyValues.map(value => ({type: 'constant', label: value})),
|
86
|
+
span: /^[\w\P{ASCII}\-]+$/u,
|
87
|
+
};
|
65
88
|
}
|
66
|
-
|
67
|
-
return {
|
68
|
-
from: node.name === 'ValueName' ? node.from : cx.pos,
|
69
|
-
options: propertyValues.map(value => ({type: 'constant', label: value})),
|
70
|
-
span: /^[\w\P{ASCII}\-]+$/u,
|
71
|
-
};
|
89
|
+
return null;
|
72
90
|
}
|
73
91
|
|
74
92
|
function findColorsAndCurves(
|
@@ -380,12 +398,49 @@ export function cssBindings(): CodeMirror.Extension {
|
|
380
398
|
});
|
381
399
|
}
|
382
400
|
|
383
|
-
export class CSSPlugin extends Plugin {
|
401
|
+
export class CSSPlugin extends Plugin implements SDK.TargetManager.SDKModelObserver<SDK.CSSModel.CSSModel> {
|
402
|
+
#cssModel?: SDK.CSSModel.CSSModel;
|
403
|
+
|
404
|
+
constructor(uiSourceCode: Workspace.UISourceCode.UISourceCode, _transformer?: SourceFrame.SourceFrame.Transformer) {
|
405
|
+
super(uiSourceCode, _transformer);
|
406
|
+
SDK.TargetManager.TargetManager.instance().observeModels(SDK.CSSModel.CSSModel, this);
|
407
|
+
}
|
408
|
+
|
384
409
|
static accepts(uiSourceCode: Workspace.UISourceCode.UISourceCode): boolean {
|
385
410
|
return uiSourceCode.contentType().isStyleSheet();
|
386
411
|
}
|
387
412
|
|
413
|
+
modelAdded(cssModel: SDK.CSSModel.CSSModel): void {
|
414
|
+
if (this.#cssModel) {
|
415
|
+
return;
|
416
|
+
}
|
417
|
+
this.#cssModel = cssModel;
|
418
|
+
}
|
419
|
+
modelRemoved(cssModel: SDK.CSSModel.CSSModel): void {
|
420
|
+
if (this.#cssModel === cssModel) {
|
421
|
+
this.#cssModel = undefined;
|
422
|
+
}
|
423
|
+
}
|
424
|
+
|
388
425
|
editorExtension(): CodeMirror.Extension {
|
389
|
-
return [cssBindings(),
|
426
|
+
return [cssBindings(), this.#cssCompletion(), cssSwatches()];
|
427
|
+
}
|
428
|
+
|
429
|
+
#cssCompletion(): CodeMirror.Extension {
|
430
|
+
const {cssCompletionSource} = CodeMirror.css;
|
431
|
+
|
432
|
+
// CodeMirror binds the function below to the state object.
|
433
|
+
// Therefore, we can't access `this` and retrieve the following properties.
|
434
|
+
// Instead, retrieve them up front to bind them to the correct closure.
|
435
|
+
const uiSourceCode = this.uiSourceCode;
|
436
|
+
const cssModel = this.#cssModel;
|
437
|
+
|
438
|
+
return CodeMirror.autocompletion({
|
439
|
+
override:
|
440
|
+
[async(cx: CodeMirror.CompletionContext):
|
441
|
+
Promise<CodeMirror.CompletionResult|null> => {
|
442
|
+
return (await specificCssCompletion(cx, uiSourceCode, cssModel)) || cssCompletionSource(cx);
|
443
|
+
}],
|
444
|
+
});
|
390
445
|
}
|
391
446
|
}
|
@@ -12,6 +12,7 @@ import * as QuickOpen from '../../ui/legacy/components/quick_open/quick_open.js'
|
|
12
12
|
import * as SourceFrame from '../../ui/legacy/components/source_frame/source_frame.js';
|
13
13
|
import * as UI from '../../ui/legacy/legacy.js';
|
14
14
|
|
15
|
+
import * as Components from './components/components.js';
|
15
16
|
import {EditingLocationHistoryManager} from './EditingLocationHistoryManager.js';
|
16
17
|
import sourcesViewStyles from './sourcesView.css.js';
|
17
18
|
|
@@ -377,6 +378,10 @@ export class SourcesView extends Common.ObjectWrapper.eventMixin<EventTypes, typ
|
|
377
378
|
sourceView = new SourceFrame.ImageView.ImageView(uiSourceCode.mimeType(), uiSourceCode);
|
378
379
|
} else if (contentType === Common.ResourceType.resourceTypes.Font) {
|
379
380
|
sourceView = new SourceFrame.FontView.FontView(uiSourceCode.mimeType(), uiSourceCode);
|
381
|
+
} else if (
|
382
|
+
uiSourceCode.name() === HEADER_OVERRIDES_FILENAME &&
|
383
|
+
Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.HEADER_OVERRIDES)) {
|
384
|
+
sourceView = new Components.HeadersView.HeadersView(uiSourceCode);
|
380
385
|
} else {
|
381
386
|
sourceFrame = new UISourceCodeFrame(uiSourceCode);
|
382
387
|
}
|
@@ -399,10 +404,18 @@ export class SourcesView extends Common.ObjectWrapper.eventMixin<EventTypes, typ
|
|
399
404
|
if (widget instanceof SourceFrame.FontView.FontView) {
|
400
405
|
return SourceViewType.FontView;
|
401
406
|
}
|
407
|
+
if (widget instanceof Components.HeadersView.HeadersView) {
|
408
|
+
return SourceViewType.HeadersView;
|
409
|
+
}
|
402
410
|
return SourceViewType.SourceView;
|
403
411
|
}
|
404
412
|
|
405
|
-
#
|
413
|
+
#sourceViewTypeForUISourceCode(uiSourceCode: Workspace.UISourceCode.UISourceCode): SourceViewType {
|
414
|
+
if (uiSourceCode.name() === HEADER_OVERRIDES_FILENAME &&
|
415
|
+
Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.HEADER_OVERRIDES)) {
|
416
|
+
return SourceViewType.HeadersView;
|
417
|
+
}
|
418
|
+
const contentType = uiSourceCode.contentType();
|
406
419
|
switch (contentType) {
|
407
420
|
case Common.ResourceType.resourceTypes.Image:
|
408
421
|
return SourceViewType.ImageView;
|
@@ -417,8 +430,7 @@ export class SourcesView extends Common.ObjectWrapper.eventMixin<EventTypes, typ
|
|
417
430
|
const uiSourceCode = event.data;
|
418
431
|
const widget = this.sourceViewByUISourceCode.get(uiSourceCode);
|
419
432
|
if (widget) {
|
420
|
-
|
421
|
-
if (this.#sourceViewTypeForWidget(widget) !== this.#sourceViewTypeForContentType(contentType)) {
|
433
|
+
if (this.#sourceViewTypeForWidget(widget) !== this.#sourceViewTypeForUISourceCode(uiSourceCode)) {
|
422
434
|
// Remove the exisiting editor tab and create a new one of the correct type.
|
423
435
|
this.removeUISourceCodes([uiSourceCode]);
|
424
436
|
this.showSourceLocation(uiSourceCode);
|
@@ -602,20 +614,18 @@ export class SourcesView extends Common.ObjectWrapper.eventMixin<EventTypes, typ
|
|
602
614
|
}
|
603
615
|
|
604
616
|
save(): void {
|
605
|
-
this.
|
617
|
+
this.saveSourceView(this.visibleView());
|
606
618
|
}
|
607
619
|
|
608
620
|
saveAll(): void {
|
609
621
|
const sourceFrames = this.editorContainer.fileViews();
|
610
|
-
sourceFrames.forEach(this.
|
622
|
+
sourceFrames.forEach(this.saveSourceView.bind(this));
|
611
623
|
}
|
612
624
|
|
613
|
-
private
|
614
|
-
if (
|
615
|
-
|
625
|
+
private saveSourceView(sourceView: UI.Widget.Widget|null): void {
|
626
|
+
if (sourceView instanceof UISourceCodeFrame || sourceView instanceof Components.HeadersView.HeadersView) {
|
627
|
+
sourceView.commitEditing();
|
616
628
|
}
|
617
|
-
const uiSourceCodeFrame = (sourceFrame as UISourceCodeFrame);
|
618
|
-
uiSourceCodeFrame.commitEditing();
|
619
629
|
}
|
620
630
|
|
621
631
|
toggleBreakpointsActiveState(active: boolean): void {
|
@@ -764,9 +774,12 @@ export class ActionDelegate implements UI.ActionRegistration.ActionDelegate {
|
|
764
774
|
}
|
765
775
|
}
|
766
776
|
|
777
|
+
const HEADER_OVERRIDES_FILENAME = '.headers';
|
778
|
+
|
767
779
|
// eslint-disable-next-line rulesdir/const_enum
|
768
780
|
enum SourceViewType {
|
769
781
|
ImageView = 'ImageView',
|
770
782
|
FontView = 'FontView',
|
783
|
+
HeadersView = 'HeadersView',
|
771
784
|
SourceView = 'SourceView',
|
772
785
|
}
|
@@ -0,0 +1,32 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright 2022 The Chromium Authors. All rights reserved.
|
3
|
+
* Use of this source code is governed by a BSD-style license that can be
|
4
|
+
* found in the LICENSE file.
|
5
|
+
*/
|
6
|
+
|
7
|
+
:host {
|
8
|
+
flex-grow: 1;
|
9
|
+
}
|
10
|
+
|
11
|
+
.center-wrapper {
|
12
|
+
height: 100%;
|
13
|
+
display: flex;
|
14
|
+
justify-content: center;
|
15
|
+
align-items: center;
|
16
|
+
}
|
17
|
+
|
18
|
+
.centered {
|
19
|
+
margin: 1em;
|
20
|
+
max-width: 300px;
|
21
|
+
text-align: center;
|
22
|
+
}
|
23
|
+
|
24
|
+
.error-header {
|
25
|
+
font-weight: bold;
|
26
|
+
margin-bottom: 1em;
|
27
|
+
}
|
28
|
+
|
29
|
+
.error-body {
|
30
|
+
line-height: 1.5em;
|
31
|
+
color: var(--color-text-secondary);
|
32
|
+
}
|
@@ -0,0 +1,89 @@
|
|
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 * as i18n from '../../../core/i18n/i18n.js';
|
6
|
+
import * as Persistence from '../../../models/persistence/persistence.js';
|
7
|
+
import type * as Workspace from '../../../models/workspace/workspace.js';
|
8
|
+
import * as ComponentHelpers from '../../../ui/components/helpers/helpers.js';
|
9
|
+
import * as UI from '../../../ui/legacy/legacy.js';
|
10
|
+
import * as LitHtml from '../../../ui/lit-html/lit-html.js';
|
11
|
+
|
12
|
+
import HeadersViewStyles from './HeadersView.css.js';
|
13
|
+
|
14
|
+
const UIStrings = {
|
15
|
+
/**
|
16
|
+
*@description Error message for files which cannot not be parsed.
|
17
|
+
*@example {.headers} PH1
|
18
|
+
*/
|
19
|
+
errorWhenParsing: 'Error when parsing \'\'{PH1}\'\'.',
|
20
|
+
/**
|
21
|
+
*@description Explainer for files which cannot be parsed.
|
22
|
+
*@example {.headers} PH1
|
23
|
+
*/
|
24
|
+
parsingErrorExplainer:
|
25
|
+
'This is most likely due to a syntax error in \'\'{PH1}\'\'. Try opening this file in an external editor to fix the error or delete the file and re-create the override.',
|
26
|
+
};
|
27
|
+
const str_ = i18n.i18n.registerUIStrings('panels/sources/components/HeadersView.ts', UIStrings);
|
28
|
+
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
29
|
+
|
30
|
+
export class HeadersView extends UI.View.SimpleView {
|
31
|
+
readonly #headersViewComponent = new HeadersViewComponent();
|
32
|
+
#uiSourceCode: Workspace.UISourceCode.UISourceCode;
|
33
|
+
|
34
|
+
constructor(uiSourceCode: Workspace.UISourceCode.UISourceCode) {
|
35
|
+
super('HeadersView');
|
36
|
+
this.#uiSourceCode = uiSourceCode;
|
37
|
+
this.element.appendChild(this.#headersViewComponent);
|
38
|
+
this.#headersViewComponent.data = {
|
39
|
+
uiSourceCode: this.#uiSourceCode,
|
40
|
+
};
|
41
|
+
}
|
42
|
+
|
43
|
+
commitEditing(): void {
|
44
|
+
this.#uiSourceCode.commitWorkingCopy();
|
45
|
+
Persistence.NetworkPersistenceManager.NetworkPersistenceManager.instance().updateInterceptionPatterns();
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
export interface HeadersViewComponentData {
|
50
|
+
uiSourceCode: Workspace.UISourceCode.UISourceCode;
|
51
|
+
}
|
52
|
+
|
53
|
+
export class HeadersViewComponent extends HTMLElement {
|
54
|
+
static readonly litTagName = LitHtml.literal`devtools-sources-headers-view`;
|
55
|
+
readonly #shadow = this.attachShadow({mode: 'open'});
|
56
|
+
#uiSourceCode: Workspace.UISourceCode.UISourceCode|null = null;
|
57
|
+
|
58
|
+
connectedCallback(): void {
|
59
|
+
this.#shadow.adoptedStyleSheets = [HeadersViewStyles];
|
60
|
+
}
|
61
|
+
|
62
|
+
set data(data: HeadersViewComponentData) {
|
63
|
+
this.#uiSourceCode = data.uiSourceCode;
|
64
|
+
this.#render();
|
65
|
+
}
|
66
|
+
|
67
|
+
#render(): void {
|
68
|
+
const fileName = this.#uiSourceCode?.name() || '.headers';
|
69
|
+
// clang-format off
|
70
|
+
LitHtml.render(LitHtml.html`
|
71
|
+
<div class="center-wrapper">
|
72
|
+
<div class="centered">
|
73
|
+
<div class="error-header">${i18nString(UIStrings.errorWhenParsing, {PH1: fileName})}</div>
|
74
|
+
<div class="error-body">${i18nString(UIStrings.parsingErrorExplainer, {PH1: fileName})}</div>
|
75
|
+
</div>
|
76
|
+
</div>
|
77
|
+
`, this.#shadow, {host: this});
|
78
|
+
// clang-format on
|
79
|
+
}
|
80
|
+
}
|
81
|
+
|
82
|
+
ComponentHelpers.CustomElements.defineComponent('devtools-sources-headers-view', HeadersViewComponent);
|
83
|
+
|
84
|
+
declare global {
|
85
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
86
|
+
interface HTMLElementTagNameMap {
|
87
|
+
'devtools-sources-headers-view': HeadersViewComponent;
|
88
|
+
}
|
89
|
+
}
|
@@ -0,0 +1,62 @@
|
|
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 * as SDK from '../../../core/sdk/sdk.js';
|
6
|
+
import * as LitHtml from '../../lit-html/lit-html.js';
|
7
|
+
import * as ComponentHelpers from '../helpers/helpers.js';
|
8
|
+
|
9
|
+
import chromeLinkStyles from './chromeLink.css.js';
|
10
|
+
|
11
|
+
declare global {
|
12
|
+
interface HTMLElementTagNameMap {
|
13
|
+
'devtools-chrome-link': ChromeLink;
|
14
|
+
}
|
15
|
+
}
|
16
|
+
|
17
|
+
// Use this component to render links to 'chrome://...'-URLs
|
18
|
+
// (for which regular <x-link>s do not work).
|
19
|
+
export class ChromeLink extends HTMLElement {
|
20
|
+
static readonly litTagName = LitHtml.literal`devtools-chrome-link`;
|
21
|
+
readonly #shadow = this.attachShadow({mode: 'open'});
|
22
|
+
readonly #boundRender = this.#render.bind(this);
|
23
|
+
#href: string = '';
|
24
|
+
|
25
|
+
connectedCallback(): void {
|
26
|
+
this.#shadow.adoptedStyleSheets = [chromeLinkStyles];
|
27
|
+
void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender);
|
28
|
+
}
|
29
|
+
|
30
|
+
set href(href: string) {
|
31
|
+
if (!href.startsWith('chrome://')) {
|
32
|
+
throw new Error('ChromeLink href needs to start with \'chrome://\'');
|
33
|
+
}
|
34
|
+
this.#href = href;
|
35
|
+
void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#boundRender);
|
36
|
+
}
|
37
|
+
|
38
|
+
// Navigating to a chrome:// link via a normal anchor doesn't work, so we "navigate"
|
39
|
+
// there using CDP.
|
40
|
+
openSettingsTab(event: Event): void {
|
41
|
+
if (event.type === 'click' || (event.type === 'keydown' && self.isEnterOrSpaceKey(event))) {
|
42
|
+
const mainTarget = SDK.TargetManager.TargetManager.instance().mainTarget();
|
43
|
+
mainTarget && mainTarget.targetAgent().invoke_createTarget({url: this.#href});
|
44
|
+
event.consume(true);
|
45
|
+
}
|
46
|
+
}
|
47
|
+
|
48
|
+
#render(): void {
|
49
|
+
// clang-format off
|
50
|
+
LitHtml.render(
|
51
|
+
/* x-link doesn't work with custom click/keydown handlers */
|
52
|
+
/* eslint-disable rulesdir/ban_a_tags_in_lit_html */
|
53
|
+
LitHtml.html`
|
54
|
+
<a href=${this.#href} class="link" target="_blank"
|
55
|
+
@click=${this.openSettingsTab}
|
56
|
+
@keydown=${this.openSettingsTab}><slot></slot></a>
|
57
|
+
`, this.#shadow, {host: this});
|
58
|
+
// clang-format on
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
ComponentHelpers.CustomElements.defineComponent('devtools-chrome-link', ChromeLink);
|
@@ -0,0 +1,12 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright 2022 The Chromium Authors. All rights reserved.
|
3
|
+
* Use of this source code is governed by a BSD-style license that can be
|
4
|
+
* found in the LICENSE file.
|
5
|
+
*/
|
6
|
+
|
7
|
+
.link {
|
8
|
+
color: var(--color-link);
|
9
|
+
text-decoration: underline;
|
10
|
+
cursor: pointer;
|
11
|
+
padding: 2px 0; /* adjust focus ring size */
|
12
|
+
}
|
package/package.json
CHANGED