1 /* See license.txt for terms of usage */
  2 
  3 define([
  4     "firebug/lib/trace",
  5     "firebug/lib/string"
  6 ],
  7 function(FBTrace, Str) {
  8 
  9 // ********************************************************************************************* //
 10 // Constants
 11 
 12 var Ci = Components.interfaces;
 13 var Cc = Components.classes;
 14 var Cu = Components.utils;
 15 
 16 var Xml = {};
 17 
 18 // ************************************************************************************************
 19 // HTML and XML Serialization
 20 
 21 var getElementType = Xml.getElementType = function(node)
 22 {
 23     if (isElementXUL(node))
 24         return 'xul';
 25     else if (isElementSVG(node))
 26         return 'svg';
 27     else if (isElementMathML(node))
 28         return 'mathml';
 29     else if (isElementXHTML(node))
 30         return 'xhtml';
 31     else if (isElementHTML(node))
 32         return 'html';
 33 }
 34 
 35 var getElementSimpleType = Xml.getElementSimpleType = function(node)
 36 {
 37     if (isElementSVG(node))
 38         return 'svg';
 39     else if (isElementMathML(node))
 40         return 'mathml';
 41     else
 42         return 'html';
 43 }
 44 
 45 var isElementHTML = Xml.isElementHTML = function(node)
 46 {
 47     return node.nodeName == node.nodeName.toUpperCase() && node.namespaceURI == 'http://www.w3.org/1999/xhtml';
 48 }
 49 
 50 var isElementXHTML = Xml.isElementXHTML = function(node)
 51 {
 52     return node.nodeName != node.nodeName.toUpperCase() && node.namespaceURI == 'http://www.w3.org/1999/xhtml';
 53 }
 54 
 55 var isElementMathML = Xml.isElementMathML = function(node)
 56 {
 57     return node.namespaceURI == 'http://www.w3.org/1998/Math/MathML';
 58 }
 59 
 60 var isElementSVG = Xml.isElementSVG = function(node)
 61 {
 62     return node.namespaceURI == 'http://www.w3.org/2000/svg';
 63 }
 64 
 65 var isElementXUL = Xml.isElementXUL = function(node)
 66 {
 67     return node instanceof XULElement;
 68 }
 69 
 70 var getNodeName = Xml.getNodeName = function(node)
 71 {
 72     var name = node.nodeName;
 73     return isElementHTML(node) ? name.toLowerCase() : name;
 74 }
 75 
 76 var getLocalName = Xml.getLocalName = function(node)
 77 {
 78     var name = node.localName;
 79     return isElementHTML(node) ? name.toLowerCase() : name;
 80 }
 81 
 82 // End tags for void elements are forbidden http://wiki.whatwg.org/wiki/HTML_vs._XHTML
 83 Xml.selfClosingTags =
 84 {
 85     "meta": 1,
 86     "link": 1,
 87     "area": 1,
 88     "base": 1,
 89     "col": 1,
 90     "input": 1,
 91     "img": 1,
 92     "br": 1,
 93     "hr": 1,
 94     "param":1,
 95     "embed":1
 96 };
 97 
 98 Xml.isSelfClosing = function(element)
 99 {
100     if (isElementSVG(element) || isElementMathML(element))
101         return true;
102     var tag = element.localName.toLowerCase();
103     return (Xml.selfClosingTags.hasOwnProperty(tag));
104 };
105 
106 // xxxFlorent: could be replaced with outerHTML... if only Firebug.shouldIgnore was not called
107 Xml.getElementHTML = function(element)
108 {
109     function toHTML(elt)
110     {
111         if (elt.nodeType == Node.ELEMENT_NODE)
112         {
113             if (Firebug.shouldIgnore(elt))
114                 return;
115 
116             var nodeName = getNodeName(elt);
117             html.push('<', nodeName);
118 
119             for (var i = 0; i < elt.attributes.length; ++i)
120             {
121                 var attr = elt.attributes[i];
122 
123                 // Hide attributes set by Firebug
124                 // XXX Do we even have any?
125                 if (Str.hasPrefix(attr.localName, "firebug-"))
126                     continue;
127 
128                 // MathML
129                 if (Str.hasPrefix(attr.localName, "-moz-math"))
130                 {
131                     // just hide for now
132                     continue;
133                 }
134 
135                 html.push(' ', attr.name, '="', Str.escapeForElementAttribute(attr.value), '"');
136             }
137 
138             if (elt.firstChild)
139             {
140                 html.push('>');
141 
142                 for (var child = elt.firstChild; child; child = child.nextSibling)
143                     toHTML(child);
144 
145                 html.push('</', nodeName, '>');
146             }
147             else if (isElementSVG(elt) || isElementMathML(elt))
148             {
149                 html.push('/>');
150             }
151             else if (Xml.isSelfClosing(elt))
152             {
153                 html.push((isElementXHTML(elt))?'/>':'>');
154             }
155             else
156             {
157                 html.push('></', nodeName, '>');
158             }
159         }
160         else if (elt.nodeType == Node.TEXT_NODE)
161             html.push(Str.escapeForTextNode(elt.textContent));
162         else if (elt.nodeType == Node.CDATA_SECTION_NODE)
163             html.push('<![CDATA[', elt.nodeValue, ']]>');
164         else if (elt.nodeType == Node.COMMENT_NODE)
165             html.push('<!--', elt.nodeValue, '-->');
166     }
167 
168     var html = [];
169     toHTML(element);
170     return html.join("");
171 };
172 
173 Xml.getElementXML = function(element)
174 {
175     function toXML(elt)
176     {
177         if (elt.nodeType == Node.ELEMENT_NODE)
178         {
179             if (Firebug.shouldIgnore(elt))
180                 return;
181 
182             var nodeName = getNodeName(elt);
183             xml.push('<', nodeName);
184 
185             for (var i = 0; i < elt.attributes.length; ++i)
186             {
187                 var attr = elt.attributes[i];
188 
189                 // Hide attributes set by Firebug
190                 if (Str.hasPrefix(attr.localName, "firebug-"))
191                     continue;
192 
193                 // MathML
194                 if (Str.hasPrefix(attr.localName, "-moz-math"))
195                 {
196                     // just hide for now
197                     continue;
198                 }
199 
200                 xml.push(' ', attr.nodeName, '="', Str.escapeForElementAttribute(attr.nodeValue),'"');
201             }
202 
203             if (elt.firstChild)
204             {
205                 xml.push('>');
206 
207                 for (var child = elt.firstChild; child; child = child.nextSibling)
208                     toXML(child);
209 
210                 xml.push('</', nodeName, '>');
211             }
212             else
213                 xml.push('/>');
214         }
215         else if (elt.nodeType == Node.TEXT_NODE)
216             xml.push(elt.nodeValue);
217         else if (elt.nodeType == Node.CDATA_SECTION_NODE)
218             xml.push('<![CDATA[', elt.nodeValue, ']]>');
219         else if (elt.nodeType == Node.COMMENT_NODE)
220             xml.push('<!--', elt.nodeValue, '-->');
221     }
222 
223     var xml = [];
224     toXML(element);
225     return xml.join("");
226 };
227 
228 // ************************************************************************************************
229 // Whitespace and Entity conversions
230 
231 var domUtils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
232 
233 /**
234  * Returns true if given document is based on a XML and so displaying pretty printed XML elements.
235  */
236 Xml.isXMLPrettyPrint = function(context, win)
237 {
238     if (!context)
239         return;
240 
241     if (context.isXMLPrettyPrintDetected)
242         return context.isXMLPrettyPrint;
243 
244     try
245     {
246         var doc = win ? win.document : context.window.document;
247         if (!doc)
248         {
249             if (FBTrace.DBG_ERRORS)
250                 FBTrace.sysout("lib.isXMLPrettyPrint; NO DOCUMENT", {win:win, context:context});
251             return false;
252         }
253         if (!doc.documentElement)
254             return false;
255 
256         var bindings = domUtils.getBindingURLs(doc.documentElement);
257         for (var i = 0; i < bindings.length; i++)
258         {
259             var bindingURI = bindings.queryElementAt(i, Ci.nsIURI);
260             if (FBTrace.DBG_CSS)
261                 FBTrace.sysout("bindingURL: " + i + " " + bindingURI.resolve(""));
262 
263             context.isXMLPrettyPrintDetected = true;
264             return context.isXMLPrettyPrint = (bindingURI.resolve("") ===
265                 "chrome://global/content/xml/XMLPrettyPrint.xml");
266         }
267     }
268     catch (e)
269     {
270         if (FBTrace.DBG_ERRORS)
271             FBTrace.sysout("xml.isXMLPrettyPrint; EXCEPTION "+e, e);
272     }
273 };
274 
275 // ************************************************************************************************
276 
277 Xml.isVisible = function(elt)
278 {
279     if (Xml.isElementXUL(elt))
280     {
281         //FBTrace.sysout("isVisible elt.offsetWidth: "+elt.offsetWidth+" offsetHeight:"+
282         // elt.offsetHeight+" localName:"+ elt.localName+" nameSpace:"+elt.nameSpaceURI+"\n");
283         return (!elt.hidden && !elt.collapsed);
284     }
285 
286     try
287     {
288         return (!Xml.isElementHTML(elt) && !Xml.isElementXHTML(elt)) ||
289             elt.offsetWidth > 0 ||
290             elt.offsetHeight > 0 ||
291             elt.localName in Xml.invisibleTags;
292     }
293     catch (err)
294     {
295         if (FBTrace.DBG_ERRORS)
296             FBTrace.sysout("lib.isVisible; EXCEPTION " + err, err);
297     }
298 
299     return false;
300 };
301 
302 var invisibleTags = Xml.invisibleTags =
303 {
304     "HTML": 1,
305     "HEAD": 1,
306     "TITLE": 1,
307     "META": 1,
308     "LINK": 1,
309     "STYLE": 1,
310     "SCRIPT": 1,
311     "NOSCRIPT": 1,
312     "BR": 1,
313     "PARAM": 1,
314     "COL": 1,
315 
316     "html": 1,
317     "head": 1,
318     "title": 1,
319     "meta": 1,
320     "link": 1,
321     "style": 1,
322     "script": 1,
323     "noscript": 1,
324     "br": 1,
325     "param": 1,
326     "col": 1,
327     /*
328     "window": 1,
329     "browser": 1,
330     "frame": 1,
331     "tabbrowser": 1,
332     "WINDOW": 1,
333     "BROWSER": 1,
334     "FRAME": 1,
335     "TABBROWSER": 1,
336     */
337 };
338 
339 // ********************************************************************************************* //
340 
341 return Xml;
342 
343 // ********************************************************************************************* //
344 });
345