|
|
|
|
Impressum css-logic.js
Interaktion und PortierbarkeitJAVA
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const MAX_DATA_URL_LENGTH = 40;
/**
* Provide access to the style information in a page.
* CssLogic uses the standard DOM API, and the Gecko InspectorUtils API to
* access styling information in the page, and present this to the user in a way
* that helps them understand:
* - why their expectations may not have been fulfilled
* - how browsers process CSS
* @constructor
*/
loader.lazyRequireGetter(
this,
"InspectorCSSParserWrapper",
"resource://devtools/shared/css/lexer.js",
true
);
loader.lazyRequireGetter(
this,
"getTabPrefs",
"resource://devtools/shared/indentation.js",
true
);
const { LocalizationHelper } = require("resource://devtools/shared/l10n.js");
const styleInspectorL10N = new LocalizationHelper(
"devtools/shared/locales/styleinspector.properties"
);
/**
* Special values for filter, in addition to an href these values can be used
*/
exports.FILTER = {
// show properties for all user style sheets.
USER: "user",
// USER, plus user-agent (i.e. browser) style sheets
UA: "ua",
};
/**
* Each rule has a status, the bigger the number, the better placed it is to
* provide styling information.
*
* These statuses are localized inside the styleinspector.properties
* string bundle.
* @see csshtmltree.js RuleView._cacheStatusNames()
*/
exports.STATUS = {
BEST: 3,
MATCHED: 2,
PARENT_MATCH: 1,
UNMATCHED: 0,
UNKNOWN: -1,
};
/**
* Mapping of CSS at-Rule className to CSSRule type name.
*/
exports.CSSAtRuleClassNameType = {
CSSContainerRule: "container",
CSSCounterStyleRule: "counter-style",
CSSDocumentRule: "document",
CSSFontFaceRule: "font-face",
CSSFontFeatureValuesRule: "font-feature-values",
CSSImportRule: "import",
CSSKeyframeRule: "keyframe",
CSSKeyframesRule: "keyframes",
CSSLayerBlockRule: "layer",
CSSMediaRule: "media",
CSSNamespaceRule: "namespace",
CSSPageRule: "page",
CSSScopeRule: "scope",
CSSStartingStyleRule: "starting-style",
CSSSupportsRule: "supports",
};
/**
* Get Rule type as human-readable string (ex: "@media", "@container", …)
*
* @param {CSSRule} cssRule
* @returns {String}
*/
exports.getCSSAtRuleTypeName = function (cssRule) {
const ruleClassName = ChromeUtils.getClassName(cssRule);
const atRuleTypeName = exports.CSSAtRuleClassNameType[ruleClassName];
if (atRuleTypeName) {
return "@" + atRuleTypeName;
}
return "";
};
/**
* Lookup a l10n string in the shared styleinspector string bundle.
*
* @param {String} name
* The key to lookup.
* @returns {String} A localized version of the given key.
*/
exports.l10n = name => styleInspectorL10N.getStr(name);
exports.l10nFormatStr = (name, ...args) =>
styleInspectorL10N.getFormatStr(name, ...args);
/**
* Is the given property sheet an author stylesheet?
*
* @param {CSSStyleSheet} sheet a stylesheet
* @return {boolean} true if the given stylesheet is an author stylesheet,
* false otherwise.
*/
exports.isAuthorStylesheet = function (sheet) {
return sheet.parsingMode === "author";
};
/**
* Is the given property sheet a user stylesheet?
*
* @param {CSSStyleSheet} sheet a stylesheet
* @return {boolean} true if the given stylesheet is a user stylesheet,
* false otherwise.
*/
exports.isUserStylesheet = function (sheet) {
return sheet.parsingMode === "user";
};
/**
* Is the given property sheet a agent stylesheet?
*
* @param {CSSStyleSheet} sheet a stylesheet
* @return {boolean} true if the given stylesheet is a agent stylesheet,
* false otherwise.
*/
exports.isAgentStylesheet = function (sheet) {
return sheet.parsingMode === "agent";
};
/**
* Return a shortened version of a style sheet's source.
*
* @param {CSSStyleSheet} sheet the DOM object for the style sheet.
*/
exports.shortSource = function (sheet) {
if (!sheet) {
return exports.l10n("rule.sourceInline");
}
if (!sheet.href) {
return exports.l10n(
sheet.constructed ? "rule.sourceConstructed" : "rule.sourceInline"
);
}
let name = sheet.href;
// If the sheet is a data URL, return a trimmed version of it.
const dataUrl = sheet.href.trim().match(/^data:.*?,((?:.|\r|\n)*)$/);
if (dataUrl) {
name =
dataUrl[1].length > MAX_DATA_URL_LENGTH
? `${dataUrl[1].substr(0, MAX_DATA_URL_LENGTH - 1)}…`
: dataUrl[1];
} else {
// We try, in turn, the filename, filePath, query string, whole thing
let url = {};
try {
url = new URL(sheet.href);
} catch (ex) {
// Some UA-provided stylesheets are not valid URLs.
}
if (url.pathname) {
const index = url.pathname.lastIndexOf("/");
if (index !== -1 && index < url.pathname.length) {
name = url.pathname.slice(index + 1);
} else {
name = url.pathname;
}
} else if (url.query) {
name = url.query;
}
}
try {
name = decodeURIComponent(name);
} catch (e) {
// This may still fail if the URL contains invalid % numbers (for ex)
}
return name;
};
/**
* Return the style sheet's source, handling element, inline and constructed stylesheets.
*
* @param {CSSStyleSheet} sheet the DOM object for the style sheet.
*/
exports.longSource = function (sheet) {
if (!sheet) {
return exports.l10n("rule.sourceInline");
}
if (!sheet.href) {
return exports.l10n(
sheet.constructed ? "rule.sourceConstructed" : "rule.sourceInline"
);
}
return sheet.href;
};
const TAB_CHARS = "\t";
const SPACE_CHARS = " ";
function getLineCountInComments(text) {
let count = 0;
for (const comment of text.match(/\/\*(?:.|\n)*?\*\//gm) || []) {
count += comment.split("\n").length + 1;
}
return count;
}
/**
* Prettify minified CSS text.
* This prettifies CSS code where there is no indentation in usual places while
* keeping original indentation as-is elsewhere.
*
* Returns an object with the resulting prettified source and a list of mappings of
* token positions between the original and the prettified source. Each single mapping
* is an object that looks like this:
*
* {
* original: {line: {Number}, column: {Number}},
* generated: {line: {Number}, column: {Number}},
* }
*
* @param {String} text
* The CSS source to prettify.
* @param {Number} ruleCount
* The number of CSS rules expected in the CSS source.
* Set to null to force the text to be pretty-printed.
*
* @return {Object}
* Object with the prettified source and source mappings.
* {
* result: {String} // Prettified source
* mappings: {Array} // List of objects with mappings for lines and columns
* // between the original source and prettified source
* }
*/
// eslint-disable-next-line complexity
function prettifyCSS(text, ruleCount) {
if (prettifyCSS.LINE_SEPARATOR == null) {
const os = Services.appinfo.OS;
prettifyCSS.LINE_SEPARATOR = os === "WINNT" ? "\r\n" : "\n";
}
// Stylesheets may start and end with HTML comment tags (possibly with whitespaces
// before and after). Remove those first. Don't do anything there aren't any.
const trimmed = text.trim();
if (trimmed.startsWith(" | | |