/* * Copyright (C) 2013 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. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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. */ // FIXME: CSSManager lacks advanced multi-target support. (Stylesheets per-target) WI.CSSManager = class CSSManager extends WI.Object { constructor() { super(); WI.Frame.addEventListener(WI.Frame.Event.MainResourceDidChange, this._mainResourceDidChange, this); WI.Frame.addEventListener(WI.Frame.Event.ResourceWasAdded, this._resourceAdded, this); WI.Resource.addEventListener(WI.SourceCode.Event.ContentDidChange, this._resourceContentDidChange, this); WI.Resource.addEventListener(WI.Resource.Event.TypeDidChange, this._resourceTypeDidChange, this); WI.DOMNode.addEventListener(WI.DOMNode.Event.AttributeModified, this._nodeAttributesDidChange, this); WI.DOMNode.addEventListener(WI.DOMNode.Event.AttributeRemoved, this._nodeAttributesDidChange, this); WI.DOMNode.addEventListener(WI.DOMNode.Event.EnabledPseudoClassesChanged, this._nodePseudoClassesDidChange, this); this._colorFormatSetting = new WI.Setting("default-color-format", WI.Color.Format.Original); this._styleSheetIdentifierMap = new Map; this._styleSheetFrameURLMap = new Map; this._nodeStylesMap = {}; this._modifiedStyles = new Map; this._defaultUserPreferences = new Map; this._overridenUserPreferences = new Map; this._propertyNameCompletions = null; } // Target initializeTarget(target) { if (target.hasDomain("CSS")) target.CSSAgent.enable(); } initializeCSSPropertyNameCompletions(target) { console.assert(target.hasDomain("CSS")); if (this._propertyNameCompletions) return; target.CSSAgent.getSupportedCSSProperties((error, cssProperties) => { if (error) return; this._propertyNameCompletions = new WI.CSSPropertyNameCompletions(cssProperties); WI.CSSKeywordCompletions.addCustomCompletions(cssProperties); // CodeMirror is not included by tests so we shouldn't assume it always exists. // If it isn't available we skip MIME type associations. if (!window.CodeMirror) return; let propertyNamesForCodeMirror = {}; let valueKeywordsForCodeMirror = {"inherit": true, "initial": true, "unset": true, "revert": true, "revert-layer": true, "var": true, "env": true}; let colorKeywordsForCodeMirror = {}; function nameForCodeMirror(name) { // CodeMirror parses the vendor prefix separate from the property or keyword name, // so we need to strip vendor prefixes from our names. Also strip function parenthesis. return name.replace(/^-[^-]+-/, "").replace(/\(\)$/, "").toLowerCase(); } for (let property of cssProperties) { // Properties can also be value keywords, like when used in a transition. // So we add them to both lists. let codeMirrorPropertyName = nameForCodeMirror(property.name); propertyNamesForCodeMirror[codeMirrorPropertyName] = true; valueKeywordsForCodeMirror[codeMirrorPropertyName] = true; } for (let propertyName in WI.CSSKeywordCompletions._propertyKeywordMap) { let keywords = WI.CSSKeywordCompletions._propertyKeywordMap[propertyName]; for (let keyword of keywords) { // Skip numbers, like the ones defined for font-weight. if (keyword === WI.CSSKeywordCompletions.AllPropertyNamesPlaceholder || !isNaN(Number(keyword))) continue; valueKeywordsForCodeMirror[nameForCodeMirror(keyword)] = true; } } for (let color of WI.CSSKeywordCompletions._colors) colorKeywordsForCodeMirror[nameForCodeMirror(color)] = true; // TODO: Remove these keywords once they are built-in codemirror or once we get values from WebKit itself. valueKeywordsForCodeMirror["conic-gradient"] = true; valueKeywordsForCodeMirror["repeating-conic-gradient"] = true; function updateCodeMirrorCSSMode(mimeType) { let modeSpec = CodeMirror.resolveMode(mimeType); console.assert(modeSpec.propertyKeywords); console.assert(modeSpec.valueKeywords); console.assert(modeSpec.colorKeywords); modeSpec.propertyKeywords = propertyNamesForCodeMirror; modeSpec.valueKeywords = valueKeywordsForCodeMirror; modeSpec.colorKeywords = colorKeywordsForCodeMirror; CodeMirror.defineMIME(mimeType, modeSpec); } updateCodeMirrorCSSMode("text/css"); updateCodeMirrorCSSMode("text/x-scss"); }); if (target.hasCommand("CSS.getSupportedSystemFontFamilyNames")) { target.CSSAgent.getSupportedSystemFontFamilyNames((error, fontFamilyNames) =>{ if (error) return; WI.CSSKeywordCompletions.addPropertyCompletionValues("font-family", fontFamilyNames); WI.CSSKeywordCompletions.addPropertyCompletionValues("font", fontFamilyNames); }); } } // Static static supportsInspectorStyleSheet() { return InspectorBackend.hasCommand("CSS.createStyleSheet"); } static protocolStyleSheetOriginToEnum(origin) { switch (origin) { case InspectorBackend.Enum.CSS.StyleSheetOrigin.User: return WI.CSSStyleSheet.Type.User; case InspectorBackend.Enum.CSS.StyleSheetOrigin.UserAgent: return WI.CSSStyleSheet.Type.UserAgent; case InspectorBackend.Enum.CSS.StyleSheetOrigin.Inspector: return WI.CSSStyleSheet.Type.Inspector; } // COMPATIBILITY (iOS 14): CSS.StyleSheetOrigin.Regular was replaced with CSS.StyleSheetOrigin.Author. console.assert(!InspectorBackend.Enum.CSS.StyleSheetOrigin.Author || origin === InspectorBackend.Enum.CSS.StyleSheetOrigin.Author); console.assert(!InspectorBackend.Enum.CSS.StyleSheetOrigin.Regular || origin === InspectorBackend.Enum.CSS.StyleSheetOrigin.Regular); return WI.CSSStyleSheet.Type.Author; } static protocolGroupingTypeToEnum(type) { // COMPATIBILITY (iOS 13): CSS.Grouping did not exist yet. if (!InspectorBackend.Enum.CSS.Grouping) { switch (type) { case "mediaRule": return WI.CSSGrouping.Type.MediaRule; case "importRule": return WI.CSSGrouping.Type.MediaImportRule; case "linkedSheet": return WI.CSSGrouping.Type.MediaLinkNode; case "inlineSheet": return WI.CSSGrouping.Type.MediaStyleNode; } } return type; } static displayNameForPseudoId(pseudoId) { // Compatibility (iOS 12.2): CSS.PseudoId did not exist. if (!InspectorBackend.Enum.CSS.PseudoId) { switch (pseudoId) { case 1: // PseudoId.FirstLine return WI.unlocalizedString("::first-line"); case 2: // PseudoId.FirstLetter return WI.unlocalizedString("::first-letter"); case 3: // PseudoId.Marker return WI.unlocalizedString("::marker"); case 4: // PseudoId.Before return WI.unlocalizedString("::before"); case 5: // PseudoId.After return WI.unlocalizedString("::after"); case 6: // PseudoId.Selection return WI.unlocalizedString("::selection"); case 7: // PseudoId.Scrollbar return WI.unlocalizedString("::scrollbar"); case 8: // PseudoId.ScrollbarThumb return WI.unlocalizedString("::scrollbar-thumb"); case 9: // PseudoId.ScrollbarButton return WI.unlocalizedString("::scrollbar-button"); case 10: // PseudoId.ScrollbarTrack return WI.unlocalizedString("::scrollbar-track"); case 11: // PseudoId.ScrollbarTrackPiece return WI.unlocalizedString("::scrollbar-track-piece"); case 12: // PseudoId.ScrollbarCorner return WI.unlocalizedString("::scrollbar-corner"); case 13: // PseudoId.Resizer return WI.unlocalizedString("::resizer"); default: console.error("Unknown pseudo id", pseudoId); return ""; } } switch (pseudoId) { case CSSManager.PseudoSelectorNames.FirstLine: return WI.unlocalizedString("::first-line"); case CSSManager.PseudoSelectorNames.FirstLetter: return WI.unlocalizedString("::first-letter"); case CSSManager.PseudoSelectorNames.Highlight: return WI.unlocalizedString("::highlight"); case CSSManager.PseudoSelectorNames.Marker: return WI.unlocalizedString("::marker"); case CSSManager.PseudoSelectorNames.Before: return WI.unlocalizedString("::before"); case CSSManager.PseudoSelectorNames.After: return WI.unlocalizedString("::after"); case CSSManager.PseudoSelectorNames.Selection: return WI.unlocalizedString("::selection"); case CSSManager.PseudoSelectorNames.Backdrop: return WI.unlocalizedString("::backdrop"); case CSSManager.PseudoSelectorNames.Scrollbar: return WI.unlocalizedString("::scrollbar"); case CSSManager.PseudoSelectorNames.ScrollbarThumb: return WI.unlocalizedString("::scrollbar-thumb"); case CSSManager.PseudoSelectorNames.ScrollbarButton: return WI.unlocalizedString("::scrollbar-button"); case CSSManager.PseudoSelectorNames.ScrollbarTrack: return WI.unlocalizedString("::scrollbar-track"); case CSSManager.PseudoSelectorNames.ScrollbarTrackPiece: return WI.unlocalizedString("::scrollbar-track-piece"); case CSSManager.PseudoSelectorNames.ScrollbarCorner: return WI.unlocalizedString("::scrollbar-corner"); case CSSManager.PseudoSelectorNames.Resizer: return WI.unlocalizedString("::resizer"); default: console.error("Unknown pseudo id", pseudoId); return ""; } } static displayNameForForceablePseudoClass(pseudoClass) { switch (pseudoClass) { case WI.CSSManager.ForceablePseudoClass.Active: return WI.unlocalizedString(":active"); case WI.CSSManager.ForceablePseudoClass.Focus: return WI.unlocalizedString(":focus"); case WI.CSSManager.ForceablePseudoClass.FocusVisible: return WI.unlocalizedString(":focus-visible"); case WI.CSSManager.ForceablePseudoClass.FocusWithin: return WI.unlocalizedString(":focus-within"); case WI.CSSManager.ForceablePseudoClass.Hover: return WI.unlocalizedString(":hover"); case WI.CSSManager.ForceablePseudoClass.Target: return WI.unlocalizedString(":target"); case WI.CSSManager.ForceablePseudoClass.Visited: return WI.unlocalizedString(":visited"); } console.assert(false, "Unknown pseudo class", pseudoClass); return ""; } // Public get propertyNameCompletions() { return this._propertyNameCompletions; } get overridenUserPreferences() { return this._overridenUserPreferences; } get defaultUserPreferences() { return this._defaultUserPreferences; } get overridenUserPreferences() { return this._overridenUserPreferences; } get preferredColorFormat() { return this._colorFormatSetting.value; } get styleSheets() { return Array.from(this._styleSheetIdentifierMap.values()); } get supportsOverrideUserPreference() { return InspectorBackend.hasCommand("Page.overrideUserPreference") && this._defaultUserPreferences.size; } get supportsOverrideColorScheme() { // A backend for a platform that does not support color schemes will not dispatch an initial event (Page.defaultAppearanceDidChange or Page.defaultUserPreferencesDidChange) // with the default value for the color scheme preference which gets stored on the frontend. // COMPATIBILITY (macOS 13.0, iOS 16.0): `PrefersColorScheme` value for `Page.UserPreferenceName` did not exist yet. return this._defaultUserPreferences.has(InspectorBackend.Enum.Page.UserPreferenceName?.PrefersColorScheme) || this._defaultUserPreferences.has(WI.CSSManager.ForcedAppearancePreference); } // COMPATIBILITY (macOS 13, iOS 16.0): `Page.setForcedAppearance()` was removed in favor of `Page.overrideUserPreference()` setForcedAppearance(name) { let commandArguments = {}; switch (name) { case WI.CSSManager.Appearance.Light: commandArguments.appearance = InspectorBackend.Enum.Page.Appearance.Light; break; case WI.CSSManager.Appearance.Dark: commandArguments.appearance = InspectorBackend.Enum.Page.Appearance.Dark; break; case null: // COMPATIBILITY (iOS 14): the `appearance`` parameter of `Page.setForcedAppearance` was not optional. // Since support can't be tested directly, check for the `options`` parameter of `DOMDebugger.setDOMBreakpoint` (iOS 14.0+). // FIXME: Use explicit version checking once https://webkit.org/b/148680 is fixed. if (!InspectorBackend.hasCommand("DOMDebugger.setDOMBreakpoint", "options")) commandArguments.appearance = ""; break; default: console.assert(false, "Unknown appearance", name); return; } let target = WI.assumingMainTarget(); return target.PageAgent.setForcedAppearance.invoke(commandArguments); } set layoutContextTypeChangedMode(layoutContextTypeChangedMode) { for (let target of WI.targets) { // COMPATIBILITY (iOS 14.5): CSS.setLayoutContextTypeChangedMode did not exist. if (target.hasCommand("CSS.setLayoutContextTypeChangedMode")) target.CSSAgent.setLayoutContextTypeChangedMode(layoutContextTypeChangedMode); } } canForcePseudoClass(pseudoClass) { if (!InspectorBackend.hasCommand("CSS.forcePseudoState")) return false; if (!pseudoClass) return true; switch (pseudoClass) { case WI.CSSManager.ForceablePseudoClass.Active: case WI.CSSManager.ForceablePseudoClass.Focus: case WI.CSSManager.ForceablePseudoClass.Hover: case WI.CSSManager.ForceablePseudoClass.Visited: return true; case WI.CSSManager.ForceablePseudoClass.FocusVisible: case WI.CSSManager.ForceablePseudoClass.FocusWithin: case WI.CSSManager.ForceablePseudoClass.Target: // COMPATIBILITY (macOS 12.3, iOS 15.4): CSS.ForceablePseudoClass did not exist yet. return !!InspectorBackend.Enum.CSS.ForceablePseudoClass; } console.assert(false, "Unknown pseudo class", pseudoClass); return false; } overrideUserPreference(preference, value) { let promises = []; for (let target of WI.targets) { // COMPATIBILITY (macOS 13.0, iOS 16.0): `Page.overrideUserPreference()` did not exist yet. if (target.hasCommand("Page.overrideUserPreference") && InspectorBackend.Enum.Page.UserPreferenceName[preference]) promises.push(target.PageAgent.overrideUserPreference(preference, value)); // COMPATIBILITY (macOS 13, iOS 16.0): `Page.setForcedAppearance()` was removed in favor of `Page.overrideUserPreference()` if (preference === WI.CSSManager.ForcedAppearancePreference && target.hasCommand("Page.setForcedAppearance")) promises.push(this.setForcedAppearance(value || null)); } if (value) this._overridenUserPreferences.set(preference, value); else this._overridenUserPreferences.delete(preference); Promise.allSettled(promises).then(() => { this.mediaQueryResultChanged(); this.dispatchEventToListeners(WI.CSSManager.Event.OverridenUserPreferencesDidChange); }) } propertyNameHasOtherVendorPrefix(name) { if (!name || name.length < 4 || name.charAt(0) !== "-") return false; var match = name.match(/^(?:-moz-|-ms-|-o-|-epub-)/); if (!match) return false; return true; } propertyValueHasOtherVendorKeyword(value) { var match = value.match(/(?:-moz-|-ms-|-o-|-epub-)[-\w]+/); if (!match) return false; return true; } canonicalNameForPropertyName(name) { if (!name || name.length < 8 || name.charAt(0) !== "-") return name; // Keep in sync with prefix list from Source/WebInspectorUI/Scripts/update-inspector-css-documentation var match = name.match(/^(?:-webkit-|-khtml-|-apple-)(.+)/); if (!match) return name; return match[1]; } styleSheetForIdentifier(id) { let styleSheet = this._styleSheetIdentifierMap.get(id); if (styleSheet) return styleSheet; styleSheet = new WI.CSSStyleSheet(id); this._styleSheetIdentifierMap.set(id, styleSheet); return styleSheet; } stylesForNode(node) { if (node.id in this._nodeStylesMap) return this._nodeStylesMap[node.id]; var styles = new WI.DOMNodeStyles(node); this._nodeStylesMap[node.id] = styles; return styles; } inspectorStyleSheetsForFrame(frame) { return this.styleSheets.filter((styleSheet) => styleSheet.isInspectorStyleSheet() && styleSheet.parentFrame === frame); } preferredInspectorStyleSheetForFrame(frame, callback) { var inspectorStyleSheets = this.inspectorStyleSheetsForFrame(frame); for (let styleSheet of inspectorStyleSheets) { if (styleSheet[WI.CSSManager.PreferredInspectorStyleSheetSymbol]) { callback(styleSheet); return; } } let target = WI.assumingMainTarget(); target.CSSAgent.createStyleSheet(frame.id, function(error, styleSheetId) { if (error || !styleSheetId) { WI.reportInternalError(error || styleSheetId); return; } const url = null; let styleSheet = WI.cssManager.styleSheetForIdentifier(styleSheetId); styleSheet.updateInfo(url, frame, styleSheet.origin, styleSheet.isInlineStyleTag(), styleSheet.startLineNumber, styleSheet.startColumnNumber); styleSheet[WI.CSSManager.PreferredInspectorStyleSheetSymbol] = true; callback(styleSheet); }); } mediaTypeChanged() { // Act the same as if media queries changed. this.mediaQueryResultChanged(); } get modifiedStyles() { return Array.from(this._modifiedStyles.values()); } addModifiedStyle(style) { this._modifiedStyles.set(style.stringId, style); this.dispatchEventToListeners(WI.CSSManager.Event.ModifiedStylesChanged); } getModifiedStyle(style) { return this._modifiedStyles.get(style.stringId); } removeModifiedStyle(style) { this._modifiedStyles.delete(style.stringId); this.dispatchEventToListeners(WI.CSSManager.Event.ModifiedStylesChanged); } // PageObserver // COMPATIBILITY (macOS 13, iOS 16.0): `Page.defaultAppearanceDidChange` was removed in favor of `Page.defaultUserPreferencesDidChange` defaultAppearanceDidChange(protocolName) { let appearance = null; switch (protocolName) { case InspectorBackend.Enum.Page.Appearance.Light: appearance = WI.CSSManager.Appearance.Light; break; case InspectorBackend.Enum.Page.Appearance.Dark: appearance = WI.CSSManager.Appearance.Dark; break; default: console.error("Unknown default appearance name:", protocolName); break; } this.mediaQueryResultChanged(); this._defaultUserPreferences.set(WI.CSSManager.ForcedAppearancePreference, appearance); this.dispatchEventToListeners(WI.CSSManager.Event.DefaultUserPreferencesDidChange); } defaultUserPreferencesDidChange(userPreferences) { this._defaultUserPreferences.clear(); for (let userPreference of userPreferences) this._defaultUserPreferences.set(userPreference.name, userPreference.value) this.dispatchEventToListeners(WI.CSSManager.Event.DefaultUserPreferencesDidChange); } // CSSObserver mediaQueryResultChanged() { for (var key in this._nodeStylesMap) this._nodeStylesMap[key].mediaQueryResultDidChange(); } styleSheetChanged(styleSheetIdentifier) { var styleSheet = this.styleSheetForIdentifier(styleSheetIdentifier); console.assert(styleSheet); // Do not observe inline styles if (styleSheet.isInlineStyleAttributeStyleSheet()) return; if (!styleSheet.noteContentDidChange()) return; this._updateResourceContent(styleSheet); } styleSheetAdded(styleSheetInfo) { console.assert(!this._styleSheetIdentifierMap.has(styleSheetInfo.styleSheetId), "Attempted to add a CSSStyleSheet but identifier was already in use"); let styleSheet = this.styleSheetForIdentifier(styleSheetInfo.styleSheetId); let parentFrame = WI.networkManager.frameForIdentifier(styleSheetInfo.frameId); let origin = WI.CSSManager.protocolStyleSheetOriginToEnum(styleSheetInfo.origin); styleSheet.updateInfo(styleSheetInfo.sourceURL, parentFrame, origin, styleSheetInfo.isInline, styleSheetInfo.startLine, styleSheetInfo.startColumn); this.dispatchEventToListeners(WI.CSSManager.Event.StyleSheetAdded, {styleSheet}); } styleSheetRemoved(styleSheetIdentifier) { let styleSheet = this._styleSheetIdentifierMap.get(styleSheetIdentifier); console.assert(styleSheet, "Attempted to remove a CSSStyleSheet that was not tracked"); if (!styleSheet) return; this._styleSheetIdentifierMap.delete(styleSheetIdentifier); this.dispatchEventToListeners(WI.CSSManager.Event.StyleSheetRemoved, {styleSheet}); } // Private _nodePseudoClassesDidChange(event) { var node = event.target; for (var key in this._nodeStylesMap) { var nodeStyles = this._nodeStylesMap[key]; if (nodeStyles.node !== node && !nodeStyles.node.isDescendant(node)) continue; nodeStyles.pseudoClassesDidChange(node); } } _nodeAttributesDidChange(event) { var node = event.target; for (var key in this._nodeStylesMap) { var nodeStyles = this._nodeStylesMap[key]; if (nodeStyles.node !== node && !nodeStyles.node.isDescendant(node)) continue; nodeStyles.attributeDidChange(node, event.data.name); } } _mainResourceDidChange(event) { console.assert(event.target instanceof WI.Frame); if (!event.target.isMainFrame()) return; // Clear our maps when the main frame navigates. this._styleSheetIdentifierMap.clear(); this._styleSheetFrameURLMap.clear(); this._modifiedStyles.clear(); this._nodeStylesMap = {}; } _resourceAdded(event) { console.assert(event.target instanceof WI.Frame); var resource = event.data.resource; console.assert(resource); if (resource.type !== WI.Resource.Type.StyleSheet) return; this._clearStyleSheetsForResource(resource); } _resourceTypeDidChange(event) { console.assert(event.target instanceof WI.Resource); var resource = event.target; if (resource.type !== WI.Resource.Type.StyleSheet) return; this._clearStyleSheetsForResource(resource); } _clearStyleSheetsForResource(resource) { // Clear known stylesheets for this URL and frame. This will cause the style sheets to // be updated next time _fetchInfoForAllStyleSheets is called. this._styleSheetIdentifierMap.delete(this._frameURLMapKey(resource.parentFrame, resource.url)); } _frameURLMapKey(frame, url) { return frame.id + ":" + url; } _lookupStyleSheetForResource(resource, callback) { this._lookupStyleSheet(resource.parentFrame, resource.url, callback); } _lookupStyleSheet(frame, url, callback) { console.assert(frame instanceof WI.Frame); let key = this._frameURLMapKey(frame, url); function styleSheetsFetched() { callback(this._styleSheetFrameURLMap.get(key) || null); } let styleSheet = this._styleSheetFrameURLMap.get(key) || null; if (styleSheet) callback(styleSheet); else this._fetchInfoForAllStyleSheets(styleSheetsFetched.bind(this)); } _fetchInfoForAllStyleSheets(callback) { console.assert(typeof callback === "function"); function processStyleSheets(error, styleSheets) { this._styleSheetFrameURLMap.clear(); if (error) { callback(); return; } for (let styleSheetInfo of styleSheets) { let parentFrame = WI.networkManager.frameForIdentifier(styleSheetInfo.frameId); let origin = WI.CSSManager.protocolStyleSheetOriginToEnum(styleSheetInfo.origin); let styleSheet = this.styleSheetForIdentifier(styleSheetInfo.styleSheetId); styleSheet.updateInfo(styleSheetInfo.sourceURL, parentFrame, origin, styleSheetInfo.isInline, styleSheetInfo.startLine, styleSheetInfo.startColumn); let key = this._frameURLMapKey(parentFrame, styleSheetInfo.sourceURL); this._styleSheetFrameURLMap.set(key, styleSheet); } callback(); } let target = WI.assumingMainTarget(); target.CSSAgent.getAllStyleSheets(processStyleSheets.bind(this)); } _resourceContentDidChange(event) { var resource = event.target; if (resource === this._ignoreResourceContentDidChangeEventForResource) return; // Ignore changes to resource overrides, those are not live on the page. if (resource.localResourceOverride) return; // Ignore if it isn't a CSS style sheet. if (resource.type !== WI.Resource.Type.StyleSheet || resource.syntheticMIMEType !== "text/css") return; function applyStyleSheetChanges() { function styleSheetFound(styleSheet) { resource.__pendingChangeTimeout.cancel(); console.assert(styleSheet); if (!styleSheet) return; // To prevent updating a TextEditor's content while the user is typing in it we want to // ignore the next _updateResourceContent call. resource.__ignoreNextUpdateResourceContent = true; let revision = styleSheet.editableRevision; revision.updateRevisionContent(resource.content); } this._lookupStyleSheetForResource(resource, styleSheetFound.bind(this)); } if (!resource.__pendingChangeTimeout) resource.__pendingChangeTimeout = new Throttler(applyStyleSheetChanges.bind(this), 100); resource.__pendingChangeTimeout.fire(); } _updateResourceContent(styleSheet) { console.assert(styleSheet); function fetchedStyleSheetContent(parameters) { styleSheet.__pendingChangeTimeout.cancel(); let representedObject = parameters.sourceCode; console.assert(representedObject.url); if (!representedObject.url) return; if (!styleSheet.isInspectorStyleSheet()) { // Only try to update stylesheet resources. Other resources, like documents, can contain // multiple stylesheets and we don't have the source ranges to update those. representedObject = representedObject.parentFrame.resourcesForURL(representedObject.url).find((resource) => resource.type === WI.Resource.Type.StyleSheet); if (!representedObject) return; } if (representedObject.__ignoreNextUpdateResourceContent) { representedObject.__ignoreNextUpdateResourceContent = false; return; } this._ignoreResourceContentDidChangeEventForResource = representedObject; let revision = representedObject.editableRevision; if (styleSheet.isInspectorStyleSheet()) { revision.updateRevisionContent(representedObject.content); styleSheet.dispatchEventToListeners(WI.SourceCode.Event.ContentDidChange); } else revision.updateRevisionContent(parameters.content); this._ignoreResourceContentDidChangeEventForResource = null; } function styleSheetReady() { styleSheet.requestContent().then(fetchedStyleSheetContent.bind(this)); } function applyStyleSheetChanges() { if (styleSheet.url) styleSheetReady.call(this); else this._fetchInfoForAllStyleSheets(styleSheetReady.bind(this)); } if (!styleSheet.__pendingChangeTimeout) styleSheet.__pendingChangeTimeout = new Throttler(applyStyleSheetChanges.bind(this), 100); styleSheet.__pendingChangeTimeout.fire(); } }; WI.CSSManager.Event = { StyleSheetAdded: "css-manager-style-sheet-added", StyleSheetRemoved: "css-manager-style-sheet-removed", ModifiedStylesChanged: "css-manager-modified-styles-changed", DefaultUserPreferencesDidChange: "css-manager-default-user-preferences-did-change", OverridenUserPreferencesDidChange: "css-manager-overriden-user-preferences-did-change", }; WI.CSSManager.UserPreferenceDefaultValue = "System"; // COMPATIBILITY (macOS 13, iOS 16.0): `Page.setForcedAppearance()` was removed in favor of `Page.overrideUserPreference()` WI.CSSManager.ForcedAppearancePreference = "ForcedAppearancePreference"; WI.CSSManager.Appearance = { Light: "Light", Dark: "Dark", }; WI.CSSManager.PseudoSelectorNames = { After: "after", Before: "before", Backdrop: "backdrop", FirstLetter: "first-letter", FirstLine: "first-line", Highlight: "highlight", Marker: "marker", Resizer: "resizer", Scrollbar: "scrollbar", ScrollbarButton: "scrollbar-button", ScrollbarCorner: "scrollbar-corner", ScrollbarThumb: "scrollbar-thumb", ScrollbarTrack: "scrollbar-track", ScrollbarTrackPiece: "scrollbar-track-piece", Selection: "selection", }; WI.CSSManager.LayoutContextTypeChangedMode = { Observed: "observed", All: "all", }; WI.CSSManager.PseudoElementNames = ["before", "after"]; WI.CSSManager.ForceablePseudoClass = { Active: "active", Focus: "focus", FocusVisible: "focus-visible", FocusWithin: "focus-within", Hover: "hover", Target: "target", Visited: "visited", }; WI.CSSManager.PreferredInspectorStyleSheetSymbol = Symbol("css-manager-preferred-inspector-style-sheet");