| // Copyright 2016 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import type * as Protocol from '../../generated/protocol.js'; |
| import * as TextUtils from '../../models/text_utils/text_utils.js'; |
| import * as Common from '../common/common.js'; |
| import * as i18n from '../i18n/i18n.js'; |
| import * as Platform from '../platform/platform.js'; |
| |
| import type {CSSModel} from './CSSModel.js'; |
| import {DeferredDOMNode} from './DOMModel.js'; |
| import type {FrameAssociated} from './FrameAssociated.js'; |
| import type {PageResourceLoadInitiator} from './PageResourceLoader.js'; |
| import {ResourceTreeModel} from './ResourceTreeModel.js'; |
| import type {DebugId} from './SourceMap.js'; |
| |
| const UIStrings = { |
| /** |
| * @description Error message for when a CSS file can't be loaded |
| */ |
| couldNotFindTheOriginalStyle: 'Could not find the original style sheet.', |
| /** |
| * @description Error message to display when a source CSS file could not be retrieved. |
| */ |
| thereWasAnErrorRetrievingThe: 'There was an error retrieving the source styles.', |
| } as const; |
| const str_ = i18n.i18n.registerUIStrings('core/sdk/CSSStyleSheetHeader.ts', UIStrings); |
| const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_); |
| |
| export class CSSStyleSheetHeader implements TextUtils.ContentProvider.ContentProvider, FrameAssociated { |
| #cssModel: CSSModel; |
| id: Protocol.DOM.StyleSheetId; |
| frameId: Protocol.Page.FrameId; |
| sourceURL: Platform.DevToolsPath.UrlString; |
| hasSourceURL: boolean; |
| origin: Protocol.CSS.StyleSheetOrigin; |
| title: string; |
| disabled: boolean; |
| isInline: boolean; |
| isMutable: boolean; |
| isConstructed: boolean; |
| startLine: number; |
| startColumn: number; |
| endLine: number; |
| endColumn: number; |
| contentLength: number; |
| ownerNode: DeferredDOMNode|undefined; |
| sourceMapURL: Platform.DevToolsPath.UrlString|undefined; |
| readonly loadingFailed: boolean; |
| #originalContentProvider: TextUtils.StaticContentProvider.StaticContentProvider|null; |
| |
| constructor(cssModel: CSSModel, payload: Protocol.CSS.CSSStyleSheetHeader) { |
| this.#cssModel = cssModel; |
| this.id = payload.styleSheetId; |
| this.frameId = payload.frameId; |
| this.sourceURL = payload.sourceURL as Platform.DevToolsPath.UrlString; |
| this.hasSourceURL = Boolean(payload.hasSourceURL); |
| this.origin = payload.origin; |
| this.title = payload.title; |
| this.disabled = payload.disabled; |
| this.isInline = payload.isInline; |
| this.isMutable = payload.isMutable; |
| this.isConstructed = payload.isConstructed; |
| this.startLine = payload.startLine; |
| this.startColumn = payload.startColumn; |
| this.endLine = payload.endLine; |
| this.endColumn = payload.endColumn; |
| this.contentLength = payload.length; |
| if (payload.ownerNode) { |
| this.ownerNode = new DeferredDOMNode(cssModel.target(), payload.ownerNode); |
| } |
| this.sourceMapURL = payload.sourceMapURL as Platform.DevToolsPath.UrlString; |
| this.loadingFailed = payload.loadingFailed ?? false; |
| this.#originalContentProvider = null; |
| } |
| |
| originalContentProvider(): TextUtils.ContentProvider.ContentProvider { |
| if (!this.#originalContentProvider) { |
| const lazyContent = (async(): Promise<TextUtils.ContentData.ContentDataOrError> => { |
| const originalText = await this.#cssModel.originalStyleSheetText(this); |
| if (originalText === null) { |
| return {error: i18nString(UIStrings.couldNotFindTheOriginalStyle)}; |
| } |
| return new TextUtils.ContentData.ContentData(originalText, /* isBase64=*/ false, 'text/css'); |
| }); |
| this.#originalContentProvider = |
| new TextUtils.StaticContentProvider.StaticContentProvider(this.contentURL(), this.contentType(), lazyContent); |
| } |
| return this.#originalContentProvider; |
| } |
| |
| setSourceMapURL(sourceMapURL?: Platform.DevToolsPath.UrlString): void { |
| this.sourceMapURL = sourceMapURL; |
| } |
| |
| cssModel(): CSSModel { |
| return this.#cssModel; |
| } |
| |
| isAnonymousInlineStyleSheet(): boolean { |
| return !this.resourceURL() && !this.#cssModel.sourceMapManager().sourceMapForClient(this); |
| } |
| |
| isConstructedByNew(): boolean { |
| return this.isConstructed && this.sourceURL.length === 0; |
| } |
| |
| resourceURL(): Platform.DevToolsPath.UrlString { |
| return this.isViaInspector() ? this.viaInspectorResourceURL() : this.sourceURL; |
| } |
| |
| private getFrameURLPath(): string { |
| const model = this.#cssModel.target().model(ResourceTreeModel); |
| console.assert(Boolean(model)); |
| if (!model) { |
| return ''; |
| } |
| const frame = model.frameForId(this.frameId); |
| if (!frame) { |
| return ''; |
| } |
| console.assert(Boolean(frame)); |
| const parsedURL = new Common.ParsedURL.ParsedURL(frame.url); |
| let urlPath = parsedURL.host; |
| if (parsedURL.port) { |
| urlPath += ':' + parsedURL.port; |
| } |
| urlPath += parsedURL.folderPathComponents; |
| if (!urlPath.endsWith('/')) { |
| urlPath += '/'; |
| } |
| return urlPath; |
| } |
| |
| private viaInspectorResourceURL(): Platform.DevToolsPath.UrlString { |
| return `inspector://${this.getFrameURLPath()}inspector-stylesheet#${this.id}` as Platform.DevToolsPath.UrlString; |
| } |
| |
| lineNumberInSource(lineNumberInStyleSheet: number): number { |
| return this.startLine + lineNumberInStyleSheet; |
| } |
| |
| columnNumberInSource(lineNumberInStyleSheet: number, columnNumberInStyleSheet: number): number|undefined { |
| return (lineNumberInStyleSheet ? 0 : this.startColumn) + columnNumberInStyleSheet; |
| } |
| |
| /** |
| * Checks whether the position is in this style sheet. Assumes that the |
| * position's columnNumber is consistent with line endings. |
| */ |
| containsLocation(lineNumber: number, columnNumber: number): boolean { |
| const afterStart = |
| (lineNumber === this.startLine && columnNumber >= this.startColumn) || lineNumber > this.startLine; |
| const beforeEnd = lineNumber < this.endLine || (lineNumber === this.endLine && columnNumber <= this.endColumn); |
| return afterStart && beforeEnd; |
| } |
| |
| contentURL(): Platform.DevToolsPath.UrlString { |
| return this.resourceURL(); |
| } |
| |
| contentType(): Common.ResourceType.ResourceType { |
| return Common.ResourceType.resourceTypes.Stylesheet; |
| } |
| |
| async requestContentData(): Promise<TextUtils.ContentData.ContentDataOrError> { |
| const cssText = await this.#cssModel.getStyleSheetText(this.id); |
| if (cssText === null) { |
| return {error: i18nString(UIStrings.thereWasAnErrorRetrievingThe)}; |
| } |
| return new TextUtils.ContentData.ContentData(cssText, /* isBase64=*/ false, 'text/css'); |
| } |
| |
| async searchInContent(query: string, caseSensitive: boolean, isRegex: boolean): |
| Promise<TextUtils.ContentProvider.SearchMatch[]> { |
| const contentData = await this.requestContentData(); |
| return TextUtils.TextUtils.performSearchInContentData(contentData, query, caseSensitive, isRegex); |
| } |
| |
| isViaInspector(): boolean { |
| return this.origin === 'inspector'; |
| } |
| |
| createPageResourceLoadInitiator(): PageResourceLoadInitiator { |
| return { |
| target: this.#cssModel.target(), |
| frameId: this.frameId, |
| initiatorUrl: this.hasSourceURL ? Platform.DevToolsPath.EmptyUrlString : this.sourceURL, |
| }; |
| } |
| |
| debugId(): DebugId|null { |
| return null; |
| } |
| } |