summaryrefslogtreecommitdiffstats
path: root/toolkit/jetpack/sdk/url.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/jetpack/sdk/url.js')
-rw-r--r--toolkit/jetpack/sdk/url.js349
1 files changed, 349 insertions, 0 deletions
diff --git a/toolkit/jetpack/sdk/url.js b/toolkit/jetpack/sdk/url.js
new file mode 100644
index 000000000..ae16ac4a8
--- /dev/null
+++ b/toolkit/jetpack/sdk/url.js
@@ -0,0 +1,349 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+module.metadata = {
+ "stability": "experimental"
+};
+
+const { Cc, Ci, Cr, Cu } = require("chrome");
+
+const { Class } = require("./core/heritage");
+const base64 = require("./base64");
+var tlds = Cc["@mozilla.org/network/effective-tld-service;1"]
+ .getService(Ci.nsIEffectiveTLDService);
+
+var ios = Cc['@mozilla.org/network/io-service;1']
+ .getService(Ci.nsIIOService);
+
+var resProt = ios.getProtocolHandler("resource")
+ .QueryInterface(Ci.nsIResProtocolHandler);
+
+var URLParser = Cc["@mozilla.org/network/url-parser;1?auth=no"]
+ .getService(Ci.nsIURLParser);
+
+const { Services } = Cu.import("resource://gre/modules/Services.jsm");
+
+function newURI(uriStr, base) {
+ try {
+ let baseURI = base ? ios.newURI(base, null, null) : null;
+ return ios.newURI(uriStr, null, baseURI);
+ }
+ catch (e) {
+ if (e.result == Cr.NS_ERROR_MALFORMED_URI) {
+ throw new Error("malformed URI: " + uriStr);
+ }
+ if (e.result == Cr.NS_ERROR_FAILURE ||
+ e.result == Cr.NS_ERROR_ILLEGAL_VALUE) {
+ throw new Error("invalid URI: " + uriStr);
+ }
+ }
+}
+
+function resolveResourceURI(uri) {
+ var resolved;
+ try {
+ resolved = resProt.resolveURI(uri);
+ }
+ catch (e) {
+ if (e.result == Cr.NS_ERROR_NOT_AVAILABLE) {
+ throw new Error("resource does not exist: " + uri.spec);
+ }
+ }
+ return resolved;
+}
+
+var fromFilename = exports.fromFilename = function fromFilename(path) {
+ var file = Cc['@mozilla.org/file/local;1']
+ .createInstance(Ci.nsILocalFile);
+ file.initWithPath(path);
+ return ios.newFileURI(file).spec;
+};
+
+var toFilename = exports.toFilename = function toFilename(url) {
+ var uri = newURI(url);
+ if (uri.scheme == "resource")
+ uri = newURI(resolveResourceURI(uri));
+ if (uri.scheme == "chrome") {
+ var channel = ios.newChannelFromURI2(uri,
+ null, // aLoadingNode
+ Services.scriptSecurityManager.getSystemPrincipal(),
+ null, // aTriggeringPrincipal
+ Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
+ Ci.nsIContentPolicy.TYPE_OTHER);
+ try {
+ channel = channel.QueryInterface(Ci.nsIFileChannel);
+ return channel.file.path;
+ }
+ catch (e) {
+ if (e.result == Cr.NS_NOINTERFACE) {
+ throw new Error("chrome url isn't on filesystem: " + url);
+ }
+ }
+ }
+ if (uri.scheme == "file") {
+ var file = uri.QueryInterface(Ci.nsIFileURL).file;
+ return file.path;
+ }
+ throw new Error("cannot map to filename: " + url);
+};
+
+function URL(url, base) {
+ if (!(this instanceof URL)) {
+ return new URL(url, base);
+ }
+
+ var uri = newURI(url, base);
+
+ var userPass = null;
+ try {
+ userPass = uri.userPass ? uri.userPass : null;
+ }
+ catch (e) {
+ if (e.result != Cr.NS_ERROR_FAILURE) {
+ throw e;
+ }
+ }
+
+ var host = null;
+ try {
+ host = uri.host;
+ }
+ catch (e) {
+ if (e.result != Cr.NS_ERROR_FAILURE) {
+ throw e;
+ }
+ }
+
+ var port = null;
+ try {
+ port = uri.port == -1 ? null : uri.port;
+ }
+ catch (e) {
+ if (e.result != Cr.NS_ERROR_FAILURE) {
+ throw e;
+ }
+ }
+
+ let fileName = "/";
+ try {
+ fileName = uri.QueryInterface(Ci.nsIURL).fileName;
+ } catch (e) {
+ if (e.result != Cr.NS_NOINTERFACE) {
+ throw e;
+ }
+ }
+
+ let uriData = [uri.path, uri.path.length, {}, {}, {}, {}, {}, {}];
+ URLParser.parsePath.apply(URLParser, uriData);
+ let [{ value: filepathPos }, { value: filepathLen },
+ { value: queryPos }, { value: queryLen },
+ { value: refPos }, { value: refLen }] = uriData.slice(2);
+
+ let hash = uri.ref ? "#" + uri.ref : "";
+ let pathname = uri.path.substr(filepathPos, filepathLen);
+ let search = uri.path.substr(queryPos, queryLen);
+ search = search ? "?" + search : "";
+
+ this.__defineGetter__("fileName", () => fileName);
+ this.__defineGetter__("scheme", () => uri.scheme);
+ this.__defineGetter__("userPass", () => userPass);
+ this.__defineGetter__("host", () => host);
+ this.__defineGetter__("hostname", () => host);
+ this.__defineGetter__("port", () => port);
+ this.__defineGetter__("path", () => uri.path);
+ this.__defineGetter__("pathname", () => pathname);
+ this.__defineGetter__("hash", () => hash);
+ this.__defineGetter__("href", () => uri.spec);
+ this.__defineGetter__("origin", () => uri.prePath);
+ this.__defineGetter__("protocol", () => uri.scheme + ":");
+ this.__defineGetter__("search", () => search);
+
+ Object.defineProperties(this, {
+ toString: {
+ value() {
+ return new String(uri.spec).toString();
+ },
+ enumerable: false
+ },
+ valueOf: {
+ value() {
+ return new String(uri.spec).valueOf();
+ },
+ enumerable: false
+ },
+ toSource: {
+ value() {
+ return new String(uri.spec).toSource();
+ },
+ enumerable: false
+ },
+ // makes more sense to flatten to string, easier to travel across JSON
+ toJSON: {
+ value() {
+ return new String(uri.spec).toString();
+ },
+ enumerable: false
+ }
+ });
+
+ return this;
+};
+
+URL.prototype = Object.create(String.prototype);
+exports.URL = URL;
+
+/**
+ * Parse and serialize a Data URL.
+ *
+ * See: http://tools.ietf.org/html/rfc2397
+ *
+ * Note: Could be extended in the future to decode / encode automatically binary
+ * data.
+ */
+const DataURL = Class({
+
+ get base64 () {
+ return "base64" in this.parameters;
+ },
+
+ set base64 (value) {
+ if (value)
+ this.parameters["base64"] = "";
+ else
+ delete this.parameters["base64"];
+ },
+ /**
+ * Initialize the Data URL object. If a uri is given, it will be parsed.
+ *
+ * @param {String} [uri] The uri to parse
+ *
+ * @throws {URIError} if the Data URL is malformed
+ */
+ initialize: function(uri) {
+ // Due to bug 751834 it is not possible document and define these
+ // properties in the prototype.
+
+ /**
+ * An hashmap that contains the parameters of the Data URL. By default is
+ * empty, that accordingly to RFC is equivalent to {"charset" : "US-ASCII"}
+ */
+ this.parameters = {};
+
+ /**
+ * The MIME type of the data. By default is empty, that accordingly to RFC
+ * is equivalent to "text/plain"
+ */
+ this.mimeType = "";
+
+ /**
+ * The string that represent the data in the Data URL
+ */
+ this.data = "";
+
+ if (typeof uri === "undefined")
+ return;
+
+ uri = String(uri);
+
+ let matches = uri.match(/^data:([^,]*),(.*)$/i);
+
+ if (!matches)
+ throw new URIError("Malformed Data URL: " + uri);
+
+ let mediaType = matches[1].trim();
+
+ this.data = decodeURIComponent(matches[2].trim());
+
+ if (!mediaType)
+ return;
+
+ let parametersList = mediaType.split(";");
+
+ this.mimeType = parametersList.shift().trim();
+
+ for (let parameter, i = 0; parameter = parametersList[i++];) {
+ let pairs = parameter.split("=");
+ let name = pairs[0].trim();
+ let value = pairs.length > 1 ? decodeURIComponent(pairs[1].trim()) : "";
+
+ this.parameters[name] = value;
+ }
+
+ if (this.base64)
+ this.data = base64.decode(this.data);
+
+ },
+
+ /**
+ * Returns the object as a valid Data URL string
+ *
+ * @returns {String} The Data URL
+ */
+ toString : function() {
+ let parametersList = [];
+
+ for (let name in this.parameters) {
+ let encodedParameter = encodeURIComponent(name);
+ let value = this.parameters[name];
+
+ if (value)
+ encodedParameter += "=" + encodeURIComponent(value);
+
+ parametersList.push(encodedParameter);
+ }
+
+ // If there is at least a parameter, add an empty string in order
+ // to start with a `;` on join call.
+ if (parametersList.length > 0)
+ parametersList.unshift("");
+
+ let data = this.base64 ? base64.encode(this.data) : this.data;
+
+ return "data:" +
+ this.mimeType +
+ parametersList.join(";") + "," +
+ encodeURIComponent(data);
+ }
+});
+
+exports.DataURL = DataURL;
+
+var getTLD = exports.getTLD = function getTLD (url) {
+ let uri = newURI(url.toString());
+ let tld = null;
+ try {
+ tld = tlds.getPublicSuffix(uri);
+ }
+ catch (e) {
+ if (e.result != Cr.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS &&
+ e.result != Cr.NS_ERROR_HOST_IS_IP_ADDRESS) {
+ throw e;
+ }
+ }
+ return tld;
+};
+
+var isValidURI = exports.isValidURI = function (uri) {
+ try {
+ newURI(uri);
+ }
+ catch(e) {
+ return false;
+ }
+ return true;
+}
+
+function isLocalURL(url) {
+ if (String.indexOf(url, './') === 0)
+ return true;
+
+ try {
+ return ['resource', 'data', 'chrome'].indexOf(URL(url).scheme) > -1;
+ }
+ catch(e) {}
+
+ return false;
+}
+exports.isLocalURL = isLocalURL;