UNPKG

node-inspector-sans-ws

Version:
1,243 lines (1,063 loc) 110 kB
/* * Copyright (C) 2007 Apple Inc. All rights reserved. * Copyright (C) 2009 Joseph Pecoraro * * 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. * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY APPLE 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 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. */ /** * @constructor * @extends {WebInspector.SidebarPane} * @param {WebInspector.ComputedStyleSidebarPane} computedStylePane * @param {function(DOMAgent.NodeId, string, boolean)} setPseudoClassCallback */ WebInspector.StylesSidebarPane = function(computedStylePane, setPseudoClassCallback) { WebInspector.SidebarPane.call(this, WebInspector.UIString("Styles")); this.settingsSelectElement = document.createElement("select"); this.settingsSelectElement.className = "select-settings"; var option = document.createElement("option"); option.value = WebInspector.Color.Format.Original; option.label = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "As authored" : "As Authored"); this.settingsSelectElement.appendChild(option); option = document.createElement("option"); option.value = WebInspector.Color.Format.HEX; option.label = WebInspector.UIString("Hex Colors"); this.settingsSelectElement.appendChild(option); option = document.createElement("option"); option.value = WebInspector.Color.Format.RGB; option.label = WebInspector.UIString("RGB Colors"); this.settingsSelectElement.appendChild(option); option = document.createElement("option"); option.value = WebInspector.Color.Format.HSL; option.label = WebInspector.UIString("HSL Colors"); this.settingsSelectElement.appendChild(option); // Prevent section from collapsing. var muteEventListener = function(event) { event.consume(true); }; this.settingsSelectElement.addEventListener("click", muteEventListener, true); this.settingsSelectElement.addEventListener("change", this._changeSetting.bind(this), false); this._updateColorFormatFilter(); this.titleElement.appendChild(this.settingsSelectElement); this._elementStateButton = document.createElement("button"); this._elementStateButton.className = "pane-title-button element-state"; this._elementStateButton.title = WebInspector.UIString("Toggle Element State"); this._elementStateButton.addEventListener("click", this._toggleElementStatePane.bind(this), false); this.titleElement.appendChild(this._elementStateButton); var addButton = document.createElement("button"); addButton.className = "pane-title-button add"; addButton.id = "add-style-button-test-id"; addButton.title = WebInspector.UIString("New Style Rule"); addButton.addEventListener("click", this._createNewRule.bind(this), false); this.titleElement.appendChild(addButton); this._computedStylePane = computedStylePane; computedStylePane._stylesSidebarPane = this; this._setPseudoClassCallback = setPseudoClassCallback; this.element.addEventListener("contextmenu", this._contextMenuEventFired.bind(this), true); WebInspector.settings.colorFormat.addChangeListener(this._colorFormatSettingChanged.bind(this)); this._createElementStatePane(); this.bodyElement.appendChild(this._elementStatePane); this._sectionsContainer = document.createElement("div"); this.bodyElement.appendChild(this._sectionsContainer); this._spectrumHelper = new WebInspector.SpectrumPopupHelper(); this._linkifier = new WebInspector.Linkifier(new WebInspector.Linkifier.DefaultCSSFormatter()); WebInspector.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetAdded, this._styleSheetOrMediaQueryResultChanged, this); WebInspector.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetRemoved, this._styleSheetOrMediaQueryResultChanged, this); WebInspector.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetChanged, this._styleSheetOrMediaQueryResultChanged, this); WebInspector.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.MediaQueryResultChanged, this._styleSheetOrMediaQueryResultChanged, this); WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.AttrModified, this._attributeChanged, this); WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.AttrRemoved, this._attributeChanged, this); WebInspector.settings.showUserAgentStyles.addChangeListener(this._showUserAgentStylesSettingChanged.bind(this)); this.element.addEventListener("mousemove", this._mouseMovedOverElement.bind(this), false); document.body.addEventListener("keydown", this._keyDown.bind(this), false); document.body.addEventListener("keyup", this._keyUp.bind(this), false); } // Keep in sync with RenderStyleConstants.h PseudoId enum. Array below contains pseudo id names for corresponding enum indexes. // First item is empty due to its artificial NOPSEUDO nature in the enum. // FIXME: find a way of generating this mapping or getting it from combination of RenderStyleConstants and CSSSelector.cpp at // runtime. WebInspector.StylesSidebarPane.PseudoIdNames = [ "", "first-line", "first-letter", "before", "after", "selection", "", "-webkit-scrollbar", "-webkit-file-upload-button", "-webkit-input-placeholder", "-webkit-slider-thumb", "-webkit-search-cancel-button", "-webkit-search-decoration", "-webkit-search-results-decoration", "-webkit-search-results-button", "-webkit-media-controls-panel", "-webkit-media-controls-play-button", "-webkit-media-controls-mute-button", "-webkit-media-controls-timeline", "-webkit-media-controls-timeline-container", "-webkit-media-controls-volume-slider", "-webkit-media-controls-volume-slider-container", "-webkit-media-controls-current-time-display", "-webkit-media-controls-time-remaining-display", "-webkit-media-controls-seek-back-button", "-webkit-media-controls-seek-forward-button", "-webkit-media-controls-fullscreen-button", "-webkit-media-controls-rewind-button", "-webkit-media-controls-return-to-realtime-button", "-webkit-media-controls-toggle-closed-captions-button", "-webkit-media-controls-status-display", "-webkit-scrollbar-thumb", "-webkit-scrollbar-button", "-webkit-scrollbar-track", "-webkit-scrollbar-track-piece", "-webkit-scrollbar-corner", "-webkit-resizer", "-webkit-inner-spin-button", "-webkit-outer-spin-button" ]; WebInspector.StylesSidebarPane._colorRegex = /((?:rgb|hsl)a?\([^)]+\)|#[0-9a-fA-F]{6}|#[0-9a-fA-F]{3}|\b\w+\b(?!-))/g; /** * @param {WebInspector.CSSProperty} property */ WebInspector.StylesSidebarPane.createExclamationMark = function(property) { var exclamationElement = document.createElement("div"); exclamationElement.className = "exclamation-mark" + (WebInspector.StylesSidebarPane._ignoreErrorsForProperty(property) ? "" : " warning-icon-small"); exclamationElement.title = WebInspector.CSSMetadata.cssPropertiesMetainfo.keySet()[property.name.toLowerCase()] ? WebInspector.UIString("Invalid property value.") : WebInspector.UIString("Unknown property name."); return exclamationElement; } /** * @param {WebInspector.Color} color */ WebInspector.StylesSidebarPane._colorFormat = function(color) { const cf = WebInspector.Color.Format; var format; var formatSetting = WebInspector.settings.colorFormat.get(); if (formatSetting === cf.Original) format = cf.Original; else if (formatSetting === cf.RGB) format = (color.hasAlpha() ? cf.RGBA : cf.RGB); else if (formatSetting === cf.HSL) format = (color.hasAlpha() ? cf.HSLA : cf.HSL); else if (!color.hasAlpha()) format = (color.canBeShortHex() ? cf.ShortHEX : cf.HEX); else format = cf.RGBA; return format; } /** * @param {WebInspector.CSSProperty} property */ WebInspector.StylesSidebarPane._ignoreErrorsForProperty = function(property) { function hasUnknownVendorPrefix(string) { return !string.startsWith("-webkit-") && /^[-_][\w\d]+-\w/.test(string); } var name = property.name.toLowerCase(); // IE hack. if (name.charAt(0) === "_") return true; // IE has a different format for this. if (name === "filter") return true; // Common IE-specific property prefix. if (name.startsWith("scrollbar-")) return true; if (hasUnknownVendorPrefix(name)) return true; var value = property.value.toLowerCase(); // IE hack. if (value.endsWith("\9")) return true; if (hasUnknownVendorPrefix(value)) return true; return false; } WebInspector.StylesSidebarPane.prototype = { /** * @param {Event} event */ _contextMenuEventFired: function(event) { // We start editing upon click -> default navigation to resources panel is not available // Hence we add a soft context menu for hrefs. var contextMenu = new WebInspector.ContextMenu(event); contextMenu.appendApplicableItems(event.target); contextMenu.show(); }, get _forcedPseudoClasses() { return this.node ? (this.node.getUserProperty("pseudoState") || undefined) : undefined; }, _updateForcedPseudoStateInputs: function() { if (!this.node) return; var nodePseudoState = this._forcedPseudoClasses; if (!nodePseudoState) nodePseudoState = []; var inputs = this._elementStatePane.inputs; for (var i = 0; i < inputs.length; ++i) inputs[i].checked = nodePseudoState.indexOf(inputs[i].state) >= 0; }, /** * @param {WebInspector.DOMNode=} node * @param {boolean=} forceUpdate */ update: function(node, forceUpdate) { this._spectrumHelper.hide(); this._discardElementUnderMouse(); var refresh = false; if (forceUpdate) delete this.node; if (!forceUpdate && (node === this.node)) refresh = true; if (node && node.nodeType() === Node.TEXT_NODE && node.parentNode) node = node.parentNode; if (node && node.nodeType() !== Node.ELEMENT_NODE) node = null; if (node) this.node = node; else node = this.node; this._updateForcedPseudoStateInputs(); if (refresh) this._refreshUpdate(); else this._rebuildUpdate(); }, /** * @param {WebInspector.StylePropertiesSection=} editedSection * @param {boolean=} forceFetchComputedStyle * @param {function()=} userCallback */ _refreshUpdate: function(editedSection, forceFetchComputedStyle, userCallback) { if (this._refreshUpdateInProgress) { this._lastNodeForInnerRefresh = this.node; return; } var node = this._validateNode(userCallback); if (!node) return; function computedStyleCallback(computedStyle) { delete this._refreshUpdateInProgress; if (this._lastNodeForInnerRefresh) { delete this._lastNodeForInnerRefresh; this._refreshUpdate(editedSection, forceFetchComputedStyle, userCallback); return; } if (this.node === node && computedStyle) this._innerRefreshUpdate(node, computedStyle, editedSection); if (userCallback) userCallback(); } if (this._computedStylePane.isShowing() || forceFetchComputedStyle) { this._refreshUpdateInProgress = true; WebInspector.cssModel.getComputedStyleAsync(node.id, computedStyleCallback.bind(this)); } else { this._innerRefreshUpdate(node, null, editedSection); if (userCallback) userCallback(); } }, _rebuildUpdate: function() { if (this._rebuildUpdateInProgress) { this._lastNodeForInnerRebuild = this.node; return; } var node = this._validateNode(); if (!node) return; this._rebuildUpdateInProgress = true; var resultStyles = {}; function stylesCallback(matchedResult) { delete this._rebuildUpdateInProgress; var lastNodeForRebuild = this._lastNodeForInnerRebuild; if (lastNodeForRebuild) { delete this._lastNodeForInnerRebuild; if (lastNodeForRebuild !== this.node) { this._rebuildUpdate(); return; } } if (matchedResult && this.node === node) { resultStyles.matchedCSSRules = matchedResult.matchedCSSRules; resultStyles.pseudoElements = matchedResult.pseudoElements; resultStyles.inherited = matchedResult.inherited; this._innerRebuildUpdate(node, resultStyles); } if (lastNodeForRebuild) { // lastNodeForRebuild is the same as this.node - another rebuild has been requested. this._rebuildUpdate(); return; } } function inlineCallback(inlineStyle, attributesStyle) { resultStyles.inlineStyle = inlineStyle; resultStyles.attributesStyle = attributesStyle; } function computedCallback(computedStyle) { resultStyles.computedStyle = computedStyle; } if (this._computedStylePane.isShowing()) WebInspector.cssModel.getComputedStyleAsync(node.id, computedCallback.bind(this)); WebInspector.cssModel.getInlineStylesAsync(node.id, inlineCallback.bind(this)); WebInspector.cssModel.getMatchedStylesAsync(node.id, true, true, stylesCallback.bind(this)); }, /** * @param {function()=} userCallback */ _validateNode: function(userCallback) { if (!this.node) { this._sectionsContainer.removeChildren(); this._computedStylePane.bodyElement.removeChildren(); this.sections = {}; if (userCallback) userCallback(); return null; } return this.node; }, _styleSheetOrMediaQueryResultChanged: function() { if (this._userOperation || this._isEditingStyle) return; this._rebuildUpdate(); }, _attributeChanged: function(event) { // Any attribute removal or modification can affect the styles of "related" nodes. // Do not touch the styles if they are being edited. if (this._isEditingStyle || this._userOperation) return; if (!this._canAffectCurrentStyles(event.data.node)) return; this._rebuildUpdate(); }, _canAffectCurrentStyles: function(node) { return this.node && (this.node === node || node.parentNode === this.node.parentNode || node.isAncestor(this.node)); }, _innerRefreshUpdate: function(node, computedStyle, editedSection) { for (var pseudoId in this.sections) { var styleRules = this._refreshStyleRules(this.sections[pseudoId], computedStyle); var usedProperties = {}; this._markUsedProperties(styleRules, usedProperties); this._refreshSectionsForStyleRules(styleRules, usedProperties, editedSection); } if (computedStyle) this.sections[0][0].rebuildComputedTrace(this.sections[0]); this._nodeStylesUpdatedForTest(node, false); }, _innerRebuildUpdate: function(node, styles) { this._sectionsContainer.removeChildren(); this._computedStylePane.bodyElement.removeChildren(); this._linkifier.reset(); var styleRules = this._rebuildStyleRules(node, styles); var usedProperties = {}; this._markUsedProperties(styleRules, usedProperties); this.sections[0] = this._rebuildSectionsForStyleRules(styleRules, usedProperties, 0, null); var anchorElement = this.sections[0].inheritedPropertiesSeparatorElement; if (styles.computedStyle) this.sections[0][0].rebuildComputedTrace(this.sections[0]); for (var i = 0; i < styles.pseudoElements.length; ++i) { var pseudoElementCSSRules = styles.pseudoElements[i]; styleRules = []; var pseudoId = pseudoElementCSSRules.pseudoId; var entry = { isStyleSeparator: true, pseudoId: pseudoId }; styleRules.push(entry); // Add rules in reverse order to match the cascade order. for (var j = pseudoElementCSSRules.rules.length - 1; j >= 0; --j) { var rule = pseudoElementCSSRules.rules[j]; styleRules.push({ style: rule.style, selectorText: rule.selectorText, media: rule.media, sourceURL: rule.resourceURL(), rule: rule, editable: !!(rule.style && rule.style.id) }); } usedProperties = {}; this._markUsedProperties(styleRules, usedProperties); this.sections[pseudoId] = this._rebuildSectionsForStyleRules(styleRules, usedProperties, pseudoId, anchorElement); } this._nodeStylesUpdatedForTest(node, true); }, _nodeStylesUpdatedForTest: function(node, rebuild) { // Tests override this method. }, _refreshStyleRules: function(sections, computedStyle) { var nodeComputedStyle = computedStyle; var styleRules = []; for (var i = 0; sections && i < sections.length; ++i) { var section = sections[i]; if (section.isBlank) continue; if (section.computedStyle) section.styleRule.style = nodeComputedStyle; var styleRule = { section: section, style: section.styleRule.style, computedStyle: section.computedStyle, rule: section.rule, editable: !!(section.styleRule.style && section.styleRule.style.id), isAttribute: section.styleRule.isAttribute, isInherited: section.styleRule.isInherited, parentNode: section.styleRule.parentNode }; styleRules.push(styleRule); } return styleRules; }, _rebuildStyleRules: function(node, styles) { var nodeComputedStyle = styles.computedStyle; this.sections = {}; var styleRules = []; function addAttributesStyle() { if (!styles.attributesStyle) return; var attrStyle = { style: styles.attributesStyle, editable: false }; attrStyle.selectorText = node.nodeNameInCorrectCase() + "[" + WebInspector.UIString("Attributes Style") + "]"; styleRules.push(attrStyle); } styleRules.push({ computedStyle: true, selectorText: "", style: nodeComputedStyle, editable: false }); // Inline style has the greatest specificity. if (styles.inlineStyle && node.nodeType() === Node.ELEMENT_NODE) { var inlineStyle = { selectorText: "element.style", style: styles.inlineStyle, isAttribute: true }; styleRules.push(inlineStyle); } // Add rules in reverse order to match the cascade order. var addedAttributesStyle; for (var i = styles.matchedCSSRules.length - 1; i >= 0; --i) { var rule = styles.matchedCSSRules[i]; if (!WebInspector.settings.showUserAgentStyles.get() && (rule.isUser || rule.isUserAgent)) continue; if ((rule.isUser || rule.isUserAgent) && !addedAttributesStyle) { // Show element's Style Attributes after all author rules. addedAttributesStyle = true; addAttributesStyle(); } styleRules.push({ style: rule.style, selectorText: rule.selectorText, media: rule.media, sourceURL: rule.resourceURL(), rule: rule, editable: !!(rule.style && rule.style.id) }); } if (!addedAttributesStyle) addAttributesStyle(); // Walk the node structure and identify styles with inherited properties. var parentNode = node.parentNode; function insertInheritedNodeSeparator(node) { var entry = {}; entry.isStyleSeparator = true; entry.node = node; styleRules.push(entry); } for (var parentOrdinal = 0; parentOrdinal < styles.inherited.length; ++parentOrdinal) { var parentStyles = styles.inherited[parentOrdinal]; var separatorInserted = false; if (parentStyles.inlineStyle) { if (this._containsInherited(parentStyles.inlineStyle)) { var inlineStyle = { selectorText: WebInspector.UIString("Style Attribute"), style: parentStyles.inlineStyle, isAttribute: true, isInherited: true, parentNode: parentNode }; if (!separatorInserted) { insertInheritedNodeSeparator(parentNode); separatorInserted = true; } styleRules.push(inlineStyle); } } for (var i = parentStyles.matchedCSSRules.length - 1; i >= 0; --i) { var rulePayload = parentStyles.matchedCSSRules[i]; if (!this._containsInherited(rulePayload.style)) continue; var rule = rulePayload; if (!WebInspector.settings.showUserAgentStyles.get() && (rule.isUser || rule.isUserAgent)) continue; if (!separatorInserted) { insertInheritedNodeSeparator(parentNode); separatorInserted = true; } styleRules.push({ style: rule.style, selectorText: rule.selectorText, media: rule.media, sourceURL: rule.resourceURL(), rule: rule, isInherited: true, parentNode: parentNode, editable: !!(rule.style && rule.style.id) }); } parentNode = parentNode.parentNode; } return styleRules; }, _markUsedProperties: function(styleRules, usedProperties) { var foundImportantProperties = {}; var propertyToEffectiveRule = {}; var inheritedPropertyToNode = {}; for (var i = 0; i < styleRules.length; ++i) { var styleRule = styleRules[i]; if (styleRule.computedStyle || styleRule.isStyleSeparator) continue; if (styleRule.section && styleRule.section.noAffect) continue; styleRule.usedProperties = {}; var style = styleRule.style; var allProperties = style.allProperties; for (var j = 0; j < allProperties.length; ++j) { var property = allProperties[j]; if (!property.isLive || !property.parsedOk) continue; // Do not pick non-inherited properties from inherited styles. if (styleRule.isInherited && !WebInspector.CSSMetadata.isPropertyInherited(property.name)) continue; var canonicalName = WebInspector.CSSMetadata.canonicalPropertyName(property.name); if (foundImportantProperties.hasOwnProperty(canonicalName)) continue; var isImportant = property.priority.length; if (!isImportant && usedProperties.hasOwnProperty(canonicalName)) continue; var isKnownProperty = propertyToEffectiveRule.hasOwnProperty(canonicalName); if (!isKnownProperty && styleRule.isInherited && !inheritedPropertyToNode[canonicalName]) inheritedPropertyToNode[canonicalName] = styleRule.parentNode; if (isImportant) { if (styleRule.isInherited && isKnownProperty && styleRule.parentNode !== inheritedPropertyToNode[canonicalName]) continue; foundImportantProperties[canonicalName] = true; if (isKnownProperty) delete propertyToEffectiveRule[canonicalName].usedProperties[canonicalName]; } styleRule.usedProperties[canonicalName] = true; usedProperties[canonicalName] = true; propertyToEffectiveRule[canonicalName] = styleRule; } } }, _refreshSectionsForStyleRules: function(styleRules, usedProperties, editedSection) { // Walk the style rules and update the sections with new overloaded and used properties. for (var i = 0; i < styleRules.length; ++i) { var styleRule = styleRules[i]; var section = styleRule.section; if (styleRule.computedStyle) { section._usedProperties = usedProperties; section.update(); } else { section._usedProperties = styleRule.usedProperties; section.update(section === editedSection); } } }, _rebuildSectionsForStyleRules: function(styleRules, usedProperties, pseudoId, anchorElement) { // Make a property section for each style rule. var sections = []; var lastWasSeparator = true; for (var i = 0; i < styleRules.length; ++i) { var styleRule = styleRules[i]; if (styleRule.isStyleSeparator) { var separatorElement = document.createElement("div"); separatorElement.className = "sidebar-separator"; if (styleRule.node) { var link = WebInspector.DOMPresentationUtils.linkifyNodeReference(styleRule.node); separatorElement.appendChild(document.createTextNode(WebInspector.UIString("Inherited from") + " ")); separatorElement.appendChild(link); if (!sections.inheritedPropertiesSeparatorElement) sections.inheritedPropertiesSeparatorElement = separatorElement; } else if ("pseudoId" in styleRule) { var pseudoName = WebInspector.StylesSidebarPane.PseudoIdNames[styleRule.pseudoId]; if (pseudoName) separatorElement.textContent = WebInspector.UIString("Pseudo ::%s element", pseudoName); else separatorElement.textContent = WebInspector.UIString("Pseudo element"); } else separatorElement.textContent = styleRule.text; this._sectionsContainer.insertBefore(separatorElement, anchorElement); lastWasSeparator = true; continue; } var computedStyle = styleRule.computedStyle; // Default editable to true if it was omitted. var editable = styleRule.editable; if (typeof editable === "undefined") editable = true; if (computedStyle) var section = new WebInspector.ComputedStylePropertiesSection(this, styleRule, usedProperties); else { var section = new WebInspector.StylePropertiesSection(this, styleRule, editable, styleRule.isInherited, lastWasSeparator); section._markSelectorMatches(); } section.expanded = true; if (computedStyle) { this._computedStylePane.bodyElement.appendChild(section.element); lastWasSeparator = true; } else { this._sectionsContainer.insertBefore(section.element, anchorElement); lastWasSeparator = false; } sections.push(section); } return sections; }, _containsInherited: function(style) { var properties = style.allProperties; for (var i = 0; i < properties.length; ++i) { var property = properties[i]; // Does this style contain non-overridden inherited property? if (property.isLive && WebInspector.CSSMetadata.isPropertyInherited(property.name)) return true; } return false; }, _colorFormatSettingChanged: function(event) { this._updateColorFormatFilter(); for (var pseudoId in this.sections) { var sections = this.sections[pseudoId]; for (var i = 0; i < sections.length; ++i) sections[i].update(true); } }, _updateColorFormatFilter: function() { // Select the correct color format setting again, since it needs to be selected. var selectedIndex = 0; var value = WebInspector.settings.colorFormat.get(); var options = this.settingsSelectElement.options; for (var i = 0; i < options.length; ++i) { if (options[i].value === value) { selectedIndex = i; break; } } this.settingsSelectElement.selectedIndex = selectedIndex; }, _changeSetting: function(event) { var options = this.settingsSelectElement.options; var selectedOption = options[this.settingsSelectElement.selectedIndex]; WebInspector.settings.colorFormat.set(selectedOption.value); }, _createNewRule: function(event) { event.consume(); this.expand(); this.addBlankSection().startEditingSelector(); }, addBlankSection: function() { var blankSection = new WebInspector.BlankStylePropertiesSection(this, this.node ? this.node.appropriateSelectorFor(true) : ""); var elementStyleSection = this.sections[0][1]; this._sectionsContainer.insertBefore(blankSection.element, elementStyleSection.element.nextSibling); this.sections[0].splice(2, 0, blankSection); return blankSection; }, removeSection: function(section) { for (var pseudoId in this.sections) { var sections = this.sections[pseudoId]; var index = sections.indexOf(section); if (index === -1) continue; sections.splice(index, 1); section.element.remove(); } }, _toggleElementStatePane: function(event) { event.consume(); if (!this._elementStateButton.hasStyleClass("toggled")) { this.expand(); this._elementStateButton.addStyleClass("toggled"); this._elementStatePane.addStyleClass("expanded"); } else { this._elementStateButton.removeStyleClass("toggled"); this._elementStatePane.removeStyleClass("expanded"); } }, _createElementStatePane: function() { this._elementStatePane = document.createElement("div"); this._elementStatePane.className = "styles-element-state-pane source-code"; var table = document.createElement("table"); var inputs = []; this._elementStatePane.inputs = inputs; function clickListener(event) { var node = this._validateNode(); if (!node) return; this._setPseudoClassCallback(node.id, event.target.state, event.target.checked); } function createCheckbox(state) { var td = document.createElement("td"); var label = document.createElement("label"); var input = document.createElement("input"); input.type = "checkbox"; input.state = state; input.addEventListener("click", clickListener.bind(this), false); inputs.push(input); label.appendChild(input); label.appendChild(document.createTextNode(":" + state)); td.appendChild(label); return td; } var tr = document.createElement("tr"); tr.appendChild(createCheckbox.call(this, "active")); tr.appendChild(createCheckbox.call(this, "hover")); table.appendChild(tr); tr = document.createElement("tr"); tr.appendChild(createCheckbox.call(this, "focus")); tr.appendChild(createCheckbox.call(this, "visited")); table.appendChild(tr); this._elementStatePane.appendChild(table); }, _showUserAgentStylesSettingChanged: function() { this._rebuildUpdate(); }, willHide: function() { this._spectrumHelper.hide(); this._discardElementUnderMouse(); }, _discardElementUnderMouse: function() { if (this._elementUnderMouse) this._elementUnderMouse.removeStyleClass("styles-panel-hovered"); delete this._elementUnderMouse; }, _mouseMovedOverElement: function(e) { if (this._elementUnderMouse && e.target !== this._elementUnderMouse) this._discardElementUnderMouse(); this._elementUnderMouse = e.target; if (WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(e)) this._elementUnderMouse.addStyleClass("styles-panel-hovered"); }, _keyDown: function(e) { if ((!WebInspector.isMac() && e.keyCode === WebInspector.KeyboardShortcut.Keys.Ctrl.code) || (WebInspector.isMac() && e.keyCode === WebInspector.KeyboardShortcut.Keys.Meta.code)) { if (this._elementUnderMouse) this._elementUnderMouse.addStyleClass("styles-panel-hovered"); } }, _keyUp: function(e) { if ((!WebInspector.isMac() && e.keyCode === WebInspector.KeyboardShortcut.Keys.Ctrl.code) || (WebInspector.isMac() && e.keyCode === WebInspector.KeyboardShortcut.Keys.Meta.code)) { this._discardElementUnderMouse(); } }, __proto__: WebInspector.SidebarPane.prototype } /** * @constructor * @extends {WebInspector.SidebarPane} */ WebInspector.ComputedStyleSidebarPane = function() { WebInspector.SidebarPane.call(this, WebInspector.UIString("Computed Style")); var showInheritedCheckbox = new WebInspector.Checkbox(WebInspector.UIString("Show inherited"), "sidebar-pane-subtitle"); this.titleElement.appendChild(showInheritedCheckbox.element); if (WebInspector.settings.showInheritedComputedStyleProperties.get()) { this.bodyElement.addStyleClass("show-inherited"); showInheritedCheckbox.checked = true; } function showInheritedToggleFunction(event) { WebInspector.settings.showInheritedComputedStyleProperties.set(showInheritedCheckbox.checked); if (WebInspector.settings.showInheritedComputedStyleProperties.get()) this.bodyElement.addStyleClass("show-inherited"); else this.bodyElement.removeStyleClass("show-inherited"); } showInheritedCheckbox.addEventListener(showInheritedToggleFunction.bind(this)); } WebInspector.ComputedStyleSidebarPane.prototype = { wasShown: function() { WebInspector.SidebarPane.prototype.wasShown.call(this); if (!this._hasFreshContent) this.prepareContent(); }, /** * @param {function()=} callback */ prepareContent: function(callback) { function wrappedCallback() { this._hasFreshContent = true; if (callback) callback(); delete this._hasFreshContent; } this._stylesSidebarPane._refreshUpdate(null, true, wrappedCallback.bind(this)); }, __proto__: WebInspector.SidebarPane.prototype } /** * @constructor * @extends {WebInspector.PropertiesSection} */ WebInspector.StylePropertiesSection = function(parentPane, styleRule, editable, isInherited, isFirstSection) { WebInspector.PropertiesSection.call(this, ""); this.element.className = "styles-section matched-styles monospace" + (isFirstSection ? " first-styles-section" : ""); // We don't really use properties' disclosure. this.propertiesElement.removeStyleClass("properties-tree"); this._parentPane = parentPane; this.styleRule = styleRule; this.rule = this.styleRule.rule; this.editable = editable; this.isInherited = isInherited; if (styleRule.media) { for (var i = styleRule.media.length - 1; i >= 0; --i) { var media = styleRule.media[i]; var mediaDataElement = this.titleElement.createChild("div", "media"); var mediaText; switch (media.source) { case WebInspector.CSSMedia.Source.LINKED_SHEET: case WebInspector.CSSMedia.Source.INLINE_SHEET: mediaText = "media=\"" + media.text + "\""; break; case WebInspector.CSSMedia.Source.MEDIA_RULE: mediaText = "@media " + media.text; break; case WebInspector.CSSMedia.Source.IMPORT_RULE: mediaText = "@import " + media.text; break; } if (media.sourceURL) { var refElement = mediaDataElement.createChild("div", "subtitle"); var rawLocation; var mediaHeader; if (media.range) { mediaHeader = media.header(); if (mediaHeader) { var lineNumber = media.lineNumberInSource(); var columnNumber = media.columnNumberInSource(); console.assert(typeof lineNumber !== "undefined" && typeof columnNumber !== "undefined"); rawLocation = new WebInspector.CSSLocation(media.sourceURL, lineNumber, columnNumber); } } var anchor; if (rawLocation) anchor = this._parentPane._linkifier.linkifyCSSLocation(mediaHeader.id, rawLocation); else { // The "linkedStylesheet" case. anchor = WebInspector.linkifyResourceAsNode(media.sourceURL, undefined, "subtitle", media.sourceURL); } anchor.preferredPanel = "scripts"; anchor.style.float = "right"; refElement.appendChild(anchor); } var mediaTextElement = mediaDataElement.createChild("span"); mediaTextElement.textContent = mediaText; mediaTextElement.title = media.text; } } var selectorContainer = document.createElement("div"); this._selectorElement = document.createElement("span"); this._selectorElement.textContent = styleRule.selectorText; selectorContainer.appendChild(this._selectorElement); var openBrace = document.createElement("span"); openBrace.textContent = " {"; selectorContainer.appendChild(openBrace); selectorContainer.addEventListener("mousedown", this._handleEmptySpaceMouseDown.bind(this), false); selectorContainer.addEventListener("click", this._handleSelectorContainerClick.bind(this), false); var closeBrace = document.createElement("div"); closeBrace.textContent = "}"; this.element.appendChild(closeBrace); this._selectorElement.addEventListener("click", this._handleSelectorClick.bind(this), false); this.element.addEventListener("mousedown", this._handleEmptySpaceMouseDown.bind(this), false); this.element.addEventListener("click", this._handleEmptySpaceClick.bind(this), false); if (this.rule) { // Prevent editing the user agent and user rules. if (this.rule.isUserAgent || this.rule.isUser) this.editable = false; else { // Check this is a real CSSRule, not a bogus object coming from WebInspector.BlankStylePropertiesSection. if (this.rule.id) this.navigable = !!this.rule.resourceURL(); } this.titleElement.addStyleClass("styles-selector"); } this._usedProperties = styleRule.usedProperties; this._selectorRefElement = document.createElement("div"); this._selectorRefElement.className = "subtitle"; this._selectorRefElement.appendChild(this._createRuleOriginNode()); selectorContainer.insertBefore(this._selectorRefElement, selectorContainer.firstChild); this.titleElement.appendChild(selectorContainer); this._selectorContainer = selectorContainer; if (isInherited) this.element.addStyleClass("show-inherited"); // This one is related to inherited rules, not computed style. if (this.navigable) this.element.addStyleClass("navigable"); if (!this.editable) this.element.addStyleClass("read-only"); } WebInspector.StylePropertiesSection.prototype = { get pane() { return this._parentPane; }, collapse: function(dontRememberState) { // Overriding with empty body. }, isPropertyInherited: function(propertyName) { if (this.isInherited) { // While rendering inherited stylesheet, reverse meaning of this property. // Render truly inherited properties with black, i.e. return them as non-inherited. return !WebInspector.CSSMetadata.isPropertyInherited(propertyName); } return false; }, /** * @param {string} propertyName * @param {boolean=} isShorthand */ isPropertyOverloaded: function(propertyName, isShorthand) { if (!this._usedProperties || this.noAffect) return false; if (this.isInherited && !WebInspector.CSSMetadata.isPropertyInherited(propertyName)) { // In the inherited sections, only show overrides for the potentially inherited properties. return false; } var canonicalName = WebInspector.CSSMetadata.canonicalPropertyName(propertyName); var used = (canonicalName in this._usedProperties); if (used || !isShorthand) return !used; // Find out if any of the individual longhand properties of the shorthand // are used, if none are then the shorthand is overloaded too. var longhandProperties = this.styleRule.style.longhandProperties(propertyName); for (var j = 0; j < longhandProperties.length; ++j) { var individualProperty = longhandProperties[j]; if (WebInspector.CSSMetadata.canonicalPropertyName(individualProperty.name) in this._usedProperties) return false; } return true; }, nextEditableSibling: function() { var curSection = this; do { curSection = curSection.nextSibling; } while (curSection && !curSection.editable); if (!curSection) { curSection = this.firstSibling; while (curSection && !curSection.editable) curSection = curSection.nextSibling; } return (curSection && curSection.editable) ? curSection : null; }, previousEditableSibling: function() { var curSection = this; do { curSection = curSection.previousSibling; } while (curSection && !curSection.editable); if (!curSection) { curSection = this.lastSibling; while (curSection && !curSection.editable) curSection = curSection.previousSibling; } return (curSection && curSection.editable) ? curSection : null; }, update: function(full) { if (this.styleRule.selectorText) this._selectorElement.textContent = this.styleRule.selectorText; this._markSelectorMatches(); if (full) { this.propertiesTreeOutline.removeChildren(); this.populated = false; } else { var child = this.propertiesTreeOutline.children[0]; while (child) { child.overloaded = this.isPropertyOverloaded(child.name, child.isShorthand); child = child.traverseNextTreeElement(false, null, true); } } this.afterUpdate(); }, afterUpdate: function() { if (this._afterUpdate) { this._afterUpdate(this); delete this._afterUpdate; } }, onpopulate: function() { var style = this.styleRule.style; var allProperties = style.allProperties; this.uniqueProperties = []; var styleHasEditableSource = this.editable && !!style.range; if (styleHasEditableSource) { for (var i = 0; i < allProperties.length; ++i) { var property = allProperties[i]; this.uniqueProperties.push(property); if (property.styleBased) continue; var isShorthand = !!WebInspector.CSSMetadata.cssPropertiesMetainfo.longhands(property.name); var inherited = this.isPropertyInherited(property.name); var overloaded = property.inactive || this.isPropertyOverloaded(property.name); var item = new WebInspector.StylePropertyTreeElement(this._parentPane, this.styleRule, style, property, isShorthand, inherited, overloaded); this.propertiesTreeOutline.appendChild(item); } return; } var generatedShorthands = {}; // For style-based properties, generate shorthands with values when possible. for (var i = 0; i < allProperties.length; ++i) { var property = allProperties[i]; this.uniqueProperties.push(property); var isShorthand = !!WebInspector.CSSMetadata.cssPropertiesMetainfo.longhands(property.name); // For style-based properties, try generating shorthands. var shorthands = isShorthand ? null : WebInspector.CSSMetadata.cssPropertiesMetainfo.shorthands(property.name); var shorthandPropertyAvailable = false; for (var j = 0; shorthands && !shorthandPropertyAvailable && j < shorthands.length; ++j) { var shorthand = shorthands[j]; if (shorthand in generatedShorthands) { shorthandPropertyAvailable = true; continue; // There already is a shorthand this longhands falls under. } if (style.getLiveProperty(shorthand)) { shorthandPropertyAvailable = true; continue; // There is an explict shorthand property this longhands falls under. } if (!style.shorthandValue(shorthand)) { shorthandPropertyAvailable = false; continue; // Never generate synthetic shorthands when no value is available. } // Generate synthetic shorthand we have a value for. var shorthandProperty = new WebInspector.CSSProperty(style, style.allProperties.length, shorthand, style.shorthandValue(shorthand), "", "style", true, true); var overloaded = property.inactive || this.isPropertyOverloaded(property.name, true); var item = new WebInspector.StylePropertyTreeElement(this._parentPane, this.styleRule, style, shorthandProperty, /* isShorthand */ true, /* inherited */ false, overloaded); this.propertiesTreeOutline.appendChild(item); generatedShorthands[shorthand] = shorthandProperty; shorthandPropertyAvailable = true; } if (shorthandPropertyAvailable) continue; // Shorthand for the property found. var inherited = this.isPropertyInherited(property.name); var overloaded = property.inactive || this.isPropertyOverloaded(property.name, isShorthand); var item = new WebInspector.StylePropertyTreeElement(this._parentPane, this.styleRule, style, property, isShorthand, inherited, overloaded); this.propertiesTreeOutline.appendChild(item); } }, findTreeElementWithName: function(name) { var treeElement = this.propertiesTreeOutline.children[0]; while (treeElement) { if (treeElement.name === name) return treeElement; treeElement = treeElement.traverseNextTreeElement(true, null, true); } return null; }, _markSelectorMatches: function() { var rule = this.styleRule.rule; if (!rule) return; var matchingSelectors = rule.matchingSelectors; // .selector is rendered as non-affecting selector by default. if (this.noAffect || matchingSelectors) this._selectorElement.className = "selector"; if (!matchingSelectors) return; var selectors = rule.selectors; var fra