Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / devtools / front_end / common / ParsedURL.js
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+.-]*):\/\/([^\s\/:]*)(?::([\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 {string}
92  */
93 WebInspector.ParsedURL._decodeIfPossible = function(url)
94 {
95     var decodedURL = url;
96     try {
97         decodedURL = decodeURI(url);
98     } catch (e) { }
99     return decodedURL;
100 }
101
102 /**
103  * @param {string} url
104  * @return {!Array.<string>}
105  */
106 WebInspector.ParsedURL.splitURLIntoPathComponents = function(url)
107 {
108     var decodedURL = WebInspector.ParsedURL._decodeIfPossible(url);
109     var parsedURL = new WebInspector.ParsedURL(decodedURL);
110     var origin;
111     var folderPath;
112     var name;
113     if (parsedURL.isValid) {
114         origin = parsedURL.scheme + "://" + parsedURL.host;
115         if (parsedURL.port)
116             origin += ":" + parsedURL.port;
117         folderPath = parsedURL.folderPathComponents;
118         name = parsedURL.lastPathComponent;
119         if (parsedURL.queryParams)
120             name += "?" + parsedURL.queryParams;
121     } else {
122         origin = "";
123         folderPath = "";
124         name = url;
125     }
126     var result = [origin];
127     var splittedPath = folderPath.split("/");
128     for (var i = 1; i < splittedPath.length; ++i) {
129         if (!splittedPath[i])
130             continue;
131         result.push(splittedPath[i]);
132     }
133     result.push(name);
134     return result;
135 }
136
137 /**
138  * @param {string} baseURL
139  * @param {string} href
140  * @return {?string}
141  */
142 WebInspector.ParsedURL.completeURL = function(baseURL, href)
143 {
144     if (href) {
145         // Return special URLs as-is.
146         var trimmedHref = href.trim();
147         if (trimmedHref.startsWith("data:") || trimmedHref.startsWith("blob:") || trimmedHref.startsWith("javascript:"))
148             return href;
149
150         // Return absolute URLs as-is.
151         var parsedHref = trimmedHref.asParsedURL();
152         if (parsedHref && parsedHref.scheme)
153             return trimmedHref;
154     } else {
155         return baseURL;
156     }
157
158     var parsedURL = baseURL.asParsedURL();
159     if (parsedURL) {
160         if (parsedURL.isDataURL())
161             return href;
162         var path = href;
163
164         var query = path.indexOf("?");
165         var postfix = "";
166         if (query !== -1) {
167             postfix = path.substring(query);
168             path = path.substring(0, query);
169         } else {
170             var fragment = path.indexOf("#");
171             if (fragment !== -1) {
172                 postfix = path.substring(fragment);
173                 path = path.substring(0, fragment);
174             }
175         }
176
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;
199     }
200     return null;
201 }
202
203 WebInspector.ParsedURL.prototype = {
204     get displayName()
205     {
206         if (this._displayName)
207             return this._displayName;
208
209         if (this.isDataURL())
210             return this.dataURLDisplayName();
211         if (this.isAboutBlank())
212             return this.url;
213
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;
220     },
221
222     /**
223      * @return {string}
224      */
225     dataURLDisplayName: function()
226     {
227         if (this._dataURLDisplayName)
228             return this._dataURLDisplayName;
229         if (!this.isDataURL())
230             return "";
231         this._dataURLDisplayName = this.url.trimEnd(20);
232         return this._dataURLDisplayName;
233     },
234
235     /**
236      * @return {boolean}
237      */
238     isAboutBlank: function()
239     {
240         return this.url === "about:blank";
241     },
242
243     /**
244      * @return {boolean}
245      */
246     isDataURL: function()
247     {
248         return this.scheme === "data";
249     }
250 }
251
252 /**
253  * @param {string} string
254  * @return {?{url: string, lineNumber: (number|undefined), columnNumber: (number|undefined)}}
255  */
256 WebInspector.ParsedURL.splitLineAndColumn = function(string)
257 {
258     var lineColumnRegEx = /:(\d+)(:(\d+))?$/;
259     var lineColumnMatch = lineColumnRegEx.exec(string);
260     var lineNumber;
261     var columnNumber;
262     if (!lineColumnMatch)
263         return null;
264
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;
271     }
272
273     return { url: string.substring(0, string.length - lineColumnMatch[0].length), lineNumber: lineNumber, columnNumber: columnNumber};
274 }
275
276 /**
277  * @return {?WebInspector.ParsedURL}
278  */
279 String.prototype.asParsedURL = function()
280 {
281     var parsedURL = new WebInspector.ParsedURL(this.toString());
282     if (parsedURL.isValid)
283         return parsedURL;
284     return null;
285 }