218 lines
6.8 KiB
JavaScript
218 lines
6.8 KiB
JavaScript
/* 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/. */
|
|
|
|
/**
|
|
* Helper module alongside ParentProcessWatcherRegistry, which focus on updating the "sessionData" object.
|
|
* This object is shared across processes and threads and have to be maintained in all these runtimes.
|
|
*/
|
|
|
|
const lazy = {};
|
|
ChromeUtils.defineESModuleGetters(
|
|
lazy,
|
|
{
|
|
validateBreakpointLocation:
|
|
"resource://devtools/shared/validate-breakpoint.sys.mjs",
|
|
},
|
|
{ global: "contextual" }
|
|
);
|
|
|
|
ChromeUtils.defineLazyGetter(lazy, "validateEventBreakpoint", () => {
|
|
const { loader } = ChromeUtils.importESModule(
|
|
"resource://devtools/shared/loader/Loader.sys.mjs",
|
|
{ global: "contextual" }
|
|
);
|
|
return loader.require(
|
|
"resource://devtools/server/actors/utils/event-breakpoints.js"
|
|
).validateEventBreakpoint;
|
|
});
|
|
|
|
// List of all arrays stored in `sessionData`, which are replicated across processes and threads
|
|
const SUPPORTED_DATA = {
|
|
BLACKBOXING: "blackboxing",
|
|
BREAKPOINTS: "breakpoints",
|
|
BROWSER_ELEMENT_HOST: "browser-element-host",
|
|
XHR_BREAKPOINTS: "xhr-breakpoints",
|
|
EVENT_BREAKPOINTS: "event-breakpoints",
|
|
RESOURCES: "resources",
|
|
TARGET_CONFIGURATION: "target-configuration",
|
|
THREAD_CONFIGURATION: "thread-configuration",
|
|
TARGETS: "targets",
|
|
};
|
|
|
|
// Optional function, if data isn't a primitive data type in order to produce a key
|
|
// for the given data entry
|
|
const DATA_KEY_FUNCTION = {
|
|
[SUPPORTED_DATA.BLACKBOXING]({ url, range }) {
|
|
return (
|
|
url +
|
|
(range
|
|
? `:${range.start.line}:${range.start.column}-${range.end.line}:${range.end.column}`
|
|
: "")
|
|
);
|
|
},
|
|
[SUPPORTED_DATA.BREAKPOINTS]({ location }) {
|
|
lazy.validateBreakpointLocation(location);
|
|
const { sourceUrl, sourceId, line, column } = location;
|
|
return `${sourceUrl}:${sourceId}:${line}:${column}`;
|
|
},
|
|
[SUPPORTED_DATA.TARGET_CONFIGURATION]({ key }) {
|
|
// Configuration data entries are { key, value } objects, `key` can be used
|
|
// as the unique identifier for the entry.
|
|
return key;
|
|
},
|
|
[SUPPORTED_DATA.THREAD_CONFIGURATION]({ key }) {
|
|
// See target configuration comment
|
|
return key;
|
|
},
|
|
[SUPPORTED_DATA.XHR_BREAKPOINTS]({ path, method }) {
|
|
if (typeof path != "string") {
|
|
throw new Error(
|
|
`XHR Breakpoints expect to have path string, got ${typeof path} instead.`
|
|
);
|
|
}
|
|
if (typeof method != "string") {
|
|
throw new Error(
|
|
`XHR Breakpoints expect to have method string, got ${typeof method} instead.`
|
|
);
|
|
}
|
|
return `${path}:${method}`;
|
|
},
|
|
[SUPPORTED_DATA.EVENT_BREAKPOINTS](id) {
|
|
if (typeof id != "string") {
|
|
throw new Error(
|
|
`Event Breakpoints expect the id to be a string , got ${typeof id} instead.`
|
|
);
|
|
}
|
|
if (!lazy.validateEventBreakpoint(id)) {
|
|
throw new Error(
|
|
`The id string should be a valid event breakpoint id, ${id} is not.`
|
|
);
|
|
}
|
|
return id;
|
|
},
|
|
};
|
|
// Optional validation method to assert the shape of each session data entry
|
|
const DATA_VALIDATION_FUNCTION = {
|
|
[SUPPORTED_DATA.BREAKPOINTS]({ location }) {
|
|
lazy.validateBreakpointLocation(location);
|
|
},
|
|
[SUPPORTED_DATA.XHR_BREAKPOINTS]({ path, method }) {
|
|
if (typeof path != "string") {
|
|
throw new Error(
|
|
`XHR Breakpoints expect to have path string, got ${typeof path} instead.`
|
|
);
|
|
}
|
|
if (typeof method != "string") {
|
|
throw new Error(
|
|
`XHR Breakpoints expect to have method string, got ${typeof method} instead.`
|
|
);
|
|
}
|
|
},
|
|
[SUPPORTED_DATA.EVENT_BREAKPOINTS](id) {
|
|
if (typeof id != "string") {
|
|
throw new Error(
|
|
`Event Breakpoints expect the id to be a string , got ${typeof id} instead.`
|
|
);
|
|
}
|
|
if (!lazy.validateEventBreakpoint(id)) {
|
|
throw new Error(
|
|
`The id string should be a valid event breakpoint id, ${id} is not.`
|
|
);
|
|
}
|
|
},
|
|
};
|
|
|
|
function idFunction(v) {
|
|
if (typeof v != "string") {
|
|
throw new Error(
|
|
`Expect data entry values to be string, or be using custom data key functions. Got ${typeof v} type instead.`
|
|
);
|
|
}
|
|
return v;
|
|
}
|
|
|
|
export const SessionDataHelpers = {
|
|
SUPPORTED_DATA,
|
|
|
|
/**
|
|
* Add new values to the shared "sessionData" object.
|
|
*
|
|
* @param Object sessionData
|
|
* The data object to update.
|
|
* @param string type
|
|
* The type of data to be added
|
|
* @param Array<Object> entries
|
|
* The values to be added to this type of data
|
|
* @param String updateType
|
|
* "add" will only add the new entries in the existing data set.
|
|
* "set" will update the data set with the new entries.
|
|
*/
|
|
addOrSetSessionDataEntry(sessionData, type, entries, updateType) {
|
|
const validationFunction = DATA_VALIDATION_FUNCTION[type];
|
|
if (validationFunction) {
|
|
entries.forEach(validationFunction);
|
|
}
|
|
|
|
// When we are replacing the whole entries, things are significantly simplier
|
|
if (updateType == "set") {
|
|
sessionData[type] = entries;
|
|
return;
|
|
}
|
|
|
|
if (!sessionData[type]) {
|
|
sessionData[type] = [];
|
|
}
|
|
const toBeAdded = [];
|
|
const keyFunction = DATA_KEY_FUNCTION[type] || idFunction;
|
|
for (const entry of entries) {
|
|
const existingIndex = sessionData[type].findIndex(existingEntry => {
|
|
return keyFunction(existingEntry) === keyFunction(entry);
|
|
});
|
|
if (existingIndex === -1) {
|
|
// New entry.
|
|
toBeAdded.push(entry);
|
|
} else {
|
|
// Existing entry, update the value. This is relevant if the data-entry
|
|
// is not a primitive data-type, and the value can change for the same
|
|
// key.
|
|
sessionData[type][existingIndex] = entry;
|
|
}
|
|
}
|
|
sessionData[type].push(...toBeAdded);
|
|
},
|
|
|
|
/**
|
|
* Remove values from the shared "sessionData" object.
|
|
*
|
|
* @param Object sessionData
|
|
* The data object to update.
|
|
* @param string type
|
|
* The type of data to be remove
|
|
* @param Array<Object> entries
|
|
* The values to be removed from this type of data
|
|
* @return Boolean
|
|
* True, if at least one entries existed and has been removed.
|
|
* False, if none of the entries existed and none has been removed.
|
|
*/
|
|
removeSessionDataEntry(sessionData, type, entries) {
|
|
let includesAtLeastOne = false;
|
|
const keyFunction = DATA_KEY_FUNCTION[type] || idFunction;
|
|
for (const entry of entries) {
|
|
const idx = sessionData[type]
|
|
? sessionData[type].findIndex(existingEntry => {
|
|
return keyFunction(existingEntry) === keyFunction(entry);
|
|
})
|
|
: -1;
|
|
if (idx !== -1) {
|
|
sessionData[type].splice(idx, 1);
|
|
includesAtLeastOne = true;
|
|
}
|
|
}
|
|
if (!includesAtLeastOne) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
},
|
|
};
|