summaryrefslogtreecommitdiffstats
path: root/devtools/client/netmonitor/src/workers
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/netmonitor/src/workers')
-rw-r--r--devtools/client/netmonitor/src/workers/moz.build8
-rw-r--r--devtools/client/netmonitor/src/workers/search/index.js25
-rw-r--r--devtools/client/netmonitor/src/workers/search/moz.build10
-rw-r--r--devtools/client/netmonitor/src/workers/search/search.js349
-rw-r--r--devtools/client/netmonitor/src/workers/search/worker.js17
5 files changed, 409 insertions, 0 deletions
diff --git a/devtools/client/netmonitor/src/workers/moz.build b/devtools/client/netmonitor/src/workers/moz.build
new file mode 100644
index 0000000000..a1384c1e10
--- /dev/null
+++ b/devtools/client/netmonitor/src/workers/moz.build
@@ -0,0 +1,8 @@
+# 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/.
+
+DIRS += [
+ "search",
+]
diff --git a/devtools/client/netmonitor/src/workers/search/index.js b/devtools/client/netmonitor/src/workers/search/index.js
new file mode 100644
index 0000000000..a721454034
--- /dev/null
+++ b/devtools/client/netmonitor/src/workers/search/index.js
@@ -0,0 +1,25 @@
+/* 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 {
+ WorkerDispatcher,
+} = require("resource://devtools/client/shared/worker-utils.js");
+
+const SEARCH_WORKER_URL =
+ "resource://devtools/client/netmonitor/src/workers/search/worker.js";
+
+class SearchDispatcher extends WorkerDispatcher {
+ constructor() {
+ super(SEARCH_WORKER_URL);
+ }
+
+ // The search worker support just one task at this point,
+ // which is searching through specified resource.
+ searchInResource = this.task("searchInResource");
+}
+
+// Compared to debugger, we instantiate a singleton within the dispatcher module
+module.exports = new SearchDispatcher();
diff --git a/devtools/client/netmonitor/src/workers/search/moz.build b/devtools/client/netmonitor/src/workers/search/moz.build
new file mode 100644
index 0000000000..ee5a5dd950
--- /dev/null
+++ b/devtools/client/netmonitor/src/workers/search/moz.build
@@ -0,0 +1,10 @@
+# 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/.
+
+DevToolsModules(
+ "index.js",
+ "search.js",
+ "worker.js",
+)
diff --git a/devtools/client/netmonitor/src/workers/search/search.js b/devtools/client/netmonitor/src/workers/search/search.js
new file mode 100644
index 0000000000..880f5357b2
--- /dev/null
+++ b/devtools/client/netmonitor/src/workers/search/search.js
@@ -0,0 +1,349 @@
+/* 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/. */
+/* eslint-disable no-unused-vars */
+
+"use strict";
+
+/**
+ * Search within specified resource. Note that this function runs
+ * within a worker thread.
+ */
+function searchInResource(resource, query, modifiers) {
+ const results = [];
+
+ if (resource.url) {
+ results.push(
+ findMatches(resource, query, modifiers, {
+ key: "url",
+ label: "Url",
+ type: "url",
+ panel: "headers",
+ })
+ );
+ }
+
+ if (resource.responseHeaders) {
+ results.push(
+ findMatches(resource, query, modifiers, {
+ key: "responseHeaders.headers",
+ type: "responseHeaders",
+ panel: "headers",
+ })
+ );
+ }
+
+ if (resource.requestHeaders) {
+ results.push(
+ findMatches(resource, query, modifiers, {
+ key: "requestHeaders.headers",
+ type: "requestHeaders",
+ panel: "headers",
+ })
+ );
+ }
+
+ if (resource.requestHeadersFromUploadStream) {
+ results.push(
+ findMatches(resource, query, modifiers, {
+ key: "requestHeadersFromUploadStream.headers",
+ type: "requestHeadersFromUploadStream",
+ panel: "headers",
+ })
+ );
+ }
+
+ if (resource.responseCookies) {
+ let key = "responseCookies";
+
+ if (resource.responseCookies.cookies) {
+ key = "responseCookies.cookies";
+ }
+
+ results.push(
+ findMatches(resource, query, modifiers, {
+ key,
+ type: "responseCookies",
+ panel: "cookies",
+ })
+ );
+ }
+
+ if (resource.requestCookies) {
+ let key = "requestCookies";
+
+ if (resource.requestCookies.cookies) {
+ key = "requestCookies.cookies";
+ }
+
+ results.push(
+ findMatches(resource, query, modifiers, {
+ key,
+ type: "requestCookies",
+ panel: "cookies",
+ })
+ );
+ }
+
+ if (resource.responseContent) {
+ results.push(
+ findMatches(resource, query, modifiers, {
+ key: "responseContent.content.text",
+ type: "responseContent",
+ panel: "response",
+ })
+ );
+ }
+
+ if (resource.requestPostData) {
+ results.push(
+ findMatches(resource, query, modifiers, {
+ key: "requestPostData.postData.text",
+ type: "requestPostData",
+ panel: "request",
+ })
+ );
+ }
+
+ return getResults(results, resource);
+}
+
+/**
+ * Concatenates all results
+ * @param results
+ * @returns {*[]}
+ */
+function getResults(results, resource) {
+ const tempResults = [].concat.apply([], results);
+
+ // Generate unique result keys
+ tempResults.forEach((result, index) => {
+ result.key = index;
+ result.parentResource = resource;
+ });
+
+ return tempResults;
+}
+
+function find(query, modifiers, source) {
+ const { caseSensitive } = modifiers;
+ const value = caseSensitive ? source : source.toLowerCase();
+ const q = caseSensitive ? query : query.toLowerCase();
+
+ return value.includes(q);
+}
+
+/**
+ * Find query matches in arrays, objects and strings.
+ * @param resource
+ * @param query
+ * @param modifiers
+ * @param data
+ * @returns {*[]|[]|Array|*}
+ */
+function findMatches(resource, query, modifiers, data) {
+ if (!resource || !query || !modifiers || !data) {
+ return [];
+ }
+
+ const resourceValue = getValue(data.key, resource);
+ const resourceType = getType(resourceValue);
+
+ if (resource.hasOwnProperty("name") && resource.hasOwnProperty("value")) {
+ return searchInProperties(query, modifiers, resource, data);
+ }
+
+ switch (resourceType) {
+ case "string":
+ return searchInText(query, modifiers, resourceValue, data);
+ case "array":
+ return searchInArray(query, modifiers, resourceValue, data);
+ case "object":
+ return searchInObject(query, modifiers, resourceValue, data);
+ default:
+ return [];
+ }
+}
+
+function searchInProperties(query, modifiers, obj, data) {
+ const { name, value } = obj;
+ const match = {
+ ...data,
+ };
+
+ if (find(query, modifiers, name)) {
+ match.label = name;
+ }
+
+ if (find(query, modifiers, name) || find(query, modifiers, value)) {
+ match.value = value;
+ match.startIndex = value.indexOf(query);
+
+ return match;
+ }
+
+ return [];
+}
+
+/**
+ * Get type of resource - deals with arrays as well.
+ * @param resource
+ * @returns {*}
+ */
+function getType(resource) {
+ return Array.isArray(resource) ? "array" : typeof resource;
+}
+
+/**
+ * Function returns the value of a key, included nested keys.
+ * @param path
+ * @param obj
+ * @returns {*}
+ */
+function getValue(path, obj) {
+ const properties = Array.isArray(path) ? path : path.split(".");
+ return properties.reduce((prev, curr) => prev?.[curr], obj);
+}
+
+/**
+ * Search text for specific string and return all matches found
+ * @param query
+ * @param modifiers
+ * @param text
+ * @param data
+ * @returns {*}
+ */
+function searchInText(query, modifiers, text, data) {
+ const { type } = data;
+ const lines = text.split(/\r\n|\r|\n/);
+ const matches = [];
+
+ // iterate through each line
+ lines.forEach((curr, i) => {
+ const { caseSensitive } = modifiers;
+ const flags = caseSensitive ? "g" : "gi";
+ const regexQuery = RegExp(
+ caseSensitive ? query : query.toLowerCase(),
+ flags
+ );
+ const lineMatches = [];
+ let singleMatch;
+
+ while ((singleMatch = regexQuery.exec(lines[i])) !== null) {
+ const startIndex = regexQuery.lastIndex;
+ lineMatches.push(startIndex);
+ }
+
+ if (lineMatches.length !== 0) {
+ const line = i + 1;
+ const match = {
+ ...data,
+ label: type !== "url" ? line + "" : "Url",
+ line,
+ startIndex: lineMatches,
+ };
+
+ match.value =
+ lineMatches.length === 1
+ ? getTruncatedValue(lines[i], query, lineMatches[0])
+ : lines[i];
+
+ matches.push(match);
+ }
+ });
+
+ return matches.length === 0 ? [] : matches;
+}
+
+/**
+ * Search for query in array.
+ * Iterates through each array item and handles item based on type.
+ * @param query
+ * @param modifiers
+ * @param arr
+ * @param data
+ * @returns {*[]}
+ */
+function searchInArray(query, modifiers, arr, data) {
+ const { key, label } = data;
+ const matches = arr.map((match, i) =>
+ findMatches(match, query, modifiers, {
+ ...data,
+ label: match.hasOwnProperty("name") ? match.name : label,
+ key: key + ".[" + i + "]",
+ })
+ );
+
+ return getResults(matches);
+}
+
+/**
+ * Return query match and up to 50 characters on left and right.
+ * (50) + [matched query] + (50)
+ * @param value
+ * @param query
+ * @param startIndex
+ * @returns {*}
+ */
+function getTruncatedValue(value, query, startIndex) {
+ const valueSize = value.length;
+ const indexEnd = startIndex + query.length;
+
+ if (valueSize < 100 + query.length) {
+ return value;
+ }
+
+ const start = value.substring(startIndex, startIndex - 50);
+ const end = value.substring(indexEnd, indexEnd + 50);
+
+ return start + end;
+}
+
+/**
+ * Iterates through object, including nested objects, returns all
+ * @param query
+ * @param modifiers
+ * @param obj
+ * @param data
+ * @returns {*|[]}
+ */
+function searchInObject(query, modifiers, obj, data) {
+ const matches = data.hasOwnProperty("collector") ? data.collector : [];
+ const { caseSensitive } = modifiers;
+
+ for (const objectKey in obj) {
+ const objectKeyType = getType(obj[objectKey]);
+
+ // if the value is an object, send to search in object
+ if (objectKeyType === "object" && Object.keys(obj[objectKey].length > 1)) {
+ searchInObject(query, obj[objectKey], {
+ ...data,
+ collector: matches,
+ });
+
+ continue;
+ }
+
+ const value = !caseSensitive
+ ? obj[objectKey].toLowerCase()
+ : obj[objectKey];
+ const key = !caseSensitive ? objectKey.toLowerCase() : objectKey;
+ const q = !caseSensitive ? query.toLowerCase() : query;
+
+ if ((objectKeyType === "string" && value.includes(q)) || key.includes(q)) {
+ const match = {
+ ...data,
+ };
+
+ const startIndex = value.indexOf(q);
+
+ match.label = objectKey;
+ match.startIndex = startIndex;
+ match.value = getTruncatedValue(obj[objectKey], query, startIndex);
+
+ matches.push(match);
+ }
+ }
+
+ return matches;
+}
diff --git a/devtools/client/netmonitor/src/workers/search/worker.js b/devtools/client/netmonitor/src/workers/search/worker.js
new file mode 100644
index 0000000000..825bf34360
--- /dev/null
+++ b/devtools/client/netmonitor/src/workers/search/worker.js
@@ -0,0 +1,17 @@
+/* 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";
+
+/* eslint-env worker */
+
+/* import-globals-from search.js */
+/* import-globals-from ../../../../shared/worker-utils.js */
+importScripts(
+ "resource://devtools/client/netmonitor/src/workers/search/search.js",
+ "resource://devtools/client/shared/worker-utils.js"
+);
+
+// Implementation of search worker (runs in worker scope).
+self.onmessage = workerHandler({ searchInResource });