Bug 1574040 - Generalize the Debugger worker redux management (part 2). r=loganfsmyth

Differential Revision: https://phabricator.services.mozilla.com/D42584

--HG--
rename : devtools/client/debugger/src/actions/debuggee.js => devtools/client/debugger/src/actions/threads.js
rename : devtools/client/debugger/src/components/SecondaryPanes/Worker.js => devtools/client/debugger/src/components/SecondaryPanes/Thread.js
rename : devtools/client/debugger/src/components/SecondaryPanes/Workers.css => devtools/client/debugger/src/components/SecondaryPanes/Threads.css
rename : devtools/client/debugger/src/reducers/debuggee.js => devtools/client/debugger/src/reducers/threads.js
extra : moz-landing-system : lando
This commit is contained in:
Jason Laster 2019-08-20 20:20:46 +00:00
Родитель a07a9b2250
Коммит 3284eefc86
29 изменённых файлов: 119 добавлений и 175 удалений

Просмотреть файл

@ -47,8 +47,8 @@ add_task(async () => {
info("Check the state of redux");
ok(
panelWin.dbg.store.getState().debuggee.isWebExtension,
"isWebExtension flag in debuggee is true"
panelWin.dbg.store.getState().threads.isWebExtension,
"isWebExtension flag in threads is true"
);
info("Check whether the element displays correctly");

Просмотреть файл

@ -3,9 +3,6 @@
* file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
const { LocalizationHelper } = require("devtools/shared/l10n");
const {
gDevToolsBrowser,
} = require("devtools/client/framework/devtools-browser");
loader.lazyRequireGetter(
this,
"openContentLink",
@ -79,10 +76,6 @@ DebuggerPanel.prototype = {
openContentLink(url);
},
openWorkerToolbox: function(workerTargetFront) {
return gDevToolsBrowser.openWorkerToolbox(workerTargetFront, "jsdebugger");
},
openConsoleAndEvaluate: async function(input) {
const { hud } = await this.toolbox.selectTool("webconsole");
hud.ui.wrapper.dispatchEvaluateExpression(input);

Просмотреть файл

@ -18,7 +18,7 @@ import * as sourceTree from "./source-tree";
import * as sources from "./sources";
import * as sourcesActors from "./source-actors";
import * as tabs from "./tabs";
import * as debuggee from "./debuggee";
import * as threads from "./threads";
import * as toolbox from "./toolbox";
import * as preview from "./preview";
@ -37,7 +37,7 @@ export default {
...projectTextSearch,
...quickOpen,
...sourceTree,
...debuggee,
...threads,
...toolbox,
...preview,
};

Просмотреть файл

@ -12,7 +12,6 @@ DIRS += [
]
CompiledModules(
'debuggee.js',
'event-listeners.js',
'expressions.js',
'file-search.js',
@ -25,5 +24,6 @@ CompiledModules(
'source-tree.js',
'tabs.js',
'toolbox.js',
'threads.js',
'ui.js',
)

Просмотреть файл

@ -7,7 +7,7 @@
import { clearDocuments } from "../utils/editor";
import sourceQueue from "../utils/source-queue";
import { updateThreads } from "./debuggee";
import { updateThreads } from "./threads";
import { clearWasmStates } from "../utils/wasm";
import { getMainThread } from "../selectors";

Просмотреть файл

@ -17,19 +17,19 @@ export function updateThreads() {
const currentThreads = getThreads(getState());
const addedThreads = differenceBy(threads, currentThreads, w => w.actor);
const removedThreads = differenceBy(currentThreads, threads, w => w.actor);
const addedThreads = differenceBy(threads, currentThreads, t => t.actor);
const removedThreads = differenceBy(currentThreads, threads, t => t.actor);
if (removedThreads.length > 0) {
const sourceActors = getSourceActorsForThread(
getState(),
removedThreads.map(w => w.actor)
removedThreads.map(t => t.actor)
);
dispatch(removeSourceActors(sourceActors));
dispatch(
({
type: "REMOVE_THREADS",
cx,
threads: removedThreads.map(w => w.actor),
threads: removedThreads.map(t => t.actor),
}: Action)
);
}

Просмотреть файл

@ -5,7 +5,7 @@
// @flow
import type { ThunkArgs } from "./types";
import type { Worker, Grip } from "../types";
import type { Grip } from "../types";
/**
* @memberof actions/toolbox
@ -17,12 +17,6 @@ export function openLink(url: string) {
};
}
export function openWorkerToolbox(worker: Worker) {
return async function({ getState, panel }: ThunkArgs) {
return panel.openWorkerToolbox(worker);
};
}
export function evaluateInConsole(inputString: string) {
return async ({ panel }: ThunkArgs) => {
return panel.openConsoleAndEvaluate(inputString);

Просмотреть файл

@ -21,8 +21,8 @@ import type {
Script,
SourceId,
SourceActor,
Worker,
Range,
Thread,
} from "../../types";
import type {
@ -405,7 +405,7 @@ function getSourceForActor(actor: ActorId) {
return sourceActors[actor];
}
async function fetchThreads(): Promise<Worker[]> {
async function fetchThreads(): Promise<Thread[]> {
const options = {
breakpoints,
eventBreakpoints,

Просмотреть файл

@ -5,7 +5,7 @@
// @flow
// This module converts Firefox specific types to the generic types
import type { Frame, ThreadId, GeneratedSourceData, Worker } from "../../types";
import type { Frame, ThreadId, GeneratedSourceData, Thread } from "../../types";
import type {
PausedPacket,
FramesResponse,
@ -73,7 +73,7 @@ export function createPause(
};
}
export function createThread(actor: string, target: Target): Worker {
export function createThread(actor: string, target: Target): Thread {
return {
actor,
url: target.url || "",

Просмотреть файл

@ -21,7 +21,6 @@ import type {
Frame,
SourceId,
QueuedSourceData,
Worker,
Range,
} from "../../types";
@ -381,7 +380,6 @@ export type ThreadFront = {
export type Panel = {|
emit: (eventName: string) => void,
openLink: (url: string) => void,
openWorkerToolbox: (worker: Worker) => void,
openElementInInspector: (grip: Object) => void,
openConsoleAndEvaluate: (input: string) => void,
highlightDomElement: (grip: Object) => void,

Просмотреть файл

@ -13,17 +13,17 @@ import { getCurrentThread, getIsPaused, getContext } from "../../selectors";
import { getDisplayName, isWorker } from "../../utils/threads";
import AccessibleImage from "../shared/AccessibleImage";
import type { Context, Thread } from "../../types";
import type { Context, Thread as ThreadType } from "../../types";
type Props = {
cx: Context,
selectThread: typeof actions.selectThread,
isPaused: boolean,
thread: Thread,
thread: ThreadType,
currentThread: string,
};
export class Worker extends Component<Props> {
export class Thread extends Component<Props> {
onSelectThread = () => {
const { thread } = this.props;
this.props.selectThread(this.props.cx, thread.actor);
@ -37,7 +37,7 @@ export class Worker extends Component<Props> {
return (
<div
className={classnames("worker", {
className={classnames("thread", {
selected: thread.actor == currentThread,
})}
key={thread.actor}
@ -68,4 +68,4 @@ export default connect(
{
selectThread: actions.selectThread,
}
)(Worker);
)(Thread);

Просмотреть файл

@ -2,16 +2,16 @@
* 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/>. */
.workers-list {
.threads-list {
padding: 4px 0;
}
.workers-list * {
.threads-list * {
-moz-user-select: none;
user-select: none;
}
.workers-list > .worker {
.threads-list > .thread {
font-size: inherit;
color: var(--theme-text-color-strong);
padding: 2px 6px;
@ -23,24 +23,24 @@
align-items: center;
}
.workers-list > .worker:hover {
.threads-list > .thread:hover {
background-color: var(--search-overlays-semitransparent);
}
.workers-list > .worker.selected {
.threads-list > .thread.selected {
background-color: var(--tab-line-selected-color);
}
.workers-list .icon {
.threads-list .icon {
flex: none;
margin-inline-end: 4px;
}
.workers-list .img {
.threads-list .img {
display: block;
}
.workers-list .label {
.threads-list .label {
display: inline-block;
flex-grow: 1;
flex-shrink: 1;
@ -49,16 +49,16 @@
text-overflow: ellipsis;
}
.workers-list .pause-badge {
.threads-list .pause-badge {
flex: none;
margin-inline-start: 4px;
}
.workers-list > .worker.selected {
.threads-list > .thread.selected {
background: var(--theme-selection-background);
color: var(--theme-selection-color);
}
.workers-list > .worker.selected .img {
.threads-list > .thread.selected .img {
background-color: currentColor;
}

Просмотреть файл

@ -0,0 +1,39 @@
/* 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/>. */
// @flow
import React, { Component } from "react";
import { connect } from "../../utils/connect";
import { getAllThreads } from "../../selectors";
import Thread from "./Thread";
import type { Thread as ThreadType } from "../../types";
import "./Threads.css";
type Props = {
threads: ThreadType[],
};
export class Threads extends Component<Props> {
render() {
const { threads } = this.props;
return (
<div className="pane threads-list">
{threads.map(thread => (
<Thread thread={thread} key={thread.actor} />
))}
</div>
);
}
}
const mapStateToProps = state => ({
threads: getAllThreads(state),
});
export default connect(mapStateToProps)(Threads);

Просмотреть файл

@ -1,66 +0,0 @@
/* 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/>. */
// @flow
import React, { Component } from "react";
import { connect } from "../../utils/connect";
import actions from "../../actions";
import { getAllThreads } from "../../selectors";
import { getDisplayName } from "../../utils/threads";
import { features } from "../../utils/prefs";
import Worker from "./Worker";
import AccessibleImage from "../shared/AccessibleImage";
import type { Thread, Worker as WorkerType } from "../../types";
import "./Workers.css";
type Props = {
threads: Thread[],
openWorkerToolbox: typeof actions.openWorkerToolbox,
};
export class Workers extends Component<Props> {
renderWorker(thread: WorkerType) {
const { openWorkerToolbox } = this.props;
return (
<div
className="worker"
key={thread.actor}
onClick={() => openWorkerToolbox(thread)}
>
<div className="icon">
<AccessibleImage className="worker" />
</div>
<div className="label">{getDisplayName(thread)}</div>
</div>
);
}
render() {
const { threads } = this.props;
const workerList = features.windowlessWorkers
? threads.map(thread => <Worker thread={thread} key={thread.actor} />)
: threads
.filter((thread: any) => thread.actorID)
.map(worker => this.renderWorker((worker: any)));
return <div className="pane workers-list">{workerList}</div>;
}
}
const mapStateToProps = state => ({
threads: getAllThreads(state),
});
export default connect(
mapStateToProps,
{
openWorkerToolbox: actions.openWorkerToolbox,
}
)(Workers);

Просмотреть файл

@ -34,7 +34,7 @@ import Breakpoints from "./Breakpoints";
import Expressions from "./Expressions";
import SplitBox from "devtools-splitter";
import Frames from "./Frames";
import Workers from "./Workers";
import Threads from "./Threads";
import Accordion from "../shared/Accordion";
import CommandBar from "./CommandBar";
import UtilsBar from "./UtilsBar";
@ -322,13 +322,11 @@ class SecondaryPanes extends Component<Props, State> {
};
}
getWorkersItem(): AccordionPaneItem {
getThreadsItem(): AccordionPaneItem {
return {
header: features.windowlessWorkers
? L10N.getStr("threadsHeader")
: L10N.getStr("workersHeader"),
className: "workers-pane",
component: <Workers />,
header: L10N.getStr("threadsHeader"),
className: "threads-pane",
component: <Threads />,
opened: prefs.workersVisible,
onToggle: opened => {
prefs.workersVisible = opened;
@ -393,7 +391,7 @@ class SecondaryPanes extends Component<Props, State> {
if (horizontal) {
if (features.workers && this.props.workers.length > 0) {
items.push(this.getWorkersItem());
items.push(this.getThreadsItem());
}
items.push(this.getWatchItem());
@ -430,7 +428,7 @@ class SecondaryPanes extends Component<Props, State> {
const items: AccordionPaneItem[] = [];
if (features.workers && this.props.workers.length > 0) {
items.push(this.getWorkersItem());
items.push(this.getThreadsItem());
}
items.push(this.getWatchItem());

Просмотреть файл

@ -15,10 +15,10 @@ CompiledModules(
'Expressions.js',
'index.js',
'Scopes.js',
'Thread.js',
'Threads.js',
'UtilsBar.js',
'WhyPaused.js',
'Worker.js',
'Workers.js',
'XHRBreakpoints.js',
)
@ -29,7 +29,7 @@ DevToolsModules(
'Expressions.css',
'Scopes.css',
'SecondaryPanes.css',
'Threads.css',
'WhyPaused.css',
'Workers.css',
'XHRBreakpoints.css',
)

Просмотреть файл

@ -46,7 +46,7 @@
@import url("./components/SecondaryPanes/Scopes.css");
@import url("./components/SecondaryPanes/SecondaryPanes.css");
@import url("./components/SecondaryPanes/WhyPaused.css");
@import url("./components/SecondaryPanes/Workers.css");
@import url("./components/SecondaryPanes/Threads.css");
@import url("./components/SecondaryPanes/XHRBreakpoints.css");
@import url("./components/ShortcutsModal.css");
@import url("./components/WelcomeBox.css");

Просмотреть файл

@ -22,7 +22,6 @@ bootstrap(React, ReactDOM).then(connection => {
const win = window.open(url, "_blank");
win.focus();
},
openWorkerToolbox: worker => alert(worker.url),
openElementInInspector: grip =>
alert(`Opening node in Inspector: ${grip.class}`),
openConsoleAndEvaluate: input => alert(`console.log: ${input}`),

Просмотреть файл

@ -24,7 +24,7 @@ import preview from "./preview";
import projectTextSearch from "./project-text-search";
import quickOpen from "./quick-open";
import sourceTree from "./source-tree";
import debuggee from "./debuggee";
import threads from "./threads";
import eventListenerBreakpoints from "./event-listeners";
// eslint-disable-next-line import/named
@ -45,7 +45,7 @@ export default {
projectTextSearch,
quickOpen,
sourceTree,
debuggee,
threads,
objectInspector: objectInspector.reducer.default,
eventListenerBreakpoints,
preview,

Просмотреть файл

@ -11,7 +11,6 @@ CompiledModules(
'ast.js',
'async-requests.js',
'breakpoints.js',
'debuggee.js',
'event-listeners.js',
'expressions.js',
'file-search.js',
@ -25,5 +24,6 @@ CompiledModules(
'source-tree.js',
'sources.js',
'tabs.js',
'threads.js',
'ui.js',
)

Просмотреть файл

@ -65,7 +65,7 @@ import type {
import type { PendingSelectedLocation, Selector } from "./types";
import type { Action, DonePromiseAction, FocusItem } from "../actions/types";
import type { LoadSourceAction } from "../actions/types/SourceAction";
import type { DebuggeeState } from "./debuggee";
import type { ThreadsState } from "./threads";
import { uniq } from "lodash";
export type SourcesMap = { [SourceId]: Source };
@ -519,7 +519,7 @@ export function getBlackBoxList() {
// pick off the piece of state we're interested in. It's impossible
// (right now) to type those wrapped functions.
type OuterState = { sources: SourcesState };
type DebuggeeOuterState = { debuggee: DebuggeeState };
type ThreadsOuterState = { threads: ThreadsState };
const getSourcesState = (state: OuterState) => state.sources;
@ -718,7 +718,7 @@ export function getSourceList(state: OuterState): Source[] {
}
export function getDisplayedSourcesList(
state: OuterState & SourceActorOuterState & DebuggeeOuterState
state: OuterState & SourceActorOuterState & ThreadsOuterState
): Source[] {
return ((Object.values(getDisplayedSources(state)): any).flatMap(
Object.values
@ -838,17 +838,17 @@ const queryAllDisplayedSources: ReduceQuery<
);
function getAllDisplayedSources(
state: OuterState & DebuggeeOuterState
state: OuterState & ThreadsOuterState
): Array<SourceId> {
return queryAllDisplayedSources(state.sources.sources, {
projectDirectoryRoot: state.sources.projectDirectoryRoot,
chromeAndExtensionsEnabled: state.sources.chromeAndExtenstionsEnabled,
debuggeeIsWebExtension: state.debuggee.isWebExtension,
debuggeeIsWebExtension: state.threads.isWebExtension,
});
}
type GetDisplayedSourceIDsSelector = (
OuterState & SourceActorOuterState & DebuggeeOuterState
OuterState & SourceActorOuterState & ThreadsOuterState
) => { [ThreadId]: Set<SourceId> };
const getDisplayedSourceIDs: GetDisplayedSourceIDsSelector = createSelector(
getAllThreadsBySource,
@ -874,7 +874,7 @@ const getDisplayedSourceIDs: GetDisplayedSourceIDsSelector = createSelector(
);
type GetDisplayedSourcesSelector = (
OuterState & SourceActorOuterState & DebuggeeOuterState
OuterState & SourceActorOuterState & ThreadsOuterState
) => SourcesMapByThread;
export const getDisplayedSources: GetDisplayedSourcesSelector = createSelector(
state => state.sources.sources,

Просмотреть файл

@ -8,7 +8,7 @@ declare var it: (desc: string, func: () => void) => void;
declare var expect: (value: any) => any;
import update, { initialSourcesState, getDisplayedSources } from "../sources";
import { initialDebuggeeState } from "../debuggee";
import { initialThreadsState } from "../threads";
import updateSourceActors from "../source-actors";
import type { SourceActor } from "../../types";
import { prefs } from "../../utils/prefs";
@ -93,7 +93,7 @@ describe("sources selectors", () => {
state = {
sources: update(state.sources, insertAction),
sourceActors: updateSourceActors(state.sourceActors, insertAction),
debuggee: initialDebuggeeState(),
threads: initialThreadsState(),
};
const threadSources = getDisplayedSources(state);
expect(Object.values(threadSources.foo)).toHaveLength(3);
@ -119,7 +119,7 @@ describe("sources selectors", () => {
state = {
sources: update(state.sources, insertAction),
sourceActors: updateSourceActors(state.sourceActors, insertAction),
debuggee: initialDebuggeeState(),
threads: initialThreadsState(),
};
const threadSources = getDisplayedSources(state);
expect(Object.values(threadSources.foo)).toHaveLength(1);

Просмотреть файл

@ -5,26 +5,24 @@
// @flow
/**
* Debuggee reducer
* @module reducers/debuggee
* Threads reducer
* @module reducers/threads
*/
import { sortBy } from "lodash";
import { createSelector } from "reselect";
import { getDisplayName } from "../utils/threads";
import type { Selector, State } from "./types";
import type { Thread, ThreadList } from "../types";
import type { Action } from "../actions/types";
export type DebuggeeState = {
export type ThreadsState = {
threads: ThreadList,
mainThread: Thread,
isWebExtension: boolean,
};
export function initialDebuggeeState(): DebuggeeState {
export function initialThreadsState(): ThreadsState {
return {
threads: [],
mainThread: { actor: "", url: "", type: -1, name: "" },
@ -32,10 +30,10 @@ export function initialDebuggeeState(): DebuggeeState {
};
}
export default function debuggee(
state: DebuggeeState = initialDebuggeeState(),
export default function update(
state: ThreadsState = initialThreadsState(),
action: Action
): DebuggeeState {
): ThreadsState {
switch (action.type) {
case "CONNECT":
return {
@ -44,7 +42,11 @@ export default function debuggee(
isWebExtension: action.isWebExtension,
};
case "INSERT_THREADS":
return insertThreads(state, action.threads);
return {
...state,
threads: [...state.threads, ...action.threads],
};
case "REMOVE_THREADS":
const { threads } = action;
return {
@ -53,7 +55,7 @@ export default function debuggee(
};
case "NAVIGATE":
return {
...initialDebuggeeState(),
...initialThreadsState(),
mainThread: action.mainThread,
};
default:
@ -61,19 +63,7 @@ export default function debuggee(
}
}
function insertThreads(state, threads) {
const formatedThreads = threads.map(thread => ({
...thread,
name: getDisplayName(thread),
}));
return {
...state,
threads: [...state.threads, ...formatedThreads],
};
}
export const getThreads = (state: OuterState) => state.debuggee.threads;
export const getThreads = (state: OuterState) => state.threads.threads;
export const getWorkerCount = (state: OuterState) => getThreads(state).length;
@ -82,7 +72,7 @@ export function getWorkerByThread(state: OuterState, thread: string) {
}
export function getMainThread(state: OuterState): Thread {
return state.debuggee.mainThread;
return state.threads.mainThread;
}
export function getDebuggeeUrl(state: OuterState): string {
@ -92,7 +82,10 @@ export function getDebuggeeUrl(state: OuterState): string {
export const getAllThreads: Selector<Thread[]> = createSelector(
getMainThread,
getThreads,
(mainThread, threads) => [mainThread, ...sortBy(threads, getDisplayName)]
(mainThread, threads) => [
mainThread,
...sortBy(threads, thread => thread.name),
]
);
// checks if a path begins with a thread actor
@ -104,4 +97,4 @@ export function startsWithThreadActor(state: State, path: string) {
return match && match[1];
}
type OuterState = { debuggee: DebuggeeState };
type OuterState = { threads: ThreadsState };

Просмотреть файл

@ -12,7 +12,7 @@
import type { ASTState } from "./ast";
import type { BreakpointsState } from "./breakpoints";
import type { ExpressionState } from "./expressions";
import type { DebuggeeState } from "./debuggee";
import type { ThreadsState } from "./threads";
import type { FileSearchState } from "./file-search";
import type { PauseState } from "./pause";
import type { PreviewState } from "./preview";
@ -31,7 +31,7 @@ export type State = {
breakpoints: BreakpointsState,
expressions: Record<ExpressionState>,
eventListenerBreakpoints: EventListenersState,
debuggee: DebuggeeState,
threads: ThreadsState,
fileSearch: FileSearchState,
pause: PauseState,
preview: PreviewState,

Просмотреть файл

@ -9,7 +9,7 @@ export * from "../reducers/sources";
export * from "../reducers/tabs";
export * from "../reducers/event-listeners";
export * from "../reducers/pause";
export * from "../reducers/debuggee";
export * from "../reducers/threads";
export * from "../reducers/breakpoints";
export * from "../reducers/pending-breakpoints";
export * from "../reducers/ui";

Просмотреть файл

@ -65,7 +65,6 @@ if (isDevelopment()) {
pref("devtools.debugger.features.map-await-expression", true);
pref("devtools.debugger.features.xhr-breakpoints", true);
pref("devtools.debugger.features.original-blackbox", true);
pref("devtools.debugger.features.windowless-workers", true);
pref("devtools.debugger.features.event-listeners-breakpoints", true);
pref("devtools.debugger.features.dom-mutation-breakpoints", true);
pref("devtools.debugger.features.log-points", true);

Просмотреть файл

@ -37,8 +37,6 @@ function getValue(dbg, index) {
// Test basic windowless worker functionality: the main thread and worker can be
// separately controlled from the same debugger.
add_task(async function() {
await pushPref("devtools.debugger.features.windowless-workers", true);
const dbg = await initDebugger("doc-windowless-workers.html");
const mainThread = dbg.toolbox.threadFront.actor;

Просмотреть файл

@ -1281,8 +1281,8 @@ const selectors = {
blackbox: ".action.black-box",
projectSearchCollapsed: ".project-text-search .arrow:not(.expanded)",
projectSerchExpandedResults: ".project-text-search .result",
threadsPaneItems: ".workers-pane .worker",
threadsPaneItem: i => `.workers-pane .worker:nth-child(${i})`,
threadsPaneItems: ".threads-pane .thread",
threadsPaneItem: i => `.threads-pane .thread:nth-child(${i})`,
threadsPaneItemPause: i => `${selectors.threadsPaneItem(i)} .pause-badge`,
CodeMirrorLines: ".CodeMirror-lines",
inlinePreviewLables: ".CodeMirror-linewidget .inline-preview-label",

Просмотреть файл

@ -74,7 +74,6 @@ pref("devtools.debugger.features.autocomplete-expressions", false);
pref("devtools.debugger.features.map-expression-bindings", true);
pref("devtools.debugger.features.xhr-breakpoints", true);
pref("devtools.debugger.features.original-blackbox", true);
pref("devtools.debugger.features.windowless-workers", true);
pref("devtools.debugger.features.event-listeners-breakpoints", true);
pref("devtools.debugger.features.dom-mutation-breakpoints", false);
pref("devtools.debugger.features.log-points", true);