1 /* See license.txt for terms of usage */
  2 
  3 var FirebugReps = FBL.ns(function() { with (FBL) {
  4 
  5 // ************************************************************************************************
  6 // Constants
  7 
  8 const Cc = Components.classes;
  9 const Ci = Components.interfaces;
 10 const jsdIStackFrame = Ci.jsdIStackFrame;
 11 const jsdIScript = Ci.jsdIScript;
 12 
 13 const fbs = Cc["@joehewitt.com/firebug;1"].getService().wrappedJSObject;
 14 
 15 // ************************************************************************************************
 16 // Common Tags
 17 
 18 var OBJECTBOX = this.OBJECTBOX =
 19     SPAN({"class": "objectBox objectBox-$className", role : "presentation"});
 20 
 21 var OBJECTBLOCK = this.OBJECTBLOCK =
 22     DIV({"class": "objectBox objectBox-$className focusRow subLogRow", role : "listitem"});
 23 
 24 var OBJECTLINK = this.OBJECTLINK =
 25     A({
 26         "class": "objectLink objectLink-$className a11yFocus",
 27         _repObject: "$object"
 28     });
 29 
 30 // ************************************************************************************************
 31 
 32 this.Undefined = domplate(Firebug.Rep,
 33 {
 34     tag: OBJECTBOX("undefined"),
 35 
 36     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 37 
 38     className: "undefined",
 39 
 40     supportsObject: function(object, type)
 41     {
 42         return type == "undefined";
 43     }
 44 });
 45 
 46 // ************************************************************************************************
 47 
 48 this.Null = domplate(Firebug.Rep,
 49 {
 50     tag: OBJECTBOX("null"),
 51 
 52     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 53 
 54     className: "null",
 55 
 56     supportsObject: function(object, type)
 57     {
 58         return object == null;
 59     }
 60 });
 61 
 62 // ************************************************************************************************
 63 
 64 this.Nada = domplate(Firebug.Rep,
 65 {
 66     tag: SPAN(""),
 67 
 68     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 69 
 70     className: "nada"
 71 });
 72 
 73 // ************************************************************************************************
 74 
 75 this.Number = domplate(Firebug.Rep,
 76 {
 77     tag: OBJECTBOX("$object"),
 78 
 79     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 80 
 81     className: "number",
 82 
 83     supportsObject: function(object, type)
 84     {
 85         return type == "boolean" || type == "number";
 86     }
 87 });
 88 
 89 // ************************************************************************************************
 90 
 91 this.String = domplate(Firebug.Rep,
 92 {
 93     tag: OBJECTBOX(""$object""),
 94 
 95     shortTag: OBJECTBOX(""$object|cropMultipleLines""),
 96 
 97     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 98 
 99     className: "string",
100 
101     supportsObject: function(object, type)
102     {
103         return type == "string";
104     }
105 });
106 
107 // ************************************************************************************************
108 
109 this.XML = domplate(Firebug.Rep,
110 {
111     tag: OBJECTBOX("$object|asString"),
112 
113     shortTag: OBJECTBOX("$object|asShortString"),
114 
115     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
116 
117     className: "xml",
118 
119     supportsObject: function(object, type)
120     {
121         return type == "xml";
122     },
123 
124     asString: function(object)
125     {
126         return object.toXMLString();
127     },
128 
129     asShortString: function(object)
130     {
131         return cropMultipleLines(this.asString(object));
132     },
133 });
134 
135 // ************************************************************************************************
136 
137 this.Text = domplate(Firebug.Rep,
138 {
139     tag: OBJECTBOX("$object"),
140 
141     shortTag: OBJECTBOX("$object|cropMultipleLines"),
142 
143     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
144 
145     className: "text"
146 });
147 
148 // ************************************************************************************************
149 
150 this.Caption = domplate(Firebug.Rep,
151 {
152     tag: SPAN({"class": "caption"}, "$object")
153 });
154 
155 // ************************************************************************************************
156 
157 this.Warning = domplate(Firebug.Rep,
158 {
159     tag: DIV({"class": "warning focusRow", role : 'listitem'}, "$object|STR")
160 });
161 
162 // ************************************************************************************************
163 
164 this.Func = domplate(Firebug.Rep,
165 {
166     tag:
167         OBJECTLINK("$object|summarizeFunction"),
168 
169     summarizeFunction: function(fn)
170     {
171         var fnRegex = /function ([^(]+\([^)]*\)) \{/;
172         var fnText = safeToString(fn);
173 
174         var m = fnRegex.exec(fnText);
175         return m ? m[1] : "function()";
176     },
177 
178     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
179 
180     copySource: function(fn)
181     {
182         copyToClipboard(safeToString(fn));
183     },
184 
185     monitor: function(fn, script, monitored)
186     {
187         if (monitored)
188             Firebug.Debugger.unmonitorScript(fn, script, "monitor");
189         else
190             Firebug.Debugger.monitorScript(fn, script, "monitor");
191     },
192 
193     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
194 
195     className: "function",
196 
197     supportsObject: function(object, type)
198     {
199         return type == "function";
200     },
201 
202     inspectObject: function(fn, context)
203     {
204         var sourceLink = findSourceForFunction(fn, context);
205         if (sourceLink)
206             Firebug.chrome.select(sourceLink);
207         if (FBTrace.DBG_FUNCTION_NAME)
208             FBTrace.sysout("reps.function.inspectObject selected sourceLink is ", sourceLink);
209     },
210 
211     getTooltip: function(fn, context)
212     {
213         /*  XXjjb I think this is very expensive...
214         var script = findScriptForFunctionInContext(context, fn);
215         if (script)
216             return $STRF("Line", [normalizeURL(script.fileName), script.baseLineNumber]);
217         else
218          */
219             if (fn.toString)
220                 return fn.toString();
221     },
222 
223     getTitle: function(fn, context)
224     {
225         var name = fn.name ? fn.name : "function";
226         return name + "()";
227     },
228 
229     getContextMenuItems: function(fn, target, context, script)
230     {
231         if (!script)
232             script = findScriptForFunctionInContext(context, fn);
233         if (!script)
234             return;
235 
236         var scriptInfo = Firebug.SourceFile.getSourceFileAndLineByScript(context, script);
237         var monitored = scriptInfo ? fbs.isMonitored(scriptInfo.sourceFile.href, scriptInfo.lineNo) : false;
238 
239         var name = script ? getFunctionName(script, context) : fn.name;
240         return [
241             {label: "CopySource", command: bindFixed(this.copySource, this, fn) },
242             "-",
243             {label: $STRF("ShowCallsInConsole", [name]), nol10n: true,
244              type: "checkbox", checked: monitored,
245              command: bindFixed(this.monitor, this, fn, script, monitored) }
246         ];
247     }
248 });
249 
250 // ************************************************************************************************
251 
252 this.jsdScript = domplate(Firebug.Rep,
253 {
254     copySource: function(script)
255     {
256         var fn = unwrapIValue(script.functionObject);
257         return FirebugReps.Func.copySource(fn);
258     },
259 
260     monitor: function(fn, script, monitored)
261     {
262         fn = unwrapIValue(script.functionObject);
263         return FirebugReps.Func.monitor(fn, script, monitored);
264     },
265 
266     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
267 
268     className: "jsdScript",
269     inspectable: false,
270 
271     supportsObject: function(object, type)
272     {
273         return object instanceof jsdIScript;
274     },
275 
276     inspectObject: function(script, context)
277     {
278         var sourceLink = getSourceLinkForScript(script, context);
279         if (sourceLink)
280             Firebug.chrome.select(sourceLink);
281     },
282 
283     getRealObject: function(script, context)
284     {
285         return script;
286     },
287 
288     getTooltip: function(script)
289     {
290         return $STRF("jsdIScript", [script.tag]);
291     },
292 
293     getTitle: function(script, context)
294     {
295         var fn = unwrapIValue(script.functionObject);
296         return FirebugReps.Func.getTitle(fn, context);
297     },
298 
299     getContextMenuItems: function(script, target, context)
300     {
301         var fn = unwrapIValue(script.functionObject);
302 
303         var scriptInfo = Firebug.SourceFile.getSourceFileAndLineByScript(context, script);
304            var monitored = scriptInfo ? fbs.isMonitored(scriptInfo.sourceFile.href, scriptInfo.lineNo) : false;
305 
306         var name = getFunctionName(script, context);
307 
308         return [
309             {label: "CopySource", command: bindFixed(this.copySource, this, script) },
310             "-",
311             {label: $STRF("ShowCallsInConsole", [name]), nol10n: true,
312              type: "checkbox", checked: monitored,
313              command: bindFixed(this.monitor, this, fn, script, monitored) }
314         ];
315     }
316 });
317 
318 //************************************************************************************************
319 
320 this.Obj = domplate(Firebug.Rep,
321 {
322     tag:
323         OBJECTLINK(
324             SPAN({"class": "objectTitle"}, "$object|getTitle "),
325             SPAN({"class": "objectLeftBrace", role: "presentation"}, "{"),
326             FOR("prop", "$object|shortPropIterator",
327                 " $prop.name",
328                 SPAN({"class": "objectEqual", role: "presentation"}, "$prop.equal"),
329                 TAG("$prop.tag", {object: "$prop.object"}),
330                 SPAN({"class": "objectComma", role: "presentation"}, "$prop.delim")
331             ),
332             SPAN({"class": "objectRightBrace"}, "}")
333         ),
334 
335     shortTag:
336         OBJECTLINK(
337             SPAN({"class": "objectTitle"}, "$object|getTitle "),
338             SPAN({"class": "objectLeftBrace", role: "presentation"}, "{"),
339             FOR("prop", "$object|shortPropIterator",
340                 " $prop.name",
341                 SPAN({"class": "objectEqual", role: "presentation"}, "$prop.equal"),
342                 TAG("$prop.tag", {object: "$prop.object"}),
343                 SPAN({"class": "objectComma", role: "presentation"}, "$prop.delim")
344             ),
345             SPAN({"class": "objectRightBrace"}, "}")
346         ),
347 
348     titleTag:
349         SPAN({"class": "objectTitle"}, "$object|getTitle"),
350 
351     longPropIterator: function (object)
352     {
353         return this.propIterator(object,100);
354     },
355 
356     shortPropIterator: function (object)
357     {
358         return this.propIterator(object,1);
359     },
360 
361     propIterator: function (object, max)
362     {
363         max = max || 3;
364         if (!object)
365             return [];
366 
367         var props = [];
368         var len = 0, count = 0;
369 
370         try
371         {
372             for (var name in object)
373             {
374                 var value;
375                 try
376                 {
377                     value = object[name];
378                 }
379                 catch (exc)
380                 {
381                     continue;
382                 }
383 
384                 var t = typeof(value);
385                 if (t == "boolean" || t == "number" || (t == "string" && value)
386                     || (t == "object" && value && value.toString))
387                 {
388                     var rep = Firebug.getRep(value);
389                     var tag = rep.shortTag || rep.tag;
390                     if (t == "object")
391                     {
392                         value = rep.getTitle(value);
393                         tag = rep.titleTag;
394                     }
395                     count++;
396                     if (count <= max)
397                         props.push({tag: tag, name: name, object: value, equal: "=", delim: ", "});
398                     else
399                         break;
400                 }
401             }
402             if (count > max)
403             {
404                 props[Math.max(1,max-1)] = {
405                     object: "more...", //xxxHonza localization
406                     tag: FirebugReps.Caption.tag,
407                     name: "",
408                     equal:"",
409                     delim:""
410                 };
411             }
412             else if (props.length > 0)
413             {
414                 props[props.length-1].delim = '';
415             }
416         }
417         catch (exc)
418         {
419             // Sometimes we get exceptions when trying to read from certain objects, like
420             // StorageList, but don't let that gum up the works
421             // XXXjjb also History.previous fails because object is a web-page object which does not have
422             // permission to read the history
423         }
424         return props;
425     },
426 
427     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
428 
429     className: "object",
430 
431     supportsObject: function(object, type)
432     {
433         return true;
434     }
435 });
436 
437 // ************************************************************************************************
438 
439 this.Arr = domplate(Firebug.Rep,
440 {
441     tag:
442         OBJECTBOX({_repObject: "$object",
443             $hasTwisty: "$object|hasSpecialProperties",
444             onclick: "$onToggleProperties"},
445             SPAN({"class": "arrayLeftBracket", role: "presentation"}, "["),
446             FOR("item", "$object|longArrayIterator",
447                 TAG("$item.tag", {object: "$item.object"}),
448                 SPAN({"class": "arrayComma", role: "presentation"}, "$item.delim")
449             ),
450             SPAN({"class": "arrayRightBracket", role: "presentation"}, "]"),
451             SPAN({"class": "arrayProperties", role: "group"})
452         ),
453 
454     shortTag:
455         OBJECTBOX({_repObject: "$object",
456             $hasTwisty: "$object|hasSpecialProperties",
457             onclick: "$onToggleProperties"},
458             SPAN({"class": "arrayLeftBracket", role: "presentation"}, "["),
459             FOR("item", "$object|shortArrayIterator",
460                 TAG("$item.tag", {object: "$item.object"}),
461                 SPAN({"class": "arrayComma", role: "presentation"}, "$item.delim")
462             ),
463             SPAN({"class": "arrayRightBracket"}, "]"),
464             SPAN({"class": "arrayProperties", role: "group"})
465         ),
466 
467     longArrayIterator: function(array)
468     {
469        return this.arrayIterator(array,300);
470     },
471 
472     shortArrayIterator: function(array)
473     {
474        return this.arrayIterator(array,3);
475     },
476 
477     arrayIterator: function(array, max)
478     {
479         var items = [];
480         for (var i = 0; i < array.length && i <= max; ++i)
481         {
482             var value = array[i];
483             var rep = Firebug.getRep(value);
484             var tag = rep.shortTag || rep.tag;
485             var delim = (i == array.length-1 ? "" : ", ");
486 
487             items.push({object: value, tag: tag, delim: delim});
488         }
489 
490         if (array.length > max + 1)
491         {
492             items[max] = {
493                 object: (array.length-max) + " more...", //xxxHonza localization
494                 tag: FirebugReps.Caption.tag,
495                 delim: ""
496             };
497         }
498 
499         return items;
500     },
501 
502     toggles: {},
503 
504     getItemIndex: function(child)
505     {
506         var arrayIndex = 0;
507         for (child = child.previousSibling; child; child = child.previousSibling)
508         {
509             if (child.repObject)
510                 ++arrayIndex;
511         }
512         return arrayIndex;
513     },
514 
515     hasSpecialProperties: function(array)
516     {
517         return (array.length != array.__count__) && hasProperties(array);
518     },
519 
520     onToggleProperties: function(event)
521     {
522         var target = event.originalTarget;
523         if (hasClass(target, "objectBox-array"))
524         {
525             toggleClass(target, "opened");
526 
527             var propBox = target.getElementsByClassName("arrayProperties").item(0);
528             if (hasClass(target, "opened"))
529                 Firebug.DOMPanel.DirTable.tag.replace(
530                     {object: target.repObject, toggles: this.toggles}, propBox);
531             else
532                 clearNode(propBox);
533         }
534     },
535 
536     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
537 
538     className: "array",
539 
540     supportsObject: function(object)
541     {
542         return this.isArray(object);
543     },
544 
545     // http://code.google.com/p/fbug/issues/detail?id=874
546     // BEGIN Yahoo BSD Source (modified here)  YAHOO.lang.isArray, YUI 2.2.2 June 2007
547     isArray: function(obj) {
548         try {
549             if (!obj)
550                 return false;
551             else if (obj instanceof Ci.nsIDOMHistory) // do this first to avoid security 1000 errors
552                 return false;
553             else if (obj instanceof StorageList) // do this first to avoid security 1000 errors
554                 return false;
555             else if (isFinite(obj.length) && typeof obj.splice === 'function')
556                 return true;
557             else if (isFinite(obj.length) && typeof obj.callee === 'function') // arguments
558                 return true;
559             else if (obj instanceof HTMLCollection)
560                 return true;
561             else if (obj instanceof NodeList)
562                 return true;
563             else
564                 return false;
565         }
566         catch(exc)
567         {
568             if (FBTrace.DBG_ERRORS)
569             {
570                 FBTrace.sysout("isArray FAILS:", exc);  /* Something weird: without the try/catch, OOM, with no exception?? */
571                 FBTrace.sysout("isArray Fails on obj "+obj);
572             }
573         }
574 
575         return false;
576     },
577     // END Yahoo BSD SOURCE See license below.
578 
579     getTitle: function(object, context)
580     {
581         return "[" + object.length + "]";
582     }
583 });
584 
585 // ************************************************************************************************
586 
587 this.Property = domplate(Firebug.Rep,
588 {
589     supportsObject: function(object)
590     {
591         return object instanceof Property;
592     },
593 
594     getRealObject: function(prop, context)
595     {
596         return prop.object[prop.name];
597     },
598 
599     getTitle: function(prop, context)
600     {
601         return prop.name;
602     }
603 });
604 
605 // ************************************************************************************************
606 
607 this.NetFile = domplate(this.Obj,
608 {
609     supportsObject: function(object)
610     {
611         return object instanceof Firebug.NetFile;
612     },
613 
614     browseObject: function(file, context)
615     {
616         openNewTab(file.href);
617         return true;
618     },
619 
620     getRealObject: function(file, context)
621     {
622         return null;
623     }
624 });
625 
626 // ************************************************************************************************
627 
628 this.Except = domplate(Firebug.Rep,
629 {
630     tag:
631         OBJECTBOX({_repObject: "$object"}, "$object.message"),
632 
633     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
634 
635     className: "exception",
636 
637     supportsObject: function(object)
638     {
639         return object instanceof ErrorCopy;
640     }
641 });
642 
643 
644 // ************************************************************************************************
645 
646 this.Element = domplate(Firebug.Rep,
647 {
648     tag:
649         OBJECTLINK(
650             "<",
651             SPAN({"class": "nodeTag"}, "$object.localName|toLowerCase"),
652             FOR("attr", "$object|attrIterator",
653                 " $attr.localName="", SPAN({"class": "nodeValue"}, "$attr.nodeValue"), """
654             ),
655             ">"
656          ),
657 
658     shortTag:
659         OBJECTLINK(
660             SPAN({"class": "$object|getVisible"},
661                 SPAN({"class": "selectorTag"}, "$object|getSelectorTag"),
662                 SPAN({"class": "selectorId"}, "$object|getSelectorId"),
663                 SPAN({"class": "selectorClass"}, "$object|getSelectorClass"),
664                 SPAN({"class": "selectorValue"}, "$object|getValue")
665             )
666          ),
667 
668     getVisible: function(elt)
669     {
670         return isVisible(elt) ? "" : "selectorHidden";
671     },
672 
673     getSelectorTag: function(elt)
674     {
675         return elt.localName.toLowerCase();
676     },
677 
678     getSelectorId: function(elt)
679     {
680         return elt.id ? ("#" + elt.id) : "";
681     },
682 
683     getSelectorClass: function(elt)
684     {
685         return elt.getAttribute("class")
686             ? ("." + elt.getAttribute("class").split(" ")[0])
687             : "";
688     },
689 
690     getValue: function(elt)
691     {
692         var value;
693 
694         if (elt instanceof HTMLImageElement)
695             value = getFileName(elt.getAttribute("src"));
696         else if (elt instanceof HTMLAnchorElement)
697             value = getFileName(elt.getAttribute("href"));
698         else if (elt instanceof HTMLInputElement)
699             value = elt.getAttribute("value");
700         else if (elt instanceof HTMLFormElement)
701             value = getFileName(elt.getAttribute("action"));
702         else if (elt instanceof HTMLScriptElement)
703             value = getFileName(elt.getAttribute("src"));
704 
705         return value ? " " + cropMultipleLines(value, 20) : "";
706      },
707 
708      attrIterator: function(elt)
709      {
710          var attrs = [];
711          var idAttr, classAttr;
712          if (elt.attributes)
713          {
714              for (var i = 0; i < elt.attributes.length; ++i)
715              {
716                  var attr = elt.attributes[i];
717                  if (attr.localName.indexOf("-moz-math") != -1)
718                      continue;
719                  if (attr.localName.indexOf("firebug-") != -1)
720                      continue;
721                  else if (attr.localName == "id")
722                      idAttr = attr;
723                  else if (attr.localName == "class")
724                      classAttr = attr;
725                  else
726                      attrs.push(attr);
727              }
728          }
729          if (classAttr)
730             attrs.splice(0, 0, classAttr);
731         if (idAttr)
732            attrs.splice(0, 0, idAttr);
733          return attrs;
734      },
735 
736      shortAttrIterator: function(elt)
737      {
738          var attrs = [];
739          if (elt.attributes)
740          {
741              for (var i = 0; i < elt.attributes.length; ++i)
742              {
743                  var attr = elt.attributes[i];
744                  if (attr.localName == "id" || attr.localName == "class")
745                      attrs.push(attr);
746              }
747          }
748 
749          return attrs;
750      },
751 
752      getHidden: function(elt)
753      {
754          return isVisible(elt) ? "" : "nodeHidden";
755      },
756 
757      getXPath: function(elt)
758      {
759          return getElementTreeXPath(elt);
760      },
761 
762      getNodeTextGroups: function(element)
763      {
764          var text =  element.textContent;
765          if (!Firebug.showFullTextNodes)
766          {
767              text=cropString(text,50);
768          }
769 
770          var escapeGroups=[];
771 
772          if (Firebug.showTextNodesWithWhitespace)
773              escapeGroups.push({
774                 'group': 'whitespace',
775                 'class': 'nodeWhiteSpace',
776                 'extra': {
777                     '\t': '_Tab',
778                     '\n': '_Para',
779                     ' ' : '_Space'
780                 }
781              });
782          if (Firebug.showTextNodesWithEntities)
783              escapeGroups.push({
784                  'group':'text',
785                  'class':'nodeTextEntity',
786                  'extra':{}
787              });
788 
789          if (escapeGroups.length)
790              return escapeGroupsForEntities(text, escapeGroups);
791          else
792              return [{str:text,'class':'',extra:''}];
793      },
794 
795     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
796 
797     copyHTML: function(elt)
798     {
799         var html = getElementHTML(elt);
800         copyToClipboard(html);
801     },
802 
803     copyInnerHTML: function(elt)
804     {
805         copyToClipboard(elt.innerHTML);
806     },
807 
808     copyXPath: function(elt)
809     {
810         var xpath = getElementXPath(elt);
811         copyToClipboard(xpath);
812     },
813 
814     persistor: function(context, xpath)
815     {
816         var elts = xpath
817             ? getElementsByXPath(context.window.document, xpath)
818             : null;
819 
820         return elts && elts.length ? elts[0] : null;
821     },
822 
823     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
824 
825     className: "element",
826 
827     supportsObject: function(object)
828     {
829         return object instanceof Element;
830     },
831 
832     browseObject: function(elt, context)
833     {
834         var tag = elt.localName.toLowerCase();
835         if (tag == "script")
836             openNewTab(elt.src);
837         else if (tag == "link")
838             openNewTab(elt.href);
839         else if (tag == "a")
840             openNewTab(elt.href);
841         else if (tag == "img")
842             openNewTab(elt.src);
843 
844         return true;
845     },
846 
847     persistObject: function(elt, context)
848     {
849         var xpath = getElementXPath(elt);
850 
851         return bind(this.persistor, top, xpath);
852     },
853 
854     getTitle: function(element, context)
855     {
856         return getElementCSSSelector(element);
857     },
858 
859     getTooltip: function(elt)
860     {
861         return this.getXPath(elt);
862     },
863 
864     getContextMenuItems: function(elt, target, context)
865     {
866         var monitored = areEventsMonitored(elt, null, context);
867         var CopyElement = "CopyHTML";
868         if (isElementSVG(elt))
869             CopyElement = "CopySVG";
870         if (isElementMathML(elt))
871             CopyElement = "CopyMathML";
872 
873         var items=[{label: CopyElement, command: bindFixed(this.copyHTML, this, elt)}];
874         if (!isElementSVG(elt) && !isElementMathML(elt))
875             items.push({label: "CopyInnerHTML", command: bindFixed(this.copyInnerHTML, this, elt) });
876 
877         return items.concat([
878             {label: "CopyXPath", command: bindFixed(this.copyXPath, this, elt) },
879             "-",
880             {label: "ShowEventsInConsole", type: "checkbox", checked: monitored,
881              command: bindFixed(toggleMonitorEvents, FBL, elt, null, monitored, context) },
882             "-",
883             {label: "ScrollIntoView", command: bindFixed(elt.scrollIntoView, elt) }
884         ]);
885     }
886 });
887 
888 // ************************************************************************************************
889 
890 this.TextNode = domplate(Firebug.Rep,
891 {
892     tag:
893         OBJECTLINK(
894             "<",
895             SPAN({"class": "nodeTag"}, "TextNode"),
896             " textContent="", SPAN({"class": "nodeValue"}, "$object.textContent|cropMultipleLines"), """,
897             ">"
898             ),
899 
900     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
901 
902     className: "textNode",
903 
904     inspectObject: function(node, context)
905     {
906         // Text nodes have two displays in HTML panel, inline and distinct
907         // node. We need to examine which case we are dealing with in order to
908         // select the proper object.
909         if (Firebug.HTMLLib.hasNoElementChildren(node.parentNode))
910         {
911             node = node.parentNode;
912         }
913 
914         Firebug.chrome.select(node, "html", "domSide");
915     },
916 
917     supportsObject: function(object)
918     {
919         return object instanceof Text;
920     },
921 
922     getTitle: function(win, context)
923     {
924         return "textNode";
925     }
926 });
927 
928 
929 // ************************************************************************************************
930 
931 var regexpConstructorRE = /RegExp/;
932 this.RegExp = domplate(Firebug.Rep,
933 {
934     tag:
935         OBJECTLINK(
936             SPAN({"class": "objectTitle"}, "$object|getTitle")
937         ),
938     
939     className: "regexp",
940     
941     supportsObject: function(object, type)
942     {
943         return type == "object" && object && object.constructor && object.constructor.toString && regexpConstructorRE.test(object.constructor.toString());
944     }
945 });
946 
947 
948 // ************************************************************************************************
949 
950 this.Document = domplate(Firebug.Rep,
951 {
952     tag:
953         OBJECTLINK("Document ", SPAN({"class": "objectPropValue"}, "$object|getLocation")),
954 
955     getLocation: function(doc)
956     {
957         return doc.location ? getFileName(doc.location.href) : "";
958     },
959 
960     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
961 
962     className: "object",
963 
964     supportsObject: function(object)
965     {
966         return object instanceof Document || object instanceof XMLDocument;
967     },
968 
969     browseObject: function(doc, context)
970     {
971         openNewTab(doc.location.href);
972         return true;
973     },
974 
975     persistObject: function(doc, context)
976     {
977         return this.persistor;
978     },
979 
980     persistor: function(context)
981     {
982         return context.window.document;
983     },
984 
985     getTitle: function(win, context)
986     {
987         return "document";
988     },
989 
990     getTooltip: function(doc)
991     {
992         return doc.location.href;
993     }
994 });
995 
996 // ************************************************************************************************
997 
998 this.StyleSheet = domplate(Firebug.Rep,
999 {
1000     tag:
1001         OBJECTLINK("StyleSheet ", SPAN({"class": "objectPropValue"}, "$object|getLocation")),
1002 
1003     getLocation: function(styleSheet)
1004     {
1005         return getFileName(styleSheet.href);
1006     },
1007 
1008     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1009 
1010     copyURL: function(styleSheet)
1011     {
1012         copyToClipboard(styleSheet.href);
1013     },
1014 
1015     openInTab: function(styleSheet)
1016     {
1017         openNewTab(styleSheet.href);
1018     },
1019 
1020     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1021 
1022     className: "object",
1023 
1024     supportsObject: function(object)
1025     {
1026         return object instanceof CSSStyleSheet;
1027     },
1028 
1029     browseObject: function(styleSheet, context)
1030     {
1031         openNewTab(styleSheet.href);
1032         return true;
1033     },
1034 
1035     persistObject: function(styleSheet, context)
1036     {
1037         return bind(this.persistor, top, styleSheet.href);
1038     },
1039 
1040     getTooltip: function(styleSheet)
1041     {
1042         return styleSheet.href;
1043     },
1044 
1045     getContextMenuItems: function(styleSheet, target, context)
1046     {
1047         return [
1048             {label: "CopyLocation", command: bindFixed(this.copyURL, this, styleSheet) },
1049             "-",
1050             {label: "OpenInTab", command: bindFixed(this.openInTab, this, styleSheet) }
1051         ];
1052     },
1053 
1054     persistor: function(context, href)
1055     {
1056         return getStyleSheetByHref(href, context);
1057     }
1058 });
1059 
1060 // ************************************************************************************************
1061 
1062 this.Window = domplate(Firebug.Rep,
1063 {
1064     tag:
1065         OBJECTLINK("Window ", SPAN({"class": "objectPropValue"}, "$object|getLocation")),
1066 
1067     getLocation: function(win)
1068     {
1069         try
1070         {
1071             return (win && win.location && !win.closed) ? getFileName(win.location.href) : "";
1072         }
1073         catch (exc)
1074         {
1075             if (FBTrace.DBG_ERRORS)
1076                 FBTrace.sysout("reps.Window window closed?");
1077         }
1078     },
1079 
1080     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1081 
1082     className: "object",
1083 
1084     supportsObject: function(object)
1085     {
1086         return object instanceof Window;
1087     },
1088 
1089     browseObject: function(win, context)
1090     {
1091         openNewTab(win.location.href);
1092         return true;
1093     },
1094 
1095     persistObject: function(win, context)
1096     {
1097         return this.persistor;
1098     },
1099 
1100     persistor: function(context)
1101     {
1102         return context.window;
1103     },
1104 
1105     getTitle: function(win, context)
1106     {
1107         return "window";
1108     },
1109 
1110     getTooltip: function(win)
1111     {
1112         if (win && !win.closed)
1113             return win.location.href;
1114     }
1115 });
1116 
1117 // ************************************************************************************************
1118 
1119 this.Event = domplate(Firebug.Rep,
1120 {
1121     tag: TAG("$copyEventTag", {object: "$object|copyEvent"}),
1122 
1123     copyEventTag:
1124         OBJECTLINK("$object|summarizeEvent"),
1125 
1126     summarizeEvent: function(event)
1127     {
1128         var info = [event.type, ' '];
1129 
1130         var eventFamily = getEventFamily(event.type);
1131         if (eventFamily == "mouse")
1132             info.push("clientX=", event.clientX, ", clientY=", event.clientY);
1133         else if (eventFamily == "key")
1134             info.push("charCode=", event.charCode, ", keyCode=", event.keyCode);
1135 
1136         return info.join("");
1137     },
1138 
1139     copyEvent: function(event)
1140     {
1141         return new EventCopy(event);
1142     },
1143 
1144     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1145 
1146     className: "object",
1147 
1148     supportsObject: function(object)
1149     {
1150         return object instanceof Event || object instanceof EventCopy;
1151     },
1152 
1153     getTitle: function(event, context)
1154     {
1155         return "Event " + event.type;
1156     }
1157 });
1158 
1159 // ************************************************************************************************
1160 
1161 this.SourceLink = domplate(Firebug.Rep,
1162 {
1163     tag:
1164         OBJECTLINK(
1165             {$collapsed: "$object|hideSourceLink"},
1166             DIV("$object|getSourceLinkTitle"),
1167             DIV({$systemLink: "$object|isSystemLink"}, "$object|getSystemFlagTitle")),
1168 
1169     isSystemLink: function(sourceLink)
1170     {
1171         return sourceLink && isSystemURL(sourceLink.href);
1172     },
1173 
1174     hideSourceLink: function(sourceLink)
1175     {
1176         try
1177         {
1178             return (sourceLink && sourceLink.href && sourceLink.href.indexOf) ?
1179                 (sourceLink.href.indexOf("XPCSafeJSObjectWrapper") != -1) : true;
1180         }
1181         catch (e)
1182         {
1183             // xxxHonza: I see "Security error" code: "1000" nsresult: "0x805303e8 (NS_ERROR_DOM_SECURITY_ERR)"
1184             // when accessing globalStorage property of a page.
1185             if (FBTrace.DBG_ERRORS)
1186                 FBTrace.sysout("reps.hideSourceLink; EXCEPTION " + sourceLink + ", " + e, e);
1187         }
1188 
1189         return true;
1190     },
1191 
1192     getSourceLinkTitle: function(sourceLink)
1193     {
1194         if (!sourceLink)
1195             return "";
1196 
1197         try
1198         {
1199             var fileName = getFileName(sourceLink.href);
1200             fileName = decodeURIComponent(fileName);
1201         }
1202         catch(exc)
1203         {
1204             if (FBTrace.DBG_ERRORS)
1205                 FBTrace.sysout("reps.getSourceLinkTitle decodeURIComponent fails for \'"+fileName+"\': "+exc, exc);
1206             fileName = sourceLink.href;
1207         }
1208 
1209         var maxWidth = Firebug.sourceLinkLabelWidth;
1210         if (maxWidth > 0)
1211             fileName = cropString(fileName, maxWidth);
1212 
1213         if (sourceLink.instance)
1214             return $STRF("InstanceLine", [fileName, sourceLink.instance+1, sourceLink.line]);
1215         else if (sourceLink.line)
1216             return $STRF("Line", [fileName, sourceLink.line]);
1217         else
1218             return fileName;
1219     },
1220 
1221     getSystemFlagTitle: function(sourceLink)
1222     {
1223         if (this.isSystemLink(sourceLink))
1224             return $STRF("SystemItem", [""]);
1225         else
1226             return "";
1227     },
1228 
1229     copyLink: function(sourceLink)
1230     {
1231         copyToClipboard(sourceLink.href);
1232     },
1233 
1234     openInTab: function(sourceLink)
1235     {
1236         openNewTab(sourceLink.href);
1237     },
1238 
1239     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1240 
1241     className: "sourceLink",
1242 
1243     supportsObject: function(object)
1244     {
1245         return object instanceof SourceLink;
1246     },
1247 
1248     getTooltip: function(sourceLink)
1249     {
1250         var text;
1251         try
1252         {
1253             text = decodeURI(sourceLink.href);
1254         }
1255         catch(exc)
1256         {
1257             if (FBTrace.DBG_ERRORS)
1258                 FBTrace.sysout("reps.getTooltip decodeURI fails for " + sourceLink.href, exc);
1259         }
1260 
1261         text = unescape(sourceLink.href);
1262 
1263         var lines = splitLines(text);
1264         if (lines.length < 10)
1265             return text;
1266 
1267         lines.splice(10);
1268         return lines.join("") + "...";
1269     },
1270 
1271     inspectObject: function(sourceLink, context)
1272     {
1273         if (sourceLink.type == "js")
1274         {
1275             var scriptFile = getSourceFileByHref(sourceLink.href, context);
1276             if (scriptFile)
1277                 return Firebug.chrome.select(sourceLink);
1278         }
1279         else if (sourceLink.type == "css")
1280         {
1281             // If an object is defined, treat it as the highest priority for
1282             // inspect actions
1283             if (sourceLink.object) {
1284                 Firebug.chrome.select(sourceLink.object);
1285                 return;
1286             }
1287 
1288             var stylesheet = getStyleSheetByHref(sourceLink.href, context);
1289             if (stylesheet)
1290             {
1291                 var ownerNode = stylesheet.ownerNode;
1292                 if (ownerNode)
1293                 {
1294                     Firebug.chrome.select(sourceLink, "html");
1295                     return;
1296                 }
1297 
1298                 var panel = context.getPanel("stylesheet");
1299                 if (panel && panel.getRuleByLine(stylesheet, sourceLink.line))
1300                     return Firebug.chrome.select(sourceLink);
1301             }
1302         }
1303         else if (sourceLink.type == "net")
1304         {
1305             return Firebug.chrome.select(sourceLink);
1306         }
1307 
1308         // Fallback is to just open the view-source window on the file
1309         viewSource(sourceLink.href, sourceLink.line);
1310     },
1311 
1312     browseObject: function(sourceLink, context)
1313     {
1314         openNewTab(sourceLink.href);
1315         return true;
1316     },
1317 
1318     getContextMenuItems: function(sourceLink, target, context)
1319     {
1320         return [
1321             {label: "CopyLocation", command: bindFixed(this.copyLink, this, sourceLink) },
1322             "-",
1323             {label: "OpenInTab", command: bindFixed(this.openInTab, this, sourceLink) }
1324         ];
1325     }
1326 });
1327 
1328 // ************************************************************************************************
1329 
1330 this.SourceFile = domplate(this.SourceLink,
1331 {
1332     tag:
1333         OBJECTLINK({$collapsed: "$object|hideSourceLink"}, "$object|getSourceLinkTitle"),
1334 
1335     persistor: function(context, href)
1336     {
1337         return getSourceFileByHref(href, context);
1338     },
1339 
1340     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1341 
1342     className: "sourceFile",
1343 
1344     supportsObject: function(object)
1345     {
1346         return object instanceof Firebug.SourceFile;
1347     },
1348 
1349     persistObject: function(sourceFile)
1350     {
1351         return bind(this.persistor, top, sourceFile.href);
1352     },
1353 
1354     browseObject: function(sourceLink, context)
1355     {
1356     },
1357 
1358     getTooltip: function(sourceFile)
1359     {
1360         return sourceFile.href;
1361     }
1362 });
1363 
1364 // ************************************************************************************************
1365 
1366 this.StackFrame = domplate(Firebug.Rep,  // XXXjjb Since the repObject is fn the stack does not have correct line numbers
1367 {
1368     tag:
1369         OBJECTBLOCK(
1370             A({"class": "objectLink a11yFocus", _repObject: "$object"}, "$object|getCallName"),
1371             SPAN("("),
1372             FOR("arg", "$object|argIterator",
1373                 TAG("$arg.tag", {object: "$arg.value"}),
1374                 SPAN({"class": "arrayComma"}, "$arg.delim")
1375             ),
1376             SPAN(")"),
1377             SPAN({"class": "objectLink-sourceLink objectLink a11yFocus",
1378                 _repObject: "$object|getSourceLink",
1379                 role: "link"},
1380                 "$object|getSourceLinkTitle")
1381         ),
1382 
1383     getCallName: function(frame)
1384     {
1385         if (frame.fn && frame.fn != "anonymous")
1386             return frame.fn;
1387         return getFunctionName(frame.script, frame.context, null, true);
1388     },
1389 
1390     getSourceLinkTitle: function(frame)
1391     {
1392         var fileName = cropString(getFileName(frame.href), 17);
1393         return $STRF("Line", [fileName, frame.line]);
1394     },
1395 
1396     argIterator: function(frame)
1397     {
1398         if (!frame.args)
1399             return [];
1400 
1401         var items = [];
1402 
1403         for (var i = 0; i < frame.args.length; ++i)
1404         {
1405             var arg = frame.args[i];
1406 
1407             if (!arg)
1408                 break;
1409 
1410             if (arg.value) // then we got these from jsd
1411             {
1412                 var rep = Firebug.getRep(arg.value);
1413                 var tag = rep.shortTag ? rep.shortTag : rep.tag;
1414 
1415                 var delim = (i == frame.args.length-1 ? "" : ", ");
1416 
1417                 items.push({name: arg.name, value: arg.value, tag: tag, delim: delim});
1418             }
1419             else  // eg from Error object
1420             {
1421                 var delim = (i == frame.args.length-1 ? "" : ", ");
1422                 var rep = Firebug.getRep(arg);
1423                 var tag = rep.shortTag ? rep.shortTag : rep.tag;
1424 
1425                 items.push({value: arg, tag: tag, delim: delim});
1426             }
1427         }
1428 
1429         return items;
1430     },
1431 
1432     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1433 
1434     className: "stackFrame",
1435 
1436     supportsObject: function(object)
1437     {
1438         return object instanceof StackFrame;
1439     },
1440 
1441     inspectObject: function(stackFrame, context)
1442     {
1443         Firebug.chrome.select(this.getSourceLink(stackFrame));
1444     },
1445 
1446     getTooltip: function(stackFrame, context)
1447     {
1448         return $STRF("Line", [stackFrame.href, stackFrame.line]);
1449     },
1450 
1451     getSourceLink: function(stackFrame)
1452     {
1453         var sourceLink = new SourceLink(stackFrame.href, stackFrame.line, "js");
1454         return sourceLink;
1455     },
1456 });
1457 
1458 // ************************************************************************************************
1459 
1460 this.StackTrace = domplate(Firebug.Rep,
1461 {
1462     tag:
1463         DIV({role : "group", 'aria-label' : $STR('aria.labels.stack trace')},
1464             FOR("frame", "$object.frames",
1465                 TAG(this.StackFrame.tag, {object: "$frame"})
1466             )
1467         ),
1468 
1469     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1470 
1471     className: "stackTrace",
1472 
1473     supportsObject: function(object)
1474     {
1475         return object instanceof StackTrace;
1476     }
1477 });
1478 
1479 // ************************************************************************************************
1480 
1481 this.jsdStackFrame = domplate(Firebug.Rep,
1482 {
1483     inspectable: false,
1484 
1485     className: "jsdIStactFrame",
1486 
1487     supportsObject: function(object)
1488     {
1489         return (object instanceof jsdIStackFrame);
1490     },
1491 
1492     getTitle: function(frame, context)
1493     {
1494         if (!frame.isValid) return "(invalid frame)"; // XXXjjb avoid frame.script == null
1495         return getFunctionName(frame.script, context);
1496     },
1497 
1498     getTooltip: function(frame, context)
1499     {
1500         if (!frame.isValid) return "(invalid frame; did Firebug suspend?)";  // XXXjjb avoid frame.script == null
1501         var sourceInfo = Firebug.SourceFile.getSourceFileAndLineByScript(context, frame.script, frame);
1502         if (sourceInfo)
1503             return $STRF("Line", [sourceInfo.sourceFile.href, sourceInfo.lineNo]);
1504         else
1505             return $STRF("Line", [frame.script.fileName, frame.line]);
1506     },
1507 
1508     getContextMenuItems: function(frame, target, context)
1509     {
1510         var fn = unwrapIValue(frame.script.functionObject);
1511         return FirebugReps.Func.getContextMenuItems(fn, target, context, frame.script);
1512     }
1513 });
1514 
1515 // ************************************************************************************************
1516 
1517 this.ErrorMessage = domplate(Firebug.Rep,
1518 {
1519     tag:
1520         OBJECTBOX({
1521                 $hasTwisty: "$object|hasStackTrace",
1522                 $hasBreakSwitch: "$object|hasBreakSwitch",
1523                 $breakForError: "$object|hasErrorBreak",
1524                 _repObject: "$object",
1525                 _stackTrace: "$object|getLastErrorStackTrace",
1526                 onclick: "$onToggleError"},
1527 
1528             DIV({"class": "errorTitle focusRow subLogRow", role : 'listitem'},
1529                 SPAN({"class": "errorDuplication"}, "$object.msgId|getDuplication"),
1530                 "$object.message|getMessage"
1531             ),
1532             DIV({"class": "errorTrace", role : 'presentation'}),
1533             DIV({"class": "errorSourceBox errorSource-$object|getSourceType focusRow subLogRow", role : "listitem"},
1534                 IMG({"class": "errorBreak a11yFocus", src:"blank.gif", role : 'checkbox', 'aria-checked':"$object|hasErrorBreak", title: "Break on this error"}),
1535                 A({"class": "errorSource a11yFocus"}, "$object|getLine"),
1536                 TAG(this.SourceLink.tag, {object: "$object|getSourceLink"})
1537             )
1538         ),
1539 
1540     getLastErrorStackTrace: function(error)
1541     {
1542         return error.trace;
1543     },
1544 
1545     hasStackTrace: function(error)
1546     {
1547         var url = error.href.toString();
1548         var fromCommandLine = (url.indexOf("XPCSafeJSObjectWrapper") != -1);
1549         return !fromCommandLine && error.trace;
1550     },
1551 
1552     hasBreakSwitch: function(error)
1553     {
1554         return error.href && error.lineNo > 0;
1555     },
1556 
1557     hasErrorBreak: function(error)
1558     {
1559         return fbs.hasErrorBreakpoint(normalizeURL(error.href), error.lineNo);
1560     },
1561 
1562     getMessage: function(message)
1563     {
1564         var re = /\[Exception... "(.*?)" nsresult:/;
1565         var m = re.exec(message);
1566         return m ? m[1] : message;
1567     },
1568 
1569     getDuplication: function(msgId)
1570     {
1571         return ""; // filled in later
1572     },
1573 
1574     getLine: function(error)
1575     {
1576         if (error.source)
1577             return cropMultipleLines(error.source, 80);
1578         if (error.category == "js" && error.href && error.href.indexOf("XPCSafeJSObjectWrapper") != -1)
1579             return "";
1580         var source = error.getSourceLine();
1581         if (source)
1582             return cropString(source, 80);
1583         return "";
1584     },
1585 
1586     getSourceLink: function(error)
1587     {
1588         var ext = error.category == "css" ? "css" : "js";
1589         return error.lineNo ? new SourceLink(error.href, error.lineNo, ext) : null;
1590     },
1591 
1592     getSourceType: function(error)
1593     {
1594         // Errors occurring inside of HTML event handlers look like "foo.html (line 1)"
1595         // so let's try to skip those
1596         if (error.source)
1597             return "syntax";
1598         else if (error.lineNo == 1 && getFileExtension(error.href) != "js")
1599             return "none";
1600         else if (error.category == "css")
1601             return "show";
1602         else if (!error.href || !error.lineNo)
1603             return "none";
1604         else
1605             return "show";
1606     },
1607 
1608     onToggleError: function(event)
1609     {
1610         var target = event.currentTarget;
1611         if (hasClass(event.target, "errorBreak"))
1612         {
1613             var panel = Firebug.getElementPanel(event.target);
1614             this.breakOnThisError(target.repObject, panel.context);
1615         }
1616         else if (hasClass(event.target, "errorSource"))
1617         {
1618             var panel = Firebug.getElementPanel(event.target);
1619             this.inspectObject(target.repObject, panel.context);
1620         }
1621         else if (hasClass(event.target, "errorTitle"))
1622         {
1623             var traceBox = target.childNodes[1];
1624             toggleClass(target, "opened");
1625             event.target.setAttribute('aria-expanded', hasClass(target, "opened"));
1626             if (hasClass(target, "opened"))
1627             {
1628                 if (target.stackTrace)
1629                     var node = FirebugReps.StackTrace.tag.append({object: target.stackTrace}, traceBox);
1630                 if (Firebug.A11yModel.enabled)
1631                 {
1632                     var panel = Firebug.getElementPanel(event.target);
1633                     dispatch([Firebug.A11yModel], "modifyLogRow", [panel , traceBox]);
1634                 }
1635             }
1636             else
1637                 clearNode(traceBox);
1638         }
1639     },
1640 
1641     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1642 
1643     copyError: function(error)
1644     {
1645         var message = [
1646             this.getMessage(error.message),
1647             error.href,
1648             "Line " +  error.lineNo
1649         ];
1650         copyToClipboard(message.join("\n"));
1651     },
1652 
1653     breakOnThisError: function(error, context)
1654     {
1655 
1656         var sourceFile = context.sourceFileMap[normalizeURL(error.href)];
1657         if (!sourceFile)
1658         {
1659             Firebug.Console.logFormatted(["reps.breakOnThisError has not source file for error.href: "+error.href, error], context, 'error', true);
1660             return;
1661         }
1662 
1663         if (this.hasErrorBreak(error))
1664             Firebug.Debugger.clearErrorBreakpoint(sourceFile, error.lineNo);
1665         else
1666             Firebug.Debugger.setErrorBreakpoint(sourceFile, error.lineNo);
1667     },
1668 
1669     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1670 
1671     className: "errorMessage",
1672     inspectable: false,
1673 
1674     supportsObject: function(object)
1675     {
1676         return object instanceof ErrorMessage;
1677     },
1678 
1679     inspectObject: function(error, context)
1680     {
1681         var sourceLink = this.getSourceLink(error);
1682         FirebugReps.SourceLink.inspectObject(sourceLink, context);
1683     },
1684 
1685     getContextMenuItems: function(error, target, context)
1686     {
1687         var breakOnThisError = this.hasErrorBreak(error);
1688 
1689         var items = [
1690             {label: "CopyError", command: bindFixed(this.copyError, this, error) }
1691         ];
1692 
1693         if (error.category == "css")
1694         {
1695             items.push(
1696                 "-",
1697                 {label: "BreakOnThisError", type: "checkbox", checked: breakOnThisError,
1698                  command: bindFixed(this.breakOnThisError, this, error) },
1699 
1700                 optionMenu("BreakOnAllErrors", "breakOnErrors")
1701             );
1702         }
1703 
1704         return items;
1705     }
1706 });
1707 
1708 // ************************************************************************************************
1709 
1710 this.Assert = domplate(Firebug.Rep,
1711 {
1712     tag:
1713         DIV(
1714             DIV({"class": "errorTitle"}),
1715             DIV({"class": "assertDescription"})
1716         ),
1717 
1718     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1719 
1720     className: "assert",
1721 
1722     inspectObject: function(error, context)
1723     {
1724         var sourceLink = this.getSourceLink(error);
1725         Firebug.chrome.select(sourceLink);
1726     },
1727 
1728     getContextMenuItems: function(error, target, context)
1729     {
1730         var breakOnThisError = this.hasErrorBreak(error);
1731 
1732         return [
1733             {label: "CopyError", command: bindFixed(this.copyError, this, error) },
1734             "-",
1735             {label: "BreakOnThisError", type: "checkbox", checked: breakOnThisError,
1736              command: bindFixed(this.breakOnThisError, this, error) },
1737             {label: "BreakOnAllErrors", type: "checkbox", checked: Firebug.breakOnErrors,
1738              command: bindFixed(this.breakOnAllErrors, this, error) }
1739         ];
1740     }
1741 });
1742 
1743 // ************************************************************************************************
1744 
1745 this.SourceText = domplate(Firebug.Rep,
1746 {
1747     tag:
1748         DIV(
1749             FOR("line", "$object|lineIterator",
1750                 DIV({"class": "sourceRow", role : "presentation"},
1751                     SPAN({"class": "sourceLine", role : "presentation"}, "$line.lineNo"),
1752                     SPAN({"class": "sourceRowText", role : "presentation"}, "$line.text")
1753                 )
1754             )
1755         ),
1756 
1757     lineIterator: function(sourceText)
1758     {
1759         var maxLineNoChars = (sourceText.lines.length + "").length;
1760         var list = [];
1761 
1762         for (var i = 0; i < sourceText.lines.length; ++i)
1763         {
1764             // Make sure all line numbers are the same width (with a fixed-width font)
1765             var lineNo = (i+1) + "";
1766             while (lineNo.length < maxLineNoChars)
1767                 lineNo = " " + lineNo;
1768 
1769             list.push({lineNo: lineNo, text: sourceText.lines[i]});
1770         }
1771 
1772         return list;
1773     },
1774 
1775     getHTML: function(sourceText)
1776     {
1777         return getSourceLineRange(sourceText, 1, sourceText.lines.length);
1778     }
1779 });
1780 
1781 //************************************************************************************************
1782 
1783 this.nsIDOMHistory = domplate(Firebug.Rep,
1784 {
1785     tag:OBJECTBOX({onclick: "$showHistory"},
1786             OBJECTLINK("$object|summarizeHistory")
1787         ),
1788 
1789     className: "nsIDOMHistory",
1790 
1791     summarizeHistory: function(history)
1792     {
1793         try
1794         {
1795             var items = history.length;
1796             return items + " history entries";
1797         }
1798         catch(exc)
1799         {
1800             return "object does not support history (nsIDOMHistory)";
1801         }
1802     },
1803 
1804     showHistory: function(history)
1805     {
1806         try
1807         {
1808             var items = history.length;  // if this throws, then unsupported
1809             Firebug.chrome.select(history);
1810         }
1811         catch (exc)
1812         {
1813         }
1814     },
1815 
1816     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1817 
1818     supportsObject: function(object, type)
1819     {
1820         return (object instanceof Ci.nsIDOMHistory);
1821     }
1822 });
1823 
1824 // ************************************************************************************************
1825 
1826 this.ApplicationCache = domplate(Firebug.Rep,
1827 {
1828     tag:OBJECTBOX({onclick: "$showApplicationCache"},
1829             OBJECTLINK("$object|summarizeCache")
1830         ),
1831 
1832     summarizeCache: function(applicationCache)
1833     {
1834         try
1835         {
1836             return applicationCache.length + " items in offline cache";
1837         }
1838         catch(exc)
1839         {
1840             return "https://bugzilla.mozilla.org/show_bug.cgi?id=422264";
1841         }
1842     },
1843 
1844     showApplicationCache: function(event)
1845     {
1846         openNewTab("https://bugzilla.mozilla.org/show_bug.cgi?id=422264");
1847     },
1848 
1849     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1850 
1851     className: "applicationCache",
1852 
1853     supportsObject: function(object, type)
1854     {
1855         if (Ci.nsIDOMOfflineResourceList)
1856             return (object instanceof Ci.nsIDOMOfflineResourceList);
1857     }
1858 
1859 });
1860 
1861 this.Storage = domplate(Firebug.Rep,
1862 {
1863     tag: OBJECTBOX({onclick: "$show"}, OBJECTLINK("$object|summarize")),
1864 
1865     summarize: function(storage)
1866     {
1867         return storage.length +" items in Storage";
1868     },
1869     show: function(storage)
1870     {
1871         openNewTab("http://dev.w3.org/html5/webstorage/#storage-0");
1872     },
1873     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1874 
1875     className: "Storage",
1876 
1877     supportsObject: function(object, type)
1878     {
1879         return (object instanceof Storage);
1880     }
1881 
1882 });
1883 
1884 // ************************************************************************************************
1885 
1886 Firebug.registerRep(
1887     this.nsIDOMHistory, // make this early to avoid exceptions
1888     this.Undefined,
1889     this.Null,
1890     this.Number,
1891     this.RegExp,
1892     this.String,
1893     this.Window,
1894     this.ApplicationCache, // must come before Arr (array) else exceptions.
1895     this.ErrorMessage,
1896     this.Element,
1897     this.TextNode,
1898     this.Document,
1899     this.StyleSheet,
1900     this.Event,
1901     this.SourceLink,
1902     this.SourceFile,
1903     this.StackTrace,
1904     this.StackFrame,
1905     this.jsdStackFrame,
1906     this.jsdScript,
1907     this.NetFile,
1908     this.Property,
1909     this.Except,
1910     this.XML,
1911     this.Arr
1912 );
1913 
1914 Firebug.setDefaultReps(this.Func, this.Obj);
1915 
1916 }});
1917 
1918 // ************************************************************************************************
1919 /*
1920  * The following is http://developer.yahoo.com/yui/license.txt and applies to only code labeled "Yahoo BSD Source"
1921  * in only this file reps.js.  John J. Barton June 2007.
1922  *
1923 Software License Agreement (BSD License)
1924 
1925 Copyright (c) 2006, Yahoo! Inc.
1926 All rights reserved.
1927 
1928 Redistribution and use of this software in source and binary forms, with or without modification, are
1929 permitted provided that the following conditions are met:
1930 
1931 * Redistributions of source code must retain the above
1932   copyright notice, this list of conditions and the
1933   following disclaimer.
1934 
1935 * Redistributions in binary form must reproduce the above
1936   copyright notice, this list of conditions and the
1937   following disclaimer in the documentation and/or other
1938   materials provided with the distribution.
1939 
1940 * Neither the name of Yahoo! Inc. nor the names of its
1941   contributors may be used to endorse or promote products
1942   derived from this software without specific prior
1943   written permission of Yahoo! Inc.
1944 
1945 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
1946 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
1947 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
1948 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
1949 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
1950 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
1951 TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
1952 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1953  * /
1954  */
1955