summaryrefslogtreecommitdiffstats
path: root/devtools/client/netmonitor/src/utils/filter-autocomplete-provider.js
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--devtools/client/netmonitor/src/utils/filter-autocomplete-provider.js209
1 files changed, 209 insertions, 0 deletions
diff --git a/devtools/client/netmonitor/src/utils/filter-autocomplete-provider.js b/devtools/client/netmonitor/src/utils/filter-autocomplete-provider.js
new file mode 100644
index 0000000000..991027c67b
--- /dev/null
+++ b/devtools/client/netmonitor/src/utils/filter-autocomplete-provider.js
@@ -0,0 +1,209 @@
+/* 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";
+
+const {
+ FILTER_FLAGS,
+ SUPPORTED_HTTP_CODES,
+} = require("resource://devtools/client/netmonitor/src/constants.js");
+
+const {
+ getRequestPriorityAsText,
+} = require("resource://devtools/client/netmonitor/src/utils/format-utils.js");
+
+/**
+ * Generates a value for the given filter
+ * ie. if flag = status-code, will generate "200" from the given request item.
+ * For flags related to cookies, it might generate an array based on the request
+ * ie. ["cookie-name-1", "cookie-name-2", ...]
+ *
+ * @param {string} flag - flag specified in filter, ie. "status-code"
+ * @param {object} request - Network request item
+ * @return {string|Array} - The output is a string or an array based on the request
+ */
+function getAutocompleteValuesForFlag(flag, request) {
+ let values = [];
+ let { responseCookies = { cookies: [] } } = request;
+ responseCookies = responseCookies.cookies || responseCookies;
+
+ switch (flag) {
+ case "status-code":
+ // Sometimes status comes as Number
+ values.push(String(request.status));
+ break;
+ case "scheme":
+ values.push(request.urlDetails.scheme);
+ break;
+ case "domain":
+ values.push(request.urlDetails.host);
+ break;
+ case "remote-ip":
+ values.push(request.remoteAddress);
+ break;
+ case "cause":
+ values.push(request.cause.type);
+ break;
+ case "mime-type":
+ values.push((request.mimeType || "").replace(/;.+/, ""));
+ break;
+ case "set-cookie-name":
+ values = responseCookies.map(c => c.name);
+ break;
+ case "set-cookie-value":
+ values = responseCookies.map(c => c.value);
+ break;
+ case "priority":
+ values.push(getRequestPriorityAsText(request.priority));
+ break;
+ case "set-cookie-domain":
+ values = responseCookies.map(c =>
+ c.hasOwnProperty("domain") ? c.domain : request.urlDetails.host
+ );
+ break;
+ case "is":
+ values = ["cached", "from-cache", "running"];
+ break;
+ case "has-response-header":
+ // Some requests not having responseHeaders..?
+ values = request.responseHeaders
+ ? request.responseHeaders.headers.map(h => h.name)
+ : [];
+ break;
+ case "protocol":
+ values.push(request.httpVersion);
+ break;
+ case "method":
+ default:
+ values.push(request[flag]);
+ }
+
+ return values;
+}
+
+/**
+ * For a given lastToken passed ie. "is:", returns an array of populated flag
+ * values for consumption in autocompleteProvider
+ * ie. ["is:cached", "is:running", "is:from-cache"]
+ *
+ * @param {string} lastToken - lastToken parsed from filter input, ie "is:"
+ * @param {object} requests - List of requests from which values are generated
+ * @return {Array} - array of autocomplete values
+ */
+function getLastTokenFlagValues(lastToken, requests) {
+ // The last token must be a string like "method:GET" or "method:", Any token
+ // without a ":" cant be used to parse out flag values
+ if (!lastToken.includes(":")) {
+ return [];
+ }
+
+ // Parse out possible flag from lastToken
+ let [flag, typedFlagValue] = lastToken.split(":");
+ let isNegativeFlag = false;
+
+ // Check if flag is used with negative match
+ if (flag.startsWith("-")) {
+ flag = flag.slice(1);
+ isNegativeFlag = true;
+ }
+
+ // Flag is some random string, return
+ if (!FILTER_FLAGS.includes(flag)) {
+ return [];
+ }
+
+ let values = [];
+ for (const request of requests) {
+ values.push(...getAutocompleteValuesForFlag(flag, request));
+ }
+ values = [...new Set(values)];
+
+ return values
+ .filter(value => value)
+ .filter(value => {
+ if (typedFlagValue && value) {
+ const lowerTyped = typedFlagValue.toLowerCase();
+ const lowerValue = value.toLowerCase();
+ return lowerValue.includes(lowerTyped) && lowerValue !== lowerTyped;
+ }
+ return (
+ typeof value !== "undefined" && value !== "" && value !== "undefined"
+ );
+ })
+ .sort()
+ .map(value => (isNegativeFlag ? `-${flag}:${value}` : `${flag}:${value}`));
+}
+
+/**
+ * Generates an autocomplete list for the search-box for network monitor
+ *
+ * It expects an entire string of the searchbox ie "is:cached pr".
+ * The string is then tokenized into "is:cached" and "pr"
+ *
+ * @param {string} filter - The entire search string of the search box
+ * @param {object} requests - Iteratable object of requests displayed
+ * @return {Array} - The output is an array of objects as below
+ * [{value: "is:cached protocol", displayValue: "protocol"}[, ...]]
+ * `value` is used to update the search-box input box for given item
+ * `displayValue` is used to render the autocomplete list
+ */
+function autocompleteProvider(filter, requests) {
+ if (!filter) {
+ return [];
+ }
+
+ const negativeAutocompleteList = FILTER_FLAGS.map(item => `-${item}`);
+ const baseList = [...FILTER_FLAGS, ...negativeAutocompleteList].map(
+ item => `${item}:`
+ );
+
+ // The last token is used to filter the base autocomplete list
+ const tokens = filter.split(/\s+/g);
+ const lastToken = tokens[tokens.length - 1];
+ const previousTokens = tokens.slice(0, tokens.length - 1);
+
+ // Autocomplete list is not generated for empty lastToken
+ if (!lastToken) {
+ return [];
+ }
+
+ let autocompleteList;
+ const availableValues = getLastTokenFlagValues(lastToken, requests);
+ if (availableValues.length) {
+ autocompleteList = availableValues;
+ } else {
+ const isNegativeFlag = lastToken.startsWith("-");
+
+ // Stores list of HTTP codes that starts with value of lastToken
+ const filteredStatusCodes = SUPPORTED_HTTP_CODES.filter(item => {
+ item = isNegativeFlag ? item.substr(1) : item;
+ return item.toLowerCase().startsWith(lastToken.toLowerCase());
+ });
+
+ if (filteredStatusCodes.length) {
+ // Shows an autocomplete list of "status-code" values from filteredStatusCodes
+ autocompleteList = isNegativeFlag
+ ? filteredStatusCodes.map(item => `-status-code:${item}`)
+ : filteredStatusCodes.map(item => `status-code:${item}`);
+ } else {
+ // Shows an autocomplete list of values from baseList
+ // that starts with value of lastToken
+ autocompleteList = baseList.filter(item => {
+ return (
+ item.toLowerCase().startsWith(lastToken.toLowerCase()) &&
+ item.toLowerCase() !== lastToken.toLowerCase()
+ );
+ });
+ }
+ }
+
+ return autocompleteList.sort().map(item => ({
+ value: [...previousTokens, item].join(" "),
+ displayValue: item,
+ }));
+}
+
+module.exports = {
+ autocompleteProvider,
+};