1 /* See license.txt for terms of usage */
  2 
  3 define([
  4     "firebug/lib/object",
  5     "firebug/firebug",
  6     "firebug/lib/domplate",
  7     "firebug/lib/locale",
  8     "firebug/lib/events",
  9     "firebug/lib/css",
 10     "firebug/lib/dom",
 11     "firebug/lib/xml",
 12     "firebug/lib/url",
 13     "firebug/lib/array",
 14     "firebug/js/sourceLink",
 15     "firebug/chrome/menu",
 16     "firebug/lib/options",
 17     "firebug/lib/string",
 18     "firebug/lib/persist",
 19     "firebug/css/cssModule",
 20     "firebug/css/cssReps"
 21 ],
 22 function(Obj, Firebug, Domplate, Locale, Events, Css, Dom, Xml, Url, Arr, SourceLink, Menu,
 23     Options, Str, Persist, CSSModule, CSSInfoTip) {
 24 
 25 with (Domplate) {
 26 
 27 //********************************************************************************************* //
 28 // Constants
 29 
 30 const Cu = Components.utils;
 31 
 32 const statusClasses = ["cssUnmatched", "cssParentMatch", "cssOverridden", "cssBestMatch"];
 33 
 34 try
 35 {
 36     Cu.import("resource:///modules/devtools/CssLogic.jsm");
 37 }
 38 catch (err)
 39 {
 40     if (FBTrace.DBG_ERRORS)
 41         FBTrace.sysout("cssComputedPanel: EXCEPTION CssLogic is not available!");
 42 }
 43 
 44 // ********************************************************************************************* //
 45 // CSS Computed panel (HTML side panel)
 46 
 47 function CSSComputedPanel() {}
 48 
 49 CSSComputedPanel.prototype = Obj.extend(Firebug.Panel,
 50 {
 51     template: domplate(
 52     {
 53         computedStylesTag:
 54             DIV({"class": "a11yCSSView", role: "list", "aria-label":
 55                 Locale.$STR("aria.labels.computed styles")}),
 56 
 57         groupedStylesTag:
 58             FOR("group", "$groups",
 59                 DIV({"class": "computedStylesGroup", $opened: "$group.opened", role: "list",
 60                         $hidden: "$group.props|hasNoStyles", _repObject: "$group"},
 61                     H1({"class": "cssComputedHeader groupHeader focusRow", role: "listitem"},
 62                         DIV({"class": "twisty", role: "presentation"}),
 63                         SPAN({"class": "cssComputedLabel"}, "$group.title")
 64                     ),
 65                     TAG("$stylesTag", {props: "$group.props"})
 66                 )
 67             ),
 68 
 69         stylesTag:
 70             TABLE({"class": "computedStyleTable", role: "list"},
 71                 TBODY({role: "presentation"},
 72                     FOR("prop", "$props",
 73                         TR({"class": "focusRow computedStyleRow computedStyle",
 74                                 $opened: "$prop.opened", role: "listitem",
 75                                 $hasSelectors: "$prop|hasSelectors", _repObject: "$prop"},
 76                             TD({"class": "stylePropName", role: "presentation"},
 77                                 "$prop.property"
 78                             ),
 79                             TD({role: "presentation"},
 80                                 SPAN({"class": "stylePropValue"}, "$prop.value|formatValue"))
 81                         ),
 82                         TR({"class": "focusRow computedStyleRow matchedSelectors", _repObject: "$prop"},
 83                             TD({colspan: 2},
 84                                 TAG("$selectorsTag", {prop: "$prop"})
 85                             )
 86                         )
 87                     )
 88                 )
 89             ),
 90 
 91         selectorsTag:
 92             TABLE({"class": "matchedSelectorsTable", role: "list"},
 93                 TBODY({role: "presentation"},
 94                     FOR("selector", "$prop.matchedSelectors",
 95                         TR({"class": "focusRow computedStyleRow styleSelector "+
 96                             "$selector.status|getStatusClass", role: "listitem",
 97                                 _repObject: "$selector"},
 98                             TD({"class": "selectorName", role: "presentation"},
 99                                 "$selector.selector.text"),
100                             TD({"class": "propValue", role: "presentation"},
101                                 SPAN({"class": "stylePropValue"}, "$selector.value|formatValue")),
102                             TD({"class": "styleSourceLink", role: "presentation"},
103                                 TAG(FirebugReps.SourceLink.tag, {object: "$selector|getSourceLink"})
104                             )
105                         )
106                     )
107                 )
108             ),
109 
110         getStatusClass: function(status)
111         {
112             return statusClasses[status];
113         },
114 
115         hasNoStyles: function(props)
116         {
117             return props.length == 0;
118         },
119 
120         hasSelectors: function(prop)
121         {
122             return prop.matchedRuleCount != 0;
123         },
124 
125         getSourceLink: function(selector)
126         {
127             var href = selector.href.href || selector.href;
128             var line = selector.ruleLine;
129             var rule = selector.selector._cssRule._domRule;
130 
131             var instance = Css.getInstanceForStyleSheet(rule.parentStyleSheet);
132             var sourceLink = line != -1 ? new SourceLink.SourceLink(href, line, "css",
133                 rule, instance) : null;
134 
135             return sourceLink;
136         },
137 
138         formatValue: function(value)
139         {
140             if (Options.get("colorDisplay") == "hex")
141                 value = Css.rgbToHex(value);
142             else if (Options.get("colorDisplay") == "hsl")
143                 value = Css.rgbToHSL(value);
144 
145             var limit = Options.get("stringCropLength");
146             if (limit > 0)
147                 value = Str.cropString(value, limit);
148 
149             // Add a zero-width space after a comma to allow line breaking
150             return value.replace(/,/g, ",\u200B");
151         }
152     }),
153 
154     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
155 
156     updateComputedView: function(element)
157     {
158         // The current selection can be null.
159         if (!element)
160             return;
161 
162         var doc = element.ownerDocument;
163         var win = doc.defaultView;
164 
165         // Update now if the document is loaded, otherwise wait for "load" event.
166         if (doc.readyState == "complete")
167             return this.doUpdateComputedView(element);
168 
169         if (this.updateInProgress)
170             return;
171 
172         var self = this;
173         var onWindowLoadHandler = function()
174         {
175             self.context.removeEventListener(win, "load", onWindowLoadHandler, true);
176             self.updateInProgress = false;
177             self.doUpdateComputedView(element);
178         }
179 
180         this.context.addEventListener(win, "load", onWindowLoadHandler, true);
181         this.updateInProgress = true;
182     },
183 
184     doUpdateComputedView: function(element)
185     {
186         function isUnwantedProp(propName)
187         {
188             return !Firebug.showMozillaSpecificStyles && Str.hasPrefix(propName, "-moz")
189         }
190 
191         var win = element.ownerDocument.defaultView;
192         var computedStyle = win.getComputedStyle(element);
193 
194         try
195         {
196             if (this.cssLogic)
197                 this.cssLogic.highlight(element);
198         }
199         catch (e)
200         {
201             // An exception is thrown if the document is not fully loaded yet
202             // The cssLogic API needs to be used after "load" has been fired.
203             if (FBTrace.DBG_ERRORS)
204                 FBTrace.sysout("computedPanel.doUpdateComputedView; EXCEPTION " + e, e);
205         }
206 
207         var props = [];
208         for (var i = 0; i < computedStyle.length; ++i)
209         {
210             var prop = this.cssLogic ? this.cssLogic.getPropertyInfo(computedStyle[i]) :
211                 Firebug.CSSModule.getPropertyInfo(computedStyle, computedStyle[i]);
212 
213             if (isUnwantedProp(prop.property) ||
214                 (this.cssLogic && !Firebug.showUserAgentCSS && prop.matchedRuleCount == 0))
215             {
216                 continue;
217             }
218 
219             props.push(prop);
220         }
221 
222         var parentNode = this.template.computedStylesTag.replace({}, this.panelNode);
223 
224         if (props.length != 0)
225         {
226             if (Firebug.computedStylesDisplay == "alphabetical")
227             {
228                 this.sortProperties(props);
229 
230                 for (var i = 0; i < props.length; ++i)
231                     props[i].opened = this.styleOpened[props[i].property];
232 
233                 var result = this.template.stylesTag.replace({props: props}, parentNode);
234             }
235             else
236             {
237                 var groups = [];
238                 for (var groupName in styleGroups)
239                 {
240                     var title = Locale.$STR("StyleGroup-" + groupName);
241                     var group = {name: groupName, title: title, props: []};
242 
243                     var groupProps = styleGroups[groupName];
244                     for (var i = 0; i < groupProps.length; ++i)
245                     {
246                         var propName = groupProps[i];
247                         if (isUnwantedProp(propName))
248                             continue;
249 
250                         var prop = this.cssLogic ? this.cssLogic.getPropertyInfo(propName) :
251                             Firebug.CSSModule.getPropertyInfo(computedStyle, propName);
252 
253                         if (!Firebug.showUserAgentCSS && prop.matchedRuleCount == 0)
254                             continue;
255 
256                         prop.opened = this.styleOpened[propName];
257 
258                         group.props.push(prop);
259 
260                         for (var j = 0; j < props.length; ++j)
261                         {
262                             if (props[j].property == propName)
263                             {
264                                 props.splice(j, 1);
265                                 break;
266                             }
267                         }
268                     }
269 
270                     group.opened = this.groupOpened[groupName];
271 
272                     groups.push(group);
273                 }
274 
275                 if (props.length > 0)
276                 {
277                     var group = groups[groups.length-1];
278                     for (var i = 0; i < props.length; ++i)
279                     {
280                         var propName = props[i].property;
281                         if (isUnwantedProp(propName))
282                             continue;
283 
284                         var prop = this.cssLogic ? this.cssLogic.getPropertyInfo(propName) :
285                             Firebug.CSSModule.getPropertyInfo(computedStyle, propName);
286 
287                         prop.opened = this.styleOpened[propName];
288 
289                         group.props.push(prop);
290                     }
291 
292                     group.opened = this.groupOpened[group.name];
293                 }
294 
295                 var result = this.template.groupedStylesTag.replace({groups: groups}, parentNode);
296             }
297         }
298         else
299         {
300             FirebugReps.Warning.tag.replace({object: "computed.No_User-Defined_Styles"},
301                 this.panelNode);
302         }
303 
304         if (this.scrollTop)
305         {
306             this.panelNode.scrollTop = this.scrollTop;
307             delete this.scrollTop;
308         }
309 
310         Events.dispatch(this.fbListeners, "onCSSRulesAdded", [this, result]);
311     },
312 
313     toggleGroup: function(node)
314     {
315         var groupNode = Dom.getAncestorByClass(node, "computedStylesGroup");
316         var group = Firebug.getRepObject(groupNode);
317 
318         Css.toggleClass(groupNode, "opened");
319         var opened = Css.hasClass(groupNode, "opened");
320         this.groupOpened[group.name] = opened;
321 
322         if (opened)
323         {
324             var offset = Dom.getClientOffset(node);
325             var titleAtTop = offset.y < this.panelNode.scrollTop;
326 
327             Dom.scrollTo(groupNode, this.panelNode, null,
328                 groupNode.offsetHeight > this.panelNode.clientHeight || titleAtTop ? "top" : "bottom");
329         }
330     },
331 
332     toggleAllStyles: function(event, expand)
333     {
334         var computedStyles = this.panelNode.getElementsByClassName("computedStyle");
335 
336         for (var i = 0; i < computedStyles.length; ++i)
337         {
338             if (!Css.hasClass(computedStyles[i], "hasSelectors"))
339                 continue;
340 
341             var isOpened = Css.hasClass(computedStyles[i], "opened");
342             if ((expand && !isOpened) || (!expand && isOpened))
343                 this.toggleStyle(computedStyles[i], false);
344         }
345     },
346 
347     toggleStyle: function(node, scroll)
348     {
349         var styleNode = Dom.getAncestorByClass(node, "computedStyle");
350         var style = Firebug.getRepObject(styleNode);
351 
352         Css.toggleClass(styleNode, "opened");
353         var opened = Css.hasClass(styleNode, "opened");
354         this.styleOpened[style.property] = Css.hasClass(styleNode, "opened");
355 
356         if (opened && scroll)
357         {
358             var selectorsNode = styleNode.nextSibling;
359             var offset = Dom.getClientOffset(styleNode);
360             var titleAtTop = offset.y < this.panelNode.scrollTop;
361             var totalHeight = styleNode.offsetHeight + selectorsNode.offsetHeight;
362             var alignAtTop = totalHeight > this.panelNode.clientHeight || titleAtTop;
363 
364             Dom.scrollTo(alignAtTop ? styleNode : selectorsNode, this.panelNode, null,
365                 alignAtTop ? "top" : "bottom", alignAtTop);
366         }
367     },
368 
369     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
370     // Events
371 
372     onClick: function(event)
373     {
374         if (!Events.isLeftClick(event))
375             return;
376 
377         var cssComputedHeader = Dom.getAncestorByClass(event.target, "cssComputedHeader");
378         if (cssComputedHeader)
379         {
380             this.toggleGroup(event.target);
381             return;
382         }
383 
384         var computedStyle = Dom.getAncestorByClass(event.target, "computedStyle");
385         if (computedStyle && Css.hasClass(computedStyle, "hasSelectors"))
386         {
387             this.toggleStyle(event.target, true);
388             return;
389         }
390     },
391 
392     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
393     // extends Panel
394 
395     name: "computed",
396     parentPanel: "html",
397     order: 1,
398 
399     initialize: function()
400     {
401         if (typeof CssLogic != "undefined")
402             this.cssLogic = new CssLogic();
403 
404         this.groupOpened = [];
405         for (var groupName in styleGroups)
406             this.groupOpened[groupName] = true;
407 
408         this.styleOpened = [];
409 
410         // Listen for CSS changes so the Computed panel is properly updated when needed.
411         Firebug.CSSModule.addListener(this);
412 
413         this.onClick = Obj.bind(this.onClick, this);
414 
415         Firebug.Panel.initialize.apply(this, arguments);
416     },
417 
418     destroy: function(state)
419     {
420         state.scrollTop = this.panelNode.scrollTop ? this.panelNode.scrollTop : this.lastScrollTop;
421         state.groupOpened = this.groupOpened;
422         state.styleOpened = this.styleOpened;
423 
424         Persist.persistObjects(this, state);
425 
426         Firebug.CSSModule.removeListener(this);
427 
428         Firebug.Panel.destroyNode.apply(this, arguments);
429     },
430 
431     initializeNode: function(oldPanelNode)
432     {
433         Events.addEventListener(this.panelNode, "click", this.onClick, false);
434 
435         Firebug.Panel.initializeNode.apply(this, arguments);
436     },
437 
438     destroyNode: function()
439     {
440         Events.removeEventListener(this.panelNode, "click", this.onClick, false);
441 
442         Firebug.Panel.destroyNode.apply(this, arguments);
443     },
444 
445     show: function(state)
446     {
447         // Wait for loadedContext to restore the panel
448         if (this.context.loaded)
449         {
450             Persist.restoreObjects(this, state);
451 
452             if (state)
453             {
454                 if (state.scrollTop)
455                     this.scrollTop = state.scrollTop;
456 
457                 if (state.groupOpened)
458                     this.groupOpened = state.groupOpened;
459 
460                 if (state.styleOpened)
461                     this.styleOpened = state.styleOpened;
462             }
463         }
464 
465         if (this.selection)
466             this.refresh();
467     },
468 
469     hide: function()
470     {
471         this.lastScrollTop = this.panelNode.scrollTop;
472     },
473 
474     updateView: function(element)
475     {
476         this.updateComputedView(element);
477     },
478 
479     supportsObject: function(object, type)
480     {
481         return object instanceof window.Element ? 1 : 0;
482     },
483 
484     refresh: function()
485     {
486         this.updateSelection(this.selection);
487     },
488 
489     updateSelection: function(element)
490     {
491         this.updateComputedView(element);
492     },
493 
494     updateOption: function(name, value)
495     {
496         var options = new Set();
497         options.add("showUserAgentCSS");
498         options.add("computedStylesDisplay");
499         options.add("colorDisplay");
500         options.add("showMozillaSpecificStyles");
501 
502         if (options.has(name))
503             this.refresh();
504     },
505 
506     getOptionsMenuItems: function()
507     {
508         var items = [];
509 
510         if (this.cssLogic)
511         {
512             items.push(
513                 Menu.optionMenu("Show_User_Agent_CSS", "showUserAgentCSS",
514                 "style.option.tip.Show_User_Agent_CSS")
515             );
516         }
517 
518         items.push(
519             {
520                 label: "Sort_alphabetically",
521                 type: "checkbox",
522                 checked: Firebug.computedStylesDisplay == "alphabetical",
523                 tooltiptext: "computed.option.tip.Sort_Alphabetically",
524                 command: Obj.bind(this.toggleDisplay, this)
525             },
526             Menu.optionMenu("Show_Mozilla_specific_styles",
527                 "showMozillaSpecificStyles",
528                 "computed.option.tip.Show_Mozilla_Specific_Styles")
529         );
530 
531         items = Arr.extendArray(items, CSSModule.getColorDisplayOptionMenuItems());
532 
533         return items;
534     },
535 
536     getContextMenuItems: function(style, target)
537     {
538         var items = [];
539         var computedStyles = this.panelNode.getElementsByClassName("computedStyle");
540         var expandAll = false;
541         var collapseAll = false;
542         for (var i = 0; i < computedStyles.length; ++i)
543         {
544             if (!Css.hasClass(computedStyles[i], "hasSelectors"))
545                 continue;
546 
547             if (!expandAll && !Css.hasClass(computedStyles[i], "opened"))
548                 expandAll = true;
549             if (!collapseAll && Css.hasClass(computedStyles[i], "opened"))
550                 collapseAll = true;
551         }
552 
553         if (expandAll)
554         {
555             items.push(
556                 {
557                     label: "computed.option.label.Expand_All_Styles",
558                     command: Obj.bind(this.toggleAllStyles, this, true),
559                     tooltiptext: "computed.option.tip.Expand_All_Styles"
560                 }
561             );
562         }
563 
564         if (collapseAll)
565         {
566             items.push(
567                 {
568                     label: "computed.option.label.Collapse_All_Styles",
569                     command: Obj.bind(this.toggleAllStyles, this, false),
570                     tooltiptext: "computed.option.tip.Collapse_All_Styles"
571                 }
572             );
573         }
574 
575         return items;
576     },
577 
578     onMouseDown: function(event)
579     {
580         if (!Events.isLeftClick(event))
581             return;
582 
583         var cssComputedHeader = Dom.getAncestorByClass(event.target, "cssComputedHeader");
584         if (cssComputedHeader)
585             this.toggleNode(event);
586     },
587 
588     toggleNode: function(event)
589     {
590         var group = Dom.getAncestorByClass(event.target, "computedStylesGroup");
591         var groupName = group.getElementsByClassName("cssComputedLabel")[0].textContent;
592 
593         Css.toggleClass(group, "opened");
594         this.groupOpened[groupName] = Css.hasClass(group, "opened");
595     },
596 
597     toggleDisplay: function()
598     {
599         var display = Firebug.computedStylesDisplay == "alphabetical" ? "grouped" : "alphabetical";
600         Options.set("computedStylesDisplay", display);
601     },
602 
603     sortProperties: function(props)
604     {
605         props.sort(function(a, b)
606         {
607             return a.property > b.property ? 1 : -1;
608         });
609     },
610 
611     getStylesheetURL: function(rule, getBaseUri)
612     {
613         // If parentStyleSheet.href is null, then per the CSS standard this is an inline style.
614         if (rule && rule.parentStyleSheet && rule.parentStyleSheet.href)
615             return rule.parentStyleSheet.href;
616         else if (getBaseUri)
617             return this.selection.ownerDocument.baseURI;
618         else
619             return this.selection.ownerDocument.location.href;
620     },
621 
622     showInfoTip: function(infoTip, target, x, y, rangeParent, rangeOffset)
623     {
624         var propValue = Dom.getAncestorByClass(target, "stylePropValue");
625         if (propValue)
626         {
627             var propInfo = Firebug.getRepObject(target);
628 
629             var prop = propInfo.property, value = propInfo.value;
630             var cssValue;
631 
632             if (prop == "font" || prop == "font-family")
633             {
634                 if (value.charAt(rangeOffset) == ",")
635                     return;
636 
637                 cssValue = Firebug.CSSModule.parseCSSFontFamilyValue(value, rangeOffset, prop);
638             }
639             else
640             {
641                 cssValue = Firebug.CSSModule.parseCSSValue(value, rangeOffset);
642             }
643 
644             if (!cssValue)
645                 return false;
646 
647             if (cssValue.value == this.infoTipValue)
648                 return true;
649 
650             this.infoTipValue = cssValue.value;
651 
652             switch (cssValue.type)
653             {
654                 case "rgb":
655                 case "hsl":
656                 case "gradient":
657                 case "colorKeyword":
658                     this.infoTipType = "color";
659                     this.infoTipObject = cssValue.value;
660                     return CSSInfoTip.populateColorInfoTip(infoTip, cssValue.value);
661 
662                 case "url":
663                     if (Css.isImageRule(Xml.getElementSimpleType(propInfo), prop))
664                     {
665                         var baseURL = typeof propInfo.href == "object" ? propInfo.href.href : propInfo.href;
666                         if (!baseURL)
667                             baseURL = propInfo.matchedSelectors[0].href;
668                         var relURL = Firebug.CSSModule.parseURLValue(cssValue.value);
669                         var absURL = Url.isDataURL(relURL) ? relURL : Url.absoluteURL(relURL, baseURL);
670                         var repeat = Firebug.CSSModule.parseRepeatValue(value);
671 
672                         this.infoTipType = "image";
673                         this.infoTipObject = absURL;
674 
675                         return CSSInfoTip.populateImageInfoTip(infoTip, absURL, repeat);
676                     }
677                     break;
678 
679                 case "fontFamily":
680                     return CSSInfoTip.populateFontFamilyInfoTip(infoTip, cssValue.value);
681             }
682 
683             delete this.infoTipType;
684             delete this.infoTipValue;
685             delete this.infoTipObject;
686 
687             return false;
688         }
689     },
690 
691     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
692     // Change Listener
693 
694     onCSSInsertRule: function(styleSheet, cssText, ruleIndex)
695     {
696         // Force update, this causes updateSelection to be called.
697         // See {@link Firebug.Panel.select}
698         this.selection = null;
699     },
700 
701     onCSSDeleteRule: function(styleSheet, ruleIndex)
702     {
703         this.selection = null;
704     },
705 
706     onCSSSetProperty: function(style, propName, propValue, propPriority, prevValue,
707         prevPriority, rule, baseText)
708     {
709         this.selection = null;
710     },
711 
712     onCSSRemoveProperty: function(style, propName, prevValue, prevPriority, rule, baseText)
713     {
714         this.selection = null;
715     }
716 });
717 
718 //********************************************************************************************* //
719 //Helpers
720 
721 const styleGroups =
722 {
723     text: [
724         "font-family",
725         "font-size",
726         "font-weight",
727         "font-style",
728         "font-size-adjust",
729         "color",
730         "text-transform",
731         "text-decoration",
732         "letter-spacing",
733         "word-spacing",
734         "line-height",
735         "text-align",
736         "vertical-align",
737         "direction",
738         "column-count",
739         "column-gap",
740         "column-width",
741         "-moz-tab-size", // FF4.0
742         "-moz-font-feature-settings", // FF4.0
743         "-moz-font-language-override", // FF4.0
744         "-moz-text-blink", // FF6.0
745         "-moz-text-decoration-color", // FF6.0
746         "-moz-text-decoration-line", // FF6.0
747         "-moz-text-decoration-style", // FF6.0
748         "hyphens", // FF 6.0
749         "text-overflow" // FF7.0
750     ],
751 
752     background: [
753         "background-color",
754         "background-image",
755         "background-repeat",
756         "background-position",
757         "background-attachment",
758         "opacity",
759         "background-clip",
760         "-moz-background-inline-policy",
761         "background-origin",
762         "background-size",
763         "-moz-image-region"
764     ],
765 
766     box: [
767         "width",
768         "height",
769         "top",
770         "right",
771         "bottom",
772         "left",
773         "margin-top",
774         "margin-right",
775         "margin-bottom",
776         "margin-left",
777         "padding-top",
778         "padding-right",
779         "padding-bottom",
780         "padding-left",
781         "-moz-padding-start",
782         "-moz-padding-end",
783         "border-top-width",
784         "border-right-width",
785         "border-bottom-width",
786         "border-left-width",
787         "border-top-color",
788         "-moz-border-top-colors",
789         "border-right-color",
790         "-moz-border-right-colors",
791         "border-bottom-color",
792         "-moz-border-bottom-colors",
793         "border-left-color",
794         "-moz-border-left-colors",
795         "border-top-style",
796         "border-right-style",
797         "border-bottom-style",
798         "border-left-style",
799         "-moz-border-end",
800         "-moz-border-end-color",
801         "-moz-border-end-style",
802         "-moz-border-end-width",
803         "-moz-border-image",
804         "-moz-border-start",
805         "-moz-border-start-color",
806         "-moz-border-start-style",
807         "-moz-border-start-width",
808         "border-top-left-radius",
809         "border-top-right-radius",
810         "border-bottom-left-radius",
811         "border-bottom-right-radius",
812         "-moz-outline-radius-bottomleft",
813         "-moz-outline-radius-bottomright",
814         "-moz-outline-radius-topleft",
815         "-moz-outline-radius-topright",
816         "box-shadow",
817         "outline-color",
818         "outline-offset",
819         "outline-top-width",
820         "outline-right-width",
821         "outline-bottom-width",
822         "outline-left-width",
823         "outline-top-color",
824         "outline-right-color",
825         "outline-bottom-color",
826         "outline-left-color",
827         "outline-top-style",
828         "outline-right-style",
829         "outline-bottom-style",
830         "outline-left-style",
831         "-moz-box-align",
832         "-moz-box-direction",
833         "-moz-box-flex",
834         "-moz-box-ordinal-group",
835         "-moz-box-orient",
836         "-moz-box-pack",
837         "-moz-box-sizing",
838         "-moz-margin-start",
839         "-moz-margin-end"
840     ],
841 
842     layout: [
843         "position",
844         "display",
845         "visibility",
846         "z-index",
847         "overflow-x",  // http://www.w3.org/TR/2002/WD-css3-box-20021024/#overflow
848         "overflow-y",
849         "overflow-clip",
850         "transform",
851         "transform-origin",
852         "white-space",
853         "clip",
854         "float",
855         "clear",
856         "-moz-appearance",
857         "-moz-stack-sizing",
858         "-moz-column-count",
859         "-moz-column-gap",
860         "-moz-column-width",
861         "-moz-column-rule",
862         "-moz-column-rule-width",
863         "-moz-column-rule-style",
864         "-moz-column-rule-color",
865         "-moz-float-edge",
866         "orient"
867     ],
868 
869     other: []
870 };
871 
872 //********************************************************************************************* //
873 //Registration
874 
875 Firebug.registerPanel(CSSComputedPanel);
876 
877 return CSSComputedPanel;
878 
879 //********************************************************************************************* //
880 }});
881