ComputedStyleWidget.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839
  1. export default class ComputedStyleWidget extends UI.ThrottledWidget{constructor(){super(true);this.registerRequiredCSS('elements/computedStyleSidebarPane.css');this._alwaysShowComputedProperties={'display':true,'height':true,'width':true};this._computedStyleModel=new Elements.ComputedStyleModel();this._computedStyleModel.addEventListener(Elements.ComputedStyleModel.Events.ComputedStyleChanged,this.update,this);this._showInheritedComputedStylePropertiesSetting=Common.settings.createSetting('showInheritedComputedStyleProperties',false);this._showInheritedComputedStylePropertiesSetting.addChangeListener(this._showInheritedComputedStyleChanged.bind(this));const hbox=this.contentElement.createChild('div','hbox styles-sidebar-pane-toolbar');const filterContainerElement=hbox.createChild('div','styles-sidebar-pane-filter-box');const filterInput=Elements.StylesSidebarPane.createPropertyFilterElement(ls`Filter`,hbox,filterCallback.bind(this));UI.ARIAUtils.setAccessibleName(filterInput,Common.UIString('Filter Computed Styles'));filterContainerElement.appendChild(filterInput);this.setDefaultFocusedElement(filterInput);const toolbar=new UI.Toolbar('styles-pane-toolbar',hbox);toolbar.appendToolbarItem(new UI.ToolbarSettingCheckbox(this._showInheritedComputedStylePropertiesSetting,undefined,Common.UIString('Show all')));this._noMatchesElement=this.contentElement.createChild('div','gray-info-message');this._noMatchesElement.textContent=ls`No matching property`;this._propertiesOutline=new UI.TreeOutlineInShadow();this._propertiesOutline.hideOverflow();this._propertiesOutline.setShowSelectionOnKeyboardFocus(true);this._propertiesOutline.setFocusable(true);this._propertiesOutline.registerRequiredCSS('elements/computedStyleWidgetTree.css');this._propertiesOutline.element.classList.add('monospace','computed-properties');this.contentElement.appendChild(this._propertiesOutline.element);this._linkifier=new Components.Linkifier(_maxLinkLength);function filterCallback(regex){this._filterRegex=regex;this._updateFilter(regex);}
  2. const fontsWidget=new Elements.PlatformFontsWidget(this._computedStyleModel);fontsWidget.show(this.contentElement);}
  3. onResize(){const isNarrow=this.contentElement.offsetWidth<260;this._propertiesOutline.contentElement.classList.toggle('computed-narrow',isNarrow);}
  4. _showInheritedComputedStyleChanged(){this.update();}
  5. doUpdate(){const promises=[this._computedStyleModel.fetchComputedStyle(),this._fetchMatchedCascade()];return Promise.all(promises).spread(this._innerRebuildUpdate.bind(this));}
  6. _fetchMatchedCascade(){const node=this._computedStyleModel.node();if(!node||!this._computedStyleModel.cssModel()){return Promise.resolve((null));}
  7. return this._computedStyleModel.cssModel().cachedMatchedCascadeForNode(node).then(validateStyles.bind(this));function validateStyles(matchedStyles){return matchedStyles&&matchedStyles.node()===this._computedStyleModel.node()?matchedStyles:null;}}
  8. _processColor(text){const color=Common.Color.parse(text);if(!color){return createTextNode(text);}
  9. const swatch=InlineEditor.ColorSwatch.create();swatch.setColor(color);swatch.setFormat(Common.Color.detectColorFormat(color));return swatch;}
  10. _innerRebuildUpdate(nodeStyle,matchedStyles){const expandedProperties=new Set();for(const treeElement of this._propertiesOutline.rootElement().children()){if(!treeElement.expanded){continue;}
  11. const propertyName=treeElement[_propertySymbol].name;expandedProperties.add(propertyName);}
  12. const hadFocus=this._propertiesOutline.element.hasFocus();this._propertiesOutline.removeChildren();this._linkifier.reset();const cssModel=this._computedStyleModel.cssModel();if(!nodeStyle||!matchedStyles||!cssModel){this._noMatchesElement.classList.remove('hidden');return;}
  13. const uniqueProperties=nodeStyle.computedStyle.keysArray();uniqueProperties.sort(propertySorter);const propertyTraces=this._computePropertyTraces(matchedStyles);const inhertiedProperties=this._computeInheritedProperties(matchedStyles);const showInherited=this._showInheritedComputedStylePropertiesSetting.get();for(let i=0;i<uniqueProperties.length;++i){const propertyName=uniqueProperties[i];const propertyValue=nodeStyle.computedStyle.get(propertyName);const canonicalName=SDK.cssMetadata().canonicalPropertyName(propertyName);const inherited=!inhertiedProperties.has(canonicalName);if(!showInherited&&inherited&&!(propertyName in this._alwaysShowComputedProperties)){continue;}
  14. if(!showInherited&&propertyName.startsWith('--')){continue;}
  15. if(propertyName!==canonicalName&&propertyValue===nodeStyle.computedStyle.get(canonicalName)){continue;}
  16. const propertyElement=createElement('div');propertyElement.classList.add('computed-style-property');propertyElement.classList.toggle('computed-style-property-inherited',inherited);const renderer=new Elements.StylesSidebarPropertyRenderer(null,nodeStyle.node,propertyName,(propertyValue));renderer.setColorHandler(this._processColor.bind(this));const propertyNameElement=renderer.renderName();propertyNameElement.classList.add('property-name');propertyElement.appendChild(propertyNameElement);const colon=createElementWithClass('span','delimeter');colon.textContent=': ';propertyNameElement.appendChild(colon);const propertyValueElement=propertyElement.createChild('span','property-value');const propertyValueText=renderer.renderValue();propertyValueText.classList.add('property-value-text');propertyValueElement.appendChild(propertyValueText);const semicolon=createElementWithClass('span','delimeter');semicolon.textContent=';';propertyValueElement.appendChild(semicolon);const treeElement=new UI.TreeElement();treeElement.title=propertyElement;treeElement[_propertySymbol]={name:propertyName,value:propertyValue};const isOdd=this._propertiesOutline.rootElement().children().length%2===0;treeElement.listItemElement.classList.toggle('odd-row',isOdd);this._propertiesOutline.appendChild(treeElement);if(!this._propertiesOutline.selectedTreeElement){treeElement.select(!hadFocus);}
  17. const trace=propertyTraces.get(propertyName);if(trace){const activeProperty=this._renderPropertyTrace(cssModel,matchedStyles,nodeStyle.node,treeElement,trace);treeElement.listItemElement.addEventListener('mousedown',e=>e.consume(),false);treeElement.listItemElement.addEventListener('dblclick',e=>e.consume(),false);treeElement.listItemElement.addEventListener('click',handleClick.bind(null,treeElement),false);treeElement.listItemElement.addEventListener('contextmenu',this._handleContextMenuEvent.bind(this,matchedStyles,activeProperty));const gotoSourceElement=UI.Icon.create('mediumicon-arrow-in-circle','goto-source-icon');gotoSourceElement.addEventListener('click',this._navigateToSource.bind(this,activeProperty));propertyValueElement.appendChild(gotoSourceElement);if(expandedProperties.has(propertyName)){treeElement.expand();}}}
  18. this._updateFilter(this._filterRegex);function propertySorter(a,b){if(a.startsWith('--')^b.startsWith('--')){return a.startsWith('--')?1:-1;}
  19. if(a.startsWith('-webkit')^b.startsWith('-webkit')){return a.startsWith('-webkit')?1:-1;}
  20. const canonical1=SDK.cssMetadata().canonicalPropertyName(a);const canonical2=SDK.cssMetadata().canonicalPropertyName(b);return canonical1.compareTo(canonical2);}
  21. function handleClick(treeElement,event){if(!treeElement.expanded){treeElement.expand();}else{treeElement.collapse();}
  22. event.consume();}}
  23. _navigateToSource(cssProperty,event){Common.Revealer.reveal(cssProperty);event.consume(true);}
  24. _renderPropertyTrace(cssModel,matchedStyles,node,rootTreeElement,tracedProperties){let activeProperty=null;for(const property of tracedProperties){const trace=createElement('div');trace.classList.add('property-trace');if(matchedStyles.propertyState(property)===SDK.CSSMatchedStyles.PropertyState.Overloaded){trace.classList.add('property-trace-inactive');}else{activeProperty=property;}
  25. const renderer=new Elements.StylesSidebarPropertyRenderer(null,node,property.name,(property.value));renderer.setColorHandler(this._processColor.bind(this));const valueElement=renderer.renderValue();valueElement.classList.add('property-trace-value');valueElement.addEventListener('click',this._navigateToSource.bind(this,property),false);const gotoSourceElement=UI.Icon.create('mediumicon-arrow-in-circle','goto-source-icon');gotoSourceElement.addEventListener('click',this._navigateToSource.bind(this,property));valueElement.insertBefore(gotoSourceElement,valueElement.firstChild);trace.appendChild(valueElement);const rule=property.ownerStyle.parentRule;const selectorElement=trace.createChild('span','property-trace-selector');selectorElement.textContent=rule?rule.selectorText():'element.style';selectorElement.title=selectorElement.textContent;if(rule){const linkSpan=trace.createChild('span','trace-link');linkSpan.appendChild(Elements.StylePropertiesSection.createRuleOriginNode(matchedStyles,this._linkifier,rule));}
  26. const traceTreeElement=new UI.TreeElement();traceTreeElement.title=trace;traceTreeElement.listItemElement.addEventListener('contextmenu',this._handleContextMenuEvent.bind(this,matchedStyles,property));rootTreeElement.appendChild(traceTreeElement);}
  27. return(activeProperty);}
  28. _handleContextMenuEvent(matchedStyles,property,event){const contextMenu=new UI.ContextMenu(event);const rule=property.ownerStyle.parentRule;if(rule){const header=rule.styleSheetId?matchedStyles.cssModel().styleSheetHeaderForId(rule.styleSheetId):null;if(header&&!header.isAnonymousInlineStyleSheet()){contextMenu.defaultSection().appendItem(ls`Navigate to selector source`,()=>{Elements.StylePropertiesSection.tryNavigateToRuleLocation(matchedStyles,rule);});}}
  29. contextMenu.defaultSection().appendItem(ls`Navigate to style`,()=>Common.Revealer.reveal(property));contextMenu.show();}
  30. _computePropertyTraces(matchedStyles){const result=new Map();for(const style of matchedStyles.nodeStyles()){const allProperties=style.allProperties();for(const property of allProperties){if(!property.activeInStyle()||!matchedStyles.propertyState(property)){continue;}
  31. if(!result.has(property.name)){result.set(property.name,[]);}
  32. result.get(property.name).push(property);}}
  33. return result;}
  34. _computeInheritedProperties(matchedStyles){const result=new Set();for(const style of matchedStyles.nodeStyles()){for(const property of style.allProperties()){if(!matchedStyles.propertyState(property)){continue;}
  35. result.add(SDK.cssMetadata().canonicalPropertyName(property.name));}}
  36. return result;}
  37. _updateFilter(regex){const children=this._propertiesOutline.rootElement().children();let hasMatch=false;for(const child of children){const property=child[_propertySymbol];const matched=!regex||regex.test(property.name)||regex.test(property.value);child.hidden=!matched;hasMatch|=matched;}
  38. this._noMatchesElement.classList.toggle('hidden',!!hasMatch);}}
  39. export const _maxLinkLength=30;export const _propertySymbol=Symbol('property');self.Elements=self.Elements||{};Elements=Elements||{};Elements.ComputedStyleWidget=ComputedStyleWidget;Elements.ComputedStyleWidget._maxLinkLength=_maxLinkLength;Elements.ComputedStyleWidget._propertySymbol=_propertySymbol;