summaryrefslogtreecommitdiffstats
path: root/devtools/client/debugger/src/utils/sources-tree
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/debugger/src/utils/sources-tree')
-rw-r--r--devtools/client/debugger/src/utils/sources-tree/getURL.js180
-rw-r--r--devtools/client/debugger/src/utils/sources-tree/moz.build9
-rw-r--r--devtools/client/debugger/src/utils/sources-tree/tests/getUrl.spec.js50
-rw-r--r--devtools/client/debugger/src/utils/sources-tree/utils.js44
4 files changed, 283 insertions, 0 deletions
diff --git a/devtools/client/debugger/src/utils/sources-tree/getURL.js b/devtools/client/debugger/src/utils/sources-tree/getURL.js
new file mode 100644
index 0000000000..c06e522723
--- /dev/null
+++ b/devtools/client/debugger/src/utils/sources-tree/getURL.js
@@ -0,0 +1,180 @@
+/* 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/>. */
+
+import { parse } from "../url";
+
+const {
+ getUnicodeHostname,
+ getUnicodeUrlPath,
+} = require("devtools/client/shared/unicode-url");
+
+export function getFilenameFromPath(pathname) {
+ let filename = "";
+ if (pathname) {
+ filename = pathname.substring(pathname.lastIndexOf("/") + 1);
+ // This file does not have a name. Default should be (index).
+ if (filename == "") {
+ filename = "(index)";
+ } else if (filename == ":formatted") {
+ filename = "(index:formatted)";
+ }
+ }
+ return filename;
+}
+
+function getFileExtension(path) {
+ if (!path) {
+ return "";
+ }
+
+ const lastIndex = path.lastIndexOf(".");
+ return lastIndex !== -1 ? path.slice(lastIndex + 1).toLowerCase() : "";
+}
+
+const NoDomain = "(no domain)";
+const def = {
+ path: "",
+ search: "",
+ group: "",
+ filename: "",
+ fileExtension: "",
+};
+
+/**
+ * Compute the URL which may be displayed in the Source Tree.
+ *
+ * @param {String} url
+ * The source absolute URL as a string
+ * @param {String} extensionName
+ * Optional, but mandatory when passing a moz-extension URL.
+ * Name of the extension serving this moz-extension source.
+ * @return URL Object
+ * A URL object to represent this source.
+ *
+ * Note that this isn't the standard URL object.
+ * This is augmented with custom properties like:
+ * - `group`, which is mostly the host of the source's URL.
+ * This is used to sort sources in the Source tree.
+ * - `fileExtension`, lowercased file extension of the source
+ * (if any extension is available)
+ * - `path` and `pathname` have some special behavior.
+ * See `parse` implementation.
+ */
+export function getDisplayURL(url, extensionName = null) {
+ if (!url) {
+ return def;
+ }
+
+ const { pathname, search, protocol, host } = parse(url);
+ const filename = getUnicodeUrlPath(getFilenameFromPath(pathname));
+
+ switch (protocol) {
+ case "javascript:":
+ // Ignore `javascript:` URLs for now
+ return def;
+
+ case "moz-extension:":
+ return {
+ ...def,
+ path: pathname,
+ search,
+ filename,
+ fileExtension: getFileExtension(pathname),
+ // For moz-extension, we replace the uuid by the extension name
+ // that we receive from the SourceActor.extensionName attribute.
+ // `extensionName` might be null for content script of disabled add-ons.
+ group: extensionName || `${protocol}//${host}`,
+ };
+ case "resource:":
+ return {
+ ...def,
+ path: pathname,
+ search,
+ filename,
+ fileExtension: getFileExtension(pathname),
+ group: `${protocol}//${host || ""}`,
+ };
+ case "webpack:":
+ return {
+ ...def,
+ path: pathname,
+ search,
+ filename,
+ fileExtension: getFileExtension(pathname),
+ group: `Webpack`,
+ };
+ case "ng:":
+ return {
+ ...def,
+ path: pathname,
+ search,
+ filename,
+ fileExtension: getFileExtension(pathname),
+ group: `Angular`,
+ };
+ case "about:":
+ // An about page is a special case
+ return {
+ ...def,
+ path: "/",
+ search,
+ filename,
+ fileExtension: getFileExtension("/"),
+ group: url,
+ };
+
+ case "data:":
+ return {
+ ...def,
+ path: "/",
+ search,
+ filename: url,
+ fileExtension: getFileExtension("/"),
+ group: NoDomain,
+ };
+
+ case "":
+ if (pathname && pathname.startsWith("/")) {
+ // use file protocol for a URL like "/foo/bar.js"
+ return {
+ ...def,
+ path: pathname,
+ search,
+ filename,
+ fileExtension: getFileExtension(pathname),
+ group: "file://",
+ };
+ } else if (!host) {
+ return {
+ ...def,
+ path: pathname,
+ search,
+ filename,
+ fileExtension: getFileExtension(pathname),
+ group: "",
+ };
+ }
+ break;
+
+ case "http:":
+ case "https:":
+ return {
+ ...def,
+ path: pathname,
+ search,
+ filename,
+ fileExtension: getFileExtension(pathname),
+ group: getUnicodeHostname(host),
+ };
+ }
+
+ return {
+ ...def,
+ path: pathname,
+ search,
+ fileExtension: getFileExtension(pathname),
+ filename,
+ group: protocol ? `${protocol}//` : "",
+ };
+}
diff --git a/devtools/client/debugger/src/utils/sources-tree/moz.build b/devtools/client/debugger/src/utils/sources-tree/moz.build
new file mode 100644
index 0000000000..400c0f0d1a
--- /dev/null
+++ b/devtools/client/debugger/src/utils/sources-tree/moz.build
@@ -0,0 +1,9 @@
+# vim: set filetype=python:
+# 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/.
+
+CompiledModules(
+ "getURL.js",
+ "utils.js",
+)
diff --git a/devtools/client/debugger/src/utils/sources-tree/tests/getUrl.spec.js b/devtools/client/debugger/src/utils/sources-tree/tests/getUrl.spec.js
new file mode 100644
index 0000000000..51919ffc4e
--- /dev/null
+++ b/devtools/client/debugger/src/utils/sources-tree/tests/getUrl.spec.js
@@ -0,0 +1,50 @@
+/* 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/>. */
+
+import { getDisplayURL } from "../getURL";
+
+describe("getUrl", () => {
+ it("handles normal url with http and https for filename", function () {
+ const urlObject = getDisplayURL("https://a/b.js");
+ expect(urlObject.filename).toBe("b.js");
+
+ const urlObject2 = getDisplayURL("http://a/b.js");
+ expect(urlObject2.filename).toBe("b.js");
+ });
+
+ it("handles url with querystring for filename", function () {
+ const urlObject = getDisplayURL("https://a/b.js?key=randomKey");
+ expect(urlObject.filename).toBe("b.js");
+ });
+
+ it("handles url with '#' for filename", function () {
+ const urlObject = getDisplayURL("https://a/b.js#specialSection");
+ expect(urlObject.filename).toBe("b.js");
+ });
+
+ it("handles url with no file extension for filename", function () {
+ const urlObject = getDisplayURL("https://a/c");
+ expect(urlObject.filename).toBe("c");
+ });
+
+ it("handles url with no name for filename", function () {
+ const urlObject = getDisplayURL("https://a/");
+ expect(urlObject.filename).toBe("(index)");
+ });
+
+ it("separates resources by protocol and host", () => {
+ const urlObject = getDisplayURL("moz-extension://xyz/123");
+ expect(urlObject.group).toBe("moz-extension://xyz");
+ });
+
+ it("creates a group name for webpack", () => {
+ const urlObject = getDisplayURL("webpack:///src/component.jsx");
+ expect(urlObject.group).toBe("Webpack");
+ });
+
+ it("creates a group name for angular source", () => {
+ const urlObject = getDisplayURL("ng://src/component.jsx");
+ expect(urlObject.group).toBe("Angular");
+ });
+});
diff --git a/devtools/client/debugger/src/utils/sources-tree/utils.js b/devtools/client/debugger/src/utils/sources-tree/utils.js
new file mode 100644
index 0000000000..0a2f41752b
--- /dev/null
+++ b/devtools/client/debugger/src/utils/sources-tree/utils.js
@@ -0,0 +1,44 @@
+/* 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/>. */
+
+import { parse } from "../../utils/url";
+
+/**
+ * Get the relative path of the url
+ * Does not include any query parameters or fragment parts
+ *
+ * @param string url
+ * @returns string path
+ */
+export function getRelativePath(url) {
+ const { pathname } = parse(url);
+ if (!pathname) {
+ return url;
+ }
+ const index = pathname.indexOf("/");
+ if (index !== -1) {
+ const path = pathname.slice(index + 1);
+ // If the path is empty this is likely the index file.
+ // e.g http://foo.com/
+ if (path == "") {
+ return "(index)";
+ }
+ return path;
+ }
+ return "";
+}
+
+/**
+ *
+ * @param {String} name: Name (e.g. computed in SourcesTreeItem renderItemName),
+ * which might include URI search.
+ * @returns {String} result of `decodedURI(name)`, or name if it `name` is malformed.
+ */
+export function safeDecodeItemName(name) {
+ try {
+ return decodeURI(name);
+ } catch (e) {
+ return name;
+ }
+}