Source code

Revision control

Copy as Markdown

Other Tools

/* 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 { assert } = require("resource://devtools/shared/DevToolsUtils.js");
const {
getDOMMutationBreakpoint,
getDOMMutationBreakpoints,
exports.registerWalkerListeners = registerWalkerListeners;
function registerWalkerListeners(store, walker) {
walker.on("mutations", mutations => handleWalkerMutations(mutations, store));
}
/**
* Called when a target is destroyed. This will allow the reducer to remove breakpoints on
* nodeFront associated with the passed target
*
* @param {ToolboxStore} store: The toolbox redux store
* @param {TargetFront} targetFront
*/
function removeTarget(store, targetFront) {
store.dispatch({
type: "REMOVE_TARGET",
targetFront,
});
}
exports.removeTarget = removeTarget;
function handleWalkerMutations(mutations, store) {
// If we got BP updates for detach/unload, we want to drop those nodes from
// the list of active DOM mutation breakpoints. We explicitly check these
// cases because BP updates could also happen due to explicitly API
// operations to add/remove bps.
const mutationItems = mutations.filter(
mutation => mutation.type === "mutationBreakpoint"
);
if (mutationItems.length) {
store.dispatch(updateBreakpointsForMutations(mutationItems));
}
}
exports.createDOMMutationBreakpoint = createDOMMutationBreakpoint;
function createDOMMutationBreakpoint(nodeFront, mutationType) {
assert(typeof nodeFront === "object" && nodeFront);
assert(typeof mutationType === "string");
return async function ({ dispatch }) {
const walker = nodeFront.walkerFront;
dispatch({
type: "ADD_DOM_MUTATION_BREAKPOINT",
nodeFront,
mutationType,
});
await walker.setMutationBreakpoints(nodeFront, {
[mutationType]: true,
});
};
}
exports.deleteDOMMutationBreakpoint = deleteDOMMutationBreakpoint;
function deleteDOMMutationBreakpoint(nodeFront, mutationType) {
assert(typeof nodeFront === "object" && nodeFront);
assert(typeof mutationType === "string");
return async function ({ dispatch }) {
const walker = nodeFront.walkerFront;
await walker.setMutationBreakpoints(nodeFront, {
[mutationType]: false,
});
dispatch({
type: "REMOVE_DOM_MUTATION_BREAKPOINT",
nodeFront,
mutationType,
});
};
}
function updateBreakpointsForMutations(mutationItems) {
return async function ({ dispatch, getState }) {
const removedNodeFronts = [];
const changedNodeFronts = new Set();
for (const { target: nodeFront, mutationReason } of mutationItems) {
switch (mutationReason) {
case "api":
changedNodeFronts.add(nodeFront);
break;
default:
console.error(
"Unexpected mutation reason",
mutationReason,
", removing"
);
// Fall Through
case "detach":
case "unload":
removedNodeFronts.push(nodeFront);
break;
}
}
if (removedNodeFronts.length) {
dispatch({
type: "REMOVE_DOM_MUTATION_BREAKPOINTS_FOR_FRONTS",
nodeFronts: removedNodeFronts,
});
}
if (changedNodeFronts.size > 0) {
const enabledStates = [];
for (const {
id,
nodeFront,
mutationType,
enabled,
} of getDOMMutationBreakpoints(getState())) {
if (changedNodeFronts.has(nodeFront)) {
const bpEnabledOnFront = nodeFront.mutationBreakpoints[mutationType];
if (bpEnabledOnFront !== enabled) {
// Sync the bp state from the front into the store.
enabledStates.push([id, bpEnabledOnFront]);
}
}
}
dispatch({
type: "SET_DOM_MUTATION_BREAKPOINTS_ENABLED_STATE",
enabledStates,
});
}
};
}
exports.toggleDOMMutationBreakpointState = toggleDOMMutationBreakpointState;
function toggleDOMMutationBreakpointState(id, enabled) {
assert(typeof id === "string");
assert(typeof enabled === "boolean");
return async function ({ getState }) {
const bp = getDOMMutationBreakpoint(getState(), id);
if (!bp) {
throw new Error(`No DOM mutation BP with ID ${id}`);
}
const walker = bp.nodeFront.getParent();
await walker.setMutationBreakpoints(bp.nodeFront, {
[bp.mutationType]: enabled,
});
};
}