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.
30 WebInspector.StylesSidebarPane = function(computedStylePane)
32 WebInspector.SidebarPane.call(this, WebInspector.UIString("Styles"));
34 this.settingsSelectElement = document.createElement("select");
35 this.settingsSelectElement.className = "select-settings";
37 var option = document.createElement("option");
38 option.value = WebInspector.StylesSidebarPane.ColorFormat.Original;
39 option.label = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "As authored" : "As Authored");
40 this.settingsSelectElement.appendChild(option);
42 var option = document.createElement("option");
43 option.value = WebInspector.StylesSidebarPane.ColorFormat.HEX;
44 option.label = WebInspector.UIString("Hex Colors");
45 this.settingsSelectElement.appendChild(option);
47 option = document.createElement("option");
48 option.value = WebInspector.StylesSidebarPane.ColorFormat.RGB;
49 option.label = WebInspector.UIString("RGB Colors");
50 this.settingsSelectElement.appendChild(option);
52 option = document.createElement("option");
53 option.value = WebInspector.StylesSidebarPane.ColorFormat.HSL;
54 option.label = WebInspector.UIString("HSL Colors");
55 this.settingsSelectElement.appendChild(option);
57 // Prevent section from collapsing.
58 var muteEventListener = function(event) { event.stopPropagation(); event.preventDefault(); };
60 this.settingsSelectElement.addEventListener("click", muteEventListener, true);
61 this.settingsSelectElement.addEventListener("change", this._changeSetting.bind(this), false);
62 this._updateColorFormatFilter();
64 this.titleElement.appendChild(this.settingsSelectElement);
66 this._elementStateButton = document.createElement("button");
67 this._elementStateButton.className = "pane-title-button element-state";
68 this._elementStateButton.title = WebInspector.UIString("Toggle Element State");
69 this._elementStateButton.addEventListener("click", this._toggleElementStatePane.bind(this), false);
70 this.titleElement.appendChild(this._elementStateButton);
72 var addButton = document.createElement("button");
73 addButton.className = "pane-title-button add";
74 addButton.id = "add-style-button-test-id";
75 addButton.title = WebInspector.UIString("New Style Rule");
76 addButton.addEventListener("click", this._createNewRule.bind(this), false);
77 this.titleElement.appendChild(addButton);
79 this._computedStylePane = computedStylePane;
80 this.element.addEventListener("contextmenu", this._contextMenuEventFired.bind(this), true);
81 WebInspector.settings.colorFormat.addChangeListener(this._colorFormatSettingChanged.bind(this));
83 this._createElementStatePane();
84 this.bodyElement.appendChild(this._elementStatePane);
85 this._sectionsContainer = document.createElement("div");
86 this.bodyElement.appendChild(this._sectionsContainer);
88 WebInspector.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetChanged, this._styleSheetChanged, this);
89 WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.AttrModified, this._attributesUpdated, this);
90 WebInspector.settings.showUserAgentStyles.addChangeListener(this._showUserAgentStylesSettingChanged.bind(this));
93 WebInspector.StylesSidebarPane.ColorFormat = {
104 WebInspector.StylesSidebarPane.StyleValueDelimiters = " \t\n\"':;,/()";
106 // Taken from http://www.w3.org/TR/CSS21/propidx.html.
107 WebInspector.StylesSidebarPane.InheritedProperties = [
108 "azimuth", "border-collapse", "border-spacing", "caption-side", "color", "cursor", "direction", "elevation",
109 "empty-cells", "font-family", "font-size", "font-style", "font-variant", "font-weight", "font", "letter-spacing",
110 "line-height", "list-style-image", "list-style-position", "list-style-type", "list-style", "orphans", "pitch-range",
111 "pitch", "quotes", "richness", "speak-header", "speak-numeral", "speak-punctuation", "speak", "speech-rate", "stress",
112 "text-align", "text-indent", "text-transform", "text-shadow", "visibility", "voice-family", "volume", "white-space", "widows", "word-spacing"
115 // Keep in sync with RenderStyleConstants.h PseudoId enum. Array below contains pseudo id names for corresponding enum indexes.
116 // First item is empty due to its artificial NOPSEUDO nature in the enum.
117 // FIXME: find a way of generating this mapping or getting it from combination of RenderStyleConstants and CSSSelector.cpp at
119 WebInspector.StylesSidebarPane.PseudoIdNames = [
120 "", "first-line", "first-letter", "before", "after", "selection", "", "-webkit-scrollbar", "-webkit-file-upload-button",
121 "-webkit-input-placeholder", "-webkit-slider-thumb", "-webkit-search-cancel-button", "-webkit-search-decoration",
122 "-webkit-search-results-decoration", "-webkit-search-results-button", "-webkit-media-controls-panel",
123 "-webkit-media-controls-play-button", "-webkit-media-controls-mute-button", "-webkit-media-controls-timeline",
124 "-webkit-media-controls-timeline-container", "-webkit-media-controls-volume-slider",
125 "-webkit-media-controls-volume-slider-container", "-webkit-media-controls-current-time-display",
126 "-webkit-media-controls-time-remaining-display", "-webkit-media-controls-seek-back-button", "-webkit-media-controls-seek-forward-button",
127 "-webkit-media-controls-fullscreen-button", "-webkit-media-controls-rewind-button", "-webkit-media-controls-return-to-realtime-button",
128 "-webkit-media-controls-toggle-closed-captions-button", "-webkit-media-controls-status-display", "-webkit-scrollbar-thumb",
129 "-webkit-scrollbar-button", "-webkit-scrollbar-track", "-webkit-scrollbar-track-piece", "-webkit-scrollbar-corner",
130 "-webkit-resizer", "-webkit-input-list-button", "-webkit-inner-spin-button", "-webkit-outer-spin-button"
133 WebInspector.StylesSidebarPane.CSSNumberRegex = /^(-?(?:\d+(?:\.\d+)?|\.\d+))$/;
135 WebInspector.StylesSidebarPane.alteredFloatNumber = function(number, event)
137 var arrowKeyPressed = (event.keyIdentifier === "Up" || event.keyIdentifier === "Down");
138 // If the number is near zero or the number is one and the direction will take it near zero.
139 var numberNearZero = (number < 1 && number > -1);
140 if (number === 1 && event.keyIdentifier === "Down")
141 numberNearZero = true;
142 else if (number === -1 && event.keyIdentifier === "Up")
143 numberNearZero = true;
146 if (numberNearZero && event.altKey && arrowKeyPressed) {
147 if (event.keyIdentifier === "Down")
148 result = Math.ceil(number - 1);
150 result = Math.floor(number + 1);
152 // Jump by 10 when shift is down or jump by 0.1 when near zero or Alt/Option is down.
153 // Also jump by 10 for page up and down, or by 100 if shift is held with a page key.
154 var changeAmount = 1;
155 if (event.shiftKey && !arrowKeyPressed)
157 else if (event.shiftKey || !arrowKeyPressed)
159 else if (event.altKey || numberNearZero)
162 if (event.keyIdentifier === "Down" || event.keyIdentifier === "PageDown")
165 // Make the new number and constrain it to a precision of 6, this matches numbers the engine returns.
166 // Use the Number constructor to forget the fixed precision, so 1.100000 will print as 1.1.
167 result = Number((number + changeAmount).toFixed(6));
168 if (!String(result).match(WebInspector.StylesSidebarPane.CSSNumberRegex))
175 WebInspector.StylesSidebarPane.alteredHexNumber = function(hexString, event)
177 var number = parseInt(hexString, 16);
178 if (isNaN(number) || !isFinite(number))
181 var maxValue = Math.pow(16, hexString.length) - 1;
182 var arrowKeyPressed = (event.keyIdentifier === "Up" || event.keyIdentifier === "Down");
186 delta = (event.keyIdentifier === "Up") ? 1 : -1;
188 delta = (event.keyIdentifier === "PageUp") ? 16 : -16;
193 var result = number + delta;
195 result = 0; // Color hex values are never negative, so clamp to 0.
196 else if (result > maxValue)
199 // Ensure the result length is the same as the original hex value.
200 var resultString = result.toString(16).toUpperCase();
201 for (var i = 0, lengthDelta = hexString.length - resultString.length; i < lengthDelta; ++i)
202 resultString = "0" + resultString;
206 WebInspector.StylesSidebarPane.prototype = {
207 _contextMenuEventFired: function(event)
209 var contextMenu = new WebInspector.ContextMenu();
210 if (WebInspector.populateHrefContextMenu(contextMenu, this.node, event))
211 contextMenu.show(event);
214 update: function(node, forceUpdate)
221 if (!forceUpdate && (node === this.node))
224 if (node && node.nodeType() === Node.TEXT_NODE && node.parentNode)
225 node = node.parentNode;
227 if (node && node.nodeType() !== Node.ELEMENT_NODE)
235 this._innerUpdate(refresh, null);
238 _innerUpdate: function(refresh, editedSection, userCallback)
240 var node = this.node;
242 this._sectionsContainer.removeChildren();
243 this._computedStylePane.bodyElement.removeChildren();
250 function stylesCallback(styles)
252 if (this.node === node && styles)
253 this._rebuildUpdate(node, styles);
258 function computedStyleCallback(computedStyle)
260 if (this.node === node && computedStyle)
261 this._refreshUpdate(node, computedStyle, editedSection);
267 WebInspector.cssModel.getComputedStyleAsync(node.id, computedStyleCallback.bind(this));
269 WebInspector.cssModel.getStylesAsync(node.id, this._forcedPseudoClasses, stylesCallback.bind(this));
272 _styleSheetChanged: function()
274 if (this._userOperation || this._isEditingStyle)
277 this._innerUpdate(false);
280 _attributesUpdated: function(event)
282 if (this.node !== event.data)
285 // "style" attribute might have changed. Update styles unless they are being edited.
286 if (!this._isEditingStyle && !this._userOperation)
287 this._innerUpdate(false);
290 _refreshUpdate: function(node, computedStyle, editedSection)
292 for (var pseudoId in this.sections) {
293 var styleRules = this._refreshStyleRules(this.sections[pseudoId], computedStyle);
294 var usedProperties = {};
295 var disabledComputedProperties = {};
296 this._markUsedProperties(styleRules, usedProperties, disabledComputedProperties);
297 this._refreshSectionsForStyleRules(styleRules, usedProperties, disabledComputedProperties, editedSection);
299 // Trace the computed style.
300 this.sections[0][0].rebuildComputedTrace(this.sections[0]);
302 this._nodeStylesUpdatedForTest(node, true);
305 _rebuildUpdate: function(node, styles)
307 this._sectionsContainer.removeChildren();
308 this._computedStylePane.bodyElement.removeChildren();
310 var styleRules = this._rebuildStyleRules(node, styles);
311 var usedProperties = {};
312 var disabledComputedProperties = {};
313 this._markUsedProperties(styleRules, usedProperties, disabledComputedProperties);
314 this.sections[0] = this._rebuildSectionsForStyleRules(styleRules, usedProperties, disabledComputedProperties, 0);
315 var anchorElement = this.sections[0].inheritedPropertiesSeparatorElement;
316 // Trace the computed style.
317 this.sections[0][0].rebuildComputedTrace(this.sections[0]);
319 for (var i = 0; i < styles.pseudoElements.length; ++i) {
320 var pseudoElementCSSRules = styles.pseudoElements[i];
323 var pseudoId = pseudoElementCSSRules.pseudoId;
325 var entry = { isStyleSeparator: true, pseudoId: pseudoId };
326 styleRules.push(entry);
328 // Add rules in reverse order to match the cascade order.
329 for (var j = pseudoElementCSSRules.rules.length - 1; j >= 0; --j) {
330 var rule = pseudoElementCSSRules.rules[j];
331 styleRules.push({ style: rule.style, selectorText: rule.selectorText, sourceURL: rule.sourceURL, rule: rule, editable: !!(rule.style && rule.style.id) });
334 disabledComputedProperties = {};
335 this._markUsedProperties(styleRules, usedProperties, disabledComputedProperties);
336 this.sections[pseudoId] = this._rebuildSectionsForStyleRules(styleRules, usedProperties, disabledComputedProperties, pseudoId, anchorElement);
339 this._nodeStylesUpdatedForTest(node, false);
342 _nodeStylesUpdatedForTest: function(node, refresh)
344 // Tests override this method.
347 _refreshStyleRules: function(sections, computedStyle)
349 var nodeComputedStyle = computedStyle;
351 for (var i = 0; sections && i < sections.length; ++i) {
352 var section = sections[i];
353 if (section instanceof WebInspector.BlankStylePropertiesSection)
355 if (section.computedStyle)
356 section.styleRule.style = nodeComputedStyle;
357 var styleRule = { section: section, style: section.styleRule.style, computedStyle: section.computedStyle, rule: section.rule, editable: !!(section.styleRule.style && section.styleRule.style.id) };
358 styleRules.push(styleRule);
363 _rebuildStyleRules: function(node, styles)
365 var nodeComputedStyle = styles.computedStyle;
370 function addStyleAttributes()
372 for (var name in styles.styleAttributes) {
373 var attrStyle = { style: styles.styleAttributes[name], editable: false };
374 attrStyle.selectorText = WebInspector.panels.elements.treeOutline.nodeNameToCorrectCase(node.nodeName()) + "[" + name;
375 if (node.getAttribute(name))
376 attrStyle.selectorText += "=" + node.getAttribute(name);
377 attrStyle.selectorText += "]";
378 styleRules.push(attrStyle);
382 styleRules.push({ computedStyle: true, selectorText: "", style: nodeComputedStyle, editable: false });
384 // Inline style has the greatest specificity.
385 if (styles.inlineStyle && node.nodeType() === Node.ELEMENT_NODE) {
386 var inlineStyle = { selectorText: "element.style", style: styles.inlineStyle, isAttribute: true };
387 styleRules.push(inlineStyle);
390 // Add rules in reverse order to match the cascade order.
391 if (styles.matchedCSSRules.length)
392 styleRules.push({ isStyleSeparator: true, text: WebInspector.UIString("Matched CSS Rules") });
393 var addedStyleAttributes;
394 for (var i = styles.matchedCSSRules.length - 1; i >= 0; --i) {
395 var rule = styles.matchedCSSRules[i];
396 if (!WebInspector.settings.showUserAgentStyles.get() && (rule.isUser || rule.isUserAgent))
398 if ((rule.isUser || rule.isUserAgent) && !addedStyleAttributes) {
399 // Show element's Style Attributes after all author rules.
400 addedStyleAttributes = true;
401 addStyleAttributes();
403 styleRules.push({ style: rule.style, selectorText: rule.selectorText, sourceURL: rule.sourceURL, rule: rule, editable: !!(rule.style && rule.style.id) });
406 if (!addedStyleAttributes)
407 addStyleAttributes();
409 // Walk the node structure and identify styles with inherited properties.
410 var parentNode = node.parentNode;
411 function insertInheritedNodeSeparator(node)
414 entry.isStyleSeparator = true;
416 styleRules.push(entry);
419 for (var parentOrdinal = 0; parentOrdinal < styles.inherited.length; ++parentOrdinal) {
420 var parentStyles = styles.inherited[parentOrdinal];
421 var separatorInserted = false;
422 if (parentStyles.inlineStyle) {
423 if (this._containsInherited(parentStyles.inlineStyle)) {
424 var inlineStyle = { selectorText: WebInspector.UIString("Style Attribute"), style: parentStyles.inlineStyle, isAttribute: true, isInherited: true };
425 if (!separatorInserted) {
426 insertInheritedNodeSeparator(parentNode);
427 separatorInserted = true;
429 styleRules.push(inlineStyle);
433 for (var i = parentStyles.matchedCSSRules.length - 1; i >= 0; --i) {
434 var rulePayload = parentStyles.matchedCSSRules[i];
435 if (!this._containsInherited(rulePayload.style))
437 var rule = rulePayload;
438 if (!WebInspector.settings.showUserAgentStyles.get() && (rule.isUser || rule.isUserAgent))
441 if (!separatorInserted) {
442 insertInheritedNodeSeparator(parentNode);
443 separatorInserted = true;
445 styleRules.push({ style: rule.style, selectorText: rule.selectorText, sourceURL: rule.sourceURL, rule: rule, isInherited: true, editable: !!(rule.style && rule.style.id) });
447 parentNode = parentNode.parentNode;
452 _markUsedProperties: function(styleRules, usedProperties, disabledComputedProperties)
454 var priorityUsed = false;
456 // Walk the style rules and make a list of all used and overloaded properties.
457 for (var i = 0; i < styleRules.length; ++i) {
458 var styleRule = styleRules[i];
459 if (styleRule.computedStyle || styleRule.isStyleSeparator)
461 if (styleRule.section && styleRule.section.noAffect)
464 styleRule.usedProperties = {};
466 var style = styleRule.style;
467 var allProperties = style.allProperties;
468 for (var j = 0; j < allProperties.length; ++j) {
469 var property = allProperties[j];
470 if (!property.isLive || !property.parsedOk)
472 var name = property.name;
474 if (!priorityUsed && property.priority.length)
477 // If the property name is already used by another rule then this rule's
478 // property is overloaded, so don't add it to the rule's usedProperties.
479 if (!(name in usedProperties))
480 styleRule.usedProperties[name] = true;
482 if (name === "font") {
483 // The font property is not reported as a shorthand. Report finding the individual
484 // properties so they are visible in computed style.
485 // FIXME: remove this when http://bugs.webkit.org/show_bug.cgi?id=15598 is fixed.
486 styleRule.usedProperties["font-family"] = true;
487 styleRule.usedProperties["font-size"] = true;
488 styleRule.usedProperties["font-style"] = true;
489 styleRule.usedProperties["font-variant"] = true;
490 styleRule.usedProperties["font-weight"] = true;
491 styleRule.usedProperties["line-height"] = true;
495 // Add all the properties found in this style to the used properties list.
496 // Do this here so only future rules are affect by properties used in this rule.
497 for (var name in styleRules[i].usedProperties)
498 usedProperties[name] = true;
502 // Walk the properties again and account for !important.
503 var foundPriorityProperties = [];
505 // Walk in direct order to detect the active/most specific rule providing a priority
506 // (in this case all subsequent !important values get canceled.)
507 for (var i = 0; i < styleRules.length; ++i) {
508 if (styleRules[i].computedStyle || styleRules[i].isStyleSeparator)
511 var style = styleRules[i].style;
512 var allProperties = style.allProperties;
513 for (var j = 0; j < allProperties.length; ++j) {
514 var property = allProperties[j];
515 if (!property.isLive)
517 var name = property.name;
518 if (property.priority.length) {
519 if (!(name in foundPriorityProperties))
520 styleRules[i].usedProperties[name] = true;
522 delete styleRules[i].usedProperties[name];
523 foundPriorityProperties[name] = true;
524 } else if (name in foundPriorityProperties)
525 delete styleRules[i].usedProperties[name];
531 _refreshSectionsForStyleRules: function(styleRules, usedProperties, disabledComputedProperties, editedSection)
533 // Walk the style rules and update the sections with new overloaded and used properties.
534 for (var i = 0; i < styleRules.length; ++i) {
535 var styleRule = styleRules[i];
536 var section = styleRule.section;
537 if (styleRule.computedStyle) {
538 section._disabledComputedProperties = disabledComputedProperties;
539 section._usedProperties = usedProperties;
542 section._usedProperties = styleRule.usedProperties;
543 section.update(section === editedSection);
548 _rebuildSectionsForStyleRules: function(styleRules, usedProperties, disabledComputedProperties, pseudoId, anchorElement)
550 // Make a property section for each style rule.
552 var lastWasSeparator = true;
553 for (var i = 0; i < styleRules.length; ++i) {
554 var styleRule = styleRules[i];
555 if (styleRule.isStyleSeparator) {
556 var separatorElement = document.createElement("div");
557 separatorElement.className = "styles-sidebar-separator";
558 if (styleRule.node) {
559 var link = WebInspector.panels.elements.linkifyNodeReference(styleRule.node);
560 separatorElement.appendChild(document.createTextNode(WebInspector.UIString("Inherited from") + " "));
561 separatorElement.appendChild(link);
562 if (!sections.inheritedPropertiesSeparatorElement)
563 sections.inheritedPropertiesSeparatorElement = separatorElement;
564 } else if ("pseudoId" in styleRule) {
565 var pseudoName = WebInspector.StylesSidebarPane.PseudoIdNames[styleRule.pseudoId];
567 separatorElement.textContent = WebInspector.UIString("Pseudo ::%s element", pseudoName);
569 separatorElement.textContent = WebInspector.UIString("Pseudo element");
571 separatorElement.textContent = styleRule.text;
572 this._sectionsContainer.insertBefore(separatorElement, anchorElement);
573 lastWasSeparator = true;
576 var computedStyle = styleRule.computedStyle;
578 // Default editable to true if it was omitted.
579 var editable = styleRule.editable;
580 if (typeof editable === "undefined")
584 var section = new WebInspector.ComputedStylePropertiesSection(styleRule, usedProperties, disabledComputedProperties, styleRules);
586 var section = new WebInspector.StylePropertiesSection(this, styleRule, editable, styleRule.isInherited, lastWasSeparator);
588 section.expanded = true;
591 this._computedStylePane.bodyElement.appendChild(section.element);
592 lastWasSeparator = true;
594 this._sectionsContainer.insertBefore(section.element, anchorElement);
595 lastWasSeparator = false;
597 sections.push(section);
602 _containsInherited: function(style)
604 var properties = style.allProperties;
605 for (var i = 0; i < properties.length; ++i) {
606 var property = properties[i];
607 // Does this style contain non-overridden inherited property?
608 if (property.isLive && property.name in WebInspector.StylesSidebarPane.InheritedProperties)
614 _colorFormatSettingChanged: function(event)
616 this._updateColorFormatFilter();
617 for (var pseudoId in this.sections) {
618 var sections = this.sections[pseudoId];
619 for (var i = 0; i < sections.length; ++i)
620 sections[i].update(true);
624 _updateColorFormatFilter: function()
626 // Select the correct color format setting again, since it needs to be selected.
627 var selectedIndex = 0;
628 var value = WebInspector.settings.colorFormat.get();
629 var options = this.settingsSelectElement.options;
630 for (var i = 0; i < options.length; ++i) {
631 if (options[i].value === value) {
636 this.settingsSelectElement.selectedIndex = selectedIndex;
639 _changeSetting: function(event)
641 var options = this.settingsSelectElement.options;
642 var selectedOption = options[this.settingsSelectElement.selectedIndex];
643 WebInspector.settings.colorFormat.set(selectedOption.value);
646 _createNewRule: function(event)
648 event.stopPropagation();
649 if (WebInspector.isEditingAnyField())
652 this.expanded = true;
653 this.addBlankSection().startEditingSelector();
656 addBlankSection: function()
658 var blankSection = new WebInspector.BlankStylePropertiesSection(this, this.node ? this.node.appropriateSelectorFor(true) : "");
659 blankSection.pane = this;
661 var elementStyleSection = this.sections[0][1];
662 this._sectionsContainer.insertBefore(blankSection.element, elementStyleSection.element.nextSibling);
664 this.sections[0].splice(2, 0, blankSection);
669 removeSection: function(section)
671 for (var pseudoId in this.sections) {
672 var sections = this.sections[pseudoId];
673 var index = sections.indexOf(section);
676 sections.splice(index, 1);
677 if (section.element.parentNode)
678 section.element.parentNode.removeChild(section.element);
682 registerShortcuts: function()
684 var section = WebInspector.shortcutsScreen.section(WebInspector.UIString("Styles Pane"));
685 var shortcut = WebInspector.KeyboardShortcut;
687 shortcut.shortcutToString(shortcut.Keys.Tab),
688 shortcut.shortcutToString(shortcut.Keys.Tab, shortcut.Modifiers.Shift)
690 section.addRelatedKeys(keys, WebInspector.UIString("Next/previous property"));
692 shortcut.shortcutToString(shortcut.Keys.Up),
693 shortcut.shortcutToString(shortcut.Keys.Down)
695 section.addRelatedKeys(keys, WebInspector.UIString("Increment/decrement value"));
697 shortcut.shortcutToString(shortcut.Keys.Up, shortcut.Modifiers.Shift),
698 shortcut.shortcutToString(shortcut.Keys.Down, shortcut.Modifiers.Shift)
700 section.addRelatedKeys(keys, WebInspector.UIString("Increment/decrement by %f", 10));
702 shortcut.shortcutToString(shortcut.Keys.PageUp),
703 shortcut.shortcutToString(shortcut.Keys.PageDown)
705 section.addRelatedKeys(keys, WebInspector.UIString("Increment/decrement by %f", 10));
707 shortcut.shortcutToString(shortcut.Keys.PageUp, shortcut.Modifiers.Shift),
708 shortcut.shortcutToString(shortcut.Keys.PageDown, shortcut.Modifiers.Shift)
710 section.addRelatedKeys(keys, WebInspector.UIString("Increment/decrement by %f", 100));
712 shortcut.shortcutToString(shortcut.Keys.PageUp, shortcut.Modifiers.Alt),
713 shortcut.shortcutToString(shortcut.Keys.PageDown, shortcut.Modifiers.Alt)
715 section.addRelatedKeys(keys, WebInspector.UIString("Increment/decrement by %f", 0.1));
718 _toggleElementStatePane: function(event)
720 event.stopPropagation();
721 if (!this._elementStateButton.hasStyleClass("toggled")) {
723 this._elementStateButton.addStyleClass("toggled");
724 this._elementStatePane.addStyleClass("expanded");
726 this._elementStateButton.removeStyleClass("toggled");
727 this._elementStatePane.removeStyleClass("expanded");
728 // Clear flags on hide.
729 if (this._forcedPseudoClasses) {
730 for (var i = 0; i < this._elementStatePane.inputs.length; ++i)
731 this._elementStatePane.inputs[i].checked = false;
732 delete this._forcedPseudoClasses;
733 this._innerUpdate(false);
738 _createElementStatePane: function()
740 this._elementStatePane = document.createElement("div");
741 this._elementStatePane.className = "styles-element-state-pane source-code";
742 var table = document.createElement("table");
745 this._elementStatePane.inputs = inputs;
747 function clickListener(event)
749 var pseudoClasses = [];
750 for (var i = 0; i < inputs.length; ++i) {
751 if (inputs[i].checked)
752 pseudoClasses.push(inputs[i].state);
754 this._forcedPseudoClasses = pseudoClasses.length ? pseudoClasses : undefined;
755 this._innerUpdate(false);
758 function createCheckbox(state)
760 var td = document.createElement("td");
761 var label = document.createElement("label");
762 var input = document.createElement("input");
763 input.type = "checkbox";
765 input.addEventListener("click", clickListener.bind(this), false);
767 label.appendChild(input);
768 label.appendChild(document.createTextNode(":" + state));
769 td.appendChild(label);
773 var tr = document.createElement("tr");
774 tr.appendChild(createCheckbox.call(this, "active"));
775 tr.appendChild(createCheckbox.call(this, "hover"));
776 table.appendChild(tr);
778 tr = document.createElement("tr");
779 tr.appendChild(createCheckbox.call(this, "focus"));
780 tr.appendChild(createCheckbox.call(this, "visited"));
781 table.appendChild(tr);
783 this._elementStatePane.appendChild(table);
786 _showUserAgentStylesSettingChanged: function()
788 this._innerUpdate(false);
792 WebInspector.StylesSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype;
794 WebInspector.ComputedStyleSidebarPane = function()
796 WebInspector.SidebarPane.call(this, WebInspector.UIString("Computed Style"));
797 var showInheritedCheckbox = new WebInspector.Checkbox(WebInspector.UIString("Show inherited"), "sidebar-pane-subtitle");
798 this.titleElement.appendChild(showInheritedCheckbox.element);
800 if (WebInspector.settings.showInheritedComputedStyleProperties.get()) {
801 this.bodyElement.addStyleClass("show-inherited");
802 showInheritedCheckbox.checked = true;
805 function showInheritedToggleFunction(event)
807 WebInspector.settings.showInheritedComputedStyleProperties.set(showInheritedCheckbox.checked);
808 if (WebInspector.settings.showInheritedComputedStyleProperties.get())
809 this.bodyElement.addStyleClass("show-inherited");
811 this.bodyElement.removeStyleClass("show-inherited");
814 showInheritedCheckbox.addEventListener(showInheritedToggleFunction.bind(this));
817 WebInspector.ComputedStyleSidebarPane.prototype.__proto__ = WebInspector.SidebarPane.prototype;
819 WebInspector.StylePropertiesSection = function(parentPane, styleRule, editable, isInherited, isFirstSection)
821 WebInspector.PropertiesSection.call(this, "");
822 this.element.className = "styles-section monospace" + (isFirstSection ? " first-styles-section" : "");
824 this._selectorElement = document.createElement("span");
825 this._selectorElement.textContent = styleRule.selectorText;
826 this.titleElement.appendChild(this._selectorElement);
827 if (Preferences.debugMode)
828 this._selectorElement.addEventListener("click", this._debugShowStyle.bind(this), false);
830 var openBrace = document.createElement("span");
831 openBrace.textContent = " {";
832 this.titleElement.appendChild(openBrace);
834 var closeBrace = document.createElement("div");
835 closeBrace.textContent = "}";
836 this.element.appendChild(closeBrace);
838 this._selectorElement.addEventListener("dblclick", this._handleSelectorDoubleClick.bind(this), false);
839 this.element.addEventListener("dblclick", this._handleEmptySpaceDoubleClick.bind(this), false);
841 this._parentPane = parentPane;
842 this.styleRule = styleRule;
843 this.rule = this.styleRule.rule;
844 this.editable = editable;
845 this.isInherited = isInherited;
847 // Prevent editing the user agent and user rules.
848 var isUserAgent = this.rule && this.rule.isUserAgent;
849 var isUser = this.rule && this.rule.isUser;
850 var isViaInspector = this.rule && this.rule.isViaInspector;
852 if (isUserAgent || isUser)
853 this.editable = false;
855 this._usedProperties = styleRule.usedProperties;
858 this.titleElement.addStyleClass("styles-selector");
860 function linkifyUncopyable(url, line)
862 var link = WebInspector.linkifyResourceAsNode(url, line);
867 if (this.styleRule.sourceURL)
868 this.subtitleElement.appendChild(linkifyUncopyable(this.styleRule.sourceURL, this.rule.sourceLine));
869 else if (isUserAgent)
870 subtitle = WebInspector.UIString("user agent stylesheet");
872 subtitle = WebInspector.UIString("user stylesheet");
873 else if (isViaInspector)
874 subtitle = WebInspector.UIString("via inspector");
875 else if (this.rule && this.rule.sourceURL)
876 this.subtitleElement.appendChild(linkifyUncopyable(this.rule.sourceURL, this.rule.sourceLine));
879 this.element.addStyleClass("show-inherited"); // This one is related to inherited rules, not compted style.
881 this.subtitle = subtitle;
883 this.identifier = styleRule.selectorText;
885 this.identifier += ":" + this.subtitle;
888 this.element.addStyleClass("read-only");
891 WebInspector.StylePropertiesSection.prototype = {
892 collapse: function(dontRememberState)
894 // Overriding with empty body.
897 isPropertyInherited: function(propertyName)
899 if (this.isInherited) {
900 // While rendering inherited stylesheet, reverse meaning of this property.
901 // Render truly inherited properties with black, i.e. return them as non-inherited.
902 return !(propertyName in WebInspector.StylesSidebarPane.InheritedProperties);
907 isPropertyOverloaded: function(propertyName, shorthand)
909 if (!this._usedProperties || this.noAffect)
912 if (this.isInherited && !(propertyName in WebInspector.StylesSidebarPane.InheritedProperties)) {
913 // In the inherited sections, only show overrides for the potentially inherited properties.
917 var used = (propertyName in this._usedProperties);
918 if (used || !shorthand)
921 // Find out if any of the individual longhand properties of the shorthand
922 // are used, if none are then the shorthand is overloaded too.
923 var longhandProperties = this.styleRule.style.getLonghandProperties(propertyName);
924 for (var j = 0; j < longhandProperties.length; ++j) {
925 var individualProperty = longhandProperties[j];
926 if (individualProperty.name in this._usedProperties)
933 nextEditableSibling: function()
935 var curSection = this;
937 curSection = curSection.nextSibling;
938 } while (curSection && !curSection.editable);
941 curSection = this.firstSibling;
942 while (curSection && !curSection.editable)
943 curSection = curSection.nextSibling;
946 return (curSection && curSection.editable) ? curSection : null;
949 previousEditableSibling: function()
951 var curSection = this;
953 curSection = curSection.previousSibling;
954 } while (curSection && !curSection.editable);
957 curSection = this.lastSibling;
958 while (curSection && !curSection.editable)
959 curSection = curSection.previousSibling;
962 return (curSection && curSection.editable) ? curSection : null;
965 update: function(full)
968 this.propertiesTreeOutline.removeChildren();
969 this.populated = false;
971 var child = this.propertiesTreeOutline.children[0];
973 child.overloaded = this.isPropertyOverloaded(child.name, child.shorthand);
974 child = child.traverseNextTreeElement(false, null, true);
980 afterUpdate: function()
982 if (this._afterUpdate) {
983 this._afterUpdate(this);
984 delete this._afterUpdate;
988 onpopulate: function()
990 var style = this.styleRule.style;
992 var handledProperties = {};
993 var shorthandNames = {};
995 this.uniqueProperties = [];
996 var allProperties = style.allProperties;
997 for (var i = 0; i < allProperties.length; ++i)
998 this.uniqueProperties.push(allProperties[i]);
1000 // Collect all shorthand names.
1001 for (var i = 0; i < this.uniqueProperties.length; ++i) {
1002 var property = this.uniqueProperties[i];
1003 if (property.disabled)
1005 if (property.shorthand)
1006 shorthandNames[property.shorthand] = true;
1009 // Collect all shorthand names.
1010 for (var i = 0; i < this.uniqueProperties.length; ++i) {
1011 var property = this.uniqueProperties[i];
1012 var disabled = property.disabled;
1013 if (!disabled && this.disabledComputedProperties && !(property.name in this.usedProperties) && property.name in this.disabledComputedProperties)
1016 var shorthand = !disabled ? property.shorthand : null;
1018 if (shorthand && shorthand in handledProperties)
1022 property = style.getLiveProperty(shorthand);
1024 property = new WebInspector.CSSProperty(style, style.allProperties.length, shorthand, style.getShorthandValue(shorthand), style.getShorthandPriority(shorthand), "style", true, true, "");
1027 var isShorthand = !!(property.isLive && (shorthand || shorthandNames[property.name]));
1028 var inherited = this.isPropertyInherited(property.name);
1029 var overloaded = this.isPropertyOverloaded(property.name, isShorthand);
1031 var item = new WebInspector.StylePropertyTreeElement(this._parentPane, this.styleRule, style, property, isShorthand, inherited, overloaded);
1032 this.propertiesTreeOutline.appendChild(item);
1033 handledProperties[property.name] = property;
1037 findTreeElementWithName: function(name)
1039 var treeElement = this.propertiesTreeOutline.children[0];
1040 while (treeElement) {
1041 if (treeElement.name === name)
1043 treeElement = treeElement.traverseNextTreeElement(true, null, true);
1048 addNewBlankProperty: function(optionalIndex)
1050 var style = this.styleRule.style;
1051 var property = style.newBlankProperty();
1052 var item = new WebInspector.StylePropertyTreeElement(this._parentPane, this.styleRule, style, property, false, false, false);
1053 this.propertiesTreeOutline.appendChild(item);
1054 item.listItemElement.textContent = "";
1055 item._newProperty = true;
1060 _debugShowStyle: function(anchor)
1063 function removeStyleBox(element, event)
1065 if (event.target === element) {
1066 event.stopPropagation();
1069 document.body.removeChild(element);
1070 document.getElementById("main").removeEventListener("mousedown", boundHandler, true);
1073 if (!event.shiftKey)
1076 var container = document.createElement("div");
1077 var element = document.createElement("span");
1078 container.appendChild(element);
1079 element.style.background = "yellow";
1080 element.style.display = "inline-block";
1081 container.style.cssText = "z-index: 2000000; position: absolute; top: 50px; left: 50px; white-space: pre; overflow: auto; background: white; font-family: monospace; font-size: 12px; border: 1px solid black; opacity: 0.85; -webkit-user-select: text; padding: 2px;";
1082 container.style.width = (document.body.offsetWidth - 100) + "px";
1083 container.style.height = (document.body.offsetHeight - 100) + "px";
1084 document.body.appendChild(container);
1086 element.textContent = this.rule.selectorText + " {" + ((this.styleRule.style.cssText !== undefined) ? this.styleRule.style.cssText : "<no cssText>") + "}";
1088 element.textContent = this.styleRule.style.cssText;
1089 boundHandler = removeStyleBox.bind(null, container);
1090 document.getElementById("main").addEventListener("mousedown", boundHandler, true);
1093 _handleEmptySpaceDoubleClick: function(event)
1095 if (event.target.hasStyleClass("header") || this.element.hasStyleClass("read-only")) {
1096 event.stopPropagation();
1100 this.addNewBlankProperty().startEditing();
1103 _handleSelectorClick: function(event)
1105 event.stopPropagation();
1108 _handleSelectorDoubleClick: function(event)
1110 this._startEditingOnMouseEvent();
1111 event.stopPropagation();
1114 _startEditingOnMouseEvent: function()
1119 if (!this.rule && this.propertiesTreeOutline.children.length === 0) {
1121 this.addNewBlankProperty().startEditing();
1128 this.startEditingSelector();
1131 startEditingSelector: function()
1133 var element = this._selectorElement;
1134 if (WebInspector.isBeingEdited(element))
1137 this._selectorElement.scrollIntoViewIfNeeded(false);
1138 WebInspector.startEditing(this._selectorElement, {
1140 commitHandler: this.editingSelectorCommitted.bind(this),
1141 cancelHandler: this.editingSelectorCancelled.bind(this)
1143 window.getSelection().setBaseAndExtent(element, 0, element, 1);
1146 _moveEditorFromSelector: function(moveDirection)
1151 if (moveDirection === "forward") {
1153 var firstChild = this.propertiesTreeOutline.children[0];
1155 this.addNewBlankProperty().startEditing();
1157 firstChild.startEditing(firstChild.nameElement);
1159 var previousSection = this.previousEditableSibling();
1160 if (!previousSection)
1163 previousSection.expand();
1164 previousSection.addNewBlankProperty().startEditing();
1168 editingSelectorCommitted: function(element, newContent, oldContent, context, moveDirection)
1170 if (newContent === oldContent)
1171 return this._moveEditorFromSelector(moveDirection);
1175 function successCallback(newRule, doesAffectSelectedNode)
1177 if (!doesAffectSelectedNode) {
1178 self.noAffect = true;
1179 self.element.addStyleClass("no-affect");
1181 delete self.noAffect;
1182 self.element.removeStyleClass("no-affect");
1185 self.rule = newRule;
1186 self.styleRule = { section: self, style: newRule.style, selectorText: newRule.selectorText, sourceURL: newRule.sourceURL, rule: newRule };
1188 var oldIdentifier = this.identifier;
1189 self.identifier = newRule.selectorText + ":" + self.subtitleElement.textContent;
1193 WebInspector.panels.elements.renameSelector(oldIdentifier, this.identifier, oldContent, newContent);
1195 self._moveEditorFromSelector(moveDirection);
1198 var selectedNode = WebInspector.panels.elements.selectedDOMNode();
1199 WebInspector.cssModel.setRuleSelector(this.rule.id, selectedNode ? selectedNode.id : 0, newContent, successCallback, moveToNextIfNeeded.bind(this));
1202 editingSelectorCancelled: function()
1204 // Do nothing, this is overridden by BlankStylePropertiesSection.
1208 WebInspector.StylePropertiesSection.prototype.__proto__ = WebInspector.PropertiesSection.prototype;
1210 WebInspector.ComputedStylePropertiesSection = function(styleRule, usedProperties, disabledComputedProperties)
1212 WebInspector.PropertiesSection.call(this, "");
1213 this.headerElement.addStyleClass("hidden");
1214 this.element.className = "styles-section monospace first-styles-section read-only computed-style";
1215 this.styleRule = styleRule;
1216 this._usedProperties = usedProperties;
1217 this._disabledComputedProperties = disabledComputedProperties;
1218 this._alwaysShowComputedProperties = { "display": true, "height": true, "width": true };
1219 this.computedStyle = true;
1220 this._propertyTreeElements = {};
1221 this._expandedPropertyNames = {};
1224 WebInspector.ComputedStylePropertiesSection.prototype = {
1225 collapse: function(dontRememberState)
1227 // Overriding with empty body.
1230 _isPropertyInherited: function(propertyName)
1232 return !(propertyName in this._usedProperties) && !(propertyName in this._alwaysShowComputedProperties) && !(propertyName in this._disabledComputedProperties);
1237 this._expandedPropertyNames = {};
1238 for (var name in this._propertyTreeElements) {
1239 if (this._propertyTreeElements[name].expanded)
1240 this._expandedPropertyNames[name] = true;
1242 this._propertyTreeElements = {};
1243 this.propertiesTreeOutline.removeChildren();
1244 this.populated = false;
1247 onpopulate: function()
1249 function sorter(a, b)
1251 return a.name.localeCompare(b.name);
1254 var style = this.styleRule.style;
1255 var uniqueProperties = [];
1256 var allProperties = style.allProperties;
1257 for (var i = 0; i < allProperties.length; ++i)
1258 uniqueProperties.push(allProperties[i]);
1259 uniqueProperties.sort(sorter);
1261 this._propertyTreeElements = {};
1262 for (var i = 0; i < uniqueProperties.length; ++i) {
1263 var property = uniqueProperties[i];
1264 var inherited = this._isPropertyInherited(property.name);
1265 var item = new WebInspector.StylePropertyTreeElement(this._parentPane, this.styleRule, style, property, false, inherited, false);
1266 this.propertiesTreeOutline.appendChild(item);
1267 this._propertyTreeElements[property.name] = item;
1271 rebuildComputedTrace: function(sections)
1273 for (var i = 0; i < sections.length; ++i) {
1274 var section = sections[i];
1275 if (section.computedStyle || section instanceof WebInspector.BlankStylePropertiesSection)
1278 for (var j = 0; j < section.uniqueProperties.length; ++j) {
1279 var property = section.uniqueProperties[j];
1280 if (property.disabled)
1282 if (section.isInherited && !(property.name in WebInspector.StylesSidebarPane.InheritedProperties))
1285 var treeElement = this._propertyTreeElements[property.name];
1287 var selectorText = section.styleRule.selectorText;
1288 var value = property.value;
1289 var title = "<span style='color: gray'>" + selectorText + "</span> - " + value;
1290 var subtitle = " <span style='float:right'>" + section.subtitleElement.innerHTML + "</span>";
1291 var childElement = new TreeElement(null, null, false);
1292 childElement.titleHTML = title + subtitle;
1293 treeElement.appendChild(childElement);
1294 if (section.isPropertyOverloaded(property.name))
1295 childElement.listItemElement.addStyleClass("overloaded");
1296 if (!property.parsedOk)
1297 childElement.listItemElement.addStyleClass("not-parsed-ok");
1302 // Restore expanded state after update.
1303 for (var name in this._expandedPropertyNames) {
1304 if (name in this._propertyTreeElements)
1305 this._propertyTreeElements[name].expand();
1310 WebInspector.ComputedStylePropertiesSection.prototype.__proto__ = WebInspector.PropertiesSection.prototype;
1312 WebInspector.BlankStylePropertiesSection = function(parentPane, defaultSelectorText)
1314 WebInspector.StylePropertiesSection.call(this, parentPane, {selectorText: defaultSelectorText, rule: {isViaInspector: true}}, true, false, false);
1315 this.element.addStyleClass("blank-section");
1318 WebInspector.BlankStylePropertiesSection.prototype = {
1321 // Do nothing, blank sections are not expandable.
1324 editingSelectorCommitted: function(element, newContent, oldContent, context)
1326 function successCallback(newRule, doesSelectorAffectSelectedNode)
1328 var styleRule = { section: this, style: newRule.style, selectorText: newRule.selectorText, sourceURL: newRule.sourceURL, rule: newRule };
1329 this.makeNormal(styleRule);
1331 if (!doesSelectorAffectSelectedNode) {
1332 this.noAffect = true;
1333 this.element.addStyleClass("no-affect");
1336 this.subtitleElement.textContent = WebInspector.UIString("via inspector");
1338 if (this.element.parentElement) // Might have been detached already.
1339 this.addNewBlankProperty().startEditing();
1342 WebInspector.cssModel.addRule(this.pane.node.id, newContent, successCallback.bind(this), this.editingSelectorCancelled.bind(this));
1345 editingSelectorCancelled: function()
1347 this.pane.removeSection(this);
1350 makeNormal: function(styleRule)
1352 this.element.removeStyleClass("blank-section");
1353 this.styleRule = styleRule;
1354 this.rule = styleRule.rule;
1355 this.identifier = styleRule.selectorText + ":via inspector";
1356 this.__proto__ = WebInspector.StylePropertiesSection.prototype;
1360 WebInspector.BlankStylePropertiesSection.prototype.__proto__ = WebInspector.StylePropertiesSection.prototype;
1362 WebInspector.StylePropertyTreeElement = function(parentPane, styleRule, style, property, shorthand, inherited, overloaded)
1364 this._parentPane = parentPane;
1365 this._styleRule = styleRule;
1367 this.property = property;
1368 this.shorthand = shorthand;
1369 this._inherited = inherited;
1370 this._overloaded = overloaded;
1372 // Pass an empty title, the title gets made later in onattach.
1373 TreeElement.call(this, "", null, shorthand);
1376 WebInspector.StylePropertyTreeElement.prototype = {
1379 return this._inherited;
1384 if (x === this._inherited)
1386 this._inherited = x;
1392 return this._overloaded;
1397 if (x === this._overloaded)
1399 this._overloaded = x;
1405 return this.property.disabled;
1410 if (!this.disabled || !this.property.text)
1411 return this.property.name;
1413 var text = this.property.text;
1414 var index = text.indexOf(":");
1416 return this.property.name;
1418 return text.substring(0, index).trim();
1424 return ""; // rely upon raw text to render it in the value field
1425 return this.property.priority;
1430 if (!this.disabled || !this.property.text)
1431 return this.property.value;
1433 var match = this.property.text.match(/(.*);\s*/);
1434 if (!match || !match[1])
1435 return this.property.value;
1437 var text = match[1];
1438 var index = text.indexOf(":");
1440 return this.property.value;
1442 return text.substring(index + 1).trim();
1447 return this.property.parsedOk;
1450 onattach: function()
1455 updateTitle: function()
1457 var value = this.value;
1461 var enabledCheckboxElement;
1462 if (this.parsedOk) {
1463 enabledCheckboxElement = document.createElement("input");
1464 enabledCheckboxElement.className = "enabled-button";
1465 enabledCheckboxElement.type = "checkbox";
1466 enabledCheckboxElement.checked = !this.disabled;
1467 enabledCheckboxElement.addEventListener("change", this.toggleEnabled.bind(this), false);
1470 var nameElement = document.createElement("span");
1471 nameElement.className = "webkit-css-property";
1472 nameElement.textContent = this.name;
1473 this.nameElement = nameElement;
1475 var valueElement = document.createElement("span");
1476 valueElement.className = "value";
1477 this.valueElement = valueElement;
1479 var cf = WebInspector.StylesSidebarPane.ColorFormat;
1484 function processValue(regex, processor, nextProcessor, valueText)
1486 var container = document.createDocumentFragment();
1488 var items = valueText.replace(regex, "\0$1\0").split("\0");
1489 for (var i = 0; i < items.length; ++i) {
1490 if ((i % 2) === 0) {
1492 container.appendChild(nextProcessor(items[i]));
1494 container.appendChild(document.createTextNode(items[i]));
1496 var processedNode = processor(items[i]);
1498 container.appendChild(processedNode);
1505 function linkifyURL(url)
1508 var match = hrefUrl.match(/['"]?([^'"]+)/);
1511 var container = document.createDocumentFragment();
1512 container.appendChild(document.createTextNode("url("));
1513 if (self._styleRule.sourceURL)
1514 hrefUrl = WebInspector.completeURL(self._styleRule.sourceURL, hrefUrl);
1515 else if (WebInspector.panels.elements.selectedDOMNode())
1516 hrefUrl = WebInspector.resourceURLForRelatedNode(WebInspector.panels.elements.selectedDOMNode(), hrefUrl);
1517 var hasResource = !!WebInspector.resourceForURL(hrefUrl);
1518 // FIXME: WebInspector.linkifyURLAsNode() should really use baseURI.
1519 container.appendChild(WebInspector.linkifyURLAsNode(hrefUrl, url, null, !hasResource));
1520 container.appendChild(document.createTextNode(")"));
1524 function processColor(text)
1527 var color = new WebInspector.Color(text);
1529 return document.createTextNode(text);
1532 var swatchElement = document.createElement("span");
1533 swatchElement.title = WebInspector.UIString("Click to change color format");
1534 swatchElement.className = "swatch";
1535 swatchElement.style.setProperty("background-color", text);
1537 swatchElement.addEventListener("click", changeColorDisplay, false);
1538 swatchElement.addEventListener("dblclick", function(event) { event.stopPropagation() }, false);
1541 var formatSetting = WebInspector.settings.colorFormat.get();
1542 if (formatSetting === cf.Original)
1543 format = cf.Original;
1544 else if (Preferences.showColorNicknames && color.nickname)
1545 format = cf.Nickname;
1546 else if (formatSetting === cf.RGB)
1547 format = (color.simple ? cf.RGB : cf.RGBA);
1548 else if (formatSetting === cf.HSL)
1549 format = (color.simple ? cf.HSL : cf.HSLA);
1550 else if (color.simple)
1551 format = (color.hasShortHex() ? cf.ShortHEX : cf.HEX);
1555 var colorValueElement = document.createElement("span");
1556 colorValueElement.textContent = color.toString(format);
1558 function nextFormat(curFormat)
1560 // The format loop is as follows:
1564 // * nickname (if the color has a nickname)
1565 // * if the color is simple:
1566 // - shorthex (if has short hex)
1568 switch (curFormat) {
1570 return color.simple ? cf.RGB : cf.RGBA;
1574 return color.simple ? cf.HSL : cf.HSLA;
1581 return color.hasShortHex() ? cf.ShortHEX : cf.HEX;
1593 return color.hasShortHex() ? cf.ShortHEX : cf.HEX;
1602 function changeColorDisplay(event)
1605 format = nextFormat(format);
1606 var currentValue = color.toString(format || "");
1607 } while (format && currentValue === color.value && format !== cf.Original);
1610 colorValueElement.textContent = currentValue;
1613 var container = document.createDocumentFragment();
1614 container.appendChild(swatchElement);
1615 container.appendChild(colorValueElement);
1619 var colorRegex = /((?:rgb|hsl)a?\([^)]+\)|#[0-9a-fA-F]{6}|#[0-9a-fA-F]{3}|\b\w+\b(?!-))/g;
1620 var colorProcessor = processValue.bind(window, colorRegex, processColor, null);
1622 valueElement.appendChild(processValue(/url\(\s*([^)\s]+)\s*\)/g, linkifyURL, WebInspector.CSSKeywordCompletions.isColorAwareProperty(self.name) ? colorProcessor : null, value));
1625 this.listItemElement.removeChildren();
1626 nameElement.normalize();
1627 valueElement.normalize();
1629 if (!this.treeOutline)
1632 // Append the checkbox for root elements of an editable section.
1633 if (enabledCheckboxElement && this.treeOutline.section && this.treeOutline.section.editable && this.parent.root)
1634 this.listItemElement.appendChild(enabledCheckboxElement);
1635 this.listItemElement.appendChild(nameElement);
1636 this.listItemElement.appendChild(document.createTextNode(": "));
1637 this.listItemElement.appendChild(valueElement);
1638 this.listItemElement.appendChild(document.createTextNode(";"));
1640 if (!this.parsedOk) {
1641 // Avoid having longhands under an invalid shorthand.
1642 this.hasChildren = false;
1643 this.listItemElement.addStyleClass("not-parsed-ok");
1645 if (this.property.inactive)
1646 this.listItemElement.addStyleClass("inactive");
1648 this.tooltip = this.property.propertyText;
1651 _updatePane: function(userCallback)
1653 if (this.treeOutline && this.treeOutline.section && this.treeOutline.section.pane)
1654 this.treeOutline.section.pane._innerUpdate(true, this.treeOutline.section, userCallback);
1661 toggleEnabled: function(event)
1663 var disabled = !event.target.checked;
1665 function callback(newStyle)
1670 this.style = newStyle;
1671 this._styleRule.style = newStyle;
1673 if (this.treeOutline.section && this.treeOutline.section.pane)
1674 this.treeOutline.section.pane.dispatchEventToListeners("style property toggled");
1679 this.property.setDisabled(disabled, callback.bind(this));
1682 updateState: function()
1684 if (!this.listItemElement)
1687 if (this.style.isPropertyImplicit(this.name) || this.value === "initial")
1688 this.listItemElement.addStyleClass("implicit");
1690 this.listItemElement.removeStyleClass("implicit");
1692 this.selectable = !this.inherited;
1694 this.listItemElement.addStyleClass("inherited");
1696 this.listItemElement.removeStyleClass("inherited");
1698 if (this.overloaded)
1699 this.listItemElement.addStyleClass("overloaded");
1701 this.listItemElement.removeStyleClass("overloaded");
1704 this.listItemElement.addStyleClass("disabled");
1706 this.listItemElement.removeStyleClass("disabled");
1709 onpopulate: function()
1711 // Only populate once and if this property is a shorthand.
1712 if (this.children.length || !this.shorthand)
1715 var longhandProperties = this.style.getLonghandProperties(this.name);
1716 for (var i = 0; i < longhandProperties.length; ++i) {
1717 var name = longhandProperties[i].name;
1720 if (this.treeOutline.section) {
1721 var inherited = this.treeOutline.section.isPropertyInherited(name);
1722 var overloaded = this.treeOutline.section.isPropertyOverloaded(name);
1725 var liveProperty = this.style.getLiveProperty(name);
1726 var item = new WebInspector.StylePropertyTreeElement(this._parentPane, this._styleRule, this.style, liveProperty, false, inherited, overloaded);
1727 this.appendChild(item);
1731 ondblclick: function(event)
1733 this.startEditing(event.target);
1734 event.stopPropagation();
1737 restoreNameElement: function()
1739 // Restore <span class="webkit-css-property"> if it doesn't yet exist or was accidentally deleted.
1740 if (this.nameElement === this.listItemElement.querySelector(".webkit-css-property"))
1743 this.nameElement = document.createElement("span");
1744 this.nameElement.className = "webkit-css-property";
1745 this.nameElement.textContent = "";
1746 this.listItemElement.insertBefore(this.nameElement, this.listItemElement.firstChild);
1749 startEditing: function(selectElement)
1751 // FIXME: we don't allow editing of longhand properties under a shorthand right now.
1752 if (this.parent.shorthand)
1755 if (this.treeOutline.section && !this.treeOutline.section.editable)
1759 selectElement = this.nameElement; // No arguments passed in - edit the name element by default.
1761 selectElement = selectElement.enclosingNodeOrSelfWithClass("webkit-css-property") || selectElement.enclosingNodeOrSelfWithClass("value");
1763 var isEditingName = selectElement === this.nameElement;
1764 if (!isEditingName && selectElement !== this.valueElement) {
1765 // Double-click in the LI - start editing value.
1766 isEditingName = false;
1767 selectElement = this.valueElement;
1770 if (WebInspector.isBeingEdited(selectElement))
1774 expanded: this.expanded,
1775 hasChildren: this.hasChildren,
1776 keyDownListener: isEditingName ? null : this.editingValueKeyDown.bind(this),
1777 isEditingName: isEditingName,
1780 // Lie about our children to prevent expanding on double click and to collapse shorthands.
1781 this.hasChildren = false;
1784 selectElement.addEventListener("keydown", context.keyDownListener, false);
1785 if (selectElement.parentElement)
1786 selectElement.parentElement.addStyleClass("child-editing");
1787 selectElement.textContent = selectElement.textContent; // remove color swatch and the like
1789 function shouldCommitValueSemicolon(text, cursorPosition)
1791 // FIXME: should this account for semicolons inside comments?
1793 for (var i = 0; i < cursorPosition; ++i) {
1795 if (ch === "\\" && openQuote !== "")
1796 ++i; // skip next character inside string
1797 else if (!openQuote && (ch === "\"" || ch === "'"))
1799 else if (openQuote === ch)
1805 function nameValueFinishHandler(context, isEditingName, event)
1807 // FIXME: the ":"/";" detection does not work for non-US layouts due to the event being keydown rather than keypress.
1808 var isFieldInputTerminated = (event.keyCode === WebInspector.KeyboardShortcut.Keys.Semicolon.code) &&
1809 (isEditingName ? event.shiftKey : (!event.shiftKey && shouldCommitValueSemicolon(event.target.textContent, event.target.selectionLeftOffset)));
1810 if (isEnterKey(event) || isFieldInputTerminated) {
1811 // Enter or colon (for name)/semicolon outside of string (for value).
1812 event.preventDefault();
1813 return "move-forward";
1814 } else if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code || event.keyIdentifier === "U+001B")
1816 else if (!isEditingName && this._newProperty && event.keyCode === WebInspector.KeyboardShortcut.Keys.Backspace.code) {
1817 // For a new property, when Backspace is pressed at the beginning of new property value, move back to the property name.
1818 var selection = window.getSelection();
1819 if (selection.isCollapsed && !selection.focusOffset) {
1820 event.preventDefault();
1821 return "move-backward";
1823 } else if (event.keyIdentifier === "U+0009") // Tab key.
1824 return "move-" + (event.shiftKey ? "backward" : "forward");
1827 function pasteHandler(context, event)
1829 var data = event.clipboardData.getData("Text");
1832 var colonIdx = data.indexOf(":");
1835 var name = data.substring(0, colonIdx).trim();
1836 var value = data.substring(colonIdx + 1).trim();
1838 event.preventDefault();
1840 if (!("originalName" in context)) {
1841 context.originalName = this.nameElement.textContent;
1842 context.originalValue = this.valueElement.textContent;
1844 this.nameElement.textContent = name;
1845 this.valueElement.textContent = value;
1846 this.nameElement.normalize();
1847 this.valueElement.normalize();
1849 return "move-forward";
1852 delete this.originalPropertyText;
1854 this._parentPane._isEditingStyle = true;
1855 if (selectElement.parentElement)
1856 selectElement.parentElement.scrollIntoViewIfNeeded(false);
1857 WebInspector.startEditing(selectElement, {
1859 commitHandler: this.editingCommitted.bind(this),
1860 cancelHandler: this.editingCancelled.bind(this),
1861 customFinishHandler: nameValueFinishHandler.bind(this, context, isEditingName),
1862 pasteHandler: isEditingName ? pasteHandler.bind(this, context) : null
1865 this._prompt = new WebInspector.StylesSidebarPane.CSSPropertyPrompt(selectElement, isEditingName ? WebInspector.cssNameCompletions : WebInspector.CSSKeywordCompletions.forProperty(this.nameElement.textContent));
1866 window.getSelection().setBaseAndExtent(selectElement, 0, selectElement, 1);
1869 editingValueKeyDown: function(event)
1873 if (this._handleUpOrDownKeyPressed(event))
1876 this._applyFreeFlowStyleTextEdit();
1879 _applyFreeFlowStyleTextEdit: function(now)
1881 if (this._applyFreeFlowStyleTextEditTimer)
1882 clearTimeout(this._applyFreeFlowStyleTextEditTimer);
1886 this.applyStyleText(this.nameElement.textContent + ": " + this.valueElement.textContent);
1891 this._applyFreeFlowStyleTextEditTimer = setTimeout(apply.bind(this), 100);
1894 kickFreeFlowStyleEditForTest: function()
1896 this._applyFreeFlowStyleTextEdit(true);
1899 _handleUpOrDownKeyPressed: function(event)
1901 var arrowKeyPressed = (event.keyIdentifier === "Up" || event.keyIdentifier === "Down");
1902 var pageKeyPressed = (event.keyIdentifier === "PageUp" || event.keyIdentifier === "PageDown");
1903 if (!arrowKeyPressed && !pageKeyPressed)
1906 var selection = window.getSelection();
1907 if (!selection.rangeCount)
1910 var selectionRange = selection.getRangeAt(0);
1911 if (selectionRange.commonAncestorContainer !== this.valueElement && !selectionRange.commonAncestorContainer.isDescendant(this.valueElement))
1914 var wordRange = selectionRange.startContainer.rangeOfWord(selectionRange.startOffset, WebInspector.StylesSidebarPane.StyleValueDelimiters, this.valueElement);
1915 var wordString = wordRange.toString();
1916 var replacementString;
1917 var prefix, suffix, number;
1920 matches = /(.*#)([\da-fA-F]+)(.*)/.exec(wordString);
1921 if (matches && matches.length) {
1922 prefix = matches[1];
1923 suffix = matches[3];
1924 number = WebInspector.StylesSidebarPane.alteredHexNumber(matches[2], event);
1926 replacementString = prefix + number + suffix;
1928 matches = /(.*?)(-?(?:\d+(?:\.\d+)?|\.\d+))(.*)/.exec(wordString);
1929 if (matches && matches.length) {
1930 prefix = matches[1];
1931 suffix = matches[3];
1932 number = WebInspector.StylesSidebarPane.alteredFloatNumber(parseFloat(matches[2]), event);
1933 if (number === null) {
1934 // Need to check for null explicitly.
1938 replacementString = prefix + number + suffix;
1942 if (replacementString) {
1943 var replacementTextNode = document.createTextNode(replacementString);
1945 wordRange.deleteContents();
1946 wordRange.insertNode(replacementTextNode);
1948 var finalSelectionRange = document.createRange();
1949 finalSelectionRange.setStart(replacementTextNode, 0);
1950 finalSelectionRange.setEnd(replacementTextNode, replacementString.length);
1952 selection.removeAllRanges();
1953 selection.addRange(finalSelectionRange);
1955 event.handled = true;
1956 event.preventDefault();
1958 // Synthesize property text disregarding any comments, custom whitespace etc.
1959 this.applyStyleText(this.nameElement.textContent + ": " + this.valueElement.textContent);
1964 editingEnded: function(context)
1966 if (this._applyFreeFlowStyleTextEditTimer)
1967 clearTimeout(this._applyFreeFlowStyleTextEditTimer);
1969 this.hasChildren = context.hasChildren;
1970 if (context.expanded)
1972 var editedElement = context.isEditingName ? this.nameElement : this.valueElement;
1973 if (!context.isEditingName)
1974 editedElement.removeEventListener("keydown", context.keyDownListener, false);
1975 if (editedElement.parentElement)
1976 editedElement.parentElement.removeStyleClass("child-editing");
1978 delete this._parentPane._isEditingStyle;
1981 editingCancelled: function(element, context)
1983 this._removePrompt();
1984 this._revertStyleUponEditingCanceled(this.originalPropertyText);
1985 // This should happen last, as it clears the info necessary to restore the property value after [Page]Up/Down changes.
1986 this.editingEnded(context);
1989 _revertStyleUponEditingCanceled: function(originalPropertyText)
1991 if (typeof originalPropertyText === "string") {
1992 delete this.originalPropertyText;
1993 this.applyStyleText(originalPropertyText, true, false, true);
1995 if (this._newProperty)
1996 this.treeOutline.removeChild(this);
2002 editingCommitted: function(element, userInput, previousContent, context, moveDirection)
2004 this._removePrompt();
2005 this.editingEnded(context);
2006 var isEditingName = context.isEditingName;
2008 // Determine where to move to before making changes
2009 var createNewProperty, moveToPropertyName, moveToSelector;
2011 var moveToOther = (isEditingName ^ (moveDirection === "forward"));
2012 var abandonNewProperty = this._newProperty && !userInput && (moveToOther || isEditingName);
2013 if (moveDirection === "forward" && !isEditingName || moveDirection === "backward" && isEditingName) {
2015 moveTo = (moveDirection === "forward" ? moveTo.nextSibling : moveTo.previousSibling);
2016 } while(moveTo && !moveTo.selectable);
2019 moveToPropertyName = moveTo.name;
2020 else if (moveDirection === "forward" && (!this._newProperty || userInput))
2021 createNewProperty = true;
2022 else if (moveDirection === "backward")
2023 moveToSelector = true;
2026 // Make the Changes and trigger the moveToNextCallback after updating.
2027 var blankInput = /^\s*$/.test(userInput);
2028 var isDataPasted = "originalName" in context;
2029 var isDirtyViaPaste = isDataPasted && (this.nameElement.textContent !== context.originalName || this.valueElement.textContent !== context.originalValue);
2030 var shouldCommitNewProperty = this._newProperty && (moveToOther || (!moveDirection && !isEditingName) || (isEditingName && blankInput));
2031 if (((userInput !== previousContent || isDirtyViaPaste) && !this._newProperty) || shouldCommitNewProperty) {
2032 this.treeOutline.section._afterUpdate = moveToNextCallback.bind(this, this._newProperty, !blankInput, this.treeOutline.section);
2034 if (blankInput || (this._newProperty && /^\s*$/.test(this.valueElement.textContent)))
2038 propertyText = userInput + ": " + this.valueElement.textContent;
2040 propertyText = this.nameElement.textContent + ": " + userInput;
2042 this.applyStyleText(propertyText, true, true);
2044 if (!isDataPasted && !this._newProperty)
2046 moveToNextCallback.call(this, this._newProperty, false, this.treeOutline.section);
2049 var moveToIndex = moveTo && this.treeOutline ? this.treeOutline.children.indexOf(moveTo) : -1;
2051 // The Callback to start editing the next/previous property/selector.
2052 function moveToNextCallback(alreadyNew, valueChanged, section)
2057 // User just tabbed through without changes.
2058 if (moveTo && moveTo.parent) {
2059 moveTo.startEditing(!isEditingName ? moveTo.nameElement : moveTo.valueElement);
2063 // User has made a change then tabbed, wiping all the original treeElements.
2064 // Recalculate the new treeElement for the same property we were going to edit next.
2065 if (moveTo && !moveTo.parent) {
2066 var propertyElements = section.propertiesTreeOutline.children;
2067 if (moveDirection === "forward" && blankInput && !isEditingName)
2069 if (moveToIndex >= propertyElements.length && !this._newProperty)
2070 createNewProperty = true;
2072 var treeElement = moveToIndex >= 0 ? propertyElements[moveToIndex] : null;
2074 treeElement.startEditing(!isEditingName ? treeElement.nameElement : treeElement.valueElement);
2076 } else if (!alreadyNew)
2077 moveToSelector = true;
2081 // Create a new attribute in this section (or move to next editable selector if possible).
2082 if (createNewProperty) {
2083 if (alreadyNew && !valueChanged && (isEditingName ^ (moveDirection === "backward")))
2086 section.addNewBlankProperty().startEditing();
2090 if (abandonNewProperty) {
2091 var sectionToEdit = moveDirection === "backward" ? section : section.nextEditableSibling();
2092 if (sectionToEdit) {
2093 if (sectionToEdit.rule)
2094 sectionToEdit.startEditingSelector();
2096 sectionToEdit._moveEditorFromSelector(moveDirection);
2101 if (moveToSelector) {
2103 section.startEditingSelector();
2105 section._moveEditorFromSelector(moveDirection);
2110 _removePrompt: function()
2112 // BUG 53242. This cannot go into editingEnded(), as it should always happen first for any editing outcome.
2114 this._prompt.removeFromElement();
2115 delete this._prompt;
2119 _hasBeenModifiedIncrementally: function()
2121 // New properties applied via up/down have an originalPropertyText and will be deleted later
2122 // on, if cancelled, when the empty string gets applied as their style text.
2123 return typeof this.originalPropertyText === "string";
2126 applyStyleText: function(styleText, updateInterface, majorChange, isRevert)
2128 function userOperationFinishedCallback(parentPane, updateInterface)
2130 if (updateInterface)
2131 delete parentPane._userOperation;
2134 // Leave a way to cancel editing after incremental changes.
2135 if (!isRevert && !updateInterface && !this._hasBeenModifiedIncrementally()) {
2136 // Remember the rule's original CSS text on [Page](Up|Down), so it can be restored
2137 // if the editing is canceled.
2138 this.originalPropertyText = this.property.propertyText;
2141 var section = this.treeOutline.section;
2142 var elementsPanel = WebInspector.panels.elements;
2143 styleText = styleText.replace(/\s/g, " ").trim(); // Replace with whitespace.
2144 var styleTextLength = styleText.length;
2145 if (!styleTextLength && updateInterface && !isRevert && this._newProperty && !this._hasBeenModifiedIncrementally()) {
2146 // The user deleted everything and never applied a new property value via Up/Down scrolling, so remove the tree element and update.
2147 this.parent.removeChild(this);
2148 section.afterUpdate();
2152 var currentNode = this._parentPane.node;
2153 if (updateInterface)
2154 this._parentPane._userOperation = true;
2156 function callback(userCallback, originalPropertyText, newStyle)
2159 if (updateInterface) {
2160 // It did not apply, cancel editing.
2161 this._revertStyleUponEditingCanceled(originalPropertyText);
2167 this.style = newStyle;
2168 this.property = newStyle.propertyAt(this.property.index);
2169 this._styleRule.style = this.style;
2171 if (section && section.pane)
2172 section.pane.dispatchEventToListeners("style edited");
2174 if (updateInterface && currentNode === section.pane.node) {
2175 this._updatePane(userCallback);
2182 // Append a ";" if the new text does not end in ";".
2183 // FIXME: this does not handle trailing comments.
2184 if (styleText.length && !/;\s*$/.test(styleText))
2186 this.property.setText(styleText, majorChange, callback.bind(this, userOperationFinishedCallback.bind(null, this._parentPane, updateInterface), this.originalPropertyText));
2190 WebInspector.StylePropertyTreeElement.prototype.__proto__ = TreeElement.prototype;
2192 WebInspector.StylesSidebarPane.CSSPropertyPrompt = function(element, cssCompletions)
2194 WebInspector.TextPrompt.call(this, element, this._buildPropertyCompletions.bind(this), WebInspector.StylesSidebarPane.StyleValueDelimiters, true);
2195 this._cssCompletions = cssCompletions;
2198 WebInspector.StylesSidebarPane.CSSPropertyPrompt.prototype = {
2199 upKeyPressed: function(event)
2201 this._handleNameOrValueUpDown(event);
2204 downKeyPressed: function(event)
2206 this._handleNameOrValueUpDown(event);
2209 tabKeyPressed: function(event)
2211 this.acceptAutoComplete();
2214 _handleNameOrValueUpDown: function(event)
2216 var reverse = event.keyIdentifier === "Up";
2217 if (this.autoCompleteElement)
2218 this.complete(false, reverse); // Accept the current suggestion, if any.
2220 // Select the word suffix to affect it when computing the subsequent suggestion.
2221 this._selectCurrentWordSuffix();
2224 this.complete(false, reverse); // Actually increment/decrement the suggestion.
2225 event.handled = true;
2228 _selectCurrentWordSuffix: function()
2230 var selection = window.getSelection();
2231 if (!selection.rangeCount)
2234 var selectionRange = selection.getRangeAt(0);
2235 if (!selectionRange.commonAncestorContainer.isDescendant(this.element))
2237 var wordSuffixRange = selectionRange.startContainer.rangeOfWord(selectionRange.startOffset, WebInspector.StylesSidebarPane.StyleValueDelimiters, this.element, "forward");
2238 if (!wordSuffixRange.toString())
2240 selection.removeAllRanges();
2241 selection.addRange(wordSuffixRange);
2244 _buildPropertyCompletions: function(wordRange, bestMatchOnly, completionsReadyCallback)
2246 var prefix = wordRange.toString().toLowerCase();
2247 if (!prefix && bestMatchOnly)
2251 if (bestMatchOnly) {
2253 var firstMatch = this._cssCompletions.firstStartsWith(prefix);
2255 results.push(firstMatch);
2256 return completionsReadyCallback(results);
2259 results = this._cssCompletions.startsWith(prefix);
2261 completionsReadyCallback(results);
2265 WebInspector.StylesSidebarPane.CSSPropertyPrompt.prototype.__proto__ = WebInspector.TextPrompt.prototype;