CSSOverviewModel.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. export default class CSSOverviewModel extends SDK.SDKModel{constructor(target){super(target);this._runtimeAgent=target.runtimeAgent();this._cssAgent=target.cssAgent();this._domAgent=target.domAgent();this._domSnapshotAgent=target.domsnapshotAgent();this._overlayAgent=target.overlayAgent();}
  2. highlightNode(node){const highlightConfig={contentColor:Common.Color.PageHighlight.Content.toProtocolRGBA(),showInfo:true};this._overlayAgent.invoke_hideHighlight({});this._overlayAgent.invoke_highlightNode({backendNodeId:node,highlightConfig});}
  3. async getNodeStyleStats(){const backgroundColors=new Map();const textColors=new Map();const fillColors=new Map();const borderColors=new Map();const fontInfo=new Map();const unusedDeclarations=new Map();const snapshotConfig={computedStyles:['background-color','color','fill','border-top-width','border-top-color','border-bottom-width','border-bottom-color','border-left-width','border-left-color','border-right-width','border-right-color','font-family','font-size','font-weight','line-height','position','top','right','bottom','left','display','width','height','vertical-align']};const storeColor=(id,nodeId,target)=>{if(id===-1){return;}
  4. const colorText=strings[id];const color=Common.Color.parse(colorText);if(!color||color.rgba()[3]===0){return;}
  5. const colorFormatted=color.hasAlpha()?color.asString(Common.Color.Format.HEXA):color.asString(Common.Color.Format.HEX);const colorValues=target.get(colorFormatted)||new Set();colorValues.add(nodeId);target.set(colorFormatted,colorValues);};const isSVGNode=nodeName=>{const validNodes=new Set(['altglyph','circle','ellipse','path','polygon','polyline','rect','svg','text','textpath','tref','tspan']);return validNodes.has(nodeName.toLowerCase());};const isReplacedContent=nodeName=>{const validNodes=new Set(['iframe','video','embed','img']);return validNodes.has(nodeName.toLowerCase());};const isTableElementWithDefaultStyles=(nodeName,display)=>{const validNodes=new Set(['tr','td','thead','tbody']);return validNodes.has(nodeName.toLowerCase())&&display.startsWith('table');};let elementCount=0;const{documents,strings}=await this._domSnapshotAgent.invoke_captureSnapshot(snapshotConfig);for(const{nodes,layout}of documents){elementCount+=layout.nodeIndex.length;for(let idx=0;idx<layout.styles.length;idx++){const styles=layout.styles[idx];const nodeIdx=layout.nodeIndex[idx];const nodeId=nodes.backendNodeId[nodeIdx];const nodeName=nodes.nodeName[nodeIdx];const[backgroundColorIdx,textColorIdx,fillIdx,borderTopWidthIdx,borderTopColorIdx,borderBottomWidthIdx,borderBottomColorIdx,borderLeftWidthIdx,borderLeftColorIdx,borderRightWidthIdx,borderRightColorIdx,fontFamilyIdx,fontSizeIdx,fontWeightIdx,lineHeightIdx,positionIdx,topIdx,rightIdx,bottomIdx,leftIdx,displayIdx,widthIdx,heightIdx,verticalAlignIdx]=styles;storeColor(backgroundColorIdx,nodeId,backgroundColors);storeColor(textColorIdx,nodeId,textColors);if(isSVGNode(strings[nodeName])){storeColor(fillIdx,nodeId,fillColors);}
  6. if(strings[borderTopWidthIdx]!=='0px'){storeColor(borderTopColorIdx,nodeId,borderColors);}
  7. if(strings[borderBottomWidthIdx]!=='0px'){storeColor(borderBottomColorIdx,nodeId,borderColors);}
  8. if(strings[borderLeftWidthIdx]!=='0px'){storeColor(borderLeftColorIdx,nodeId,borderColors);}
  9. if(strings[borderRightWidthIdx]!=='0px'){storeColor(borderRightColorIdx,nodeId,borderColors);}
  10. if(fontFamilyIdx!==-1){const fontFamily=strings[fontFamilyIdx];const fontFamilyInfo=fontInfo.get(fontFamily)||new Map();const sizeLabel='font-size';const weightLabel='font-weight';const lineHeightLabel='line-height';const size=fontFamilyInfo.get(sizeLabel)||new Map();const weight=fontFamilyInfo.get(weightLabel)||new Map();const lineHeight=fontFamilyInfo.get(lineHeightLabel)||new Map();if(fontSizeIdx!==-1){const fontSizeValue=strings[fontSizeIdx];const nodes=size.get(fontSizeValue)||[];nodes.push(nodeId);size.set(fontSizeValue,nodes);}
  11. if(fontWeightIdx!==-1){const fontWeightValue=strings[fontWeightIdx];const nodes=weight.get(fontWeightValue)||[];nodes.push(nodeId);weight.set(fontWeightValue,nodes);}
  12. if(lineHeightIdx!==-1){const lineHeightValue=strings[lineHeightIdx];const nodes=lineHeight.get(lineHeightValue)||[];nodes.push(nodeId);lineHeight.set(lineHeightValue,nodes);}
  13. fontFamilyInfo.set(sizeLabel,size);fontFamilyInfo.set(weightLabel,weight);fontFamilyInfo.set(lineHeightLabel,lineHeight);fontInfo.set(fontFamily,fontFamilyInfo);}
  14. CssOverview.CSSOverviewUnusedDeclarations.checkForUnusedPositionValues(unusedDeclarations,nodeId,strings,positionIdx,topIdx,leftIdx,rightIdx,bottomIdx);if(!isSVGNode(strings[nodeName])&&!isReplacedContent(strings[nodeName])){CssOverview.CSSOverviewUnusedDeclarations.checkForUnusedWidthAndHeightValues(unusedDeclarations,nodeId,strings,displayIdx,widthIdx,heightIdx);}
  15. if(verticalAlignIdx!==-1&&!isTableElementWithDefaultStyles(strings[nodeName],strings[displayIdx])){CssOverview.CSSOverviewUnusedDeclarations.checkForInvalidVerticalAlignment(unusedDeclarations,nodeId,strings,displayIdx,verticalAlignIdx);}}}
  16. return{backgroundColors,textColors,fillColors,borderColors,fontInfo,unusedDeclarations,elementCount};}
  17. getComputedStyleForNode(nodeId){return this._cssAgent.getComputedStyleForNode(nodeId);}
  18. async getMediaQueries(){const queries=await this._cssAgent.getMediaQueries();const queryMap=new Map();if(!queries){return queryMap;}
  19. for(const query of queries){if(query.source==='linkedSheet'){continue;}
  20. const entries=queryMap.get(query.text)||[];entries.push(query);queryMap.set(query.text,entries);}
  21. return queryMap;}
  22. async getGlobalStylesheetStats(){const expression=`(function() {
  23. let styleRules = 0;
  24. let inlineStyles = 0;
  25. let externalSheets = 0;
  26. const stats = {
  27. // Simple.
  28. type: new Set(),
  29. class: new Set(),
  30. id: new Set(),
  31. universal: new Set(),
  32. attribute: new Set(),
  33. // Non-simple.
  34. nonSimple: new Set()
  35. };
  36. for (const styleSheet of document.styleSheets) {
  37. if (styleSheet.href) {
  38. externalSheets++;
  39. } else {
  40. inlineStyles++;
  41. }
  42. // Attempting to grab rules can trigger a DOMException.
  43. // Try it and if it fails skip to the next stylesheet.
  44. let rules;
  45. try {
  46. rules = styleSheet.rules;
  47. } catch (err) {
  48. continue;
  49. }
  50. for (const rule of rules) {
  51. if ('selectorText' in rule) {
  52. styleRules++;
  53. // Each group that was used.
  54. for (const selectorGroup of rule.selectorText.split(',')) {
  55. // Each selector in the group.
  56. for (const selector of selectorGroup.split(\/[\\t\\n\\f\\r ]+\/g)) {
  57. if (selector.startsWith('.')) {
  58. // Class.
  59. stats.class.add(selector);
  60. } else if (selector.startsWith('#')) {
  61. // Id.
  62. stats.id.add(selector);
  63. } else if (selector.startsWith('*')) {
  64. // Universal.
  65. stats.universal.add(selector);
  66. } else if (selector.startsWith('[')) {
  67. // Attribute.
  68. stats.attribute.add(selector);
  69. } else {
  70. // Type or non-simple selector.
  71. const specialChars = \/[#\.:\\[\\]|\\+>~]\/;
  72. if (specialChars.test(selector)) {
  73. stats.nonSimple.add(selector);
  74. } else {
  75. stats.type.add(selector);
  76. }
  77. }
  78. }
  79. }
  80. }
  81. }
  82. }
  83. return {
  84. styleRules,
  85. inlineStyles,
  86. externalSheets,
  87. stats: {
  88. // Simple.
  89. type: stats.type.size,
  90. class: stats.class.size,
  91. id: stats.id.size,
  92. universal: stats.universal.size,
  93. attribute: stats.attribute.size,
  94. // Non-simple.
  95. nonSimple: stats.nonSimple.size
  96. }
  97. }
  98. })()`;const{result}=await this._runtimeAgent.invoke_evaluate({expression,returnByValue:true});if(result.type!=='object'){return;}
  99. return result.value;}}
  100. SDK.SDKModel.register(CSSOverviewModel,SDK.Target.Capability.DOM,false);self.CssOverview=self.CssOverview||{};CssOverview=CssOverview||{};CssOverview.CSSOverviewModel=CSSOverviewModel;