blob: 68db77fca99deeccfb3cbfc4f8d73e8ef552866c [file] [log] [blame]
// 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) 2012 Google Inc. All rights reserved.
* Copyright (C) 2007, 2008 Apple Inc. 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 i18n from '../i18n/i18n.js';
import type * as Platform from '../platform/platform.js';
import {ParsedURL} from './ParsedURL.js';
const UIStrings = {
/**
*@description Text that appears in a tooltip the xhr and fetch resource types filter.
*/
xhrAndFetch: '`XHR` and `Fetch`',
/**
*@description Text that appears in a tooltip for the JavaScript types filter.
*/
scripts: 'Scripts',
/**
*@description Text that appears on a button for the JavaScript resource type filter.
*/
js: 'JS',
/**
*@description Text that appears in a tooltip for the css types filter.
*/
stylesheets: 'Stylesheets',
/**
*@description Text that appears on a button for the css resource type filter.
*/
css: 'CSS',
/**
*@description Text that appears in a tooltip for the image types filter.
*/
images: 'Images',
/**
*@description Text that appears on a button for the image resource type filter.
*/
img: 'Img',
/**
*@description Text that appears on a button for the media resource type filter.
*/
media: 'Media',
/**
*@description Text that appears in a tooltip for the resource types filter.
*/
fonts: 'Fonts',
/**
*@description Text that appears on a button for the font resource type filter.
*/
font: 'Font',
/**
*@description Text for documents, a type of resources
*/
documents: 'Documents',
/**
*@description Text that appears on a button for the document resource type filter.
*/
doc: 'Doc',
/**
*@description Text that appears in a tooltip for the websocket types filter.
*/
websockets: 'WebSockets',
/**
*@description Text that appears on a button for the websocket resource type filter.
*/
ws: 'WS',
/**
*@description Text that appears in a tooltip for the WebAssembly types filter.
*/
webassembly: 'WebAssembly',
/**
*@description Text that appears on a button for the WebAssembly resource type filter.
*/
wasm: 'Wasm',
/**
*@description Text that appears on a button for the manifest resource type filter.
*/
manifest: 'Manifest',
/**
*@description Text for other types of items
*/
other: 'Other',
/**
*@description Name of a network resource type
*/
document: 'Document',
/**
*@description Name of a network resource type
*/
stylesheet: 'Stylesheet',
/**
*@description Text in Image View of the Sources panel
*/
image: 'Image',
/**
*@description Label for a group of JavaScript files
*/
script: 'Script',
/**
*@description Name of a network resource type
*/
texttrack: 'TextTrack',
/**
*@description Name of a network resource type
*/
fetch: 'Fetch',
/**
*@description Name of a network resource type
*/
eventsource: 'EventSource',
/**
*@description Name of a network resource type
*/
websocket: 'WebSocket',
/**
*@description Name of a network resource type
*/
webtransport: 'WebTransport',
/**
*@description Name of a network resource type
*/
signedexchange: 'SignedExchange',
/**
*@description Name of a network resource type
*/
ping: 'Ping',
/**
*@description Name of a network resource type
*/
cspviolationreport: 'CSPViolationReport',
/**
*@description Name of a network initiator type
*/
preflight: 'Preflight',
/**
*@description Name of a network initiator type
*/
webbundle: 'WebBundle',
};
const str_ = i18n.i18n.registerUIStrings('core/common/ResourceType.ts', UIStrings);
const i18nLazyString = i18n.i18n.getLazilyComputedLocalizedString.bind(undefined, str_);
export class ResourceType {
readonly #nameInternal: string;
readonly #titleInternal: () => Platform.UIString.LocalizedString;
readonly #categoryInternal: ResourceCategory;
readonly #isTextTypeInternal: boolean;
constructor(
name: string, title: () => Platform.UIString.LocalizedString, category: ResourceCategory, isTextType: boolean) {
this.#nameInternal = name;
this.#titleInternal = title;
this.#categoryInternal = category;
this.#isTextTypeInternal = isTextType;
}
static fromMimeType(mimeType: string|null): ResourceType {
if (!mimeType) {
return resourceTypes.Other;
}
if (mimeType.startsWith('text/html')) {
return resourceTypes.Document;
}
if (mimeType.startsWith('text/css')) {
return resourceTypes.Stylesheet;
}
if (mimeType.startsWith('image/')) {
return resourceTypes.Image;
}
if (mimeType.startsWith('text/')) {
return resourceTypes.Script;
}
if (mimeType.includes('font')) {
return resourceTypes.Font;
}
if (mimeType.includes('script')) {
return resourceTypes.Script;
}
if (mimeType.includes('octet')) {
return resourceTypes.Other;
}
if (mimeType.includes('application')) {
return resourceTypes.Script;
}
return resourceTypes.Other;
}
static fromMimeTypeOverride(mimeType: string|null): ResourceType|null {
if (mimeType === 'application/manifest+json') {
return resourceTypes.Manifest;
}
if (mimeType === 'application/wasm') {
return resourceTypes.Wasm;
}
if (mimeType === 'application/webbundle') {
return resourceTypes.WebBundle;
}
return null;
}
static fromURL(url: string): ResourceType|null {
return resourceTypeByExtension.get(ParsedURL.extractExtension(url)) || null;
}
static fromName(name: string): ResourceType|null {
for (const resourceTypeId in resourceTypes) {
const resourceType = (resourceTypes as {
[x: string]: ResourceType,
})[resourceTypeId];
if (resourceType.name() === name) {
return resourceType;
}
}
return null;
}
static mimeFromURL(url: Platform.DevToolsPath.UrlString): string|undefined {
const name = ParsedURL.extractName(url);
if (mimeTypeByName.has(name)) {
return mimeTypeByName.get(name);
}
const ext = ParsedURL.extractExtension(url).toLowerCase();
return mimeTypeByExtension.get(ext);
}
static mimeFromExtension(ext: string): string|undefined {
return mimeTypeByExtension.get(ext);
}
name(): string {
return this.#nameInternal;
}
title(): string {
return this.#titleInternal();
}
category(): ResourceCategory {
return this.#categoryInternal;
}
isTextType(): boolean {
return this.#isTextTypeInternal;
}
isScript(): boolean {
return this.#nameInternal === 'script' || this.#nameInternal === 'sm-script';
}
hasScripts(): boolean {
return this.isScript() || this.isDocument();
}
isStyleSheet(): boolean {
return this.#nameInternal === 'stylesheet' || this.#nameInternal === 'sm-stylesheet';
}
isDocument(): boolean {
return this.#nameInternal === 'document';
}
isDocumentOrScriptOrStyleSheet(): boolean {
return this.isDocument() || this.isScript() || this.isStyleSheet();
}
isFont(): boolean {
return this.#nameInternal === 'font';
}
isImage(): boolean {
return this.#nameInternal === 'image';
}
isFromSourceMap(): boolean {
return this.#nameInternal.startsWith('sm-');
}
isWebbundle(): boolean {
return this.#nameInternal === 'webbundle';
}
toString(): string {
return this.#nameInternal;
}
canonicalMimeType(): string {
if (this.isDocument()) {
return 'text/html';
}
if (this.isScript()) {
return 'text/javascript';
}
if (this.isStyleSheet()) {
return 'text/css';
}
return '';
}
}
export class ResourceCategory {
title: () => Platform.UIString.LocalizedString;
shortTitle: () => Platform.UIString.LocalizedString;
constructor(title: () => Platform.UIString.LocalizedString, shortTitle: () => Platform.UIString.LocalizedString) {
this.title = title;
this.shortTitle = shortTitle;
}
}
export const resourceCategories = {
XHR: new ResourceCategory(i18nLazyString(UIStrings.xhrAndFetch), i18n.i18n.lockedLazyString('Fetch/XHR')),
Script: new ResourceCategory(i18nLazyString(UIStrings.scripts), i18nLazyString(UIStrings.js)),
Stylesheet: new ResourceCategory(i18nLazyString(UIStrings.stylesheets), i18nLazyString(UIStrings.css)),
Image: new ResourceCategory(i18nLazyString(UIStrings.images), i18nLazyString(UIStrings.img)),
Media: new ResourceCategory(i18nLazyString(UIStrings.media), i18nLazyString(UIStrings.media)),
Font: new ResourceCategory(i18nLazyString(UIStrings.fonts), i18nLazyString(UIStrings.font)),
Document: new ResourceCategory(i18nLazyString(UIStrings.documents), i18nLazyString(UIStrings.doc)),
WebSocket: new ResourceCategory(i18nLazyString(UIStrings.websockets), i18nLazyString(UIStrings.ws)),
Wasm: new ResourceCategory(i18nLazyString(UIStrings.webassembly), i18nLazyString(UIStrings.wasm)),
Manifest: new ResourceCategory(i18nLazyString(UIStrings.manifest), i18nLazyString(UIStrings.manifest)),
Other: new ResourceCategory(i18nLazyString(UIStrings.other), i18nLazyString(UIStrings.other)),
};
/**
* This enum is a superset of all types defined in WebCore::InspectorPageAgent::resourceTypeJson
* For DevTools-only types that are based on MIME-types that are backed by other request types
* (for example Wasm that is based on Fetch), additional types are added here.
* For these types, make sure to update `fromMimeTypeOverride` to implement the custom logic.
*/
export const resourceTypes = {
Document: new ResourceType('document', i18nLazyString(UIStrings.document), resourceCategories.Document, true),
Stylesheet: new ResourceType('stylesheet', i18nLazyString(UIStrings.stylesheet), resourceCategories.Stylesheet, true),
Image: new ResourceType('image', i18nLazyString(UIStrings.image), resourceCategories.Image, false),
Media: new ResourceType('media', i18nLazyString(UIStrings.media), resourceCategories.Media, false),
Font: new ResourceType('font', i18nLazyString(UIStrings.font), resourceCategories.Font, false),
Script: new ResourceType('script', i18nLazyString(UIStrings.script), resourceCategories.Script, true),
TextTrack: new ResourceType('texttrack', i18nLazyString(UIStrings.texttrack), resourceCategories.Other, true),
XHR: new ResourceType('xhr', i18n.i18n.lockedLazyString('XHR'), resourceCategories.XHR, true),
Fetch: new ResourceType('fetch', i18nLazyString(UIStrings.fetch), resourceCategories.XHR, true),
Prefetch: new ResourceType('prefetch', i18n.i18n.lockedLazyString('Prefetch'), resourceCategories.Document, true),
EventSource: new ResourceType('eventsource', i18nLazyString(UIStrings.eventsource), resourceCategories.XHR, true),
WebSocket: new ResourceType('websocket', i18nLazyString(UIStrings.websocket), resourceCategories.WebSocket, false),
// TODO(yoichio): Consider creating new category WT or WS/WT with WebSocket.
WebTransport:
new ResourceType('webtransport', i18nLazyString(UIStrings.webtransport), resourceCategories.WebSocket, false),
Wasm: new ResourceType('wasm', i18nLazyString(UIStrings.wasm), resourceCategories.Wasm, false),
Manifest: new ResourceType('manifest', i18nLazyString(UIStrings.manifest), resourceCategories.Manifest, true),
SignedExchange:
new ResourceType('signed-exchange', i18nLazyString(UIStrings.signedexchange), resourceCategories.Other, false),
Ping: new ResourceType('ping', i18nLazyString(UIStrings.ping), resourceCategories.Other, false),
CSPViolationReport: new ResourceType(
'csp-violation-report', i18nLazyString(UIStrings.cspviolationreport), resourceCategories.Other, false),
Other: new ResourceType('other', i18nLazyString(UIStrings.other), resourceCategories.Other, false),
Preflight: new ResourceType('preflight', i18nLazyString(UIStrings.preflight), resourceCategories.Other, true),
SourceMapScript: new ResourceType('sm-script', i18nLazyString(UIStrings.script), resourceCategories.Script, true),
SourceMapStyleSheet:
new ResourceType('sm-stylesheet', i18nLazyString(UIStrings.stylesheet), resourceCategories.Stylesheet, true),
WebBundle: new ResourceType('webbundle', i18nLazyString(UIStrings.webbundle), resourceCategories.Other, false),
};
const mimeTypeByName = new Map([
// CoffeeScript
['Cakefile', 'text/x-coffeescript'],
]);
// clang-format off
export const resourceTypeByExtension = new Map([
['js', resourceTypes.Script],
['mjs', resourceTypes.Script],
['css', resourceTypes.Stylesheet],
['xsl', resourceTypes.Stylesheet],
['avif', resourceTypes.Image],
['avifs', resourceTypes.Image],
['bmp', resourceTypes.Image],
['gif', resourceTypes.Image],
['ico', resourceTypes.Image],
['jpeg', resourceTypes.Image],
['jpg', resourceTypes.Image],
['jxl', resourceTypes.Image],
['png', resourceTypes.Image],
['svg', resourceTypes.Image],
['tif', resourceTypes.Image],
['tiff', resourceTypes.Image],
['vue', resourceTypes.Document],
['webmanifest', resourceTypes.Manifest],
['webp', resourceTypes.Media],
['otf', resourceTypes.Font],
['ttc', resourceTypes.Font],
['ttf', resourceTypes.Font],
['woff', resourceTypes.Font],
['woff2', resourceTypes.Font],
['wasm', resourceTypes.Wasm],
]);
// clang-format on
export const mimeTypeByExtension = new Map([
// Web extensions
['js', 'text/javascript'],
['mjs', 'text/javascript'],
['css', 'text/css'],
['html', 'text/html'],
['htm', 'text/html'],
['xml', 'application/xml'],
['xsl', 'application/xml'],
['wasm', 'application/wasm'],
['webmanifest', 'application/manifest+json'],
// HTML Embedded Scripts, ASP], JSP
['asp', 'application/x-aspx'],
['aspx', 'application/x-aspx'],
['jsp', 'application/x-jsp'],
// C/C++
['c', 'text/x-c++src'],
['cc', 'text/x-c++src'],
['cpp', 'text/x-c++src'],
['h', 'text/x-c++src'],
['m', 'text/x-c++src'],
['mm', 'text/x-c++src'],
// CoffeeScript
['coffee', 'text/x-coffeescript'],
// Dart
['dart', 'text/javascript'],
// TypeScript
['ts', 'text/typescript'],
['tsx', 'text/typescript-jsx'],
// JSON
['json', 'application/json'],
['gyp', 'application/json'],
['gypi', 'application/json'],
// C#
['cs', 'text/x-csharp'],
// Java
['java', 'text/x-java'],
// Less
['less', 'text/x-less'],
// PHP
['php', 'application/x-httpd-php'],
['phtml', 'application/x-httpd-php'],
// Python
['py', 'text/x-python'],
// Shell
['sh', 'text/x-sh'],
// SCSS
['scss', 'text/x-scss'],
// Video Text Tracks.
['vtt', 'text/vtt'],
// LiveScript
['ls', 'text/x-livescript'],
// Markdown
['md', 'text/markdown'],
// ClojureScript
['cljs', 'text/x-clojure'],
['cljc', 'text/x-clojure'],
['cljx', 'text/x-clojure'],
// Stylus
['styl', 'text/x-styl'],
// JSX
['jsx', 'text/jsx'],
// Image
['avif', 'image/avif'],
['avifs', 'image/avif-sequence'],
['bmp', 'image/bmp'],
['gif', 'image/gif'],
['ico', 'image/ico'],
['jpeg', 'image/jpeg'],
['jpg', 'image/jpeg'],
['jxl', 'image/jxl'],
['png', 'image/png'],
['svg', 'image/svg+xml'],
['tif', 'image/tif'],
['tiff', 'image/tiff'],
['webp', 'image/webp'],
// Font
['otf', 'font/otf'],
['ttc', 'font/collection'],
['ttf', 'font/ttf'],
['woff', 'font/woff'],
['woff2', 'font/woff2'],
// Vue
['vue', 'text/html'],
]);