/* * Copyright (C) 2013 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ function removeURLFragment(url) { var hashIndex = url.indexOf("#"); if (hashIndex >= 0) return url.substring(0, hashIndex); return url; } function relativePath(path, basePath) { console.assert(path.charAt(0) === "/"); console.assert(basePath.charAt(0) === "/"); var pathComponents = path.split("/"); var baseComponents = basePath.replace(/\/$/, "").split("/"); var finalComponents = []; var index = 1; for (; index < pathComponents.length && index < baseComponents.length; ++index) { if (pathComponents[index] !== baseComponents[index]) break; } for (var i = index; i < baseComponents.length; ++i) finalComponents.push(".."); for (var i = index; i < pathComponents.length; ++i) finalComponents.push(pathComponents[i]); return finalComponents.join("/"); } function parseSecurityOrigin(securityOrigin) { securityOrigin = securityOrigin ? securityOrigin.trim() : ""; var match = securityOrigin.match(/^([^:]+):\/\/([^\/:]*)(?::([\d]+))?$/i); if (!match) return {scheme: null, host: null, port: null}; var scheme = match[1].toLowerCase(); var host = match[2].toLowerCase(); var port = Number(match[3]) || null; return {scheme, host, port}; } function parseDataURL(url) { if (!url.startsWith("data:")) return null; // data:[][;charset=][;base64], let match = url.match(/^data:([^;,]*)?(?:;charset=([^;,]*?))?(;base64)?,(.*)$/); if (!match) return null; let scheme = "data"; let mimeType = match[1] || "text/plain"; let charset = match[2] || "US-ASCII"; let base64 = !!match[3]; let data = decodeURIComponent(match[4]); return {scheme, mimeType, charset, base64, data}; } function parseURL(url) { url = url ? url.trim() : ""; if (url.startsWith("data:")) return {scheme: "data", host: null, port: null, path: null, queryString: null, fragment: null, lastPathComponent: null}; var match = url.match(/^([^:]+):\/\/([^\/:]*)(?::([\d]+))?(?:(\/[^#]*)(?:#(.*))?)?$/i); if (!match) return {scheme: null, host: null, port: null, path: null, queryString: null, fragment: null, lastPathComponent: null}; var scheme = match[1].toLowerCase(); var host = match[2].toLowerCase(); var port = Number(match[3]) || null; var wholePath = match[4] || null; var fragment = match[5] || null; var path = wholePath; var queryString = null; // Split the path and the query string. if (wholePath) { var indexOfQuery = wholePath.indexOf("?"); if (indexOfQuery !== -1) { path = wholePath.substring(0, indexOfQuery); queryString = wholePath.substring(indexOfQuery + 1); } path = resolveDotsInPath(path); } // Find last path component. var lastPathComponent = null; if (path && path !== "/") { // Skip the trailing slash if there is one. var endOffset = path[path.length - 1] === "/" ? 1 : 0; var lastSlashIndex = path.lastIndexOf("/", path.length - 1 - endOffset); if (lastSlashIndex !== -1) lastPathComponent = path.substring(lastSlashIndex + 1, path.length - endOffset); } return {scheme, host, port, path, queryString, fragment, lastPathComponent}; } function absoluteURL(partialURL, baseURL) { partialURL = partialURL ? partialURL.trim() : ""; // Return data and javascript URLs as-is. if (partialURL.startsWith("data:") || partialURL.startsWith("javascript:") || partialURL.startsWith("mailto:")) return partialURL; // If the URL has a scheme it is already a full URL, so return it. if (parseURL(partialURL).scheme) return partialURL; // If there is no partial URL, just return the base URL. if (!partialURL) return baseURL || null; var baseURLComponents = parseURL(baseURL); // The base URL needs to be an absolute URL. Return null if it isn't. if (!baseURLComponents.scheme) return null; // A URL that starts with "//" is a full URL without the scheme. Use the base URL scheme. if (partialURL[0] === "/" && partialURL[1] === "/") return baseURLComponents.scheme + ":" + partialURL; // The path can be null for URLs that have just a scheme and host (like "http://apple.com"). So make the path be "/". if (!baseURLComponents.path) baseURLComponents.path = "/"; // Generate the base URL prefix that is used in the rest of the cases. var baseURLPrefix = baseURLComponents.scheme + "://" + baseURLComponents.host + (baseURLComponents.port ? (":" + baseURLComponents.port) : ""); // A URL that starts with "?" is just a query string that gets applied to the base URL (replacing the base URL query string and fragment). if (partialURL[0] === "?") return baseURLPrefix + baseURLComponents.path + partialURL; // A URL that starts with "/" is an absolute path that gets applied to the base URL (replacing the base URL path, query string and fragment). if (partialURL[0] === "/") return baseURLPrefix + resolveDotsInPath(partialURL); // A URL that starts with "#" is just a fragment that gets applied to the base URL (replacing the base URL fragment, maintaining the query string). if (partialURL[0] === "#") { let queryStringComponent = baseURLComponents.queryString ? "?" + baseURLComponents.queryString : ""; return baseURLPrefix + baseURLComponents.path + queryStringComponent + partialURL; } // Generate the base path that is used in the final case by removing everything after the last "/" from the base URL's path. var basePath = baseURLComponents.path.substring(0, baseURLComponents.path.lastIndexOf("/")) + "/"; return baseURLPrefix + resolveDotsInPath(basePath + partialURL); } function parseLocationQueryParameters(arrayResult) { // The first character is always the "?". return parseQueryString(window.location.search.substring(1), arrayResult); } function parseQueryString(queryString, arrayResult) { if (!queryString) return arrayResult ? [] : {}; function decode(string) { try { // Replace "+" with " " then decode percent encoded values. return decodeURIComponent(string.replace(/\+/g, " ")); } catch (e) { return string; } } var parameters = arrayResult ? [] : {}; var parameterStrings = queryString.split("&"); for (var i = 0; i < parameterStrings.length; ++i) { var pair = parameterStrings[i].split("=").map(decode); if (arrayResult) parameters.push({name: pair[0], value: pair[1]}); else parameters[pair[0]] = pair[1]; } return parameters; } WebInspector.displayNameForURL = function(url, urlComponents) { if (url.startsWith("data:")) return WebInspector.truncateURL(url); if (!urlComponents) urlComponents = parseURL(url); var displayName; try { displayName = decodeURIComponent(urlComponents.lastPathComponent || ""); } catch (e) { displayName = urlComponents.lastPathComponent; } return displayName || WebInspector.displayNameForHost(urlComponents.host) || url; }; WebInspector.truncateURL = function(url, multiline = false, dataURIMaxSize = 6) { if (!url.startsWith("data:")) return url; const dataIndex = url.indexOf(",") + 1; let header = url.slice(0, dataIndex); if (multiline) header += "\n"; const data = url.slice(dataIndex); if (data.length < dataURIMaxSize) return header + data; const firstChunk = data.slice(0, Math.ceil(dataURIMaxSize / 2)); const ellipsis = "\u2026"; const middleChunk = multiline ? `\n${ellipsis}\n` : ellipsis; const lastChunk = data.slice(-Math.floor(dataURIMaxSize / 2)); return header + firstChunk + middleChunk + lastChunk; }; WebInspector.displayNameForHost = function(host) { // FIXME : This should decode punycode hostnames. return host; };