--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -61,17 +61,17 @@ loader.lazyRequireGetter(this, "ToolboxB
loader.lazyRequireGetter(this, "SourceMapService",
"devtools/client/framework/source-map-service", true);
loader.lazyRequireGetter(this, "HUDService",
"devtools/client/webconsole/hudservice");
loader.lazyRequireGetter(this, "viewSource",
"devtools/client/shared/view-source");
loader.lazyGetter(this, "registerHarOverlay", () => {
- return require("devtools/client/netmonitor/har/toolbox-overlay").register;
+ return require("devtools/client/netmonitor/src/har/toolbox-overlay").register;
});
/**
* A "Toolbox" is the component that holds all the tools for one specific
* target. Visually, it's a document that includes the tools tabs and all
* the iframes where the tool panels will be living in.
*
* @param {object} target
deleted file mode 100644
--- a/devtools/client/netmonitor/har/har-builder.js
+++ /dev/null
@@ -1,482 +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/. */
-
-"use strict";
-
-const Services = require("Services");
-const appInfo = Services.appinfo;
-const { LocalizationHelper } = require("devtools/shared/l10n");
-const { CurlUtils } = require("devtools/client/shared/curl");
-const {
- getFormDataSections,
- getUrlQuery,
- parseQueryString,
-} = require("devtools/client/netmonitor/utils/request-utils");
-
-const L10N = new LocalizationHelper("devtools/client/locales/har.properties");
-const HAR_VERSION = "1.1";
-
-/**
- * This object is responsible for building HAR file. See HAR spec:
- * https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HAR/Overview.html
- * http://www.softwareishard.com/blog/har-12-spec/
- *
- * @param {Object} options configuration object
- *
- * The following options are supported:
- *
- * - items {Array}: List of Network requests to be exported.
- *
- * - id {String}: ID of the exported page.
- *
- * - title {String}: Title of the exported page.
- *
- * - includeResponseBodies {Boolean}: Set to true to include HTTP response
- * bodies in the result data structure.
- */
-var HarBuilder = function (options) {
- this._options = options;
- this._pageMap = [];
-};
-
-HarBuilder.prototype = {
- // Public API
-
- /**
- * This is the main method used to build the entire result HAR data.
- * The process is asynchronous since it can involve additional RDP
- * communication (e.g. resolving long strings).
- *
- * @returns {Promise} A promise that resolves to the HAR object when
- * the entire build process is done.
- */
- build: function () {
- this.promises = [];
-
- // Build basic structure for data.
- let log = this.buildLog();
-
- // Build entries.
- for (let file of this._options.items) {
- log.entries.push(this.buildEntry(log, file));
- }
-
- // Some data needs to be fetched from the backend during the
- // build process, so wait till all is done.
- return Promise.all(this.promises).then(() => ({ log }));
- },
-
- // Helpers
-
- buildLog: function () {
- return {
- version: HAR_VERSION,
- creator: {
- name: appInfo.name,
- version: appInfo.version
- },
- browser: {
- name: appInfo.name,
- version: appInfo.version
- },
- pages: [],
- entries: [],
- };
- },
-
- buildPage: function (file) {
- let page = {};
-
- // Page start time is set when the first request is processed
- // (see buildEntry)
- page.startedDateTime = 0;
- page.id = "page_" + this._options.id;
- page.title = this._options.title;
-
- return page;
- },
-
- getPage: function (log, file) {
- let id = this._options.id;
- let page = this._pageMap[id];
- if (page) {
- return page;
- }
-
- this._pageMap[id] = page = this.buildPage(file);
- log.pages.push(page);
-
- return page;
- },
-
- buildEntry: function (log, file) {
- let page = this.getPage(log, file);
-
- let entry = {};
- entry.pageref = page.id;
- entry.startedDateTime = dateToJSON(new Date(file.startedMillis));
- entry.time = file.endedMillis - file.startedMillis;
-
- entry.request = this.buildRequest(file);
- entry.response = this.buildResponse(file);
- entry.cache = this.buildCache(file);
- entry.timings = file.eventTimings ? file.eventTimings.timings : {};
-
- if (file.remoteAddress) {
- entry.serverIPAddress = file.remoteAddress;
- }
-
- if (file.remotePort) {
- entry.connection = file.remotePort + "";
- }
-
- // Compute page load start time according to the first request start time.
- if (!page.startedDateTime) {
- page.startedDateTime = entry.startedDateTime;
- page.pageTimings = this.buildPageTimings(page, file);
- }
-
- return entry;
- },
-
- buildPageTimings: function (page, file) {
- // Event timing info isn't available
- let timings = {
- onContentLoad: -1,
- onLoad: -1
- };
-
- return timings;
- },
-
- buildRequest: function (file) {
- let request = {
- bodySize: 0
- };
-
- request.method = file.method;
- request.url = file.url;
- request.httpVersion = file.httpVersion || "";
-
- request.headers = this.buildHeaders(file.requestHeaders);
- request.headers = this.appendHeadersPostData(request.headers, file);
- request.cookies = this.buildCookies(file.requestCookies);
-
- request.queryString = parseQueryString(getUrlQuery(file.url)) || [];
-
- request.postData = this.buildPostData(file);
-
- request.headersSize = file.requestHeaders.headersSize;
-
- // Set request body size, but make sure the body is fetched
- // from the backend.
- if (file.requestPostData) {
- this.fetchData(file.requestPostData.postData.text).then(value => {
- request.bodySize = value.length;
- });
- }
-
- return request;
- },
-
- /**
- * Fetch all header values from the backend (if necessary) and
- * build the result HAR structure.
- *
- * @param {Object} input Request or response header object.
- */
- buildHeaders: function (input) {
- if (!input) {
- return [];
- }
-
- return this.buildNameValuePairs(input.headers);
- },
-
- appendHeadersPostData: function (input = [], file) {
- if (!file.requestPostData) {
- return input;
- }
-
- this.fetchData(file.requestPostData.postData.text).then(value => {
- let multipartHeaders = CurlUtils.getHeadersFromMultipartText(value);
- for (let header of multipartHeaders) {
- input.push(header);
- }
- });
-
- return input;
- },
-
- buildCookies: function (input) {
- if (!input) {
- return [];
- }
-
- return this.buildNameValuePairs(input.cookies);
- },
-
- buildNameValuePairs: function (entries) {
- let result = [];
-
- // HAR requires headers array to be presented, so always
- // return at least an empty array.
- if (!entries) {
- return result;
- }
-
- // Make sure header values are fully fetched from the server.
- entries.forEach(entry => {
- this.fetchData(entry.value).then(value => {
- result.push({
- name: entry.name,
- value: value
- });
- });
- });
-
- return result;
- },
-
- buildPostData: function (file) {
- let postData = {
- mimeType: findValue(file.requestHeaders.headers, "content-type"),
- params: [],
- text: ""
- };
-
- if (!file.requestPostData) {
- return postData;
- }
-
- if (file.requestPostData.postDataDiscarded) {
- postData.comment = L10N.getStr("har.requestBodyNotIncluded");
- return postData;
- }
-
- // Load request body from the backend.
- this.fetchData(file.requestPostData.postData.text).then(postDataText => {
- postData.text = postDataText;
-
- // If we are dealing with URL encoded body, parse parameters.
- let { headers } = file.requestHeaders;
- if (CurlUtils.isUrlEncodedRequest({ headers, postDataText })) {
- postData.mimeType = "application/x-www-form-urlencoded";
-
- // Extract form parameters and produce nice HAR array.
- getFormDataSections(
- file.requestHeaders,
- file.requestHeadersFromUploadStream,
- file.requestPostData,
- ).then(formDataSections => {
- formDataSections.forEach(section => {
- let paramsArray = parseQueryString(section);
- if (paramsArray) {
- postData.params = [...postData.params, ...paramsArray];
- }
- });
- });
- }
- });
-
- return postData;
- },
-
- buildResponse: function (file) {
- let response = {
- status: 0
- };
-
- // Arbitrary value if it's aborted to make sure status has a number
- if (file.status) {
- response.status = parseInt(file.status, 10);
- }
-
- let responseHeaders = file.responseHeaders;
-
- response.statusText = file.statusText || "";
- response.httpVersion = file.httpVersion || "";
-
- response.headers = this.buildHeaders(responseHeaders);
- response.cookies = this.buildCookies(file.responseCookies);
- response.content = this.buildContent(file);
-
- let headers = responseHeaders ? responseHeaders.headers : null;
- let headersSize = responseHeaders ? responseHeaders.headersSize : -1;
-
- response.redirectURL = findValue(headers, "Location");
- response.headersSize = headersSize;
-
- // 'bodySize' is size of the received response body in bytes.
- // Set to zero in case of responses coming from the cache (304).
- // Set to -1 if the info is not available.
- if (typeof file.transferredSize != "number") {
- response.bodySize = (response.status == 304) ? 0 : -1;
- } else {
- response.bodySize = file.transferredSize;
- }
-
- return response;
- },
-
- buildContent: function (file) {
- let content = {
- mimeType: file.mimeType,
- size: -1
- };
-
- let responseContent = file.responseContent;
- if (responseContent && responseContent.content) {
- content.size = responseContent.content.size;
- content.encoding = responseContent.content.encoding;
- }
-
- let includeBodies = this._options.includeResponseBodies;
- let contentDiscarded = responseContent ?
- responseContent.contentDiscarded : false;
-
- // The comment is appended only if the response content
- // is explicitly discarded.
- if (!includeBodies || contentDiscarded) {
- content.comment = L10N.getStr("har.responseBodyNotIncluded");
- return content;
- }
-
- if (responseContent) {
- let text = responseContent.content.text;
- this.fetchData(text).then(value => {
- content.text = value;
- });
- }
-
- return content;
- },
-
- buildCache: function (file) {
- let cache = {};
-
- if (!file.fromCache) {
- return cache;
- }
-
- // There is no such info yet in the Net panel.
- // cache.beforeRequest = {};
-
- if (file.cacheEntry) {
- cache.afterRequest = this.buildCacheEntry(file.cacheEntry);
- } else {
- cache.afterRequest = null;
- }
-
- return cache;
- },
-
- buildCacheEntry: function (cacheEntry) {
- let cache = {};
-
- cache.expires = findValue(cacheEntry, "Expires");
- cache.lastAccess = findValue(cacheEntry, "Last Fetched");
- cache.eTag = "";
- cache.hitCount = findValue(cacheEntry, "Fetch Count");
-
- return cache;
- },
-
- getBlockingEndTime: function (file) {
- if (file.resolveStarted && file.connectStarted) {
- return file.resolvingTime;
- }
-
- if (file.connectStarted) {
- return file.connectingTime;
- }
-
- if (file.sendStarted) {
- return file.sendingTime;
- }
-
- return (file.sendingTime > file.startTime) ?
- file.sendingTime : file.waitingForTime;
- },
-
- // RDP Helpers
-
- fetchData: function (string) {
- let promise = this._options.getString(string).then(value => {
- return value;
- });
-
- // Building HAR is asynchronous and not done till all
- // collected promises are resolved.
- this.promises.push(promise);
-
- return promise;
- }
-};
-
-// Helpers
-
-/**
- * Find specified value within an array of name-value pairs
- * (used for headers, cookies and cache entries)
- */
-function findValue(arr, name) {
- if (!arr) {
- return "";
- }
-
- name = name.toLowerCase();
- let result = arr.find(entry => entry.name.toLowerCase() == name);
- return result ? result.value : "";
-}
-
-/**
- * Generate HAR representation of a date.
- * (YYYY-MM-DDThh:mm:ss.sTZD, e.g. 2009-07-24T19:20:30.45+01:00)
- * See also HAR Schema: http://janodvarko.cz/har/viewer/
- *
- * Note: it would be great if we could utilize Date.toJSON(), but
- * it doesn't return proper time zone offset.
- *
- * An example:
- * This helper returns: 2015-05-29T16:10:30.424+02:00
- * Date.toJSON() returns: 2015-05-29T14:10:30.424Z
- *
- * @param date {Date} The date object we want to convert.
- */
-function dateToJSON(date) {
- function f(n, c) {
- if (!c) {
- c = 2;
- }
- let s = new String(n);
- while (s.length < c) {
- s = "0" + s;
- }
- return s;
- }
-
- let result = date.getFullYear() + "-" +
- f(date.getMonth() + 1) + "-" +
- f(date.getDate()) + "T" +
- f(date.getHours()) + ":" +
- f(date.getMinutes()) + ":" +
- f(date.getSeconds()) + "." +
- f(date.getMilliseconds(), 3);
-
- let offset = date.getTimezoneOffset();
- let positive = offset > 0;
-
- // Convert to positive number before using Math.floor (see issue 5512)
- offset = Math.abs(offset);
- let offsetHours = Math.floor(offset / 60);
- let offsetMinutes = Math.floor(offset % 60);
- let prettyOffset = (positive > 0 ? "-" : "+") + f(offsetHours) +
- ":" + f(offsetMinutes);
-
- return result + prettyOffset;
-}
-
-// Exports from this module
-exports.HarBuilder = HarBuilder;
--- a/devtools/client/netmonitor/moz.build
+++ b/devtools/client/netmonitor/moz.build
@@ -1,27 +1,17 @@
# 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/.
DIRS += [
- 'actions',
- 'components',
- 'har',
- 'middleware',
- 'reducers',
- 'selectors',
- 'utils',
+ 'src'
]
DevToolsModules(
- 'constants.js',
- 'netmonitor-controller.js',
- 'panel.js',
- 'request-list-context-menu.js',
- 'request-list-tooltip.js',
- 'waterfall-background.js',
+ 'netmonitor.js',
+ 'panel.js'
)
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
with Files('**'):
BUG_COMPONENT = ('Firefox', 'Developer Tools: Netmonitor')
--- a/devtools/client/netmonitor/netmonitor.js
+++ b/devtools/client/netmonitor/netmonitor.js
@@ -15,26 +15,28 @@ var Netmonitor = {
window,
commonLibRequire: toolbox.browserRequire,
}).require;
const EventEmitter = require("devtools/shared/event-emitter");
const { createFactory } = require("devtools/client/shared/vendor/react");
const { render } = require("devtools/client/shared/vendor/react-dom");
const Provider = createFactory(require("devtools/client/shared/vendor/react-redux").Provider);
- const { configureStore } = require("./utils/create-store");
+ const { configureStore } = require("./src/utils/create-store");
const store = window.gStore = configureStore();
- const { NetMonitorController } = require("./netmonitor-controller");
+ const { NetMonitorController } = require("./src/netmonitor-controller");
+ NetMonitorController.toolbox = toolbox;
+ NetMonitorController._target = toolbox.target;
this.NetMonitorController = NetMonitorController;
// Inject EventEmitter into netmonitor window.
EventEmitter.decorate(window);
// Components
- const App = createFactory(require("./components/App"));
+ const App = createFactory(require("./src/components/App"));
this.root = document.querySelector(".root");
render(Provider({ store }, App()), this.root);
return NetMonitorController.startupNetMonitor({
client: {
getTabTarget: () => toolbox.target,
--- a/devtools/client/netmonitor/netmonitor.xhtml
+++ b/devtools/client/netmonitor/netmonitor.xhtml
@@ -7,11 +7,11 @@
<head>
<link rel="stylesheet" href="chrome://devtools/content/shared/widgets/widgets.css"/>
<link rel="stylesheet" href="chrome://devtools/skin/widgets.css"/>
<link rel="stylesheet" href="chrome://devtools/skin/netmonitor.css"/>
<script src="chrome://devtools/content/shared/theme-switching.js"/>
</head>
<body class="theme-sidebar" role="application">
<div class="root"></div>
- <script src="netmonitor.js" defer="true"/>
+ <script src="netmonitor.js"/>
</body>
</html>
rename from devtools/client/netmonitor/actions/batching.js
rename to devtools/client/netmonitor/src/actions/batching.js
rename from devtools/client/netmonitor/actions/filters.js
rename to devtools/client/netmonitor/src/actions/filters.js
rename from devtools/client/netmonitor/actions/index.js
rename to devtools/client/netmonitor/src/actions/index.js
rename from devtools/client/netmonitor/actions/moz.build
rename to devtools/client/netmonitor/src/actions/moz.build
rename from devtools/client/netmonitor/actions/requests.js
rename to devtools/client/netmonitor/src/actions/requests.js
rename from devtools/client/netmonitor/actions/selection.js
rename to devtools/client/netmonitor/src/actions/selection.js
rename from devtools/client/netmonitor/actions/sort.js
rename to devtools/client/netmonitor/src/actions/sort.js
rename from devtools/client/netmonitor/actions/timing-markers.js
rename to devtools/client/netmonitor/src/actions/timing-markers.js
rename from devtools/client/netmonitor/actions/ui.js
rename to devtools/client/netmonitor/src/actions/ui.js
rename from devtools/client/netmonitor/components/App.js
rename to devtools/client/netmonitor/src/components/App.js
rename from devtools/client/netmonitor/components/CookiesPanel.js
rename to devtools/client/netmonitor/src/components/CookiesPanel.js
rename from devtools/client/netmonitor/components/CustomRequestPanel.js
rename to devtools/client/netmonitor/src/components/CustomRequestPanel.js
rename from devtools/client/netmonitor/components/Editor.js
rename to devtools/client/netmonitor/src/components/Editor.js
rename from devtools/client/netmonitor/components/HeadersPanel.js
rename to devtools/client/netmonitor/src/components/HeadersPanel.js
rename from devtools/client/netmonitor/components/MDNLink.js
rename to devtools/client/netmonitor/src/components/MDNLink.js
rename from devtools/client/netmonitor/components/MonitorPanel.js
rename to devtools/client/netmonitor/src/components/MonitorPanel.js
rename from devtools/client/netmonitor/components/NetworkDetailsPanel.js
rename to devtools/client/netmonitor/src/components/NetworkDetailsPanel.js
rename from devtools/client/netmonitor/components/ParamsPanel.js
rename to devtools/client/netmonitor/src/components/ParamsPanel.js
rename from devtools/client/netmonitor/components/PreviewPanel.js
rename to devtools/client/netmonitor/src/components/PreviewPanel.js
rename from devtools/client/netmonitor/components/PropertiesView.js
rename to devtools/client/netmonitor/src/components/PropertiesView.js
rename from devtools/client/netmonitor/components/RequestList.js
rename to devtools/client/netmonitor/src/components/RequestList.js
rename from devtools/client/netmonitor/components/RequestListContent.js
rename to devtools/client/netmonitor/src/components/RequestListContent.js
rename from devtools/client/netmonitor/components/RequestListEmptyNotice.js
rename to devtools/client/netmonitor/src/components/RequestListEmptyNotice.js
rename from devtools/client/netmonitor/components/RequestListHeader.js
rename to devtools/client/netmonitor/src/components/RequestListHeader.js
rename from devtools/client/netmonitor/components/RequestListItem.js
rename to devtools/client/netmonitor/src/components/RequestListItem.js
rename from devtools/client/netmonitor/components/ResponsePanel.js
rename to devtools/client/netmonitor/src/components/ResponsePanel.js
rename from devtools/client/netmonitor/components/SecurityPanel.js
rename to devtools/client/netmonitor/src/components/SecurityPanel.js
rename from devtools/client/netmonitor/components/StatisticsPanel.js
rename to devtools/client/netmonitor/src/components/StatisticsPanel.js
rename from devtools/client/netmonitor/components/TabboxPanel.js
rename to devtools/client/netmonitor/src/components/TabboxPanel.js
rename from devtools/client/netmonitor/components/TimingsPanel.js
rename to devtools/client/netmonitor/src/components/TimingsPanel.js
rename from devtools/client/netmonitor/components/toolbar.js
rename to devtools/client/netmonitor/src/components/Toolbar.js
rename from devtools/client/netmonitor/components/moz.build
rename to devtools/client/netmonitor/src/components/moz.build
rename from devtools/client/netmonitor/constants.js
rename to devtools/client/netmonitor/src/constants.js
rename from devtools/client/netmonitor/har/har-automation.js
rename to devtools/client/netmonitor/src/har/har-automation.js
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/src/har/har-builder.js
@@ -0,0 +1,482 @@
+/* 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 Services = require("Services");
+const appInfo = Services.appinfo;
+const { LocalizationHelper } = require("devtools/shared/l10n");
+const { CurlUtils } = require("devtools/client/shared/curl");
+const {
+ getFormDataSections,
+ getUrlQuery,
+ parseQueryString,
+} = require("../utils/request-utils");
+
+const L10N = new LocalizationHelper("devtools/client/locales/har.properties");
+const HAR_VERSION = "1.1";
+
+/**
+ * This object is responsible for building HAR file. See HAR spec:
+ * https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/HAR/Overview.html
+ * http://www.softwareishard.com/blog/har-12-spec/
+ *
+ * @param {Object} options configuration object
+ *
+ * The following options are supported:
+ *
+ * - items {Array}: List of Network requests to be exported.
+ *
+ * - id {String}: ID of the exported page.
+ *
+ * - title {String}: Title of the exported page.
+ *
+ * - includeResponseBodies {Boolean}: Set to true to include HTTP response
+ * bodies in the result data structure.
+ */
+var HarBuilder = function (options) {
+ this._options = options;
+ this._pageMap = [];
+};
+
+HarBuilder.prototype = {
+ // Public API
+
+ /**
+ * This is the main method used to build the entire result HAR data.
+ * The process is asynchronous since it can involve additional RDP
+ * communication (e.g. resolving long strings).
+ *
+ * @returns {Promise} A promise that resolves to the HAR object when
+ * the entire build process is done.
+ */
+ build: function () {
+ this.promises = [];
+
+ // Build basic structure for data.
+ let log = this.buildLog();
+
+ // Build entries.
+ for (let file of this._options.items) {
+ log.entries.push(this.buildEntry(log, file));
+ }
+
+ // Some data needs to be fetched from the backend during the
+ // build process, so wait till all is done.
+ return Promise.all(this.promises).then(() => ({ log }));
+ },
+
+ // Helpers
+
+ buildLog: function () {
+ return {
+ version: HAR_VERSION,
+ creator: {
+ name: appInfo.name,
+ version: appInfo.version
+ },
+ browser: {
+ name: appInfo.name,
+ version: appInfo.version
+ },
+ pages: [],
+ entries: [],
+ };
+ },
+
+ buildPage: function (file) {
+ let page = {};
+
+ // Page start time is set when the first request is processed
+ // (see buildEntry)
+ page.startedDateTime = 0;
+ page.id = "page_" + this._options.id;
+ page.title = this._options.title;
+
+ return page;
+ },
+
+ getPage: function (log, file) {
+ let id = this._options.id;
+ let page = this._pageMap[id];
+ if (page) {
+ return page;
+ }
+
+ this._pageMap[id] = page = this.buildPage(file);
+ log.pages.push(page);
+
+ return page;
+ },
+
+ buildEntry: function (log, file) {
+ let page = this.getPage(log, file);
+
+ let entry = {};
+ entry.pageref = page.id;
+ entry.startedDateTime = dateToJSON(new Date(file.startedMillis));
+ entry.time = file.endedMillis - file.startedMillis;
+
+ entry.request = this.buildRequest(file);
+ entry.response = this.buildResponse(file);
+ entry.cache = this.buildCache(file);
+ entry.timings = file.eventTimings ? file.eventTimings.timings : {};
+
+ if (file.remoteAddress) {
+ entry.serverIPAddress = file.remoteAddress;
+ }
+
+ if (file.remotePort) {
+ entry.connection = file.remotePort + "";
+ }
+
+ // Compute page load start time according to the first request start time.
+ if (!page.startedDateTime) {
+ page.startedDateTime = entry.startedDateTime;
+ page.pageTimings = this.buildPageTimings(page, file);
+ }
+
+ return entry;
+ },
+
+ buildPageTimings: function (page, file) {
+ // Event timing info isn't available
+ let timings = {
+ onContentLoad: -1,
+ onLoad: -1
+ };
+
+ return timings;
+ },
+
+ buildRequest: function (file) {
+ let request = {
+ bodySize: 0
+ };
+
+ request.method = file.method;
+ request.url = file.url;
+ request.httpVersion = file.httpVersion || "";
+
+ request.headers = this.buildHeaders(file.requestHeaders);
+ request.headers = this.appendHeadersPostData(request.headers, file);
+ request.cookies = this.buildCookies(file.requestCookies);
+
+ request.queryString = parseQueryString(getUrlQuery(file.url)) || [];
+
+ request.postData = this.buildPostData(file);
+
+ request.headersSize = file.requestHeaders.headersSize;
+
+ // Set request body size, but make sure the body is fetched
+ // from the backend.
+ if (file.requestPostData) {
+ this.fetchData(file.requestPostData.postData.text).then(value => {
+ request.bodySize = value.length;
+ });
+ }
+
+ return request;
+ },
+
+ /**
+ * Fetch all header values from the backend (if necessary) and
+ * build the result HAR structure.
+ *
+ * @param {Object} input Request or response header object.
+ */
+ buildHeaders: function (input) {
+ if (!input) {
+ return [];
+ }
+
+ return this.buildNameValuePairs(input.headers);
+ },
+
+ appendHeadersPostData: function (input = [], file) {
+ if (!file.requestPostData) {
+ return input;
+ }
+
+ this.fetchData(file.requestPostData.postData.text).then(value => {
+ let multipartHeaders = CurlUtils.getHeadersFromMultipartText(value);
+ for (let header of multipartHeaders) {
+ input.push(header);
+ }
+ });
+
+ return input;
+ },
+
+ buildCookies: function (input) {
+ if (!input) {
+ return [];
+ }
+
+ return this.buildNameValuePairs(input.cookies);
+ },
+
+ buildNameValuePairs: function (entries) {
+ let result = [];
+
+ // HAR requires headers array to be presented, so always
+ // return at least an empty array.
+ if (!entries) {
+ return result;
+ }
+
+ // Make sure header values are fully fetched from the server.
+ entries.forEach(entry => {
+ this.fetchData(entry.value).then(value => {
+ result.push({
+ name: entry.name,
+ value: value
+ });
+ });
+ });
+
+ return result;
+ },
+
+ buildPostData: function (file) {
+ let postData = {
+ mimeType: findValue(file.requestHeaders.headers, "content-type"),
+ params: [],
+ text: ""
+ };
+
+ if (!file.requestPostData) {
+ return postData;
+ }
+
+ if (file.requestPostData.postDataDiscarded) {
+ postData.comment = L10N.getStr("har.requestBodyNotIncluded");
+ return postData;
+ }
+
+ // Load request body from the backend.
+ this.fetchData(file.requestPostData.postData.text).then(postDataText => {
+ postData.text = postDataText;
+
+ // If we are dealing with URL encoded body, parse parameters.
+ let { headers } = file.requestHeaders;
+ if (CurlUtils.isUrlEncodedRequest({ headers, postDataText })) {
+ postData.mimeType = "application/x-www-form-urlencoded";
+
+ // Extract form parameters and produce nice HAR array.
+ getFormDataSections(
+ file.requestHeaders,
+ file.requestHeadersFromUploadStream,
+ file.requestPostData,
+ ).then(formDataSections => {
+ formDataSections.forEach(section => {
+ let paramsArray = parseQueryString(section);
+ if (paramsArray) {
+ postData.params = [...postData.params, ...paramsArray];
+ }
+ });
+ });
+ }
+ });
+
+ return postData;
+ },
+
+ buildResponse: function (file) {
+ let response = {
+ status: 0
+ };
+
+ // Arbitrary value if it's aborted to make sure status has a number
+ if (file.status) {
+ response.status = parseInt(file.status, 10);
+ }
+
+ let responseHeaders = file.responseHeaders;
+
+ response.statusText = file.statusText || "";
+ response.httpVersion = file.httpVersion || "";
+
+ response.headers = this.buildHeaders(responseHeaders);
+ response.cookies = this.buildCookies(file.responseCookies);
+ response.content = this.buildContent(file);
+
+ let headers = responseHeaders ? responseHeaders.headers : null;
+ let headersSize = responseHeaders ? responseHeaders.headersSize : -1;
+
+ response.redirectURL = findValue(headers, "Location");
+ response.headersSize = headersSize;
+
+ // 'bodySize' is size of the received response body in bytes.
+ // Set to zero in case of responses coming from the cache (304).
+ // Set to -1 if the info is not available.
+ if (typeof file.transferredSize != "number") {
+ response.bodySize = (response.status == 304) ? 0 : -1;
+ } else {
+ response.bodySize = file.transferredSize;
+ }
+
+ return response;
+ },
+
+ buildContent: function (file) {
+ let content = {
+ mimeType: file.mimeType,
+ size: -1
+ };
+
+ let responseContent = file.responseContent;
+ if (responseContent && responseContent.content) {
+ content.size = responseContent.content.size;
+ content.encoding = responseContent.content.encoding;
+ }
+
+ let includeBodies = this._options.includeResponseBodies;
+ let contentDiscarded = responseContent ?
+ responseContent.contentDiscarded : false;
+
+ // The comment is appended only if the response content
+ // is explicitly discarded.
+ if (!includeBodies || contentDiscarded) {
+ content.comment = L10N.getStr("har.responseBodyNotIncluded");
+ return content;
+ }
+
+ if (responseContent) {
+ let text = responseContent.content.text;
+ this.fetchData(text).then(value => {
+ content.text = value;
+ });
+ }
+
+ return content;
+ },
+
+ buildCache: function (file) {
+ let cache = {};
+
+ if (!file.fromCache) {
+ return cache;
+ }
+
+ // There is no such info yet in the Net panel.
+ // cache.beforeRequest = {};
+
+ if (file.cacheEntry) {
+ cache.afterRequest = this.buildCacheEntry(file.cacheEntry);
+ } else {
+ cache.afterRequest = null;
+ }
+
+ return cache;
+ },
+
+ buildCacheEntry: function (cacheEntry) {
+ let cache = {};
+
+ cache.expires = findValue(cacheEntry, "Expires");
+ cache.lastAccess = findValue(cacheEntry, "Last Fetched");
+ cache.eTag = "";
+ cache.hitCount = findValue(cacheEntry, "Fetch Count");
+
+ return cache;
+ },
+
+ getBlockingEndTime: function (file) {
+ if (file.resolveStarted && file.connectStarted) {
+ return file.resolvingTime;
+ }
+
+ if (file.connectStarted) {
+ return file.connectingTime;
+ }
+
+ if (file.sendStarted) {
+ return file.sendingTime;
+ }
+
+ return (file.sendingTime > file.startTime) ?
+ file.sendingTime : file.waitingForTime;
+ },
+
+ // RDP Helpers
+
+ fetchData: function (string) {
+ let promise = this._options.getString(string).then(value => {
+ return value;
+ });
+
+ // Building HAR is asynchronous and not done till all
+ // collected promises are resolved.
+ this.promises.push(promise);
+
+ return promise;
+ }
+};
+
+// Helpers
+
+/**
+ * Find specified value within an array of name-value pairs
+ * (used for headers, cookies and cache entries)
+ */
+function findValue(arr, name) {
+ if (!arr) {
+ return "";
+ }
+
+ name = name.toLowerCase();
+ let result = arr.find(entry => entry.name.toLowerCase() == name);
+ return result ? result.value : "";
+}
+
+/**
+ * Generate HAR representation of a date.
+ * (YYYY-MM-DDThh:mm:ss.sTZD, e.g. 2009-07-24T19:20:30.45+01:00)
+ * See also HAR Schema: http://janodvarko.cz/har/viewer/
+ *
+ * Note: it would be great if we could utilize Date.toJSON(), but
+ * it doesn't return proper time zone offset.
+ *
+ * An example:
+ * This helper returns: 2015-05-29T16:10:30.424+02:00
+ * Date.toJSON() returns: 2015-05-29T14:10:30.424Z
+ *
+ * @param date {Date} The date object we want to convert.
+ */
+function dateToJSON(date) {
+ function f(n, c) {
+ if (!c) {
+ c = 2;
+ }
+ let s = new String(n);
+ while (s.length < c) {
+ s = "0" + s;
+ }
+ return s;
+ }
+
+ let result = date.getFullYear() + "-" +
+ f(date.getMonth() + 1) + "-" +
+ f(date.getDate()) + "T" +
+ f(date.getHours()) + ":" +
+ f(date.getMinutes()) + ":" +
+ f(date.getSeconds()) + "." +
+ f(date.getMilliseconds(), 3);
+
+ let offset = date.getTimezoneOffset();
+ let positive = offset > 0;
+
+ // Convert to positive number before using Math.floor (see issue 5512)
+ offset = Math.abs(offset);
+ let offsetHours = Math.floor(offset / 60);
+ let offsetMinutes = Math.floor(offset % 60);
+ let prettyOffset = (positive > 0 ? "-" : "+") + f(offsetHours) +
+ ":" + f(offsetMinutes);
+
+ return result + prettyOffset;
+}
+
+// Exports from this module
+exports.HarBuilder = HarBuilder;
rename from devtools/client/netmonitor/har/har-collector.js
rename to devtools/client/netmonitor/src/har/har-collector.js
rename from devtools/client/netmonitor/har/har-exporter.js
rename to devtools/client/netmonitor/src/har/har-exporter.js
rename from devtools/client/netmonitor/har/har-utils.js
rename to devtools/client/netmonitor/src/har/har-utils.js
rename from devtools/client/netmonitor/har/moz.build
rename to devtools/client/netmonitor/src/har/moz.build
rename from devtools/client/netmonitor/har/test/.eslintrc.js
rename to devtools/client/netmonitor/src/har/test/.eslintrc.js
rename from devtools/client/netmonitor/har/test/browser.ini
rename to devtools/client/netmonitor/src/har/test/browser.ini
rename from devtools/client/netmonitor/har/test/browser_net_har_copy_all_as_har.js
rename to devtools/client/netmonitor/src/har/test/browser_net_har_copy_all_as_har.js
rename from devtools/client/netmonitor/har/test/browser_net_har_post_data.js
rename to devtools/client/netmonitor/src/har/test/browser_net_har_post_data.js
rename from devtools/client/netmonitor/har/test/browser_net_har_throttle_upload.js
rename to devtools/client/netmonitor/src/har/test/browser_net_har_throttle_upload.js
rename from devtools/client/netmonitor/har/test/head.js
rename to devtools/client/netmonitor/src/har/test/head.js
rename from devtools/client/netmonitor/har/test/html_har_post-data-test-page.html
rename to devtools/client/netmonitor/src/har/test/html_har_post-data-test-page.html
rename from devtools/client/netmonitor/har/toolbox-overlay.js
rename to devtools/client/netmonitor/src/har/toolbox-overlay.js
rename from devtools/client/netmonitor/middleware/batching.js
rename to devtools/client/netmonitor/src/middleware/batching.js
rename from devtools/client/netmonitor/middleware/moz.build
rename to devtools/client/netmonitor/src/middleware/moz.build
rename from devtools/client/netmonitor/middleware/prefs.js
rename to devtools/client/netmonitor/src/middleware/prefs.js
new file mode 100644
--- /dev/null
+++ b/devtools/client/netmonitor/src/moz.build
@@ -0,0 +1,21 @@
+# 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/.
+
+DIRS += [
+ 'actions',
+ 'components',
+ 'har',
+ 'middleware',
+ 'reducers',
+ 'selectors',
+ 'utils',
+]
+
+DevToolsModules(
+ 'constants.js',
+ 'netmonitor-controller.js',
+ 'request-list-context-menu.js',
+ 'request-list-tooltip.js',
+ 'waterfall-background.js',
+)
rename from devtools/client/netmonitor/netmonitor-controller.js
rename to devtools/client/netmonitor/src/netmonitor-controller.js
rename from devtools/client/netmonitor/reducers/batching.js
rename to devtools/client/netmonitor/src/reducers/batching.js
rename from devtools/client/netmonitor/reducers/filters.js
rename to devtools/client/netmonitor/src/reducers/filters.js
rename from devtools/client/netmonitor/reducers/index.js
rename to devtools/client/netmonitor/src/reducers/index.js
rename from devtools/client/netmonitor/reducers/moz.build
rename to devtools/client/netmonitor/src/reducers/moz.build
rename from devtools/client/netmonitor/reducers/requests.js
rename to devtools/client/netmonitor/src/reducers/requests.js
rename from devtools/client/netmonitor/reducers/sort.js
rename to devtools/client/netmonitor/src/reducers/sort.js
rename from devtools/client/netmonitor/reducers/timing-markers.js
rename to devtools/client/netmonitor/src/reducers/timing-markers.js
rename from devtools/client/netmonitor/reducers/ui.js
rename to devtools/client/netmonitor/src/reducers/ui.js
rename from devtools/client/netmonitor/request-list-context-menu.js
rename to devtools/client/netmonitor/src/request-list-context-menu.js
rename from devtools/client/netmonitor/request-list-tooltip.js
rename to devtools/client/netmonitor/src/request-list-tooltip.js
rename from devtools/client/netmonitor/selectors/filters.js
rename to devtools/client/netmonitor/src/selectors/filters.js
rename from devtools/client/netmonitor/selectors/index.js
rename to devtools/client/netmonitor/src/selectors/index.js
rename from devtools/client/netmonitor/selectors/moz.build
rename to devtools/client/netmonitor/src/selectors/moz.build
rename from devtools/client/netmonitor/selectors/requests.js
rename to devtools/client/netmonitor/src/selectors/requests.js
rename from devtools/client/netmonitor/selectors/ui.js
rename to devtools/client/netmonitor/src/selectors/ui.js
rename from devtools/client/netmonitor/utils/client.js
rename to devtools/client/netmonitor/src/utils/client.js
rename from devtools/client/netmonitor/utils/create-store.js
rename to devtools/client/netmonitor/src/utils/create-store.js
rename from devtools/client/netmonitor/utils/filter-predicates.js
rename to devtools/client/netmonitor/src/utils/filter-predicates.js
rename from devtools/client/netmonitor/utils/format-utils.js
rename to devtools/client/netmonitor/src/utils/format-utils.js
rename from devtools/client/netmonitor/utils/l10n.js
rename to devtools/client/netmonitor/src/utils/l10n.js
rename from devtools/client/netmonitor/utils/mdn-utils.js
rename to devtools/client/netmonitor/src/utils/mdn-utils.js
rename from devtools/client/netmonitor/utils/moz.build
rename to devtools/client/netmonitor/src/utils/moz.build
rename from devtools/client/netmonitor/utils/prefs.js
rename to devtools/client/netmonitor/src/utils/prefs.js
rename from devtools/client/netmonitor/utils/request-utils.js
rename to devtools/client/netmonitor/src/utils/request-utils.js
rename from devtools/client/netmonitor/utils/sort-predicates.js
rename to devtools/client/netmonitor/src/utils/sort-predicates.js
rename from devtools/client/netmonitor/waterfall-background.js
rename to devtools/client/netmonitor/src/waterfall-background.js