2 * Copyright (C) 2007 Apple Inc. All rights reserved.
3 * Copyright (C) 2009 Joseph Pecoraro
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15 * its contributors may be used to endorse or promote products derived
16 * from this software without specific prior written permission.
18 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 * @extends {WebInspector.SidebarPane}
33 * @param {!WebInspector.ComputedStyleSidebarPane} computedStylePane
34 * @param {function(!DOMAgent.NodeId, string, boolean)} setPseudoClassCallback
36 WebInspector.StylesSidebarPane = function(computedStylePane, setPseudoClassCallback)
38 WebInspector.SidebarPane.call(this, WebInspector.UIString("Styles"));
40 this._elementStateButton = document.createElement("button");
41 this._elementStateButton.className = "pane-title-button element-state";
42 this._elementStateButton.title = WebInspector.UIString("Toggle Element State");
43 this._elementStateButton.addEventListener("click", this._toggleElementStatePane.bind(this), false);
44 this.titleElement.appendChild(this._elementStateButton);
46 var addButton = document.createElement("button");
47 addButton.className = "pane-title-button add";
48 addButton.id = "add-style-button-test-id";
49 addButton.title = WebInspector.UIString("New Style Rule");
50 addButton.addEventListener("click", this._createNewRule.bind(this), false);
51 this.titleElement.appendChild(addButton);
53 this._computedStylePane = computedStylePane;
54 computedStylePane.setHostingPane(this);
55 this._setPseudoClassCallback = setPseudoClassCallback;
56 this.element.addEventListener("contextmenu", this._contextMenuEventFired.bind(this), true);
57 WebInspector.settings.colorFormat.addChangeListener(this._colorFormatSettingChanged.bind(this));
59 this._createElementStatePane();
60 this.bodyElement.appendChild(this._elementStatePane);
61 this._sectionsContainer = document.createElement("div");
62 this.bodyElement.appendChild(this._sectionsContainer);
64 this._spectrumHelper = new WebInspector.SpectrumPopupHelper();
65 this._linkifier = new WebInspector.Linkifier(new WebInspector.Linkifier.DefaultCSSFormatter());
67 WebInspector.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetAdded, this._styleSheetOrMediaQueryResultChanged, this);
68 WebInspector.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetRemoved, this._styleSheetOrMediaQueryResultChanged, this);
69 WebInspector.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetChanged, this._styleSheetOrMediaQueryResultChanged, this);
70 WebInspector.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.MediaQueryResultChanged, this._styleSheetOrMediaQueryResultChanged, this);
71 WebInspector.domModel.addEventListener(WebInspector.DOMModel.Events.AttrModified, this._attributeChanged, this);
72 WebInspector.domModel.addEventListener(WebInspector.DOMModel.Events.AttrRemoved, this._attributeChanged, this);
73 WebInspector.settings.showUserAgentStyles.addChangeListener(this._showUserAgentStylesSettingChanged.bind(this));
74 WebInspector.resourceTreeModel.addEventListener(WebInspector.ResourceTreeModel.EventTypes.FrameResized, this._frameResized, this);
75 this.element.classList.add("styles-pane");
76 this.element.classList.toggle("show-user-styles", WebInspector.settings.showUserAgentStyles.get());
77 this.element.addEventListener("mousemove", this._mouseMovedOverElement.bind(this), false);
78 document.body.addEventListener("keydown", this._keyDown.bind(this), false);
79 document.body.addEventListener("keyup", this._keyUp.bind(this), false);
82 // Keep in sync with RenderStyleConstants.h PseudoId enum. Array below contains pseudo id names for corresponding enum indexes.
83 // First item is empty due to its artificial NOPSEUDO nature in the enum.
84 // FIXME: find a way of generating this mapping or getting it from combination of RenderStyleConstants and CSSSelector.cpp at
86 WebInspector.StylesSidebarPane.PseudoIdNames = [
87 "", "first-line", "first-letter", "before", "after", "selection", "", "-webkit-scrollbar", "-webkit-file-upload-button",
88 "-webkit-input-placeholder", "-webkit-slider-thumb", "-webkit-search-cancel-button", "-webkit-search-decoration",
89 "-webkit-search-results-decoration", "-webkit-search-results-button", "-webkit-media-controls-panel",
90 "-webkit-media-controls-play-button", "-webkit-media-controls-mute-button", "-webkit-media-controls-timeline",
91 "-webkit-media-controls-timeline-container", "-webkit-media-controls-volume-slider",
92 "-webkit-media-controls-volume-slider-container", "-webkit-media-controls-current-time-display",
93 "-webkit-media-controls-time-remaining-display", "-webkit-media-controls-fullscreen-button",
94 "-webkit-media-controls-toggle-closed-captions-button", "-webkit-media-controls-status-display", "-webkit-scrollbar-thumb",
95 "-webkit-scrollbar-button", "-webkit-scrollbar-track", "-webkit-scrollbar-track-piece", "-webkit-scrollbar-corner",
96 "-webkit-resizer", "-webkit-inner-spin-button", "-webkit-outer-spin-button"
99 WebInspector.StylesSidebarPane._colorRegex = /((?:rgb|hsl)a?\([^)]+\)|#[0-9a-fA-F]{6}|#[0-9a-fA-F]{3}|\b\w+\b(?!-))/g;
102 * @param {!WebInspector.CSSProperty} property
104 WebInspector.StylesSidebarPane.createExclamationMark = function(property)
106 var exclamationElement = document.createElement("div");
107 exclamationElement.className = "exclamation-mark" + (WebInspector.StylesSidebarPane._ignoreErrorsForProperty(property) ? "" : " warning-icon-small");
108 exclamationElement.title = WebInspector.CSSMetadata.cssPropertiesMetainfo.keySet()[property.name.toLowerCase()] ? WebInspector.UIString("Invalid property value.") : WebInspector.UIString("Unknown property name.");
109 return exclamationElement;
113 * @param {!WebInspector.Color} color
115 WebInspector.StylesSidebarPane._colorFormat = function(color)
117 const cf = WebInspector.Color.Format;
119 var formatSetting = WebInspector.settings.colorFormat.get();
120 if (formatSetting === cf.Original)
121 format = cf.Original;
122 else if (formatSetting === cf.RGB)
123 format = (color.hasAlpha() ? cf.RGBA : cf.RGB);
124 else if (formatSetting === cf.HSL)
125 format = (color.hasAlpha() ? cf.HSLA : cf.HSL);
126 else if (!color.hasAlpha())
127 format = (color.canBeShortHex() ? cf.ShortHEX : cf.HEX);
135 * @param {!WebInspector.CSSProperty} property
137 WebInspector.StylesSidebarPane._ignoreErrorsForProperty = function(property) {
138 function hasUnknownVendorPrefix(string)
140 return !string.startsWith("-webkit-") && /^[-_][\w\d]+-\w/.test(string);
143 var name = property.name.toLowerCase();
146 if (name.charAt(0) === "_")
149 // IE has a different format for this.
150 if (name === "filter")
153 // Common IE-specific property prefix.
154 if (name.startsWith("scrollbar-"))
156 if (hasUnknownVendorPrefix(name))
159 var value = property.value.toLowerCase();
162 if (value.endsWith("\9"))
164 if (hasUnknownVendorPrefix(value))
170 WebInspector.StylesSidebarPane.prototype = {
172 * @param {?Event} event
174 _contextMenuEventFired: function(event)
176 // We start editing upon click -> default navigation to resources panel is not available
177 // Hence we add a soft context menu for hrefs.
178 var contextMenu = new WebInspector.ContextMenu(event);
179 contextMenu.appendApplicableItems(/** @type {!Node} */ (event.target));
184 * @param {!Element} matchedStylesElement
185 * @param {!Element} computedStylesElement
187 setFilterBoxContainers: function(matchedStylesElement, computedStylesElement)
189 matchedStylesElement.appendChild(this._createCSSFilterControl());
190 this._computedStylePane.setFilterBoxContainer(computedStylesElement);
196 _createCSSFilterControl: function()
198 var filterInput = this._createPropertyFilterElement(false, searchHandler.bind(this));
201 * @param {?RegExp} regex
202 * @this {WebInspector.StylesSidebarPane}
204 function searchHandler(regex)
206 this._filterRegex = regex;
212 get _forcedPseudoClasses()
214 return this.node ? (this.node.getUserProperty("pseudoState") || undefined) : undefined;
217 _updateForcedPseudoStateInputs: function()
222 var hasPseudoType = !!this.node.pseudoType();
223 this._elementStateButton.classList.toggle("hidden", hasPseudoType);
224 this._elementStatePane.classList.toggle("expanded", !hasPseudoType && this._elementStateButton.classList.contains("toggled"));
226 var nodePseudoState = this._forcedPseudoClasses;
227 if (!nodePseudoState)
228 nodePseudoState = [];
230 var inputs = this._elementStatePane.inputs;
231 for (var i = 0; i < inputs.length; ++i)
232 inputs[i].checked = nodePseudoState.indexOf(inputs[i].state) >= 0;
236 * @param {?WebInspector.DOMNode} node
237 * @param {boolean=} forceUpdate
239 update: function(node, forceUpdate)
241 this._spectrumHelper.hide();
242 this._discardElementUnderMouse();
249 if (!forceUpdate && (node === this.node))
252 if (node && node.nodeType() === Node.TEXT_NODE && node.parentNode)
253 node = node.parentNode;
255 if (node && node.nodeType() !== Node.ELEMENT_NODE)
263 this._updateForcedPseudoStateInputs();
266 this._refreshUpdate();
268 this._rebuildUpdate();
272 * @param {!WebInspector.StylePropertiesSection=} editedSection
273 * @param {boolean=} forceFetchComputedStyle
274 * @param {function()=} userCallback
276 _refreshUpdate: function(editedSection, forceFetchComputedStyle, userCallback)
278 var callbackWrapper = function()
280 if (this._filterRegex)
281 this._updateFilter(false);
286 if (this._refreshUpdateInProgress) {
287 this._lastNodeForInnerRefresh = this.node;
291 var node = this._validateNode(userCallback);
296 * @param {?WebInspector.CSSStyleDeclaration} computedStyle
297 * @this {WebInspector.StylesSidebarPane}
299 function computedStyleCallback(computedStyle)
301 delete this._refreshUpdateInProgress;
303 if (this._lastNodeForInnerRefresh) {
304 delete this._lastNodeForInnerRefresh;
305 this._refreshUpdate(editedSection, forceFetchComputedStyle, callbackWrapper);
309 if (this.node === node && computedStyle)
310 this._innerRefreshUpdate(node, computedStyle, editedSection);
315 if (this._computedStylePane.isShowing() || forceFetchComputedStyle) {
316 this._refreshUpdateInProgress = true;
317 WebInspector.cssModel.getComputedStyleAsync(node.id, computedStyleCallback.bind(this));
319 this._innerRefreshUpdate(node, null, editedSection);
324 _rebuildUpdate: function()
326 if (this._rebuildUpdateInProgress) {
327 this._lastNodeForInnerRebuild = this.node;
331 var node = this._validateNode();
335 this._rebuildUpdateInProgress = true;
337 var resultStyles = {};
340 * @param {?*} matchedResult
341 * @this {WebInspector.StylesSidebarPane}
343 function stylesCallback(matchedResult)
345 delete this._rebuildUpdateInProgress;
347 var lastNodeForRebuild = this._lastNodeForInnerRebuild;
348 if (lastNodeForRebuild) {
349 delete this._lastNodeForInnerRebuild;
350 if (lastNodeForRebuild !== this.node) {
351 this._rebuildUpdate();
356 if (matchedResult && this.node === node) {
357 resultStyles.matchedCSSRules = matchedResult.matchedCSSRules;
358 resultStyles.pseudoElements = matchedResult.pseudoElements;
359 resultStyles.inherited = matchedResult.inherited;
360 this._innerRebuildUpdate(node, resultStyles);
363 if (lastNodeForRebuild) {
364 // lastNodeForRebuild is the same as this.node - another rebuild has been requested.
365 this._rebuildUpdate();
371 * @param {?WebInspector.CSSStyleDeclaration} inlineStyle
372 * @param {?WebInspector.CSSStyleDeclaration} attributesStyle
374 function inlineCallback(inlineStyle, attributesStyle)
376 resultStyles.inlineStyle = inlineStyle;
377 resultStyles.attributesStyle = attributesStyle;
381 * @param {?WebInspector.CSSStyleDeclaration} computedStyle
383 function computedCallback(computedStyle)
385 resultStyles.computedStyle = computedStyle;
388 if (this._computedStylePane.isShowing())
389 WebInspector.cssModel.getComputedStyleAsync(node.id, computedCallback);
390 WebInspector.cssModel.getInlineStylesAsync(node.id, inlineCallback);
391 WebInspector.cssModel.getMatchedStylesAsync(node.id, true, true, stylesCallback.bind(this));
395 * @param {function()=} userCallback
397 _validateNode: function(userCallback)
400 this._sectionsContainer.removeChildren();
401 this._computedStylePane.bodyElement.removeChildren();
410 _styleSheetOrMediaQueryResultChanged: function()
412 if (this._userOperation || this._isEditingStyle)
415 this._rebuildUpdate();
418 _frameResized: function()
421 * @this {WebInspector.StylesSidebarPane}
423 function refreshContents()
425 this._rebuildUpdate();
426 delete this._activeTimer;
429 if (this._activeTimer)
430 clearTimeout(this._activeTimer);
432 this._activeTimer = setTimeout(refreshContents.bind(this), 100);
435 _attributeChanged: function(event)
437 // Any attribute removal or modification can affect the styles of "related" nodes.
438 // Do not touch the styles if they are being edited.
439 if (this._isEditingStyle || this._userOperation)
442 if (!this._canAffectCurrentStyles(event.data.node))
445 this._rebuildUpdate();
448 _canAffectCurrentStyles: function(node)
450 return this.node && (this.node === node || node.parentNode === this.node.parentNode || node.isAncestor(this.node));
453 _innerRefreshUpdate: function(node, computedStyle, editedSection)
455 for (var pseudoId in this.sections) {
456 var styleRules = this._refreshStyleRules(this.sections[pseudoId], computedStyle);
457 var usedProperties = {};
458 this._markUsedProperties(styleRules, usedProperties);
459 this._refreshSectionsForStyleRules(styleRules, usedProperties, editedSection);
462 this.sections[0][0].rebuildComputedTrace(this.sections[0]);
464 this._nodeStylesUpdatedForTest(node, false);
467 _innerRebuildUpdate: function(node, styles)
469 this._sectionsContainer.removeChildren();
470 this._computedStylePane.bodyElement.removeChildren();
471 this._linkifier.reset();
473 var styleRules = this._rebuildStyleRules(node, styles);
474 var usedProperties = {};
475 this._markUsedProperties(styleRules, usedProperties);
476 this.sections[0] = this._rebuildSectionsForStyleRules(styleRules, usedProperties, null);
477 var anchorElement = this.sections[0].inheritedPropertiesSeparatorElement;
479 if (styles.computedStyle)
480 this.sections[0][0].rebuildComputedTrace(this.sections[0]);
482 for (var i = 0; i < styles.pseudoElements.length; ++i) {
483 var pseudoElementCSSRules = styles.pseudoElements[i];
486 var pseudoId = pseudoElementCSSRules.pseudoId;
488 var entry = { isStyleSeparator: true, pseudoId: pseudoId };
489 styleRules.push(entry);
491 // Add rules in reverse order to match the cascade order.
492 for (var j = pseudoElementCSSRules.rules.length - 1; j >= 0; --j) {
493 var rule = pseudoElementCSSRules.rules[j];
494 styleRules.push({ style: rule.style, selectorText: rule.selectorText, media: rule.media, sourceURL: rule.resourceURL(), rule: rule, editable: !!(rule.style && rule.style.id) });
497 this._markUsedProperties(styleRules, usedProperties);
498 this.sections[pseudoId] = this._rebuildSectionsForStyleRules(styleRules, usedProperties, anchorElement);
501 if (this._filterRegex)
502 this._updateFilter(false);
503 this._nodeStylesUpdatedForTest(node, true);
506 _nodeStylesUpdatedForTest: function(node, rebuild)
508 // Tests override this method.
511 _refreshStyleRules: function(sections, computedStyle)
513 var nodeComputedStyle = computedStyle;
515 for (var i = 0; sections && i < sections.length; ++i) {
516 var section = sections[i];
519 if (section.computedStyle)
520 section.styleRule.style = nodeComputedStyle;
521 var styleRule = { section: section, style: section.styleRule.style, computedStyle: section.computedStyle, rule: section.rule, editable: !!(section.styleRule.style && section.styleRule.style.id),
522 isAttribute: section.styleRule.isAttribute, isInherited: section.styleRule.isInherited, parentNode: section.styleRule.parentNode };
523 styleRules.push(styleRule);
528 _rebuildStyleRules: function(node, styles)
530 var nodeComputedStyle = styles.computedStyle;
535 function addAttributesStyle()
537 if (!styles.attributesStyle)
539 var attrStyle = { style: styles.attributesStyle, editable: false };
540 attrStyle.selectorText = node.nodeNameInCorrectCase() + "[" + WebInspector.UIString("Attributes Style") + "]";
541 styleRules.push(attrStyle);
544 styleRules.push({ computedStyle: true, selectorText: "", style: nodeComputedStyle, editable: false });
546 if (!!node.pseudoType())
547 styleRules.push({ isStyleSeparator: true, isPlaceholder: true });
549 // Inline style has the greatest specificity.
550 if (styles.inlineStyle && node.nodeType() === Node.ELEMENT_NODE) {
551 var inlineStyle = { selectorText: "element.style", style: styles.inlineStyle, isAttribute: true };
552 styleRules.push(inlineStyle);
555 // Add rules in reverse order to match the cascade order.
556 var addedAttributesStyle;
557 for (var i = styles.matchedCSSRules.length - 1; i >= 0; --i) {
558 var rule = styles.matchedCSSRules[i];
559 if ((rule.isUser || rule.isUserAgent) && !addedAttributesStyle) {
560 // Show element's Style Attributes after all author rules.
561 addedAttributesStyle = true;
562 addAttributesStyle();
564 styleRules.push({ style: rule.style, selectorText: rule.selectorText, media: rule.media, sourceURL: rule.resourceURL(), rule: rule, editable: !!(rule.style && rule.style.id) });
567 if (!addedAttributesStyle)
568 addAttributesStyle();
570 // Walk the node structure and identify styles with inherited properties.
571 var parentNode = node.parentNode;
572 function insertInheritedNodeSeparator(node)
575 entry.isStyleSeparator = true;
577 styleRules.push(entry);
580 for (var parentOrdinal = 0; parentOrdinal < styles.inherited.length; ++parentOrdinal) {
581 var parentStyles = styles.inherited[parentOrdinal];
582 var separatorInserted = false;
583 if (parentStyles.inlineStyle) {
584 if (this._containsInherited(parentStyles.inlineStyle)) {
585 var inlineStyle = { selectorText: WebInspector.UIString("Style Attribute"), style: parentStyles.inlineStyle, isAttribute: true, isInherited: true, parentNode: parentNode };
586 if (!separatorInserted) {
587 insertInheritedNodeSeparator(parentNode);
588 separatorInserted = true;
590 styleRules.push(inlineStyle);
594 for (var i = parentStyles.matchedCSSRules.length - 1; i >= 0; --i) {
595 var rulePayload = parentStyles.matchedCSSRules[i];
596 if (!this._containsInherited(rulePayload.style))
598 var rule = rulePayload;
600 if (!separatorInserted) {
601 insertInheritedNodeSeparator(parentNode);
602 separatorInserted = true;
604 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) });
606 parentNode = parentNode.parentNode;
611 _markUsedProperties: function(styleRules, usedProperties)
613 var foundImportantProperties = {};
614 var propertyToEffectiveRule = {};
615 var inheritedPropertyToNode = {};
616 for (var i = 0; i < styleRules.length; ++i) {
617 var styleRule = styleRules[i];
618 if (styleRule.computedStyle || styleRule.isStyleSeparator)
620 if (styleRule.section && styleRule.section.noAffect)
623 styleRule.usedProperties = {};
625 var style = styleRule.style;
626 var allProperties = style.allProperties;
627 for (var j = 0; j < allProperties.length; ++j) {
628 var property = allProperties[j];
629 if (!property.isLive || !property.parsedOk)
632 // Do not pick non-inherited properties from inherited styles.
633 if (styleRule.isInherited && !WebInspector.CSSMetadata.isPropertyInherited(property.name))
636 var canonicalName = WebInspector.CSSMetadata.canonicalPropertyName(property.name);
637 if (foundImportantProperties.hasOwnProperty(canonicalName))
640 if (!property.important && usedProperties.hasOwnProperty(canonicalName))
643 var isKnownProperty = propertyToEffectiveRule.hasOwnProperty(canonicalName);
644 if (!isKnownProperty && styleRule.isInherited && !inheritedPropertyToNode[canonicalName])
645 inheritedPropertyToNode[canonicalName] = styleRule.parentNode;
647 if (property.important) {
648 if (styleRule.isInherited && isKnownProperty && styleRule.parentNode !== inheritedPropertyToNode[canonicalName])
651 foundImportantProperties[canonicalName] = true;
653 delete propertyToEffectiveRule[canonicalName].usedProperties[canonicalName];
656 styleRule.usedProperties[canonicalName] = true;
657 usedProperties[canonicalName] = true;
658 propertyToEffectiveRule[canonicalName] = styleRule;
663 _refreshSectionsForStyleRules: function(styleRules, usedProperties, editedSection)
665 // Walk the style rules and update the sections with new overloaded and used properties.
666 for (var i = 0; i < styleRules.length; ++i) {
667 var styleRule = styleRules[i];
668 var section = styleRule.section;
669 if (styleRule.computedStyle) {
670 section._usedProperties = usedProperties;
673 section._usedProperties = styleRule.usedProperties;
674 section.update(section === editedSection);
680 * @param {!Array.<!Object>} styleRules
681 * @param {!Object.<string, boolean>} usedProperties
682 * @param {?Element} anchorElement
684 _rebuildSectionsForStyleRules: function(styleRules, usedProperties, anchorElement)
686 // Make a property section for each style rule.
688 for (var i = 0; i < styleRules.length; ++i) {
689 var styleRule = styleRules[i];
690 if (styleRule.isStyleSeparator) {
691 var separatorElement = document.createElement("div");
692 if (styleRule.isPlaceholder) {
693 separatorElement.className = "styles-sidebar-placeholder";
694 this._sectionsContainer.insertBefore(separatorElement, anchorElement);
697 separatorElement.className = "sidebar-separator";
698 if (styleRule.node) {
699 var link = WebInspector.DOMPresentationUtils.linkifyNodeReference(styleRule.node);
700 separatorElement.appendChild(document.createTextNode(WebInspector.UIString("Inherited from") + " "));
701 separatorElement.appendChild(link);
702 if (!sections.inheritedPropertiesSeparatorElement)
703 sections.inheritedPropertiesSeparatorElement = separatorElement;
704 } else if ("pseudoId" in styleRule) {
705 var pseudoName = WebInspector.StylesSidebarPane.PseudoIdNames[styleRule.pseudoId];
707 separatorElement.textContent = WebInspector.UIString("Pseudo ::%s element", pseudoName);
709 separatorElement.textContent = WebInspector.UIString("Pseudo element");
711 separatorElement.textContent = styleRule.text;
712 this._sectionsContainer.insertBefore(separatorElement, anchorElement);
715 var computedStyle = styleRule.computedStyle;
717 // Default editable to true if it was omitted.
718 var editable = styleRule.editable;
719 if (typeof editable === "undefined")
723 var section = new WebInspector.ComputedStylePropertiesSection(this, styleRule, usedProperties);
725 var section = new WebInspector.StylePropertiesSection(this, styleRule, editable, styleRule.isInherited);
726 section._markSelectorMatches();
728 section.expanded = true;
731 this._computedStylePane.bodyElement.appendChild(section.element);
733 this._sectionsContainer.insertBefore(section.element, anchorElement);
734 sections.push(section);
739 _containsInherited: function(style)
741 var properties = style.allProperties;
742 for (var i = 0; i < properties.length; ++i) {
743 var property = properties[i];
744 // Does this style contain non-overridden inherited property?
745 if (property.isLive && WebInspector.CSSMetadata.isPropertyInherited(property.name))
751 _colorFormatSettingChanged: function(event)
753 for (var pseudoId in this.sections) {
754 var sections = this.sections[pseudoId];
755 for (var i = 0; i < sections.length; ++i)
756 sections[i].update(true);
760 _createNewRule: function(event)
764 this.addBlankSection().startEditingSelector();
768 * @return {!WebInspector.BlankStylePropertiesSection}
770 addBlankSection: function()
772 var blankSection = new WebInspector.BlankStylePropertiesSection(this, this.node ? WebInspector.DOMPresentationUtils.simpleSelector(this.node) : "");
774 var elementStyleSection = this.sections[0][1];
775 this._sectionsContainer.insertBefore(blankSection.element, elementStyleSection.element.nextSibling);
777 this.sections[0].splice(2, 0, blankSection);
782 removeSection: function(section)
784 for (var pseudoId in this.sections) {
785 var sections = this.sections[pseudoId];
786 var index = sections.indexOf(section);
789 sections.splice(index, 1);
790 section.element.remove();
794 _toggleElementStatePane: function(event)
798 var buttonToggled = !this._elementStateButton.classList.contains("toggled");
801 this._elementStateButton.classList.toggle("toggled", buttonToggled);
802 this._elementStatePane.classList.toggle("expanded", buttonToggled);
805 _createElementStatePane: function()
807 this._elementStatePane = document.createElement("div");
808 this._elementStatePane.className = "styles-element-state-pane source-code";
809 var table = document.createElement("table");
812 this._elementStatePane.inputs = inputs;
815 * @param {?Event} event
816 * @this {WebInspector.StylesSidebarPane}
818 function clickListener(event)
820 var node = this._validateNode();
823 this._setPseudoClassCallback(node.id, event.target.state, event.target.checked);
827 * @param {string} state
829 * @this {WebInspector.StylesSidebarPane}
831 function createCheckbox(state)
833 var td = document.createElement("td");
834 var label = document.createElement("label");
835 var input = document.createElement("input");
836 input.type = "checkbox";
838 input.addEventListener("click", clickListener.bind(this), false);
840 label.appendChild(input);
841 label.appendChild(document.createTextNode(":" + state));
842 td.appendChild(label);
846 var tr = table.createChild("tr");
847 tr.appendChild(createCheckbox.call(this, "active"));
848 tr.appendChild(createCheckbox.call(this, "hover"));
850 tr = table.createChild("tr");
851 tr.appendChild(createCheckbox.call(this, "focus"));
852 tr.appendChild(createCheckbox.call(this, "visited"));
854 this._elementStatePane.appendChild(table);
860 filterRegex: function()
862 return this._filterRegex;
866 * @param {boolean} isComputedStyleFilter
868 * @param {function(?RegExp)} filterCallback
870 _createPropertyFilterElement: function(isComputedStyleFilter, filterCallback)
872 var input = document.createElement("input");
874 input.placeholder = isComputedStyleFilter ? WebInspector.UIString("Filter") : WebInspector.UIString("Find in Styles");
875 var boundSearchHandler = searchHandler.bind(this);
878 * @this {WebInspector.StylesSidebarPane}
880 function searchHandler()
882 var regex = input.value ? new RegExp(input.value.escapeForRegExp(), "i") : null;
883 filterCallback(regex);
884 input.parentNode.classList.toggle("styles-filter-engaged", !!input.value);
885 this._updateFilter(isComputedStyleFilter);
887 input.addEventListener("input", boundSearchHandler, false);
890 * @param {?Event} event
892 function keydownHandler(event)
895 if (event.keyIdentifier !== Esc || !input.value)
899 boundSearchHandler();
901 input.addEventListener("keydown", keydownHandler, false);
907 * @param {boolean} isComputedStyleFilter
909 _updateFilter: function(isComputedStyleFilter)
911 for (var pseudoId in this.sections) {
912 var sections = this.sections[pseudoId];
913 for (var i = 0; i < sections.length; ++i) {
914 var section = sections[i];
915 if (isComputedStyleFilter !== !!section.computedStyle)
917 section._updateFilter();
923 * @param {!WebInspector.Event} event
925 _showUserAgentStylesSettingChanged: function(event)
927 var showStyles = /** @type {boolean} */ (event.data);
928 this.element.classList.toggle("show-user-styles", showStyles);
933 this._spectrumHelper.hide();
934 this._discardElementUnderMouse();
937 _discardElementUnderMouse: function()
939 if (this._elementUnderMouse)
940 this._elementUnderMouse.classList.remove("styles-panel-hovered");
941 delete this._elementUnderMouse;
944 _mouseMovedOverElement: function(e)
946 if (this._elementUnderMouse && e.target !== this._elementUnderMouse)
947 this._discardElementUnderMouse();
948 this._elementUnderMouse = e.target;
949 if (WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(e))
950 this._elementUnderMouse.classList.add("styles-panel-hovered");
953 _keyDown: function(e)
955 if ((!WebInspector.isMac() && e.keyCode === WebInspector.KeyboardShortcut.Keys.Ctrl.code) ||
956 (WebInspector.isMac() && e.keyCode === WebInspector.KeyboardShortcut.Keys.Meta.code)) {
957 if (this._elementUnderMouse)
958 this._elementUnderMouse.classList.add("styles-panel-hovered");
964 if ((!WebInspector.isMac() && e.keyCode === WebInspector.KeyboardShortcut.Keys.Ctrl.code) ||
965 (WebInspector.isMac() && e.keyCode === WebInspector.KeyboardShortcut.Keys.Meta.code)) {
966 this._discardElementUnderMouse();
970 __proto__: WebInspector.SidebarPane.prototype
975 * @extends {WebInspector.SidebarPane}
977 WebInspector.ComputedStyleSidebarPane = function()
979 WebInspector.SidebarPane.call(this, WebInspector.UIString("Computed Style"));
982 WebInspector.ComputedStyleSidebarPane.prototype = {
984 * @param {!WebInspector.StylesSidebarPane} pane
986 setHostingPane: function(pane)
988 this._stylesSidebarPane = pane;
991 setFilterBoxContainer: function(element)
993 element.appendChild(this._stylesSidebarPane._createPropertyFilterElement(true, filterCallback.bind(this)));
996 * @param {?RegExp} regex
997 * @this {WebInspector.ComputedStyleSidebarPane}
999 function filterCallback(regex)
1001 this._filterRegex = regex;
1005 wasShown: function()
1007 WebInspector.SidebarPane.prototype.wasShown.call(this);
1008 if (!this._hasFreshContent)
1009 this.prepareContent();
1013 * @param {function()=} callback
1015 prepareContent: function(callback)
1018 * @this {WebInspector.ComputedStyleSidebarPane}
1020 function wrappedCallback() {
1021 this._hasFreshContent = true;
1024 delete this._hasFreshContent;
1026 this._stylesSidebarPane._refreshUpdate(null, true, wrappedCallback.bind(this));
1032 filterRegex: function()
1034 return this._filterRegex;
1037 __proto__: WebInspector.SidebarPane.prototype
1042 * @extends {WebInspector.PropertiesSection}
1043 * @param {!WebInspector.StylesSidebarPane} parentPane
1044 * @param {!Object} styleRule
1045 * @param {boolean} editable
1046 * @param {boolean} isInherited
1048 WebInspector.StylePropertiesSection = function(parentPane, styleRule, editable, isInherited)
1050 WebInspector.PropertiesSection.call(this, "");
1052 this._parentPane = parentPane;
1053 this.styleRule = styleRule;
1054 this.rule = this.styleRule.rule;
1055 this.editable = editable;
1056 this.isInherited = isInherited;
1058 var extraClasses = (this.rule && (this.rule.isUser || this.rule.isUserAgent) ? " user-rule" : "");
1059 this.element.className = "styles-section matched-styles monospace" + extraClasses;
1060 // We don't really use properties' disclosure.
1061 this.propertiesElement.classList.remove("properties-tree");
1063 if (styleRule.media) {
1064 for (var i = styleRule.media.length - 1; i >= 0; --i) {
1065 var media = styleRule.media[i];
1066 var mediaDataElement = this.titleElement.createChild("div", "media");
1068 switch (media.source) {
1069 case WebInspector.CSSMedia.Source.LINKED_SHEET:
1070 case WebInspector.CSSMedia.Source.INLINE_SHEET:
1071 mediaText = "media=\"" + media.text + "\"";
1073 case WebInspector.CSSMedia.Source.MEDIA_RULE:
1074 mediaText = "@media " + media.text;
1076 case WebInspector.CSSMedia.Source.IMPORT_RULE:
1077 mediaText = "@import " + media.text;
1081 if (media.sourceURL) {
1082 var refElement = mediaDataElement.createChild("div", "subtitle");
1086 mediaHeader = media.header();
1088 var lineNumber = media.lineNumberInSource();
1089 var columnNumber = media.columnNumberInSource();
1090 console.assert(typeof lineNumber !== "undefined" && typeof columnNumber !== "undefined");
1091 rawLocation = new WebInspector.CSSLocation(media.sourceURL, lineNumber, columnNumber);
1097 anchor = this._parentPane._linkifier.linkifyCSSLocation(mediaHeader.id, rawLocation);
1099 // The "linkedStylesheet" case.
1100 anchor = WebInspector.linkifyResourceAsNode(media.sourceURL, undefined, "subtitle", media.sourceURL);
1102 anchor.style.float = "right";
1103 refElement.appendChild(anchor);
1106 var mediaTextElement = mediaDataElement.createChild("span");
1107 mediaTextElement.textContent = mediaText;
1108 mediaTextElement.title = media.text;
1112 var selectorContainer = document.createElement("div");
1113 this._selectorElement = document.createElement("span");
1114 this._selectorElement.textContent = styleRule.selectorText;
1115 selectorContainer.appendChild(this._selectorElement);
1117 var openBrace = document.createElement("span");
1118 openBrace.textContent = " {";
1119 selectorContainer.appendChild(openBrace);
1120 selectorContainer.addEventListener("mousedown", this._handleEmptySpaceMouseDown.bind(this), false);
1121 selectorContainer.addEventListener("click", this._handleSelectorContainerClick.bind(this), false);
1123 var closeBrace = document.createElement("div");
1124 closeBrace.textContent = "}";
1125 this.element.appendChild(closeBrace);
1127 this._selectorElement.addEventListener("click", this._handleSelectorClick.bind(this), false);
1128 this.element.addEventListener("mousedown", this._handleEmptySpaceMouseDown.bind(this), false);
1129 this.element.addEventListener("click", this._handleEmptySpaceClick.bind(this), false);
1132 // Prevent editing the user agent and user rules.
1133 if (this.rule.isUserAgent || this.rule.isUser)
1134 this.editable = false;
1136 // Check this is a real CSSRule, not a bogus object coming from WebInspector.BlankStylePropertiesSection.
1138 this.navigable = !!this.rule.resourceURL();
1140 this.titleElement.classList.add("styles-selector");
1143 this._usedProperties = styleRule.usedProperties;
1145 this._selectorRefElement = document.createElement("div");
1146 this._selectorRefElement.className = "subtitle";
1147 this._updateRuleOrigin();
1148 selectorContainer.insertBefore(this._selectorRefElement, selectorContainer.firstChild);
1149 this.titleElement.appendChild(selectorContainer);
1150 this._selectorContainer = selectorContainer;
1153 this.element.classList.add("styles-show-inherited"); // This one is related to inherited rules, not computed style.
1156 this.element.classList.add("navigable");
1159 this.element.classList.add("read-only");
1162 WebInspector.StylePropertiesSection.prototype = {
1165 return this._parentPane;
1168 collapse: function()
1170 // Overriding with empty body.
1173 handleClick: function()
1175 // Avoid consuming events.
1179 * @param {string} propertyName
1182 isPropertyInherited: function(propertyName)
1184 if (this.isInherited) {
1185 // While rendering inherited stylesheet, reverse meaning of this property.
1186 // Render truly inherited properties with black, i.e. return them as non-inherited.
1187 return !WebInspector.CSSMetadata.isPropertyInherited(propertyName);
1193 * @param {string} propertyName
1194 * @param {boolean=} isShorthand
1197 isPropertyOverloaded: function(propertyName, isShorthand)
1199 if (!this._usedProperties || this.noAffect)
1202 if (this.isInherited && !WebInspector.CSSMetadata.isPropertyInherited(propertyName)) {
1203 // In the inherited sections, only show overrides for the potentially inherited properties.
1207 var canonicalName = WebInspector.CSSMetadata.canonicalPropertyName(propertyName);
1208 var used = (canonicalName in this._usedProperties);
1209 if (used || !isShorthand)
1212 // Find out if any of the individual longhand properties of the shorthand
1213 // are used, if none are then the shorthand is overloaded too.
1214 var longhandProperties = this.styleRule.style.longhandProperties(propertyName);
1215 for (var j = 0; j < longhandProperties.length; ++j) {
1216 var individualProperty = longhandProperties[j];
1217 if (WebInspector.CSSMetadata.canonicalPropertyName(individualProperty.name) in this._usedProperties)
1225 * @return {?WebInspector.StylePropertiesSection}
1227 nextEditableSibling: function()
1229 var curSection = this;
1231 curSection = curSection.nextSibling;
1232 } while (curSection && !curSection.editable);
1235 curSection = this.firstSibling;
1236 while (curSection && !curSection.editable)
1237 curSection = curSection.nextSibling;
1240 return (curSection && curSection.editable) ? curSection : null;
1244 * @return {?WebInspector.StylePropertiesSection}
1246 previousEditableSibling: function()
1248 var curSection = this;
1250 curSection = curSection.previousSibling;
1251 } while (curSection && !curSection.editable);
1254 curSection = this.lastSibling;
1255 while (curSection && !curSection.editable)
1256 curSection = curSection.previousSibling;
1259 return (curSection && curSection.editable) ? curSection : null;
1262 update: function(full)
1264 if (this.styleRule.selectorText)
1265 this._selectorElement.textContent = this.styleRule.selectorText;
1266 this._markSelectorMatches();
1268 this.propertiesTreeOutline.removeChildren();
1269 this.populated = false;
1271 var child = this.propertiesTreeOutline.children[0];
1273 child.overloaded = this.isPropertyOverloaded(child.name, child.isShorthand);
1274 child = child.traverseNextTreeElement(false, null, true);
1280 afterUpdate: function()
1282 if (this._afterUpdate) {
1283 this._afterUpdate(this);
1284 delete this._afterUpdate;
1288 onpopulate: function()
1290 var style = this.styleRule.style;
1291 var allProperties = style.allProperties;
1292 this.uniqueProperties = [];
1294 var styleHasEditableSource = this.editable && !!style.range;
1295 if (styleHasEditableSource) {
1296 for (var i = 0; i < allProperties.length; ++i) {
1297 var property = allProperties[i];
1298 this.uniqueProperties.push(property);
1299 if (property.styleBased)
1302 var isShorthand = !!WebInspector.CSSMetadata.cssPropertiesMetainfo.longhands(property.name);
1303 var inherited = this.isPropertyInherited(property.name);
1304 var overloaded = property.inactive || this.isPropertyOverloaded(property.name);
1305 var item = new WebInspector.StylePropertyTreeElement(this._parentPane, this.styleRule, style, property, isShorthand, inherited, overloaded);
1306 this.propertiesTreeOutline.appendChild(item);
1311 var generatedShorthands = {};
1312 // For style-based properties, generate shorthands with values when possible.
1313 for (var i = 0; i < allProperties.length; ++i) {
1314 var property = allProperties[i];
1315 this.uniqueProperties.push(property);
1316 var isShorthand = !!WebInspector.CSSMetadata.cssPropertiesMetainfo.longhands(property.name);
1318 // For style-based properties, try generating shorthands.
1319 var shorthands = isShorthand ? null : WebInspector.CSSMetadata.cssPropertiesMetainfo.shorthands(property.name);
1320 var shorthandPropertyAvailable = false;
1321 for (var j = 0; shorthands && !shorthandPropertyAvailable && j < shorthands.length; ++j) {
1322 var shorthand = shorthands[j];
1323 if (shorthand in generatedShorthands) {
1324 shorthandPropertyAvailable = true;
1325 continue; // There already is a shorthand this longhands falls under.
1327 if (style.getLiveProperty(shorthand)) {
1328 shorthandPropertyAvailable = true;
1329 continue; // There is an explict shorthand property this longhands falls under.
1331 if (!style.shorthandValue(shorthand)) {
1332 shorthandPropertyAvailable = false;
1333 continue; // Never generate synthetic shorthands when no value is available.
1336 // Generate synthetic shorthand we have a value for.
1337 var shorthandProperty = new WebInspector.CSSProperty(style, style.allProperties.length, shorthand, style.shorthandValue(shorthand), false, false, true, true);
1338 var overloaded = property.inactive || this.isPropertyOverloaded(property.name, true);
1339 var item = new WebInspector.StylePropertyTreeElement(this._parentPane, this.styleRule, style, shorthandProperty, /* isShorthand */ true, /* inherited */ false, overloaded);
1340 this.propertiesTreeOutline.appendChild(item);
1341 generatedShorthands[shorthand] = shorthandProperty;
1342 shorthandPropertyAvailable = true;
1344 if (shorthandPropertyAvailable)
1345 continue; // Shorthand for the property found.
1347 var inherited = this.isPropertyInherited(property.name);
1348 var overloaded = property.inactive || this.isPropertyOverloaded(property.name, isShorthand);
1349 var item = new WebInspector.StylePropertyTreeElement(this._parentPane, this.styleRule, style, property, isShorthand, inherited, overloaded);
1350 this.propertiesTreeOutline.appendChild(item);
1354 _updateFilter: function()
1356 if (this.styleRule.isAttribute)
1358 var regex = this._parentPane.filterRegex();
1359 var hideRule = regex && !regex.test(this.element.textContent);
1360 this.element.classList.toggle("hidden", hideRule);
1364 var children = this.propertiesTreeOutline.children;
1365 for (var i = 0; i < children.length; ++i)
1366 children[i]._updateFilter();
1368 if (this.styleRule.rule)
1369 this._markSelectorHighlights();
1372 _markSelectorMatches: function()
1374 var rule = this.styleRule.rule;
1378 var matchingSelectors = rule.matchingSelectors;
1379 // .selector is rendered as non-affecting selector by default.
1380 if (this.noAffect || matchingSelectors)
1381 this._selectorElement.className = "selector";
1382 if (!matchingSelectors)
1385 var selectors = rule.selectors;
1386 var fragment = document.createDocumentFragment();
1387 var currentMatch = 0;
1388 for (var i = 0; i < selectors.length ; ++i) {
1390 fragment.appendChild(document.createTextNode(", "));
1391 var isSelectorMatching = matchingSelectors[currentMatch] === i;
1392 if (isSelectorMatching)
1394 var rawLocation = new WebInspector.CSSLocation(rule.sourceURL, rule.lineNumberInSource(i), rule.columnNumberInSource(i));
1395 var matchingSelectorClass = isSelectorMatching ? " selector-matches" : "";
1396 var selectorElement = document.createElement("span");
1397 selectorElement.className = "simple-selector" + matchingSelectorClass;
1399 selectorElement._selectorIndex = i;
1400 selectorElement.textContent = selectors[i].value;
1402 fragment.appendChild(selectorElement);
1405 this._selectorElement.removeChildren();
1406 this._selectorElement.appendChild(fragment);
1407 this._markSelectorHighlights();
1410 _markSelectorHighlights: function()
1412 var selectors = this._selectorElement.getElementsByClassName("simple-selector");
1413 var regex = this.pane.filterRegex();
1414 for (var i = 0; i < selectors.length; ++i) {
1415 var selectorMatchesFilter = regex && regex.test(selectors[i].textContent);
1416 selectors[i].classList.toggle("filter-match", selectorMatchesFilter);
1420 _checkWillCancelEditing: function()
1422 var willCauseCancelEditing = this._willCauseCancelEditing;
1423 delete this._willCauseCancelEditing;
1424 return willCauseCancelEditing;
1427 _handleSelectorContainerClick: function(event)
1429 if (this._checkWillCancelEditing() || !this.editable)
1431 if (event.target === this._selectorContainer)
1432 this.addNewBlankProperty(0).startEditing();
1436 * @param {number=} index
1437 * @return {!WebInspector.StylePropertyTreeElement}
1439 addNewBlankProperty: function(index)
1441 var style = this.styleRule.style;
1442 var property = style.newBlankProperty(index);
1443 var item = new WebInspector.StylePropertyTreeElement(this._parentPane, this.styleRule, style, property, false, false, false);
1444 index = property.index;
1445 this.propertiesTreeOutline.insertChild(item, index);
1446 item.listItemElement.textContent = "";
1447 item._newProperty = true;
1452 _createRuleOriginNode: function()
1455 * @param {string} url
1456 * @param {number} line
1458 function linkifyUncopyable(url, line)
1460 var link = WebInspector.linkifyResourceAsNode(url, line, "", url + ":" + (line + 1));
1461 link.classList.add("webkit-html-resource-link");
1462 link.setAttribute("data-uncopyable", link.textContent);
1463 link.textContent = "";
1467 if (this.styleRule.sourceURL) {
1468 var firstMatchingIndex = this.styleRule.rule.matchingSelectors && this.rule.matchingSelectors.length ? this.rule.matchingSelectors[0] : 0;
1469 var matchingSelectorLocation = new WebInspector.CSSLocation(this.styleRule.sourceURL, this.rule.lineNumberInSource(firstMatchingIndex), this.rule.columnNumberInSource(firstMatchingIndex));
1470 return this._parentPane._linkifier.linkifyCSSLocation(this.rule.id.styleSheetId, matchingSelectorLocation) || linkifyUncopyable(this.styleRule.sourceURL, this.rule.lineNumberInSource());
1474 return document.createTextNode("");
1476 if (this.rule.isUserAgent)
1477 return document.createTextNode(WebInspector.UIString("user agent stylesheet"));
1478 if (this.rule.isUser)
1479 return document.createTextNode(WebInspector.UIString("user stylesheet"));
1480 if (this.rule.isViaInspector)
1481 return document.createTextNode(WebInspector.UIString("via inspector"));
1482 return document.createTextNode("");
1485 _handleEmptySpaceMouseDown: function()
1487 this._willCauseCancelEditing = this._parentPane._isEditingStyle;
1490 _handleEmptySpaceClick: function(event)
1495 if (!window.getSelection().isCollapsed)
1498 if (this._checkWillCancelEditing())
1501 if (event.target.classList.contains("header") || this.element.classList.contains("read-only") || event.target.enclosingNodeOrSelfWithClass("media")) {
1506 this.addNewBlankProperty().startEditing();
1509 _handleSelectorClick: function(event)
1511 if (WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event) && this.navigable && event.target.classList.contains("simple-selector")) {
1512 var index = event.target._selectorIndex;
1513 var styleSheetHeader = WebInspector.cssModel.styleSheetHeaderForId(this.rule.id.styleSheetId);
1514 var uiLocation = styleSheetHeader.rawLocationToUILocation(this.rule.lineNumberInSource(index), this.rule.columnNumberInSource(index));
1515 WebInspector.Revealer.reveal(uiLocation);
1518 this._startEditingOnMouseEvent();
1519 event.consume(true);
1522 _startEditingOnMouseEvent: function()
1527 if (!this.rule && this.propertiesTreeOutline.children.length === 0) {
1529 this.addNewBlankProperty().startEditing();
1536 this.startEditingSelector();
1539 startEditingSelector: function()
1541 var element = this._selectorElement;
1542 if (WebInspector.isBeingEdited(element))
1545 element.scrollIntoViewIfNeeded(false);
1546 element.textContent = element.textContent; // Reset selector marks in group.
1548 var config = new WebInspector.InplaceEditor.Config(this.editingSelectorCommitted.bind(this), this.editingSelectorCancelled.bind(this));
1549 WebInspector.InplaceEditor.startEditing(this._selectorElement, config);
1551 window.getSelection().setBaseAndExtent(element, 0, element, 1);
1552 this._parentPane._isEditingStyle = true;
1555 _moveEditorFromSelector: function(moveDirection)
1557 this._markSelectorMatches();
1562 if (moveDirection === "forward") {
1564 var firstChild = this.propertiesTreeOutline.children[0];
1565 while (firstChild && firstChild.inherited)
1566 firstChild = firstChild.nextSibling;
1568 this.addNewBlankProperty().startEditing();
1570 firstChild.startEditing(firstChild.nameElement);
1572 var previousSection = this.previousEditableSibling();
1573 if (!previousSection)
1576 previousSection.expand();
1577 previousSection.addNewBlankProperty().startEditing();
1581 editingSelectorCommitted: function(element, newContent, oldContent, context, moveDirection)
1583 this._editingSelectorEnded();
1585 newContent = newContent.trim();
1586 if (newContent === oldContent) {
1587 // Revert to a trimmed version of the selector if need be.
1588 this._selectorElement.textContent = newContent;
1589 this._moveEditorFromSelector(moveDirection);
1593 var selectedNode = this._parentPane.node;
1596 * @param {!WebInspector.CSSRule} newRule
1597 * @this {WebInspector.StylePropertiesSection}
1599 function successCallback(newRule)
1601 var doesAffectSelectedNode = newRule.matchingSelectors.length > 0;
1602 if (!doesAffectSelectedNode) {
1603 this.noAffect = true;
1604 this.element.classList.add("no-affect");
1606 delete this.noAffect;
1607 this.element.classList.remove("no-affect");
1610 this.rule = newRule;
1611 this.styleRule = { section: this, style: newRule.style, selectorText: newRule.selectorText, media: newRule.media, sourceURL: newRule.resourceURL(), rule: newRule };
1613 this._parentPane.update(selectedNode);
1614 this._updateRuleOrigin();
1616 finishOperationAndMoveEditor.call(this, moveDirection);
1620 * @this {WebInspector.StylePropertiesSection}
1622 function finishOperationAndMoveEditor(direction)
1624 delete this._parentPane._userOperation;
1625 this._moveEditorFromSelector(direction);
1628 // This gets deleted in finishOperationAndMoveEditor(), which is called both on success and failure.
1629 this._parentPane._userOperation = true;
1630 WebInspector.cssModel.setRuleSelector(this.rule.id, selectedNode ? selectedNode.id : 0, newContent, successCallback.bind(this), finishOperationAndMoveEditor.bind(this, moveDirection));
1633 _updateRuleOrigin: function()
1635 this._selectorRefElement.removeChildren();
1636 this._selectorRefElement.appendChild(this._createRuleOriginNode());
1639 _editingSelectorEnded: function()
1641 delete this._parentPane._isEditingStyle;
1644 editingSelectorCancelled: function()
1646 this._editingSelectorEnded();
1648 // Mark the selectors in group if necessary.
1649 // This is overridden by BlankStylePropertiesSection.
1650 this._markSelectorMatches();
1653 __proto__: WebInspector.PropertiesSection.prototype
1658 * @extends {WebInspector.PropertiesSection}
1659 * @param {!WebInspector.StylesSidebarPane} stylesPane
1660 * @param {!Object} styleRule
1661 * @param {!Object.<string, boolean>} usedProperties
1663 WebInspector.ComputedStylePropertiesSection = function(stylesPane, styleRule, usedProperties)
1665 WebInspector.PropertiesSection.call(this, "");
1667 var subtitle = this.headerElement.createChild("div", "sidebar-pane-subtitle vbox");
1668 var showInheritedCheckbox = new WebInspector.Checkbox(WebInspector.UIString("Show inherited properties"), "hbox");
1669 subtitle.appendChild(showInheritedCheckbox.element);
1671 this._hasFreshContent = false;
1674 * @this {WebInspector.ComputedStylePropertiesSection}
1676 function showInheritedToggleFunction()
1678 var showInherited = showInheritedCheckbox.checked;
1679 WebInspector.settings.showInheritedComputedStyleProperties.set(showInherited);
1681 this.element.classList.add("styles-show-inherited");
1683 this.element.classList.remove("styles-show-inherited");
1686 showInheritedCheckbox.addEventListener(showInheritedToggleFunction.bind(this));
1688 this.element.className = "styles-section monospace read-only computed-style";
1689 if (WebInspector.settings.showInheritedComputedStyleProperties.get()) {
1690 this.element.classList.add("styles-show-inherited");
1691 showInheritedCheckbox.checked = true;
1694 this._stylesPane = stylesPane;
1695 this.styleRule = styleRule;
1696 this._usedProperties = usedProperties;
1697 this._alwaysShowComputedProperties = { "display": true, "height": true, "width": true };
1698 this.computedStyle = true;
1699 this._propertyTreeElements = {};
1700 this._expandedPropertyNames = {};
1703 WebInspector.ComputedStylePropertiesSection.prototype = {
1704 collapse: function(dontRememberState)
1706 // Overriding with empty body.
1709 _isPropertyInherited: function(propertyName)
1711 var canonicalName = WebInspector.CSSMetadata.canonicalPropertyName(propertyName);
1712 return !(canonicalName in this._usedProperties) && !(canonicalName in this._alwaysShowComputedProperties);
1717 this._expandedPropertyNames = {};
1718 for (var name in this._propertyTreeElements) {
1719 if (this._propertyTreeElements[name].expanded)
1720 this._expandedPropertyNames[name] = true;
1722 this._propertyTreeElements = {};
1723 this.propertiesTreeOutline.removeChildren();
1724 this.populated = false;
1727 _updateFilter: function()
1729 var children = this.propertiesTreeOutline.children;
1730 for (var i = 0; i < children.length; ++i)
1731 children[i]._updateFilter();
1734 onpopulate: function()
1736 function sorter(a, b)
1738 return a.name.compareTo(b.name);
1741 var style = this.styleRule.style;
1745 var uniqueProperties = [];
1746 var allProperties = style.allProperties;
1747 for (var i = 0; i < allProperties.length; ++i)
1748 uniqueProperties.push(allProperties[i]);
1749 uniqueProperties.sort(sorter);
1751 this._propertyTreeElements = {};
1752 for (var i = 0; i < uniqueProperties.length; ++i) {
1753 var property = uniqueProperties[i];
1754 var inherited = this._isPropertyInherited(property.name);
1755 var item = new WebInspector.ComputedStylePropertyTreeElement(this._stylesPane, this.styleRule, style, property, inherited);
1756 this.propertiesTreeOutline.appendChild(item);
1757 this._propertyTreeElements[property.name] = item;
1761 rebuildComputedTrace: function(sections)
1763 for (var i = 0; i < sections.length; ++i) {
1764 var section = sections[i];
1765 if (section.computedStyle || section.isBlank)
1768 for (var j = 0; j < section.uniqueProperties.length; ++j) {
1769 var property = section.uniqueProperties[j];
1770 if (property.disabled)
1772 if (section.isInherited && !WebInspector.CSSMetadata.isPropertyInherited(property.name))
1775 var treeElement = this._propertyTreeElements[property.name.toLowerCase()];
1777 var fragment = document.createDocumentFragment();
1778 var selector = fragment.createChild("span");
1779 selector.style.color = "gray";
1780 selector.textContent = section.styleRule.selectorText;
1781 fragment.appendChild(document.createTextNode(" - " + property.value + " "));
1782 var subtitle = fragment.createChild("span");
1783 subtitle.style.float = "right";
1784 subtitle.appendChild(section._createRuleOriginNode());
1785 var childElement = new TreeElement(fragment, null, false);
1786 treeElement.appendChild(childElement);
1787 if (property.inactive || section.isPropertyOverloaded(property.name))
1788 childElement.listItemElement.classList.add("overloaded");
1789 if (!property.parsedOk) {
1790 childElement.listItemElement.classList.add("not-parsed-ok");
1791 childElement.listItemElement.insertBefore(WebInspector.StylesSidebarPane.createExclamationMark(property), childElement.listItemElement.firstChild);
1792 if (WebInspector.StylesSidebarPane._ignoreErrorsForProperty(property))
1793 childElement.listItemElement.classList.add("has-ignorable-error");
1799 // Restore expanded state after update.
1800 for (var name in this._expandedPropertyNames) {
1801 if (name in this._propertyTreeElements)
1802 this._propertyTreeElements[name].expand();
1806 __proto__: WebInspector.PropertiesSection.prototype
1811 * @extends {WebInspector.StylePropertiesSection}
1812 * @param {!WebInspector.StylesSidebarPane} stylesPane
1813 * @param {string} defaultSelectorText
1815 WebInspector.BlankStylePropertiesSection = function(stylesPane, defaultSelectorText)
1817 WebInspector.StylePropertiesSection.call(this, stylesPane, {selectorText: defaultSelectorText, rule: {isViaInspector: true}}, true, false);
1818 this.element.classList.add("blank-section");
1821 WebInspector.BlankStylePropertiesSection.prototype = {
1824 return !this._normal;
1830 WebInspector.StylePropertiesSection.prototype.expand.call(this);
1833 editingSelectorCommitted: function(element, newContent, oldContent, context, moveDirection)
1835 if (!this.isBlank) {
1836 WebInspector.StylePropertiesSection.prototype.editingSelectorCommitted.call(this, element, newContent, oldContent, context, moveDirection);
1841 * @param {!WebInspector.CSSRule} newRule
1842 * @this {WebInspector.StylePropertiesSection}
1844 function successCallback(newRule)
1846 var doesSelectorAffectSelectedNode = newRule.matchingSelectors.length > 0;
1847 var styleRule = { section: this, style: newRule.style, selectorText: newRule.selectorText, sourceURL: newRule.resourceURL(), rule: newRule };
1848 this.makeNormal(styleRule);
1850 if (!doesSelectorAffectSelectedNode) {
1851 this.noAffect = true;
1852 this.element.classList.add("no-affect");
1855 this._updateRuleOrigin();
1857 if (this.element.parentElement) // Might have been detached already.
1858 this._moveEditorFromSelector(moveDirection);
1860 delete this._parentPane._userOperation;
1861 this._editingSelectorEnded();
1862 this._markSelectorMatches();
1866 newContent = newContent.trim();
1867 this._parentPane._userOperation = true;
1869 WebInspector.cssModel.requestViaInspectorStylesheet(this.pane.node, viaInspectorCallback.bind(this));
1872 * @this {WebInspector.BlankStylePropertiesSection}
1873 * @param {?WebInspector.CSSStyleSheetHeader} styleSheetHeader
1875 function viaInspectorCallback(styleSheetHeader)
1877 if (!styleSheetHeader) {
1878 this.editingSelectorCancelled();
1881 WebInspector.cssModel.addRule(styleSheetHeader.id, this.pane.node, newContent, successCallback.bind(this), this.editingSelectorCancelled.bind(this));
1885 editingSelectorCancelled: function()
1887 delete this._parentPane._userOperation;
1888 if (!this.isBlank) {
1889 WebInspector.StylePropertiesSection.prototype.editingSelectorCancelled.call(this);
1893 this._editingSelectorEnded();
1894 this.pane.removeSection(this);
1897 makeNormal: function(styleRule)
1899 this.element.classList.remove("blank-section");
1900 this.styleRule = styleRule;
1901 this.rule = styleRule.rule;
1903 // FIXME: replace this instance by a normal WebInspector.StylePropertiesSection.
1904 this._normal = true;
1907 __proto__: WebInspector.StylePropertiesSection.prototype
1912 * @extends {TreeElement}
1913 * @param {!Object} styleRule
1914 * @param {!WebInspector.CSSStyleDeclaration} style
1915 * @param {!WebInspector.CSSProperty} property
1916 * @param {boolean} inherited
1917 * @param {boolean} overloaded
1918 * @param {boolean} hasChildren
1920 WebInspector.StylePropertyTreeElementBase = function(styleRule, style, property, inherited, overloaded, hasChildren)
1922 this._styleRule = styleRule;
1924 this.property = property;
1925 this._inherited = inherited;
1926 this._overloaded = overloaded;
1928 // Pass an empty title, the title gets made later in onattach.
1929 TreeElement.call(this, "", null, hasChildren);
1931 this.selectable = false;
1934 WebInspector.StylePropertyTreeElementBase.prototype = {
1936 * @return {?WebInspector.DOMNode}
1940 return null; // Overridden by ancestors.
1944 * @return {?WebInspector.StylesSidebarPane}
1946 editablePane: function()
1948 return null; // Overridden by ancestors.
1952 * @return {!WebInspector.StylesSidebarPane|!WebInspector.ComputedStyleSidebarPane}
1954 parentPane: function()
1956 throw "Not implemented";
1961 return this._inherited;
1967 hasIgnorableError: function()
1969 return !this.parsedOk && WebInspector.StylesSidebarPane._ignoreErrorsForProperty(this.property);
1974 if (x === this._inherited)
1976 this._inherited = x;
1982 return this._overloaded;
1987 if (x === this._overloaded)
1989 this._overloaded = x;
1995 return this.property.disabled;
2000 if (!this.disabled || !this.property.text)
2001 return this.property.name;
2003 var text = this.property.text;
2004 var index = text.indexOf(":");
2006 return this.property.name;
2008 text = text.substring(0, index).trim();
2009 if (text.startsWith("/*"))
2010 text = text.substring(2).trim();
2016 if (!this.disabled || !this.property.text)
2017 return this.property.value;
2019 var match = this.property.text.match(/(.*);\s*/);
2020 if (!match || !match[1])
2021 return this.property.value;
2023 var text = match[1];
2024 var index = text.indexOf(":");
2026 return this.property.value;
2028 return text.substring(index + 1).trim();
2033 return this.property.parsedOk;
2036 onattach: function()
2041 updateTitle: function()
2043 var value = this.value;
2047 var nameElement = document.createElement("span");
2048 nameElement.className = "webkit-css-property";
2049 nameElement.textContent = this.name;
2050 nameElement.title = this.property.propertyText;
2051 this.nameElement = nameElement;
2053 this._expandElement = document.createElement("span");
2054 this._expandElement.className = "expand-element";
2056 var valueElement = document.createElement("span");
2057 valueElement.className = "value";
2058 this.valueElement = valueElement;
2061 * @param {!RegExp} regex
2062 * @return {!DocumentFragment}
2064 function processValue(regex, processor, nextProcessor, valueText)
2066 var container = document.createDocumentFragment();
2068 var items = valueText.replace(regex, "\0$1\0").split("\0");
2069 for (var i = 0; i < items.length; ++i) {
2070 if ((i % 2) === 0) {
2072 container.appendChild(nextProcessor(items[i]));
2074 container.appendChild(document.createTextNode(items[i]));
2076 var processedNode = processor(items[i]);
2078 container.appendChild(processedNode);
2086 * @param {string} url
2088 * @this {WebInspector.StylePropertyTreeElementBase}
2090 function linkifyURL(url)
2093 var match = hrefUrl.match(/['"]?([^'"]+)/);
2096 var container = document.createDocumentFragment();
2097 container.appendChild(document.createTextNode("url("));
2098 if (this._styleRule.sourceURL)
2099 hrefUrl = WebInspector.ParsedURL.completeURL(this._styleRule.sourceURL, hrefUrl);
2100 else if (this.node())
2101 hrefUrl = this.node().resolveURL(hrefUrl);
2102 var hasResource = hrefUrl && !!WebInspector.resourceForURL(hrefUrl);
2103 // FIXME: WebInspector.linkifyURLAsNode() should really use baseURI.
2104 container.appendChild(WebInspector.linkifyURLAsNode(hrefUrl || url, url, undefined, !hasResource));
2105 container.appendChild(document.createTextNode(")"));
2110 var colorProcessor = processValue.bind(null, WebInspector.StylesSidebarPane._colorRegex, this._processColor.bind(this, nameElement, valueElement), null);
2111 valueElement.appendChild(processValue(/url\(\s*([^)]+)\s*\)/g, linkifyURL.bind(this), WebInspector.CSSMetadata.isColorAwareProperty(this.name) && this.parsedOk ? colorProcessor : null, value));
2114 this.listItemElement.removeChildren();
2115 nameElement.normalize();
2116 valueElement.normalize();
2118 if (!this.treeOutline)
2122 this.listItemElement.createChild("span", "styles-clipboard-only").createTextChild("/* ");
2123 this.listItemElement.appendChild(nameElement);
2124 this.listItemElement.appendChild(document.createTextNode(": "));
2125 this.listItemElement.appendChild(this._expandElement);
2126 this.listItemElement.appendChild(valueElement);
2127 this.listItemElement.appendChild(document.createTextNode(";"));
2129 this.listItemElement.createChild("span", "styles-clipboard-only").createTextChild(" */");
2131 if (!this.parsedOk) {
2132 // Avoid having longhands under an invalid shorthand.
2133 this.hasChildren = false;
2134 this.listItemElement.classList.add("not-parsed-ok");
2136 // Add a separate exclamation mark IMG element with a tooltip.
2137 this.listItemElement.insertBefore(WebInspector.StylesSidebarPane.createExclamationMark(this.property), this.listItemElement.firstChild);
2139 if (this.property.inactive)
2140 this.listItemElement.classList.add("inactive");
2141 this._updateFilter();
2144 _updateFilter: function()
2146 var regEx = this.parentPane().filterRegex();
2147 this.listItemElement.classList.toggle("filter-match", !!regEx && (regEx.test(this.property.name) || regEx.test(this.property.value)));
2151 * @param {!Element} nameElement
2152 * @param {!Element} valueElement
2153 * @param {string} text
2155 _processColor: function(nameElement, valueElement, text)
2157 var color = WebInspector.Color.parse(text);
2159 // We can be called with valid non-color values of |text| (like 'none' from border style)
2161 return document.createTextNode(text);
2163 var format = WebInspector.StylesSidebarPane._colorFormat(color);
2164 var spectrumHelper = this.editablePane() && this.editablePane()._spectrumHelper;
2165 var spectrum = spectrumHelper ? spectrumHelper.spectrum() : null;
2167 var isEditable = !!(this._styleRule && this._styleRule.editable !== false); // |editable| is true by default.
2168 var colorSwatch = new WebInspector.ColorSwatch(!isEditable);
2169 colorSwatch.setColorString(text);
2170 colorSwatch.element.addEventListener("click", swatchClick.bind(this), false);
2172 var scrollerElement;
2173 var boundSpectrumChanged = spectrumChanged.bind(this);
2174 var boundSpectrumHidden = spectrumHidden.bind(this);
2177 * @param {!WebInspector.Event} e
2178 * @this {WebInspector.StylePropertyTreeElementBase}
2180 function spectrumChanged(e)
2182 var colorString = /** @type {string} */ (e.data);
2183 spectrum.displayText = colorString;
2184 colorValueElement.textContent = colorString;
2185 colorSwatch.setColorString(colorString);
2186 this.applyStyleText(nameElement.textContent + ": " + valueElement.textContent, false, false, false);
2190 * @param {!WebInspector.Event} event
2191 * @this {WebInspector.StylePropertyTreeElementBase}
2193 function spectrumHidden(event)
2195 if (scrollerElement)
2196 scrollerElement.removeEventListener("scroll", repositionSpectrum, false);
2197 var commitEdit = event.data;
2198 var propertyText = !commitEdit && this.originalPropertyText ? this.originalPropertyText : (nameElement.textContent + ": " + valueElement.textContent);
2199 this.applyStyleText(propertyText, true, true, false);
2200 spectrum.removeEventListener(WebInspector.Spectrum.Events.ColorChanged, boundSpectrumChanged);
2201 spectrumHelper.removeEventListener(WebInspector.SpectrumPopupHelper.Events.Hidden, boundSpectrumHidden);
2203 delete this.editablePane()._isEditingStyle;
2204 delete this.originalPropertyText;
2207 function repositionSpectrum()
2209 spectrumHelper.reposition(colorSwatch.element);
2214 * @this {WebInspector.StylePropertyTreeElementBase}
2216 function swatchClick(e)
2220 // Shift + click toggles color formats.
2221 // Click opens colorpicker, only if the element is not in computed styles section.
2222 if (!spectrumHelper || e.shiftKey) {
2223 changeColorDisplay();
2230 var visible = spectrumHelper.toggle(colorSwatch.element, color, format);
2232 spectrum.displayText = color.toString(format);
2233 this.originalPropertyText = this.property.propertyText;
2234 this.editablePane()._isEditingStyle = true;
2235 spectrum.addEventListener(WebInspector.Spectrum.Events.ColorChanged, boundSpectrumChanged);
2236 spectrumHelper.addEventListener(WebInspector.SpectrumPopupHelper.Events.Hidden, boundSpectrumHidden);
2238 scrollerElement = colorSwatch.element.enclosingNodeOrSelfWithClass("scroll-target");
2239 if (scrollerElement)
2240 scrollerElement.addEventListener("scroll", repositionSpectrum, false);
2242 console.error("Unable to handle color picker scrolling");
2246 var colorValueElement = document.createElement("span");
2247 colorValueElement.textContent = color.toString(format);
2250 * @param {string} curFormat
2252 function nextFormat(curFormat)
2254 // The format loop is as follows:
2258 // * nickname (if the color has a nickname)
2259 // * if the color is simple:
2260 // - shorthex (if has short hex)
2262 var cf = WebInspector.Color.Format;
2264 switch (curFormat) {
2266 return !color.hasAlpha() ? cf.RGB : cf.RGBA;
2270 return !color.hasAlpha() ? cf.HSL : cf.HSLA;
2274 if (color.nickname())
2276 if (!color.hasAlpha())
2277 return color.canBeShortHex() ? cf.ShortHEX : cf.HEX;
2288 if (!color.hasAlpha())
2289 return color.canBeShortHex() ? cf.ShortHEX : cf.HEX;
2298 function changeColorDisplay()
2301 format = nextFormat(format);
2302 var currentValue = color.toString(format);
2303 } while (currentValue === colorValueElement.textContent);
2304 colorValueElement.textContent = currentValue;
2307 var container = document.createElement("nobr");
2308 container.appendChild(colorSwatch.element);
2309 container.appendChild(colorValueElement);
2313 updateState: function()
2315 if (!this.listItemElement)
2318 if (this.style.isPropertyImplicit(this.name))
2319 this.listItemElement.classList.add("implicit");
2321 this.listItemElement.classList.remove("implicit");
2323 if (this.hasIgnorableError())
2324 this.listItemElement.classList.add("has-ignorable-error");
2326 this.listItemElement.classList.remove("has-ignorable-error");
2329 this.listItemElement.classList.add("inherited");
2331 this.listItemElement.classList.remove("inherited");
2333 if (this.overloaded)
2334 this.listItemElement.classList.add("overloaded");
2336 this.listItemElement.classList.remove("overloaded");
2339 this.listItemElement.classList.add("disabled");
2341 this.listItemElement.classList.remove("disabled");
2344 __proto__: TreeElement.prototype
2349 * @extends {WebInspector.StylePropertyTreeElementBase}
2350 * @param {!WebInspector.StylesSidebarPane} stylesPane
2351 * @param {!Object} styleRule
2352 * @param {!WebInspector.CSSStyleDeclaration} style
2353 * @param {!WebInspector.CSSProperty} property
2354 * @param {boolean} inherited
2356 WebInspector.ComputedStylePropertyTreeElement = function(stylesPane, styleRule, style, property, inherited)
2358 WebInspector.StylePropertyTreeElementBase.call(this, styleRule, style, property, inherited, false, false);
2359 this._stylesPane = stylesPane;
2362 WebInspector.ComputedStylePropertyTreeElement.prototype = {
2364 * @return {?WebInspector.DOMNode}
2368 return this._stylesPane.node;
2372 * @return {?WebInspector.StylesSidebarPane}
2374 editablePane: function()
2380 * @return {!WebInspector.ComputedStyleSidebarPane}
2382 parentPane: function()
2384 return this._stylesPane._computedStylePane;
2387 _updateFilter: function()
2389 var regEx = this.parentPane().filterRegex();
2390 this.listItemElement.classList.toggle("hidden", !!regEx && (!regEx.test(this.property.name) && !regEx.test(this.property.value)));
2393 __proto__: WebInspector.StylePropertyTreeElementBase.prototype
2398 * @extends {WebInspector.StylePropertyTreeElementBase}
2399 * @param {!WebInspector.StylesSidebarPane} stylesPane
2400 * @param {!Object} styleRule
2401 * @param {!WebInspector.CSSStyleDeclaration} style
2402 * @param {!WebInspector.CSSProperty} property
2403 * @param {boolean} isShorthand
2404 * @param {boolean} inherited
2405 * @param {boolean} overloaded
2407 WebInspector.StylePropertyTreeElement = function(stylesPane, styleRule, style, property, isShorthand, inherited, overloaded)
2409 WebInspector.StylePropertyTreeElementBase.call(this, styleRule, style, property, inherited, overloaded, isShorthand);
2410 this._parentPane = stylesPane;
2411 this.isShorthand = isShorthand;
2414 WebInspector.StylePropertyTreeElement.prototype = {
2416 * @return {?WebInspector.DOMNode}
2420 return this._parentPane.node;
2424 * @return {?WebInspector.StylesSidebarPane}
2426 editablePane: function()
2428 return this._parentPane;
2432 * @return {!WebInspector.StylesSidebarPane}
2434 parentPane: function()
2436 return this._parentPane;
2440 * @return {?WebInspector.StylePropertiesSection}
2444 return this.treeOutline && this.treeOutline.section;
2448 * @param {function()=} userCallback
2450 _updatePane: function(userCallback)
2452 var section = this.section();
2453 if (section && section.pane)
2454 section.pane._refreshUpdate(section, false, userCallback);
2462 * @param {?Event} event
2464 toggleEnabled: function(event)
2466 var disabled = !event.target.checked;
2469 * @param {?WebInspector.CSSStyleDeclaration} newStyle
2470 * @this {WebInspector.StylePropertyTreeElement}
2472 function callback(newStyle)
2474 delete this._parentPane._userOperation;
2479 newStyle.parentRule = this.style.parentRule;
2480 this.style = newStyle;
2481 this._styleRule.style = newStyle;
2483 var section = this.section();
2484 if (section && section.pane)
2485 section.pane.dispatchEventToListeners("style property toggled");
2490 this._parentPane._userOperation = true;
2491 this.property.setDisabled(disabled, callback.bind(this));
2495 onpopulate: function()
2497 // Only populate once and if this property is a shorthand.
2498 if (this.children.length || !this.isShorthand)
2501 var longhandProperties = this.style.longhandProperties(this.name);
2502 for (var i = 0; i < longhandProperties.length; ++i) {
2503 var name = longhandProperties[i].name;
2504 var inherited = false;
2505 var overloaded = false;
2507 var section = this.section();
2509 inherited = section.isPropertyInherited(name);
2510 overloaded = section.isPropertyOverloaded(name);
2513 var liveProperty = this.style.getLiveProperty(name);
2517 var item = new WebInspector.StylePropertyTreeElement(this._parentPane, this._styleRule, this.style, liveProperty, false, inherited, overloaded);
2518 this.appendChild(item);
2522 onattach: function()
2524 WebInspector.StylePropertyTreeElementBase.prototype.onattach.call(this);
2526 this.listItemElement.addEventListener("mousedown", this._mouseDown.bind(this));
2527 this.listItemElement.addEventListener("mouseup", this._resetMouseDownElement.bind(this));
2528 this.listItemElement.addEventListener("click", this._mouseClick.bind(this));
2531 _mouseDown: function(event)
2533 if (this._parentPane) {
2534 this._parentPane._mouseDownTreeElement = this;
2535 this._parentPane._mouseDownTreeElementIsName = this._isNameElement(event.target);
2536 this._parentPane._mouseDownTreeElementIsValue = this._isValueElement(event.target);
2540 _resetMouseDownElement: function()
2542 if (this._parentPane) {
2543 delete this._parentPane._mouseDownTreeElement;
2544 delete this._parentPane._mouseDownTreeElementIsName;
2545 delete this._parentPane._mouseDownTreeElementIsValue;
2549 updateTitle: function()
2551 WebInspector.StylePropertyTreeElementBase.prototype.updateTitle.call(this);
2553 if (this.parsedOk && this.section() && this.parent.root) {
2554 var enabledCheckboxElement = document.createElement("input");
2555 enabledCheckboxElement.className = "enabled-button";
2556 enabledCheckboxElement.type = "checkbox";
2557 enabledCheckboxElement.checked = !this.disabled;
2558 enabledCheckboxElement.addEventListener("click", this.toggleEnabled.bind(this), false);
2559 this.listItemElement.insertBefore(enabledCheckboxElement, this.listItemElement.firstChild);
2563 _mouseClick: function(event)
2565 if (!window.getSelection().isCollapsed)
2568 event.consume(true);
2570 if (event.target === this.listItemElement) {
2571 var section = this.section();
2572 if (!section || !section.editable)
2575 if (section._checkWillCancelEditing())
2577 section.addNewBlankProperty(this.property.index + 1).startEditing();
2581 if (WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event) && this.section().navigable) {
2582 this._navigateToSource(event.target);
2586 this.startEditing(event.target);
2590 * @param {!Element} element
2592 _navigateToSource: function(element)
2594 console.assert(this.section().navigable);
2595 var propertyNameClicked = element === this.nameElement;
2596 WebInspector.Revealer.reveal(this.property.uiLocation(propertyNameClicked));
2600 * @param {!Element} element
2602 _isNameElement: function(element)
2604 return element.enclosingNodeOrSelfWithClass("webkit-css-property") === this.nameElement;
2608 * @param {!Element} element
2610 _isValueElement: function(element)
2612 return !!element.enclosingNodeOrSelfWithClass("value");
2616 * @param {!Element=} selectElement
2618 startEditing: function(selectElement)
2620 // FIXME: we don't allow editing of longhand properties under a shorthand right now.
2621 if (this.parent.isShorthand)
2624 if (selectElement === this._expandElement)
2627 var section = this.section();
2628 if (section && !section.editable)
2632 selectElement = this.nameElement; // No arguments passed in - edit the name element by default.
2634 selectElement = selectElement.enclosingNodeOrSelfWithClass("webkit-css-property") || selectElement.enclosingNodeOrSelfWithClass("value");
2636 if (WebInspector.isBeingEdited(selectElement))
2639 var isEditingName = selectElement === this.nameElement;
2641 this.valueElement.textContent = restoreURLs(this.valueElement.textContent, this.value);
2644 * @param {string} fieldValue
2645 * @param {string} modelValue
2648 function restoreURLs(fieldValue, modelValue)
2650 const urlRegex = /\b(url\([^)]*\))/g;
2651 var splitFieldValue = fieldValue.split(urlRegex);
2652 if (splitFieldValue.length === 1)
2654 var modelUrlRegex = new RegExp(urlRegex);
2655 for (var i = 1; i < splitFieldValue.length; i += 2) {
2656 var match = modelUrlRegex.exec(modelValue);
2658 splitFieldValue[i] = match[0];
2660 return splitFieldValue.join("");
2664 expanded: this.expanded,
2665 hasChildren: this.hasChildren,
2666 isEditingName: isEditingName,
2667 previousContent: selectElement.textContent
2670 // Lie about our children to prevent expanding on double click and to collapse shorthands.
2671 this.hasChildren = false;
2673 if (selectElement.parentElement)
2674 selectElement.parentElement.classList.add("child-editing");
2675 selectElement.textContent = selectElement.textContent; // remove color swatch and the like
2678 * @this {WebInspector.StylePropertyTreeElement}
2680 function pasteHandler(context, event)
2682 var data = event.clipboardData.getData("Text");
2685 var colonIdx = data.indexOf(":");
2688 var name = data.substring(0, colonIdx).trim();
2689 var value = data.substring(colonIdx + 1).trim();
2691 event.preventDefault();
2693 if (!("originalName" in context)) {
2694 context.originalName = this.nameElement.textContent;
2695 context.originalValue = this.valueElement.textContent;
2697 this.property.name = name;
2698 this.property.value = value;
2699 this.nameElement.textContent = name;
2700 this.valueElement.textContent = value;
2701 this.nameElement.normalize();
2702 this.valueElement.normalize();
2704 this.editingCommitted(event.target.textContent, context, "forward");
2708 * @this {WebInspector.StylePropertyTreeElement}
2710 function blurListener(context, event)
2712 var treeElement = this._parentPane._mouseDownTreeElement;
2713 var moveDirection = "";
2714 if (treeElement === this) {
2715 if (isEditingName && this._parentPane._mouseDownTreeElementIsValue)
2716 moveDirection = "forward";
2717 if (!isEditingName && this._parentPane._mouseDownTreeElementIsName)
2718 moveDirection = "backward";
2720 this.editingCommitted(event.target.textContent, context, moveDirection);
2723 delete this.originalPropertyText;
2725 this._parentPane._isEditingStyle = true;
2726 if (selectElement.parentElement)
2727 selectElement.parentElement.scrollIntoViewIfNeeded(false);
2729 var applyItemCallback = !isEditingName ? this._applyFreeFlowStyleTextEdit.bind(this, true) : undefined;
2730 this._prompt = new WebInspector.StylesSidebarPane.CSSPropertyPrompt(isEditingName ? WebInspector.CSSMetadata.cssPropertiesMetainfo : WebInspector.CSSMetadata.keywordsForProperty(this.nameElement.textContent), this, isEditingName);
2731 if (applyItemCallback) {
2732 this._prompt.addEventListener(WebInspector.TextPrompt.Events.ItemApplied, applyItemCallback, this);
2733 this._prompt.addEventListener(WebInspector.TextPrompt.Events.ItemAccepted, applyItemCallback, this);
2735 var proxyElement = this._prompt.attachAndStartEditing(selectElement, blurListener.bind(this, context));
2737 proxyElement.addEventListener("keydown", this.editingNameValueKeyDown.bind(this, context), false);
2738 proxyElement.addEventListener("keypress", this.editingNameValueKeyPress.bind(this, context), false);
2740 proxyElement.addEventListener("paste", pasteHandler.bind(this, context), false);
2742 window.getSelection().setBaseAndExtent(selectElement, 0, selectElement, 1);
2745 editingNameValueKeyDown: function(context, event)
2750 var isEditingName = context.isEditingName;
2753 if (isEnterKey(event)) {
2754 event.preventDefault();
2756 } else if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code || event.keyIdentifier === "U+001B")
2758 else if (!isEditingName && this._newProperty && event.keyCode === WebInspector.KeyboardShortcut.Keys.Backspace.code) {
2759 // For a new property, when Backspace is pressed at the beginning of new property value, move back to the property name.
2760 var selection = window.getSelection();
2761 if (selection.isCollapsed && !selection.focusOffset) {
2762 event.preventDefault();
2763 result = "backward";
2765 } else if (event.keyIdentifier === "U+0009") { // Tab key.
2766 result = event.shiftKey ? "backward" : "forward";
2767 event.preventDefault();
2773 this.editingCancelled(null, context);
2777 this.editingCommitted(event.target.textContent, context, result);
2786 this._applyFreeFlowStyleTextEdit(false);
2789 editingNameValueKeyPress: function(context, event)
2791 function shouldCommitValueSemicolon(text, cursorPosition)
2793 // FIXME: should this account for semicolons inside comments?
2795 for (var i = 0; i < cursorPosition; ++i) {
2797 if (ch === "\\" && openQuote !== "")
2798 ++i; // skip next character inside string
2799 else if (!openQuote && (ch === "\"" || ch === "'"))
2801 else if (openQuote === ch)
2807 var keyChar = String.fromCharCode(event.charCode);
2808 var isFieldInputTerminated = (context.isEditingName ? keyChar === ":" : keyChar === ";" && shouldCommitValueSemicolon(event.target.textContent, event.target.selectionLeftOffset()));
2809 if (isFieldInputTerminated) {
2810 // Enter or colon (for name)/semicolon outside of string (for value).
2811 event.consume(true);
2812 this.editingCommitted(event.target.textContent, context, "forward");
2817 _applyFreeFlowStyleTextEdit: function(now)
2819 if (this._applyFreeFlowStyleTextEditTimer)
2820 clearTimeout(this._applyFreeFlowStyleTextEditTimer);
2823 * @this {WebInspector.StylePropertyTreeElement}
2827 var valueText = this.valueElement.textContent;
2828 if (valueText.indexOf(";") === -1)
2829 this.applyStyleText(this.nameElement.textContent + ": " + valueText, false, false, false);
2834 this._applyFreeFlowStyleTextEditTimer = setTimeout(apply.bind(this), 100);
2837 kickFreeFlowStyleEditForTest: function()
2839 this._applyFreeFlowStyleTextEdit(true);
2842 editingEnded: function(context)
2844 this._resetMouseDownElement();
2845 if (this._applyFreeFlowStyleTextEditTimer)
2846 clearTimeout(this._applyFreeFlowStyleTextEditTimer);
2848 this.hasChildren = context.hasChildren;
2849 if (context.expanded)
2851 var editedElement = context.isEditingName ? this.nameElement : this.valueElement;
2852 // The proxyElement has been deleted, no need to remove listener.
2853 if (editedElement.parentElement)
2854 editedElement.parentElement.classList.remove("child-editing");
2856 delete this._parentPane._isEditingStyle;
2859 editingCancelled: function(element, context)
2861 this._removePrompt();
2862 this._revertStyleUponEditingCanceled(this.originalPropertyText);
2863 // This should happen last, as it clears the info necessary to restore the property value after [Page]Up/Down changes.
2864 this.editingEnded(context);
2867 _revertStyleUponEditingCanceled: function(originalPropertyText)
2869 if (typeof originalPropertyText === "string") {
2870 delete this.originalPropertyText;
2871 this.applyStyleText(originalPropertyText, true, false, true);
2873 if (this._newProperty)
2874 this.treeOutline.removeChild(this);
2880 _findSibling: function(moveDirection)
2884 target = (moveDirection === "forward" ? target.nextSibling : target.previousSibling);
2885 } while(target && target.inherited);
2891 * @param {string} userInput
2892 * @param {!Object} context
2893 * @param {string} moveDirection
2895 editingCommitted: function(userInput, context, moveDirection)
2897 this._removePrompt();
2898 this.editingEnded(context);
2899 var isEditingName = context.isEditingName;
2901 // Determine where to move to before making changes
2902 var createNewProperty, moveToPropertyName, moveToSelector;
2903 var isDataPasted = "originalName" in context;
2904 var isDirtyViaPaste = isDataPasted && (this.nameElement.textContent !== context.originalName || this.valueElement.textContent !== context.originalValue);
2905 var isPropertySplitPaste = isDataPasted && isEditingName && this.valueElement.textContent !== context.originalValue;
2907 var moveToOther = (isEditingName ^ (moveDirection === "forward"));
2908 var abandonNewProperty = this._newProperty && !userInput && (moveToOther || isEditingName);
2909 if (moveDirection === "forward" && (!isEditingName || isPropertySplitPaste) || moveDirection === "backward" && isEditingName) {
2910 moveTo = moveTo._findSibling(moveDirection);
2912 moveToPropertyName = moveTo.name;
2913 else if (moveDirection === "forward" && (!this._newProperty || userInput))
2914 createNewProperty = true;
2915 else if (moveDirection === "backward")
2916 moveToSelector = true;
2919 // Make the Changes and trigger the moveToNextCallback after updating.
2920 var moveToIndex = moveTo && this.treeOutline ? this.treeOutline.children.indexOf(moveTo) : -1;
2921 var blankInput = /^\s*$/.test(userInput);
2922 var shouldCommitNewProperty = this._newProperty && (isPropertySplitPaste || moveToOther || (!moveDirection && !isEditingName) || (isEditingName && blankInput));
2923 var section = this.section();
2924 if (((userInput !== context.previousContent || isDirtyViaPaste) && !this._newProperty) || shouldCommitNewProperty) {
2925 section._afterUpdate = moveToNextCallback.bind(this, this._newProperty, !blankInput, section);
2927 if (blankInput || (this._newProperty && /^\s*$/.test(this.valueElement.textContent)))
2931 propertyText = userInput + ": " + this.property.value;
2933 propertyText = this.property.name + ": " + userInput;
2935 this.applyStyleText(propertyText, true, true, false);
2938 this.property.name = userInput;
2940 this.property.value = userInput;
2941 if (!isDataPasted && !this._newProperty)
2943 moveToNextCallback.call(this, this._newProperty, false, section);
2947 * The Callback to start editing the next/previous property/selector.
2948 * @this {WebInspector.StylePropertyTreeElement}
2950 function moveToNextCallback(alreadyNew, valueChanged, section)
2955 // User just tabbed through without changes.
2956 if (moveTo && moveTo.parent) {
2957 moveTo.startEditing(!isEditingName ? moveTo.nameElement : moveTo.valueElement);
2961 // User has made a change then tabbed, wiping all the original treeElements.
2962 // Recalculate the new treeElement for the same property we were going to edit next.
2963 if (moveTo && !moveTo.parent) {
2964 var propertyElements = section.propertiesTreeOutline.children;
2965 if (moveDirection === "forward" && blankInput && !isEditingName)
2967 if (moveToIndex >= propertyElements.length && !this._newProperty)
2968 createNewProperty = true;
2970 var treeElement = moveToIndex >= 0 ? propertyElements[moveToIndex] : null;
2972 var elementToEdit = !isEditingName || isPropertySplitPaste ? treeElement.nameElement : treeElement.valueElement;
2973 if (alreadyNew && blankInput)
2974 elementToEdit = moveDirection === "forward" ? treeElement.nameElement : treeElement.valueElement;
2975 treeElement.startEditing(elementToEdit);
2977 } else if (!alreadyNew)
2978 moveToSelector = true;
2982 // Create a new attribute in this section (or move to next editable selector if possible).
2983 if (createNewProperty) {
2984 if (alreadyNew && !valueChanged && (isEditingName ^ (moveDirection === "backward")))
2987 section.addNewBlankProperty().startEditing();
2991 if (abandonNewProperty) {
2992 moveTo = this._findSibling(moveDirection);
2993 var sectionToEdit = (moveTo || moveDirection === "backward") ? section : section.nextEditableSibling();
2994 if (sectionToEdit) {
2995 if (sectionToEdit.rule)
2996 sectionToEdit.startEditingSelector();
2998 sectionToEdit._moveEditorFromSelector(moveDirection);
3003 if (moveToSelector) {
3005 section.startEditingSelector();
3007 section._moveEditorFromSelector(moveDirection);
3012 _removePrompt: function()
3014 // BUG 53242. This cannot go into editingEnded(), as it should always happen first for any editing outcome.
3016 this._prompt.detach();
3017 delete this._prompt;
3021 _hasBeenModifiedIncrementally: function()
3023 // New properties applied via up/down or live editing have an originalPropertyText and will be deleted later
3024 // on, if cancelled, when the empty string gets applied as their style text.
3025 return typeof this.originalPropertyText === "string" || (!!this.property.propertyText && this._newProperty);
3028 applyStyleText: function(styleText, updateInterface, majorChange, isRevert)
3030 function userOperationFinishedCallback(parentPane, updateInterface)
3032 if (updateInterface)
3033 delete parentPane._userOperation;
3036 // Leave a way to cancel editing after incremental changes.
3037 if (!isRevert && !updateInterface && !this._hasBeenModifiedIncrementally()) {
3038 // Remember the rule's original CSS text on [Page](Up|Down), so it can be restored
3039 // if the editing is canceled.
3040 this.originalPropertyText = this.property.propertyText;
3043 if (!this.treeOutline)
3046 var section = this.section();
3047 styleText = styleText.replace(/\s/g, " ").trim(); // Replace with whitespace.
3048 var styleTextLength = styleText.length;
3049 if (!styleTextLength && updateInterface && !isRevert && this._newProperty && !this._hasBeenModifiedIncrementally()) {
3050 // The user deleted everything and never applied a new property value via Up/Down scrolling/live editing, so remove the tree element and update.
3051 this.parent.removeChild(this);
3052 section.afterUpdate();
3056 var currentNode = this._parentPane.node;
3057 if (updateInterface)
3058 this._parentPane._userOperation = true;
3061 * @param {function()} userCallback
3062 * @param {string} originalPropertyText
3063 * @param {?WebInspector.CSSStyleDeclaration} newStyle
3064 * @this {WebInspector.StylePropertyTreeElement}
3066 function callback(userCallback, originalPropertyText, newStyle)
3069 if (updateInterface) {
3070 // It did not apply, cancel editing.
3071 this._revertStyleUponEditingCanceled(originalPropertyText);
3077 if (this._newProperty)
3078 this._newPropertyInStyle = true;
3079 newStyle.parentRule = this.style.parentRule;
3080 this.style = newStyle;
3081 this.property = newStyle.propertyAt(this.property.index);
3082 this._styleRule.style = this.style;
3084 if (section && section.pane)
3085 section.pane.dispatchEventToListeners("style edited");
3087 if (updateInterface && currentNode === this.node()) {
3088 this._updatePane(userCallback);
3095 // Append a ";" if the new text does not end in ";".
3096 // FIXME: this does not handle trailing comments.
3097 if (styleText.length && !/;\s*$/.test(styleText))
3099 var overwriteProperty = !!(!this._newProperty || this._newPropertyInStyle);
3100 this.property.setText(styleText, majorChange, overwriteProperty, callback.bind(this, userOperationFinishedCallback.bind(null, this._parentPane, updateInterface), this.originalPropertyText));
3106 ondblclick: function()
3108 return true; // handled
3112 * @param {?Event} event
3115 isEventWithinDisclosureTriangle: function(event)
3117 return event.target === this._expandElement;
3120 __proto__: WebInspector.StylePropertyTreeElementBase.prototype
3125 * @extends {WebInspector.TextPrompt}
3126 * @param {!WebInspector.CSSMetadata} cssCompletions
3127 * @param {!WebInspector.StylePropertyTreeElement} sidebarPane
3128 * @param {boolean} isEditingName
3130 WebInspector.StylesSidebarPane.CSSPropertyPrompt = function(cssCompletions, sidebarPane, isEditingName)
3132 // Use the same callback both for applyItemCallback and acceptItemCallback.
3133 WebInspector.TextPrompt.call(this, this._buildPropertyCompletions.bind(this), WebInspector.StyleValueDelimiters);
3134 this.setSuggestBoxEnabled("generic-suggest");
3135 this._cssCompletions = cssCompletions;
3136 this._sidebarPane = sidebarPane;
3137 this._isEditingName = isEditingName;
3140 this.disableDefaultSuggestionForEmptyInput();
3143 WebInspector.StylesSidebarPane.CSSPropertyPrompt.prototype = {
3145 * @param {?Event} event
3148 onKeyDown: function(event)
3150 switch (event.keyIdentifier) {
3155 if (this._handleNameOrValueUpDown(event)) {
3156 event.preventDefault();
3161 if (this.autoCompleteElement && !this.autoCompleteElement.textContent.length) {
3162 this.tabKeyPressed();
3168 return WebInspector.TextPrompt.prototype.onKeyDown.call(this, event);
3171 onMouseWheel: function(event)
3173 if (this._handleNameOrValueUpDown(event)) {
3174 event.consume(true);
3177 WebInspector.TextPrompt.prototype.onMouseWheel.call(this, event);
3184 tabKeyPressed: function()
3186 this.acceptAutoComplete();
3188 // Always tab to the next field.
3193 * @param {?Event} event
3196 _handleNameOrValueUpDown: function(event)
3199 * @param {string} originalValue
3200 * @param {string} replacementString
3201 * @this {WebInspector.StylesSidebarPane.CSSPropertyPrompt}
3203 function finishHandler(originalValue, replacementString)
3205 // Synthesize property text disregarding any comments, custom whitespace etc.
3206 this._sidebarPane.applyStyleText(this._sidebarPane.nameElement.textContent + ": " + this._sidebarPane.valueElement.textContent, false, false, false);
3209 // Handle numeric value increment/decrement only at this point.
3210 if (!this._isEditingName && WebInspector.handleElementValueModifications(event, this._sidebarPane.valueElement, finishHandler.bind(this), this._isValueSuggestion.bind(this)))
3217 * @param {string} word
3220 _isValueSuggestion: function(word)
3224 word = word.toLowerCase();
3225 return this._cssCompletions.keySet().hasOwnProperty(word);
3229 * @param {!Element} proxyElement
3230 * @param {!Range} wordRange
3231 * @param {boolean} force
3232 * @param {function(!Array.<string>, number=)} completionsReadyCallback
3234 _buildPropertyCompletions: function(proxyElement, wordRange, force, completionsReadyCallback)
3236 var prefix = wordRange.toString().toLowerCase();
3237 if (!prefix && !force && (this._isEditingName || proxyElement.textContent.length)) {
3238 completionsReadyCallback([]);
3242 var results = this._cssCompletions.startsWith(prefix);
3243 var selectedIndex = this._cssCompletions.mostUsedOf(results);
3244 completionsReadyCallback(results, selectedIndex);
3247 __proto__: WebInspector.TextPrompt.prototype