UNPKG

monaca-lib

Version:

Monaca cloud API bindings for JavaScript

1,371 lines (1,202 loc) 141 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(!WebInspector.DOMNode, string, boolean)=} setPseudoClassCallback */ WebInspector.StylesSidebarPane = function(computedStylePane, setPseudoClassCallback) { WebInspector.SidebarPane.call(this, WebInspector.UIString("Styles")); if (!Runtime.experiments.isEnabled("animationInspection")) { this._animationsControlButton = createElement("button"); this._animationsControlButton.className = "pane-title-button animations-controls"; this._animationsControlButton.title = WebInspector.UIString("Animations Controls"); this._animationsControlButton.addEventListener("click", this._toggleAnimationsControlPane.bind(this), false); this.titleElement.appendChild(this._animationsControlButton); } this._elementStateButton = 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 = 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._createNewRuleInViaInspectorStyleSheet.bind(this), false); this.titleElement.appendChild(addButton); addButton.createChild("div", "long-click-glyph fill"); this._addButtonLongClickController = new WebInspector.LongClickController(addButton); this._addButtonLongClickController.addEventListener(WebInspector.LongClickController.Events.LongClick, this._onAddButtonLongClick.bind(this)); this._addButtonLongClickController.enable(); this._computedStylePane = computedStylePane; computedStylePane.setHostingPane(this); this._setPseudoClassCallback = setPseudoClassCallback; this.element.addEventListener("contextmenu", this._contextMenuEventFired.bind(this), true); WebInspector.settings.colorFormat.addChangeListener(this._colorFormatSettingChanged.bind(this)); WebInspector.settings.showUserAgentStyles.addChangeListener(this._showUserAgentStylesSettingChanged.bind(this)); this._createElementStatePane(); this.bodyElement.appendChild(this._elementStatePane); this._createAnimationsControlPane(); this.bodyElement.appendChild(this._animationsControlPane); this._sectionsContainer = createElement("div"); this.bodyElement.appendChild(this._sectionsContainer); this._stylesPopoverHelper = new WebInspector.StylesPopoverHelper(); this._spectrum = new WebInspector.Spectrum(); this._bezierEditor = new WebInspector.BezierEditor(); this._linkifier = new WebInspector.Linkifier(new WebInspector.Linkifier.DefaultCSSFormatter()); this.element.classList.add("styles-pane"); this.element.classList.toggle("show-user-styles", WebInspector.settings.showUserAgentStyles.get()); this.element.addEventListener("mousemove", this._mouseMovedOverElement.bind(this), false); this._keyDownBound = this._keyDown.bind(this); this._keyUpBound = this._keyUp.bind(this); } // Keep in sync with LayoutStyleConstants.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 LayoutStyleConstants and CSSSelector.cpp at // runtime. WebInspector.StylesSidebarPane.PseudoIdNames = [ "", "first-line", "first-letter", "before", "after", "backdrop", "selection", "", "-webkit-scrollbar", "-webkit-scrollbar-thumb", "-webkit-scrollbar-button", "-webkit-scrollbar-track", "-webkit-scrollbar-track-piece", "-webkit-scrollbar-corner", "-webkit-resizer" ]; WebInspector.StylesSidebarPane._colorRegex = /((?:rgb|hsl)a?\([^)]+\)|#[0-9a-fA-F]{6}|#[0-9a-fA-F]{3}|\b\w+\b(?!-))/g; WebInspector.StylesSidebarPane._bezierRegex = /(cubic-bezier\([^)]+\))/g; /** * @enum {string} */ WebInspector.StylesSidebarPane.Events = { SelectorEditingStarted: "SelectorEditingStarted", SelectorEditingEnded: "SelectorEditingEnded" }; /** * @param {!WebInspector.CSSProperty} property * @return {!Element} */ WebInspector.StylesSidebarPane.createExclamationMark = function(property) { var exclamationElement = 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.CSSProperty} property * @return {boolean} */ WebInspector.StylesSidebarPane._ignoreErrorsForProperty = function(property) { /** * @param {string} string */ 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 = { /** * @return {?WebInspector.DOMNode} */ node: function() { return this._node; }, /** * @param {!WebInspector.Event} event */ _onAddButtonLongClick: function(event) { this._addButtonLongClickController.reset(); var cssModel = this._target.cssModel; var headers = cssModel.styleSheetHeaders().filter(styleSheetResourceHeader); /** @type {!Array.<{text: string, handler: function()}>} */ var contextMenuDescriptors = []; for (var i = 0; i < headers.length; ++i) { var header = headers[i]; var handler = this._createNewRuleInStyleSheet.bind(this, header); contextMenuDescriptors.push({ text: WebInspector.displayNameForURL(header.resourceURL()), handler: handler }); } contextMenuDescriptors.sort(compareDescriptors); var contextMenu = new WebInspector.ContextMenu(/** @type {!Event} */(event.data)); for (var i = 0; i < contextMenuDescriptors.length; ++i) { var descriptor = contextMenuDescriptors[i]; contextMenu.appendItem(descriptor.text, descriptor.handler); } if (!contextMenu.isEmpty()) contextMenu.appendSeparator(); contextMenu.appendItem("inspector-stylesheet", this._createNewRuleInViaInspectorStyleSheet.bind(this)); contextMenu.show(); /** * @param {!{text: string, handler: function()}} descriptor1 * @param {!{text: string, handler: function()}} descriptor2 * @return {number} */ function compareDescriptors(descriptor1, descriptor2) { return String.naturalOrderComparator(descriptor1.text, descriptor2.text); } /** * @param {!WebInspector.CSSStyleSheetHeader} header * @return {boolean} */ function styleSheetResourceHeader(header) { return !header.isViaInspector() && !header.isInline && !!header.resourceURL(); } }, /** * @param {!WebInspector.DOMNode} node */ updateEditingSelectorForNode: function(node) { var selectorText = WebInspector.DOMPresentationUtils.simpleSelector(node); if (!selectorText) return; this._editingSelectorSection.setSelectorText(selectorText); }, /** * @return {boolean} */ isEditingSelector: function() { return !!this._editingSelectorSection; }, /** * @param {!WebInspector.StylePropertiesSection} section */ _startEditingSelector: function(section) { this._editingSelectorSection = section; this.dispatchEventToListeners(WebInspector.StylesSidebarPane.Events.SelectorEditingStarted); }, _finishEditingSelector: function() { delete this._editingSelectorSection; this.dispatchEventToListeners(WebInspector.StylesSidebarPane.Events.SelectorEditingEnded); }, /** * @param {!WebInspector.CSSRule} editedRule * @param {!WebInspector.TextRange} oldRange * @param {!WebInspector.TextRange} newRange */ _styleSheetRuleEdited: function(editedRule, oldRange, newRange) { if (!editedRule.styleSheetId) return; for (var pseudoId in this.sections) { var styleRuleSections = this.sections[pseudoId]; for (var i = 0; i < styleRuleSections.length; ++i) { var section = styleRuleSections[i]; if (section.computedStyle) continue; section._styleSheetRuleEdited(editedRule, oldRange, newRange); } } }, /** * @param {!WebInspector.CSSMedia} oldMedia * @param {!WebInspector.CSSMedia} newMedia */ _styleSheetMediaEdited: function(oldMedia, newMedia) { if (!oldMedia.parentStyleSheetId) return; for (var pseudoId in this.sections) { var styleRuleSections = this.sections[pseudoId]; for (var i = 0; i < styleRuleSections.length; ++i) { var section = styleRuleSections[i]; if (section.computedStyle) continue; section._styleSheetMediaEdited(oldMedia, newMedia); } } }, /** * @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(/** @type {!Node} */ (event.target)); contextMenu.show(); }, /** * @param {!Element} matchedStylesElement */ setFilterBoxContainer: function(matchedStylesElement) { matchedStylesElement.appendChild(this._createCSSFilterControl()); }, /** * @return {!Element} */ _createCSSFilterControl: function() { var filterInput = WebInspector.StylesSidebarPane._createPropertyFilterElement(WebInspector.UIString("Find in Styles"), searchHandler.bind(this)); /** * @param {?RegExp} regex * @this {WebInspector.StylesSidebarPane} */ function searchHandler(regex) { this._filterRegex = regex; this._updateFilter(); } return filterInput; }, get _forcedPseudoClasses() { return this._node ? (this._node.getUserProperty(WebInspector.CSSStyleModel.PseudoStatePropertyName) || undefined) : undefined; }, _updateForcedPseudoStateInputs: function() { if (!this._node) return; var hasPseudoType = !!this._node.pseudoType(); this._elementStateButton.classList.toggle("hidden", hasPseudoType); this._elementStatePane.classList.toggle("expanded", !hasPseudoType && this._elementStateButton.classList.contains("toggled")); 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 */ setNode: function(node) { this._stylesPopoverHelper.hide(); this._discardElementUnderMouse(); if (node && node.nodeType() === Node.TEXT_NODE && node.parentNode) node = node.parentNode; if (node && node.nodeType() !== Node.ELEMENT_NODE) node = null; this._node = node; if (node) this._updateTarget(node.target()); this._computedStylePane.setNode(node); this._resetCache(); this._scheduleUpdate(); }, _scheduleUpdate: function() { if (!this.isShowing() && !this._computedStylePane.isShowing()) { this._updateWhenVisible = true; return; } this._rebuildUpdate(); }, /** * @param {!WebInspector.Target} target */ _updateTarget: function(target) { if (this._target === target) return; if (this._target) { this._target.cssModel.removeEventListener(WebInspector.CSSStyleModel.Events.StyleSheetAdded, this._styleSheetOrMediaQueryResultChanged, this); this._target.cssModel.removeEventListener(WebInspector.CSSStyleModel.Events.StyleSheetRemoved, this._styleSheetOrMediaQueryResultChanged, this); this._target.cssModel.removeEventListener(WebInspector.CSSStyleModel.Events.StyleSheetChanged, this._styleSheetOrMediaQueryResultChanged, this); this._target.cssModel.removeEventListener(WebInspector.CSSStyleModel.Events.MediaQueryResultChanged, this._styleSheetOrMediaQueryResultChanged, this); this._target.domModel.removeEventListener(WebInspector.DOMModel.Events.AttrModified, this._attributeChanged, this); this._target.domModel.removeEventListener(WebInspector.DOMModel.Events.AttrRemoved, this._attributeChanged, this); this._target.resourceTreeModel.removeEventListener(WebInspector.ResourceTreeModel.EventTypes.FrameResized, this._frameResized, this); } this._target = target; this._target.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetAdded, this._styleSheetOrMediaQueryResultChanged, this); this._target.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetRemoved, this._styleSheetOrMediaQueryResultChanged, this); this._target.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetChanged, this._styleSheetOrMediaQueryResultChanged, this); this._target.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.MediaQueryResultChanged, this._styleSheetOrMediaQueryResultChanged, this); this._target.domModel.addEventListener(WebInspector.DOMModel.Events.AttrModified, this._attributeChanged, this); this._target.domModel.addEventListener(WebInspector.DOMModel.Events.AttrRemoved, this._attributeChanged, this); this._target.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.FrameResized, this._frameResized, this); }, /** * @param {!WebInspector.StylePropertiesSection=} editedSection */ _refreshUpdate: function(editedSection) { var node = this._validateNode(); if (!node) return; this._innerRefreshUpdate(node, editedSection); if (this._filterRegex) this._updateFilter(); }, _rebuildUpdate: function() { this._updateForcedPseudoStateInputs(); if (this._rebuildUpdateInProgress) { this._lastNodeForInnerRebuild = this.node(); return; } var node = this._validateNode(); if (!node) return; this._rebuildUpdateInProgress = true; this._fetchMatchedCascade() .then(onStylesLoaded.bind(this)); /** * @param {?{matched: !WebInspector.SectionCascade, pseudo: !Map.<number, !WebInspector.SectionCascade>}} cascades * @this {WebInspector.StylesSidebarPane} */ function onStylesLoaded(cascades) { var lastRequestedRebuildNode = this._lastNodeForInnerRebuild || node; delete this._rebuildUpdateInProgress; delete this._lastNodeForInnerRebuild; if (node !== lastRequestedRebuildNode) { this._rebuildUpdate(); return; } this._innerRebuildUpdate(cascades); } }, _resetCache: function() { delete this._matchedCascadePromise; this._resetComputedCache(); }, _resetComputedCache: function() { delete this._computedCascadePromise; delete this._animationPropertiesPromise; }, /** * @return {!Promise.<?{matched: !WebInspector.SectionCascade, pseudo: !Map.<number, !WebInspector.SectionCascade>}>} */ _fetchMatchedCascade: function() { var node = this.node(); if (!node) return Promise.resolve(/** @type {?{matched: !WebInspector.SectionCascade, pseudo: !Map.<number, !WebInspector.SectionCascade>}} */(null)); if (!this._matchedCascadePromise) this._matchedCascadePromise = new Promise(this._getMatchedStylesForNode.bind(this, node)).then(buildMatchedCascades.bind(this, node)); return this._matchedCascadePromise; /** * @param {!WebInspector.DOMNode} node * @param {!WebInspector.StylesSidebarPane.MatchedRulesPayload} payload * @return {?{matched: !WebInspector.SectionCascade, pseudo: !Map.<number, !WebInspector.SectionCascade>}} * @this {WebInspector.StylesSidebarPane} */ function buildMatchedCascades(node, payload) { if (node !== this.node() || !payload.fulfilled()) return null; return { matched: this._buildMatchedRulesSectionCascade(node, payload), pseudo: this._buildPseudoCascades(node, payload) }; } }, /** * @return {!Promise.<?WebInspector.SectionCascade>} */ _fetchComputedCascade: function() { var node = this.node(); if (!node) return Promise.resolve(/** @type {?WebInspector.SectionCascade} */(null)); if (!this._computedCascadePromise) this._computedCascadePromise = new Promise(getComputedStyle.bind(null, node)).then(buildComputedCascade.bind(this, node)); return this._computedCascadePromise; /** * @param {!WebInspector.DOMNode} node * @param {function(?WebInspector.CSSStyleDeclaration)} resolve */ function getComputedStyle(node, resolve) { node.target().cssModel.getComputedStyleAsync(node.id, resolve); } /** * @param {!WebInspector.DOMNode} node * @param {?WebInspector.CSSStyleDeclaration} styles * @return {?WebInspector.SectionCascade} * @this {WebInspector.StylesSidebarPane} */ function buildComputedCascade(node, styles) { if (node !== this.node()) return null; if (!styles) return null; var computedCascade = new WebInspector.SectionCascade(); computedCascade.appendModelFromStyle(styles, ""); return computedCascade; } }, /** * @return {!Promise.<!Map.<string, string>>} */ _fetchAnimationProperties: function() { var node = this.node(); if (!node) return Promise.resolve(new Map()); if (!this._animationPropertiesPromise) this._animationPropertiesPromise = new Promise(this._getAnimationPropertiesForNode.bind(this, node)).then(onAnimationProperties.bind(this)); return this._animationPropertiesPromise; /** * @param {!Map.<string, string>} properties * @return {!Map.<string, string>} * @this {WebInspector.StylesSidebarPane} */ function onAnimationProperties(properties) { return this.node() !== node ? new Map() : properties; } }, /** * @param {!WebInspector.DOMNode} node * @param {function(!WebInspector.StylesSidebarPane.MatchedRulesPayload)} callback */ _getMatchedStylesForNode: function(node, callback) { var target = node.target(); target.cssModel.getInlineStylesAsync(node.id, inlineCallback); target.cssModel.getMatchedStylesAsync(node.id, false, false, matchedCallback); var payload = new WebInspector.StylesSidebarPane.MatchedRulesPayload(); /** * @param {?WebInspector.CSSStyleDeclaration} inlineStyle * @param {?WebInspector.CSSStyleDeclaration} attributesStyle */ function inlineCallback(inlineStyle, attributesStyle) { payload.inlineStyle = /** @type {?WebInspector.CSSStyleDeclaration} */(inlineStyle); payload.attributesStyle = /** @type {?WebInspector.CSSStyleDeclaration} */(attributesStyle); } /** * @param {?*} matchedResult */ function matchedCallback(matchedResult) { if (matchedResult) { payload.matchedCSSRules = /** @type {?Array.<!WebInspector.CSSRule>} */(matchedResult.matchedCSSRules); payload.pseudoElements = /** @type {?Array.<{pseudoId: number, rules: !Array.<!WebInspector.CSSRule>}>} */(matchedResult.pseudoElements); payload.inherited = /** @type {?Array.<{matchedCSSRules: !Array.<!WebInspector.CSSRule>}>} */(matchedResult.inherited); } callback(payload); } }, /** * @param {!WebInspector.DOMNode} node * @param {function(!Map<string, string>)} callback */ _getAnimationPropertiesForNode: function(node, callback) { if (Runtime.experiments.isEnabled("animationInspection")) node.target().animationModel.getAnimationPlayers(node.id, false, animationPlayersCallback); else callback(new Map()); /** * @param {?Array.<!WebInspector.AnimationModel.AnimationPlayer>} animationPlayers */ function animationPlayersCallback(animationPlayers) { var animationProperties = new Map(); if (!animationPlayers) return; for (var i = 0; i < animationPlayers.length; i++) { var player = animationPlayers[i]; if (!player.source().keyframesRule()) continue; var animationCascade = new WebInspector.SectionCascade(); var keyframes = player.source().keyframesRule().keyframes(); for (var j = 0; j < keyframes.length; j++) animationCascade.appendModelFromStyle(keyframes[j].style(), ""); for (var property of animationCascade.allUsedProperties()) animationProperties.set(property, player.name()); } callback(animationProperties); } }, _validateNode: function() { if (!this._node) { this._sectionsContainer.removeChildren(); this.sections = {}; return null; } return this._node; }, /** * @param {boolean} editing */ setEditingStyle: function(editing) { this._isEditingStyle = editing; }, _styleSheetOrMediaQueryResultChanged: function() { if (this._userOperation || this._isEditingStyle) { this._resetComputedCache(); return; } this._resetCache(); this._scheduleUpdate(); }, _frameResized: function() { /** * @this {WebInspector.StylesSidebarPane} */ function refreshContents() { this._styleSheetOrMediaQueryResultChanged(); delete this._activeTimer; } if (this._activeTimer) clearTimeout(this._activeTimer); this._activeTimer = setTimeout(refreshContents.bind(this), 100); }, /** * @param {!WebInspector.Event} event */ _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) { this._resetComputedCache(); return; } if (!this._canAffectCurrentStyles(event.data.node)) return; this._resetCache(); this._scheduleUpdate(); }, /** * @param {?WebInspector.DOMNode} node */ _canAffectCurrentStyles: function(node) { return this._node && (this._node === node || node.parentNode === this._node.parentNode || node.isAncestor(this._node)); }, /** * @param {!WebInspector.DOMNode} node * @param {!WebInspector.StylePropertiesSection=} editedSection */ _innerRefreshUpdate: function(node, editedSection) { for (var pseudoId in this.sections) { var sections = this.sections[pseudoId].filter(nonBlankSections); for (var section of sections) section.update(section === editedSection); } this._computedStylePane.update(); this._nodeStylesUpdatedForTest(node, false); /** * @param {!WebInspector.StylePropertiesSection} section * @return {boolean} */ function nonBlankSections(section) { return !section.isBlank; } }, /** * @param {?{matched: !WebInspector.SectionCascade, pseudo: !Map.<number, !WebInspector.SectionCascade>}} cascades */ _innerRebuildUpdate: function(cascades) { this._linkifier.reset(); this._sectionsContainer.removeChildren(); this.sections = {}; var node = this.node(); if (!cascades || !node) return; if (!!node.pseudoType()) this._appendTopPadding(); this.sections[0] = this._rebuildSectionsForStyleRules(cascades.matched); this._computedStylePane.update(); var pseudoIds = cascades.pseudo.keysArray().sort(); for (var pseudoId of pseudoIds) { this._appendSectionPseudoIdSeparator(pseudoId); this.sections[pseudoId] = this._rebuildSectionsForStyleRules(cascades.pseudo.get(pseudoId)); } if (this._filterRegex) this._updateFilter(); this._nodeStylesUpdatedForTest(node, true); this._updateAnimationsPlaybackRate(); }, /** * @param {!WebInspector.DOMNode} node * @param {!WebInspector.StylesSidebarPane.MatchedRulesPayload} styles * @return {!Map<number, !WebInspector.SectionCascade>} */ _buildPseudoCascades: function(node, styles) { var pseudoCascades = new Map(); for (var i = 0; i < styles.pseudoElements.length; ++i) { var pseudoElementCSSRules = styles.pseudoElements[i]; var pseudoId = pseudoElementCSSRules.pseudoId; // Add rules in reverse order to match the cascade order. var pseudoElementCascade = new WebInspector.SectionCascade(); for (var j = pseudoElementCSSRules.rules.length - 1; j >= 0; --j) { var rule = pseudoElementCSSRules.rules[j]; pseudoElementCascade.appendModelFromRule(rule); } pseudoCascades.set(pseudoId, pseudoElementCascade); } return pseudoCascades; }, /** * @param {!WebInspector.DOMNode} node * @param {boolean} rebuild */ _nodeStylesUpdatedForTest: function(node, rebuild) { // For sniffing in tests. }, /** * @param {!WebInspector.DOMNode} node * @param {!WebInspector.StylesSidebarPane.MatchedRulesPayload} styles * @return {!WebInspector.SectionCascade} */ _buildMatchedRulesSectionCascade: function(node, styles) { var cascade = new WebInspector.SectionCascade(); function addAttributesStyle() { if (!styles.attributesStyle) return; var selectorText = node.nodeNameInCorrectCase() + "[" + WebInspector.UIString("Attributes Style") + "]"; cascade.appendModelFromStyle(styles.attributesStyle, selectorText); } // Inline style has the greatest specificity. if (styles.inlineStyle && node.nodeType() === Node.ELEMENT_NODE) { var model = cascade.appendModelFromStyle(styles.inlineStyle, "element.style"); model.setIsAttribute(true); } // 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 ((rule.isInjected || rule.isUserAgent) && !addedAttributesStyle) { // Show element's Style Attributes after all author rules. addedAttributesStyle = true; addAttributesStyle(); } cascade.appendModelFromRule(rule); } if (!addedAttributesStyle) addAttributesStyle(); // Walk the node structure and identify styles with inherited properties. var parentNode = node.parentNode; for (var parentOrdinal = 0; parentOrdinal < styles.inherited.length; ++parentOrdinal) { var parentStyles = styles.inherited[parentOrdinal]; if (parentStyles.inlineStyle) { if (this._containsInherited(parentStyles.inlineStyle)) { var model = cascade.appendModelFromStyle(parentStyles.inlineStyle, WebInspector.UIString("Style Attribute"), parentNode); model.setIsAttribute(true); } } for (var i = parentStyles.matchedCSSRules.length - 1; i >= 0; --i) { var rulePayload = parentStyles.matchedCSSRules[i]; if (!this._containsInherited(rulePayload.style)) continue; cascade.appendModelFromRule(rulePayload, parentNode); } parentNode = parentNode.parentNode; } return cascade; }, _appendTopPadding: function() { var separatorElement = createElement("div"); separatorElement.className = "styles-sidebar-placeholder"; this._sectionsContainer.appendChild(separatorElement); }, /** * @param {number} pseudoId */ _appendSectionPseudoIdSeparator: function(pseudoId) { var separatorElement = createElement("div"); separatorElement.className = "sidebar-separator"; var pseudoName = WebInspector.StylesSidebarPane.PseudoIdNames[pseudoId]; if (pseudoName) separatorElement.textContent = WebInspector.UIString("Pseudo ::%s element", pseudoName); else separatorElement.textContent = WebInspector.UIString("Pseudo element"); this._sectionsContainer.appendChild(separatorElement); }, /** * @param {!WebInspector.DOMNode} node */ _appendSectionInheritedNodeSeparator: function(node) { var separatorElement = createElement("div"); separatorElement.className = "sidebar-separator"; var link = WebInspector.DOMPresentationUtils.linkifyNodeReference(node); separatorElement.createTextChild(WebInspector.UIString("Inherited from") + " "); separatorElement.appendChild(link); this._sectionsContainer.appendChild(separatorElement); }, /** * @param {!WebInspector.SectionCascade} cascade * @return {!Array.<!WebInspector.StylePropertiesSection>} */ _rebuildSectionsForStyleRules: function(cascade) { var sections = []; var lastParentNode = null; for (var sectionModel of cascade.sectionModels()) { var parentNode = sectionModel.parentNode(); if (parentNode && parentNode !== lastParentNode) { lastParentNode = parentNode; this._appendSectionInheritedNodeSeparator(lastParentNode); } var section = new WebInspector.StylePropertiesSection(this, sectionModel); section._markSelectorMatches(); section.onpopulate(); this._sectionsContainer.appendChild(section.element); sections.push(section); } return sections; }, /** * @param {!WebInspector.CSSStyleDeclaration} style * @return {boolean} */ _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; }, /** * @param {!WebInspector.Event} event */ _colorFormatSettingChanged: function(event) { for (var pseudoId in this.sections) { var sections = this.sections[pseudoId]; for (var i = 0; i < sections.length; ++i) sections[i].update(true); } }, /** * @param {?Event} event */ _createNewRuleInViaInspectorStyleSheet: function(event) { var cssModel = this._target.cssModel; cssModel.requestViaInspectorStylesheet(this._node, this._createNewRuleInStyleSheet.bind(this)); }, /** * @param {?WebInspector.CSSStyleSheetHeader} styleSheetHeader */ _createNewRuleInStyleSheet: function(styleSheetHeader) { if (!styleSheetHeader) return; styleSheetHeader.requestContent(onStyleSheetContent.bind(this, styleSheetHeader.id)); /** * @param {string} styleSheetId * @param {string} text * @this {WebInspector.StylesSidebarPane} */ function onStyleSheetContent(styleSheetId, text) { var lines = text.split("\n"); var range = WebInspector.TextRange.createFromLocation(lines.length - 1, lines[lines.length - 1].length); this._addBlankSection(this.sections[0][0], styleSheetId, range); } }, /** * @param {!WebInspector.StylePropertiesSection} insertAfterSection * @param {string} styleSheetId * @param {!WebInspector.TextRange} ruleLocation */ _addBlankSection: function(insertAfterSection, styleSheetId, ruleLocation) { this.expand(); var blankSection = new WebInspector.BlankStylePropertiesSection(this, this._node ? WebInspector.DOMPresentationUtils.simpleSelector(this._node) : "", styleSheetId, ruleLocation, insertAfterSection.styleRule); this._sectionsContainer.insertBefore(blankSection.element, insertAfterSection.element.nextSibling); var index = this.sections[0].indexOf(insertAfterSection); this.sections[0].splice(index + 1, 0, blankSection); blankSection.startEditingSelector(); }, /** * @param {!WebInspector.StylePropertiesSection} section */ 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(); } }, /** * @param {!Event} event */ _toggleElementStatePane: function(event) { event.consume(); var buttonToggled = !this._elementStateButton.classList.contains("toggled"); if (buttonToggled) this.expand(); this._elementStateButton.classList.toggle("toggled", buttonToggled); this._elementStatePane.classList.toggle("expanded", buttonToggled); this._animationsControlButton.classList.remove("toggled"); this._animationsControlPane.classList.remove("expanded"); }, _createElementStatePane: function() { this._elementStatePane = createElement("div"); this._elementStatePane.className = "styles-element-state-pane source-code"; var table = createElement("table"); var inputs = []; this._elementStatePane.inputs = inputs; /** * @param {!Event} event * @this {WebInspector.StylesSidebarPane} */ function clickListener(event) { var node = this._validateNode(); if (!node) return; this._setPseudoClassCallback(node, event.target.state, event.target.checked); } /** * @param {string} state * @return {!Element} * @this {WebInspector.StylesSidebarPane} */ function createCheckbox(state) { var td = createElement("td"); var label = createCheckboxLabel(":" + state); var input = label.checkboxElement; input.state = state; input.addEventListener("click", clickListener.bind(this), false); inputs.push(input); td.appendChild(label); return td; } var tr = table.createChild("tr"); tr.appendChild(createCheckbox.call(this, "active")); tr.appendChild(createCheckbox.call(this, "hover")); tr = table.createChild("tr"); tr.appendChild(createCheckbox.call(this, "focus")); tr.appendChild(createCheckbox.call(this, "visited")); this._elementStatePane.appendChild(table); }, /** * @param {!Event} event */ _toggleAnimationsControlPane: function(event) { event.consume(); var buttonToggled = !this._animationsControlButton.classList.contains("toggled"); if (buttonToggled) this.expand(); this._animationsControlButton.classList.toggle("toggled", buttonToggled); this._animationsControlPane.classList.toggle("expanded", buttonToggled); this._elementStateButton.classList.remove("toggled"); this._elementStatePane.classList.remove("expanded"); }, _updateAnimationsPlaybackRate: function() { /** * @param {?Protocol.Error} error * @param {number} playbackRate * @this {WebInspector.StylesSidebarPane} */ function setPlaybackRate(error, playbackRate) { this._animationsPlaybackSlider.value = WebInspector.AnimationsSidebarPane.GlobalPlaybackRates.indexOf(playbackRate); this._animationsPlaybackLabel.textContent = playbackRate + "x"; } if (this._target) this._target.pageAgent().getAnimationsPlaybackRate(setPlaybackRate.bind(this)); }, _createAnimationsControlPane: function() { /** * @param {!Event} event * @this {WebInspector.StylesSidebarPane} */ function playbackSliderInputHandler(event) { this._animationsPlaybackRate = WebInspector.AnimationsSidebarPane.GlobalPlaybackRates[event.target.value]; this._target.pageAgent().setAnimationsPlaybackRate(this._animationsPaused ? 0 : this._animationsPlaybackRate); this._animationsPlaybackLabel.textContent = this._animationsPlaybackRate + "x"; WebInspector.userMetrics.AnimationsPlaybackRateChanged.record(); } /** * @this {WebInspector.StylesSidebarPane} */ function pauseButtonHandler() { this._animationsPaused = !this._animationsPaused; this._target.pageAgent().setAnimationsPlaybackRate(this._animationsPaused ? 0 : this._animationsPlaybackRate); WebInspector.userMetrics.AnimationsPlaybackRateChanged.record(); this._animationsPauseButton.element.classList.toggle("pause-status-bar-item"); this._animationsPauseButton.element.classList.toggle("play-status-bar-item"); } this._animationsPaused = false; this._animationsPlaybackRate = 1; this._updateAnimationsPlaybackRate(); this._animationsControlPane = createElementWithClass("div", "styles-animations-controls-pane"); var labelElement = createElement("div"); labelElement.createTextChild("Animations"); this._animationsControlPane.appendChild(labelElement); var container = this._animationsControlPane.createChild("div", "animations-controls"); var statusBar = new WebInspector.StatusBar(); this._animationsPauseButton = new WebInspector.StatusBarButton("", "pause-status-bar-item"); statusBar.appendStatusBarItem(this._animationsPauseButton); this._animationsPauseButton.addEventListener("click", pauseButtonHandler.bind(this)); container.appendChild(statusBar.element); this._animationsPlaybackSlider = container.createChild("input"); this._animationsPlaybackSlider.type = "range"; this._animationsPlaybackSlider.min = 0; this._animationsPlaybackSlider.max = WebInspector.AnimationsSidebarPane.GlobalPlaybackRates.length - 1; this._animationsPlaybackSlider.value = this._animationsPlaybackSlider.max; this._animationsPlaybackSlider.addEventListener("input", playbackSliderInputHandler.bind(this)); this._animationsPlaybackLabel = container.createChild("div", "playback-label"); this._animationsPlaybackLabel.createTextChild("1x"); }, /** * @return {?RegExp} */ filterRegex: function() { return this._filterRegex; }, _updateFilter: function() { for (var pseudoId in this.sections) { var sections = this.sections[pseudoId]; for (var i = 0; i < sections.length; ++i) { var section = sections[i]; section._updateFilter(); } } }, /** * @param {!WebInspector.Event} event */ _showUserAgentStylesSettingChanged: function(event) { var showStyles = /** @type {boolean} */ (event.data); this.element.classList.toggle("show-user-styles", showStyles); }, /** * @override */ wasShown: function() { WebInspector.SidebarPane.prototype.wasShown.call(this); this.element.ownerDocument.body.addEventListener("keydown", this._keyDownBound, false); this.element.ownerDocument.body.addEventListener("keyup", this._keyUpBound, false); if (this._updateWhenVisible) { this._rebuildUpdate(); delete this._updateWhenVisible; } }, /** * @override */ willHide: function() { this.element.ownerDocument.body.removeEventListener("keydown", this._keyDownBound, false); this.element.ownerDocument.body.removeEventListener("keyup", this._keyUpBound, false); this._stylesPopoverHelper.hide(); this._discardElementUnderMouse(); WebInspector.SidebarPane.prototype.willHide.call(this); }, _discardElementUnderMouse: function() { if (this._elementUnderMouse) this._elementUnderMouse.classList.remove("styles-panel-hovered"); delete this._elementUnderMouse; }, /** * @param {!Event} event */ _mouseMovedOverElement: function(event) { if (this._elementUnderMouse && event.target !== this._elementUnderMouse) this._discardElementUnderMouse(); this._elementUnderMouse = event.target; if (WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(/** @type {!MouseEvent} */(event))) this._elementUnderMouse.classList.add("styles-panel-hovered"); }, /** * @param {!Event} event */ _keyDown: function(event) { if ((!WebInspector.isMac() && event.keyCode === WebInspector.KeyboardShortcut.Keys.Ctrl.code) || (WebInspector.isMac() && event.keyCode === WebInspector.KeyboardShortcut.Keys.Meta.code)) { if (this._elementUnderMouse) this._elementUnderMouse.classList.add("styles-panel-hovered"); } }, /** * @param {!Event} event */ _keyUp: function(event) { if ((!WebInspector.isMac() && event.keyCode === WebInspector.KeyboardShortcut.Keys.Ctrl.code) || (WebInspector.isMac() && event.keyCode === WebInspector.KeyboardShortcut.Keys.Meta.code)) { this._discardElementUnderMouse(); } }, __proto__: WebInspector.SidebarPane.prototype } /** * @param {string} placeholder * @return {!Element} * @param {function(?RegExp)} filterCallback */ WebInspector.StylesSidebarPane._createPropertyFilterElement = function(placeholder, filterCallback) { var input = createElement("input"); input.type = "text"; input.placeholder = placeholder; function searchHandler() { var regex = input.value ? new RegExp(input.value.escapeForRegExp(), "i") : null; filterCallback(regex); input.parentNode.classList.toggle("styles-filter-engaged", !!input.value); } input.addEventListener("input", searchHandler, false); /** * @param {!Event} event */ function keydownHandler(event) { var Esc = "U+001B"; if (event.keyIdentifier !== Esc || !input.value) return; event.consume(true); input.value = ""; searchHandler(); } input.addEventListener("keydown", keydownHandler, false); return input; } /** * @constructor * @extends {WebInspector.ElementsSidebarPane} */ WebInspector.ComputedStyleSidebarPane = function() { WebInspector.ElementsSidebarPane.call(this, WebInspector.UIString("Computed Style")); WebInspector.settings.showInheritedComputedStyleProperties.addChangeListener(this._showInheritedComputedStyleChanged.bind(this)); this._linkifier = new WebInspector.Linkifier(new WebInspector.Linkifier.DefaultCSSFormatter()); } WebInspector.ComputedStyleSidebarPane.prototype = { _showInheritedComputedStyleChanged: function() { this._computedStyleSection.update(); this._computedStyleSection._rebuildComputedTrace(); }, /** * @override * @param {?WebInspector.DOMNode} node */ setNode: function(node) { if (node) this._target = node.target(); WebInspector.ElementsSidebarPane.prototype.setNode.call(this, node); }, /** * @override * @param {!WebInspector.Throttler.FinishCallback} finishedCallback */ doUpdate: function(finishedCallback) { var promises = [ this._stylesSidebarPane._fetchComputedCascade(), this._stylesSidebarPane._fetchMatchedCascade(), this._stylesSidebarPane._fetchAnimationProperties() ]; Promise.all(promises) .spread(this._innerRebuildUpdate.bind(this)) .then(finishedCallback); }, /** * @param {?WebInspector.SectionCascade} computedCascade @param {?{matched: !WebInspector.SectionCascade, pseudo: !Map.<number, !WebInspector.SectionCascade>}} cascades * @param {!Map.<string, string>} animationProperties */ _innerRebuildUpdate: function(computedCascade, cascades, animationProperties) { this._linkifier.reset(); this.bodyElement.removeChildren(); if (!computedCascade || !cascades) return; var computedStyleRule = computedCascade.sectionModels()[0]; this._computedStyleSection = new WebInspector.ComputedStylePropertiesSection(this, computedStyleRule, cascades.matched, animationProperties); this._computedStyleSection.expand(); this._computedStyleSection._rebuildComputedTrace(); this.bodyElement.appendChild(this._computedStyle