Raymond Camden's Blog Rss

Bug with CFDUMP/output, impacts XML/JSON services

3

Posted in ColdFusion | Posted on 01-27-2010 | 2,661 views

While working on a ColdFusion Builder extension (no, not that one) I ran into a bug that caused the response dialog to constantly return an 'invalid XML' error. An Adobe engineer discovered that the issue was with the cfdump tag I had in the extension code, but didn't explain why that was the cause. I did a quick test and saw what the problem was.

First, consider the following simple template:

view plain print about
1-<cfdump var="#url#" output="/Users/ray/Desktop/dump.html" format="html">-
2
3done

As you can see, I've got a cfdump that is directed to the file system. This is a handy trick when you are working with CFM files that are Ajax bound, or in the case of ColdFusion Builder, happen between the IDE and the server. When you view this in the browser, you should see -- and done, right? (With some whitespace of course.) However, this is what you get:

view plain print about
1- <style>
2
3
4    table.cfdump_wddx,
5    table.cfdump_xml,
6    table.cfdump_struct,
7    table.cfdump_varundefined,
8    table.cfdump_array,
9    table.cfdump_query,
10    table.cfdump_cfc,
11    table.cfdump_object,
12    table.cfdump_binary,
13    table.cfdump_udf,
14    table.cfdump_udfbody,
15    table.cfdump_udfarguments {
16        font-size: xx-small;
17        font-family: verdana, arial, helvetica, sans-serif;
18        cell-spacing: 2px;
19    }
20
21    table.cfdump_wddx th,
22    table.cfdump_xml th,
23    table.cfdump_struct th,
24    table.cfdump_varundefined th,
25    table.cfdump_array th,
26    table.cfdump_query th,
27    table.cfdump_cfc th,
28    table.cfdump_object th,
29    table.cfdump_binary th,
30    table.cfdump_udf th,
31    table.cfdump_udfbody th,
32    table.cfdump_udfarguments th {
33        text-align: left;
34        color: white;
35        padding: 5px;
36    }
37
38    table.cfdump_wddx td,
39    table.cfdump_xml td,
40    table.cfdump_struct td,
41    table.cfdump_varundefined td,
42    table.cfdump_array td,
43    table.cfdump_query td,
44    table.cfdump_cfc td,
45    table.cfdump_object td,
46    table.cfdump_binary td,
47    table.cfdump_udf td,
48    table.cfdump_udfbody td,
49    table.cfdump_udfarguments td {
50        padding: 3px;
51        background-color: #ffffff;
52        vertical-align : top;
53    }
54
55    table.cfdump_wddx {
56        background-color: #000000;
57    }
58    table.cfdump_wddx th.wddx {
59        background-color: #444444;
60    }
61
62
63    table.cfdump_xml {
64        background-color: #888888;
65    }
66    table.cfdump_xml th.xml {
67        background-color: #aaaaaa;
68    }
69    table.cfdump_xml td.xml {
70        background-color: #dddddd;
71    }
72
73    table.cfdump_struct {
74        background-color: #0000cc ;
75    }
76    table.cfdump_struct th.struct {
77        background-color: #4444cc ;
78    }
79    table.cfdump_struct td.struct {
80        background-color: #ccddff;
81    }
82
83    table.cfdump_varundefined {
84        background-color: #CC3300 ;
85    }
86    table.cfdump_varundefined th.varundefined {
87        background-color: #CC3300 ;
88    }
89    table.cfdump_varundefined td.varundefined {
90        background-color: #ccddff;
91    }
92
93    table.cfdump_array {
94        background-color: #006600 ;
95    }
96    table.cfdump_array th.array {
97        background-color: #009900 ;
98    }
99    table.cfdump_array td.array {
100        background-color: #ccffcc ;
101    }
102
103    table.cfdump_query {
104        background-color: #884488 ;
105    }
106    table.cfdump_query th.query {
107        background-color: #aa66aa ;
108    }
109    table.cfdump_query td.query {
110        background-color: #ffddff ;
111    }
112
113
114    table.cfdump_cfc {
115        background-color: #ff0000;
116    }
117    table.cfdump_cfc th.cfc{
118        background-color: #ff4444;
119    }
120    table.cfdump_cfc td.cfc {
121        background-color: #ffcccc;
122    }
123
124
125    table.cfdump_object {
126        background-color : #ff0000;
127    }
128    table.cfdump_object th.object{
129        background-color: #ff4444;
130    }
131
132    table.cfdump_binary {
133        background-color : #eebb00;
134    }
135    table.cfdump_binary th.binary {
136        background-color: #ffcc44;
137    }
138    table.cfdump_binary td {
139        font-size: x-small;
140    }
141    table.cfdump_udf {
142        background-color: #aa4400;
143    }
144    table.cfdump_udf th.udf {
145        background-color: #cc6600;
146    }
147    table.cfdump_udfarguments {
148        background-color: #dddddd;
149        cell-spacing: 3;
150    }
151    table.cfdump_udfarguments th {
152        background-color: #eeeeee;
153        color: #000000;
154    }
155
156</style> <script language="javascript">
157
158
159// for queries we have more than one td element to collapse/expand
160    var expand = "open";
161
162    dump = function( obj ) {
163        var out = "" ;
164        if ( typeof obj == "object" ) {
165            for ( key in obj ) {
166                if ( typeof obj[key] != "function" ) out += key + ': ' + obj[key] + '<br>' ;
167            }
168        }
169    }
170
171
172    cfdump_toggleRow = function(source) {
173        //target is the right cell
174        if(document.all) target = source.parentElement.cells[1];
175        else {
176            var element = null;
177            var vLen = source.parentNode.childNodes.length;
178            for(var i=vLen-1;i>
0;i--){
179                if(source.parentNode.childNodes[i].nodeType == 1){
180                    element = source.parentNode.childNodes[i];
181                    break;
182                }
183            }
184            if(element == null)
185                target = source.parentNode.lastChild;
186            else
187                target = element;
188        }
189        //target = source.parentNode.lastChild ;
190        cfdump_toggleTarget( target, cfdump_toggleSource( source ) ) ;
191    }
192
193    cfdump_toggleXmlDoc = function(source) {
194
195        var caption = source.innerHTML.split( ' [' ) ;
196
197        // toggle source (header)
198        if ( source.style.fontStyle == 'italic' ) {
199            // closed -> short
200            source.style.fontStyle = 'normal' ;
201            source.innerHTML = caption[0] + ' [short version]' ;
202            source.title = 'click to maximize' ;
203            switchLongToState = 'closed' ;
204            switchShortToState = 'open' ;
205        } else if ( source.innerHTML.indexOf('[short version]') != -1 ) {
206            // short -> full
207            source.innerHTML = caption[0] + ' [long version]' ;
208            source.title = 'click to collapse' ;
209            switchLongToState = 'open' ;
210            switchShortToState = 'closed' ;
211        } else {
212            // full -> closed
213            source.style.fontStyle = 'italic' ;
214            source.title = 'click to expand' ;
215            source.innerHTML = caption[0] ;
216            switchLongToState = 'closed' ;
217            switchShortToState = 'closed' ;
218        }
219
220        // Toggle the target (everething below the header row).
221        // First two rows are XMLComment and XMLRoot - they are part
222        // of the long dump, the rest are direct children - part of the
223        // short dump
224        if(document.all) {
225            var table = source.parentElement.parentElement ;
226            for ( var i = 1; i < table.rows.length; i++ ) {
227                target = table.rows[i] ;
228                if ( i < 3 ) cfdump_toggleTarget( target, switchLongToState ) ;
229                else cfdump_toggleTarget( target, switchShortToState ) ;
230            }
231        }
232        else {
233            var table = source.parentNode.parentNode ;
234            var row = 1;
235            for ( var i = 1; i < table.childNodes.length; i++ ) {
236                target = table.childNodes[i] ;
237                if( target.style ) {
238                    if ( row < 3 ) {
239                        cfdump_toggleTarget( target, switchLongToState ) ;
240                    } else {
241                        cfdump_toggleTarget( target, switchShortToState ) ;
242                    }
243                    row++;
244                }
245            }
246        }
247    }
248
249    cfdump_toggleTable = function(source) {
250
251        var switchToState = cfdump_toggleSource( source ) ;
252        if(document.all) {
253            var table = source.parentElement.parentElement ;
254            for ( var i = 1; i < table.rows.length; i++ ) {
255                target = table.rows[i] ;
256                cfdump_toggleTarget( target, switchToState ) ;
257            }
258        }
259        else {
260            var table = source.parentNode.parentNode ;
261            for ( var i = 1; i < table.childNodes.length; i++ ) {
262                target = table.childNodes[i] ;
263                if(target.style) {
264                    cfdump_toggleTarget( target, switchToState ) ;
265                }
266            }
267        }
268    }
269
270    cfdump_toggleSource = function( source ) {
271        if ( source.style.fontStyle == 'italic' || source.style.fontStyle == null) {
272            source.style.fontStyle = 'normal' ;
273            source.title = 'click to collapse' ;
274            return 'open' ;
275        } else {
276            source.style.fontStyle = 'italic' ;
277            source.title = 'click to expand' ;
278            return 'closed' ;
279        }
280    }
281
282    cfdump_toggleTarget = function( target, switchToState ) {
283        if ( switchToState == 'open' )    target.style.display = '' ;
284        else target.style.display = 'none' ;
285    }
286
287    // collapse all td elements for queries
288    cfdump_toggleRow_qry = function(source) {
289        expand = (source.title == "click to collapse") ? "closed" : "open";
290        if(document.all) {
291            var nbrChildren = source.parentElement.cells.length;
292            if(nbrChildren >
1){
293                for(i=nbrChildren-1;i>
0;i--){
294                    target = source.parentElement.cells[i];
295                    cfdump_toggleTarget( target,expand ) ;
296                    cfdump_toggleSource_qry(source);
297                }
298            }
299            else {
300                //target is the right cell
301                target = source.parentElement.cells[1];
302                cfdump_toggleTarget( target, cfdump_toggleSource( source ) ) ;
303            }
304        }
305        else{
306            var target = null;
307            var vLen = source.parentNode.childNodes.length;
308            for(var i=vLen-1;i>
1;i--){
309                if(source.parentNode.childNodes[i].nodeType == 1){
310                    target = source.parentNode.childNodes[i];
311                    cfdump_toggleTarget( target,expand );
312                    cfdump_toggleSource_qry(source);
313                }
314            }
315            if(target == null){
316                //target is the last cell
317                target = source.parentNode.lastChild;
318                cfdump_toggleTarget( target, cfdump_toggleSource( source ) ) ;
319            }
320        }
321    }
322
323    cfdump_toggleSource_qry = function(source) {
324        if(expand == "closed"){
325            source.title = "click to expand";
326            source.style.fontStyle = "italic";
327        }
328        else{
329            source.title = "click to collapse";
330            source.style.fontStyle = "normal";
331        }
332    }
333
334</script> -
335
336done

As you can see, the CSS and JavaScript used by cfdump "leaked" out into the page result. It is also correctly stored in the HTML file so that part isn't broken. This only involves the HTML format of the dump. If you switch the format to text, then nothing is output (except one white space character).

If you want to make use of cfdump like this and are building anything that is supposed to return only XML or JSON, then you can wrap your dump in cfsilent tags. It doesn't impact the output to the file and it correctly hides the CSS/JavaScript.

I reported this as bug 81819. Please vote for it!

Comments

[Add Comment] [Subscribe to Comments]

Wow, that seems like a pretty crazy problem. The outputting to a file was added in CF8. I wonder how many times I've used this for debugging without noting this problem (if this problem even existed in CF8). Good catch. Consider it voted for.
a bit OT,

table.cfdump_udfarguments {
background-color: #dddddd;
cell-spacing: 3;
}


cell-spacing?
That's invalid CSS, right? Just log a bug report for it.