Home | History | Annotate | Download | only in front_end
      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