1 /* See license.txt for terms of usage */
  2 
  3 define([
  4     "firebug/lib/xpcom",
  5     "firebug/lib/trace",
  6     "firebug/lib/deprecated",
  7     "firebug/js/stackFrame",
  8     "firebug/lib/string"
  9 ],
 10 function(Xpcom, FBTrace, Deprecated, StackFrame, Str) {
 11 
 12 // ********************************************************************************************* //
 13 // Constants
 14 
 15 const Cc = Components.classes;
 16 const Ci = Components.interfaces;
 17 const Cr = Components.results;
 18 
 19 const NS_SEEK_SET = Ci.nsISeekableStream.NS_SEEK_SET;
 20 const ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
 21 
 22 var Http = {};
 23 
 24 // ********************************************************************************************* //
 25 // Module Implementation
 26 
 27 Http.readFromStream = function(stream, charset, noClose)
 28 {
 29     // Causes a memory leak (see https://bugzilla.mozilla.org/show_bug.cgi?id=699801)
 30     //var sis = Cc["@mozilla.org/binaryinputstream;1"].createInstance(Ci.nsIBinaryInputStream);
 31     //sis.setInputStream(stream);
 32 
 33     var sis = Cc["@mozilla.org/scriptableinputstream;1"].
 34         createInstance(Ci.nsIScriptableInputStream);
 35     sis.init(stream);
 36 
 37     var segments = [];
 38     for (var count = stream.available(); count; count = stream.available())
 39         segments.push(sis.readBytes(count));
 40 
 41     if (!noClose)
 42         sis.close();
 43 
 44     var text = segments.join("");
 45 
 46     try
 47     {
 48         return Str.convertToUnicode(text, charset);
 49     }
 50     catch (err)
 51     {
 52         if (FBTrace.DBG_ERRORS)
 53             FBTrace.sysout("http.readFromStream EXCEPTION charset: " + charset, err);
 54     }
 55 
 56     return text;
 57 };
 58 
 59 Http.readPostTextFromPage = function(url, context)
 60 {
 61     if (url == context.browser.contentWindow.location.href)
 62     {
 63         try
 64         {
 65             var webNav = context.browser.webNavigation;
 66             var descriptor = (webNav instanceof Ci.nsIWebPageDescriptor) ?
 67                 webNav.currentDescriptor : null;
 68 
 69             if (!(descriptor instanceof Ci.nsISHEntry))
 70                 return;
 71 
 72             var entry = descriptor;
 73             if (entry && entry.postData)
 74             {
 75                 if (!(entry.postData instanceof Ci.nsISeekableStream))
 76                     return;
 77 
 78                 var postStream = entry.postData;
 79                 postStream.seek(NS_SEEK_SET, 0);
 80 
 81                 var charset = context.window.document.characterSet;
 82                 return Http.readFromStream(postStream, charset, true);
 83             }
 84          }
 85          catch (exc)
 86          {
 87              if (FBTrace.DBG_ERRORS)
 88                 FBTrace.sysout("http.readPostText FAILS, url:"+url, exc);
 89          }
 90      }
 91 };
 92 
 93 Http.getResource = function(aURL)
 94 {
 95     try
 96     {
 97         var channel = ioService.newChannel(aURL, null, null);
 98         var input = channel.open();
 99 
100         return Http.readFromStream(input);
101     }
102     catch (e)
103     {
104         if (FBTrace.DBG_ERRORS)
105             FBTrace.sysout("lib.getResource FAILS for \'"+aURL+"\'", e);
106     }
107 };
108 
109 /**
110  * Returns a posted data for specified request object. The return value might contain
111  * headers (if request.uploadStreamHasHeaders is set to true). You can remove these
112  * headers using {@link Http.removeHeadersFromPostText}
113  * 
114  * @param {Object} request The request object
115  * @param {Object} context Current Firebug context (to get charset of the current document)
116  */
117 Http.readPostTextFromRequest = function(request, context)
118 {
119     try
120     {
121         var is = (request instanceof Ci.nsIUploadChannel) ? request.uploadStream : null;
122         if (is)
123         {
124             if (!(is instanceof Ci.nsISeekableStream))
125                 return;
126 
127             var ss = is;
128             var prevOffset;
129             if (ss)
130             {
131                 prevOffset = ss.tell();
132                 ss.seek(NS_SEEK_SET, 0);
133             }
134 
135             // Read data from the stream..
136             var charset = (context && context.window) ? context.window.document.characterSet : null;
137             var text = Http.readFromStream(is, charset, true);
138 
139             // Seek locks the file, so seek to the beginning only if necko hasn't read it yet,
140             // since necko doesn't seek to 0 before reading (at least not till 459384 is fixed).
141             if (ss && prevOffset == 0)
142                 ss.seek(NS_SEEK_SET, 0);
143 
144             return text;
145         }
146     }
147     catch(exc)
148     {
149         if (FBTrace.DBG_ERRORS)
150             FBTrace.sysout("http.readPostTextFromRequest FAILS ", exc);
151     }
152 
153     return null;
154 };
155 
156 /**
157  * Removes headers from post body, https://bugzilla.mozilla.org/show_bug.cgi?id=649338
158  * 
159  * @param {Object} request Channel implementing nsIUploadChannel2
160  * @param {Object} text Extracted text (can include headers at the beginning).
161  */
162 Http.removeHeadersFromPostText = function(request, text)
163 {
164     if (!text)
165         return text;
166 
167     if (typeof(Ci.nsIUploadChannel2) == "undefined")
168         return text;
169 
170     if (!(request instanceof Ci.nsIUploadChannel2))
171         return text;
172 
173     if (!request.uploadStreamHasHeaders)
174         return text;
175 
176     var headerSeparator = "\r\n\r\n";
177     var index = text.indexOf(headerSeparator);
178     if (index == -1)
179         return text;
180 
181     return text.substring(index + headerSeparator.length);
182 }
183 
184 /**
185  * Returns an array of headers from posted data (appended by Firefox)
186  * 
187  * @param {Object} request Channel implementing nsIUploadChannel2
188  * @param {Object} text Posted data from the channel object.
189  */
190 Http.getHeadersFromPostText = function(request, text)
191 {
192     var headers = [];
193     if (!text)
194         return headers;
195 
196     if (typeof(Ci.nsIUploadChannel2) == "undefined")
197         return headers;
198 
199     if (!(request instanceof Ci.nsIUploadChannel2))
200         return headers;
201 
202     if (!request.uploadStreamHasHeaders)
203         return headers;
204 
205     var headerSeparator = "\r\n\r\n";
206     var index = text.indexOf(headerSeparator);
207     if (index == -1)
208         return headers;
209 
210     var text = text.substring(0, index);
211     var lines = Str.splitLines(text);
212 
213     for (var i=0; i<lines.length; i++)
214     {
215         var header = lines[i].split(":");
216         if (header.length != 2)
217             continue;
218 
219         headers.push({
220             name: Str.trim(header[0]),
221             value: Str.trim(header[1]),
222         });
223     }
224 
225     return headers;
226 }
227 
228 Http.getInputStreamFromString = function(dataString)
229 {
230     var stringStream = Cc["@mozilla.org/io/string-input-stream;1"].
231         createInstance(Ci.nsIStringInputStream);
232 
233     if ("data" in stringStream) // Gecko 1.9 or newer
234         stringStream.data = dataString;
235     else // 1.8 or older
236         stringStream.setData(dataString, dataString.length);
237 
238     return stringStream;
239 };
240 
241 Http.getWindowForRequest = function(request)
242 {
243     var loadContext = Http.getRequestLoadContext(request);
244     try
245     {
246         if (loadContext)
247             return loadContext.associatedWindow;
248     }
249     catch (ex)
250     {
251     }
252 
253     return null;
254 };
255 
256 Http.getRequestLoadContext = function(request)
257 {
258     try
259     {
260         if (request && request.notificationCallbacks)
261         {
262             StackFrame.suspendShowStackTrace();
263             return request.notificationCallbacks.getInterface(Ci.nsILoadContext);
264         }
265     }
266     catch (exc)
267     {
268     }
269     finally
270     {
271         StackFrame.resumeShowStackTrace();
272     }
273 
274     try
275     {
276         if (request && request.loadGroup && request.loadGroup.notificationCallbacks)
277         {
278             StackFrame.suspendShowStackTrace();
279             return request.loadGroup.notificationCallbacks.getInterface(Ci.nsILoadContext);
280         }
281     }
282     catch (exc)
283     {
284     }
285     finally
286     {
287         StackFrame.resumeShowStackTrace();
288     }
289 
290     return null;
291 };
292 
293 Http.getRequestWebProgress = Deprecated.deprecated("Use getRequestLoadContext function",
294     Http.getRequestLoadContext);
295 
296 // ********************************************************************************************* //
297 // HTTP Channel Fields
298 
299 Http.safeGetRequestName = function(request)
300 {
301     try
302     {
303         return request.name;
304     }
305     catch (exc)
306     {
307     }
308 
309     return null;
310 }
311 
312 Http.safeGetURI = function(browser)
313 {
314     try
315     {
316         return browser.currentURI;
317     }
318     catch (exc)
319     {
320     }
321 
322     return null;
323 }
324 
325 Http.safeGetContentType = function(request)
326 {
327     try
328     {
329         return new String(request.contentType).toLowerCase();
330     }
331     catch (err)
332     {
333     }
334 
335     return null;
336 }
337 
338 Http.safeGetXHRResponseText = function(xhr)
339 {
340     try
341     {
342         return xhr.responseText;
343     }
344     catch (err)
345     {
346     }
347 
348     return null;
349 }
350 
351 // ********************************************************************************************* //
352 // IP Adress and port number (Requires Gecko 5).
353 
354 Http.safeGetLocalAddress = function(request)
355 {
356     try
357     {
358         if (request instanceof Ci.nsIHttpChannelInternal)
359             return request.localAddress;
360     }
361     catch (err)
362     {
363     }
364     return null;
365 }
366 
367 Http.safeGetLocalPort = function(request)
368 {
369     try
370     {
371         if (request instanceof Ci.nsIHttpChannelInternal)
372             return request.localPort;
373     }
374     catch (err)
375     {
376     }
377     return null;
378 }
379 
380 Http.safeGetRemoteAddress = function(request)
381 {
382     try
383     {
384         if (request instanceof Ci.nsIHttpChannelInternal)
385             return request.remoteAddress;
386     }
387     catch (err)
388     {
389     }
390     return null;
391 }
392 
393 Http.safeGetRemotePort = function(request)
394 {
395     try
396     {
397         if (request instanceof Ci.nsIHttpChannelInternal)
398             return request.remotePort;
399     }
400     catch (err)
401     {
402     }
403     return null;
404 }
405 
406 // ********************************************************************************************* //
407 // XHR
408 
409 Http.isXHR = function(request)
410 {
411     try
412     {
413         var callbacks = request.notificationCallbacks;
414         StackFrame.suspendShowStackTrace();
415         var xhrRequest = callbacks ? callbacks.getInterface(Ci.nsIXMLHttpRequest) : null;
416         return (xhrRequest != null);
417     }
418     catch (exc)
419     {
420     }
421     finally
422     {
423         StackFrame.resumeShowStackTrace();
424     }
425 
426     return false;
427 },
428 
429 // ********************************************************************************************* //
430 // Conversions
431 
432 Http.convertToUnicode = Deprecated.deprecated("Function moved to 'firebug/lib/string' module",
433     Str.convertToUnicode);
434 
435 Http.convertFromUnicode = Deprecated.deprecated("Function moved to 'firebug/lib/string' module",
436     Str.convertFromUnicode);
437 
438 // ************************************************************************************************
439 // Network Tracing
440 
441 Http.getStateDescription = function(flag)
442 {
443     var state = [];
444     var nsIWebProgressListener = Ci.nsIWebProgressListener;
445     if (flag & nsIWebProgressListener.STATE_START) state.push("STATE_START");
446     else if (flag & nsIWebProgressListener.STATE_REDIRECTING) state.push("STATE_REDIRECTING");
447     else if (flag & nsIWebProgressListener.STATE_TRANSFERRING) state.push("STATE_TRANSFERRING");
448     else if (flag & nsIWebProgressListener.STATE_NEGOTIATING) state.push("STATE_NEGOTIATING");
449     else if (flag & nsIWebProgressListener.STATE_STOP) state.push("STATE_STOP");
450 
451     if (flag & nsIWebProgressListener.STATE_IS_REQUEST) state.push("STATE_IS_REQUEST");
452     if (flag & nsIWebProgressListener.STATE_IS_DOCUMENT) state.push("STATE_IS_DOCUMENT");
453     if (flag & nsIWebProgressListener.STATE_IS_NETWORK) state.push("STATE_IS_NETWORK");
454     if (flag & nsIWebProgressListener.STATE_IS_WINDOW) state.push("STATE_IS_WINDOW");
455     if (flag & nsIWebProgressListener.STATE_RESTORING) state.push("STATE_RESTORING");
456     if (flag & nsIWebProgressListener.STATE_IS_INSECURE) state.push("STATE_IS_INSECURE");
457     if (flag & nsIWebProgressListener.STATE_IS_BROKEN) state.push("STATE_IS_BROKEN");
458     if (flag & nsIWebProgressListener.STATE_IS_SECURE) state.push("STATE_IS_SECURE");
459     if (flag & nsIWebProgressListener.STATE_SECURE_HIGH) state.push("STATE_SECURE_HIGH");
460     if (flag & nsIWebProgressListener.STATE_SECURE_MED) state.push("STATE_SECURE_MED");
461     if (flag & nsIWebProgressListener.STATE_SECURE_LOW) state.push("STATE_SECURE_LOW");
462 
463     return state.join(", ");
464 };
465 
466 Http.getStatusDescription = function(status)
467 {
468     var nsISocketTransport = Ci.nsISocketTransport;
469     var nsITransport = Ci.nsITransport;
470 
471     if (status == nsISocketTransport.STATUS_RESOLVING) return "STATUS_RESOLVING";
472     if (status == nsISocketTransport.STATUS_CONNECTING_TO) return "STATUS_CONNECTING_TO";
473     if (status == nsISocketTransport.STATUS_CONNECTED_TO) return "STATUS_CONNECTED_TO";
474     if (status == nsISocketTransport.STATUS_SENDING_TO) return "STATUS_SENDING_TO";
475     if (status == nsISocketTransport.STATUS_WAITING_FOR) return "STATUS_WAITING_FOR";
476     if (status == nsISocketTransport.STATUS_RECEIVING_FROM) return "STATUS_RECEIVING_FROM";
477     if (status == nsITransport.STATUS_READING) return "STATUS_READING";
478     if (status == nsITransport.STATUS_WRITING) return "STATUS_WRITING";
479 };
480 
481 Http.getLoadFlagsDescription = function(loadFlags)
482 {
483     var flags = [];
484     var nsIChannel = Ci.nsIChannel;
485     var nsICachingChannel = Ci.nsICachingChannel;
486 
487     if (loadFlags & nsIChannel.LOAD_DOCUMENT_URI) flags.push("LOAD_DOCUMENT_URI");
488     if (loadFlags & nsIChannel.LOAD_RETARGETED_DOCUMENT_URI) flags.push("LOAD_RETARGETED_DOCUMENT_URI");
489     if (loadFlags & nsIChannel.LOAD_REPLACE) flags.push("LOAD_REPLACE");
490     if (loadFlags & nsIChannel.LOAD_INITIAL_DOCUMENT_URI) flags.push("LOAD_INITIAL_DOCUMENT_URI");
491     if (loadFlags & nsIChannel.LOAD_TARGETED) flags.push("LOAD_TARGETED");
492     if (loadFlags & nsIChannel.LOAD_CALL_CONTENT_SNIFFERS) flags.push("LOAD_CALL_CONTENT_SNIFFERS");
493     if (loadFlags & nsICachingChannel.LOAD_NO_NETWORK_IO) flags.push("LOAD_NO_NETWORK_IO");
494     if (loadFlags & nsICachingChannel.LOAD_CHECK_OFFLINE_CACHE) flags.push("LOAD_CHECK_OFFLINE_CACHE");
495     if (loadFlags & nsICachingChannel.LOAD_BYPASS_LOCAL_CACHE) flags.push("LOAD_BYPASS_LOCAL_CACHE");
496     if (loadFlags & nsICachingChannel.LOAD_BYPASS_LOCAL_CACHE_IF_BUSY) flags.push("LOAD_BYPASS_LOCAL_CACHE_IF_BUSY");
497     if (loadFlags & nsICachingChannel.LOAD_ONLY_FROM_CACHE) flags.push("LOAD_ONLY_FROM_CACHE");
498     if (loadFlags & nsICachingChannel.LOAD_ONLY_IF_MODIFIED) flags.push("LOAD_ONLY_IF_MODIFIED");
499 
500     return flags.join(", ");
501 };
502 
503 // ********************************************************************************************* //
504 
505 Http.BaseProgressListener =
506 {
507     QueryInterface : function(iid)
508     {
509         if (iid.equals(Ci.nsIWebProgressListener) ||
510             iid.equals(Ci.nsISupportsWeakReference) ||
511             iid.equals(Ci.nsISupports))
512         {
513             return this;
514         }
515 
516         throw Components.results.NS_NOINTERFACE;
517     },
518 
519     stateIsRequest: false,
520     onLocationChange: function() {},
521     onStateChange : function() {},
522     onProgressChange : function() {},
523     onStatusChange : function() {},
524     onSecurityChange : function() {},
525     onLinkIconAvailable : function() {}
526 };
527 
528 // ********************************************************************************************* //
529 // Registration
530 
531 return Http;
532 
533 // ********************************************************************************************* //
534 });
535