Skip to content

Commit

Permalink
preload: Add a pretty print toggle to preloading panel in Devtools
Browse files Browse the repository at this point in the history
This CL adds a pretty print toggle to preloading panel in Devtools, and
make pretty print by default. Also, pretty print can be disabled by
`auto-pretty-print-minified` as source panel.

Preloading panel pretty print on:
https://drive.google.com/file/d/1R4ZAUhwpEB6cEIbz3P_LNtiqoGi3kpXY/view?usp=sharing

Preloading panel pretty print off:
https://drive.google.com/file/d/1-EIviebw2B5iZCWAz03atbcKVsFe2umW/view?usp=sharing

Bug: 40279147
Change-Id: I35f9b35a191b33db426021ef0c03e9f058e005a2
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/5820294
Reviewed-by: Wolfgang Beyer <[email protected]>
Commit-Queue: Huanpo Lin <[email protected]>
  • Loading branch information
HuanPo Lin authored and Devtools-frontend LUCI CQ committed Sep 24, 2024
1 parent 048bfeb commit e483ef2
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 20 deletions.
35 changes: 32 additions & 3 deletions front_end/panels/application/preloading/PreloadingView.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import type * as Common from '../../../core/common/common.js';
import * as Common from '../../../core/common/common.js';
import * as i18n from '../../../core/i18n/i18n.js';
import * as Platform from '../../../core/platform/platform.js';
import {assertNotNullOrUndefined} from '../../../core/platform/platform.js';
import * as SDK from '../../../core/sdk/sdk.js';
import * as Protocol from '../../../generated/protocol.js';
import * as Bindings from '../../../models/bindings/bindings.js';
import * as Buttons from '../../../ui/components/buttons/buttons.js';
import type * as DataGrid from '../../../ui/components/data_grid/data_grid.js';
import * as SplitView from '../../../ui/components/split_view/split_view.js';
// eslint-disable-next-line rulesdir/es_modules_import
Expand Down Expand Up @@ -72,6 +73,10 @@ const UIStrings = {
*@description Text in grid and details: Preloading failed.
*/
statusFailure: 'Failure',
/**
*@description Text to pretty print a file
*/
prettyPrint: 'Pretty print',
};
const str_ = i18n.i18n.registerUIStrings('panels/application/preloading/PreloadingView.ts', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
Expand Down Expand Up @@ -179,6 +184,8 @@ export class PreloadingRuleSetView extends UI.Widget.VBox {
private readonly ruleSetGrid = new PreloadingComponents.RuleSetGrid.RuleSetGrid();
private readonly ruleSetDetails = new PreloadingComponents.RuleSetDetailsView.RuleSetDetailsView();

private shouldPrettyPrint = Common.Settings.Settings.instance().moduleSetting('auto-pretty-print-minified').get();

constructor(model: SDK.PreloadingModel.PreloadingModel) {
super(/* isWebComponent */ true, /* delegatesFocus */ false);

Expand Down Expand Up @@ -209,17 +216,38 @@ export class PreloadingRuleSetView extends UI.Widget.VBox {
this.warningsView.show(this.warningsContainer);

this.ruleSetGrid.addEventListener('cellfocused', this.onRuleSetsGridCellFocused.bind(this));

const onPrettyPrintToggle = (): void => {
this.shouldPrettyPrint = !this.shouldPrettyPrint;
this.updateRuleSetDetails();
};

LitHtml.render(
LitHtml.html`
<${SplitView.SplitView.SplitView.litTagName} .horizontal=${true} style="--min-sidebar-size: 0px">
<${SplitView.SplitView.SplitView.litTagName} .horizontal=${
true} style="--min-sidebar-size: max(100vh-200px, 0px)">
<div slot="main" class="overflow-auto" style="height: 100%">
${this.ruleSetGrid}
</div>
<div slot="sidebar" class="overflow-auto" style="height: 100%"
jslog=${VisualLogging.section('rule-set-details')}>
${this.ruleSetDetails}
</div>
</${SplitView.SplitView.SplitView.litTagName}>`,
</${SplitView.SplitView.SplitView.litTagName}>
<div class="pretty-print-button" style="border-top: 1px solid var(--sys-color-divider)">
<${Buttons.Button.Button.litTagName}
.iconName=${'brackets'}
.toggledIconName=${'brackets'}
.toggled=${this.shouldPrettyPrint}
.toggleType=${Buttons.Button.ToggleType.PRIMARY}
.title=${i18nString(UIStrings.prettyPrint)}
.variant=${Buttons.Button.Variant.ICON_TOGGLE}
.size=${Buttons.Button.Size.SMALL}
@click=${onPrettyPrintToggle}
jslog=${VisualLogging.action().track({click: true}).context('preloading-status-panel-pretty-print')}>
</${Buttons.Button.Button.litTagName}>
</div>
`,
this.contentElement, {host: this});
this.hsplit = this.contentElement.querySelector('devtools-split-view') as SplitView.SplitView.SplitView;
}
Expand Down Expand Up @@ -249,6 +277,7 @@ export class PreloadingRuleSetView extends UI.Widget.VBox {
private updateRuleSetDetails(): void {
const id = this.focusedRuleSetId;
const ruleSet = id === null ? null : this.model.getRuleSetById(id);
this.ruleSetDetails.shouldPrettyPrint = this.shouldPrettyPrint;
this.ruleSetDetails.data = ruleSet;

if (ruleSet === null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ import * as PreloadingComponents from './components.js';

const coordinator = Coordinator.RenderCoordinator.RenderCoordinator.instance();

async function renderRuleSetDetailsView(data: PreloadingComponents.RuleSetDetailsView.RuleSetDetailsViewData):
Promise<HTMLElement> {
async function renderRuleSetDetailsView(
data: PreloadingComponents.RuleSetDetailsView.RuleSetDetailsViewData,
shouldPrettyPrint: boolean): Promise<HTMLElement> {
const component = new PreloadingComponents.RuleSetDetailsView.RuleSetDetailsView();
component.shouldPrettyPrint = shouldPrettyPrint;
component.data = data;
renderElementIntoDOM(component);
assert.isNotNull(component.shadowRoot);
Expand All @@ -29,7 +31,8 @@ describeWithEnvironment('RuleSetDetailsView', () => {
it('renders nothing if not selected', async () => {
const data = null;

const component = await renderRuleSetDetailsView(data);
const component = await renderRuleSetDetailsView(data, false);
await coordinator.done({waitForWork: true});
assert.isNotNull(component.shadowRoot);
assert.strictEqual(component.shadowRoot.textContent, '');
});
Expand All @@ -50,7 +53,8 @@ describeWithEnvironment('RuleSetDetailsView', () => {
`,
backendNodeId: 1 as Protocol.DOM.BackendNodeId,
};
const component = await renderRuleSetDetailsView(data);
const component = await renderRuleSetDetailsView(data, false);
await coordinator.done({waitForWork: true});
assert.deepEqual(component.shadowRoot?.getElementById('error-message-text')?.textContent, undefined);

const textEditor = component.shadowRoot?.querySelector('devtools-text-editor') as TextEditor.TextEditor.TextEditor;
Expand All @@ -74,7 +78,8 @@ describeWithEnvironment('RuleSetDetailsView', () => {
url: 'https://example.com/speculationrules.json',
requestId: 'reqeustId' as Protocol.Network.RequestId,
};
const component = await renderRuleSetDetailsView(data);
const component = await renderRuleSetDetailsView(data, false);
await coordinator.done({waitForWork: true});
assert.deepEqual(component.shadowRoot?.getElementById('error-message-text')?.textContent, undefined);
const textEditor = component.shadowRoot?.querySelector('devtools-text-editor') as TextEditor.TextEditor.TextEditor;
assert.strictEqual(textEditor.state.doc.toString(), data.sourceText);
Expand All @@ -94,7 +99,8 @@ describeWithEnvironment('RuleSetDetailsView', () => {
errorType: Protocol.Preload.RuleSetErrorType.SourceIsNotJsonObject,
errorMessage: 'Line: 6, column: 1, Syntax error.',
};
const component = await renderRuleSetDetailsView(data);
const component = await renderRuleSetDetailsView(data, false);
await coordinator.done({waitForWork: true});
assert.deepEqual(
component.shadowRoot?.getElementById('error-message-text')?.textContent, 'Line: 6, column: 1, Syntax error.');
const textEditor = component.shadowRoot?.querySelector('devtools-text-editor') as TextEditor.TextEditor.TextEditor;
Expand All @@ -118,11 +124,38 @@ describeWithEnvironment('RuleSetDetailsView', () => {
errorType: Protocol.Preload.RuleSetErrorType.InvalidRulesSkipped,
errorMessage: 'A list rule must have a "urls" array.',
};
const component = await renderRuleSetDetailsView(data);
const component = await renderRuleSetDetailsView(data, false);
await coordinator.done({waitForWork: true});
assert.deepEqual(
component.shadowRoot?.getElementById('error-message-text')?.textContent,
'A list rule must have a "urls" array.');
const textEditor = component.shadowRoot?.querySelector('devtools-text-editor') as TextEditor.TextEditor.TextEditor;
assert.strictEqual(textEditor.state.doc.toString(), data.sourceText);
});

it('renders formatted rule set', async () => {
const data: Protocol.Preload.RuleSet = {
id: 'ruleSetId:1' as Protocol.Preload.RuleSetId,
loaderId: 'loaderId:1' as Protocol.Network.LoaderId,
sourceText: '{"prefetch":[{"source": "list","urls": ["/subresource.js"]}]}',
backendNodeId: 1 as Protocol.DOM.BackendNodeId,
};
const component = await renderRuleSetDetailsView(data, true);
await coordinator.done({waitForWork: true});
assert.deepEqual(component.shadowRoot?.getElementById('error-message-text')?.textContent, undefined);

const textEditor = component.shadowRoot?.querySelector('devtools-text-editor') as TextEditor.TextEditor.TextEditor;
// Formatted sourceText should be different from the data.sourceText in this case.
assert.notEqual(textEditor.state.doc.toString(), data.sourceText);
assert.strictEqual(textEditor.state.doc.toString(), `{
"prefetch": [
{
"source": "list",
"urls": [
"/subresource.js"
]
}
]
}`);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import {assertNotNullOrUndefined} from '../../../../core/platform/platform.js';
import * as SDK from '../../../../core/sdk/sdk.js';
import type * as Protocol from '../../../../generated/protocol.js';
import * as Formatter from '../../../../models/formatter/formatter.js';
import * as CodeMirror from '../../../../third_party/codemirror.next/codemirror.next.js';
import * as CodeHighlighter from '../../../../ui/components/code_highlighter/code_highlighter.js';
import * as IconButton from '../../../../ui/components/icon_button/icon_button.js';
Expand All @@ -26,6 +27,7 @@ export type RuleSetDetailsViewData = RuleSet|null;
export class RuleSetDetailsView extends LegacyWrapper.LegacyWrapper.WrappableComponent<UI.Widget.VBox> {
readonly #shadow = this.attachShadow({mode: 'open'});
#data: RuleSetDetailsViewData = null;
#shouldPrettyPrint: boolean = true;
#editorState?: CodeMirror.EditorState;

connectedCallback(): void {
Expand All @@ -37,7 +39,13 @@ export class RuleSetDetailsView extends LegacyWrapper.LegacyWrapper.WrappableCom
void this.#render();
}

set shouldPrettyPrint(shouldPrettyPrint: boolean) {
this.#shouldPrettyPrint = shouldPrettyPrint;
}

async #render(): Promise<void> {
const sourceText = await this.#getSourceText();

await coordinator.write('RuleSetDetailsView render', () => {
if (this.#data === null) {
LitHtml.render(LitHtml.nothing, this.#shadow, {host: this});
Expand All @@ -52,7 +60,7 @@ export class RuleSetDetailsView extends LegacyWrapper.LegacyWrapper.WrappableCom
${this.#maybeError()}
</div>
<div class="text-ellipsis">
${this.#renderSource()}
${this.#renderSource(sourceText)}
</div>
`, this.#shadow, {host: this});
// clang-format on
Expand Down Expand Up @@ -85,28 +93,37 @@ export class RuleSetDetailsView extends LegacyWrapper.LegacyWrapper.WrappableCom
// clang-format on
}

#renderSource(): LitHtml.LitTemplate {
#renderSource(sourceText: string): LitHtml.LitTemplate {
this.#editorState = CodeMirror.EditorState.create({
doc: this.#data?.sourceText,
doc: sourceText,
extensions: [
TextEditor.Config.baseConfiguration(this.#data?.sourceText || ''),
TextEditor.Config.baseConfiguration(sourceText || ''),
CodeMirror.lineNumbers(),
CodeMirror.EditorState.readOnly.of(true),
codeMirrorJsonType as CodeMirror.Extension,
CodeMirror.syntaxHighlighting(CodeHighlighter.CodeHighlighter.highlightStyle),
],
});

// Disabled until https://crbug.com/1079231 is fixed.
// clang-format off
// TODO(https://crbug.com/1425354): Add Raw button.
return LitHtml.html`
<${TextEditor.TextEditor.TextEditor.litTagName} .style.flexGrow = '1' .state=${
this.#editorState
}></${TextEditor.TextEditor.TextEditor.litTagName}>
`;
return LitHtml.html`
<${TextEditor.TextEditor.TextEditor.litTagName} .style.flexGrow = '1' .state=${
this.#editorState
}></${TextEditor.TextEditor.TextEditor.litTagName}>
`;
// clang-format on
}

async #getSourceText(): Promise<string> {
if (this.#shouldPrettyPrint && this.#data?.sourceText !== undefined) {
const formattedResult =
await Formatter.ScriptFormatter.formatScriptContent('application/json', this.#data.sourceText);
return formattedResult.formattedContent;
}

return this.#data?.sourceText || '';
}
}

customElements.define('devtools-resources-rulesets-details-view', RuleSetDetailsView);
Expand Down

0 comments on commit e483ef2

Please sign in to comment.