/* 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 EventEmitter = require("resource://devtools/shared/event-emitter.js"); loader.lazyRequireGetter( this, "TRACER_LOG_METHODS", "resource://devtools/shared/specs/tracer.js", true ); class TracerCommand extends EventEmitter { constructor({ commands }) { super(); this.#targetConfigurationCommand = commands.targetConfigurationCommand; this.#resourceCommand = commands.resourceCommand; } // The tracer has been requested to start, but doesn't necessarily mean it actually started tracing JS executions. // The tracer may wait for next user interaction/document load before being active. isTracingEnabled = false; // The tracer is actively tracking JS executions. isTracingActive = false; #resourceCommand; #targetConfigurationCommand; async initialize() { return this.#resourceCommand.watchResources( [this.#resourceCommand.TYPES.JSTRACER_STATE], { onAvailable: this.onResourcesAvailable } ); } destroy() { this.#resourceCommand.unwatchResources( [this.#resourceCommand.TYPES.JSTRACER_STATE], { onAvailable: this.onResourcesAvailable } ); } onResourcesAvailable = resources => { for (const resource of resources) { if (resource.resourceType != this.#resourceCommand.TYPES.JSTRACER_STATE) { continue; } // Clear the list of collected frames each time we start a new tracer record. // The tracer will reset its frame counter to zero on stop, but on the frontend // we may inspect frames after the tracer is stopped, until we start a new one. if (resource.enabled) { resource.targetFront.getJsTracerCollectedFramesArray().length = 0; } if ( resource.enabled == this.isTracingActive && resource.enabled == this.isTracingEnabled ) { continue; } this.isTracingActive = resource.enabled; // In case the tracer is started without the DevTools frontend, also force it to be reported as enabled this.isTracingEnabled = resource.enabled; this.emit("toggle"); } }; /** * Get the dictionary passed to the server codebase as a SessionData. * This contains all settings to fine tune the tracer actual behavior. * * @return {JSON} * Configuration object. */ getTracingOptions() { const logMethod = Services.prefs.getStringPref( "devtools.debugger.javascript-tracing-log-method", "" ); return { logMethod, // Force enabling DOM Mutation logging as soon as we selected the sidebar log output traceDOMMutations: logMethod == TRACER_LOG_METHODS.DEBUGGER_SIDEBAR || logMethod == TRACER_LOG_METHODS.PROFILER ? ["add", "attributes", "remove"] : null, traceValues: Services.prefs.getBoolPref( "devtools.debugger.javascript-tracing-values", false ), traceOnNextInteraction: Services.prefs.getBoolPref( "devtools.debugger.javascript-tracing-on-next-interaction", false ), traceOnNextLoad: Services.prefs.getBoolPref( "devtools.debugger.javascript-tracing-on-next-load", false ), traceFunctionReturn: Services.prefs.getBoolPref( "devtools.debugger.javascript-tracing-function-return", false ), }; } /** * Toggle JavaScript tracing for all targets. */ async toggle() { this.isTracingEnabled = !this.isTracingEnabled; // May be wait for the web console to be fully initialized and listening to tracer resources before enabling it await this.emitAsync("toggle"); await this.#targetConfigurationCommand.updateConfiguration({ tracerOptions: this.isTracingEnabled ? this.getTracingOptions() : undefined, }); } } module.exports = TracerCommand;