summaryrefslogtreecommitdiffstats
path: root/devtools/client/netmonitor/request-utils.js
blob: 90fb0c957469ae33d5a339421cfa05adaf599233 (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
"use strict";
/* eslint-disable mozilla/reject-some-requires */
const { Ci } = require("chrome");
const { KeyCodes } = require("devtools/client/shared/keycodes");
const { Task } = require("devtools/shared/task");
const NetworkHelper = require("devtools/shared/webconsole/network-helper");

/**
 * Helper method to get a wrapped function which can be bound to as
 * an event listener directly and is executed only when data-key is
 * present in event.target.
 *
 * @param function callback
 *          Function to execute execute when data-key
 *          is present in event.target.
 * @param bool onlySpaceOrReturn
 *          Flag to indicate if callback should only be called
            when the space or return button is pressed
 * @return function
 *          Wrapped function with the target data-key as the first argument
 *          and the event as the second argument.
 */
exports.getKeyWithEvent = function (callback, onlySpaceOrReturn) {
  return function (event) {
    let key = event.target.getAttribute("data-key");
    let filterKeyboardEvent = !onlySpaceOrReturn ||
                              event.keyCode === KeyCodes.DOM_VK_SPACE ||
                              event.keyCode === KeyCodes.DOM_VK_RETURN;

    if (key && filterKeyboardEvent) {
      callback.call(null, key);
    }
  };
};

/**
 * Extracts any urlencoded form data sections (e.g. "?foo=bar&baz=42") from a
 * POST request.
 *
 * @param object headers
 *        The "requestHeaders".
 * @param object uploadHeaders
 *        The "requestHeadersFromUploadStream".
 * @param object postData
 *        The "requestPostData".
 * @param object getString
          Callback to retrieve a string from a LongStringGrip.
 * @return array
 *        A promise that is resolved with the extracted form data.
 */
exports.getFormDataSections = Task.async(function* (headers, uploadHeaders, postData,
                                                    getString) {
  let formDataSections = [];

  let { headers: requestHeaders } = headers;
  let { headers: payloadHeaders } = uploadHeaders;
  let allHeaders = [...payloadHeaders, ...requestHeaders];

  let contentTypeHeader = allHeaders.find(e => {
    return e.name.toLowerCase() == "content-type";
  });

  let contentTypeLongString = contentTypeHeader ? contentTypeHeader.value : "";

  let contentType = yield getString(contentTypeLongString);

  if (contentType.includes("x-www-form-urlencoded")) {
    let postDataLongString = postData.postData.text;
    let text = yield getString(postDataLongString);

    for (let section of text.split(/\r\n|\r|\n/)) {
      // Before displaying it, make sure this section of the POST data
      // isn't a line containing upload stream headers.
      if (payloadHeaders.every(header => !section.startsWith(header.name))) {
        formDataSections.push(section);
      }
    }
  }

  return formDataSections;
});

/**
 * Form a data: URI given a mime type, encoding, and some text.
 *
 * @param {String} mimeType the mime type
 * @param {String} encoding the encoding to use; if not set, the
 *        text will be base64-encoded.
 * @param {String} text the text of the URI.
 * @return {String} a data: URI
 */
exports.formDataURI = function (mimeType, encoding, text) {
  if (!encoding) {
    encoding = "base64";
    text = btoa(unescape(encodeURIComponent(text)));
  }
  return "data:" + mimeType + ";" + encoding + "," + text;
};

/**
 * Write out a list of headers into a chunk of text
 *
 * @param array headers
 *        Array of headers info {name, value}
 * @return string text
 *         List of headers in text format
 */
exports.writeHeaderText = function (headers) {
  return headers.map(({name, value}) => name + ": " + value).join("\n");
};

/**
 * Helper for getting an abbreviated string for a mime type.
 *
 * @param string mimeType
 * @return string
 */
exports.getAbbreviatedMimeType = function (mimeType) {
  if (!mimeType) {
    return "";
  }
  return (mimeType.split(";")[0].split("/")[1] || "").split("+")[0];
};

/**
 * Helpers for getting details about an nsIURL.
 *
 * @param nsIURL | string url
 * @return string
 */
exports.getUriNameWithQuery = function (url) {
  if (!(url instanceof Ci.nsIURL)) {
    url = NetworkHelper.nsIURL(url);
  }

  let name = NetworkHelper.convertToUnicode(
    unescape(url.fileName || url.filePath || "/"));
  let query = NetworkHelper.convertToUnicode(unescape(url.query));

  return name + (query ? "?" + query : "");
};

exports.getUriHostPort = function (url) {
  if (!(url instanceof Ci.nsIURL)) {
    url = NetworkHelper.nsIURL(url);
  }
  return NetworkHelper.convertToUnicode(unescape(url.hostPort));
};

exports.getUriHost = function (url) {
  return exports.getUriHostPort(url).replace(/:\d+$/, "");
};

/**
 * Convert a nsIContentPolicy constant to a display string
 */
const LOAD_CAUSE_STRINGS = {
  [Ci.nsIContentPolicy.TYPE_INVALID]: "invalid",
  [Ci.nsIContentPolicy.TYPE_OTHER]: "other",
  [Ci.nsIContentPolicy.TYPE_SCRIPT]: "script",
  [Ci.nsIContentPolicy.TYPE_IMAGE]: "img",
  [Ci.nsIContentPolicy.TYPE_STYLESHEET]: "stylesheet",
  [Ci.nsIContentPolicy.TYPE_OBJECT]: "object",
  [Ci.nsIContentPolicy.TYPE_DOCUMENT]: "document",
  [Ci.nsIContentPolicy.TYPE_SUBDOCUMENT]: "subdocument",
  [Ci.nsIContentPolicy.TYPE_REFRESH]: "refresh",
  [Ci.nsIContentPolicy.TYPE_XBL]: "xbl",
  [Ci.nsIContentPolicy.TYPE_PING]: "ping",
  [Ci.nsIContentPolicy.TYPE_XMLHTTPREQUEST]: "xhr",
  [Ci.nsIContentPolicy.TYPE_OBJECT_SUBREQUEST]: "objectSubdoc",
  [Ci.nsIContentPolicy.TYPE_DTD]: "dtd",
  [Ci.nsIContentPolicy.TYPE_FONT]: "font",
  [Ci.nsIContentPolicy.TYPE_MEDIA]: "media",
  [Ci.nsIContentPolicy.TYPE_WEBSOCKET]: "websocket",
  [Ci.nsIContentPolicy.TYPE_CSP_REPORT]: "csp",
  [Ci.nsIContentPolicy.TYPE_XSLT]: "xslt",
  [Ci.nsIContentPolicy.TYPE_BEACON]: "beacon",
  [Ci.nsIContentPolicy.TYPE_FETCH]: "fetch",
  [Ci.nsIContentPolicy.TYPE_IMAGESET]: "imageset",
  [Ci.nsIContentPolicy.TYPE_WEB_MANIFEST]: "webManifest",
  [Ci.nsIContentPolicy.TYPE_SAVEAS_DOWNLOAD]: "saveasDownload"
};

exports.loadCauseString = function (causeType) {
  return LOAD_CAUSE_STRINGS[causeType] || "unknown";
};