gecko-dev/devtools/client/webconsole/new-webconsole.js

282 строки
7.9 KiB
JavaScript

/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* 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 {Utils: WebConsoleUtils} = require("devtools/client/webconsole/utils");
const EventEmitter = require("devtools/shared/event-emitter");
const promise = require("promise");
const defer = require("devtools/shared/defer");
const Services = require("Services");
const { gDevTools } = require("devtools/client/framework/devtools");
const { JSTerm } = require("devtools/client/webconsole/jsterm");
const { WebConsoleConnectionProxy } = require("devtools/client/webconsole/webconsole-connection-proxy");
const PREF_MESSAGE_TIMESTAMP = "devtools.webconsole.timestampMessages";
// XXX: This file is incomplete (see bug 1326937).
// It's used when loading the webconsole with devtools-launchpad, but will ultimately be
// the entry point for the new frontend
/**
* A WebConsoleFrame instance is an interactive console initialized *per target*
* that displays console log data as well as provides an interactive terminal to
* manipulate the target's document content.
*
* The WebConsoleFrame is responsible for the actual Web Console UI
* implementation.
*
* @constructor
* @param object webConsoleOwner
* The WebConsole owner object.
*/
function NewWebConsoleFrame(webConsoleOwner) {
this.owner = webConsoleOwner;
this.hudId = this.owner.hudId;
this.isBrowserConsole = this.owner._browserConsole;
this.NEW_CONSOLE_OUTPUT_ENABLED = true;
this.window = this.owner.iframeWindow;
this._onToolboxPrefChanged = this._onToolboxPrefChanged.bind(this);
EventEmitter.decorate(this);
}
NewWebConsoleFrame.prototype = {
/**
* Getter for the debugger WebConsoleClient.
* @type object
*/
get webConsoleClient() {
return this.proxy ? this.proxy.webConsoleClient : null;
},
/**
* Initialize the WebConsoleFrame instance.
* @return object
* A promise object that resolves once the frame is ready to use.
*/
init() {
this._initUI();
let connectionInited = this._initConnection();
// Don't reject if the history fails to load for some reason.
// This would be fine, the panel will just start with empty history.
let allReady = this.jsterm.historyLoaded.catch(() => {}).then(() => {
return connectionInited;
});
// This notification is only used in tests. Don't chain it onto
// the returned promise because the console panel needs to be attached
// to the toolbox before the web-console-created event is receieved.
let notifyObservers = () => {
let id = WebConsoleUtils.supportsString(this.hudId);
if (Services.obs) {
Services.obs.notifyObservers(id, "web-console-created");
}
};
allReady.then(notifyObservers, notifyObservers)
.then(this.newConsoleOutput.init);
return allReady;
},
destroy() {
if (this._destroyer) {
return this._destroyer.promise;
}
this._destroyer = defer();
Services.prefs.addObserver(PREF_MESSAGE_TIMESTAMP, this._onToolboxPrefChanged);
this.React = this.ReactDOM = this.FrameView = null;
let onDestroy = () => {
this._destroyer.resolve(null);
};
if (this.proxy) {
this.proxy.disconnect().then(onDestroy);
this.proxy = null;
} else {
onDestroy();
}
return this._destroyer.promise;
},
_onUpdateListeners() {
},
logWarningAboutReplacedAPI() {
},
handleNetworkEventUpdate() {
},
/**
* Setter for saving of network request and response bodies.
*
* @param boolean value
* The new value you want to set.
*/
setSaveRequestAndResponseBodies(value) {
if (!this.webConsoleClient) {
// Don't continue if the webconsole disconnected.
return promise.resolve(null);
}
let deferred = defer();
let newValue = !!value;
let toSet = {
"NetworkMonitor.saveRequestAndResponseBodies": newValue,
};
// Make sure the web console client connection is established first.
this.webConsoleClient.setPreferences(toSet, response => {
if (!response.error) {
this._saveRequestAndResponseBodies = newValue;
deferred.resolve(response);
} else {
deferred.reject(response.error);
}
});
return deferred.promise;
},
/**
* Connect to the server using the remote debugging protocol.
*
* @private
* @return object
* A promise object that is resolved/reject based on the connection
* result.
*/
_initConnection: function () {
if (this._initDefer) {
return this._initDefer.promise;
}
this._initDefer = defer();
this.proxy = new WebConsoleConnectionProxy(this, this.owner.target);
this.proxy.connect().then(() => {
// on success
this._initDefer.resolve(this);
}, (reason) => {
// on failure
// TODO Print a message to console
this._initDefer.reject(reason);
});
return this._initDefer.promise;
},
_initUI: function () {
this.document = this.window.document;
this.rootElement = this.document.documentElement;
this.outputNode = this.document.getElementById("output-container");
this.completeNode = this.document.querySelector(".jsterm-complete-node");
this.inputNode = this.document.querySelector(".jsterm-input-node");
this.jsterm = new JSTerm(this);
this.jsterm.init();
let toolbox = gDevTools.getToolbox(this.owner.target);
// @TODO Remove this once JSTerm is handled with React/Redux.
this.window.jsterm = this.jsterm;
// @TODO Once the toolbox has been converted to React, see if passing
// in JSTerm is still necessary.
// Handle both launchpad and toolbox loading
let Wrapper = this.owner.NewConsoleOutputWrapper || this.window.NewConsoleOutput;
this.newConsoleOutput = new Wrapper(
this.outputNode, this.jsterm, toolbox, this.owner, this.document);
// Toggle the timestamp on preference change
Services.prefs.addObserver(PREF_MESSAGE_TIMESTAMP, this._onToolboxPrefChanged);
this._onToolboxPrefChanged();
},
/**
* Handler for page location changes.
*
* @param string uri
* New page location.
* @param string title
* New page title.
*/
onLocationChange: function (uri, title) {
this.contentLocation = uri;
if (this.owner.onLocationChange) {
this.owner.onLocationChange(uri, title);
}
},
/**
* Release an actor.
*
* @private
* @param string actor
* The actor ID you want to release.
*/
_releaseObject: function (actor) {
if (this.proxy) {
this.proxy.releaseActor(actor);
}
},
/**
* Called when the message timestamp pref changes.
*/
_onToolboxPrefChanged: function () {
let newValue = Services.prefs.getBoolPref(PREF_MESSAGE_TIMESTAMP);
this.newConsoleOutput.dispatchTimestampsToggle(newValue);
},
/**
* Handler for the tabNavigated notification.
*
* @param string event
* Event name.
* @param object packet
* Notification packet received from the server.
*/
handleTabNavigated: function (event, packet) {
if (event == "will-navigate") {
if (this.persistLog) {
// Add a _type to hit convertCachedPacket.
packet._type = true;
this.newConsoleOutput.dispatchMessageAdd(packet);
} else {
this.clearOutput(false);
}
}
if (packet.url) {
this.onLocationChange(packet.url, packet.title);
}
if (event == "navigate" && !packet.nativeConsoleAPI) {
this.logWarningAboutReplacedAPI();
}
},
clearOutput(clearStorage) {
this.newConsoleOutput.dispatchMessagesClear();
this.webConsoleClient.clearNetworkRequests();
if (clearStorage) {
this.webConsoleClient.clearMessagesCache();
}
},
};
exports.NewWebConsoleFrame = NewWebConsoleFrame;