1 /* See license.txt for terms of usage */ 2 3 define([ 4 "firebug/firebug", 5 "firebug/lib/locale", 6 "firebug/lib/events", 7 "firebug/lib/url", 8 "firebug/chrome/firefox", 9 "firebug/lib/xpcom", 10 "firebug/lib/http", 11 "firebug/lib/string", 12 "firebug/lib/xml" 13 ], 14 function(Firebug, Locale, Events, Url, Firefox, Xpcom, Http, Str, Xml) { 15 16 // ********************************************************************************************* // 17 // Constants 18 19 const Cc = Components.classes; 20 const Ci = Components.interfaces; 21 const Cr = Components.results; 22 23 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // 24 25 const mimeExtensionMap = 26 { 27 "txt": "text/plain", 28 "html": "text/html", 29 "htm": "text/html", 30 "xhtml": "text/html", 31 "xml": "text/xml", 32 "css": "text/css", 33 "js": "application/x-javascript", 34 "jss": "application/x-javascript", 35 "jpg": "image/jpg", 36 "jpeg": "image/jpeg", 37 "gif": "image/gif", 38 "png": "image/png", 39 "bmp": "image/bmp", 40 "swf": "application/x-shockwave-flash", 41 "flv": "video/x-flv", 42 "webm": "video/webm" 43 }; 44 45 const mimeCategoryMap = 46 { 47 "text/plain": "txt", 48 "application/octet-stream": "bin", 49 "text/html": "html", 50 "text/xml": "html", 51 "application/rss+xml": "html", 52 "application/atom+xml": "html", 53 "application/xhtml+xml": "html", 54 "application/mathml+xml": "html", 55 "application/rdf+xml": "html", 56 "text/css": "css", 57 "application/x-javascript": "js", 58 "text/javascript": "js", 59 "application/javascript" : "js", 60 "text/ecmascript": "js", 61 "application/ecmascript" : "js", // RFC4329 62 "image/jpeg": "image", 63 "image/jpg": "image", 64 "image/gif": "image", 65 "image/png": "image", 66 "image/bmp": "image", 67 "application/x-shockwave-flash": "flash", 68 "video/x-flv": "flash", 69 "audio/mpeg3": "media", 70 "audio/x-mpeg-3": "media", 71 "video/mpeg": "media", 72 "video/x-mpeg": "media", 73 "video/webm": "media", 74 "audio/ogg": "media", 75 "application/ogg": "media", 76 "application/x-ogg": "media", 77 "application/x-midi": "media", 78 "audio/midi": "media", 79 "audio/x-mid": "media", 80 "audio/x-midi": "media", 81 "music/crescendo": "media", 82 "audio/wav": "media", 83 "audio/x-wav": "media" 84 }; 85 86 const fileCategories = 87 { 88 "undefined": 1, 89 "html": 1, 90 "css": 1, 91 "js": 1, 92 "xhr": 1, 93 "image": 1, 94 "flash": 1, 95 "media": 1, 96 "txt": 1, 97 "bin": 1 98 }; 99 100 const textFileCategories = 101 { 102 "txt": 1, 103 "html": 1, 104 "xhr": 1, 105 "css": 1, 106 "js": 1 107 }; 108 109 const binaryFileCategories = 110 { 111 "bin": 1, 112 "flash": 1, 113 "media": 1 114 }; 115 116 const binaryCategoryMap = 117 { 118 "image": 1, 119 "flash" : 1 120 }; 121 122 // ********************************************************************************************* // 123 124 var NetUtils = 125 { 126 isXHR: Http.isXHR, // deprecated 127 128 mimeExtensionMap: mimeExtensionMap, 129 mimeCategoryMap: mimeCategoryMap, 130 fileCategories: fileCategories, 131 textFileCategories: textFileCategories, 132 binaryFileCategories: binaryFileCategories, 133 binaryCategoryMap: binaryCategoryMap, 134 135 now: function() 136 { 137 return (new Date()).getTime(); 138 }, 139 140 getFrameLevel: function(win) 141 { 142 var level = 0; 143 for (; win && (win != win.parent) && (win.parent instanceof window.Window); win = win.parent) 144 ++level; 145 return level; 146 }, 147 148 findHeader: function(headers, name) 149 { 150 if (!headers) 151 return null; 152 153 name = name.toLowerCase(); 154 for (var i = 0; i < headers.length; ++i) 155 { 156 var headerName = headers[i].name.toLowerCase(); 157 if (headerName == name) 158 return headers[i].value; 159 } 160 }, 161 162 formatPostText: function(text) 163 { 164 if (text instanceof window.XMLDocument) 165 return Xml.getElementXML(text.documentElement); 166 else 167 return text; 168 }, 169 170 getPostText: function(file, context, noLimit) 171 { 172 if (!file.postText) 173 { 174 file.postText = Http.readPostTextFromRequest(file.request, context); 175 176 if (!file.postText && context) 177 file.postText = Http.readPostTextFromPage(file.href, context); 178 } 179 180 if (!file.postText) 181 return file.postText; 182 183 var limit = Firebug.netDisplayedPostBodyLimit; 184 if (file.postText.length > limit && !noLimit) 185 { 186 return Str.cropString(file.postText, limit, 187 "\n\n... " + Locale.$STR("net.postDataSizeLimitMessage") + " ...\n\n"); 188 } 189 190 return file.postText; 191 }, 192 193 getResponseText: function(file, context) 194 { 195 // The response can be also empty string so, check agains "undefined". 196 return (typeof(file.responseText) != "undefined") ? 197 file.responseText : 198 context.sourceCache.loadText(file.href, file.method, file); 199 }, 200 201 matchesContentType: function(headerValue, contentType) 202 { 203 var contentTypes = (typeof contentType == "string" ? [contentType] : contentType); 204 for (var i = 0; i < contentTypes.length; ++i) 205 { 206 // The header value doesn't have to match the content type exactly; 207 // there can be a charset specified. So, test for a prefix instead. 208 if (Str.hasPrefix(headerValue, contentTypes[i])) 209 return true; 210 } 211 return false; 212 }, 213 214 isURLEncodedRequest: function(file, context) 215 { 216 var text = NetUtils.getPostText(file, context); 217 if (text && Str.hasPrefix(text.toLowerCase(), "content-type: application/x-www-form-urlencoded")) 218 return true; 219 220 var headerValue = NetUtils.findHeader(file.requestHeaders, "content-type"); 221 return (headerValue && 222 NetUtils.matchesContentType(headerValue, "application/x-www-form-urlencoded")); 223 }, 224 225 isMultiPartRequest: function(file, context) 226 { 227 var text = NetUtils.getPostText(file, context); 228 if (text && Str.hasPrefix(text.toLowerCase(), "content-type: multipart/form-data")) 229 return true; 230 return false; 231 }, 232 233 getMimeType: function(mimeType, uri) 234 { 235 if (!mimeType || !(mimeCategoryMap.hasOwnProperty(mimeType))) 236 { 237 var ext = Url.getFileExtension(uri); 238 if (!ext) 239 return mimeType; 240 else 241 { 242 var extMimeType = mimeExtensionMap[ext.toLowerCase()]; 243 return extMimeType ? extMimeType : mimeType; 244 } 245 } 246 else 247 return mimeType; 248 }, 249 250 getDateFromSeconds: function(s) 251 { 252 var d = new Date(); 253 d.setTime(s*1000); 254 return d; 255 }, 256 257 getHttpHeaders: function(request, file, context) 258 { 259 if (!(request instanceof Ci.nsIHttpChannel)) 260 return; 261 262 // xxxHonza: is there any problem to do this in requestedFile method? 263 file.method = request.requestMethod; 264 file.urlParams = Url.parseURLParams(file.href); 265 266 try 267 { 268 file.status = request.responseStatus; 269 } 270 catch (e) { } 271 272 try 273 { 274 file.mimeType = NetUtils.getMimeType(request.contentType, request.name); 275 } 276 catch (e) { } 277 278 try 279 { 280 if (!file.requestHeaders) 281 { 282 var requestHeaders = []; 283 request.visitRequestHeaders({ 284 visitHeader: function(name, value) 285 { 286 requestHeaders.push({name: name, value: value}); 287 } 288 }); 289 file.requestHeaders = requestHeaders; 290 } 291 } 292 catch (e) { } 293 294 try 295 { 296 if (!file.responseHeaders) 297 { 298 var responseHeaders = []; 299 request.visitResponseHeaders({ 300 visitHeader: function(name, value) 301 { 302 responseHeaders.push({name: name, value: value}); 303 } 304 }); 305 file.responseHeaders = responseHeaders; 306 307 if (context) 308 { 309 // Response haeaders are available now, dispatch an event to listeners 310 Events.dispatch(Firebug.NetMonitor.fbListeners, "onResponseHeaders", 311 [context, file]); 312 } 313 } 314 } 315 catch (e) { } 316 }, 317 318 getFileCategory: function(file) 319 { 320 if (file.category) 321 { 322 if (FBTrace.DBG_NET) 323 FBTrace.sysout("net.getFileCategory; current: " + file.category + " for: " + 324 file.href, file); 325 return file.category; 326 } 327 328 if (file.isXHR) 329 { 330 if (FBTrace.DBG_NET) 331 FBTrace.sysout("net.getFileCategory; XHR for: " + file.href, file); 332 return file.category = "xhr"; 333 } 334 335 if (!file.mimeType) 336 { 337 var ext = Url.getFileExtension(file.href); 338 if (ext) 339 file.mimeType = mimeExtensionMap[ext.toLowerCase()]; 340 } 341 342 /*if (FBTrace.DBG_NET) 343 FBTrace.sysout("net.getFileCategory; " + mimeCategoryMap[file.mimeType] + 344 ", mimeType: " + file.mimeType + " for: " + file.href, file);*/ 345 346 if (!file.mimeType) 347 return ""; 348 349 // Solve cases when charset is also specified, eg "text/html; charset=UTF-8". 350 var mimeType = file.mimeType; 351 if (mimeType) 352 mimeType = mimeType.split(";")[0]; 353 354 return (file.category = mimeCategoryMap[mimeType]); 355 }, 356 357 getPageTitle: function(context) 358 { 359 var title = context.getTitle(); 360 return (title) ? title : context.getName(); 361 }, 362 363 getBlockingEndTime: function(file) 364 { 365 //var blockingEnd = (file.sendingTime > file.startTime) ? file.sendingTime : file.waitingForTime; 366 367 if (file.resolveStarted && file.connectStarted) 368 return file.resolvingTime; 369 370 if (file.connectStarted) 371 return file.connectingTime; 372 373 if (file.sendStarted) 374 return file.sendingTime; 375 376 return file.waitingForTime; 377 }, 378 379 getTimeLabelFromMs: function(ms) 380 { 381 var time = new Date(); 382 time.setTime(ms); 383 return this.getTimeLabel(time); 384 }, 385 386 getTimeLabel: function(date) 387 { 388 var m = date.getMinutes() + ""; 389 var s = date.getSeconds() + ""; 390 var ms = date.getMilliseconds() + ""; 391 return "[" + ((m.length > 1) ? m : "0" + m) + ":" + 392 ((s.length > 1) ? s : "0" + s) + "." + 393 ((ms.length > 2) ? ms : ((ms.length > 1) ? "0" + ms : "00" + ms)) + "]"; 394 }, 395 396 openResponseInTab: function(file) 397 { 398 try 399 { 400 var response = NetUtils.getResponseText(file, this.context); 401 var inputStream = Http.getInputStreamFromString(response); 402 var stream = Xpcom.CCIN("@mozilla.org/binaryinputstream;1", "nsIBinaryInputStream"); 403 stream.setInputStream(inputStream); 404 var encodedResponse = btoa(stream.readBytes(stream.available())); 405 var dataURI = "data:" + file.request.contentType + ";base64," + encodedResponse; 406 407 var tabBrowser = Firefox.getTabBrowser(); 408 tabBrowser.selectedTab = tabBrowser.addTab(dataURI); 409 } 410 catch (err) 411 { 412 if (FBTrace.DBG_ERRORS) 413 FBTrace.sysout("net.openResponseInTab EXCEPTION", err); 414 } 415 }, 416 417 traceRequestTiming: function(msg, file) 418 { 419 var blockingEnd = this.getBlockingEndTime(file); 420 421 //Helper log for debugging timing problems. 422 var timeLog = {}; 423 timeLog.startTime = this.getTimeLabelFromMs(file.startTime); 424 timeLog.resolvingTime = this.getTimeLabelFromMs(file.resolvingTime); 425 timeLog.connectingTime = this.getTimeLabelFromMs(file.connectingTime); 426 timeLog.connectedTime = this.getTimeLabelFromMs(file.connectedTime); 427 timeLog.blockingEnd = this.getTimeLabelFromMs(blockingEnd); 428 timeLog.sendingTime = this.getTimeLabelFromMs(file.sendingTime); 429 timeLog.waitingForTime = this.getTimeLabelFromMs(file.waitingForTime); 430 timeLog.respondedTime = this.getTimeLabelFromMs(file.respondedTime); 431 timeLog.endTime = this.getTimeLabelFromMs(file.endTime); 432 433 if (file.request instanceof Ci.nsITimedChannel) 434 { 435 timeLog.startTime += " - " + this.getTimeLabelFromMs(file.request.channelCreationTime/1000); 436 timeLog.startTime += this.getTimeLabelFromMs(file.request.asyncOpenTime/1000); 437 timeLog.resolvingTime += " - " + this.getTimeLabelFromMs(file.request.domainLookupStartTime/1000); 438 timeLog.resolvingTime += this.getTimeLabelFromMs(file.request.domainLookupEndTime/1000); 439 timeLog.connectingTime += " - " + this.getTimeLabelFromMs(file.request.connectStartTime/1000); 440 timeLog.connectedTime += " - " + this.getTimeLabelFromMs(file.request.connectEndTime/1000); 441 timeLog.sendingTime += " - " + this.getTimeLabelFromMs(file.request.requestStartTime/1000); 442 timeLog.respondedTime += " - " + this.getTimeLabelFromMs(file.request.responseStartTime/1000); 443 timeLog.endTime += " - " + this.getTimeLabelFromMs(file.request.responseEndTime/1000); 444 timeLog.cacheReadStartTime = this.getTimeLabelFromMs(file.request.cacheReadStartTime/1000); 445 timeLog.cacheReadEndTime = this.getTimeLabelFromMs(file.request.cacheReadEndTime/1000); 446 timeLog.timingEnabled = file.request.timingEnabled; 447 } 448 449 FBTrace.sysout(msg + " " + file.href, timeLog); 450 } 451 }; 452 453 // ********************************************************************************************* // 454 // Registration 455 456 return NetUtils; 457 458 // ********************************************************************************************* // 459 }); 460