/* * Copyright (C) 2013, 2015 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. */ WebInspector.RulesStyleDetailsPanel = class RulesStyleDetailsPanel extends WebInspector.StyleDetailsPanel { constructor(delegate) { super(delegate, "rules", "rules", WebInspector.UIString("Styles \u2014 Rules")); this._sections = []; this._previousFocusedSection = null; this._ruleMediaAndInherticanceList = []; this._propertyToSelectAndHighlight = null; this._emptyFilterResultsElement = document.createElement("div"); this._emptyFilterResultsElement.classList.add("no-filter-results"); this._emptyFilterResultsMessage = document.createElement("div"); this._emptyFilterResultsMessage.classList.add("no-filter-results-message"); this._emptyFilterResultsMessage.textContent = WebInspector.UIString("No Results Found"); this._emptyFilterResultsElement.appendChild(this._emptyFilterResultsMessage); this._boundRemoveSectionWithActiveEditor = this._removeSectionWithActiveEditor.bind(this); } // Public refresh(significantChange) { // We only need to do a rebuild on significant changes. Other changes are handled // by the sections and text editors themselves. if (!significantChange) { super.refresh(); return; } if (!this._forceSignificantChange) { this._sectionWithActiveEditor = null; for (var section of this._sections) { if (!section.editorActive) continue; this._sectionWithActiveEditor = section; break; } if (this._sectionWithActiveEditor) { this._sectionWithActiveEditor.addEventListener(WebInspector.CSSStyleDeclarationSection.Event.Blurred, this._boundRemoveSectionWithActiveEditor); return; } } var newSections = []; var newDOMFragment = document.createDocumentFragment(); var previousMediaList = []; var previousSection = null; var pseudoElements = this.nodeStyles.pseudoElements; var pseudoElementsStyle = []; for (var pseudoIdentifier in pseudoElements) pseudoElementsStyle = pseudoElementsStyle.concat(pseudoElements[pseudoIdentifier].orderedStyles); var orderedPseudoStyles = uniqueOrderedStyles(pseudoElementsStyle); // Reverse the array to allow ensure that splicing the array will not mess with the order. if (orderedPseudoStyles.length) orderedPseudoStyles.reverse(); function mediaListsEqual(a, b) { a = a || []; b = b || []; if (a.length !== b.length) return false; for (var i = 0; i < a.length; ++i) { var aMedia = a[i]; var bMedia = b[i]; if (aMedia.type !== bMedia.type) return false; if (aMedia.text !== bMedia.text) return false; if (!aMedia.sourceCodeLocation && bMedia.sourceCodeLocation) return false; if (aMedia.sourceCodeLocation && !aMedia.sourceCodeLocation.isEqual(bMedia.sourceCodeLocation)) return false; } return true; } function uniqueOrderedStyles(orderedStyles) { var uniqueStyles = []; for (var style of orderedStyles) { var rule = style.ownerRule; if (!rule) { uniqueStyles.push(style); continue; } var found = false; for (var existingStyle of uniqueStyles) { if (rule.isEqualTo(existingStyle.ownerRule)) { found = true; break; } } if (!found) uniqueStyles.push(style); } return uniqueStyles; } function appendStyleSection(style) { var section = style.__rulesSection; if (section && section.focused && !this._previousFocusedSection) this._previousFocusedSection = section; if (!section) { section = new WebInspector.CSSStyleDeclarationSection(this, style); style.__rulesSection = section; } else section.refresh(); if (style.isInspectorRule() && this._newInspectorRuleSelector === style.selectorText && !style.hasProperties()) { this._previousFocusedSection = section; this._newInspectorRuleSelector = null; } // Reset lastInGroup in case the order/grouping changed. section.lastInGroup = false; newDOMFragment.appendChild(section.element); newSections.push(section); previousSection = section; } function insertMediaOrInheritanceLabel(style) { if (previousSection && previousSection.style.type === WebInspector.CSSStyleDeclaration.Type.Inline) previousSection.lastInGroup = true; var hasMediaOrInherited = []; if (previousSection && previousSection.style.node !== style.node) { previousSection.lastInGroup = true; var prefixElement = document.createElement("strong"); prefixElement.textContent = WebInspector.UIString("Inherited From: "); var inheritedLabel = document.createElement("div"); inheritedLabel.className = "label"; inheritedLabel.appendChild(prefixElement); inheritedLabel.appendChild(WebInspector.linkifyNodeReference(style.node)); newDOMFragment.appendChild(inheritedLabel); hasMediaOrInherited.push(inheritedLabel); } // Only include the media list if it is different from the previous media list shown. var currentMediaList = (style.ownerRule && style.ownerRule.mediaList) || []; if (!mediaListsEqual(previousMediaList, currentMediaList)) { previousMediaList = currentMediaList; // Break the section group even if the media list is empty. That way the user knows // the previous displayed media list does not apply to the next section. if (previousSection) previousSection.lastInGroup = true; for (var media of currentMediaList) { var prefixElement = document.createElement("strong"); prefixElement.textContent = WebInspector.UIString("Media: "); var mediaLabel = document.createElement("div"); mediaLabel.className = "label"; mediaLabel.append(prefixElement, media.text); if (media.sourceCodeLocation) mediaLabel.append(" \u2014 ", WebInspector.createSourceCodeLocationLink(media.sourceCodeLocation, true)); newDOMFragment.appendChild(mediaLabel); hasMediaOrInherited.push(mediaLabel); } } if (!hasMediaOrInherited.length && style.type !== WebInspector.CSSStyleDeclaration.Type.Inline) { if (previousSection && !previousSection.lastInGroup) hasMediaOrInherited = this._ruleMediaAndInherticanceList.lastValue; else { var prefixElement = document.createElement("strong"); prefixElement.textContent = WebInspector.UIString("Media: "); var mediaLabel = document.createElement("div"); mediaLabel.className = "label"; mediaLabel.append(prefixElement, "all"); newDOMFragment.appendChild(mediaLabel); hasMediaOrInherited.push(mediaLabel); } } this._ruleMediaAndInherticanceList.push(hasMediaOrInherited); } function insertAllMatchingPseudoStyles(force) { if (!orderedPseudoStyles.length) return; if (force) { for (var j = orderedPseudoStyles.length - 1; j >= 0; --j) { var pseudoStyle = orderedPseudoStyles[j]; insertMediaOrInheritanceLabel.call(this, pseudoStyle); appendStyleSection.call(this, pseudoStyle); } orderedPseudoStyles = []; } if (!previousSection) return; var ownerRule = previousSection.style.ownerRule; if (!ownerRule) return; for (var j = orderedPseudoStyles.length - 1; j >= 0; --j) { var pseudoStyle = orderedPseudoStyles[j]; if (!pseudoStyle.ownerRule.selectorIsGreater(ownerRule.mostSpecificSelector)) continue; insertMediaOrInheritanceLabel.call(this, pseudoStyle); appendStyleSection.call(this, pseudoStyle); ownerRule = pseudoStyle.ownerRule; orderedPseudoStyles.splice(j, 1); } } this._ruleMediaAndInherticanceList = []; var orderedStyles = uniqueOrderedStyles(this.nodeStyles.orderedStyles); for (var style of orderedStyles) { var isUserAgentStyle = style.ownerRule && style.ownerRule.type === WebInspector.CSSStyleSheet.Type.UserAgent; insertAllMatchingPseudoStyles.call(this, isUserAgentStyle || style.inherited); insertMediaOrInheritanceLabel.call(this, style); appendStyleSection.call(this, style); } // Just in case there are any pseudo-selectors left that haven't been added. insertAllMatchingPseudoStyles.call(this, true); if (previousSection) previousSection.lastInGroup = true; this.element.removeChildren(); this.element.appendChild(newDOMFragment); this.element.appendChild(this._emptyFilterResultsElement); this._sections = newSections; for (var i = 0; i < this._sections.length; ++i) this._sections[i].updateLayout(); super.refresh(); } scrollToSectionAndHighlightProperty(property) { if (!this._visible) { this._propertyToSelectAndHighlight = property; return false; } for (var section of this._sections) { if (section.highlightProperty(property)) return true; } return false; } cssStyleDeclarationSectionEditorFocused(focusedSection) { for (let section of this._sections) { if (section !== focusedSection) section.clearSelection(); } this._previousFocusedSection = focusedSection; } cssStyleDeclarationSectionBlurActiveEditor() { this._previousFocusedSection = null; } cssStyleDeclarationSectionEditorNextRule(currentSection) { currentSection.clearSelection(); var index = this._sections.indexOf(currentSection); this._sections[index < this._sections.length - 1 ? index + 1 : 0].focusRuleSelector(); } cssStyleDeclarationSectionEditorPreviousRule(currentSection, selectLastProperty) { currentSection.clearSelection(); if (selectLastProperty || currentSection.selectorLocked) { var index = this._sections.indexOf(currentSection); index = index > 0 ? index - 1 : this._sections.length - 1; var section = this._sections[index]; while (section.locked) { index = index > 0 ? index - 1 : this._sections.length - 1; section = this._sections[index]; } section.focus(); section.selectLastProperty(); return; } currentSection.focusRuleSelector(true); } filterDidChange(filterBar) { for (var labels of this._ruleMediaAndInherticanceList) { for (var i = 0; i < labels.length; ++i) { labels[i].classList.toggle(WebInspector.CSSStyleDetailsSidebarPanel.NoFilterMatchInSectionClassName, filterBar.hasActiveFilters()); if (i === labels.length - 1) labels[i].classList.toggle("filter-matching-label", filterBar.hasActiveFilters()); } } var matchFound = !filterBar.hasActiveFilters(); for (var i = 0; i < this._sections.length; ++i) { var section = this._sections[i]; if (section.findMatchingPropertiesAndSelectors(filterBar.filters.text) && filterBar.hasActiveFilters()) { if (this._ruleMediaAndInherticanceList[i].length) { for (var label of this._ruleMediaAndInherticanceList[i]) label.classList.remove(WebInspector.CSSStyleDetailsSidebarPanel.NoFilterMatchInSectionClassName); } else section.element.classList.add(WebInspector.CSSStyleDetailsSidebarPanel.FilterMatchingSectionHasLabelClassName); matchFound = true; } } this.element.classList.toggle("filter-non-matching", !matchFound); } cssStyleDeclarationSectionFocusNewInspectorRuleWithSelector(selector) { this._newInspectorRuleSelector = selector; } newRuleButtonClicked() { if (this.nodeStyles.node.isInShadowTree()) return; for (let existingRule of this.nodeStyles.rulesForSelector()) { if (this.focusEmptySectionWithStyle(existingRule.style)) return; } this._newInspectorRuleSelector = this.nodeStyles.node.appropriateSelectorFor(true); this.nodeStyles.addRule(this._newInspectorRuleSelector); } sectionForStyle(style) { if (style.__rulesSection) return style.__rulesSection; for (let section of this._sections) { if (section.style === style) return section; } return null; } focusEmptySectionWithStyle(style) { if (style.hasProperties()) return false; let section = this.sectionForStyle(style); if (!section) return false; section.focus(); return true; } // Protected shown() { super.shown(); // Associate the style and section objects so they can be reused. // Also update the layout in case we changed widths while hidden. for (var i = 0; i < this._sections.length; ++i) { var section = this._sections[i]; section.style.__rulesSection = section; section.updateLayout(); } } hidden() { super.hidden(); // Disconnect the style and section objects so they have a chance // to release their objects when this panel is not visible. for (var i = 0; i < this._sections.length; ++i) delete this._sections[i].style.__rulesSection; } widthDidChange() { for (var i = 0; i < this._sections.length; ++i) this._sections[i].updateLayout(); } nodeStylesRefreshed(event) { super.nodeStylesRefreshed(event); if (this._propertyToSelectAndHighlight) { this.scrollToSectionAndHighlightProperty(this._propertyToSelectAndHighlight); this._propertyToSelectAndHighlight = null; } if (this._previousFocusedSection && this._visible) this._previousFocusedSection.focus(); } // Private _removeSectionWithActiveEditor(event) { this._sectionWithActiveEditor.removeEventListener(WebInspector.CSSStyleDeclarationSection.Event.Blurred, this._boundRemoveSectionWithActiveEditor); this.refresh(true); } };