2 * Copyright (C) 2012 Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
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
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.
33 WebInspector.ParsedURL = function(url)
41 this.queryParams = "";
43 this.folderPathComponents = "";
44 this.lastPathComponent = "";
47 // 1 - scheme (using the RFC3986 grammar)
52 var match = url.match(/^([A-Za-z][A-Za-z0-9+.-]*):\/\/([^\s\/:]*)(?::([\d]+))?(?:(\/[^#]*)(?:#(.*))?)?$/i);
55 this.scheme = match[1].toLowerCase();
58 this.path = match[4] || "/";
59 this.fragment = match[5];
61 if (this.url.startsWith("data:")) {
65 if (this.url === "about:blank") {
66 this.scheme = "about";
72 // First cut the query params.
74 var indexOfQuery = path.indexOf("?");
75 if (indexOfQuery !== -1) {
76 this.queryParams = path.substring(indexOfQuery + 1)
77 path = path.substring(0, indexOfQuery);
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);
86 this.lastPathComponent = path;
93 WebInspector.ParsedURL._decodeIfPossible = function(url)
97 decodedURL = decodeURI(url);
103 * @param {string} url
104 * @return {!Array.<string>}
106 WebInspector.ParsedURL.splitURLIntoPathComponents = function(url)
108 var decodedURL = WebInspector.ParsedURL._decodeIfPossible(url);
109 var parsedURL = new WebInspector.ParsedURL(decodedURL);
113 if (parsedURL.isValid) {
114 origin = parsedURL.scheme + "://" + parsedURL.host;
116 origin += ":" + parsedURL.port;
117 folderPath = parsedURL.folderPathComponents;
118 name = parsedURL.lastPathComponent;
119 if (parsedURL.queryParams)
120 name += "?" + parsedURL.queryParams;
126 var result = [origin];
127 var splittedPath = folderPath.split("/");
128 for (var i = 1; i < splittedPath.length; ++i) {
129 if (!splittedPath[i])
131 result.push(splittedPath[i]);
138 * @param {string} baseURL
139 * @param {string} href
142 WebInspector.ParsedURL.completeURL = function(baseURL, href)
145 // Return special URLs as-is.
146 var trimmedHref = href.trim();
147 if (trimmedHref.startsWith("data:") || trimmedHref.startsWith("blob:") || trimmedHref.startsWith("javascript:"))
150 // Return absolute URLs as-is.
151 var parsedHref = trimmedHref.asParsedURL();
152 if (parsedHref && parsedHref.scheme)
158 var parsedURL = baseURL.asParsedURL();
160 if (parsedURL.isDataURL())
164 var query = path.indexOf("?");
167 postfix = path.substring(query);
168 path = path.substring(0, query);
170 var fragment = path.indexOf("#");
171 if (fragment !== -1) {
172 postfix = path.substring(fragment);
173 path = path.substring(0, fragment);
177 if (!path) { // empty path, must be postfix
178 var basePath = parsedURL.path;
179 if (postfix.charAt(0) === "?") {
180 // A href of "?foo=bar" implies "basePath?foo=bar".
181 // With "basePath?a=b" and "?foo=bar" we should get "basePath?foo=bar".
182 var baseQuery = parsedURL.path.indexOf("?");
183 if (baseQuery !== -1)
184 basePath = basePath.substring(0, baseQuery);
185 } // else it must be a fragment
186 return parsedURL.scheme + "://" + parsedURL.host + (parsedURL.port ? (":" + parsedURL.port) : "") + basePath + postfix;
187 } else if (path.charAt(0) !== "/") { // relative path
188 var prefix = parsedURL.path;
189 var prefixQuery = prefix.indexOf("?");
190 if (prefixQuery !== -1)
191 prefix = prefix.substring(0, prefixQuery);
192 prefix = prefix.substring(0, prefix.lastIndexOf("/")) + "/";
193 path = prefix + path;
194 } else if (path.length > 1 && path.charAt(1) === "/") {
195 // href starts with "//" which is a full URL with the protocol dropped (use the baseURL protocol).
196 return parsedURL.scheme + ":" + path + postfix;
197 } // else absolute path
198 return parsedURL.scheme + "://" + parsedURL.host + (parsedURL.port ? (":" + parsedURL.port) : "") + normalizePath(path) + postfix;
203 WebInspector.ParsedURL.prototype = {
206 if (this._displayName)
207 return this._displayName;
209 if (this.isDataURL())
210 return this.dataURLDisplayName();
211 if (this.isAboutBlank())
214 this._displayName = this.lastPathComponent;
215 if (!this._displayName)
216 this._displayName = (this.host || "") + "/";
217 if (this._displayName === "/")
218 this._displayName = this.url;
219 return this._displayName;
225 dataURLDisplayName: function()
227 if (this._dataURLDisplayName)
228 return this._dataURLDisplayName;
229 if (!this.isDataURL())
231 this._dataURLDisplayName = this.url.trimEnd(20);
232 return this._dataURLDisplayName;
238 isAboutBlank: function()
240 return this.url === "about:blank";
246 isDataURL: function()
248 return this.scheme === "data";
253 * @param {string} string
254 * @return {?{url: string, lineNumber: (number|undefined), columnNumber: (number|undefined)}}
256 WebInspector.ParsedURL.splitLineAndColumn = function(string)
258 var lineColumnRegEx = /:(\d+)(:(\d+))?$/;
259 var lineColumnMatch = lineColumnRegEx.exec(string);
262 if (!lineColumnMatch)
265 lineNumber = parseInt(lineColumnMatch[1], 10);
266 // Immediately convert line and column to 0-based numbers.
267 lineNumber = isNaN(lineNumber) ? undefined : lineNumber - 1;
268 if (typeof(lineColumnMatch[3]) === "string") {
269 columnNumber = parseInt(lineColumnMatch[3], 10);
270 columnNumber = isNaN(columnNumber) ? undefined : columnNumber - 1;
273 return { url: string.substring(0, string.length - lineColumnMatch[0].length), lineNumber: lineNumber, columnNumber: columnNumber};
277 * @return {?WebInspector.ParsedURL}
279 String.prototype.asParsedURL = function()
281 var parsedURL = new WebInspector.ParsedURL(this.toString());
282 if (parsedURL.isValid)