@quick-game/cli
Version:
Command line interface for rapid qg development
495 lines • 22.6 kB
JavaScript
// Copyright 2022 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import { assertNotNullOrUndefined } from '../../../core/platform/platform.js';
import * as Platform from '../../../core/platform/platform.js';
import * as SplitView from '../../../ui/components/split_view/split_view.js';
import * as i18n from '../../../core/i18n/i18n.js';
import * as UI from '../../../ui/legacy/legacy.js';
import * as SDK from '../../../core/sdk/sdk.js';
import * as LitHtml from '../../../ui/lit-html/lit-html.js';
import * as Bindings from '../../../models/bindings/bindings.js';
import * as PreloadingComponents from './components/components.js';
// eslint-disable-next-line rulesdir/es_modules_import
import emptyWidgetStyles from '../../../ui/legacy/emptyWidget.css.js';
import preloadingViewStyles from './preloadingView.css.js';
const UIStrings = {
/**
*@description DropDown title for filtering preloading attempts by rule set
*/
filterFilterByRuleSet: 'Filter by rule set',
/**
*@description DropDown text for filtering preloading attempts by rule set: No filter
*/
filterAllPreloads: 'All preloads',
/**
*@description Text in grid: Rule set is valid
*/
validityValid: 'Valid',
/**
*@description Text in grid: Rule set must be a valid JSON object
*/
validityInvalid: 'Invalid',
/**
*@description Text in grid: Rule set contains invalid rules and they are ignored
*/
validitySomeRulesInvalid: 'Some rules invalid',
/**
*@description Text in grid and details: Preloading attempt is not yet triggered.
*/
statusNotTriggered: 'Not triggered',
/**
*@description Text in grid and details: Preloading attempt is eligible but pending.
*/
statusPending: 'Pending',
/**
*@description Text in grid and details: Preloading is running.
*/
statusRunning: 'Running',
/**
*@description Text in grid and details: Preloading finished and the result is ready for the next navigation.
*/
statusReady: 'Ready',
/**
*@description Text in grid and details: Ready, then used.
*/
statusSuccess: 'Success',
/**
*@description Text in grid and details: Preloading failed.
*/
statusFailure: 'Failure',
};
const str_ = i18n.i18n.registerUIStrings('panels/application/preloading/PreloadingView.ts', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
class PreloadingUIUtils {
static status(status) {
// See content/public/browser/preloading.h PreloadingAttemptOutcome.
switch (status) {
case "NotTriggered" /* SDK.PreloadingModel.PreloadingStatus.NotTriggered */:
return i18nString(UIStrings.statusNotTriggered);
case "Pending" /* SDK.PreloadingModel.PreloadingStatus.Pending */:
return i18nString(UIStrings.statusPending);
case "Running" /* SDK.PreloadingModel.PreloadingStatus.Running */:
return i18nString(UIStrings.statusRunning);
case "Ready" /* SDK.PreloadingModel.PreloadingStatus.Ready */:
return i18nString(UIStrings.statusReady);
case "Success" /* SDK.PreloadingModel.PreloadingStatus.Success */:
return i18nString(UIStrings.statusSuccess);
case "Failure" /* SDK.PreloadingModel.PreloadingStatus.Failure */:
return i18nString(UIStrings.statusFailure);
// NotSupported is used to handle unreachable case. For example,
// there is no code path for
// PreloadingTriggeringOutcome::kTriggeredButPending in prefetch,
// which is mapped to NotSupported. So, we regard it as an
// internal error.
case "NotSupported" /* SDK.PreloadingModel.PreloadingStatus.NotSupported */:
return i18n.i18n.lockedString('Internal error');
}
}
static preloadsStatusSummary(countsByStatus) {
const LIST = [
"NotTriggered" /* SDK.PreloadingModel.PreloadingStatus.NotTriggered */,
"Pending" /* SDK.PreloadingModel.PreloadingStatus.Pending */,
"Running" /* SDK.PreloadingModel.PreloadingStatus.Running */,
"Ready" /* SDK.PreloadingModel.PreloadingStatus.Ready */,
"Success" /* SDK.PreloadingModel.PreloadingStatus.Success */,
"Failure" /* SDK.PreloadingModel.PreloadingStatus.Failure */,
];
return LIST.filter(status => (countsByStatus?.get(status) || 0) > 0)
.map(status => (countsByStatus?.get(status) || 0) + ' ' + this.status(status))
.join(', ');
}
// Summary of error of rule set shown in grid.
static validity({ errorType }) {
switch (errorType) {
case undefined:
return i18nString(UIStrings.validityValid);
case "SourceIsNotJsonObject" /* Protocol.Preload.RuleSetErrorType.SourceIsNotJsonObject */:
return i18nString(UIStrings.validityInvalid);
case "InvalidRulesSkipped" /* Protocol.Preload.RuleSetErrorType.InvalidRulesSkipped */:
return i18nString(UIStrings.validitySomeRulesInvalid);
}
}
// Where a rule set came from, shown in grid.
static location(ruleSet) {
if (ruleSet.backendNodeId !== undefined) {
return i18n.i18n.lockedString('<script>');
}
if (ruleSet.url !== undefined) {
return ruleSet.url;
}
throw Error('unreachable');
}
static processLocalId(id) {
// RuleSetId is form of '<processId>.<processLocalId>'
const index = id.indexOf('.');
return index === -1 ? id : id.slice(index + 1);
}
// TODO(https://crbug.com/1410709): Move
// front_end/panels/application/preloading/components/PreloadingString.ts
// to
// front_end/panels/application/preloading/helper/PreloadingString.ts
// and use PreloadingString.ruleSetLocationShort.
static ruleSetLocationShort(ruleSet, pageURL) {
const url = ruleSet.url === undefined ? pageURL : ruleSet.url;
return Bindings.ResourceUtils.displayNameForURL(url);
}
}
function pageURL() {
return SDK.TargetManager.TargetManager.instance().scopeTarget()?.inspectedURL() ||
'';
}
export class PreloadingRuleSetView extends UI.Widget.VBox {
model;
focusedRuleSetId = null;
focusedPreloadingAttemptId = null;
warningsContainer;
warningsView = new PreloadingWarningsView();
hsplit;
ruleSetGrid = new PreloadingComponents.RuleSetGrid.RuleSetGrid();
ruleSetDetails = new PreloadingComponents.RuleSetDetailsView.RuleSetDetailsView();
constructor(model) {
super(/* isWebComponent */ true, /* delegatesFocus */ false);
this.model = model;
SDK.TargetManager.TargetManager.instance().addScopeChangeListener(this.onScopeChange.bind(this));
SDK.TargetManager.TargetManager.instance().addModelListener(SDK.PreloadingModel.PreloadingModel, SDK.PreloadingModel.Events.ModelUpdated, this.render, this, { scoped: true });
SDK.TargetManager.TargetManager.instance().addModelListener(SDK.PreloadingModel.PreloadingModel, SDK.PreloadingModel.Events.WarningsUpdated, this.warningsView.onWarningsUpdated, this.warningsView, { scoped: true });
// this (VBox)
// +- warningsContainer
// +- PreloadingWarningsView
// +- hsplit
// +- leftContainer
// +- RuleSetGrid
// +- rightContainer
// +- RuleSetDetailsView
//
// - If an row of RuleSetGrid selected, RuleSetDetailsView shows details of it.
// - If not, RuleSetDetailsView hides.
this.warningsContainer = document.createElement('div');
this.warningsContainer.classList.add('flex-none');
this.contentElement.insertBefore(this.warningsContainer, this.contentElement.firstChild);
this.warningsView.show(this.warningsContainer);
this.ruleSetGrid.addEventListener('cellfocused', this.onRuleSetsGridCellFocused.bind(this));
LitHtml.render(LitHtml.html `
<${SplitView.SplitView.SplitView.litTagName} .horizontal=${true} style="--min-sidebar-size: 0px">
<div slot="main" class="overflow-auto" style="height: 100%">
${this.ruleSetGrid}
</div>
<div slot="sidebar" class="overflow-auto" style="height: 100%">
${this.ruleSetDetails}
</div>
</${SplitView.SplitView.SplitView.litTagName}>`, this.contentElement, { host: this });
this.hsplit = this.contentElement.querySelector('devtools-split-view');
}
wasShown() {
super.wasShown();
this.registerCSSFiles([emptyWidgetStyles, preloadingViewStyles]);
this.warningsView.wasShown();
this.render();
}
onScopeChange() {
const model = SDK.TargetManager.TargetManager.instance().scopeTarget()?.model(SDK.PreloadingModel.PreloadingModel);
assertNotNullOrUndefined(model);
this.model = model;
this.render();
}
revealRuleSet(revealInfo) {
this.focusedRuleSetId = revealInfo.ruleSetId;
this.render();
}
updateRuleSetDetails() {
const id = this.focusedRuleSetId;
const ruleSet = id === null ? null : this.model.getRuleSetById(id);
this.ruleSetDetails.data = ruleSet;
if (ruleSet === null) {
this.hsplit.style.setProperty('--current-main-area-size', '100%');
}
else {
this.hsplit.style.setProperty('--current-main-area-size', '60%');
}
}
render() {
// Update rule sets grid
const countsByRuleSetId = this.model.getPreloadCountsByRuleSetId();
const ruleSetRows = this.model.getAllRuleSets().map(({ id, value }) => {
const countsByStatus = countsByRuleSetId.get(id) || new Map();
return {
ruleSet: value,
preloadsStatusSummary: PreloadingUIUtils.preloadsStatusSummary(countsByStatus),
};
});
this.ruleSetGrid.update({ rows: ruleSetRows, pageURL: pageURL() });
this.updateRuleSetDetails();
}
onRuleSetsGridCellFocused(event) {
const focusedEvent = event;
this.focusedRuleSetId =
focusedEvent.data.row.cells.find(cell => cell.columnId === 'id')?.value;
this.render();
}
getInfobarContainerForTest() {
return this.warningsView.contentElement;
}
getRuleSetGridForTest() {
return this.ruleSetGrid;
}
getRuleSetDetailsForTest() {
return this.ruleSetDetails;
}
}
export class PreloadingAttemptView extends UI.Widget.VBox {
model;
focusedPreloadingAttemptId = null;
warningsContainer;
warningsView = new PreloadingWarningsView();
preloadingGrid = new PreloadingComponents.PreloadingGrid.PreloadingGrid();
preloadingDetails = new PreloadingComponents.PreloadingDetailsReportView.PreloadingDetailsReportView();
ruleSetSelector;
constructor(model) {
super(/* isWebComponent */ true, /* delegatesFocus */ false);
this.model = model;
SDK.TargetManager.TargetManager.instance().addScopeChangeListener(this.onScopeChange.bind(this));
SDK.TargetManager.TargetManager.instance().addModelListener(SDK.PreloadingModel.PreloadingModel, SDK.PreloadingModel.Events.ModelUpdated, this.render, this, { scoped: true });
SDK.TargetManager.TargetManager.instance().addModelListener(SDK.PreloadingModel.PreloadingModel, SDK.PreloadingModel.Events.WarningsUpdated, this.warningsView.onWarningsUpdated, this.warningsView, { scoped: true });
// this (VBox)
// +- warningsContainer
// +- PreloadingWarningsView
// +- VBox
// +- toolbar (filtering)
// +- hsplit
// +- leftContainer
// +- PreloadingGrid
// +- rightContainer
// +- PreloadingDetailsReportView
//
// - If an row of PreloadingGrid selected, PreloadingDetailsReportView shows details of it.
// - If not, PreloadingDetailsReportView shows some messages.
this.warningsContainer = document.createElement('div');
this.warningsContainer.classList.add('flex-none');
this.contentElement.insertBefore(this.warningsContainer, this.contentElement.firstChild);
this.warningsView.show(this.warningsContainer);
const vbox = new UI.Widget.VBox();
const toolbar = new UI.Toolbar.Toolbar('preloading-toolbar', vbox.contentElement);
this.ruleSetSelector = new PreloadingRuleSetSelector(() => this.render());
toolbar.appendToolbarItem(this.ruleSetSelector.item());
this.preloadingGrid.addEventListener('cellfocused', this.onPreloadingGridCellFocused.bind(this));
LitHtml.render(LitHtml.html `
<${SplitView.SplitView.SplitView.litTagName} .horizontal=${true} style="--min-sidebar-size: 0px">
<div slot="main" class="overflow-auto" style="height: 100%">
${this.preloadingGrid}
</div>
<div slot="sidebar" class="overflow-auto" style="height: 100%">
${this.preloadingDetails}
</div>
</${SplitView.SplitView.SplitView.litTagName}>`, vbox.contentElement, { host: this });
vbox.show(this.contentElement);
}
wasShown() {
super.wasShown();
this.registerCSSFiles([emptyWidgetStyles, preloadingViewStyles]);
this.warningsView.wasShown();
this.render();
}
onScopeChange() {
const model = SDK.TargetManager.TargetManager.instance().scopeTarget()?.model(SDK.PreloadingModel.PreloadingModel);
assertNotNullOrUndefined(model);
this.model = model;
this.render();
}
setFilter(filter) {
const id = filter.ruleSetId;
this.model.getRuleSetById(id) && this.ruleSetSelector.select(id);
}
updatePreloadingDetails() {
const id = this.focusedPreloadingAttemptId;
const preloadingAttempt = id === null ? null : this.model.getPreloadingAttemptById(id);
if (preloadingAttempt === null) {
this.preloadingDetails.data = null;
}
else {
const ruleSets = preloadingAttempt.ruleSetIds.map(id => this.model.getRuleSetById(id)).filter(x => x !== null);
this.preloadingDetails.data = {
preloadingAttempt,
ruleSets,
pageURL: pageURL(),
};
}
}
render() {
// Update preloaidng grid
const filteringRuleSetId = this.ruleSetSelector.getSelected();
const rows = this.model.getPreloadingAttempts(filteringRuleSetId).map(({ id, value }) => {
const attempt = value;
const ruleSets = attempt.ruleSetIds.flatMap(id => {
const ruleSet = this.model.getRuleSetById(id);
return ruleSet === null ? [] : [ruleSet];
});
return {
id,
attempt,
ruleSets,
};
});
this.preloadingGrid.update({ rows, pageURL: pageURL() });
this.updatePreloadingDetails();
}
onPreloadingGridCellFocused(event) {
const focusedEvent = event;
this.focusedPreloadingAttemptId = focusedEvent.data.row.cells.find(cell => cell.columnId === 'id')?.value;
this.render();
}
getRuleSetSelectorToolbarItemForTest() {
return this.ruleSetSelector.item();
}
getPreloadingGridForTest() {
return this.preloadingGrid;
}
getPreloadingDetailsForTest() {
return this.preloadingDetails;
}
selectRuleSetOnFilterForTest(id) {
this.ruleSetSelector.select(id);
}
}
export class PreloadingResultView extends UI.Widget.VBox {
model;
warningsContainer;
warningsView = new PreloadingWarningsView();
usedPreloading = new PreloadingComponents.UsedPreloadingView.UsedPreloadingView();
constructor(model) {
super(/* isWebComponent */ true, /* delegatesFocus */ false);
this.model = model;
SDK.TargetManager.TargetManager.instance().addScopeChangeListener(this.onScopeChange.bind(this));
SDK.TargetManager.TargetManager.instance().addModelListener(SDK.PreloadingModel.PreloadingModel, SDK.PreloadingModel.Events.ModelUpdated, this.render, this, { scoped: true });
SDK.TargetManager.TargetManager.instance().addModelListener(SDK.PreloadingModel.PreloadingModel, SDK.PreloadingModel.Events.WarningsUpdated, this.warningsView.onWarningsUpdated, this.warningsView, { scoped: true });
this.warningsContainer = document.createElement('div');
this.warningsContainer.classList.add('flex-none');
this.contentElement.insertBefore(this.warningsContainer, this.contentElement.firstChild);
this.warningsView.show(this.warningsContainer);
const usedPreloadingContainer = new UI.Widget.VBox();
usedPreloadingContainer.contentElement.appendChild(this.usedPreloading);
usedPreloadingContainer.show(this.contentElement);
}
wasShown() {
super.wasShown();
this.registerCSSFiles([emptyWidgetStyles, preloadingViewStyles]);
this.warningsView.wasShown();
this.render();
}
onScopeChange() {
const model = SDK.TargetManager.TargetManager.instance().scopeTarget()?.model(SDK.PreloadingModel.PreloadingModel);
assertNotNullOrUndefined(model);
this.model = model;
this.render();
}
render() {
this.usedPreloading.data = {
pageURL: SDK.TargetManager.TargetManager.instance().scopeTarget()?.inspectedURL() ||
'',
attempts: this.model.getPreloadingAttemptsOfPreviousPage().map(({ value }) => value),
};
}
getUsedPreloadingForTest() {
return this.usedPreloading;
}
}
class PreloadingRuleSetSelector {
model;
onSelectionChanged = () => { };
toolbarItem;
listModel;
dropDown;
constructor(onSelectionChanged) {
const model = SDK.TargetManager.TargetManager.instance().scopeTarget()?.model(SDK.PreloadingModel.PreloadingModel);
assertNotNullOrUndefined(model);
this.model = model;
SDK.TargetManager.TargetManager.instance().addScopeChangeListener(this.onScopeChange.bind(this));
SDK.TargetManager.TargetManager.instance().addModelListener(SDK.PreloadingModel.PreloadingModel, SDK.PreloadingModel.Events.ModelUpdated, this.onModelUpdated, this, { scoped: true });
this.listModel = new UI.ListModel.ListModel();
this.dropDown = new UI.SoftDropDown.SoftDropDown(this.listModel, this);
this.dropDown.setRowHeight(36);
this.dropDown.setPlaceholderText(i18nString(UIStrings.filterAllPreloads));
this.toolbarItem = new UI.Toolbar.ToolbarItem(this.dropDown.element);
this.toolbarItem.setTitle(i18nString(UIStrings.filterFilterByRuleSet));
this.toolbarItem.element.classList.add('toolbar-has-dropdown');
// Initializes `listModel` and `dropDown` using data of the model.
this.onModelUpdated();
// Prevents emitting onSelectionChanged on the first call of `this.onModelUpdated()` for initialization.
this.onSelectionChanged = onSelectionChanged;
}
onScopeChange() {
const model = SDK.TargetManager.TargetManager.instance().scopeTarget()?.model(SDK.PreloadingModel.PreloadingModel);
assertNotNullOrUndefined(model);
this.model = model;
this.onModelUpdated();
}
onModelUpdated() {
const ids = this.model.getAllRuleSets().map(({ id }) => id);
const items = [null, ...ids];
const selected = this.dropDown.getSelectedItem();
const newSelected = items.indexOf(selected) === -1 ? null : selected;
this.listModel.replaceAll(items);
this.dropDown.selectItem(newSelected);
}
getSelected() {
return this.dropDown.getSelectedItem();
}
select(id) {
this.dropDown.selectItem(id);
}
// Method for UI.Toolbar.Provider
item() {
return this.toolbarItem;
}
// Method for UI.SoftDropDown.Delegate<Protocol.Preload.RuleSetId|null>
titleFor(id) {
if (id === null) {
return i18nString(UIStrings.filterAllPreloads);
}
const ruleSet = this.model.getRuleSetById(id);
if (ruleSet === null) {
return i18n.i18n.lockedString('Internal error');
}
return PreloadingUIUtils.ruleSetLocationShort(ruleSet, pageURL());
}
subtitleFor(id) {
const countsByStatus = this.model.getPreloadCountsByRuleSetId().get(id) || new Map();
return PreloadingUIUtils.preloadsStatusSummary(countsByStatus);
}
// Method for UI.SoftDropDown.Delegate<Protocol.Preload.RuleSetId|null>
createElementForItem(id) {
const element = document.createElement('div');
const shadowRoot = UI.Utils.createShadowRootWithCoreStyles(element, { cssFile: undefined, delegatesFocus: undefined });
const title = shadowRoot.createChild('div', 'title');
UI.UIUtils.createTextChild(title, Platform.StringUtilities.trimEndWithMaxLength(this.titleFor(id), 100));
const subTitle = shadowRoot.createChild('div', 'subtitle');
UI.UIUtils.createTextChild(subTitle, this.subtitleFor(id));
return element;
}
// Method for UI.SoftDropDown.Delegate<Protocol.Preload.RuleSetId|null>
isItemSelectable(_id) {
return true;
}
// Method for UI.SoftDropDown.Delegate<Protocol.Preload.RuleSetId|null>
itemSelected(_id) {
this.onSelectionChanged();
}
// Method for UI.SoftDropDown.Delegate<Protocol.Preload.RuleSetId|null>
highlightedItemChanged(_from, _to, _fromElement, _toElement) {
}
}
export class PreloadingWarningsView extends UI.Widget.VBox {
infobar = new PreloadingComponents.PreloadingDisabledInfobar.PreloadingDisabledInfobar();
constructor() {
super(/* isWebComponent */ false, /* delegatesFocus */ false);
}
wasShown() {
super.wasShown();
this.registerCSSFiles([emptyWidgetStyles]);
this.contentElement.append(this.infobar);
}
onWarningsUpdated(args) {
this.infobar.data = args.data;
}
}
//# sourceMappingURL=PreloadingView.js.map