/* 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/>. */
function initialState(previousState = { searchValueOrGrip: NO_SEARCH_VALUE }) { return { // These fields are mutable as they are large arrays and UI will rerender based on their size
// The three next array are always of the same size. // List of all trace resources, as defined by the server codebase (See the TracerActor)
mutableTraces: [], // Array of arrays. This is of the same size as mutableTraces. // Store the indexes within mutableTraces of each children matching the same index in mutableTraces.
mutableChildren: [], // Indexes of parents within mutableTraces.
mutableParents: [],
// Frames are also a trace resources, but they are stored in a dedicated array.
mutableFrames: [],
// List of indexes within mutableTraces of top level trace, without any parent.
mutableTopTraces: [],
// Similar to mutableTopTraces except that filter out unwanted DOM Events.
mutableFilteredTopTraces: [],
// List of all trace resources indexes within mutableTraces which are about dom mutations
mutableMutationTraces: [],
// List of all traces matching the current search string // (this isn't only top traces)
mutableMatchingTraces: [],
// If the user started searching for some value, it may be an invalid expression // and the error related to this will be stored as a string in this attribute.
searchExceptionMessage: null,
// If a valid search has been requested, the actual value for this search is stored in this attribute. // It can be either a primitive data type, or an object actor form (aka grip)
searchValueOrGrip: previousState.searchValueOrGrip,
// List of all event names which triggered some JavaScript code in the current tracer record.
mutableEventNames: new Set(),
// List of all possible DOM Events (similar to DOM Event panel) // This is initialized once on debugger startup. // This is a Map of category objects consumed by EventListeners React component, // keyed by DOM Event name (string) communicated by the Tracer. // DOM Event name can look like this: // - global.click (click fired on window object) // - node.mousemove (mousemove fired on a DOM Element) // - xhr.error (error on an XMLHttpRequest object) // - worker.error (error from a worker) // - setTimeout (setTimeout function being called) // - setTimeoutCallback (setTimeout callback being fired)
domEventInfoByTracerName:
previousState.domEventInfoByTracerName || new Map(),
// List of DOM Events "categories" currently available in the current traces // Categories are objects consumed by the EventListener React component.
domEventCategories: [],
// List of DOM Events which should be show and be in `mutableFilteredTopTraces`
activeDomEvents: [],
// List of DOM Events names to be highlighted in the left timeline
highlightedDomEvents: [],
// Index of the currently selected trace within `mutableTraces`.
selectedTraceIndex: null,
// Object like the one generated by `generateInlinePreview`, but for the currently selected trace
previews: null,
// Runtime versions to help show warning when there is a mismatch between frontend and backend versions
localPlatformVersion: null,
remotePlatformVersion: null,
// Is it currently recording trace *and* is collecting values
traceValues: false,
};
}
// Also compute the inline preview data when we select a trace // and we have the values recording enabled.
previews: generatePreviewsForTrace(state, trace),
};
}
// Reset the selected trace and previews when we pause/step/select a frame in the scope panel, // so that it is no longer highlighted, nor do we show inline variables. return {
...state,
selectedTraceIndex: null,
previews: null,
};
}
case"SET_SELECTED_LOCATION": { // Traces are reference to the generated location only, so ignore any original source being selected // and wait for SET_GENERATED_SELECTED_LOCATION instead. if (action.location.source.isOriginal) return state;
// Ignore if the currently selected trace matches the new location. if (
state.selectedTrace &&
locationMatchTrace(action.location, state.selectedTrace)
) { return state;
}
// Lookup for a trace matching the newly selected location for (const trace of state.mutableTraces) { if (locationMatchTrace(action.location, trace)) { return {
...state,
selectedTrace: trace,
};
}
}
return {
...state,
selectedTrace: null,
};
}
case"SET_GENERATED_SELECTED_LOCATION": { // When selecting an original location, we have to wait for the newly selected original location // to be mapped to a generated location so that we can find a matching trace.
// Ignore if the currently selected trace matches the new location. if (
state.selectedTrace &&
locationMatchTrace(action.generatedLocation, state.selectedTrace)
) { return state;
}
// Lookup for a trace matching the newly selected location for (const trace of state.mutableTraces) { if (locationMatchTrace(action.generatedLocation, trace)) { return {
...state,
selectedTrace: trace,
};
}
}
// If all the DOM events are shown, return the unfiltered list as-is. if (action.active.length == state.mutableEventNames.size) { return {
...state,
mutableFilteredTopTraces: mutableTopTraces,
activeDomEvents: action.active,
};
}
// Update `mutableFilteredTopTraces` by re-filtering all top traces from `mutableTopTraces` // and considering the new list of DOM event names const mutableFilteredTopTraces = []; for (const traceIndex of mutableTopTraces) { const trace = mutableTraces[traceIndex]; const type = trace[TRACER_FIELDS_INDEXES.TYPE]; if (type == "event") { const eventName = trace[TRACER_FIELDS_INDEXES.EVENT_NAME];
// Map JS Tracer event name into an Event Breakpoint's ID, as `action.active` is an array of such IDs. // (from "node.click" to "event.mouse.click") const id =
state.domEventInfoByTracerName.get(eventName)?.id ||
`event.unclassified.${eventName}`;
function matchParent(traceIndex, depth) { // The very last element is the one matching traceIndex, // so pick the one added just before. // We consider that traces are reported by the server in the execution order.
let idx = mutableTraces.length - 2; while (idx != null) { const trace = mutableTraces[idx]; if (!trace) { break;
} const currentDepth = trace[TRACER_FIELDS_INDEXES.DEPTH]; if (currentDepth < depth) {
mutableChildren[idx].push(traceIndex);
mutableParents.push(idx); return;
}
idx = mutableParents[idx];
}
// If no parent was found, flag it as top level trace
mutableTopTraces.push(traceIndex);
mutableFilteredTopTraces.push(traceIndex);
mutableParents.push(null);
} for (const traceResource of traces) { // For now, only consider traces from the top level target/thread if (!traceResource.targetFront.isTopLevel) { continue;
}
const type = traceResource[TRACER_FIELDS_INDEXES.TYPE];
switch (type) { case"frame": { // Store the object used by SmartTraces
mutableFrames.push({
functionDisplayName: traceResource[TRACER_FIELDS_INDEXES.FRAME_NAME],
source: traceResource[TRACER_FIELDS_INDEXES.FRAME_URL],
sourceId: traceResource[TRACER_FIELDS_INDEXES.FRAME_SOURCEID],
line: traceResource[TRACER_FIELDS_INDEXES.FRAME_LINE],
column: traceResource[TRACER_FIELDS_INDEXES.FRAME_COLUMN],
}); break;
}
// Map JS Tracer event name into an Event Breakpoint's ID, as `action.active` is an array of such IDs. // (from "node.click" to "event.mouse.click") const id =
state.domEventInfoByTracerName.get(eventName)?.id ||
`event.unclassified.${eventName}`;
// Only register in the filtered list, if this event type isn't filtered out. // (do this after `registerDOMEvent`, as that will populate `activeDomEvents` array. if (state.activeDomEvents.includes(id)) {
mutableFilteredTopTraces.push(traceIndex);
} break;
}
}
}
}
// EventListener's category for all events that are not breakable, and not returned by the thread actor, and not in `domEventInfoByTracerName`. const UNCLASSIFIED_CATEGORY = { id: "unclassified", name: "Unclassified" };
/** * Register this possibly new event type in data set used to display EventListener React component. * * @param {Object} state * @param {String} eventName
*/ function registerDOMEvent(state, eventName) { if (state.mutableEventNames.has(eventName)) { return;
}
state.mutableEventNames.add(eventName);
// `domEventInfoByTracerName` is defined by the server and only register the events // for which we can set breakpoints for. // Fallback to a "unclassified" category for all these missing event types. const { category, id, name } = state.domEventInfoByTracerName.get(
eventName
) || {
category: UNCLASSIFIED_CATEGORY,
id: `event.unclassified.${eventName}`,
name: eventName,
};
// By default, when we get a new event type, it is made visible if (!state.activeDomEvents.includes(id)) {
state.activeDomEvents.push(id);
}
let newCategory = state.domEventCategories.find(
cat => cat.name == category.name
); if (!newCategory) { // Create a new category with an empty event list
newCategory = { id: category.id, name: category.name, events: [] };
state.domEventCategories = [...state.domEventCategories];
addSortedCategoryOrEvent(state.domEventCategories, newCategory);
} if (!newCategory.events.some(e => e.name == eventName)) { // Register this new event in the category's event list
addSortedCategoryOrEvent(newCategory.events, { id, name }); // Clone the root object to force a re-render of EventListeners React component // Cloning newCategory(.events) wouldn't be enough as that's not returned by a mapStateToProps.
state.domEventCategories = [...state.domEventCategories];
}
}
function addSortedCategoryOrEvent(array, newElement) { const index = lazy.BinarySearch.insertionIndexOf( function (a, b) { // Both category and event are using `name` as display label return a.name.localeCompare(b.name);
},
array,
newElement
);
array.splice(index, 0, newElement);
}
/** * Reports if a given trace matches the current searched argument value. * * @param {Object} trace * The trace object communicated by the backend. * @param {any primitive|ObjectActor's form} searchValueOrGrip * Either a primitive value (string, number, boolean, …) to match directly, * or, an object actor form where we could match the actor ID.
*/ function isTraceMatchingSearch(trace, searchValueOrGrip) { const argumentValues = trace[TRACER_FIELDS_INDEXES.ENTER_ARGS]; if (!argumentValues) { returnfalse;
} if (searchValueOrGrip) { const { actor } = searchValueOrGrip; if (actor) { return argumentValues.some(v => v.actor === searchValueOrGrip.actor);
}
} // `null` and `undefined` aren't serialized as-is and have a special grip object if (searchValueOrGrip === null) { return argumentValues.some(v => v?.type == "null");
} elseif (searchValueOrGrip === undefined) { return argumentValues.some(v => v?.type == "undefined");
} return argumentValues.some(v => v === searchValueOrGrip);
}
/** * Generate the previews object consumed by InlinePreviews React component. * * @param {Object} state * @param {Object} trace * Trace reducer object. * @return {Object} * Previews consumed by InlinePreviews.
*/ function generatePreviewsForTrace(state, trace) {
let previews = state.previews; const argumentValues = trace[TRACER_FIELDS_INDEXES.ENTER_ARGS]; const argumentNames = trace[TRACER_FIELDS_INDEXES.ENTER_ARG_NAMES]; if (argumentNames && argumentValues) { const frameIndex = trace[TRACER_FIELDS_INDEXES.FRAME_INDEX]; const frame = state.mutableFrames[frameIndex]; // Subtracting 1 from line as codemirror lines are 0 indexed const line = frame.line - 1; const column = frame.column;
const preview = []; for (let i = 0; i < argumentNames.length; i++) { const name = argumentNames[i];
// Values are either primitives, or an Object Front const objectGrip = argumentValues[i]?.getGrip
? argumentValues[i]?.getGrip()
: argumentValues[i];
preview.push({ // All the argument will be show at the exact same spot. // Ideally it would be nice to show them next to each argument, // but the tracer currently expose the location of the first instruction // in the function body.
line,
column,
// This attribute helps distinguish pause from trace previews
type: "trace",
name,
value: objectGrip,
});
}
// This is the shape of data expected by InlinePreviews component
previews = {
[line]: preview,
};
} return previews;
}
export default update;
¤ Dauer der Verarbeitung: 0.17 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.