Web Inspector: Color picker should not be available in Computed Styles pane.
[WebKit-https.git] / Source / WebCore / inspector / front-end / StylesSidebarPane.js
1 /*
2  * Copyright (C) 2007 Apple Inc.  All rights reserved.
3  * Copyright (C) 2009 Joseph Pecoraro
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
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.
17  *
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.
28  */
29
30 /**
31  * @constructor
32  * @extends {WebInspector.SidebarPane}
33  * @param {WebInspector.ComputedStyleSidebarPane} computedStylePane
34  * @param {function(DOMAgent.NodeId, string, boolean)} setPseudoClassCallback
35  */
36 WebInspector.StylesSidebarPane = function(computedStylePane, setPseudoClassCallback)
37 {
38     WebInspector.SidebarPane.call(this, WebInspector.UIString("Styles"));
39
40     this.settingsSelectElement = document.createElement("select");
41     this.settingsSelectElement.className = "select-settings";
42
43     var option = document.createElement("option");
44     option.value = WebInspector.Color.Format.Original;
45     option.label = WebInspector.UIString(WebInspector.useLowerCaseMenuTitles() ? "As authored" : "As Authored");
46     this.settingsSelectElement.appendChild(option);
47
48     option = document.createElement("option");
49     option.value = WebInspector.Color.Format.HEX;
50     option.label = WebInspector.UIString("Hex Colors");
51     this.settingsSelectElement.appendChild(option);
52
53     option = document.createElement("option");
54     option.value = WebInspector.Color.Format.RGB;
55     option.label = WebInspector.UIString("RGB Colors");
56     this.settingsSelectElement.appendChild(option);
57
58     option = document.createElement("option");
59     option.value = WebInspector.Color.Format.HSL;
60     option.label = WebInspector.UIString("HSL Colors");
61     this.settingsSelectElement.appendChild(option);
62
63     // Prevent section from collapsing.
64     var muteEventListener = function(event) { event.consume(true); };
65
66     this.settingsSelectElement.addEventListener("click", muteEventListener, true);
67     this.settingsSelectElement.addEventListener("change", this._changeSetting.bind(this), false);
68     this._updateColorFormatFilter();
69
70     this.titleElement.appendChild(this.settingsSelectElement);
71
72     this._elementStateButton = document.createElement("button");
73     this._elementStateButton.className = "pane-title-button element-state";
74     this._elementStateButton.title = WebInspector.UIString("Toggle Element State");
75     this._elementStateButton.addEventListener("click", this._toggleElementStatePane.bind(this), false);
76     this.titleElement.appendChild(this._elementStateButton);
77
78     var addButton = document.createElement("button");
79     addButton.className = "pane-title-button add";
80     addButton.id = "add-style-button-test-id";
81     addButton.title = WebInspector.UIString("New Style Rule");
82     addButton.addEventListener("click", this._createNewRule.bind(this), false);
83     this.titleElement.appendChild(addButton);
84
85     this._computedStylePane = computedStylePane;
86     computedStylePane._stylesSidebarPane = this;
87     this._setPseudoClassCallback = setPseudoClassCallback;
88     this.element.addEventListener("contextmenu", this._contextMenuEventFired.bind(this), true);
89     WebInspector.settings.colorFormat.addChangeListener(this._colorFormatSettingChanged.bind(this));
90
91     this._createElementStatePane();
92     this.bodyElement.appendChild(this._elementStatePane);
93     this._sectionsContainer = document.createElement("div");
94     this.bodyElement.appendChild(this._sectionsContainer);
95
96     this._spectrumHelper = new WebInspector.SpectrumPopupHelper();
97     this._linkifier = new WebInspector.Linkifier(new WebInspector.Linkifier.DefaultCSSFormatter());
98
99     WebInspector.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.StyleSheetChanged, this._styleSheetOrMediaQueryResultChanged, this);
100     WebInspector.cssModel.addEventListener(WebInspector.CSSStyleModel.Events.MediaQueryResultChanged, this._styleSheetOrMediaQueryResultChanged, this);
101     WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.AttrModified, this._attributeChanged, this);
102     WebInspector.domAgent.addEventListener(WebInspector.DOMAgent.Events.AttrRemoved, this._attributeChanged, this);
103     WebInspector.settings.showUserAgentStyles.addChangeListener(this._showUserAgentStylesSettingChanged.bind(this));
104 }
105
106 // Keep in sync with RenderStyleConstants.h PseudoId enum. Array below contains pseudo id names for corresponding enum indexes.
107 // First item is empty due to its artificial NOPSEUDO nature in the enum.
108 // FIXME: find a way of generating this mapping or getting it from combination of RenderStyleConstants and CSSSelector.cpp at
109 // runtime.
110 WebInspector.StylesSidebarPane.PseudoIdNames = [
111     "", "first-line", "first-letter", "before", "after", "selection", "", "-webkit-scrollbar", "-webkit-file-upload-button",
112     "-webkit-input-placeholder", "-webkit-slider-thumb", "-webkit-search-cancel-button", "-webkit-search-decoration",
113     "-webkit-search-results-decoration", "-webkit-search-results-button", "-webkit-media-controls-panel",
114     "-webkit-media-controls-play-button", "-webkit-media-controls-mute-button", "-webkit-media-controls-timeline",
115     "-webkit-media-controls-timeline-container", "-webkit-media-controls-volume-slider",
116     "-webkit-media-controls-volume-slider-container", "-webkit-media-controls-current-time-display",
117     "-webkit-media-controls-time-remaining-display", "-webkit-media-controls-seek-back-button", "-webkit-media-controls-seek-forward-button",
118     "-webkit-media-controls-fullscreen-button", "-webkit-media-controls-rewind-button", "-webkit-media-controls-return-to-realtime-button",
119     "-webkit-media-controls-toggle-closed-captions-button", "-webkit-media-controls-status-display", "-webkit-scrollbar-thumb",
120     "-webkit-scrollbar-button", "-webkit-scrollbar-track", "-webkit-scrollbar-track-piece", "-webkit-scrollbar-corner",
121     "-webkit-resizer", "-webkit-inner-spin-button", "-webkit-outer-spin-button"
122 ];
123
124 WebInspector.StylesSidebarPane.canonicalPropertyName = function(name)
125 {
126     if (!name || name.length < 9 || name.charAt(0) !== "-")
127         return name;
128     var match = name.match(/(?:-webkit-|-khtml-|-apple-)(.+)/);
129     if (!match)
130         return name;
131     return match[1];
132 }
133
134 WebInspector.StylesSidebarPane.createExclamationMark = function(propertyName)
135 {
136     var exclamationElement = document.createElement("img");
137     exclamationElement.className = "exclamation-mark";
138     exclamationElement.title = WebInspector.CSSMetadata.cssPropertiesMetainfo.keySet()[propertyName.toLowerCase()] ? WebInspector.UIString("Invalid property value.") : WebInspector.UIString("Unknown property name.");
139     return exclamationElement;
140 }
141
142 WebInspector.StylesSidebarPane.prototype = {
143     /**
144      * @param {Event} event
145      */
146     _contextMenuEventFired: function(event)
147     {
148         // We start editing upon click -> default navigation to resources panel is not available
149         // Hence we add a soft context menu for hrefs.
150         var contextMenu = new WebInspector.ContextMenu(event);
151         contextMenu.appendApplicableItems(event.target);
152         contextMenu.show();
153     },
154
155     get _forcedPseudoClasses()
156     {
157         return this.node ? (this.node.getUserProperty("pseudoState") || undefined) : undefined;
158     },
159
160     _updateForcedPseudoStateInputs: function()
161     {
162         if (!this.node)
163             return;
164
165         var nodePseudoState = this._forcedPseudoClasses;
166         if (!nodePseudoState)
167             nodePseudoState = [];
168
169         var inputs = this._elementStatePane.inputs;
170         for (var i = 0; i < inputs.length; ++i)
171             inputs[i].checked = nodePseudoState.indexOf(inputs[i].state) >= 0;
172     },
173
174     /**
175      * @param {WebInspector.DOMNode=} node
176      * @param {boolean=} forceUpdate
177      */
178     update: function(node, forceUpdate)
179     {
180         this._spectrumHelper.hide();
181
182         var refresh = false;
183
184         if (forceUpdate)
185             delete this.node;
186
187         if (!forceUpdate && (node === this.node))
188             refresh = true;
189
190         if (node && node.nodeType() === Node.TEXT_NODE && node.parentNode)
191             node = node.parentNode;
192
193         if (node && node.nodeType() !== Node.ELEMENT_NODE)
194             node = null;
195
196         if (node)
197             this.node = node;
198         else
199             node = this.node;
200
201         this._updateForcedPseudoStateInputs();
202
203         if (refresh)
204             this._refreshUpdate();
205         else
206             this._rebuildUpdate();
207     },
208
209     /**
210      * @param {WebInspector.StylePropertiesSection=} editedSection
211      * @param {boolean=} forceFetchComputedStyle
212      * @param {function()=} userCallback
213      */
214     _refreshUpdate: function(editedSection, forceFetchComputedStyle, userCallback)
215     {
216         if (this._refreshUpdateInProgress) {
217             this._lastNodeForInnerRefresh = this.node;
218             return;
219         }
220
221         var node = this._validateNode(userCallback);
222         if (!node)
223             return;
224
225         function computedStyleCallback(computedStyle)
226         {
227             delete this._refreshUpdateInProgress;
228
229             if (this._lastNodeForInnerRefresh) {
230                 delete this._lastNodeForInnerRefresh;
231                 this._refreshUpdate(editedSection, forceFetchComputedStyle, userCallback);
232                 return;
233             }
234
235             if (this.node === node && computedStyle)
236                 this._innerRefreshUpdate(node, computedStyle, editedSection);
237
238             if (userCallback)
239                 userCallback();
240         }
241
242         if (this._computedStylePane.isShowing() || forceFetchComputedStyle) {
243             this._refreshUpdateInProgress = true;
244             WebInspector.cssModel.getComputedStyleAsync(node.id, computedStyleCallback.bind(this));
245         } else {
246             this._innerRefreshUpdate(node, null, editedSection);
247             if (userCallback)
248                 userCallback();
249         }
250     },
251
252     _rebuildUpdate: function()
253     {
254         if (this._rebuildUpdateInProgress) {
255             this._lastNodeForInnerRebuild = this.node;
256             return;
257         }
258
259         var node = this._validateNode();
260         if (!node)
261             return;
262
263         this._rebuildUpdateInProgress = true;
264
265         var resultStyles = {};
266
267         function stylesCallback(matchedResult)
268         {
269             delete this._rebuildUpdateInProgress;
270
271             var lastNodeForRebuild = this._lastNodeForInnerRebuild;
272             if (lastNodeForRebuild) {
273                 delete this._lastNodeForInnerRebuild;
274                 if (lastNodeForRebuild !== this.node) {
275                     this._rebuildUpdate();
276                     return;
277                 }
278             }
279
280             if (matchedResult && this.node === node) {
281                 resultStyles.matchedCSSRules = matchedResult.matchedCSSRules;
282                 resultStyles.pseudoElements = matchedResult.pseudoElements;
283                 resultStyles.inherited = matchedResult.inherited;
284                 this._innerRebuildUpdate(node, resultStyles);
285             }
286
287             if (lastNodeForRebuild) {
288                 // lastNodeForRebuild is the same as this.node - another rebuild has been requested.
289                 this._rebuildUpdate();
290                 return;
291             }
292         }
293
294         function inlineCallback(inlineStyle, attributesStyle)
295         {
296             resultStyles.inlineStyle = inlineStyle;
297             resultStyles.attributesStyle = attributesStyle;
298         }
299
300         function computedCallback(computedStyle)
301         {
302             resultStyles.computedStyle = computedStyle;
303         }
304
305         if (this._computedStylePane.isShowing())
306             WebInspector.cssModel.getComputedStyleAsync(node.id, computedCallback.bind(this));
307         WebInspector.cssModel.getInlineStylesAsync(node.id, inlineCallback.bind(this));
308         WebInspector.cssModel.getMatchedStylesAsync(node.id, true, true, stylesCallback.bind(this));
309     },
310
311     /**
312      * @param {function()=} userCallback
313      */
314     _validateNode: function(userCallback)
315     {
316         if (!this.node) {
317             this._sectionsContainer.removeChildren();
318             this._computedStylePane.bodyElement.removeChildren();
319             this.sections = {};
320             if (userCallback)
321                 userCallback();
322             return null;
323         }
324         return this.node;
325     },
326
327     _styleSheetOrMediaQueryResultChanged: function()
328     {
329         if (this._userOperation || this._isEditingStyle)
330             return;
331
332         this._rebuildUpdate();
333     },
334
335     _attributeChanged: function(event)
336     {
337         // Any attribute removal or modification can affect the styles of "related" nodes.
338         // Do not touch the styles if they are being edited.
339         if (this._isEditingStyle || this._userOperation)
340             return;
341
342         if (!this._canAffectCurrentStyles(event.data.node))
343             return;
344
345         this._rebuildUpdate();
346     },
347
348     _canAffectCurrentStyles: function(node)
349     {
350         return this.node && (this.node === node || node.parentNode === this.node.parentNode || node.isAncestor(this.node));
351     },
352
353     _innerRefreshUpdate: function(node, computedStyle, editedSection)
354     {
355         for (var pseudoId in this.sections) {
356             var styleRules = this._refreshStyleRules(this.sections[pseudoId], computedStyle);
357             var usedProperties = {};
358             this._markUsedProperties(styleRules, usedProperties);
359             this._refreshSectionsForStyleRules(styleRules, usedProperties, editedSection);
360         }
361         if (computedStyle)
362             this.sections[0][0].rebuildComputedTrace(this.sections[0]);
363
364         this._nodeStylesUpdatedForTest(node, false);
365     },
366
367     _innerRebuildUpdate: function(node, styles)
368     {
369         this._sectionsContainer.removeChildren();
370         this._computedStylePane.bodyElement.removeChildren();
371         this._linkifier.reset();
372
373         var styleRules = this._rebuildStyleRules(node, styles);
374         var usedProperties = {};
375         this._markUsedProperties(styleRules, usedProperties);
376         this.sections[0] = this._rebuildSectionsForStyleRules(styleRules, usedProperties, 0, null);
377         var anchorElement = this.sections[0].inheritedPropertiesSeparatorElement;
378
379         if (styles.computedStyle)        
380             this.sections[0][0].rebuildComputedTrace(this.sections[0]);
381
382         for (var i = 0; i < styles.pseudoElements.length; ++i) {
383             var pseudoElementCSSRules = styles.pseudoElements[i];
384
385             styleRules = [];
386             var pseudoId = pseudoElementCSSRules.pseudoId;
387
388             var entry = { isStyleSeparator: true, pseudoId: pseudoId };
389             styleRules.push(entry);
390
391             // Add rules in reverse order to match the cascade order.
392             for (var j = pseudoElementCSSRules.rules.length - 1; j >= 0; --j) {
393                 var rule = pseudoElementCSSRules.rules[j];
394                 styleRules.push({ style: rule.style, selectorText: rule.selectorText, media: rule.media, sourceURL: rule.sourceURL, rule: rule, editable: !!(rule.style && rule.style.id) });
395             }
396             usedProperties = {};
397             this._markUsedProperties(styleRules, usedProperties);
398             this.sections[pseudoId] = this._rebuildSectionsForStyleRules(styleRules, usedProperties, pseudoId, anchorElement);
399         }
400
401         this._nodeStylesUpdatedForTest(node, true);
402     },
403
404     _nodeStylesUpdatedForTest: function(node, rebuild)
405     {
406         // Tests override this method.
407     },
408
409     _refreshStyleRules: function(sections, computedStyle)
410     {
411         var nodeComputedStyle = computedStyle;
412         var styleRules = [];
413         for (var i = 0; sections && i < sections.length; ++i) {
414             var section = sections[i];
415             if (section.isBlank)
416                 continue;
417             if (section.computedStyle)
418                 section.styleRule.style = nodeComputedStyle;
419             var styleRule = { section: section, style: section.styleRule.style, computedStyle: section.computedStyle, rule: section.rule, editable: !!(section.styleRule.style && section.styleRule.style.id), isAttribute: section.styleRule.isAttribute, isInherited: section.styleRule.isInherited };
420             styleRules.push(styleRule);
421         }
422         return styleRules;
423     },
424
425     _rebuildStyleRules: function(node, styles)
426     {
427         var nodeComputedStyle = styles.computedStyle;
428         this.sections = {};
429
430         var styleRules = [];
431
432         function addAttributesStyle()
433         {
434             if (!styles.attributesStyle)
435                 return;
436             var attrStyle = { style: styles.attributesStyle, editable: false };
437             attrStyle.selectorText = node.nodeNameInCorrectCase() + "[" + WebInspector.UIString("Attributes Style") + "]";
438             styleRules.push(attrStyle);
439         }
440
441         styleRules.push({ computedStyle: true, selectorText: "", style: nodeComputedStyle, editable: false });
442
443         // Inline style has the greatest specificity.
444         if (styles.inlineStyle && node.nodeType() === Node.ELEMENT_NODE) {
445             var inlineStyle = { selectorText: "element.style", style: styles.inlineStyle, isAttribute: true };
446             styleRules.push(inlineStyle);
447         }
448
449         // Add rules in reverse order to match the cascade order.
450         if (styles.matchedCSSRules.length)
451             styleRules.push({ isStyleSeparator: true, text: WebInspector.UIString("Matched CSS Rules") });
452         var addedAttributesStyle;
453         for (var i = styles.matchedCSSRules.length - 1; i >= 0; --i) {
454             var rule = styles.matchedCSSRules[i];
455             if (!WebInspector.settings.showUserAgentStyles.get() && (rule.isUser || rule.isUserAgent))
456                 continue;
457             if ((rule.isUser || rule.isUserAgent) && !addedAttributesStyle) {
458                 // Show element's Style Attributes after all author rules.
459                 addedAttributesStyle = true;
460                 addAttributesStyle();
461             }
462             styleRules.push({ style: rule.style, selectorText: rule.selectorText, media: rule.media, sourceURL: rule.sourceURL, rule: rule, editable: !!(rule.style && rule.style.id) });
463         }
464
465         if (!addedAttributesStyle)
466             addAttributesStyle();
467
468         // Walk the node structure and identify styles with inherited properties.
469         var parentNode = node.parentNode;
470         function insertInheritedNodeSeparator(node)
471         {
472             var entry = {};
473             entry.isStyleSeparator = true;
474             entry.node = node;
475             styleRules.push(entry);
476         }
477
478         for (var parentOrdinal = 0; parentOrdinal < styles.inherited.length; ++parentOrdinal) {
479             var parentStyles = styles.inherited[parentOrdinal];
480             var separatorInserted = false;
481             if (parentStyles.inlineStyle) {
482                 if (this._containsInherited(parentStyles.inlineStyle)) {
483                     var inlineStyle = { selectorText: WebInspector.UIString("Style Attribute"), style: parentStyles.inlineStyle, isAttribute: true, isInherited: true, parentNode: parentNode };
484                     if (!separatorInserted) {
485                         insertInheritedNodeSeparator(parentNode);
486                         separatorInserted = true;
487                     }
488                     styleRules.push(inlineStyle);
489                 }
490             }
491
492             for (var i = parentStyles.matchedCSSRules.length - 1; i >= 0; --i) {
493                 var rulePayload = parentStyles.matchedCSSRules[i];
494                 if (!this._containsInherited(rulePayload.style))
495                     continue;
496                 var rule = rulePayload;
497                 if (!WebInspector.settings.showUserAgentStyles.get() && (rule.isUser || rule.isUserAgent))
498                     continue;
499
500                 if (!separatorInserted) {
501                     insertInheritedNodeSeparator(parentNode);
502                     separatorInserted = true;
503                 }
504                 styleRules.push({ style: rule.style, selectorText: rule.selectorText, media: rule.media, sourceURL: rule.sourceURL, rule: rule, isInherited: true, parentNode: parentNode, editable: !!(rule.style && rule.style.id) });
505             }
506             parentNode = parentNode.parentNode;
507         }
508         return styleRules;
509     },
510
511     _markUsedProperties: function(styleRules, usedProperties)
512     {
513         var foundImportantProperties = {};
514         var propertyToEffectiveRule = {};
515         for (var i = 0; i < styleRules.length; ++i) {
516             var styleRule = styleRules[i];
517             if (styleRule.computedStyle || styleRule.isStyleSeparator)
518                 continue;
519             if (styleRule.section && styleRule.section.noAffect)
520                 continue;
521
522             styleRule.usedProperties = {};
523
524             var style = styleRule.style;
525             var allProperties = style.allProperties;
526             for (var j = 0; j < allProperties.length; ++j) {
527                 var property = allProperties[j];
528                 if (!property.isLive || !property.parsedOk)
529                     continue;
530
531                 var canonicalName = WebInspector.StylesSidebarPane.canonicalPropertyName(property.name);
532                 // Do not pick non-inherited properties from inherited styles.
533                 if (styleRule.isInherited && !WebInspector.CSSMetadata.InheritedProperties[canonicalName])
534                     continue;
535
536                 if (foundImportantProperties.hasOwnProperty(canonicalName))
537                     continue;
538
539                 var isImportant = property.priority.length;
540                 if (!isImportant && usedProperties.hasOwnProperty(canonicalName))
541                     continue;
542
543                 if (isImportant) {
544                     foundImportantProperties[canonicalName] = true;
545                     if (propertyToEffectiveRule.hasOwnProperty(canonicalName))
546                         delete propertyToEffectiveRule[canonicalName].usedProperties[canonicalName];
547                 }
548
549                 styleRule.usedProperties[canonicalName] = true;
550                 usedProperties[canonicalName] = true;
551                 propertyToEffectiveRule[canonicalName] = styleRule;
552             }
553         }
554     },
555
556     _refreshSectionsForStyleRules: function(styleRules, usedProperties, editedSection)
557     {
558         // Walk the style rules and update the sections with new overloaded and used properties.
559         for (var i = 0; i < styleRules.length; ++i) {
560             var styleRule = styleRules[i];
561             var section = styleRule.section;
562             if (styleRule.computedStyle) {
563                 section._usedProperties = usedProperties;
564                 section.update();
565             } else {
566                 section._usedProperties = styleRule.usedProperties;
567                 section.update(section === editedSection);
568             }
569         }
570     },
571
572     _rebuildSectionsForStyleRules: function(styleRules, usedProperties, pseudoId, anchorElement)
573     {
574         // Make a property section for each style rule.
575         var sections = [];
576         var lastWasSeparator = true;
577         for (var i = 0; i < styleRules.length; ++i) {
578             var styleRule = styleRules[i];
579             if (styleRule.isStyleSeparator) {
580                 var separatorElement = document.createElement("div");
581                 separatorElement.className = "sidebar-separator";
582                 if (styleRule.node) {
583                     var link = WebInspector.DOMPresentationUtils.linkifyNodeReference(styleRule.node);
584                     separatorElement.appendChild(document.createTextNode(WebInspector.UIString("Inherited from") + " "));
585                     separatorElement.appendChild(link);
586                     if (!sections.inheritedPropertiesSeparatorElement)
587                         sections.inheritedPropertiesSeparatorElement = separatorElement;
588                 } else if ("pseudoId" in styleRule) {
589                     var pseudoName = WebInspector.StylesSidebarPane.PseudoIdNames[styleRule.pseudoId];
590                     if (pseudoName)
591                         separatorElement.textContent = WebInspector.UIString("Pseudo ::%s element", pseudoName);
592                     else
593                         separatorElement.textContent = WebInspector.UIString("Pseudo element");
594                 } else
595                     separatorElement.textContent = styleRule.text;
596                 this._sectionsContainer.insertBefore(separatorElement, anchorElement);
597                 lastWasSeparator = true;
598                 continue;
599             }
600             var computedStyle = styleRule.computedStyle;
601
602             // Default editable to true if it was omitted.
603             var editable = styleRule.editable;
604             if (typeof editable === "undefined")
605                 editable = true;
606
607             if (computedStyle)
608                 var section = new WebInspector.ComputedStylePropertiesSection(this._computedStylePane, styleRule, usedProperties);
609             else {
610                 var section = new WebInspector.StylePropertiesSection(this, styleRule, editable, styleRule.isInherited, lastWasSeparator);
611                 section._markSelectorMatches();
612             }
613             section.expanded = true;
614
615             if (computedStyle) {
616                 this._computedStylePane.bodyElement.appendChild(section.element);
617                 lastWasSeparator = true;
618             } else {
619                 this._sectionsContainer.insertBefore(section.element, anchorElement);
620                 lastWasSeparator = false;
621             }
622             sections.push(section);
623         }
624         return sections;
625     },
626
627     _containsInherited: function(style)
628     {
629         var properties = style.allProperties;
630         for (var i = 0; i < properties.length; ++i) {
631             var property = properties[i];
632             // Does this style contain non-overridden inherited property?
633             if (property.isLive && property.name in WebInspector.CSSMetadata.InheritedProperties)
634                 return true;
635         }
636         return false;
637     },
638
639     _colorFormatSettingChanged: function(event)
640     {
641         this._updateColorFormatFilter();
642         for (var pseudoId in this.sections) {
643             var sections = this.sections[pseudoId];
644             for (var i = 0; i < sections.length; ++i)
645                 sections[i].update(true);
646         }
647     },
648
649     _updateColorFormatFilter: function()
650     {
651         // Select the correct color format setting again, since it needs to be selected.
652         var selectedIndex = 0;
653         var value = WebInspector.settings.colorFormat.get();
654         var options = this.settingsSelectElement.options;
655         for (var i = 0; i < options.length; ++i) {
656             if (options[i].value === value) {
657                 selectedIndex = i;
658                 break;
659             }
660         }
661         this.settingsSelectElement.selectedIndex = selectedIndex;
662     },
663
664     _changeSetting: function(event)
665     {
666         var options = this.settingsSelectElement.options;
667         var selectedOption = options[this.settingsSelectElement.selectedIndex];
668         WebInspector.settings.colorFormat.set(selectedOption.value);
669     },
670
671     _createNewRule: function(event)
672     {
673         event.consume();
674         this.expand();
675         this.addBlankSection().startEditingSelector();
676     },
677
678     addBlankSection: function()
679     {
680         var blankSection = new WebInspector.BlankStylePropertiesSection(this, this.node ? this.node.appropriateSelectorFor(true) : "");
681
682         var elementStyleSection = this.sections[0][1];
683         this._sectionsContainer.insertBefore(blankSection.element, elementStyleSection.element.nextSibling);
684
685         this.sections[0].splice(2, 0, blankSection);
686
687         return blankSection;
688     },
689
690     removeSection: function(section)
691     {
692         for (var pseudoId in this.sections) {
693             var sections = this.sections[pseudoId];
694             var index = sections.indexOf(section);
695             if (index === -1)
696                 continue;
697             sections.splice(index, 1);
698             if (section.element.parentNode)
699                 section.element.parentNode.removeChild(section.element);
700         }
701     },
702
703     _toggleElementStatePane: function(event)
704     {
705         event.consume();
706         if (!this._elementStateButton.hasStyleClass("toggled")) {
707             this.expand();
708             this._elementStateButton.addStyleClass("toggled");
709             this._elementStatePane.addStyleClass("expanded");
710         } else {
711             this._elementStateButton.removeStyleClass("toggled");
712             this._elementStatePane.removeStyleClass("expanded");
713         }
714     },
715
716     _createElementStatePane: function()
717     {
718         this._elementStatePane = document.createElement("div");
719         this._elementStatePane.className = "styles-element-state-pane source-code";
720         var table = document.createElement("table");
721
722         var inputs = [];
723         this._elementStatePane.inputs = inputs;
724
725         function clickListener(event)
726         {
727             var node = this._validateNode();
728             if (!node)
729                 return;
730             this._setPseudoClassCallback(node.id, event.target.state, event.target.checked);
731         }
732
733         function createCheckbox(state)
734         {
735             var td = document.createElement("td");
736             var label = document.createElement("label");
737             var input = document.createElement("input");
738             input.type = "checkbox";
739             input.state = state;
740             input.addEventListener("click", clickListener.bind(this), false);
741             inputs.push(input);
742             label.appendChild(input);
743             label.appendChild(document.createTextNode(":" + state));
744             td.appendChild(label);
745             return td;
746         }
747
748         var tr = document.createElement("tr");
749         tr.appendChild(createCheckbox.call(this, "active"));
750         tr.appendChild(createCheckbox.call(this, "hover"));
751         table.appendChild(tr);
752
753         tr = document.createElement("tr");
754         tr.appendChild(createCheckbox.call(this, "focus"));
755         tr.appendChild(createCheckbox.call(this, "visited"));
756         table.appendChild(tr);
757
758         this._elementStatePane.appendChild(table);
759     },
760
761     _showUserAgentStylesSettingChanged: function()
762     {
763         this._rebuildUpdate();
764     },
765
766     willHide: function()
767     {
768         this._spectrumHelper.hide();
769     },
770
771     __proto__: WebInspector.SidebarPane.prototype
772 }
773
774 /**
775  * @constructor
776  * @extends {WebInspector.SidebarPane}
777  */
778 WebInspector.ComputedStyleSidebarPane = function()
779 {
780     WebInspector.SidebarPane.call(this, WebInspector.UIString("Computed Style"));
781     var showInheritedCheckbox = new WebInspector.Checkbox(WebInspector.UIString("Show inherited"), "sidebar-pane-subtitle");
782     this.titleElement.appendChild(showInheritedCheckbox.element);
783
784     if (WebInspector.settings.showInheritedComputedStyleProperties.get()) {
785         this.bodyElement.addStyleClass("show-inherited");
786         showInheritedCheckbox.checked = true;
787     }
788
789     function showInheritedToggleFunction(event)
790     {
791         WebInspector.settings.showInheritedComputedStyleProperties.set(showInheritedCheckbox.checked);
792         if (WebInspector.settings.showInheritedComputedStyleProperties.get())
793             this.bodyElement.addStyleClass("show-inherited");
794         else
795             this.bodyElement.removeStyleClass("show-inherited");
796     }
797
798     showInheritedCheckbox.addEventListener(showInheritedToggleFunction.bind(this));
799 }
800
801 WebInspector.ComputedStyleSidebarPane.prototype = {
802     wasShown: function()
803     {
804         WebInspector.SidebarPane.prototype.wasShown.call(this);
805         if (!this._hasFreshContent)
806             this.prepareContent();
807     },
808
809     /**
810      * @param {function()=} callback
811      */
812     prepareContent: function(callback)
813     {
814         function wrappedCallback() {
815             this._hasFreshContent = true;
816             if (callback)
817                 callback();
818             delete this._hasFreshContent;
819         }
820         this._stylesSidebarPane._refreshUpdate(null, true, wrappedCallback.bind(this));
821     },
822
823     __proto__: WebInspector.SidebarPane.prototype
824 }
825
826 /**
827  * @constructor
828  * @extends {WebInspector.PropertiesSection}
829  */
830 WebInspector.StylePropertiesSection = function(parentPane, styleRule, editable, isInherited, isFirstSection)
831 {
832     WebInspector.PropertiesSection.call(this, "");
833     this.element.className = "styles-section matched-styles monospace" + (isFirstSection ? " first-styles-section" : "");
834
835     if (styleRule.media) {
836         for (var i = styleRule.media.length - 1; i >= 0; --i) {
837             var media = styleRule.media[i];
838             var mediaDataElement = this.titleElement.createChild("div", "media");
839             var mediaText;
840             switch (media.source) {
841             case WebInspector.CSSMedia.Source.LINKED_SHEET:
842             case WebInspector.CSSMedia.Source.INLINE_SHEET:
843                 mediaText = "media=\"" + media.text + "\"";
844                 break;
845             case WebInspector.CSSMedia.Source.MEDIA_RULE:
846                 mediaText = "@media " + media.text;
847                 break;
848             case WebInspector.CSSMedia.Source.IMPORT_RULE:
849                 mediaText = "@import " + media.text;
850                 break;
851             }
852
853             if (media.sourceURL) {
854                 var refElement = mediaDataElement.createChild("div", "subtitle");
855                 var lineNumber = media.sourceLine < 0 ? undefined : media.sourceLine;
856                 var anchor = WebInspector.linkifyResourceAsNode(media.sourceURL, lineNumber, "subtitle", media.sourceURL + (isNaN(lineNumber) ? "" : (":" + (lineNumber + 1))));
857                 anchor.preferredPanel = "scripts";
858                 anchor.style.float = "right";
859                 refElement.appendChild(anchor);
860             }
861
862             var mediaTextElement = mediaDataElement.createChild("span");
863             mediaTextElement.textContent = mediaText;
864             mediaTextElement.title = media.text;
865         }
866     }
867
868     var selectorContainer = document.createElement("div");
869     this._selectorElement = document.createElement("span");
870     this._selectorElement.textContent = styleRule.selectorText;
871     selectorContainer.appendChild(this._selectorElement);
872
873     var openBrace = document.createElement("span");
874     openBrace.textContent = " {";
875     selectorContainer.appendChild(openBrace);
876     selectorContainer.addEventListener("mousedown", this._handleEmptySpaceMouseDown.bind(this), false);
877     selectorContainer.addEventListener("click", this._handleSelectorContainerClick.bind(this), false);
878
879     var closeBrace = document.createElement("div");
880     closeBrace.textContent = "}";
881     this.element.appendChild(closeBrace);
882
883     this._selectorElement.addEventListener("click", this._handleSelectorClick.bind(this), false);
884     this.element.addEventListener("mousedown", this._handleEmptySpaceMouseDown.bind(this), false);
885     this.element.addEventListener("click", this._handleEmptySpaceClick.bind(this), false);
886
887     this._parentPane = parentPane;
888     this.styleRule = styleRule;
889     this.rule = this.styleRule.rule;
890     this.editable = editable;
891     this.isInherited = isInherited;
892
893     if (this.rule) {
894         // Prevent editing the user agent and user rules.
895         if (this.rule.isUserAgent || this.rule.isUser)
896             this.editable = false;
897         this.titleElement.addStyleClass("styles-selector");
898     }
899
900     this._usedProperties = styleRule.usedProperties;
901
902     this._selectorRefElement = document.createElement("div");
903     this._selectorRefElement.className = "subtitle";
904     this._selectorRefElement.appendChild(this._createRuleOriginNode());
905     selectorContainer.insertBefore(this._selectorRefElement, selectorContainer.firstChild);
906     this.titleElement.appendChild(selectorContainer);
907     this._selectorContainer = selectorContainer;
908
909     if (isInherited)
910         this.element.addStyleClass("show-inherited"); // This one is related to inherited rules, not computed style.
911
912     if (!this.editable)
913         this.element.addStyleClass("read-only");
914 }
915
916 WebInspector.StylePropertiesSection.prototype = {
917     get pane()
918     {
919         return this._parentPane;
920     },
921
922     collapse: function(dontRememberState)
923     {
924         // Overriding with empty body.
925     },
926
927     isPropertyInherited: function(propertyName)
928     {
929         if (this.isInherited) {
930             // While rendering inherited stylesheet, reverse meaning of this property.
931             // Render truly inherited properties with black, i.e. return them as non-inherited.
932             return !(propertyName in WebInspector.CSSMetadata.InheritedProperties);
933         }
934         return false;
935     },
936
937     /**
938      * @param {string} propertyName
939      * @param {boolean=} isShorthand
940      */
941     isPropertyOverloaded: function(propertyName, isShorthand)
942     {
943         if (!this._usedProperties || this.noAffect)
944             return false;
945
946         if (this.isInherited && !(propertyName in WebInspector.CSSMetadata.InheritedProperties)) {
947             // In the inherited sections, only show overrides for the potentially inherited properties.
948             return false;
949         }
950
951         var canonicalName = WebInspector.StylesSidebarPane.canonicalPropertyName(propertyName);
952         var used = (canonicalName in this._usedProperties);
953         if (used || !isShorthand)
954             return !used;
955
956         // Find out if any of the individual longhand properties of the shorthand
957         // are used, if none are then the shorthand is overloaded too.
958         var longhandProperties = this.styleRule.style.longhandProperties(propertyName);
959         for (var j = 0; j < longhandProperties.length; ++j) {
960             var individualProperty = longhandProperties[j];
961             if (WebInspector.StylesSidebarPane.canonicalPropertyName(individualProperty.name) in this._usedProperties)
962                 return false;
963         }
964
965         return true;
966     },
967
968     nextEditableSibling: function()
969     {
970         var curSection = this;
971         do {
972             curSection = curSection.nextSibling;
973         } while (curSection && !curSection.editable);
974
975         if (!curSection) {
976             curSection = this.firstSibling;
977             while (curSection && !curSection.editable)
978                 curSection = curSection.nextSibling;
979         }
980
981         return (curSection && curSection.editable) ? curSection : null;
982     },
983
984     previousEditableSibling: function()
985     {
986         var curSection = this;
987         do {
988             curSection = curSection.previousSibling;
989         } while (curSection && !curSection.editable);
990
991         if (!curSection) {
992             curSection = this.lastSibling;
993             while (curSection && !curSection.editable)
994                 curSection = curSection.previousSibling;
995         }
996
997         return (curSection && curSection.editable) ? curSection : null;
998     },
999
1000     update: function(full)
1001     {
1002         if (this.styleRule.selectorText)
1003             this._selectorElement.textContent = this.styleRule.selectorText;
1004         this._markSelectorMatches();
1005         if (full) {
1006             this.propertiesTreeOutline.removeChildren();
1007             this.populated = false;
1008         } else {
1009             var child = this.propertiesTreeOutline.children[0];
1010             while (child) {
1011                 child.overloaded = this.isPropertyOverloaded(child.name, child.isShorthand);
1012                 child = child.traverseNextTreeElement(false, null, true);
1013             }
1014         }
1015         this.afterUpdate();
1016     },
1017
1018     afterUpdate: function()
1019     {
1020         if (this._afterUpdate) {
1021             this._afterUpdate(this);
1022             delete this._afterUpdate;
1023         }
1024     },
1025
1026     onpopulate: function()
1027     {
1028         var style = this.styleRule.style;
1029         var allProperties = style.allProperties;
1030         this.uniqueProperties = [];
1031
1032         var styleHasEditableSource = this.editable && !!style.range;
1033         if (styleHasEditableSource) {
1034             for (var i = 0; i < allProperties.length; ++i) {
1035                 var property = allProperties[i];
1036                 this.uniqueProperties.push(property);
1037                 if (property.styleBased)
1038                     continue;
1039
1040                 var isShorthand = !!WebInspector.CSSMetadata.cssPropertiesMetainfo.longhands(property.name);
1041                 var inherited = this.isPropertyInherited(property.name);
1042                 var overloaded = property.inactive || this.isPropertyOverloaded(property.name);
1043                 var item = new WebInspector.StylePropertyTreeElement(this, this._parentPane, this.styleRule, style, property, isShorthand, inherited, overloaded);
1044                 this.propertiesTreeOutline.appendChild(item);
1045             }
1046             return;
1047         }
1048
1049         var generatedShorthands = {};
1050         // For style-based properties, generate shorthands with values when possible.
1051         for (var i = 0; i < allProperties.length; ++i) {
1052             var property = allProperties[i];
1053             this.uniqueProperties.push(property);
1054             var isShorthand = !!WebInspector.CSSMetadata.cssPropertiesMetainfo.longhands(property.name);
1055
1056             // For style-based properties, try generating shorthands.
1057             var shorthands = isShorthand ? null : WebInspector.CSSMetadata.cssPropertiesMetainfo.shorthands(property.name);
1058             var shorthandPropertyAvailable = false;
1059             for (var j = 0; shorthands && !shorthandPropertyAvailable && j < shorthands.length; ++j) {
1060                 var shorthand = shorthands[j];
1061                 if (shorthand in generatedShorthands) {
1062                     shorthandPropertyAvailable = true;
1063                     continue;  // There already is a shorthand this longhands falls under.
1064                 }
1065                 if (style.getLiveProperty(shorthand)) {
1066                     shorthandPropertyAvailable = true;
1067                     continue;  // There is an explict shorthand property this longhands falls under.
1068                 }
1069                 if (!style.shorthandValue(shorthand)) {
1070                     shorthandPropertyAvailable = false;
1071                     continue;  // Never generate synthetic shorthands when no value is available.
1072                 }
1073
1074                 // Generate synthetic shorthand we have a value for.
1075                 var shorthandProperty = new WebInspector.CSSProperty(style, style.allProperties.length, shorthand, style.shorthandValue(shorthand), "", "style", true, true, undefined);
1076                 var overloaded = property.inactive || this.isPropertyOverloaded(property.name, true);
1077                 var item = new WebInspector.StylePropertyTreeElement(this, this._parentPane, this.styleRule, style, shorthandProperty,  /* isShorthand */ true, /* inherited */ false, overloaded);
1078                 this.propertiesTreeOutline.appendChild(item);
1079                 generatedShorthands[shorthand] = shorthandProperty;
1080                 shorthandPropertyAvailable = true;
1081             }
1082             if (shorthandPropertyAvailable)
1083                 continue;  // Shorthand for the property found.
1084
1085             var inherited = this.isPropertyInherited(property.name);
1086             var overloaded = property.inactive || this.isPropertyOverloaded(property.name, isShorthand);
1087             var item = new WebInspector.StylePropertyTreeElement(this, this._parentPane, this.styleRule, style, property, isShorthand, inherited, overloaded);
1088             this.propertiesTreeOutline.appendChild(item);
1089         }
1090     },
1091
1092     findTreeElementWithName: function(name)
1093     {
1094         var treeElement = this.propertiesTreeOutline.children[0];
1095         while (treeElement) {
1096             if (treeElement.name === name)
1097                 return treeElement;
1098             treeElement = treeElement.traverseNextTreeElement(true, null, true);
1099         }
1100         return null;
1101     },
1102
1103     _markSelectorMatches: function()
1104     {
1105         var rule = this.styleRule.rule;
1106         if (!rule)
1107             return;
1108
1109         var matchingSelectors = rule.matchingSelectors;
1110         // .selector is rendered as non-affecting selector by default.
1111         if (this.noAffect || matchingSelectors)
1112             this._selectorElement.className = "selector";
1113         if (!matchingSelectors)
1114             return;
1115
1116         var selectors = rule.selectors;
1117         var fragment = document.createDocumentFragment();
1118         var currentMatch = 0;
1119         for (var i = 0, lastSelectorIndex = selectors.length - 1; i <= lastSelectorIndex ; ++i) {
1120             var selectorNode;
1121             var textNode = document.createTextNode(selectors[i]);
1122             if (matchingSelectors[currentMatch] === i) {
1123                 ++currentMatch;
1124                 selectorNode = document.createElement("span");
1125                 selectorNode.className = "selector-matches";
1126                 selectorNode.appendChild(textNode);
1127             } else
1128                 selectorNode = textNode;
1129
1130             fragment.appendChild(selectorNode);
1131             if (i !== lastSelectorIndex)
1132                 fragment.appendChild(document.createTextNode(", "));
1133         }
1134
1135         this._selectorElement.removeChildren();
1136         this._selectorElement.appendChild(fragment);
1137     },
1138
1139     _checkWillCancelEditing: function()
1140     {
1141         var willCauseCancelEditing = this._willCauseCancelEditing;
1142         delete this._willCauseCancelEditing;
1143         return willCauseCancelEditing;
1144     },
1145
1146     _handleSelectorContainerClick: function(event)
1147     {
1148         if (this._checkWillCancelEditing() || !this.editable)
1149             return;
1150         if (event.target === this._selectorContainer)
1151             this.addNewBlankProperty(0).startEditing();
1152     },
1153
1154     /**
1155      * @param {number=} index
1156      */
1157     addNewBlankProperty: function(index)
1158     {
1159         var style = this.styleRule.style;
1160         var property = style.newBlankProperty(index);
1161         var item = new WebInspector.StylePropertyTreeElement(this, this._parentPane, this.styleRule, style, property, false, false, false);
1162         index = property.index;
1163         this.propertiesTreeOutline.insertChild(item, index);
1164         item.listItemElement.textContent = "";
1165         item._newProperty = true;
1166         item.updateTitle();
1167         return item;
1168     },
1169
1170     _createRuleOriginNode: function()
1171     {
1172         /**
1173          * @param {string} url
1174          * @param {number} line
1175          */
1176         function linkifyUncopyable(url, line)
1177         {
1178             var link = WebInspector.linkifyResourceAsNode(url, line, "", url + ":" + (line + 1));
1179             link.preferredPanel = "scripts";
1180             link.classList.add("webkit-html-resource-link");
1181             link.setAttribute("data-uncopyable", link.textContent);
1182             link.textContent = "";
1183             return link;
1184         }
1185
1186         if (this.styleRule.sourceURL)
1187             return this._parentPane._linkifier.linkifyCSSRuleLocation(this.rule) || linkifyUncopyable(this.styleRule.sourceURL, this.rule.sourceLine);
1188
1189         if (!this.rule)
1190             return document.createTextNode("");
1191
1192         var origin = "";
1193         if (this.rule.isUserAgent)
1194             return document.createTextNode(WebInspector.UIString("user agent stylesheet"));
1195         if (this.rule.isUser)
1196             return document.createTextNode(WebInspector.UIString("user stylesheet"));
1197         if (this.rule.isViaInspector) {
1198             var element = document.createElement("span");
1199             /**
1200              * @param {?WebInspector.Resource} resource
1201              */
1202             function callback(resource)
1203             {
1204                 if (resource)
1205                     element.appendChild(linkifyUncopyable(resource.url, this.rule.sourceLine));
1206                 else
1207                     element.textContent = WebInspector.UIString("via inspector");
1208             }
1209             WebInspector.cssModel.getViaInspectorResourceForRule(this.rule, callback.bind(this));
1210             return element;
1211         }
1212     },
1213
1214     _handleEmptySpaceMouseDown: function(event)
1215     {
1216         this._willCauseCancelEditing = this._parentPane._isEditingStyle;
1217     },
1218
1219     _handleEmptySpaceClick: function(event)
1220     {
1221         if (!this.editable)
1222             return;
1223
1224         if (!window.getSelection().isCollapsed)
1225             return;
1226
1227         if (this._checkWillCancelEditing())
1228             return;
1229
1230         if (event.target.hasStyleClass("header") || this.element.hasStyleClass("read-only") || event.target.enclosingNodeOrSelfWithClass("media")) {
1231             event.consume();
1232             return;
1233         }
1234         this.expand();
1235         this.addNewBlankProperty().startEditing();
1236     },
1237
1238     _handleSelectorClick: function(event)
1239     {
1240         this._startEditingOnMouseEvent();
1241         event.consume(true);
1242     },
1243
1244     _startEditingOnMouseEvent: function()
1245     {
1246         if (!this.editable)
1247             return;
1248
1249         if (!this.rule && this.propertiesTreeOutline.children.length === 0) {
1250             this.expand();
1251             this.addNewBlankProperty().startEditing();
1252             return;
1253         }
1254
1255         if (!this.rule)
1256             return;
1257
1258         this.startEditingSelector();
1259     },
1260
1261     startEditingSelector: function()
1262     {
1263         var element = this._selectorElement;
1264         if (WebInspector.isBeingEdited(element))
1265             return;
1266
1267         element.scrollIntoViewIfNeeded(false);
1268         element.textContent = element.textContent; // Reset selector marks in group.
1269
1270         var config = new WebInspector.EditingConfig(this.editingSelectorCommitted.bind(this), this.editingSelectorCancelled.bind(this));
1271         WebInspector.startEditing(this._selectorElement, config);
1272
1273         window.getSelection().setBaseAndExtent(element, 0, element, 1);
1274     },
1275
1276     _moveEditorFromSelector: function(moveDirection)
1277     {
1278         this._markSelectorMatches();
1279
1280         if (!moveDirection)
1281             return;
1282
1283         if (moveDirection === "forward") {
1284             this.expand();
1285             var firstChild = this.propertiesTreeOutline.children[0];
1286             while (firstChild && firstChild.inherited)
1287                 firstChild = firstChild.nextSibling;
1288             if (!firstChild)
1289                 this.addNewBlankProperty().startEditing();
1290             else
1291                 firstChild.startEditing(firstChild.nameElement);
1292         } else {
1293             var previousSection = this.previousEditableSibling();
1294             if (!previousSection)
1295                 return;
1296
1297             previousSection.expand();
1298             previousSection.addNewBlankProperty().startEditing();
1299         }
1300     },
1301
1302     editingSelectorCommitted: function(element, newContent, oldContent, context, moveDirection)
1303     {
1304         if (newContent)
1305             newContent = newContent.trim();
1306         if (newContent === oldContent) {
1307             // Revert to a trimmed version of the selector if need be.
1308             this._selectorElement.textContent = newContent;
1309             return this._moveEditorFromSelector(moveDirection);
1310         }
1311
1312         var selectedNode = this._parentPane.node;
1313
1314         function successCallback(newRule, doesAffectSelectedNode)
1315         {
1316             if (!doesAffectSelectedNode) {
1317                 this.noAffect = true;
1318                 this.element.addStyleClass("no-affect");
1319             } else {
1320                 delete this.noAffect;
1321                 this.element.removeStyleClass("no-affect");
1322             }
1323
1324             this.rule = newRule;
1325             this.styleRule = { section: this, style: newRule.style, selectorText: newRule.selectorText, media: newRule.media, sourceURL: newRule.sourceURL, rule: newRule };
1326
1327             this._parentPane.update(selectedNode);
1328
1329             finishOperationAndMoveEditor.call(this, moveDirection);
1330         }
1331
1332         function finishOperationAndMoveEditor(direction)
1333         {
1334             delete this._parentPane._userOperation;
1335             this._moveEditorFromSelector(direction);
1336         }
1337
1338         // This gets deleted in finishOperationAndMoveEditor(), which is called both on success and failure.
1339         this._parentPane._userOperation = true;
1340         WebInspector.cssModel.setRuleSelector(this.rule.id, selectedNode ? selectedNode.id : 0, newContent, successCallback.bind(this), finishOperationAndMoveEditor.bind(this, moveDirection));
1341     },
1342
1343     editingSelectorCancelled: function()
1344     {
1345         // Do nothing but mark the selectors in group if necessary.
1346         // This is overridden by BlankStylePropertiesSection.
1347         this._markSelectorMatches();
1348     },
1349
1350     __proto__: WebInspector.PropertiesSection.prototype
1351 }
1352
1353 /**
1354  * @constructor
1355  * @extends {WebInspector.PropertiesSection}
1356  * @param {!WebInspector.ComputedStyleSidebarPane} parentPane
1357  * @param {!Object} styleRule
1358  * @param {!Object.<string, boolean>} usedProperties
1359  */
1360 WebInspector.ComputedStylePropertiesSection = function(parentPane, styleRule, usedProperties)
1361 {
1362     WebInspector.PropertiesSection.call(this, "");
1363     this.headerElement.addStyleClass("hidden");
1364     this.element.className = "styles-section monospace first-styles-section read-only computed-style";
1365     this._parentPane = parentPane;
1366     this.styleRule = styleRule;
1367     this._usedProperties = usedProperties;
1368     this._alwaysShowComputedProperties = { "display": true, "height": true, "width": true };
1369     this.computedStyle = true;
1370     this._propertyTreeElements = {};
1371     this._expandedPropertyNames = {};
1372 }
1373
1374 WebInspector.ComputedStylePropertiesSection.prototype = {
1375     get pane()
1376     {
1377         return this._parentPane;
1378     },
1379
1380     collapse: function(dontRememberState)
1381     {
1382         // Overriding with empty body.
1383     },
1384
1385     _isPropertyInherited: function(propertyName)
1386     {
1387         var canonicalName = WebInspector.StylesSidebarPane.canonicalPropertyName(propertyName);
1388         return !(canonicalName in this._usedProperties) && !(canonicalName in this._alwaysShowComputedProperties);
1389     },
1390
1391     update: function()
1392     {
1393         this._expandedPropertyNames = {};
1394         for (var name in this._propertyTreeElements) {
1395             if (this._propertyTreeElements[name].expanded)
1396                 this._expandedPropertyNames[name] = true;
1397         }
1398         this._propertyTreeElements = {};
1399         this.propertiesTreeOutline.removeChildren();
1400         this.populated = false;
1401     },
1402
1403     onpopulate: function()
1404     {
1405         function sorter(a, b)
1406         {
1407             return a.name.compareTo(b.name);
1408         }
1409
1410         var style = this.styleRule.style;
1411         if (!style)
1412             return;
1413
1414         var uniqueProperties = [];
1415         var allProperties = style.allProperties;
1416         for (var i = 0; i < allProperties.length; ++i)
1417             uniqueProperties.push(allProperties[i]);
1418         uniqueProperties.sort(sorter);
1419
1420         this._propertyTreeElements = {};
1421         for (var i = 0; i < uniqueProperties.length; ++i) {
1422             var property = uniqueProperties[i];
1423             var inherited = this._isPropertyInherited(property.name);
1424             var item = new WebInspector.StylePropertyTreeElement(this, this._parentPane, this.styleRule, style, property, false, inherited, false);
1425             this.propertiesTreeOutline.appendChild(item);
1426             this._propertyTreeElements[property.name] = item;
1427         }
1428     },
1429
1430     rebuildComputedTrace: function(sections)
1431     {
1432         for (var i = 0; i < sections.length; ++i) {
1433             var section = sections[i];
1434             if (section.computedStyle || section.isBlank)
1435                 continue;
1436
1437             for (var j = 0; j < section.uniqueProperties.length; ++j) {
1438                 var property = section.uniqueProperties[j];
1439                 if (property.disabled)
1440                     continue;
1441                 if (section.isInherited && !(property.name in WebInspector.CSSMetadata.InheritedProperties))
1442                     continue;
1443
1444                 var treeElement = this._propertyTreeElements[property.name];
1445                 if (treeElement) {
1446                     var fragment = document.createDocumentFragment();
1447                     var selector = fragment.createChild("span");
1448                     selector.style.color = "gray";
1449                     selector.textContent = section.styleRule.selectorText;
1450                     fragment.appendChild(document.createTextNode(" - " + property.value + " "));
1451                     var subtitle = fragment.createChild("span");
1452                     subtitle.style.float = "right";
1453                     subtitle.appendChild(section._createRuleOriginNode());
1454                     var childElement = new TreeElement(fragment, null, false);
1455                     treeElement.appendChild(childElement);
1456                     if (property.inactive || section.isPropertyOverloaded(property.name))
1457                         childElement.listItemElement.addStyleClass("overloaded");
1458                     if (!property.parsedOk) {
1459                         childElement.listItemElement.addStyleClass("not-parsed-ok");
1460                         childElement.listItemElement.insertBefore(WebInspector.StylesSidebarPane.createExclamationMark(property.name), childElement.listItemElement.firstChild);
1461                     }
1462                 }
1463             }
1464         }
1465
1466         // Restore expanded state after update.
1467         for (var name in this._expandedPropertyNames) {
1468             if (name in this._propertyTreeElements)
1469                 this._propertyTreeElements[name].expand();
1470         }
1471     },
1472
1473     __proto__: WebInspector.PropertiesSection.prototype
1474 }
1475
1476 /**
1477  * @constructor
1478  * @extends {WebInspector.StylePropertiesSection}
1479  */
1480 WebInspector.BlankStylePropertiesSection = function(parentPane, defaultSelectorText)
1481 {
1482     WebInspector.StylePropertiesSection.call(this, parentPane, {selectorText: defaultSelectorText, rule: {isViaInspector: true}}, true, false, false);
1483     this.element.addStyleClass("blank-section");
1484 }
1485
1486 WebInspector.BlankStylePropertiesSection.prototype = {
1487     get isBlank()
1488     {
1489         return !this._normal;
1490     },
1491
1492     expand: function()
1493     {
1494         if (!this.isBlank)
1495             WebInspector.StylePropertiesSection.prototype.expand.call(this);
1496     },
1497
1498     editingSelectorCommitted: function(element, newContent, oldContent, context, moveDirection)
1499     {
1500         if (!this.isBlank) {
1501             WebInspector.StylePropertiesSection.prototype.editingSelectorCommitted.call(this, element, newContent, oldContent, context, moveDirection);
1502             return;
1503         }
1504
1505         function successCallback(newRule, doesSelectorAffectSelectedNode)
1506         {
1507             var styleRule = { section: this, style: newRule.style, selectorText: newRule.selectorText, sourceURL: newRule.sourceURL, rule: newRule };
1508             this.makeNormal(styleRule);
1509
1510             if (!doesSelectorAffectSelectedNode) {
1511                 this.noAffect = true;
1512                 this.element.addStyleClass("no-affect");
1513             }
1514
1515             this._selectorRefElement.removeChildren();
1516             this._selectorRefElement.appendChild(this._createRuleOriginNode());
1517             this.expand();
1518             if (this.element.parentElement) // Might have been detached already.
1519                 this._moveEditorFromSelector(moveDirection);
1520
1521             this._markSelectorMatches();
1522             delete this._parentPane._userOperation;
1523         }
1524
1525         if (newContent)
1526             newContent = newContent.trim();
1527         this._parentPane._userOperation = true;
1528         WebInspector.cssModel.addRule(this.pane.node.id, newContent, successCallback.bind(this), this.editingSelectorCancelled.bind(this));
1529     },
1530
1531     editingSelectorCancelled: function()
1532     {
1533         delete this._parentPane._userOperation;
1534         if (!this.isBlank) {
1535             WebInspector.StylePropertiesSection.prototype.editingSelectorCancelled.call(this);
1536             return;
1537         }
1538
1539         this.pane.removeSection(this);
1540     },
1541
1542     makeNormal: function(styleRule)
1543     {
1544         this.element.removeStyleClass("blank-section");
1545         this.styleRule = styleRule;
1546         this.rule = styleRule.rule;
1547
1548         // FIXME: replace this instance by a normal WebInspector.StylePropertiesSection.
1549         this._normal = true;
1550     },
1551
1552     __proto__: WebInspector.StylePropertiesSection.prototype
1553 }
1554
1555 /**
1556  * @constructor
1557  * @extends {TreeElement}
1558  * @param {WebInspector.StylePropertiesSection|WebInspector.ComputedStylePropertiesSection} section
1559  * @param {?WebInspector.StylesSidebarPane} parentPane
1560  * @param {Object} styleRule
1561  * @param {WebInspector.CSSStyleDeclaration} style
1562  * @param {WebInspector.CSSProperty} property
1563  * @param {boolean} isShorthand
1564  * @param {boolean} inherited
1565  * @param {boolean} overloaded
1566  */
1567 WebInspector.StylePropertyTreeElement = function(section, parentPane, styleRule, style, property, isShorthand, inherited, overloaded)
1568 {
1569     this.section = section;
1570     this._parentPane = parentPane;
1571     this._styleRule = styleRule;
1572     this.style = style;
1573     this.property = property;
1574     this.isShorthand = isShorthand;
1575     this._inherited = inherited;
1576     this._overloaded = overloaded;
1577
1578     // Pass an empty title, the title gets made later in onattach.
1579     TreeElement.call(this, "", null, isShorthand);
1580
1581     this.selectable = false;
1582 }
1583
1584 WebInspector.StylePropertyTreeElement.prototype = {
1585     get inherited()
1586     {
1587         return this._inherited;
1588     },
1589
1590     set inherited(x)
1591     {
1592         if (x === this._inherited)
1593             return;
1594         this._inherited = x;
1595         this.updateState();
1596     },
1597
1598     get overloaded()
1599     {
1600         return this._overloaded;
1601     },
1602
1603     set overloaded(x)
1604     {
1605         if (x === this._overloaded)
1606             return;
1607         this._overloaded = x;
1608         this.updateState();
1609     },
1610
1611     get disabled()
1612     {
1613         return this.property.disabled;
1614     },
1615
1616     get name()
1617     {
1618         if (!this.disabled || !this.property.text)
1619             return this.property.name;
1620
1621         var text = this.property.text;
1622         var index = text.indexOf(":");
1623         if (index < 1)
1624             return this.property.name;
1625
1626         return text.substring(0, index).trim();
1627     },
1628
1629     get priority()
1630     {
1631         if (this.disabled)
1632             return ""; // rely upon raw text to render it in the value field
1633         return this.property.priority;
1634     },
1635
1636     get value()
1637     {
1638         if (!this.disabled || !this.property.text)
1639             return this.property.value;
1640
1641         var match = this.property.text.match(/(.*);\s*/);
1642         if (!match || !match[1])
1643             return this.property.value;
1644
1645         var text = match[1];
1646         var index = text.indexOf(":");
1647         if (index < 1)
1648             return this.property.value;
1649
1650         return text.substring(index + 1).trim();
1651     },
1652
1653     get parsedOk()
1654     {
1655         return this.property.parsedOk;
1656     },
1657
1658     onattach: function()
1659     {
1660         this.updateTitle();
1661         this.listItemElement.addEventListener("mousedown", this._mouseDown.bind(this));
1662         this.listItemElement.addEventListener("mouseup", this._resetMouseDownElement.bind(this));
1663         this.listItemElement.addEventListener("click", this._mouseClick.bind(this));
1664     },
1665
1666     _mouseDown: function(event)
1667     {
1668         if (this._parentPane) {
1669             this._parentPane._mouseDownTreeElement = this;
1670             this._parentPane._mouseDownTreeElementIsName = this._isNameElement(event.target);
1671             this._parentPane._mouseDownTreeElementIsValue = this._isValueElement(event.target);
1672         }
1673     },
1674
1675     _resetMouseDownElement: function()
1676     {
1677         if (this._parentPane) {
1678             delete this._parentPane._mouseDownTreeElement;
1679             delete this._parentPane._mouseDownTreeElementIsName;
1680             delete this._parentPane._mouseDownTreeElementIsValue;
1681         }
1682     },
1683
1684     updateTitle: function()
1685     {
1686         var value = this.value;
1687
1688         this.updateState();
1689
1690         var enabledCheckboxElement;
1691         if (this.parsedOk) {
1692             enabledCheckboxElement = document.createElement("input");
1693             enabledCheckboxElement.className = "enabled-button";
1694             enabledCheckboxElement.type = "checkbox";
1695             enabledCheckboxElement.checked = !this.disabled;
1696             enabledCheckboxElement.addEventListener("click", this.toggleEnabled.bind(this), false);
1697         }
1698
1699         var nameElement = document.createElement("span");
1700         nameElement.className = "webkit-css-property";
1701         nameElement.textContent = this.name;
1702         nameElement.title = this.property.propertyText;
1703         this.nameElement = nameElement;
1704
1705         this._expandElement = document.createElement("span");
1706         this._expandElement.className = "expand-element";
1707
1708         var valueElement = document.createElement("span");
1709         valueElement.className = "value";
1710         this.valueElement = valueElement;
1711
1712         var cf = WebInspector.Color.Format;
1713
1714         if (value) {
1715             var self = this;
1716
1717             function processValue(regex, processor, nextProcessor, valueText)
1718             {
1719                 var container = document.createDocumentFragment();
1720
1721                 var items = valueText.replace(regex, "\0$1\0").split("\0");
1722                 for (var i = 0; i < items.length; ++i) {
1723                     if ((i % 2) === 0) {
1724                         if (nextProcessor)
1725                             container.appendChild(nextProcessor(items[i]));
1726                         else
1727                             container.appendChild(document.createTextNode(items[i]));
1728                     } else {
1729                         var processedNode = processor(items[i]);
1730                         if (processedNode)
1731                             container.appendChild(processedNode);
1732                     }
1733                 }
1734
1735                 return container;
1736             }
1737
1738             function linkifyURL(url)
1739             {
1740                 var hrefUrl = url;
1741                 var match = hrefUrl.match(/['"]?([^'"]+)/);
1742                 if (match)
1743                     hrefUrl = match[1];
1744                 var container = document.createDocumentFragment();
1745                 container.appendChild(document.createTextNode("url("));
1746                 if (self._styleRule.sourceURL)
1747                     hrefUrl = WebInspector.ParsedURL.completeURL(self._styleRule.sourceURL, hrefUrl);
1748                 else if (self._parentPane.node)
1749                     hrefUrl = self._parentPane.node.resolveURL(hrefUrl);
1750                 var hasResource = !!WebInspector.resourceForURL(hrefUrl);
1751                 // FIXME: WebInspector.linkifyURLAsNode() should really use baseURI.
1752                 container.appendChild(WebInspector.linkifyURLAsNode(hrefUrl, url, undefined, !hasResource));
1753                 container.appendChild(document.createTextNode(")"));
1754                 return container;
1755             }
1756
1757             function processColor(text)
1758             {
1759                 var color = WebInspector.Color.parse(text);
1760
1761                 // We can be called with valid non-color values of |text| (like 'none' from border style) 
1762                 if (!color) 
1763                     return document.createTextNode(text);
1764                 
1765                 var format = getFormat();
1766                 var hasSpectrum = self._parentPane;
1767                 var spectrumHelper = hasSpectrum ? self._parentPane._spectrumHelper : null;
1768                 var spectrum = spectrumHelper ? spectrumHelper.spectrum() : null;
1769
1770                 var colorSwatch = new WebInspector.ColorSwatch();
1771                 colorSwatch.setColorString(text);
1772                 colorSwatch.element.addEventListener("click", swatchClick, false);
1773
1774                 var scrollerElement;
1775
1776                 function spectrumChanged(e)
1777                 {
1778                     color = e.data;
1779                     var colorString = color.toString();
1780                     spectrum.displayText = colorString;
1781                     colorValueElement.textContent = colorString;
1782                     colorSwatch.setColorString(colorString);
1783                     self.applyStyleText(nameElement.textContent + ": " + valueElement.textContent, false, false, false);
1784                 }
1785
1786                 function spectrumHidden(event)
1787                 {
1788                     if (scrollerElement)
1789                         scrollerElement.removeEventListener("scroll", repositionSpectrum, false);
1790                     var commitEdit = event.data;
1791                     var propertyText = !commitEdit && self.originalPropertyText ? self.originalPropertyText : (nameElement.textContent + ": " + valueElement.textContent);
1792                     self.applyStyleText(propertyText, true, true, false);
1793                     spectrum.removeEventListener(WebInspector.Spectrum.Events.ColorChanged, spectrumChanged);
1794                     spectrumHelper.removeEventListener(WebInspector.SpectrumPopupHelper.Events.Hidden, spectrumHidden);
1795
1796                     delete self._parentPane._isEditingStyle;
1797                     delete self.originalPropertyText;
1798                 }
1799
1800                 function repositionSpectrum()
1801                 {
1802                     spectrumHelper.reposition(colorSwatch.element);
1803                 }
1804
1805                 function swatchClick(e)
1806                 {
1807                     // Shift + click toggles color formats.
1808                     // Click opens colorpicker, only if the element is not in computed styles section.
1809                     if (!spectrumHelper || e.shiftKey)
1810                         changeColorDisplay(e);
1811                     else {
1812                         var visible = spectrumHelper.toggle(colorSwatch.element, color, format);
1813
1814                         if (visible) {
1815                             spectrum.displayText = color.toString(format);
1816                             self.originalPropertyText = self.property.propertyText;
1817                             self._parentPane._isEditingStyle = true;
1818                             spectrum.addEventListener(WebInspector.Spectrum.Events.ColorChanged, spectrumChanged);
1819                             spectrumHelper.addEventListener(WebInspector.SpectrumPopupHelper.Events.Hidden, spectrumHidden);
1820
1821                             scrollerElement = colorSwatch.element.enclosingNodeOrSelfWithClass("scroll-target");
1822                             if (scrollerElement)
1823                                 scrollerElement.addEventListener("scroll", repositionSpectrum, false);
1824                             else
1825                                 console.error("Unable to handle color picker scrolling");
1826                         }
1827                     }
1828                     e.consume(true);
1829                 }
1830
1831                 function getFormat()
1832                 {
1833                     var format;
1834                     var formatSetting = WebInspector.settings.colorFormat.get();
1835                     if (formatSetting === cf.Original)
1836                         format = cf.Original;
1837                     else if (formatSetting === cf.RGB)
1838                         format = (color.simple ? cf.RGB : cf.RGBA);
1839                     else if (formatSetting === cf.HSL)
1840                         format = (color.simple ? cf.HSL : cf.HSLA);
1841                     else if (color.simple)
1842                         format = (color.hasShortHex() ? cf.ShortHEX : cf.HEX);
1843                     else
1844                         format = cf.RGBA;
1845
1846                     return format;
1847                 }
1848
1849                 var colorValueElement = document.createElement("span");
1850                 colorValueElement.textContent = color.toString(format);
1851
1852                 function nextFormat(curFormat)
1853                 {
1854                     // The format loop is as follows:
1855                     // * original
1856                     // * rgb(a)
1857                     // * hsl(a)
1858                     // * nickname (if the color has a nickname)
1859                     // * if the color is simple:
1860                     //   - shorthex (if has short hex)
1861                     //   - hex
1862                     switch (curFormat) {
1863                         case cf.Original:
1864                             return color.simple ? cf.RGB : cf.RGBA;
1865
1866                         case cf.RGB:
1867                         case cf.RGBA:
1868                             return color.simple ? cf.HSL : cf.HSLA;
1869
1870                         case cf.HSL:
1871                         case cf.HSLA:
1872                             if (color.nickname)
1873                                 return cf.Nickname;
1874                             if (color.simple)
1875                                 return color.hasShortHex() ? cf.ShortHEX : cf.HEX;
1876                             else
1877                                 return cf.Original;
1878
1879                         case cf.ShortHEX:
1880                             return cf.HEX;
1881
1882                         case cf.HEX:
1883                             return cf.Original;
1884
1885                         case cf.Nickname:
1886                             if (color.simple)
1887                                 return color.hasShortHex() ? cf.ShortHEX : cf.HEX;
1888                             else
1889                                 return cf.Original;
1890
1891                         default:
1892                             return null;
1893                     }
1894                 }
1895
1896                 function changeColorDisplay(event)
1897                 {
1898                     do {
1899                         format = nextFormat(format);
1900                         var currentValue = color.toString(format || "");
1901                     } while (format && currentValue === color.value && format !== cf.Original);
1902
1903                     if (format)
1904                         colorValueElement.textContent = currentValue;
1905                 }
1906
1907                 var container = document.createElement("nobr");
1908                 container.appendChild(colorSwatch.element);
1909                 container.appendChild(colorValueElement);
1910                 return container;
1911             }
1912
1913             var colorRegex = /((?:rgb|hsl)a?\([^)]+\)|#[0-9a-fA-F]{6}|#[0-9a-fA-F]{3}|\b\w+\b(?!-))/g;
1914             var colorProcessor = processValue.bind(window, colorRegex, processColor, null);
1915
1916             valueElement.appendChild(processValue(/url\(\s*([^)]+)\s*\)/g, linkifyURL.bind(this), WebInspector.CSSMetadata.isColorAwareProperty(self.name) ? colorProcessor : null, value));
1917         }
1918
1919         this.listItemElement.removeChildren();
1920         nameElement.normalize();
1921         valueElement.normalize();
1922
1923         if (!this.treeOutline)
1924             return;
1925
1926         // Append the checkbox for root elements of an editable section.
1927         if (enabledCheckboxElement && this.treeOutline.section && this.parent.root && !this.section.computedStyle)
1928             this.listItemElement.appendChild(enabledCheckboxElement);
1929         this.listItemElement.appendChild(nameElement);
1930         this.listItemElement.appendChild(document.createTextNode(": "));
1931         this.listItemElement.appendChild(this._expandElement);
1932         this.listItemElement.appendChild(valueElement);
1933         this.listItemElement.appendChild(document.createTextNode(";"));
1934
1935         if (!this.parsedOk) {
1936             // Avoid having longhands under an invalid shorthand.
1937             this.hasChildren = false;
1938             this.listItemElement.addStyleClass("not-parsed-ok");
1939
1940             // Add a separate exclamation mark IMG element with a tooltip.
1941             this.listItemElement.insertBefore(WebInspector.StylesSidebarPane.createExclamationMark(this.property.name), this.listItemElement.firstChild);
1942         }
1943         if (this.property.inactive)
1944             this.listItemElement.addStyleClass("inactive");
1945     },
1946
1947     _updatePane: function(userCallback)
1948     {
1949         if (this.treeOutline && this.treeOutline.section && this.treeOutline.section.pane)
1950             this.treeOutline.section.pane._refreshUpdate(this.treeOutline.section, false, userCallback);
1951         else  {
1952             if (userCallback)
1953                 userCallback();
1954         }
1955     },
1956
1957     toggleEnabled: function(event)
1958     {
1959         var disabled = !event.target.checked;
1960
1961         function callback(newStyle)
1962         {
1963             if (!newStyle)
1964                 return;
1965
1966             this.style = newStyle;
1967             this._styleRule.style = newStyle;
1968
1969             if (this.treeOutline.section && this.treeOutline.section.pane)
1970                 this.treeOutline.section.pane.dispatchEventToListeners("style property toggled");
1971
1972             this._updatePane();
1973
1974             delete this._parentPane._userOperation;
1975         }
1976
1977         this._parentPane._userOperation = true;
1978         this.property.setDisabled(disabled, callback.bind(this));
1979         event.consume();
1980     },
1981
1982     updateState: function()
1983     {
1984         if (!this.listItemElement)
1985             return;
1986
1987         if (this.style.isPropertyImplicit(this.name) || this.value === "initial")
1988             this.listItemElement.addStyleClass("implicit");
1989         else
1990             this.listItemElement.removeStyleClass("implicit");
1991
1992         if (this.inherited)
1993             this.listItemElement.addStyleClass("inherited");
1994         else
1995             this.listItemElement.removeStyleClass("inherited");
1996
1997         if (this.overloaded)
1998             this.listItemElement.addStyleClass("overloaded");
1999         else
2000             this.listItemElement.removeStyleClass("overloaded");
2001
2002         if (this.disabled)
2003             this.listItemElement.addStyleClass("disabled");
2004         else
2005             this.listItemElement.removeStyleClass("disabled");
2006     },
2007
2008     onpopulate: function()
2009     {
2010         // Only populate once and if this property is a shorthand.
2011         if (this.children.length || !this.isShorthand)
2012             return;
2013
2014         var longhandProperties = this.style.longhandProperties(this.name);
2015         for (var i = 0; i < longhandProperties.length; ++i) {
2016             var name = longhandProperties[i].name;
2017
2018             if (this.treeOutline.section) {
2019                 var inherited = this.treeOutline.section.isPropertyInherited(name);
2020                 var overloaded = this.treeOutline.section.isPropertyOverloaded(name);
2021             }
2022
2023             var liveProperty = this.style.getLiveProperty(name);
2024             if (!liveProperty)
2025                 continue;
2026
2027             var item = new WebInspector.StylePropertyTreeElement(this.section, this._parentPane, this._styleRule, this.style, liveProperty, false, inherited, overloaded);
2028             this.appendChild(item);
2029         }
2030     },
2031
2032     restoreNameElement: function()
2033     {
2034         // Restore <span class="webkit-css-property"> if it doesn't yet exist or was accidentally deleted.
2035         if (this.nameElement === this.listItemElement.querySelector(".webkit-css-property"))
2036             return;
2037
2038         this.nameElement = document.createElement("span");
2039         this.nameElement.className = "webkit-css-property";
2040         this.nameElement.textContent = "";
2041         this.listItemElement.insertBefore(this.nameElement, this.listItemElement.firstChild);
2042     },
2043
2044     _mouseClick: function(event)
2045     {
2046         if (!window.getSelection().isCollapsed)
2047             return;
2048
2049         event.consume(true);
2050
2051         if (event.target === this.listItemElement) {
2052             if (!this.section.editable) 
2053                 return;
2054
2055             if (this.section._checkWillCancelEditing())
2056                 return;
2057             this.section.addNewBlankProperty(this.property.index + 1).startEditing();
2058             return;
2059         }
2060
2061         this.startEditing(event.target);
2062     },
2063
2064     _isNameElement: function(element)
2065     {
2066         return element.enclosingNodeOrSelfWithClass("webkit-css-property") === this.nameElement;
2067     },
2068
2069     _isValueElement: function(element)
2070     {
2071         return !!element.enclosingNodeOrSelfWithClass("value");
2072     },
2073
2074     startEditing: function(selectElement)
2075     {
2076         // FIXME: we don't allow editing of longhand properties under a shorthand right now.
2077         if (this.parent.isShorthand)
2078             return;
2079
2080         if (selectElement === this._expandElement)
2081             return;
2082
2083         if (this.treeOutline.section && !this.treeOutline.section.editable)
2084             return;
2085
2086         if (!selectElement)
2087             selectElement = this.nameElement; // No arguments passed in - edit the name element by default.
2088         else
2089             selectElement = selectElement.enclosingNodeOrSelfWithClass("webkit-css-property") || selectElement.enclosingNodeOrSelfWithClass("value");
2090
2091         var isEditingName = selectElement === this.nameElement;
2092         if (!isEditingName) {
2093             if (selectElement !== this.valueElement) {
2094                 // Click in the LI - start editing value.
2095                 isEditingName = false;
2096                 selectElement = this.valueElement;
2097             }
2098
2099             this.valueElement.textContent = this.value;
2100         }
2101
2102         if (WebInspector.isBeingEdited(selectElement))
2103             return;
2104
2105         var context = {
2106             expanded: this.expanded,
2107             hasChildren: this.hasChildren,
2108             isEditingName: isEditingName,
2109             previousContent: selectElement.textContent
2110         };
2111
2112         // Lie about our children to prevent expanding on double click and to collapse shorthands.
2113         this.hasChildren = false;
2114
2115         if (selectElement.parentElement)
2116             selectElement.parentElement.addStyleClass("child-editing");
2117         selectElement.textContent = selectElement.textContent; // remove color swatch and the like
2118
2119         function pasteHandler(context, event)
2120         {
2121             var data = event.clipboardData.getData("Text");
2122             if (!data)
2123                 return;
2124             var colonIdx = data.indexOf(":");
2125             if (colonIdx < 0)
2126                 return;
2127             var name = data.substring(0, colonIdx).trim();
2128             var value = data.substring(colonIdx + 1).trim();
2129
2130             event.preventDefault();
2131
2132             if (!("originalName" in context)) {
2133                 context.originalName = this.nameElement.textContent;
2134                 context.originalValue = this.valueElement.textContent;
2135             }
2136             this.nameElement.textContent = name;
2137             this.valueElement.textContent = value;
2138             this.nameElement.normalize();
2139             this.valueElement.normalize();
2140
2141             this.editingCommitted(null, event.target.textContent, context.previousContent, context, "forward");
2142         }
2143
2144         function blurListener(context, event)
2145         {
2146             var treeElement = this._parentPane._mouseDownTreeElement;
2147             var moveDirection = "";
2148             if (treeElement === this) {
2149                 if (isEditingName && this._parentPane._mouseDownTreeElementIsValue)
2150                     moveDirection = "forward";
2151                 if (!isEditingName && this._parentPane._mouseDownTreeElementIsName)
2152                     moveDirection = "backward";
2153             }
2154             this.editingCommitted(null, event.target.textContent, context.previousContent, context, moveDirection);
2155         }
2156
2157         delete this.originalPropertyText;
2158
2159         this._parentPane._isEditingStyle = true;
2160         if (selectElement.parentElement)
2161             selectElement.parentElement.scrollIntoViewIfNeeded(false);
2162
2163         var applyItemCallback = !isEditingName ? this._applyFreeFlowStyleTextEdit.bind(this, true) : undefined;
2164         this._prompt = new WebInspector.StylesSidebarPane.CSSPropertyPrompt(isEditingName ? WebInspector.CSSMetadata.cssPropertiesMetainfo : WebInspector.CSSMetadata.keywordsForProperty(this.nameElement.textContent), this, isEditingName);
2165         if (applyItemCallback) {
2166             this._prompt.addEventListener(WebInspector.TextPrompt.Events.ItemApplied, applyItemCallback, this);
2167             this._prompt.addEventListener(WebInspector.TextPrompt.Events.ItemAccepted, applyItemCallback, this);
2168         }
2169         var proxyElement = this._prompt.attachAndStartEditing(selectElement, blurListener.bind(this, context));
2170
2171         proxyElement.addEventListener("keydown", this.editingNameValueKeyDown.bind(this, context), false);
2172         if (isEditingName)
2173             proxyElement.addEventListener("paste", pasteHandler.bind(this, context));
2174
2175         window.getSelection().setBaseAndExtent(selectElement, 0, selectElement, 1);
2176     },
2177
2178     editingNameValueKeyDown: function(context, event)
2179     {
2180         if (event.handled)
2181             return;
2182
2183         var isEditingName = context.isEditingName;
2184         var result;
2185
2186         function shouldCommitValueSemicolon(text, cursorPosition)
2187         {
2188             // FIXME: should this account for semicolons inside comments?
2189             var openQuote = "";
2190             for (var i = 0; i < cursorPosition; ++i) {
2191                 var ch = text[i];
2192                 if (ch === "\\" && openQuote !== "")
2193                     ++i; // skip next character inside string
2194                 else if (!openQuote && (ch === "\"" || ch === "'"))
2195                     openQuote = ch;
2196                 else if (openQuote === ch)
2197                     openQuote = "";
2198             }
2199             return !openQuote;
2200         }
2201
2202         // FIXME: the ":"/";" detection does not work for non-US layouts due to the event being keydown rather than keypress.
2203         var isFieldInputTerminated = (event.keyCode === WebInspector.KeyboardShortcut.Keys.Semicolon.code) &&
2204             (isEditingName ? event.shiftKey : (!event.shiftKey && shouldCommitValueSemicolon(event.target.textContent, event.target.selectionLeftOffset())));
2205         if (isEnterKey(event) || isFieldInputTerminated) {
2206             // Enter or colon (for name)/semicolon outside of string (for value).
2207             event.preventDefault();
2208             result = "forward";
2209         } else if (event.keyCode === WebInspector.KeyboardShortcut.Keys.Esc.code || event.keyIdentifier === "U+001B")
2210             result = "cancel";
2211         else if (!isEditingName && this._newProperty && event.keyCode === WebInspector.KeyboardShortcut.Keys.Backspace.code) {
2212             // For a new property, when Backspace is pressed at the beginning of new property value, move back to the property name.
2213             var selection = window.getSelection();
2214             if (selection.isCollapsed && !selection.focusOffset) {
2215                 event.preventDefault();
2216                 result = "backward";
2217             }
2218         } else if (event.keyIdentifier === "U+0009") { // Tab key.
2219             result = event.shiftKey ? "backward" : "forward";
2220             event.preventDefault();
2221         }
2222
2223         if (result) {
2224             switch (result) {
2225             case "cancel":
2226                 this.editingCancelled(null, context);
2227                 break;
2228             case "forward":
2229             case "backward":
2230                 this.editingCommitted(null, event.target.textContent, context.previousContent, context, result);
2231                 break;
2232             }
2233
2234             event.consume();
2235             return;
2236         }
2237
2238         if (!isEditingName)
2239             this._applyFreeFlowStyleTextEdit(false);
2240     },
2241
2242     _applyFreeFlowStyleTextEdit: function(now)
2243     {
2244         if (this._applyFreeFlowStyleTextEditTimer)
2245             clearTimeout(this._applyFreeFlowStyleTextEditTimer);
2246
2247         function apply()
2248         {
2249             var valueText = this.valueElement.textContent;
2250             if (valueText.indexOf(";") === -1)
2251                 this.applyStyleText(this.nameElement.textContent + ": " + valueText, false, false, false);
2252         }
2253         if (now)
2254             apply.call(this);
2255         else
2256             this._applyFreeFlowStyleTextEditTimer = setTimeout(apply.bind(this), 100);
2257     },
2258
2259     kickFreeFlowStyleEditForTest: function()
2260     {
2261         this._applyFreeFlowStyleTextEdit(true);
2262     },
2263
2264     editingEnded: function(context)
2265     {
2266         this._resetMouseDownElement();
2267         if (this._applyFreeFlowStyleTextEditTimer)
2268             clearTimeout(this._applyFreeFlowStyleTextEditTimer);
2269
2270         this.hasChildren = context.hasChildren;
2271         if (context.expanded)
2272             this.expand();
2273         var editedElement = context.isEditingName ? this.nameElement : this.valueElement;
2274         // The proxyElement has been deleted, no need to remove listener.
2275         if (editedElement.parentElement)
2276             editedElement.parentElement.removeStyleClass("child-editing");
2277
2278         delete this._parentPane._isEditingStyle;
2279     },
2280
2281     editingCancelled: function(element, context)
2282     {
2283         this._removePrompt();
2284         this._revertStyleUponEditingCanceled(this.originalPropertyText);
2285         // This should happen last, as it clears the info necessary to restore the property value after [Page]Up/Down changes.
2286         this.editingEnded(context);
2287     },
2288
2289     _revertStyleUponEditingCanceled: function(originalPropertyText)
2290     {
2291         if (typeof originalPropertyText === "string") {
2292             delete this.originalPropertyText;
2293             this.applyStyleText(originalPropertyText, true, false, true);
2294         } else {
2295             if (this._newProperty)
2296                 this.treeOutline.removeChild(this);
2297             else
2298                 this.updateTitle();
2299         }
2300     },
2301
2302     _findSibling: function(moveDirection)
2303     {
2304         var target = this;
2305         do {
2306             target = (moveDirection === "forward" ? target.nextSibling : target.previousSibling);
2307         } while(target && target.inherited);
2308
2309         return target;
2310     },
2311
2312     editingCommitted: function(element, userInput, previousContent, context, moveDirection)
2313     {
2314         this._removePrompt();
2315         this.editingEnded(context);
2316         var isEditingName = context.isEditingName;
2317
2318         // Determine where to move to before making changes
2319         var createNewProperty, moveToPropertyName, moveToSelector;
2320         var moveTo = this;
2321         var moveToOther = (isEditingName ^ (moveDirection === "forward"));
2322         var abandonNewProperty = this._newProperty && !userInput && (moveToOther || isEditingName);
2323         if (moveDirection === "forward" && !isEditingName || moveDirection === "backward" && isEditingName) {
2324             moveTo = moveTo._findSibling(moveDirection);
2325             if (moveTo)
2326                 moveToPropertyName = moveTo.name;
2327             else if (moveDirection === "forward" && (!this._newProperty || userInput))
2328                 createNewProperty = true;
2329             else if (moveDirection === "backward")
2330                 moveToSelector = true;
2331         }
2332
2333         // Make the Changes and trigger the moveToNextCallback after updating.
2334         var moveToIndex = moveTo && this.treeOutline ? this.treeOutline.children.indexOf(moveTo) : -1;
2335         var blankInput = /^\s*$/.test(userInput);
2336         var isDataPasted = "originalName" in context;
2337         var isDirtyViaPaste = isDataPasted && (this.nameElement.textContent !== context.originalName || this.valueElement.textContent !== context.originalValue);
2338         var shouldCommitNewProperty = this._newProperty && (moveToOther || (!moveDirection && !isEditingName) || (isEditingName && blankInput));
2339         if (((userInput !== previousContent || isDirtyViaPaste) && !this._newProperty) || shouldCommitNewProperty) {
2340             this.treeOutline.section._afterUpdate = moveToNextCallback.bind(this, this._newProperty, !blankInput, this.treeOutline.section);
2341             var propertyText;
2342             if (blankInput || (this._newProperty && /^\s*$/.test(this.valueElement.textContent)))
2343                 propertyText = "";
2344             else {
2345                 if (isEditingName)
2346                     propertyText = userInput + ": " + this.valueElement.textContent;
2347                 else
2348                     propertyText = this.nameElement.textContent + ": " + userInput;
2349             }
2350             this.applyStyleText(propertyText, true, true, false);
2351         } else {
2352             if (!isDataPasted && !this._newProperty)
2353                 this.updateTitle();
2354             moveToNextCallback.call(this, this._newProperty, false, this.treeOutline.section);
2355         }
2356
2357         // The Callback to start editing the next/previous property/selector.
2358         function moveToNextCallback(alreadyNew, valueChanged, section)
2359         {
2360             if (!moveDirection)
2361                 return;
2362
2363             // User just tabbed through without changes.
2364             if (moveTo && moveTo.parent) {
2365                 moveTo.startEditing(!isEditingName ? moveTo.nameElement : moveTo.valueElement);
2366                 return;
2367             }
2368
2369             // User has made a change then tabbed, wiping all the original treeElements.
2370             // Recalculate the new treeElement for the same property we were going to edit next.
2371             if (moveTo && !moveTo.parent) {
2372                 var propertyElements = section.propertiesTreeOutline.children;
2373                 if (moveDirection === "forward" && blankInput && !isEditingName)
2374                     --moveToIndex;
2375                 if (moveToIndex >= propertyElements.length && !this._newProperty)
2376                     createNewProperty = true;
2377                 else {
2378                     var treeElement = moveToIndex >= 0 ? propertyElements[moveToIndex] : null;
2379                     if (treeElement) {
2380                         var elementToEdit = !isEditingName ? treeElement.nameElement : treeElement.valueElement;
2381                         if (alreadyNew && blankInput)
2382                             elementToEdit = moveDirection === "forward" ? treeElement.nameElement : treeElement.valueElement;
2383                         treeElement.startEditing(elementToEdit);
2384                         return;
2385                     } else if (!alreadyNew)
2386                         moveToSelector = true;
2387                 }
2388             }
2389
2390             // Create a new attribute in this section (or move to next editable selector if possible).
2391             if (createNewProperty) {
2392                 if (alreadyNew && !valueChanged && (isEditingName ^ (moveDirection === "backward")))
2393                     return;
2394
2395                 section.addNewBlankProperty().startEditing();
2396                 return;
2397             }
2398
2399             if (abandonNewProperty) {
2400                 moveTo = this._findSibling(moveDirection);
2401                 var sectionToEdit = (moveTo || moveDirection === "backward") ? section : section.nextEditableSibling();
2402                 if (sectionToEdit) {
2403                     if (sectionToEdit.rule)
2404                         sectionToEdit.startEditingSelector();
2405                     else
2406                         sectionToEdit._moveEditorFromSelector(moveDirection);
2407                 }
2408                 return;
2409             }
2410
2411             if (moveToSelector) {
2412                 if (section.rule)
2413                     section.startEditingSelector();
2414                 else
2415                     section._moveEditorFromSelector(moveDirection);
2416             }
2417         }
2418     },
2419
2420     _removePrompt: function()
2421     {
2422         // BUG 53242. This cannot go into editingEnded(), as it should always happen first for any editing outcome.
2423         if (this._prompt) {
2424             this._prompt.detach();
2425             delete this._prompt;
2426         }
2427     },
2428
2429     _hasBeenModifiedIncrementally: function()
2430     {
2431         // New properties applied via up/down or live editing have an originalPropertyText and will be deleted later
2432         // on, if cancelled, when the empty string gets applied as their style text.
2433         return typeof this.originalPropertyText === "string" || (!!this.property.propertyText && this._newProperty);
2434     },
2435
2436     applyStyleText: function(styleText, updateInterface, majorChange, isRevert)
2437     {
2438         function userOperationFinishedCallback(parentPane, updateInterface)
2439         {
2440             if (updateInterface)
2441                 delete parentPane._userOperation;
2442         }
2443
2444         // Leave a way to cancel editing after incremental changes.
2445         if (!isRevert && !updateInterface && !this._hasBeenModifiedIncrementally()) {
2446             // Remember the rule's original CSS text on [Page](Up|Down), so it can be restored
2447             // if the editing is canceled.
2448             this.originalPropertyText = this.property.propertyText;
2449         }
2450
2451         if (!this.treeOutline)
2452             return;
2453
2454         var section = this.treeOutline.section;
2455         styleText = styleText.replace(/\s/g, " ").trim(); // Replace &nbsp; with whitespace.
2456         var styleTextLength = styleText.length;
2457         if (!styleTextLength && updateInterface && !isRevert && this._newProperty && !this._hasBeenModifiedIncrementally()) {
2458             // The user deleted everything and never applied a new property value via Up/Down scrolling/live editing, so remove the tree element and update.
2459             this.parent.removeChild(this);
2460             section.afterUpdate();
2461             return;
2462         }
2463
2464         var currentNode = this._parentPane.node;
2465         if (updateInterface)
2466             this._parentPane._userOperation = true;
2467
2468         function callback(userCallback, originalPropertyText, newStyle)
2469         {
2470             if (!newStyle) {
2471                 if (updateInterface) {
2472                     // It did not apply, cancel editing.
2473                     this._revertStyleUponEditingCanceled(originalPropertyText);
2474                 }
2475                 userCallback();
2476                 return;
2477             }
2478
2479             if (this._newProperty)
2480                 this._newPropertyInStyle = true;
2481             this.style = newStyle;
2482             this.property = newStyle.propertyAt(this.property.index);
2483             this._styleRule.style = this.style;
2484
2485             if (section && section.pane)
2486                 section.pane.dispatchEventToListeners("style edited");
2487
2488             if (updateInterface && currentNode === section.pane.node) {
2489                 this._updatePane(userCallback);
2490                 return;
2491             }
2492
2493             userCallback();
2494         }
2495
2496         // Append a ";" if the new text does not end in ";".
2497         // FIXME: this does not handle trailing comments.
2498         if (styleText.length && !/;\s*$/.test(styleText))
2499             styleText += ";";
2500         var overwriteProperty = !!(!this._newProperty || this._newPropertyInStyle);
2501         this.property.setText(styleText, majorChange, overwriteProperty, callback.bind(this, userOperationFinishedCallback.bind(null, this._parentPane, updateInterface), this.originalPropertyText));
2502     },
2503
2504     ondblclick: function()
2505     {
2506         return true; // handled
2507     },
2508
2509     isEventWithinDisclosureTriangle: function(event)
2510     {
2511         if (!this.section.computedStyle)
2512             return event.target === this._expandElement;
2513         return TreeElement.prototype.isEventWithinDisclosureTriangle.call(this, event);
2514     },
2515
2516     __proto__: TreeElement.prototype
2517 }
2518
2519 /**
2520  * @constructor
2521  * @extends {WebInspector.TextPrompt}
2522  * @param {function(*)=} acceptCallback
2523  */
2524 WebInspector.StylesSidebarPane.CSSPropertyPrompt = function(cssCompletions, sidebarPane, isEditingName, acceptCallback)
2525 {
2526     // Use the same callback both for applyItemCallback and acceptItemCallback.
2527     WebInspector.TextPrompt.call(this, this._buildPropertyCompletions.bind(this), WebInspector.StyleValueDelimiters);
2528     this.setSuggestBoxEnabled("generic-suggest");
2529     this._cssCompletions = cssCompletions;
2530     this._sidebarPane = sidebarPane;
2531     this._isEditingName = isEditingName;
2532 }
2533
2534 WebInspector.StylesSidebarPane.CSSPropertyPrompt.prototype = {
2535     onKeyDown: function(event)
2536     {
2537         switch (event.keyIdentifier) {
2538         case "Up":
2539         case "Down":
2540         case "PageUp":
2541         case "PageDown":
2542             if (this._handleNameOrValueUpDown(event)) {
2543                 event.preventDefault();
2544                 return;
2545             }
2546             break;
2547         }
2548
2549         WebInspector.TextPrompt.prototype.onKeyDown.call(this, event);
2550     },
2551
2552     onMouseWheel: function(event)
2553     {
2554         if (this._handleNameOrValueUpDown(event)) {
2555             event.consume(true);
2556             return;
2557         }
2558         WebInspector.TextPrompt.prototype.onMouseWheel.call(this, event);
2559     },
2560
2561     tabKeyPressed: function()
2562     {
2563         this.acceptAutoComplete();
2564
2565         // Always tab to the next field.
2566         return false;
2567     },
2568
2569     _handleNameOrValueUpDown: function(event)
2570     {
2571         function finishHandler(originalValue, replacementString)
2572         {
2573             // Synthesize property text disregarding any comments, custom whitespace etc.
2574             this._sidebarPane.applyStyleText(this._sidebarPane.nameElement.textContent + ": " + this._sidebarPane.valueElement.textContent, false, false, false);
2575         }
2576     
2577         // Handle numeric value increment/decrement only at this point.
2578         if (!this._isEditingName && WebInspector.handleElementValueModifications(event, this._sidebarPane.valueElement, finishHandler.bind(this), this._isValueSuggestion.bind(this)))
2579             return true;
2580
2581         return false;
2582     },
2583
2584     _isValueSuggestion: function(word)
2585     {
2586         if (!word)
2587             return false;
2588         word = word.toLowerCase();
2589         return this._cssCompletions.keySet().hasOwnProperty(word);
2590     },
2591
2592     /**
2593      * @param {Element} proxyElement
2594      * @param {Range} wordRange
2595      * @param {boolean} force
2596      * @param {function(Array.<string>, number=)} completionsReadyCallback
2597      */
2598     _buildPropertyCompletions: function(proxyElement, wordRange, force, completionsReadyCallback)
2599     {
2600         var prefix = wordRange.toString().toLowerCase();
2601         if (!prefix && !force)
2602             return;
2603
2604         var results = this._cssCompletions.startsWith(prefix);
2605         var selectedIndex = this._cssCompletions.mostUsedOf(results);
2606         completionsReadyCallback(results, selectedIndex);
2607     },
2608
2609     __proto__: WebInspector.TextPrompt.prototype
2610 }