@quick-game/cli
Version:
Command line interface for rapid qg development
1,067 lines • 91.6 kB
JavaScript
// Copyright 2021 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.
/*
* Copyright (C) 2007, 2008, 2010 Apple Inc. All rights reserved.
* Copyright (C) 2009 Joseph Pecoraro
* Copyright (C) 2013 Samsung Electronics. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
* its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
import * as Common from '../../core/common/common.js';
import * as Host from '../../core/host/host.js';
import * as i18n from '../../core/i18n/i18n.js';
import * as Platform from '../../core/platform/platform.js';
import * as Root from '../../core/root/root.js';
import * as SDK from '../../core/sdk/sdk.js';
import * as LegacyWrapper from '../../ui/components/legacy_wrapper/legacy_wrapper.js';
import * as SourceFrame from '../../ui/legacy/components/source_frame/source_frame.js';
import * as UI from '../../ui/legacy/legacy.js';
import { ApplicationPanelTreeElement, ExpandableApplicationPanelTreeElement } from './ApplicationPanelTreeElement.js';
import { AppManifestView } from './AppManifestView.js';
import { BackForwardCacheTreeElement } from './BackForwardCacheTreeElement.js';
import { BackgroundServiceModel } from './BackgroundServiceModel.js';
import { BackgroundServiceView } from './BackgroundServiceView.js';
import { BounceTrackingMitigationsTreeElement } from './BounceTrackingMitigationsTreeElement.js';
import * as ApplicationComponents from './components/components.js';
import { PreloadingTreeElement } from './PreloadingTreeElement.js';
import resourcesSidebarStyles from './resourcesSidebar.css.js';
import { ServiceWorkerCacheTreeElement } from './ServiceWorkerCacheTreeElement.js';
import { DatabaseModel, Events as DatabaseModelEvents } from './DatabaseModel.js';
import { DatabaseQueryView, Events as DatabaseQueryViewEvents } from './DatabaseQueryView.js';
import { DatabaseTableView } from './DatabaseTableView.js';
import { DOMStorageModel, Events as DOMStorageModelEvents } from './DOMStorageModel.js';
import { Events as IndexedDBModelEvents, IndexedDBModel, } from './IndexedDBModel.js';
import { IDBDatabaseView, IDBDataView } from './IndexedDBViews.js';
import { InterestGroupStorageModel, Events as InterestGroupModelEvents } from './InterestGroupStorageModel.js';
import { InterestGroupTreeElement } from './InterestGroupTreeElement.js';
import { OpenedWindowDetailsView, WorkerDetailsView } from './OpenedWindowDetailsView.js';
import { ServiceWorkersView } from './ServiceWorkersView.js';
import { StorageBucketsTreeParentElement } from './StorageBucketsTreeElement.js';
import { SharedStorageListTreeElement } from './SharedStorageListTreeElement.js';
import { SharedStorageModel, Events as SharedStorageModelEvents, } from './SharedStorageModel.js';
import { SharedStorageTreeElement } from './SharedStorageTreeElement.js';
import { StorageView } from './StorageView.js';
import { TrustTokensTreeElement } from './TrustTokensTreeElement.js';
import { ReportingApiTreeElement } from './ReportingApiTreeElement.js';
const UIStrings = {
/**
*@description Text in Application Panel Sidebar of the Application panel
*/
application: 'Application',
/**
*@description Text in Application Panel Sidebar of the Application panel
*/
storage: 'Storage',
/**
*@description Text in Application Panel Sidebar of the Application panel
*/
localStorage: 'Local storage',
/**
*@description Text in Application Panel Sidebar of the Application panel
*/
sessionStorage: 'Session storage',
/**
*@description Text in Application Panel Sidebar of the Application panel
*/
webSql: 'Web SQL',
/**
*@description Text for web cookies
*/
cookies: 'Cookies',
/**
*@description Text in Application Panel Sidebar of the Application panel
*/
backgroundServices: 'Background services',
/**
*@description Text in Application Panel Sidebar of the Application panel
*/
preloading: 'Preloading',
/**
*@description Text for rendering frames
*/
frames: 'Frames',
/**
*@description Text that appears on a button for the manifest resource type filter.
*/
manifest: 'Manifest',
/**
*@description Text in App Manifest View of the Application panel
*/
noManifestDetected: 'No manifest detected',
/**
*@description Text in App Manifest View of the Application panel
*/
appManifest: 'App Manifest',
/**
*@description Text in Application Panel Sidebar of the Application panel
*/
indexeddb: 'IndexedDB',
/**
*@description A context menu item in the Application Panel Sidebar of the Application panel
*/
refreshIndexeddb: 'Refresh IndexedDB',
/**
*@description Tooltip in Application Panel Sidebar of the Application panel
*@example {1.0} PH1
*/
versionSEmpty: 'Version: {PH1} (empty)',
/**
*@description Tooltip in Application Panel Sidebar of the Application panel
*@example {1.0} PH1
*/
versionS: 'Version: {PH1}',
/**
*@description Text to clear content
*/
clear: 'Clear',
/**
*@description Text in Application Panel Sidebar of the Application panel
*@example {"key path"} PH1
*/
keyPathS: 'Key path: {PH1}',
/**
*@description Text in Application Panel Sidebar of the Application panel
*/
localFiles: 'Local Files',
/**
*@description Tooltip in Application Panel Sidebar of the Application panel
*@example {https://example.com} PH1
*/
cookiesUsedByFramesFromS: 'Cookies used by frames from {PH1}',
/**
*@description Text in Frames View of the Application panel
*/
openedWindows: 'Opened Windows',
/**
*@description Label for plural of worker type: web workers
*/
webWorkers: 'Web Workers',
/**
*@description Label in frame tree for unavailable document
*/
documentNotAvailable: 'Document not available',
/**
*@description Description of content of unavailable document in Application panel
*/
theContentOfThisDocumentHasBeen: 'The content of this document has been generated dynamically via \'document.write()\'.',
/**
*@description Text in Frames View of the Application panel
*/
windowWithoutTitle: 'Window without title',
/**
*@description Default name for worker
*/
worker: 'worker',
/**
* @description Aria text for screen reader to announce they can scroll to top of manifest if invoked
*/
onInvokeManifestAlert: 'Manifest: Invoke to scroll to the top of manifest',
/**
* @description Aria text for screen reader to announce they can scroll to a section if invoked
* @example {"Identity"} PH1
*/
beforeInvokeAlert: '{PH1}: Invoke to scroll to this section in manifest',
/**
* @description Alert message for screen reader to announce which subsection is being scrolled to
* @example {"Identity"} PH1
*/
onInvokeAlert: 'Scrolled to {PH1}',
/**
* @description Application sidebar panel
*/
applicationSidebarPanel: 'Application panel sidebar',
};
const str_ = i18n.i18n.registerUIStrings('panels/application/ApplicationPanelSidebar.ts', UIStrings);
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
function assertNotMainTarget(targetId) {
if (targetId === 'main') {
throw new Error('Unexpected main target id');
}
}
export var SharedStorageTreeElementDispatcher;
(function (SharedStorageTreeElementDispatcher) {
// TODO(crbug.com/1167717): Make this a const enum.
// eslint-disable-next-line rulesdir/const_enum
let Events;
(function (Events) {
Events["SharedStorageTreeElementAdded"] = "SharedStorageTreeElementAdded";
})(Events = SharedStorageTreeElementDispatcher.Events || (SharedStorageTreeElementDispatcher.Events = {}));
})(SharedStorageTreeElementDispatcher || (SharedStorageTreeElementDispatcher = {}));
export class ApplicationPanelSidebar extends UI.Widget.VBox {
panel;
sidebarTree;
applicationTreeElement;
serviceWorkersTreeElement;
localStorageListTreeElement;
sessionStorageListTreeElement;
indexedDBListTreeElement;
interestGroupTreeElement;
databasesListTreeElement;
cookieListTreeElement;
trustTokensTreeElement;
cacheStorageListTreeElement;
sharedStorageListTreeElement;
storageBucketsTreeElement;
backForwardCacheListTreeElement;
backgroundFetchTreeElement;
backgroundSyncTreeElement;
bounceTrackingMitigationsTreeElement;
notificationsTreeElement;
paymentHandlerTreeElement;
periodicBackgroundSyncTreeElement;
pushMessagingTreeElement;
reportingApiTreeElement;
preloadingRuleSetTreeElement;
preloadingAttemptTreeElement;
preloadingResultTreeElement;
resourcesSection;
databaseTableViews;
databaseQueryViews;
databaseTreeElements;
domStorageTreeElements;
sharedStorageTreeElements;
domains;
// Holds main frame target.
target;
databaseModel;
previousHoveredElement;
sharedStorageTreeElementDispatcher;
constructor(panel) {
super();
this.panel = panel;
this.sidebarTree = new UI.TreeOutline.TreeOutlineInShadow();
this.sidebarTree.element.classList.add('resources-sidebar');
this.sidebarTree.element.classList.add('filter-all');
// Listener needs to have been set up before the elements are added
this.sidebarTree.addEventListener(UI.TreeOutline.Events.ElementAttached, this.treeElementAdded, this);
this.contentElement.appendChild(this.sidebarTree.element);
const applicationSectionTitle = i18nString(UIStrings.application);
this.applicationTreeElement = this.addSidebarSection(applicationSectionTitle);
const applicationPanelSidebar = this.applicationTreeElement.treeOutline?.contentElement;
if (applicationPanelSidebar) {
applicationPanelSidebar.ariaLabel = i18nString(UIStrings.applicationSidebarPanel);
}
const manifestTreeElement = new AppManifestTreeElement(panel);
this.applicationTreeElement.appendChild(manifestTreeElement);
manifestTreeElement.generateChildren();
this.serviceWorkersTreeElement = new ServiceWorkersTreeElement(panel);
this.applicationTreeElement.appendChild(this.serviceWorkersTreeElement);
const clearStorageTreeElement = new ClearStorageTreeElement(panel);
this.applicationTreeElement.appendChild(clearStorageTreeElement);
const storageSectionTitle = i18nString(UIStrings.storage);
const storageTreeElement = this.addSidebarSection(storageSectionTitle);
this.localStorageListTreeElement =
new ExpandableApplicationPanelTreeElement(panel, i18nString(UIStrings.localStorage), 'LocalStorage');
this.localStorageListTreeElement.setLink('https://developer.chrome.com/docs/devtools/storage/localstorage/?utm_source=devtools');
const localStorageIcon = UI.Icon.Icon.create('table', 'resource-tree-item');
this.localStorageListTreeElement.setLeadingIcons([localStorageIcon]);
storageTreeElement.appendChild(this.localStorageListTreeElement);
this.sessionStorageListTreeElement =
new ExpandableApplicationPanelTreeElement(panel, i18nString(UIStrings.sessionStorage), 'SessionStorage');
this.sessionStorageListTreeElement.setLink('https://developer.chrome.com/docs/devtools/storage/sessionstorage/?utm_source=devtools');
const sessionStorageIcon = UI.Icon.Icon.create('table', 'resource-tree-item');
this.sessionStorageListTreeElement.setLeadingIcons([sessionStorageIcon]);
storageTreeElement.appendChild(this.sessionStorageListTreeElement);
this.indexedDBListTreeElement = new IndexedDBTreeElement(panel);
this.indexedDBListTreeElement.setLink('https://developer.chrome.com/docs/devtools/storage/indexeddb/?utm_source=devtools');
storageTreeElement.appendChild(this.indexedDBListTreeElement);
this.databasesListTreeElement =
new ExpandableApplicationPanelTreeElement(panel, i18nString(UIStrings.webSql), 'Databases');
this.databasesListTreeElement.setLink('https://developer.chrome.com/docs/devtools/storage/websql/?utm_source=devtools');
const databaseIcon = UI.Icon.Icon.create('database', 'resource-tree-item');
this.databasesListTreeElement.setLeadingIcons([databaseIcon]);
storageTreeElement.appendChild(this.databasesListTreeElement);
this.cookieListTreeElement =
new ExpandableApplicationPanelTreeElement(panel, i18nString(UIStrings.cookies), 'Cookies');
this.cookieListTreeElement.setLink('https://developer.chrome.com/docs/devtools/storage/cookies/?utm_source=devtools');
const cookieIcon = UI.Icon.Icon.create('cookie', 'resource-tree-item');
this.cookieListTreeElement.setLeadingIcons([cookieIcon]);
storageTreeElement.appendChild(this.cookieListTreeElement);
this.trustTokensTreeElement = new TrustTokensTreeElement(panel);
storageTreeElement.appendChild(this.trustTokensTreeElement);
this.interestGroupTreeElement = new InterestGroupTreeElement(panel);
storageTreeElement.appendChild(this.interestGroupTreeElement);
this.sharedStorageListTreeElement = new SharedStorageListTreeElement(panel);
storageTreeElement.appendChild(this.sharedStorageListTreeElement);
this.cacheStorageListTreeElement = new ServiceWorkerCacheTreeElement(panel);
storageTreeElement.appendChild(this.cacheStorageListTreeElement);
if (Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.STORAGE_BUCKETS_TREE)) {
this.storageBucketsTreeElement = new StorageBucketsTreeParentElement(panel);
storageTreeElement.appendChild(this.storageBucketsTreeElement);
}
const backgroundServiceSectionTitle = i18nString(UIStrings.backgroundServices);
const backgroundServiceTreeElement = this.addSidebarSection(backgroundServiceSectionTitle);
this.backForwardCacheListTreeElement = new BackForwardCacheTreeElement(panel);
backgroundServiceTreeElement.appendChild(this.backForwardCacheListTreeElement);
this.backgroundFetchTreeElement =
new BackgroundServiceTreeElement(panel, "backgroundFetch" /* Protocol.BackgroundService.ServiceName.BackgroundFetch */);
backgroundServiceTreeElement.appendChild(this.backgroundFetchTreeElement);
this.backgroundSyncTreeElement =
new BackgroundServiceTreeElement(panel, "backgroundSync" /* Protocol.BackgroundService.ServiceName.BackgroundSync */);
backgroundServiceTreeElement.appendChild(this.backgroundSyncTreeElement);
this.bounceTrackingMitigationsTreeElement = new BounceTrackingMitigationsTreeElement(panel);
backgroundServiceTreeElement.appendChild(this.bounceTrackingMitigationsTreeElement);
this.notificationsTreeElement =
new BackgroundServiceTreeElement(panel, "notifications" /* Protocol.BackgroundService.ServiceName.Notifications */);
backgroundServiceTreeElement.appendChild(this.notificationsTreeElement);
this.paymentHandlerTreeElement =
new BackgroundServiceTreeElement(panel, "paymentHandler" /* Protocol.BackgroundService.ServiceName.PaymentHandler */);
backgroundServiceTreeElement.appendChild(this.paymentHandlerTreeElement);
this.periodicBackgroundSyncTreeElement =
new BackgroundServiceTreeElement(panel, "periodicBackgroundSync" /* Protocol.BackgroundService.ServiceName.PeriodicBackgroundSync */);
backgroundServiceTreeElement.appendChild(this.periodicBackgroundSyncTreeElement);
this.pushMessagingTreeElement =
new BackgroundServiceTreeElement(panel, "pushMessaging" /* Protocol.BackgroundService.ServiceName.PushMessaging */);
backgroundServiceTreeElement.appendChild(this.pushMessagingTreeElement);
this.reportingApiTreeElement = new ReportingApiTreeElement(panel);
backgroundServiceTreeElement.appendChild(this.reportingApiTreeElement);
if (Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.PRELOADING_STATUS_PANEL)) {
const preloadingSectionTitle = i18nString(UIStrings.preloading);
const preloadingSectionTreeElement = this.addSidebarSection(preloadingSectionTitle);
this.preloadingRuleSetTreeElement = PreloadingTreeElement.newForPreloadingRuleSetView(panel);
this.preloadingAttemptTreeElement = PreloadingTreeElement.newForPreloadingAttemptView(panel);
this.preloadingResultTreeElement = PreloadingTreeElement.newForPreloadingResultView(panel);
preloadingSectionTreeElement.appendChild(this.preloadingRuleSetTreeElement);
preloadingSectionTreeElement.appendChild(this.preloadingAttemptTreeElement);
preloadingSectionTreeElement.appendChild(this.preloadingResultTreeElement);
}
const resourcesSectionTitle = i18nString(UIStrings.frames);
const resourcesTreeElement = this.addSidebarSection(resourcesSectionTitle);
this.resourcesSection = new ResourcesSection(panel, resourcesTreeElement);
this.databaseTableViews = new Map();
this.databaseQueryViews = new Map();
this.databaseTreeElements = new Map();
this.domStorageTreeElements = new Map();
this.sharedStorageTreeElements = new Map();
this.domains = {};
this.sidebarTree.contentElement.addEventListener('mousemove', this.onmousemove.bind(this), false);
this.sidebarTree.contentElement.addEventListener('mouseleave', this.onmouseleave.bind(this), false);
SDK.TargetManager.TargetManager.instance().observeTargets(this, { scoped: true });
SDK.TargetManager.TargetManager.instance().addModelListener(SDK.ResourceTreeModel.ResourceTreeModel, SDK.ResourceTreeModel.Events.FrameNavigated, this.frameNavigated, this, { scoped: true });
const selection = this.panel.lastSelectedItemPath();
if (!selection.length) {
manifestTreeElement.select();
}
SDK.TargetManager.TargetManager.instance().observeModels(DOMStorageModel, {
modelAdded: (model) => this.domStorageModelAdded(model),
modelRemoved: (model) => this.domStorageModelRemoved(model),
}, { scoped: true });
SDK.TargetManager.TargetManager.instance().observeModels(IndexedDBModel, {
modelAdded: (model) => this.indexedDBModelAdded(model),
modelRemoved: (model) => this.indexedDBModelRemoved(model),
}, { scoped: true });
SDK.TargetManager.TargetManager.instance().observeModels(InterestGroupStorageModel, {
modelAdded: (model) => this.interestGroupModelAdded(model),
modelRemoved: (model) => this.interestGroupModelRemoved(model),
}, { scoped: true });
SDK.TargetManager.TargetManager.instance().observeModels(SharedStorageModel, {
modelAdded: (model) => this.sharedStorageModelAdded(model).catch(err => {
console.error(err);
}),
modelRemoved: (model) => this.sharedStorageModelRemoved(model),
}, { scoped: true });
SDK.TargetManager.TargetManager.instance().observeModels(SDK.StorageBucketsModel.StorageBucketsModel, {
modelAdded: (model) => this.storageBucketsModelAdded(model),
modelRemoved: (model) => this.storageBucketsModelRemoved(model),
}, { scoped: true });
this.sharedStorageTreeElementDispatcher =
new Common.ObjectWrapper.ObjectWrapper();
// Work-around for crbug.com/1152713: Something is wrong with custom scrollbars and size containment.
// @ts-ignore
this.contentElement.style.contain = 'layout style';
}
addSidebarSection(title) {
const treeElement = new UI.TreeOutline.TreeElement(title, true);
treeElement.listItemElement.classList.add('storage-group-list-item');
treeElement.setCollapsible(false);
treeElement.selectable = false;
this.sidebarTree.appendChild(treeElement);
UI.ARIAUtils.markAsHeading(treeElement.listItemElement, 3);
UI.ARIAUtils.setLabel(treeElement.childrenListElement, title);
return treeElement;
}
targetAdded(target) {
if (target !== target.outermostTarget()) {
return;
}
this.target = target;
this.databaseModel = target.model(DatabaseModel);
if (this.databaseModel) {
this.databaseModel.addEventListener(DatabaseModelEvents.DatabaseAdded, this.databaseAdded, this);
this.databaseModel.addEventListener(DatabaseModelEvents.DatabasesRemoved, this.resetWebSQL, this);
}
const interestGroupModel = target.model(InterestGroupStorageModel);
if (interestGroupModel) {
interestGroupModel.addEventListener(InterestGroupModelEvents.InterestGroupAccess, this.interestGroupAccess, this);
}
const resourceTreeModel = target.model(SDK.ResourceTreeModel.ResourceTreeModel);
if (!resourceTreeModel) {
return;
}
if (resourceTreeModel.cachedResourcesLoaded()) {
this.initialize();
}
resourceTreeModel.addEventListener(SDK.ResourceTreeModel.Events.CachedResourcesLoaded, this.initialize, this);
resourceTreeModel.addEventListener(SDK.ResourceTreeModel.Events.WillLoadCachedResources, this.resetWithFrames, this);
}
targetRemoved(target) {
if (target !== this.target) {
return;
}
delete this.target;
const resourceTreeModel = target.model(SDK.ResourceTreeModel.ResourceTreeModel);
if (resourceTreeModel) {
resourceTreeModel.removeEventListener(SDK.ResourceTreeModel.Events.CachedResourcesLoaded, this.initialize, this);
resourceTreeModel.removeEventListener(SDK.ResourceTreeModel.Events.WillLoadCachedResources, this.resetWithFrames, this);
}
if (this.databaseModel) {
this.databaseModel.removeEventListener(DatabaseModelEvents.DatabaseAdded, this.databaseAdded, this);
this.databaseModel.removeEventListener(DatabaseModelEvents.DatabasesRemoved, this.resetWebSQL, this);
this.databaseModel = null;
}
const interestGroupModel = target.model(InterestGroupStorageModel);
if (interestGroupModel) {
interestGroupModel.removeEventListener(InterestGroupModelEvents.InterestGroupAccess, this.interestGroupAccess, this);
}
this.resetWithFrames();
}
focus() {
this.sidebarTree.focus();
}
initialize() {
for (const frame of SDK.ResourceTreeModel.ResourceTreeModel.frames()) {
this.addCookieDocument(frame);
}
if (this.databaseModel) {
this.databaseModel.enable();
}
const interestGroupModel = this.target && this.target.model(InterestGroupStorageModel);
if (interestGroupModel) {
interestGroupModel.enable();
}
this.cacheStorageListTreeElement.initialize();
const backgroundServiceModel = this.target && this.target.model(BackgroundServiceModel) || null;
this.backgroundFetchTreeElement && this.backgroundFetchTreeElement.initialize(backgroundServiceModel);
this.backgroundSyncTreeElement && this.backgroundSyncTreeElement.initialize(backgroundServiceModel);
this.notificationsTreeElement.initialize(backgroundServiceModel);
this.paymentHandlerTreeElement.initialize(backgroundServiceModel);
this.periodicBackgroundSyncTreeElement.initialize(backgroundServiceModel);
this.pushMessagingTreeElement.initialize(backgroundServiceModel);
this.storageBucketsTreeElement?.initialize();
if (Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.PRELOADING_STATUS_PANEL)) {
const preloadingModel = this.target?.model(SDK.PreloadingModel.PreloadingModel);
if (preloadingModel) {
this.preloadingRuleSetTreeElement?.initialize(preloadingModel);
this.preloadingAttemptTreeElement?.initialize(preloadingModel);
this.preloadingResultTreeElement?.initialize(preloadingModel);
}
}
}
domStorageModelAdded(model) {
model.enable();
model.storages().forEach(this.addDOMStorage.bind(this));
model.addEventListener(DOMStorageModelEvents.DOMStorageAdded, this.domStorageAdded, this);
model.addEventListener(DOMStorageModelEvents.DOMStorageRemoved, this.domStorageRemoved, this);
}
domStorageModelRemoved(model) {
model.storages().forEach(this.removeDOMStorage.bind(this));
model.removeEventListener(DOMStorageModelEvents.DOMStorageAdded, this.domStorageAdded, this);
model.removeEventListener(DOMStorageModelEvents.DOMStorageRemoved, this.domStorageRemoved, this);
}
indexedDBModelAdded(model) {
model.enable();
this.indexedDBListTreeElement.addIndexedDBForModel(model);
}
indexedDBModelRemoved(model) {
this.indexedDBListTreeElement.removeIndexedDBForModel(model);
}
interestGroupModelAdded(model) {
model.enable();
model.addEventListener(InterestGroupModelEvents.InterestGroupAccess, this.interestGroupAccess, this);
}
interestGroupModelRemoved(model) {
model.disable();
model.removeEventListener(InterestGroupModelEvents.InterestGroupAccess, this.interestGroupAccess, this);
}
async sharedStorageModelAdded(model) {
await model.enable();
for (const storage of model.storages()) {
await this.addSharedStorage(storage);
}
model.addEventListener(SharedStorageModelEvents.SharedStorageAdded, this.sharedStorageAdded, this);
model.addEventListener(SharedStorageModelEvents.SharedStorageRemoved, this.sharedStorageRemoved, this);
model.addEventListener(SharedStorageModelEvents.SharedStorageAccess, this.sharedStorageAccess, this);
}
sharedStorageModelRemoved(model) {
model.disable();
for (const storage of model.storages()) {
this.removeSharedStorage(storage);
}
model.removeEventListener(SharedStorageModelEvents.SharedStorageAdded, this.sharedStorageAdded, this);
model.removeEventListener(SharedStorageModelEvents.SharedStorageRemoved, this.sharedStorageRemoved, this);
model.removeEventListener(SharedStorageModelEvents.SharedStorageAccess, this.sharedStorageAccess, this);
}
storageBucketsModelAdded(model) {
model.enable();
}
storageBucketsModelRemoved(model) {
this.storageBucketsTreeElement?.removeBucketsForModel(model);
}
resetWithFrames() {
this.resourcesSection.reset();
this.reset();
}
resetWebSQL() {
for (const queryView of this.databaseQueryViews.values()) {
queryView.removeEventListener(DatabaseQueryViewEvents.SchemaUpdated, event => {
void this.updateDatabaseTables(event);
}, this);
}
this.databaseTableViews.clear();
this.databaseQueryViews.clear();
this.databaseTreeElements.clear();
this.databasesListTreeElement.removeChildren();
this.databasesListTreeElement.setExpandable(false);
}
treeElementAdded(event) {
// On tree item selection its itemURL and those of its parents are persisted.
// On reload/navigation we check for matches starting from the root on the
// path to the current element. Matching nodes are expanded until we hit a
// mismatch. This way we ensure that the longest matching path starting from
// the root is expanded, even if we cannot match the whole path.
const selection = this.panel.lastSelectedItemPath();
if (!selection.length) {
return;
}
const element = event.data;
const elementPath = [element];
for (let parent = element.parent; parent && 'itemURL' in parent && parent.itemURL; parent = parent.parent) {
elementPath.push(parent);
}
let i = selection.length - 1;
let j = elementPath.length - 1;
while (i >= 0 && j >= 0 && selection[i] === elementPath[j].itemURL) {
if (!elementPath[j].expanded) {
if (i > 0) {
elementPath[j].expand();
}
if (!elementPath[j].selected) {
elementPath[j].select();
}
}
i--;
j--;
}
}
reset() {
this.domains = {};
this.resetWebSQL();
this.cookieListTreeElement.removeChildren();
this.interestGroupTreeElement.clearEvents();
}
frameNavigated(event) {
const frame = event.data;
if (frame.isOutermostFrame()) {
this.reset();
}
this.addCookieDocument(frame);
}
databaseAdded({ data: database }) {
const databaseTreeElement = new DatabaseTreeElement(this, database);
this.databaseTreeElements.set(database, databaseTreeElement);
this.databasesListTreeElement.appendChild(databaseTreeElement);
}
interestGroupAccess(event) {
this.interestGroupTreeElement.addEvent(event.data);
}
addCookieDocument(frame) {
// In case the current frame was unreachable, show it's cookies
// instead of the error interstitials because they might help to
// debug why the frame was unreachable.
const urlToParse = frame.unreachableUrl() || frame.url;
const parsedURL = Common.ParsedURL.ParsedURL.fromString(urlToParse);
if (!parsedURL || (parsedURL.scheme !== 'http' && parsedURL.scheme !== 'https' && parsedURL.scheme !== 'file')) {
return;
}
const domain = parsedURL.securityOrigin();
if (!this.domains[domain]) {
this.domains[domain] = true;
const cookieDomainTreeElement = new CookieTreeElement(this.panel, frame, domain);
this.cookieListTreeElement.appendChild(cookieDomainTreeElement);
}
}
domStorageAdded(event) {
const domStorage = event.data;
this.addDOMStorage(domStorage);
}
addDOMStorage(domStorage) {
console.assert(!this.domStorageTreeElements.get(domStorage));
console.assert(Boolean(domStorage.storageKey));
const domStorageTreeElement = new DOMStorageTreeElement(this.panel, domStorage);
this.domStorageTreeElements.set(domStorage, domStorageTreeElement);
if (domStorage.isLocalStorage) {
this.localStorageListTreeElement.appendChild(domStorageTreeElement);
}
else {
this.sessionStorageListTreeElement.appendChild(domStorageTreeElement);
}
}
domStorageRemoved(event) {
const domStorage = event.data;
this.removeDOMStorage(domStorage);
}
removeDOMStorage(domStorage) {
const treeElement = this.domStorageTreeElements.get(domStorage);
if (!treeElement) {
return;
}
const wasSelected = treeElement.selected;
const parentListTreeElement = treeElement.parent;
if (parentListTreeElement) {
parentListTreeElement.removeChild(treeElement);
if (wasSelected) {
parentListTreeElement.select();
}
}
this.domStorageTreeElements.delete(domStorage);
}
async sharedStorageAdded(event) {
await this.addSharedStorage(event.data);
}
async addSharedStorage(sharedStorage) {
const sharedStorageTreeElement = await SharedStorageTreeElement.createElement(this.panel, sharedStorage);
// A tree element for `sharedStorage.securityOrigin` may have been added while we were waiting for `sharedStorageTreeElement` to be created.
if (this.sharedStorageTreeElements.has(sharedStorage.securityOrigin)) {
return;
}
this.sharedStorageTreeElements.set(sharedStorage.securityOrigin, sharedStorageTreeElement);
this.sharedStorageListTreeElement.appendChild(sharedStorageTreeElement);
this.sharedStorageTreeElementDispatcher.dispatchEventToListeners(SharedStorageTreeElementDispatcher.Events.SharedStorageTreeElementAdded, { origin: sharedStorage.securityOrigin });
}
sharedStorageRemoved(event) {
this.removeSharedStorage(event.data);
}
removeSharedStorage(sharedStorage) {
const treeElement = this.sharedStorageTreeElements.get(sharedStorage.securityOrigin);
if (!treeElement) {
return;
}
const wasSelected = treeElement.selected;
const parentListTreeElement = treeElement.parent;
if (parentListTreeElement) {
parentListTreeElement.removeChild(treeElement);
parentListTreeElement.setExpandable(parentListTreeElement.childCount() > 0);
if (wasSelected) {
parentListTreeElement.select();
}
}
this.sharedStorageTreeElements.delete(sharedStorage.securityOrigin);
}
sharedStorageAccess(event) {
this.sharedStorageListTreeElement.addEvent(event.data);
}
selectDatabase(database) {
if (database) {
this.showDatabase(database);
const treeElement = this.databaseTreeElements.get(database);
treeElement && treeElement.select();
}
}
async showResource(resource, line, column) {
await this.resourcesSection.revealResource(resource, line, column);
}
showFrame(frame) {
this.resourcesSection.revealAndSelectFrame(frame);
}
showDatabase(database, tableName) {
if (!database) {
return;
}
let view;
if (tableName) {
let tableViews = this.databaseTableViews.get(database);
if (!tableViews) {
tableViews = {};
this.databaseTableViews.set(database, tableViews);
}
view = tableViews[tableName];
if (!view) {
view = new DatabaseTableView(database, tableName);
tableViews[tableName] = view;
}
}
else {
view = this.databaseQueryViews.get(database);
if (!view) {
view = new DatabaseQueryView(database);
this.databaseQueryViews.set(database, view);
view.addEventListener(DatabaseQueryViewEvents.SchemaUpdated, event => {
void this.updateDatabaseTables(event);
}, this);
}
}
this.innerShowView(view);
}
showFileSystem(view) {
this.innerShowView(view);
}
innerShowView(view) {
this.panel.showView(view);
}
showPreloadingRuleSetView(revealInfo) {
if (this.preloadingRuleSetTreeElement) {
this.preloadingRuleSetTreeElement.select();
this.preloadingRuleSetTreeElement.revealRuleSet(revealInfo);
}
}
showPreloadingAttemptViewWithFilter(filter) {
if (this.preloadingAttemptTreeElement) {
this.preloadingAttemptTreeElement.select();
this.preloadingAttemptTreeElement.setFilter(filter);
}
}
async updateDatabaseTables(event) {
const database = event.data;
if (!database) {
return;
}
const databasesTreeElement = this.databaseTreeElements.get(database);
if (!databasesTreeElement) {
return;
}
databasesTreeElement.invalidateChildren();
const tableViews = this.databaseTableViews.get(database);
if (!tableViews) {
return;
}
const tableNamesHash = new Set();
const panel = this.panel;
const tableNames = await database.tableNames();
for (const tableName of tableNames) {
tableNamesHash.add(tableName);
}
for (const tableName in tableViews) {
if (!(tableNamesHash.has(tableName))) {
if (panel.visibleView === tableViews[tableName]) {
panel.showView(null);
}
delete tableViews[tableName];
}
}
await databasesTreeElement.updateChildren();
}
onmousemove(event) {
const nodeUnderMouse = event.target;
if (!nodeUnderMouse) {
return;
}
const listNode = UI.UIUtils.enclosingNodeOrSelfWithNodeName(nodeUnderMouse, 'li');
if (!listNode) {
return;
}
const element = UI.TreeOutline.TreeElement.getTreeElementBylistItemNode(listNode);
if (this.previousHoveredElement === element) {
return;
}
if (this.previousHoveredElement) {
this.previousHoveredElement.hovered = false;
delete this.previousHoveredElement;
}
if (element instanceof FrameTreeElement) {
this.previousHoveredElement = element;
element.hovered = true;
}
}
onmouseleave(_event) {
if (this.previousHoveredElement) {
this.previousHoveredElement.hovered = false;
delete this.previousHoveredElement;
}
}
wasShown() {
super.wasShown();
this.sidebarTree.registerCSSFiles([resourcesSidebarStyles]);
}
}
export class BackgroundServiceTreeElement extends ApplicationPanelTreeElement {
serviceName;
view;
model;
selectedInternal;
constructor(storagePanel, serviceName) {
super(storagePanel, BackgroundServiceView.getUIString(serviceName), false);
this.serviceName = serviceName;
/* Whether the element has been selected. */
this.selectedInternal = false;
this.view = null;
this.model = null;
const backgroundServiceIcon = UI.Icon.Icon.create(this.getIconType(), 'resource-tree-item');
this.setLeadingIcons([backgroundServiceIcon]);
}
getIconType() {
switch (this.serviceName) {
case "backgroundFetch" /* Protocol.BackgroundService.ServiceName.BackgroundFetch */:
return 'arrow-up-down';
case "backgroundSync" /* Protocol.BackgroundService.ServiceName.BackgroundSync */:
return 'sync';
case "pushMessaging" /* Protocol.BackgroundService.ServiceName.PushMessaging */:
return 'cloud';
case "notifications" /* Protocol.BackgroundService.ServiceName.Notifications */:
return 'bell';
case "paymentHandler" /* Protocol.BackgroundService.ServiceName.PaymentHandler */:
return 'credit-card';
case "periodicBackgroundSync" /* Protocol.BackgroundService.ServiceName.PeriodicBackgroundSync */:
return 'watch';
default:
console.error(`Service ${this.serviceName} does not have a dedicated icon`);
return 'table';
}
}
initialize(model) {
this.model = model;
// Show the view if the model was initialized after selection.
if (this.selectedInternal && !this.view) {
this.onselect(false);
}
}
get itemURL() {
return `background-service://${this.serviceName}`;
}
get selectable() {
if (!this.model) {
return false;
}
return super.selectable;
}
onselect(selectedByUser) {
super.onselect(selectedByUser);
this.selectedInternal = true;
if (!this.model) {
return false;
}
if (!this.view) {
this.view = new BackgroundServiceView(this.serviceName, this.model);
}
this.showView(this.view);
UI.Context.Context.instance().setFlavor(BackgroundServiceView, this.view);
Host.userMetrics.panelShown('background_service_' + this.serviceName);
return false;
}
}
export class DatabaseTreeElement extends ApplicationPanelTreeElement {
sidebar;
database;
constructor(sidebar, database) {
super(sidebar.panel, database.name, true);
this.sidebar = sidebar;
this.database = database;
const icon = UI.Icon.Icon.create('database', 'resource-tree-item');
this.setLeadingIcons([icon]);
}
get itemURL() {
return 'database://' + encodeURI(this.database.name);
}
onselect(selectedByUser) {
super.onselect(selectedByUser);
this.sidebar.showDatabase(this.database);
Host.userMetrics.panelShown(Host.UserMetrics.PanelCodes[Host.UserMetrics.PanelCodes.web_sql]);
return false;
}
onexpand() {
void this.updateChildren();
}
async updateChildren() {
this.removeChildren();
const tableNames = await this.database.tableNames();
for (const tableName of tableNames) {
this.appendChild(new DatabaseTableTreeElement(this.sidebar, this.database, tableName));
}
}
}
export class DatabaseTableTreeElement extends ApplicationPanelTreeElement {
sidebar;
database;
tableName;
constructor(sidebar, database, tableName) {
super(sidebar.panel, tableName, false);
this.sidebar = sidebar;
this.database = database;
this.tableName = tableName;
const icon = UI.Icon.Icon.create('table', 'resource-tree-item');
this.setLeadingIcons([icon]);
}
get itemURL() {
return 'database://' + encodeURI(this.database.name) + '/' + encodeURI(this.tableName);
}
onselect(selectedByUser) {
super.onselect(selectedByUser);
this.sidebar.showDatabase(this.database, this.tableName);
Host.userMetrics.panelShown(Host.UserMetrics.PanelCodes[Host.UserMetrics.PanelCodes.web_sql]);
return false;
}
}
export class ServiceWorkersTreeElement extends ApplicationPanelTreeElement {
view;
constructor(storagePanel) {
super(storagePanel, i18n.i18n.lockedString('Service workers'), false);
const icon = UI.Icon.Icon.create('gears', 'resource-tree-item');
this.setLeadingIcons([icon]);
}
get itemURL() {
return 'service-workers://';
}
onselect(selectedByUser) {
super.onselect(selectedByUser);
if (!this.view) {
this.view = new ServiceWorkersView();
}
this.showView(this.view);
Host.userMetrics.panelShown(Host.UserMetrics.PanelCodes[Host.UserMetrics.PanelCodes.service_workers]);
return false;
}
}
export class AppManifestTreeElement extends ApplicationPanelTreeElement {
view;
constructor(storagePanel) {
super(storagePanel, i18nString(UIStrings.manifest), true);
const icon = UI.Icon.Icon.create('document', 'resource-tree-item');
this.setLeadingIcons([icon]);
self.onInvokeElement(this.listItemElement, this.onInvoke.bind(this));
const emptyView = new UI.EmptyWidget.EmptyWidget(i18nString(UIStrings.noManifestDetected));
// TODO(crbug.com/1156978): Replace UI.ReportView.ReportView with ReportView.ts web component.
const reportView = new UI.ReportView.ReportView(i18nString(UIStrings.appManifest));
this.view = new AppManifestView(emptyView, reportView, new Common.Throttler.Throttler(1000));
UI.ARIAUtils.setLabel(this.listItemElement, i18nString(UIStrings.onInvokeManifestAlert));
const handleExpansion = (evt) => {
this.setExpandable(evt.detail);
};
this.view.contentElement.addEventListener('manifestDetection', handleExpansion);
}
get itemURL() {
return 'manifest://';
}
onselect(selectedByUser) {
super.onselect(selectedByUser);
this.showView(this.view);
Host.userMetrics.panelShown(Host.UserMetrics.PanelCodes[Host.UserMetrics.PanelCodes.app_manifest]);
return false;
}
generateChildren() {
const staticSections = this.view.getStaticSections();
for (const section of staticSections) {
const sectionElement = section.getTitleElement();
const childTitle = section.title();
const sectionFieldElement = section.getFieldElement();
const child = new ManifestChildTreeElement(this.resourcesPanel, sectionElement, childTitle, sectionFieldElement);
this.appendChild(child);
}
}
onInvoke() {
this.view.getManifestElement().scrollIntoView();
UI.ARIAUtils.alert(i18nString(UIStrings.onInvokeAlert, { PH1: this.listItemElement.title }));
}
showManifestView() {
this.showView(this.view);
}
}
export class ManifestChildTreeElement extends ApplicationPanelTreeElement {
#sectionElement;
#sectionFieldElement;
constructor(storagePanel, element, childTitle, fieldElement) {
super(storagePanel, childTitle, false);
const icon = UI.Icon.Icon.create('document', 'resource-tree-item');
this.setLeadingIcons([icon]);
this.#sectionElement = element;
this.#sectionFieldElement = fieldElement;
self.onInvokeElement(this.listItemElement, this.onInvoke.bind(this));
this.listItemElement.addEventListener('keydown', this.onInvokeElementKeydown.bind(this));
UI.ARIAUtils.setLabel(this.listItemElement, i18nString(UIStrings.beforeInvokeAlert, { PH1: this.listItemElement.title }));
}
get itemURL() {
return 'manifest://' + this.title;
}
onInvoke() {
this.parent?.showManifestView();
this.#sectionElement.scrollIntoView();
UI.ARIAUtils.alert(i18nString(UIStrings.onInvokeAlert, { PH1: this.listItemElement.title }));
Host.userMetrics.manifestSectionSelected(this.listItemElement.title);
}
// direct focus to the corresponding element
onInvokeElementKeydown(event) {
if (event.key !== 'Tab' || event.shiftKey) {
return;
}
const checkBoxElement = this.#sectionFieldElement.querySelector('.mask-checkbox');
let focusableElement = this.#sectionFieldElement.querySelector('[tabindex="0"]');
if (checkBoxElement && checkBoxElement.shadowRoot) {
focusableElement = checkBoxElement.shadowRoot.querySelector('input') || null;
}
else if (!focusableElement) {
// special case for protocol handler section since it is a custom Element and has different structure than the others
focusableElement = this.#sectionFieldElement.querySelector('devtools-protocol-handlers-view')
?.shadowRoot?.querySelector('[tabindex="0"]') ||
null;
}
if (focusableElement) {
focusableElement?.focus();
event.consume(true);
}
}
}
export class ClearStorageTreeElement extends ApplicationPanelTreeElement {
view;
constructor(storagePanel) {
super(storagePanel, i18nString(UIStrings.storage), false);
const icon = UI.Icon.Icon.create('database', 'resource-tree-item');
this.setLeadingIcons([icon]);
}
get itemURL() {
return 'clear-storage://';
}
onselect(selectedByUser) {
super.onselect(selectedByUser);
if (!this.view) {
this.view = new StorageView();
}
this.showView(this.view);
Host.userMetrics.panelShown(Host.UserMetrics.PanelCodes[Host.UserMetrics.PanelCodes.storage]);
return false;
}
}
export class IndexedDBTreeElement extends ExpandableApplicationPanelTreeElement {
idbDatabaseTreeElements;
storageBucket;
constructor(storagePanel, storageBucket) {
super(storagePanel, i18nString(UIStrings.indexeddb), 'IndexedDB');
const icon = UI.Icon.Icon.create('database', 'resource-tree-item');
this.setLeadingIcons([icon]);
this.idbDatabaseTreeElements = [];
this.storageBucket = storageBucket;
this.initialize();
}
initialize() {
SDK.TargetManager.TargetManager.instance().addModelListener(IndexedDBModel, IndexedDBModelEvents.DatabaseAdded, this.indexedDBAdded, this, { scoped: true });
SDK.TargetManager.TargetManager.instance().addModelListener(IndexedDBModel, IndexedDBModelEvents.DatabaseRemoved, this