ee64430473404cea794e7a9a260ed744bd8c322f
[WebKit-https.git] / WebKit / WebInspector / webInspector / inspector.js
1 /*
2  * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1.  Redistributions of source code must retain the above copyright
9  *     notice, this list of conditions and the following disclaimer. 
10  * 2.  Redistributions in binary form must reproduce the above copyright
11  *     notice, this list of conditions and the following disclaimer in the
12  *     documentation and/or other materials provided with the distribution. 
13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
14  *     its contributors may be used to endorse or promote products derived
15  *     from this software without specific prior written permission. 
16  *
17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 var Inspector = null;
30 var showUserAgentStyles = true;
31
32 // Property values to omit in the computed style list.
33 // If a property has this value, it will be omitted.
34 // Note we do not provide a value for "display", "height", or "width", for example,
35 // since we always want to display those.
36 var typicalStylePropertyValue = {
37     "-webkit-background-clip": "border",
38     "-webkit-background-composite": "source-over",
39     "-webkit-background-origin": "padding",
40     "-webkit-background-size": "auto auto",
41     "-webkit-border-horizontal-spacing": "0px",
42     "-webkit-border-vertical-spacing": "0px",
43     "-webkit-box-align": "stretch",
44     "-webkit-box-direction": "normal",
45     "-webkit-box-flex": "0",
46     "-webkit-box-flex-group": "1",
47     "-webkit-box-lines": "single",
48     "-webkit-box-ordinal-group": "1",
49     "-webkit-box-orient": "horizontal",
50     "-webkit-box-pack": "start",
51     "-webkit-highlight": "none",
52     "-webkit-line-break": "normal",
53     "-webkit-line-clamp": "none",
54     "-webkit-marquee-direction": "auto",
55     "-webkit-marquee-increment": "6px",
56     "-webkit-marquee-repetition": "infinite",
57     "-webkit-marquee-style": "scroll",
58     "-webkit-nbsp-mode": "normal",
59     "-webkit-text-decorations-in-effect": "none",
60     "-webkit-text-security": "none",
61     "-webkit-user-modify": "read-only",
62     "background-attachment": "scroll",
63     "background-color": "rgba(0, 0, 0, 0)",
64     "background-image": "none",
65     "background-position-x": "auto",
66     "background-position-y": "auto",
67     "background-repeat": "repeat",
68     "border-bottom-color": "rgba(0, 0, 0, 0)",
69     "border-bottom-style": "none",
70     "border-bottom-width": "0px",
71     "border-collapse": "separate",
72     "border-left-color": "rgba(0, 0, 0, 0)",
73     "border-left-style": "none",
74     "border-left-width": "0px",
75     "border-right-color": "rgba(0, 0, 0, 0)",
76     "border-right-style": "none",
77     "border-right-width": "0px",
78     "border-top-color": "rgba(0, 0, 0, 0)",
79     "border-top-style": "none",
80     "border-top-width": "0px",
81     "bottom": "auto",
82     "caption-side": "top",
83     "clear": "none",
84     "color": "rgb(0, 0, 0)",
85     "cursor": "auto",
86     "direction": "ltr",
87     "empty-cells": "show",
88     "float": "none",
89     "font-style": "normal",
90     "font-variant": "normal",
91     "font-weight": "normal",
92     "left": "auto",
93     "letter-spacing": "normal",
94     "line-height": "normal",
95     "list-style-image": "none",
96     "list-style-position": "outside",
97     "list-style-type": "disc",
98     "margin-bottom": "0px",
99     "margin-left": "0px",
100     "margin-right": "0px",
101     "margin-top": "0px",
102     "max-height": "none",
103     "max-width": "none",
104     "min-height": "0px",
105     "min-width": "0px",
106     "opacity": "1",
107     "orphans": "2",
108     "outline-style": "none",
109     "overflow": "visible",
110     "overflow-x": "visible",
111     "overflow-y": "visible",
112     "padding-bottom": "0px",
113     "padding-left": "0px",
114     "padding-right": "0px",
115     "padding-top": "0px",
116     "page-break-after": "auto",
117     "page-break-before": "auto",
118     "page-break-inside": "auto",
119     "position": "static",
120     "resize": "none",
121     "right": "auto",
122     "table-layout": "auto",
123     "text-align": "auto",
124     "text-decoration": "none",
125     "text-indent": "0px",
126     "text-shadow": "none",
127     "text-transform": "none",
128     "top": "auto",
129     "unicode-bidi": "normal",
130     "vertical-align": "baseline",
131     "visibility": "visible",
132     "white-space": "normal",
133     "widows": "2",
134     "word-spacing": "0px",
135     "word-wrap": "normal",
136     "z-index": "normal",
137 };
138
139 // "Nicknames" for some common values that are easier to read.
140 var valueNickname = {
141     "rgb(0, 0, 0)": "black",
142     "rgb(255, 255, 255)": "white",
143     "rgba(0, 0, 0, 0)": "transparent",
144 };
145
146 // Display types for which margin is ignored.
147 var noMarginDisplayType = {
148     "table-cell": "no",
149     "table-column": "no",
150     "table-column-group": "no",
151     "table-footer-group": "no",
152     "table-header-group": "no",
153     "table-row": "no",
154     "table-row-group": "no",
155 };
156
157 // Display types for which padding is ignored.
158 var noPaddingDisplayType = {
159     "table-column": "no",
160     "table-column-group": "no",
161     "table-footer-group": "no",
162     "table-header-group": "no",
163     "table-row": "no",
164     "table-row-group": "no",
165 };
166
167 function setUpScrollbar(id)
168 {
169     var bar = new AppleVerticalScrollbar(document.getElementById(id));
170
171     bar.setTrackStart("Images/scrollTrackTop.png", 18);
172     bar.setTrackMiddle("Images/scrollTrackMiddle.png");
173     bar.setTrackEnd("Images/scrollTrackBottom.png", 18);
174     bar.setThumbStart("Images/scrollThumbTop.png", 9);
175     bar.setThumbMiddle("Images/scrollThumbMiddle.png");
176     bar.setThumbEnd("Images/scrollThumbBottom.png", 9);
177
178     return bar;
179 }
180
181 function loaded()
182 {
183     treeScrollbar = setUpScrollbar("treeScrollbar");
184
185     nodeContentsScrollArea = new AppleScrollArea(document.getElementById("nodeContentsScrollview"),
186         setUpScrollbar("nodeContentsScrollbar"));
187     elementAttributesScrollArea = new AppleScrollArea(document.getElementById("elementAttributesScrollview"),
188         setUpScrollbar("elementAttributesScrollbar"));
189     
190     styleRulesScrollArea = new AppleScrollArea(document.getElementById("styleRulesScrollview"),
191         setUpScrollbar("styleRulesScrollbar"));
192     stylePropertiesScrollArea = new AppleScrollArea(document.getElementById("stylePropertiesScrollview"),
193         setUpScrollbar("stylePropertiesScrollbar"));
194
195     jsPropertiesScrollArea = new AppleScrollArea(document.getElementById("jsPropertiesScrollview"),
196         setUpScrollbar("jsPropertiesScrollbar"));
197
198     treeScrollbar._getViewToContentRatio = function() {
199         var contentHeight = Inspector.treeViewScrollHeight();
200         var height = document.getElementById("treeScrollArea").offsetHeight;
201         if (contentHeight > height)
202             return height / contentHeight;
203         return 1.0;
204     }
205
206     treeScrollbar._computeTrackOffset = function() { return Inspector.treeViewOffsetTop(); }
207     treeScrollbar._getContentLength = function() { return Inspector.treeViewScrollHeight(); }
208     treeScrollbar._getViewLength = function() { return document.getElementById("treeScrollArea").offsetHeight; }
209     treeScrollbar._canScroll = function() { return true; }
210
211     treeScrollbar.scrollTo = function(pos) {
212         Inspector.treeViewScrollTo(pos);
213         this.verticalHasScrolled();
214     }
215
216     treeScrollbar.verticalHasScrolled = function() {
217         var new_thumb_pos = this._thumbPositionForContentPosition(Inspector.treeViewOffsetTop());
218         this._thumbStart = new_thumb_pos;
219         this._thumb.style.top = new_thumb_pos + "px";
220     }
221
222     // much better AppleScrollArea reveal
223     AppleScrollArea.prototype.reveal = function(node) {
224         var offsetY = 0;
225         var obj = node;
226         do {
227             offsetY += obj.offsetTop;
228             obj = obj.offsetParent;
229         } while (obj && obj != this.content);
230
231         var offsetX = 0;
232         obj = node;
233         do {
234             offsetX += obj.offsetLeft;
235             obj = obj.offsetParent;
236         } while (obj && obj != this.content);
237
238         var top = this.content.scrollTop;
239         var height = this.viewHeight;
240         if ((top + height) < (offsetY + node.clientHeight)) 
241             this.verticalScrollTo(offsetY - height + node.clientHeight);
242         else if (top > offsetY)
243             this.verticalScrollTo(offsetY);
244
245         var left = this.content.scrollLeft;
246         var width = this.viewWidth;
247         if ((left + width) < (offsetX + node.clientWidth)) 
248             this.horizontalScrollTo(offsetX - width + node.clientWidth);
249         else if (left > offsetX)
250             this.horizontalScrollTo(offsetX);
251     }
252
253     // Change the standard show/hide to include the entire scrollbar.
254     // This lets the content reflow to use the additional space when the scrollbar is hidden.
255     AppleScrollbar.prototype.hide = function() {
256         this._track.style.display = "none";
257         this.scrollbar.style.display = "none";
258         this.hidden = true;
259     }
260     AppleScrollbar.prototype.show = function() {
261         this._track.style.display = "block";
262         this.scrollbar.style.display = null;
263         if (this.hidden) {
264             this.hidden = false;
265             this.refresh();
266         }
267     }
268
269     window.addEventListener("resize", refreshScrollbars, false);
270
271     toggleNoSelection(false);
272     switchPane("node");
273 }
274
275 function refreshScrollbars()
276 {
277     elementAttributesScrollArea.refresh();
278     jsPropertiesScrollArea.refresh();
279     nodeContentsScrollArea.refresh();
280     stylePropertiesScrollArea.refresh();
281     styleRulesScrollArea.refresh();
282 }
283
284 var searchActive = false;
285
286 function performSearch(query)
287 {
288     var treePopup = document.getElementById("treePopup");
289     var searchField = document.getElementById("search");
290     var searchCount = document.getElementById("searchCount");
291
292     if (query.length && !searchActive) {
293         treePopup.style.display = "none";
294         searchCount.style.display = "block";
295         searchField.style.width = "150px";
296         searchActive = true;
297     } else if (!query.length && searchActive) {
298         treePopup.style.display = null;
299         searchCount.style.display = null;
300         searchField.style.width = null;
301         searchActive = false;
302     }
303
304     Inspector.searchPerformed(query);
305 }
306
307 function resultsWithXpathQuery(query)
308 {
309     var nodeList = null;
310     try {
311         var focusedNode = Inspector.focusedDOMNode();
312         nodeList = focusedNode.document.evaluate(query, focusedNode.document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE);
313     } catch(err) {
314         // ignore any exceptions. the query might be malformed, but we allow that 
315     }
316     return nodeList;
317 }
318
319 var tabNames = ["node","metrics","style","properties"];
320 var currentPane = "node";
321 var paneUpdateState = [];
322 var noSelection = false;
323
324 function toggleNoSelection(state)
325 {
326     noSelection = state;
327     if (noSelection) {
328         for (var i = 0; i < tabNames.length; i++)
329             document.getElementById(tabNames[i] + "Pane").style.display = "none";
330         document.getElementById("noSelection").style.display = null;
331     } else {
332         document.getElementById("noSelection").style.display = "none";
333         switchPane(currentPane);
334     }
335 }
336
337 function switchPane(pane)
338 {
339     currentPane = pane;
340     for (var i = 0; i < tabNames.length; i++) {
341         var paneElement = document.getElementById(tabNames[i] + "Pane");
342         var button = document.getElementById(tabNames[i] + "Button");
343         if (!button.originalClassName)
344             button.originalClassName = button.className;
345         if (pane == tabNames[i]) {
346             if (!noSelection)
347                 paneElement.style.display = null;
348             button.className = button.originalClassName + " selected";
349         } else {
350             paneElement.style.display = "none";
351             button.className = button.originalClassName;
352         }
353     }
354
355     if (noSelection)
356         return;
357
358     if (!paneUpdateState[pane]) {
359         eval("update" + pane.charAt(0).toUpperCase() + pane.substr(1) + "Pane()");
360         paneUpdateState[pane] = true;
361     } else {
362         refreshScrollbars();
363     }
364 }
365
366 function nodeTypeName(node)
367 {
368     switch (node.nodeType) {
369         case Node.ELEMENT_NODE: return "Element";
370         case Node.ATTRIBUTE_NODE: return "Attribute";
371         case Node.TEXT_NODE: return "Text";
372         case Node.CDATA_SECTION_NODE: return "Character Data";
373         case Node.ENTITY_REFERENCE_NODE: return "Entity Reference";
374         case Node.ENTITY_NODE: return "Entity";
375         case Node.PROCESSING_INSTRUCTION_NODE: return "Processing Instruction";
376         case Node.COMMENT_NODE: return "Comment";
377         case Node.DOCUMENT_NODE: return "Document";
378         case Node.DOCUMENT_TYPE_NODE: return "Document Type";
379         case Node.DOCUMENT_FRAGMENT_NODE: return "Document Fragment";
380         case Node.NOTATION_NODE: return "Notation";
381     }
382     return "(unknown)";
383 }
384
385 function updatePanes()
386 {
387     for (var i = 0; i < tabNames.length; i++)
388         paneUpdateState[tabNames[i]] = false;
389     if (noSelection)
390         return;
391     eval("update" + currentPane.charAt(0).toUpperCase() + currentPane.substr(1) + "Pane()");    
392     paneUpdateState[currentPane] = true;
393 }
394
395 function updateElementAttributes()
396 {
397     var focusedNode = Inspector.focusedDOMNode();
398     var attributesList = document.getElementById("elementAttributesList")
399
400     attributesList.innerHTML = "";
401
402     if (!focusedNode.attributes.length)
403         attributesList.innerHTML = "<span class=\"disabled\">(none)</span>";
404
405     for (i = 0; i < focusedNode.attributes.length; i++) {
406         var attr = focusedNode.attributes[i];
407         var li = document.createElement("li");
408
409         var span = document.createElement("span");
410         span.className = "property";
411         if (attr.namespaceURI)
412             span.title = attr.namespaceURI;
413         span.textContent = attr.name;
414         li.appendChild(span);
415         
416         span = document.createElement("span");
417         span.className = "relation";
418         span.textContent = "=";
419         li.appendChild(span);
420
421         span = document.createElement("span");
422         span.className = "value";
423         span.textContent = "\"" + attr.value + "\"";
424         span.title = attr.value;
425         li.appendChild(span);
426
427         if (attr.style) {
428             span = document.createElement("span");
429             span.className = "mapped";
430             span.innerHTML = "(<a href=\"javascript:selectMappedStyleRule('" + attr.name + "')\">mapped style</a>)";
431             li.appendChild(span);
432         }
433
434         attributesList.appendChild(li);
435     }
436
437     elementAttributesScrollArea.refresh();
438 }
439
440 function updateNodePane()
441 {
442     var focusedNode = Inspector.focusedDOMNode();
443
444     if (focusedNode.nodeType == Node.TEXT_NODE || focusedNode.nodeType == Node.COMMENT_NODE) {
445         document.getElementById("nodeNamespaceRow").style.display = "none";
446         document.getElementById("elementAttributes").style.display = "none";
447         document.getElementById("nodeContents").style.display = null;
448
449         document.getElementById("nodeContentsScrollview").textContent = focusedNode.nodeValue;
450         nodeContentsScrollArea.refresh();
451     } else if (focusedNode.nodeType == Node.ELEMENT_NODE) {
452         document.getElementById("elementAttributes").style.display = null;
453         document.getElementById("nodeContents").style.display = "none";
454
455         updateElementAttributes();
456         
457         if (focusedNode.namespaceURI.length > 0) {
458             document.getElementById("nodeNamespace").textContent = focusedNode.namespaceURI;
459             document.getElementById("nodeNamespace").title = focusedNode.namespaceURI;
460             document.getElementById("nodeNamespaceRow").style.display = null;
461         } else {
462             document.getElementById("nodeNamespaceRow").style.display = "none";
463         }
464     } else if (focusedNode.nodeType == Node.DOCUMENT_NODE) {
465         document.getElementById("nodeNamespaceRow").style.display = "none";
466         document.getElementById("elementAttributes").style.display = "none";
467         document.getElementById("nodeContents").style.display = "none";
468     }
469
470     document.getElementById("nodeType").textContent = nodeTypeName(focusedNode);
471     document.getElementById("nodeName").textContent = focusedNode.nodeName;
472
473     refreshScrollbars();
474 }
475
476 var styleRules = null;
477 var selectedStyleRuleIndex = 0;
478 var styleProperties = null;
479 var expandedStyleShorthands = [];
480
481 function updateStylePane()
482 {
483     var focusedNode = Inspector.focusedDOMNode();
484     if (focusedNode.nodeType == Node.TEXT_NODE && focusedNode.parentNode && focusedNode.parentNode.nodeType == Node.ELEMENT_NODE)
485         focusedNode = focusedNode.parentNode;
486     var rulesArea = document.getElementById("styleRulesScrollview");
487     var propertiesArea = document.getElementById("stylePropertiesTree");
488
489     rulesArea.innerHTML = "";
490     propertiesArea.innerHTML = "";
491     styleRules = [];
492     styleProperties = [];
493
494     if (focusedNode.nodeType == Node.ELEMENT_NODE) {
495         document.getElementById("styleRules").style.display = null;
496         document.getElementById("styleProperties").style.display = null;
497         document.getElementById("noStyle").style.display = "none";
498
499         var propertyCount = [];
500
501         var computedStyle = focusedNode.ownerDocument.defaultView.getComputedStyle(focusedNode);
502         if (computedStyle && computedStyle.length) {
503             var computedObj = {
504                 isComputedStyle: true,
505                 selectorText: "Computed Style",
506                 style: computedStyle,
507                 subtitle: "",
508             };
509             styleRules.push(computedObj);
510         }
511
512         var focusedNodeName = focusedNode.nodeName.toLowerCase();
513         for (var i = 0; i < focusedNode.attributes.length; i++) {
514             var attr = focusedNode.attributes[i];
515             if (attr.style) {
516                 var attrStyle = {
517                     attrName: attr.name,
518                     style: attr.style,
519                     subtitle: "element\u2019s \u201C" + attr.name + "\u201D attribute",
520                 };
521                 attrStyle.selectorText = focusedNodeName + "[" + attr.name;
522                 if (attr.value.length)
523                     attrStyle.selectorText += "=" + attr.value;
524                 attrStyle.selectorText += "]";
525                 styleRules.push(attrStyle);
526             }
527         }
528
529         var matchedStyleRules = focusedNode.ownerDocument.defaultView.getMatchedCSSRules(focusedNode, "", !showUserAgentStyles);
530         if (matchedStyleRules) {
531             for (var i = 0; i < matchedStyleRules.length; i++) {
532                 styleRules.push(matchedStyleRules[i]);
533             }
534         }
535
536         if (focusedNode.style.length) {
537             var inlineStyle = {
538                 selectorText: "Inline Style Attribute",
539                 style: focusedNode.style,
540                 subtitle: "element\u2019s \u201Cstyle\u201D attribute",
541             };
542             styleRules.push(inlineStyle);
543         }
544
545         if (styleRules.length && selectedStyleRuleIndex >= styleRules.length)
546             selectedStyleRuleIndex = (styleRules.length - 1);
547
548         var priorityUsed = false;
549         for (var i = (styleRules.length - 1); i >= 0; --i) {
550             styleProperties[i] = [];
551
552             var row = document.createElement("div");
553             row.className = "row";
554             if (i == selectedStyleRuleIndex)
555                 row.className += " focused";
556             if (styleRules[i].isComputedStyle)
557                 row.className += " computedStyle";
558
559             var cell = document.createElement("div");
560             cell.className = "cell selector";
561             var text = styleRules[i].selectorText;
562             cell.textContent = text;
563             cell.title = text;
564             row.appendChild(cell);
565
566             cell = document.createElement("div");
567             cell.className = "cell stylesheet";
568             var sheet;
569             if (styleRules[i].subtitle != null)
570                 sheet = styleRules[i].subtitle;
571             else if (styleRules[i].parentStyleSheet && styleRules[i].parentStyleSheet.href)
572                 sheet = styleRules[i].parentStyleSheet.href;
573             else if (styleRules[i].parentStyleSheet && !styleRules[i].parentStyleSheet.ownerNode)
574                 sheet = "user agent stylesheet";
575             else
576                 sheet = "inline stylesheet";
577             cell.textContent = sheet;
578             cell.title = sheet;
579             row.appendChild(cell);
580
581             row.styleRuleIndex = i;
582             row.addEventListener("click", styleRuleSelect, true);
583
584             var style = styleRules[i].style;
585             var styleShorthandLookup = [];
586             for (var j = 0; j < style.length; j++) {
587                 var prop = null;
588                 var name = style[j];
589                 var shorthand = style.getPropertyShorthand(name);
590                 if (shorthand)
591                     prop = styleShorthandLookup[shorthand];
592
593                 if (!priorityUsed && style.getPropertyPriority(name).length)
594                     priorityUsed = true;
595
596                 if (prop) {
597                     prop.subProperties.push(name);
598                 } else {
599                     prop = {
600                         style: style,
601                         subProperties: [name],
602                         unusedProperties: [],
603                         name: (shorthand ? shorthand : name),
604                     };
605                     styleProperties[i].push(prop);
606                     if (shorthand) {
607                         styleShorthandLookup[shorthand] = prop;
608                         if (!propertyCount[shorthand]) {
609                             propertyCount[shorthand] = 1;
610                         } else {
611                             prop.unusedProperties[shorthand] = true;
612                             propertyCount[shorthand]++;
613                         }
614                     }
615                 }
616
617                 if (styleRules[i].isComputedStyle)
618                     continue;
619
620                 if (!propertyCount[name]) {
621                     propertyCount[name] = 1;
622                 } else {
623                     prop.unusedProperties[name] = true;
624                     propertyCount[name]++;
625                 }
626             }
627
628             if (styleRules[i].isComputedStyle && styleRules.length > 1) {
629                 var divider = document.createElement("hr");
630                 divider.className = "divider";
631                 rulesArea.insertBefore(divider, rulesArea.firstChild);
632             }
633
634             if (rulesArea.firstChild)
635                 rulesArea.insertBefore(row, rulesArea.firstChild);
636             else
637                 rulesArea.appendChild(row);
638         }
639
640         if (priorityUsed) {
641             // walk the properties again and account for !important
642             var priorityCount = [];
643             for (var i = 0; i < styleRules.length; i++) {
644                 if (styleRules[i].isComputedStyle)
645                     continue;
646                 var style = styleRules[i].style;
647                 for (var j = 0; j < styleProperties[i].length; j++) {
648                     var prop = styleProperties[i][j];
649                     for (var k = 0; k < prop.subProperties.length; k++) {
650                         var name = prop.subProperties[k];
651                         if (style.getPropertyPriority(name).length) {
652                             if (!priorityCount[name]) {
653                                 if (prop.unusedProperties[name])
654                                     prop.unusedProperties[name] = false;
655                                 priorityCount[name] = 1;
656                             } else {
657                                 priorityCount[name]++;
658                             }
659                         } else if (priorityCount[name]) {
660                             prop.unusedProperties[name] = true;
661                         }
662                     }
663                 }
664             }
665         }
666
667         updateStyleProperties();
668     } else {
669         var noStyle = document.getElementById("noStyle");
670         noStyle.textContent = "Can't style " + nodeTypeName(focusedNode) + " nodes.";
671         document.getElementById("styleRules").style.display = "none";
672         document.getElementById("styleProperties").style.display = "none";
673         noStyle.style.display = null;
674     }
675
676     styleRulesScrollArea.refresh();
677 }
678
679 function styleRuleSelect(event)
680 {
681     var row = document.getElementById("styleRulesScrollview").firstChild;
682     while (row) {
683         if (row.nodeName == "DIV")
684             row.className = "row";
685         row = row.nextSibling;
686     }
687
688     row = event.currentTarget;
689     row.className = "row focused";
690
691     selectedStyleRuleIndex = row.styleRuleIndex;
692     updateStyleProperties();
693 }
694
695 function populateStyleListItem(li, prop, name)
696 {
697     var span = document.createElement("span");
698     span.className = "property";
699     span.textContent = name + ": ";
700     li.appendChild(span);
701
702     var value = prop.style.getPropertyValue(name);
703     if (!value)
704         return;
705
706     span = document.createElement("span");
707     span.className = "value";
708     var textValue = valueNickname[value] ? valueNickname[value] : value;
709     var priority = prop.style.getPropertyPriority(name);
710     if (priority.length)
711         textValue += " !" + priority;
712     span.textContent = textValue + ";";
713     span.title = textValue;
714     li.appendChild(span);
715
716     var colors = value.match(/(rgb\([0-9]+, [0-9]+, [0-9]+\))|(rgba\([0-9]+, [0-9]+, [0-9]+, [0-9]+\))/g);
717     if (colors) {
718         for (var k = 0; k < colors.length; k++) {
719             var swatch = document.createElement("span");
720             swatch.className = "colorSwatch";
721             swatch.style.backgroundColor = colors[k];
722             li.appendChild(swatch);
723         }
724     }
725 }
726
727 function updateStyleProperties()
728 {
729     var focusedNode = Inspector.focusedDOMNode();
730     var propertiesTree = document.getElementById("stylePropertiesTree");
731     propertiesTree.innerHTML = "";
732
733     if (selectedStyleRuleIndex >= styleProperties.length) {
734         stylePropertiesScrollArea.refresh();
735         return;
736     }
737
738     var properties = styleProperties[selectedStyleRuleIndex];
739     var omitTypicalValues = styleRules[selectedStyleRuleIndex].isComputedStyle;
740     for (var i = 0; i < properties.length; i++) {
741         var prop = properties[i];
742         var name = prop.name;
743         if (omitTypicalValues && typicalStylePropertyValue[name] == prop.style.getPropertyValue(name))
744             continue;
745
746         var mainli = document.createElement("li");
747         if (prop.subProperties.length > 1) {
748             mainli.className = "hasChildren";
749             if (expandedStyleShorthands[name])
750                 mainli.className += " expanded";
751             mainli.shorthand = name;
752             var button = document.createElement("button");
753             button.addEventListener("click", toggleStyleShorthand, false);
754             mainli.appendChild(button);
755         }
756
757         populateStyleListItem(mainli, prop, name);
758         propertiesTree.appendChild(mainli);
759
760         var overloadCount = 0;
761         if (prop.subProperties && prop.subProperties.length > 1) {
762             var subTree = document.createElement("ul");
763             if (!expandedStyleShorthands[name])
764                 subTree.style.display = "none";
765
766             for (var j = 0; j < prop.subProperties.length; j++) {
767                 var name = prop.subProperties[j];
768                 var li = document.createElement("li");
769                 if (prop.style.isPropertyImplicit(name) || prop.style.getPropertyValue(name) == "initial")
770                     li.className = "implicit";
771
772                 if (prop.unusedProperties[name] || prop.unusedProperties[name]) {
773                     li.className += " overloaded";
774                     overloadCount++;
775                 }
776
777                 populateStyleListItem(li, prop, name);
778                 subTree.appendChild(li);
779             }
780
781             propertiesTree.appendChild(subTree);
782         }
783
784         if (prop.unusedProperties[name] || overloadCount == prop.subProperties.length)
785             mainli.className += " overloaded";
786     }
787
788     stylePropertiesScrollArea.refresh();
789 }
790
791 function toggleStyleShorthand(event)
792 {
793     var li = event.currentTarget.parentNode;
794     if (li.className.indexOf("expanded") != -1) {
795         li.className = li.className.replace(/ expanded/, "");
796         li.nextSibling.style.display = "none";
797         expandedStyleShorthands[li.shorthand] = false;
798     } else {
799         li.className += " expanded";
800         li.nextSibling.style.display = null;
801         expandedStyleShorthands[li.shorthand] = true;
802     }
803
804     stylePropertiesScrollArea.refresh();
805 }
806
807 function toggleShowUserAgentStyles()
808 {
809     showUserAgentStyles = !showUserAgentStyles;
810     updateStylePane();
811 }
812
813 function selectMappedStyleRule(attrName)
814 {
815     if (!paneUpdateState["style"])
816         updateStylePane();
817
818     for (var i = 0; i < styleRules.length; i++)
819         if (styleRules[i].attrName == attrName)
820             break;
821
822     selectedStyleRuleIndex = i;
823
824     var row = document.getElementById("styleRulesScrollview").firstChild;
825     while (row) {
826         if (row.nodeName == "DIV") {
827             if (row.styleRuleIndex == selectedStyleRuleIndex)
828                 row.className = "row focused";
829             else
830                 row.className = "row";
831         }
832         row = row.nextSibling;
833     }
834
835     styleRulesScrollArea.refresh();
836
837     updateStyleProperties();
838     switchPane("style");
839 }
840
841 function setMetric(style, name, suffix)
842 {
843     var value = style.getPropertyValue(name + suffix);
844     if (value == "" || value == "0px")
845         value = "\u2012";
846     else
847         value = value.replace(/px$/, "");
848     document.getElementById(name).textContent = value;
849 }
850
851 function setBoxMetrics(style, box, suffix)
852 {
853     setMetric(style, box + "-left", suffix);
854     setMetric(style, box + "-right", suffix);
855     setMetric(style, box + "-top", suffix);
856     setMetric(style, box + "-bottom", suffix);
857 }
858
859 function updateMetricsPane()
860 {
861     var style;
862     var focusedNode = Inspector.focusedDOMNode();
863     if (focusedNode.nodeType == Node.ELEMENT_NODE)
864         style = focusedNode.ownerDocument.defaultView.getComputedStyle(focusedNode);
865     if (!style || style.length == 0) {
866         document.getElementById("noMetrics").style.display = null;
867         document.getElementById("marginBoxTable").style.display = "none";
868         return;
869     }
870
871     document.getElementById("noMetrics").style.display = "none";
872     document.getElementById("marginBoxTable").style.display = null;
873
874     setBoxMetrics(style, "margin", "");
875     setBoxMetrics(style, "border", "-width");
876     setBoxMetrics(style, "padding", "");
877
878     var size = style.getPropertyValue("width").replace(/px$/, "")
879         + " \u00D7 "
880         + style.getPropertyValue("height").replace(/px$/, "");
881     document.getElementById("content").textContent = size;
882
883     if (noMarginDisplayType[style.display] == "no")
884         document.getElementById("marginBoxTable").setAttribute("hide", "yes");
885     else
886         document.getElementById("marginBoxTable").removeAttribute("hide");
887
888     if (noPaddingDisplayType[style.display] == "no")
889         document.getElementById("paddingBoxTable").setAttribute("hide", "yes");
890     else
891         document.getElementById("paddingBoxTable").removeAttribute("hide");
892 }
893
894 function updatePropertiesPane()
895 {
896     // FIXME: Like the style pane, this should have a top item that's "all properties"
897     // and separate items for each item in the prototype chain. For now, we implement
898     // only the "all properties" part, and only for enumerable properties.
899
900     var focusedNode = Inspector.focusedDOMNode();
901     var list = document.getElementById("jsPropertiesList");
902     list.innerHTML = "";
903
904     for (var name in focusedNode) {
905         var li = document.createElement("li");
906
907         var span = document.createElement("span");
908         span.className = "property";
909         span.textContent = name + ": ";
910         li.appendChild(span);
911
912         var value = focusedNode[name];
913
914         span = document.createElement("span");
915         span.className = "value";
916         span.textContent = value;
917         span.title = value;
918         li.appendChild(span);
919
920         list.appendChild(li);
921     }
922
923     jsPropertiesScrollArea.refresh();
924 }