summaryrefslogtreecommitdiffstats
path: root/devtools/client/debugger/src/utils/sources-tree/getURL.js
blob: 5c9e7db6e5ee20f319c8a41f83c0e8057644572a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/* 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("resource://devtools/client/shared/unicode-url.js");

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.
 *        - `filename` which may not be quite matching the URL.
 *           When files are loaded from "/", they won't have a real name,
 *           but instead this will report "(index)".
 *        - `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;
  }

  let { pathname, search, protocol, host } = parse(url);

  // Decode encoded characters early so that all other code rely on decoded strings
  pathname = getUnicodeUrlPath(pathname);
  search = getUnicodeUrlPath(search);
  host = getUnicodeHostname(host);

  const filename = 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: getUnicodeUrlPath(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: host,
      };
  }

  return {
    ...def,
    path: pathname,
    search,
    fileExtension: getFileExtension(pathname),
    filename,
    group: protocol ? `${protocol}//` : "",
  };
}