summaryrefslogtreecommitdiffstats
path: root/devtools/client/netmonitor/har/har-automation.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/netmonitor/har/har-automation.js')
-rw-r--r--devtools/client/netmonitor/har/har-automation.js273
1 files changed, 273 insertions, 0 deletions
diff --git a/devtools/client/netmonitor/har/har-automation.js b/devtools/client/netmonitor/har/har-automation.js
new file mode 100644
index 000000000..0885c4f96
--- /dev/null
+++ b/devtools/client/netmonitor/har/har-automation.js
@@ -0,0 +1,273 @@
+/* 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";
+/* eslint-disable mozilla/reject-some-requires */
+const { Ci } = require("chrome");
+const { Class } = require("sdk/core/heritage");
+const { resolve } = require("promise");
+const Services = require("Services");
+
+loader.lazyRequireGetter(this, "HarCollector", "devtools/client/netmonitor/har/har-collector", true);
+loader.lazyRequireGetter(this, "HarExporter", "devtools/client/netmonitor/har/har-exporter", true);
+loader.lazyRequireGetter(this, "HarUtils", "devtools/client/netmonitor/har/har-utils", true);
+
+const prefDomain = "devtools.netmonitor.har.";
+
+// Helper tracer. Should be generic sharable by other modules (bug 1171927)
+const trace = {
+ log: function (...args) {
+ }
+};
+
+/**
+ * This object is responsible for automated HAR export. It listens
+ * for Network activity, collects all HTTP data and triggers HAR
+ * export when the page is loaded.
+ *
+ * The user needs to enable the following preference to make the
+ * auto-export work: devtools.netmonitor.har.enableAutoExportToFile
+ *
+ * HAR files are stored within directory that is specified in this
+ * preference: devtools.netmonitor.har.defaultLogDir
+ *
+ * If the default log directory preference isn't set the following
+ * directory is used by default: <profile>/har/logs
+ */
+var HarAutomation = Class({
+ // Initialization
+
+ initialize: function (toolbox) {
+ this.toolbox = toolbox;
+
+ let target = toolbox.target;
+ target.makeRemote().then(() => {
+ this.startMonitoring(target.client, target.form);
+ });
+ },
+
+ destroy: function () {
+ if (this.collector) {
+ this.collector.stop();
+ }
+
+ if (this.tabWatcher) {
+ this.tabWatcher.disconnect();
+ }
+ },
+
+ // Automation
+
+ startMonitoring: function (client, tabGrip, callback) {
+ if (!client) {
+ return;
+ }
+
+ if (!tabGrip) {
+ return;
+ }
+
+ this.debuggerClient = client;
+ this.tabClient = this.toolbox.target.activeTab;
+ this.webConsoleClient = this.toolbox.target.activeConsole;
+
+ this.tabWatcher = new TabWatcher(this.toolbox, this);
+ this.tabWatcher.connect();
+ },
+
+ pageLoadBegin: function (response) {
+ this.resetCollector();
+ },
+
+ resetCollector: function () {
+ if (this.collector) {
+ this.collector.stop();
+ }
+
+ // A page is about to be loaded, start collecting HTTP
+ // data from events sent from the backend.
+ this.collector = new HarCollector({
+ webConsoleClient: this.webConsoleClient,
+ debuggerClient: this.debuggerClient
+ });
+
+ this.collector.start();
+ },
+
+ /**
+ * A page is done loading, export collected data. Note that
+ * some requests for additional page resources might be pending,
+ * so export all after all has been properly received from the backend.
+ *
+ * This collector still works and collects any consequent HTTP
+ * traffic (e.g. XHRs) happening after the page is loaded and
+ * The additional traffic can be exported by executing
+ * triggerExport on this object.
+ */
+ pageLoadDone: function (response) {
+ trace.log("HarAutomation.pageLoadDone; ", response);
+
+ if (this.collector) {
+ this.collector.waitForHarLoad().then(collector => {
+ return this.autoExport();
+ });
+ }
+ },
+
+ autoExport: function () {
+ let autoExport = Services.prefs.getBoolPref(prefDomain +
+ "enableAutoExportToFile");
+
+ if (!autoExport) {
+ return resolve();
+ }
+
+ // Auto export to file is enabled, so save collected data
+ // into a file and use all the default options.
+ let data = {
+ fileName: Services.prefs.getCharPref(prefDomain + "defaultFileName"),
+ };
+
+ return this.executeExport(data);
+ },
+
+ // Public API
+
+ /**
+ * Export all what is currently collected.
+ */
+ triggerExport: function (data) {
+ if (!data.fileName) {
+ data.fileName = Services.prefs.getCharPref(prefDomain +
+ "defaultFileName");
+ }
+
+ return this.executeExport(data);
+ },
+
+ /**
+ * Clear currently collected data.
+ */
+ clear: function () {
+ this.resetCollector();
+ },
+
+ // HAR Export
+
+ /**
+ * Execute HAR export. This method fetches all data from the
+ * Network panel (asynchronously) and saves it into a file.
+ */
+ executeExport: function (data) {
+ let items = this.collector.getItems();
+ let form = this.toolbox.target.form;
+ let title = form.title || form.url;
+
+ let options = {
+ getString: this.getString.bind(this),
+ view: this,
+ items: items,
+ };
+
+ options.defaultFileName = data.fileName;
+ options.compress = data.compress;
+ options.title = data.title || title;
+ options.id = data.id;
+ options.jsonp = data.jsonp;
+ options.includeResponseBodies = data.includeResponseBodies;
+ options.jsonpCallback = data.jsonpCallback;
+ options.forceExport = data.forceExport;
+
+ trace.log("HarAutomation.executeExport; " + data.fileName, options);
+
+ return HarExporter.fetchHarData(options).then(jsonString => {
+ // Save the HAR file if the file name is provided.
+ if (jsonString && options.defaultFileName) {
+ let file = getDefaultTargetFile(options);
+ if (file) {
+ HarUtils.saveToFile(file, jsonString, options.compress);
+ }
+ }
+
+ return jsonString;
+ });
+ },
+
+ /**
+ * Fetches the full text of a string.
+ */
+ getString: function (stringGrip) {
+ return this.webConsoleClient.getString(stringGrip);
+ },
+});
+
+// Helpers
+
+function TabWatcher(toolbox, listener) {
+ this.target = toolbox.target;
+ this.listener = listener;
+
+ this.onTabNavigated = this.onTabNavigated.bind(this);
+}
+
+TabWatcher.prototype = {
+ // Connection
+
+ connect: function () {
+ this.target.on("navigate", this.onTabNavigated);
+ this.target.on("will-navigate", this.onTabNavigated);
+ },
+
+ disconnect: function () {
+ if (!this.target) {
+ return;
+ }
+
+ this.target.off("navigate", this.onTabNavigated);
+ this.target.off("will-navigate", this.onTabNavigated);
+ },
+
+ // Event Handlers
+
+ /**
+ * Called for each location change in the monitored tab.
+ *
+ * @param string aType
+ * Packet type.
+ * @param object aPacket
+ * Packet received from the server.
+ */
+ onTabNavigated: function (type, packet) {
+ switch (type) {
+ case "will-navigate": {
+ this.listener.pageLoadBegin(packet);
+ break;
+ }
+ case "navigate": {
+ this.listener.pageLoadDone(packet);
+ break;
+ }
+ }
+ },
+};
+
+// Protocol Helpers
+
+/**
+ * Returns target file for exported HAR data.
+ */
+function getDefaultTargetFile(options) {
+ let path = options.defaultLogDir ||
+ Services.prefs.getCharPref("devtools.netmonitor.har.defaultLogDir");
+ let folder = HarUtils.getLocalDirectory(path);
+ let fileName = HarUtils.getHarFileName(options.defaultFileName,
+ options.jsonp, options.compress);
+
+ folder.append(fileName);
+ folder.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("0666", 8));
+
+ return folder;
+}
+
+// Exports from this module
+exports.HarAutomation = HarAutomation;