summaryrefslogtreecommitdiffstats
path: root/devtools/client/netmonitor/custom-request-view.js
blob: 3159ffcc7107f64a4bf53d1047e6c1b39b75b6a2 (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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/* 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/. */
/* globals window, dumpn, gNetwork, $, EVENTS, NetMonitorView */
"use strict";

const {Task} = require("devtools/shared/task");
const {writeHeaderText, getKeyWithEvent} = require("./request-utils");

loader.lazyRequireGetter(this, "NetworkHelper",
  "devtools/shared/webconsole/network-helper");

/**
 * Functions handling the custom request view.
 */
function CustomRequestView() {
  dumpn("CustomRequestView was instantiated");
}

CustomRequestView.prototype = {
  /**
   * Initialization function, called when the network monitor is started.
   */
  initialize: function () {
    dumpn("Initializing the CustomRequestView");

    this.updateCustomRequestEvent = getKeyWithEvent(this.onUpdate.bind(this));
    $("#custom-pane").addEventListener("input",
      this.updateCustomRequestEvent, false);
  },

  /**
   * Destruction function, called when the network monitor is closed.
   */
  destroy: function () {
    dumpn("Destroying the CustomRequestView");

    $("#custom-pane").removeEventListener("input",
      this.updateCustomRequestEvent, false);
  },

  /**
   * Populates this view with the specified data.
   *
   * @param object data
   *        The data source (this should be the attachment of a request item).
   * @return object
   *        Returns a promise that resolves upon population the view.
   */
  populate: Task.async(function* (data) {
    $("#custom-url-value").value = data.url;
    $("#custom-method-value").value = data.method;
    this.updateCustomQuery(data.url);

    if (data.requestHeaders) {
      let headers = data.requestHeaders.headers;
      $("#custom-headers-value").value = writeHeaderText(headers);
    }
    if (data.requestPostData) {
      let postData = data.requestPostData.postData.text;
      $("#custom-postdata-value").value = yield gNetwork.getString(postData);
    }

    window.emit(EVENTS.CUSTOMREQUESTVIEW_POPULATED);
  }),

  /**
   * Handle user input in the custom request form.
   *
   * @param object field
   *        the field that the user updated.
   */
  onUpdate: function (field) {
    let selectedItem = NetMonitorView.RequestsMenu.selectedItem;
    let value;

    switch (field) {
      case "method":
        value = $("#custom-method-value").value.trim();
        selectedItem.attachment.method = value;
        break;
      case "url":
        value = $("#custom-url-value").value;
        this.updateCustomQuery(value);
        selectedItem.attachment.url = value;
        break;
      case "query":
        let query = $("#custom-query-value").value;
        this.updateCustomUrl(query);
        field = "url";
        value = $("#custom-url-value").value;
        selectedItem.attachment.url = value;
        break;
      case "body":
        value = $("#custom-postdata-value").value;
        selectedItem.attachment.requestPostData = { postData: { text: value } };
        break;
      case "headers":
        let headersText = $("#custom-headers-value").value;
        value = parseHeadersText(headersText);
        selectedItem.attachment.requestHeaders = { headers: value };
        break;
    }

    NetMonitorView.RequestsMenu.updateMenuView(selectedItem, field, value);
  },

  /**
   * Update the query string field based on the url.
   *
   * @param object url
   *        The URL to extract query string from.
   */
  updateCustomQuery: function (url) {
    let paramsArray = NetworkHelper.parseQueryString(
      NetworkHelper.nsIURL(url).query);

    if (!paramsArray) {
      $("#custom-query").hidden = true;
      return;
    }

    $("#custom-query").hidden = false;
    $("#custom-query-value").value = writeQueryText(paramsArray);
  },

  /**
   * Update the url based on the query string field.
   *
   * @param object queryText
   *        The contents of the query string field.
   */
  updateCustomUrl: function (queryText) {
    let params = parseQueryText(queryText);
    let queryString = writeQueryString(params);

    let url = $("#custom-url-value").value;
    let oldQuery = NetworkHelper.nsIURL(url).query;
    let path = url.replace(oldQuery, queryString);

    $("#custom-url-value").value = path;
  }
};

/**
 * Parse text representation of multiple HTTP headers.
 *
 * @param string text
 *        Text of headers
 * @return array
 *         Array of headers info {name, value}
 */
function parseHeadersText(text) {
  return parseRequestText(text, "\\S+?", ":");
}

/**
 * Parse readable text list of a query string.
 *
 * @param string text
 *        Text of query string represetation
 * @return array
 *         Array of query params {name, value}
 */
function parseQueryText(text) {
  return parseRequestText(text, ".+?", "=");
}

/**
 * Parse a text representation of a name[divider]value list with
 * the given name regex and divider character.
 *
 * @param string text
 *        Text of list
 * @return array
 *         Array of headers info {name, value}
 */
function parseRequestText(text, namereg, divider) {
  let regex = new RegExp("(" + namereg + ")\\" + divider + "\\s*(.+)");
  let pairs = [];

  for (let line of text.split("\n")) {
    let matches;
    if (matches = regex.exec(line)) { // eslint-disable-line
      let [, name, value] = matches;
      pairs.push({name: name, value: value});
    }
  }
  return pairs;
}

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

/**
 * Write out a list of query params into a query string
 *
 * @param array params
 *        Array of query  params {name, value}
 * @return string
 *         Query string that can be appended to a url.
 */
function writeQueryString(params) {
  return params.map(({name, value}) => name + "=" + value).join("&");
}

exports.CustomRequestView = CustomRequestView;