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 const Cc = Components.classes; 13 const Ci = Components.interfaces; 14 15 const ioService = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService); 16 17 // ********************************************************************************************* // 18 // Implementation 19 20 var Url = {}; 21 22 // ************************************************************************************************ 23 // Regular expressions 24 25 Url.reCSS = /\.css$/; 26 Url.reJavascript = /\s*javascript:\s*(.*)/; 27 Url.reFile = /file:\/\/([^\/]*)\//; 28 Url.reChrome = /chrome:\/\/([^\/]*)\//; 29 Url.reDataURL = /data:text\/javascript;fileName=([^;]*);baseLineNumber=(\d*?),((?:.*?%0A)|(?:.*))/g; 30 31 // ************************************************************************************************ 32 // URLs 33 34 Url.getFileName = function(url) 35 { 36 var split = Url.splitURLBase(url); 37 return split.name; 38 }; 39 40 Url.getProtocol = function(url) 41 { 42 var split = Url.splitURLBase(url); 43 return split.protocol; 44 }; 45 46 Url.splitURLBase = function(url) 47 { 48 if (Url.isDataURL(url)) 49 return Url.splitDataURL(url); 50 return Url.splitURLTrue(url); 51 }; 52 53 Url.splitDataURL = function(url) 54 { 55 if (!Str.hasPrefix(url, "data:")) 56 return false; // the first 5 chars must be 'data:' 57 58 var point = url.indexOf(",", 5); 59 if (point < 5) 60 return false; // syntax error 61 62 var props = { protocol: "data", encodedContent: url.substr(point+1) }; 63 64 var metadataBuffer = url.substring(5, point); 65 var metadata = metadataBuffer.split(";"); 66 for (var i = 0; i < metadata.length; i++) 67 { 68 var nv = metadata[i].split("="); 69 if (nv.length == 2) 70 props[nv[0]] = nv[1]; 71 } 72 73 // Additional Firebug-specific properties 74 if (props.hasOwnProperty("fileName")) 75 { 76 var caller_URL = decodeURIComponent(props["fileName"]); 77 var caller_split = Url.splitURLTrue(caller_URL); 78 79 props["fileName"] = caller_URL; 80 81 if (props.hasOwnProperty("baseLineNumber")) // this means it's probably an eval() 82 { 83 props["path"] = caller_split.path; 84 props["line"] = props["baseLineNumber"]; 85 var hint = decodeURIComponent(props["encodedContent"]).substr(0,200).replace(/\s*$/, ""); 86 props["name"] = "eval->"+hint; 87 } 88 else 89 { 90 props["name"] = caller_split.name; 91 props["path"] = caller_split.path; 92 } 93 } 94 else 95 { 96 if (!props.hasOwnProperty("path")) 97 props["path"] = "data:"; 98 if (!props.hasOwnProperty("name")) 99 props["name"] = decodeURIComponent(props["encodedContent"]).substr(0,200).replace(/\s*$/, ""); 100 } 101 102 return props; 103 }; 104 105 const reSplitFile = /(.*?):\/{2,3}([^\/]*)(.*?)([^\/]*?)($|\?.*)/; 106 Url.splitURLTrue = function(url) 107 { 108 var m = reSplitFile.exec(url); 109 if (!m) 110 return {name: url, path: url}; 111 else if (m[4] == "" && m[5] == "") 112 return {protocol: m[1], domain: m[2], path: m[3], name: m[3] != "/" ? m[3] : m[2]}; 113 else 114 return {protocol: m[1], domain: m[2], path: m[2]+m[3], name: m[4]+m[5]}; 115 }; 116 117 Url.getFileExtension = function(url) 118 { 119 if (!url) 120 return null; 121 122 // Remove query string from the URL if any. 123 var queryString = url.indexOf("?"); 124 if (queryString != -1) 125 url = url.substr(0, queryString); 126 127 // Now get the file extension. 128 var lastDot = url.lastIndexOf("."); 129 return url.substr(lastDot+1); 130 }; 131 132 Url.isSystemURL = function(url) 133 { 134 if (!url) return true; 135 if (url.length == 0) return true; 136 if (url[0] == "h") return false; 137 if (url.substr(0, 9) == "resource:") 138 return true; 139 else if (url.substr(0, 16) == "chrome://firebug") 140 return true; 141 else if (url == "XPCSafeJSObjectWrapper.cpp") 142 return true; 143 else if (url.substr(0, 6) == "about:") 144 return true; 145 else if (url.indexOf("firebug-service.js") != -1) 146 return true; 147 else if (url.indexOf("/modules/debuggerHalter.js") != -1) 148 return true; 149 else 150 return false; 151 }; 152 153 Url.isSystemPage = function(win) 154 { 155 try 156 { 157 var doc = win.document; 158 if (!doc) 159 return false; 160 161 // Detect pages for pretty printed XML 162 if ((doc.styleSheets.length && doc.styleSheets[0].href 163 == "chrome://global/content/xml/XMLPrettyPrint.css") 164 || (doc.styleSheets.length > 1 && doc.styleSheets[1].href 165 == "chrome://browser/skin/feeds/subscribe.css")) 166 return true; 167 168 return Url.isSystemURL(win.location.href); 169 } 170 catch (exc) 171 { 172 // Sometimes documents just aren't ready to be manipulated here, but don't let that 173 // gum up the works 174 FBTrace.sysout("Url.isSystemPage; EXCEPTION document not ready?: " + exc); 175 return false; 176 } 177 } 178 179 Url.isSystemStyleSheet = function(sheet) 180 { 181 var href = sheet && sheet.href; 182 return href && Url.isSystemURL(href); 183 }; 184 185 Url.getURIHost = function(uri) 186 { 187 try 188 { 189 if (uri) 190 return uri.host; 191 else 192 return ""; 193 } 194 catch (exc) 195 { 196 return ""; 197 } 198 } 199 200 Url.isLocalURL = function(url) 201 { 202 if (url.substr(0, 5) == "file:") 203 return true; 204 else if (url.substr(0, 8) == "wyciwyg:") 205 return true; 206 else 207 return false; 208 }; 209 210 Url.isDataURL = function(url) 211 { 212 return (url && url.substr(0,5) == "data:"); 213 }; 214 215 Url.getLocalPath = function(url) 216 { 217 if (this.isLocalURL(url)) 218 { 219 var fileHandler = ioService.getProtocolHandler("file") 220 .QueryInterface(Ci.nsIFileProtocolHandler); 221 var file = fileHandler.getFileFromURLSpec(url); 222 return file.path; 223 } 224 }; 225 226 /** 227 * Mozilla URI from non-web URL 228 * @param URL 229 * @returns undefined or nsIURI 230 */ 231 Url.getLocalSystemURI = function(url) 232 { 233 try 234 { 235 var uri = ioService.newURI(url, null, null); 236 if (uri.schemeIs("resource")) 237 { 238 var ph = ioService.getProtocolHandler("resource") 239 .QueryInterface(Ci.nsIResProtocolHandler); 240 var abspath = ph.getSubstitution(uri.host); 241 uri = ioService.newURI(uri.path.substr(1), null, abspath); 242 } 243 while (uri.schemeIs("chrome")) 244 { 245 var chromeRegistry = Cc["@mozilla.org/chrome/chrome-registry;1"] 246 .getService(Ci.nsIChromeRegistry); 247 uri = chromeRegistry.convertChromeURL(uri); 248 } 249 return uri; 250 } 251 catch(exc) 252 { 253 if (FBTrace.DBG_ERRORS) 254 FBTrace.sysout("getLocalSystemURI failed for "+url); 255 } 256 } 257 258 /* 259 * Mozilla native path for local URL 260 */ 261 Url.getLocalOrSystemPath = function(url, allowDirectories) 262 { 263 var uri = Url.getLocalSystemURI(url); 264 if (uri instanceof Ci.nsIFileURL) 265 { 266 var file = uri.file; 267 if (allowDirectories) 268 return file && file.path; 269 else 270 return file && !file.isDirectory() && file.path; 271 } 272 } 273 274 Url.getLocalOrSystemFile = function(url) 275 { 276 var uri = Url.getLocalSystemURI(url); 277 if (uri instanceof Ci.nsIFileURL) 278 return uri.file; 279 } 280 281 Url.getURLFromLocalFile = function(file) 282 { 283 var fileHandler = ioService.getProtocolHandler("file") 284 .QueryInterface(Ci.nsIFileProtocolHandler); 285 var URL = fileHandler.getURLSpecFromFile(file); 286 return URL; 287 }; 288 289 Url.getDataURLForContent = function(content, url) 290 { 291 // data:text/javascript;fileName=x%2Cy.js;baseLineNumber=10,<the-url-encoded-data> 292 var uri = "data:text/html;"; 293 uri += "fileName="+encodeURIComponent(url)+ "," 294 uri += encodeURIComponent(content); 295 return uri; 296 }, 297 298 Url.getDomain = function(url) 299 { 300 var m = /[^:]+:\/{1,3}([^\/]+)/.exec(url); 301 return m ? m[1] : ""; 302 }; 303 304 Url.getURLPath = function(url) 305 { 306 var m = /[^:]+:\/{1,3}[^\/]+(\/.*?)$/.exec(url); 307 return m ? m[1] : ""; 308 }; 309 310 Url.getPrettyDomain = function(url) 311 { 312 var m = /[^:]+:\/{1,3}(www\.)?([^\/]+)/.exec(url); 313 return m ? m[2] : ""; 314 }; 315 316 /** 317 * Returns the base URL for a given window 318 * @param {Object} win DOM window 319 * @returns {String} Base URL 320 */ 321 Url.getBaseURL = function(win) 322 { 323 if (!win) 324 return; 325 326 var base = win.document.getElementsByTagName("base").item(0); 327 return base ? base.href : win.location.href; 328 }; 329 330 Url.absoluteURL = function(url, baseURL) 331 { 332 // Replace "/./" with "/" using regular expressions (don't use string since /./ 333 // can be treated as regular expressoin too, see 3551). 334 return Url.absoluteURLWithDots(url, baseURL).replace(/\/\.\//, "/", "g"); 335 }; 336 337 Url.absoluteURLWithDots = function(url, baseURL) 338 { 339 // Should implement http://www.apps.ietf.org/rfc/rfc3986.html#sec-5 340 // or use the newURI approach described in issue 3110. 341 // See tests/content/lib/absoluteURLs.js 342 343 if (url.length === 0) 344 return baseURL; 345 346 var R_query_index = url.indexOf("?"); 347 var R_head = url; 348 if (R_query_index !== -1) 349 R_head = url.substr(0, R_query_index); 350 351 if (url.indexOf(":") !== -1) 352 return url; 353 354 var reURL = /(([^:]+:)\/{1,2}[^\/]*)(.*?)$/; 355 var m_url = reURL.exec(R_head); 356 if (m_url) 357 return url; 358 359 var B_query_index = baseURL.indexOf("?"); 360 var B_head = baseURL; 361 if (B_query_index !== -1) 362 B_head = baseURL.substr(0, B_query_index); 363 364 if (url[0] === "?") // cases where R.path is empty. 365 return B_head + url; 366 if (url[0] === "#") 367 return baseURL.split("#")[0]+url; 368 369 var m = reURL.exec(B_head); 370 if (!m) 371 return ""; 372 373 var head = m[1]; 374 var tail = m[3]; 375 if (url.substr(0, 2) == "//") 376 return m[2] + url; 377 else if (url[0] == "/") 378 { 379 return head + url; 380 } 381 else if (tail[tail.length-1] == "/") 382 return B_head + url; 383 else 384 { 385 var parts = tail.split("/"); 386 return head + parts.slice(0, parts.length-1).join("/") + "/" + url; 387 } 388 } 389 390 var reChromeCase = /chrome:\/\/([^/]*)\/(.*?)$/; 391 Url.normalizeURL = function(url) // this gets called a lot, any performance improvement welcome 392 { 393 if (!url) 394 return ""; 395 // Replace one or more characters that are not forward-slash followed by /.., by space. 396 if (url.length < 255) // guard against monsters. 397 { 398 // Replace one or more characters that are not forward-slash followed by /.., by space. 399 url = url.replace(/[^/]+\/\.\.\//, "", "g"); 400 // Issue 1496, avoid # 401 url = url.replace(/#.*/,""); 402 // For some reason, JSDS reports file URLs like "file:/" instead of "file:///", so they 403 // don't match up with the URLs we get back from the DOM 404 url = url.replace(/file:\/([^/])/g, "file:///$1"); 405 // For script tags inserted dynamically sometimes the script.fileName is bogus 406 url = url.replace(/[^\s]*\s->\s/, ""); 407 408 if (Str.hasPrefix(url, "chrome:")) 409 { 410 var m = reChromeCase.exec(url); // 1 is package name, 2 is path 411 if (m) 412 { 413 url = "chrome://"+m[1].toLowerCase()+"/"+m[2]; 414 } 415 } 416 } 417 return url; 418 }; 419 420 Url.denormalizeURL = function(url) 421 { 422 return url.replace(/file:\/\/\//g, "file:/"); 423 }; 424 425 // ********************************************************************************************* // 426 427 Url.parseURLParams = function(url) 428 { 429 var q = url ? url.indexOf("?") : -1; 430 if (q == -1) 431 return []; 432 433 var search = url.substr(q+1); 434 var h = search.lastIndexOf("#"); 435 if (h != -1) 436 search = search.substr(0, h); 437 438 if (!search) 439 return []; 440 441 return Url.parseURLEncodedText(search); 442 }; 443 444 Url.parseURLEncodedText = function(text, noLimit) 445 { 446 const maxValueLength = 25000; 447 448 var params = []; 449 450 // In case the text is empty just return the empty parameters 451 if (text == "") 452 return params; 453 454 // Unescape '+' characters that are used to encode a space. 455 // See section 2.2.in RFC 3986: http://www.ietf.org/rfc/rfc3986.txt 456 text = text.replace(/\+/g, " "); 457 458 // Unescape '&' character 459 text = Str.unescapeForURL(text); 460 461 function decodeText(text) 462 { 463 try 464 { 465 return decodeURIComponent(text); 466 } 467 catch (e) 468 { 469 return decodeURIComponent(unescape(text)); 470 } 471 } 472 473 var args = text.split("&"); 474 for (var i = 0; i < args.length; ++i) 475 { 476 try 477 { 478 var index = args[i].indexOf("="); 479 if (index != -1) 480 { 481 var paramName = args[i].substring(0, index); 482 var paramValue = args[i].substring(index + 1); 483 484 if (paramValue.length > maxValueLength && !noLimit) 485 paramValue = Locale.$STR("LargeData"); 486 487 params.push({name: decodeText(paramName), value: decodeText(paramValue)}); 488 } 489 else 490 { 491 var paramName = args[i]; 492 params.push({name: decodeText(paramName), value: ""}); 493 } 494 } 495 catch (e) 496 { 497 if (FBTrace.DBG_ERRORS) 498 { 499 FBTrace.sysout("parseURLEncodedText EXCEPTION ", e); 500 FBTrace.sysout("parseURLEncodedText EXCEPTION URI", args[i]); 501 } 502 } 503 } 504 505 params.sort(function(a, b) { return a.name <= b.name ? -1 : 1; }); 506 507 return params; 508 }; 509 510 Url.reEncodeURL = function(file, text, noLimit) 511 { 512 var lines = text.split("\n"); 513 var params = Url.parseURLEncodedText(lines[lines.length-1], noLimit); 514 515 var args = []; 516 for (var i = 0; i < params.length; ++i) 517 args.push(encodeURIComponent(params[i].name)+"="+encodeURIComponent(params[i].value)); 518 519 var url = file.href; 520 url += (url.indexOf("?") == -1 ? "?" : "&") + args.join("&"); 521 522 return url; 523 }; 524 525 /** 526 * Extracts the URL from a CSS URL definition. 527 * Example: url(../path/to/file) => ../path/to/file 528 * @param {String} url CSS URL definition 529 * @returns {String} Extracted URL 530 */ 531 Url.extractFromCSS = function(url) 532 { 533 return url.replace(/^url\(["']?(.*?)["']?\)$/, "$1"); 534 }; 535 536 Url.makeURI = function(urlString) 537 { 538 try 539 { 540 if (urlString) 541 return ioService.newURI(urlString, null, null); 542 } 543 catch (exc) 544 { 545 //var explain = {message: "Firebug.lib.makeURI FAILS", url: urlString, exception: exc}; 546 // todo convert explain to json and then to data url 547 if (FBTrace.DBG_ERRORS) 548 FBTrace.sysout("makeURI FAILS for \""+urlString+"\" ", exc); 549 550 return false; 551 } 552 } 553 554 /** 555 * Converts resource: to file: Url. 556 * @param {String} resourceURL 557 */ 558 Url.resourceToFile = function(resourceURL) 559 { 560 var resHandler = ioService.getProtocolHandler("resource") 561 .QueryInterface(Ci.nsIResProtocolHandler); 562 563 var justURL = resourceURL.split("resource://")[1]; 564 var split = justURL.split("/"); 565 var sub = split.shift(); 566 567 var path = resHandler.getSubstitution(sub).spec; 568 return path + split.join("/"); 569 } 570 571 // ********************************************************************************************* // 572 // Registration 573 574 return Url; 575 576 // ********************************************************************************************* // 577 }); 578