1 /* 2 * Copyright (C) 2012 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are 6 * met: 7 * 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following disclaimer 13 * in the documentation and/or other materials provided with the 14 * distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY GOOGLE INC. AND ITS CONTRIBUTORS 17 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. 20 * OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 21 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 22 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /** 30 * @constructor 31 * @param {string} url 32 */ 33 WebInspector.ParsedURL = function(url) 34 { 35 this.isValid = false; 36 this.url = url; 37 this.scheme = ""; 38 this.host = ""; 39 this.port = ""; 40 this.path = ""; 41 this.queryParams = ""; 42 this.fragment = ""; 43 this.folderPathComponents = ""; 44 this.lastPathComponent = ""; 45 46 // RegExp groups: 47 // 1 - scheme (using the RFC3986 grammar) 48 // 2 - hostname 49 // 3 - ?port 50 // 4 - ?path 51 // 5 - ?fragment 52 var match = url.match(/^([A-Za-z][A-Za-z0-9+.-]*):\/\/([^\/:]*)(?::([\d]+))?(?:(\/[^#]*)(?:#(.*))?)?$/i); 53 if (match) { 54 this.isValid = true; 55 this.scheme = match[1].toLowerCase(); 56 this.host = match[2]; 57 this.port = match[3]; 58 this.path = match[4] || "/"; 59 this.fragment = match[5]; 60 } else { 61 if (this.url.startsWith("data:")) { 62 this.scheme = "data"; 63 return; 64 } 65 if (this.url === "about:blank") { 66 this.scheme = "about"; 67 return; 68 } 69 this.path = this.url; 70 } 71 72 // First cut the query params. 73 var path = this.path; 74 var indexOfQuery = path.indexOf("?"); 75 if (indexOfQuery !== -1) { 76 this.queryParams = path.substring(indexOfQuery + 1) 77 path = path.substring(0, indexOfQuery); 78 } 79 80 // Then take last path component. 81 var lastSlashIndex = path.lastIndexOf("/"); 82 if (lastSlashIndex !== -1) { 83 this.folderPathComponents = path.substring(0, lastSlashIndex); 84 this.lastPathComponent = path.substring(lastSlashIndex + 1); 85 } else 86 this.lastPathComponent = path; 87 } 88 89 /** 90 * @param {string} url 91 * @return {Array.<string>} 92 */ 93 WebInspector.ParsedURL.splitURL = function(url) 94 { 95 var parsedURL = new WebInspector.ParsedURL(url); 96 var origin; 97 var folderPath; 98 var name; 99 if (parsedURL.isValid) { 100 origin = parsedURL.scheme + "://" + parsedURL.host; 101 if (parsedURL.port) 102 origin += ":" + parsedURL.port; 103 folderPath = parsedURL.folderPathComponents; 104 name = parsedURL.lastPathComponent; 105 if (parsedURL.queryParams) 106 name += "?" + parsedURL.queryParams; 107 } else { 108 origin = ""; 109 folderPath = ""; 110 name = url; 111 } 112 var result = [origin]; 113 var splittedPath = folderPath.split("/"); 114 for (var i = 1; i < splittedPath.length; ++i) 115 result.push(splittedPath[i]); 116 result.push(name); 117 return result; 118 } 119 120 /** 121 * @param {string} baseURL 122 * @param {string} href 123 * @return {?string} 124 */ 125 WebInspector.ParsedURL.completeURL = function(baseURL, href) 126 { 127 if (href) { 128 // Return special URLs as-is. 129 var trimmedHref = href.trim(); 130 if (trimmedHref.startsWith("data:") || trimmedHref.startsWith("blob:") || trimmedHref.startsWith("javascript:")) 131 return href; 132 133 // Return absolute URLs as-is. 134 var parsedHref = trimmedHref.asParsedURL(); 135 if (parsedHref && parsedHref.scheme) 136 return trimmedHref; 137 } else 138 return baseURL; 139 140 var parsedURL = baseURL.asParsedURL(); 141 if (parsedURL) { 142 if (parsedURL.isDataURL()) 143 return href; 144 var path = href; 145 if (path.charAt(0) !== "/") { 146 var basePath = parsedURL.path; 147 148 // Trim off the query part of the basePath. 149 var questionMarkIndex = basePath.indexOf("?"); 150 if (questionMarkIndex > 0) 151 basePath = basePath.substring(0, questionMarkIndex); 152 // A href of "?foo=bar" implies "basePath?foo=bar". 153 // With "basePath?a=b" and "?foo=bar" we should get "basePath?foo=bar". 154 var prefix; 155 if (path.charAt(0) === "?") { 156 var basePathCutIndex = basePath.indexOf("?"); 157 if (basePathCutIndex !== -1) 158 prefix = basePath.substring(0, basePathCutIndex); 159 else 160 prefix = basePath; 161 } else 162 prefix = basePath.substring(0, basePath.lastIndexOf("/")) + "/"; 163 164 path = prefix + path; 165 } else if (path.length > 1 && path.charAt(1) === "/") { 166 // href starts with "//" which is a full URL with the protocol dropped (use the baseURL protocol). 167 return parsedURL.scheme + ":" + path; 168 } 169 return parsedURL.scheme + "://" + parsedURL.host + (parsedURL.port ? (":" + parsedURL.port) : "") + path; 170 } 171 return null; 172 } 173 174 WebInspector.ParsedURL.prototype = { 175 get displayName() 176 { 177 if (this._displayName) 178 return this._displayName; 179 180 if (this.isDataURL()) 181 return this.dataURLDisplayName(); 182 if (this.isAboutBlank()) 183 return this.url; 184 185 this._displayName = this.lastPathComponent; 186 if (!this._displayName && this.host) 187 this._displayName = this.host + "/"; 188 if (!this._displayName && this.url) 189 this._displayName = this.url.trimURL(WebInspector.inspectedPageDomain ? WebInspector.inspectedPageDomain : ""); 190 if (this._displayName === "/") 191 this._displayName = this.url; 192 return this._displayName; 193 }, 194 195 dataURLDisplayName: function() 196 { 197 if (this._dataURLDisplayName) 198 return this._dataURLDisplayName; 199 if (!this.isDataURL()) 200 return ""; 201 this._dataURLDisplayName = this.url.trimEnd(20); 202 return this._dataURLDisplayName; 203 }, 204 205 isAboutBlank: function() 206 { 207 return this.url === "about:blank"; 208 }, 209 210 isDataURL: function() 211 { 212 return this.scheme === "data"; 213 } 214 } 215 216 /** 217 * @return {?WebInspector.ParsedURL} 218 */ 219 String.prototype.asParsedURL = function() 220 { 221 var parsedURL = new WebInspector.ParsedURL(this.toString()); 222 if (parsedURL.isValid) 223 return parsedURL; 224 return null; 225 } 226