summaryrefslogtreecommitdiffstats
path: root/devtools
diff options
context:
space:
mode:
Diffstat (limited to 'devtools')
-rw-r--r--devtools/bootstrap.js2
-rw-r--r--devtools/client/commandline/test/browser_cmd_commands.js2
-rw-r--r--devtools/client/devtools-startup.js27
-rw-r--r--devtools/client/framework/toolbox.js2
-rw-r--r--devtools/client/inspector/rules/models/rule.js11
-rw-r--r--devtools/client/inspector/rules/test/browser_rules_style-editor-link.js35
-rw-r--r--devtools/client/inspector/test/browser.ini1
-rw-r--r--devtools/client/inspector/test/browser_inspector_infobar_04.js38
-rw-r--r--devtools/client/locales/en-US/netmonitor.properties75
-rw-r--r--devtools/client/menus.js2
-rw-r--r--devtools/client/netmonitor/actions/index.js5
-rw-r--r--devtools/client/netmonitor/actions/moz.build3
-rw-r--r--devtools/client/netmonitor/actions/requests.js25
-rw-r--r--devtools/client/netmonitor/actions/sidebar.js49
-rw-r--r--devtools/client/netmonitor/actions/ui.js36
-rw-r--r--devtools/client/netmonitor/components/moz.build1
-rw-r--r--devtools/client/netmonitor/components/summary-button.js61
-rw-r--r--devtools/client/netmonitor/components/toggle-button.js32
-rw-r--r--devtools/client/netmonitor/constants.js8
-rw-r--r--devtools/client/netmonitor/moz.build1
-rw-r--r--devtools/client/netmonitor/netmonitor-view.js20
-rw-r--r--devtools/client/netmonitor/netmonitor.xul33
-rw-r--r--devtools/client/netmonitor/open-request-in-tab.js40
-rw-r--r--devtools/client/netmonitor/performance-statistics-view.js36
-rw-r--r--devtools/client/netmonitor/reducers/index.js6
-rw-r--r--devtools/client/netmonitor/reducers/moz.build3
-rw-r--r--devtools/client/netmonitor/reducers/requests.js28
-rw-r--r--devtools/client/netmonitor/reducers/sidebar.js43
-rw-r--r--devtools/client/netmonitor/reducers/ui.js40
-rw-r--r--devtools/client/netmonitor/request-list-context-menu.js4
-rw-r--r--devtools/client/netmonitor/request-utils.js2
-rw-r--r--devtools/client/netmonitor/requests-menu-view.js122
-rw-r--r--devtools/client/netmonitor/selectors/index.js72
-rw-r--r--devtools/client/netmonitor/test/browser_net_charts-03.js45
-rw-r--r--devtools/client/netmonitor/test/browser_net_charts-04.js16
-rw-r--r--devtools/client/netmonitor/test/browser_net_charts-05.js10
-rw-r--r--devtools/client/netmonitor/test/browser_net_charts-07.js16
-rw-r--r--devtools/client/netmonitor/test/browser_net_curl-utils.js16
-rw-r--r--devtools/client/netmonitor/test/browser_net_footer-summary.js31
-rw-r--r--devtools/client/netmonitor/test/browser_net_security-details.js4
-rw-r--r--devtools/client/netmonitor/test/browser_net_simple-request-data.js2
-rw-r--r--devtools/client/netmonitor/toolbar-view.js13
-rw-r--r--devtools/client/scratchpad/scratchpad.js5
-rw-r--r--devtools/client/scratchpad/scratchpad.xul2
-rw-r--r--devtools/client/scratchpad/test/browser_scratchpad_open_error_console.js2
-rw-r--r--devtools/client/shared/curl.js6
-rw-r--r--devtools/client/shared/widgets/Chart.jsm25
-rw-r--r--devtools/client/sourceeditor/editor-commands-controller.js97
-rw-r--r--devtools/client/sourceeditor/editor.js10
-rw-r--r--devtools/client/sourceeditor/moz.build1
-rw-r--r--devtools/client/styleeditor/StyleEditorUI.jsm109
-rw-r--r--devtools/client/styleeditor/StyleSheetEditor.jsm3
-rw-r--r--devtools/client/styleeditor/test/browser.ini1
-rw-r--r--devtools/client/styleeditor/test/browser_styleeditor_add_stylesheet.js37
-rw-r--r--devtools/client/styleeditor/test/browser_styleeditor_import.js2
-rw-r--r--devtools/client/themes/common.css6
-rw-r--r--devtools/client/themes/netmonitor.css30
-rw-r--r--devtools/client/webconsole/hudservice.js58
-rw-r--r--devtools/client/webconsole/panel.js2
-rw-r--r--devtools/client/webconsole/test/browser.ini1
-rw-r--r--devtools/client/webconsole/test/browser_console.js19
-rw-r--r--devtools/client/webconsole/test/browser_console_restore.js30
-rw-r--r--devtools/client/webconsole/test/head.js17
-rw-r--r--devtools/server/actors/highlighters/box-model.js15
-rw-r--r--devtools/server/actors/stylesheets.js120
-rw-r--r--devtools/server/actors/webbrowser.js6
-rw-r--r--devtools/shared/gcli/commands/cmd.js4
-rw-r--r--devtools/shared/gcli/commands/cookie.js22
-rw-r--r--devtools/shared/gcli/commands/screenshot.js6
-rw-r--r--devtools/shared/gcli/source/lib/gcli/commands/help.js4
-rw-r--r--devtools/shared/inspector/css-logic.js12
-rw-r--r--devtools/shared/locales/en-US/gcli.properties9
-rw-r--r--devtools/shared/specs/stylesheets.js8
-rw-r--r--devtools/shared/webconsole/network-helper.js27
-rw-r--r--devtools/shared/webconsole/network-monitor.js55
75 files changed, 1313 insertions, 456 deletions
diff --git a/devtools/bootstrap.js b/devtools/bootstrap.js
index bedca0c78..3c154fef4 100644
--- a/devtools/bootstrap.js
+++ b/devtools/bootstrap.js
@@ -187,7 +187,7 @@ function reload(event) {
// HUDService is going to close it on unload.
// Instead we have to manually toggle it.
if (reopenBrowserConsole) {
- let HUDService = devtools.require("devtools/client/webconsole/hudservice");
+ let {HUDService} = devtools.require("devtools/client/webconsole/hudservice");
HUDService.toggleBrowserConsole();
}
diff --git a/devtools/client/commandline/test/browser_cmd_commands.js b/devtools/client/commandline/test/browser_cmd_commands.js
index 6c69034ec..78db77bfd 100644
--- a/devtools/client/commandline/test/browser_cmd_commands.js
+++ b/devtools/client/commandline/test/browser_cmd_commands.js
@@ -4,7 +4,7 @@
// Test various GCLI commands
const TEST_URI = "data:text/html;charset=utf-8,gcli-commands";
-const HUDService = require("devtools/client/webconsole/hudservice");
+const {HUDService} = require("devtools/client/webconsole/hudservice");
// Use the old webconsole since pprint isn't working on new one (Bug 1304794)
Services.prefs.setBoolPref("devtools.webconsole.new-frontend-enabled", false);
diff --git a/devtools/client/devtools-startup.js b/devtools/client/devtools-startup.js
index 2271dd790..4a385a9d5 100644
--- a/devtools/client/devtools-startup.js
+++ b/devtools/client/devtools-startup.js
@@ -25,7 +25,7 @@ function DevToolsStartup() {}
DevToolsStartup.prototype = {
handle: function (cmdLine) {
- let consoleFlag = cmdLine.handleFlag("jsconsole", false);
+ let consoleFlag = cmdLine.handleFlag("browserconsole", false);
let debuggerFlag = cmdLine.handleFlag("jsdebugger", false);
let devtoolsFlag = cmdLine.handleFlag("devtools", false);
@@ -75,9 +75,9 @@ DevToolsStartup.prototype = {
this.initDevTools();
let { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
- let hudservice = require("devtools/client/webconsole/hudservice");
+ let { HUDService } = require("devtools/client/webconsole/hudservice");
let { console } = Cu.import("resource://gre/modules/Console.jsm", {});
- hudservice.toggleBrowserConsole().then(null, console.error);
+ HUDService.toggleBrowserConsole().then(null, console.error);
} else {
// the Browser Console was already open
window.focus();
@@ -104,12 +104,14 @@ DevToolsStartup.prototype = {
return Services.prefs.getBoolPref(pref);
});
} catch (ex) {
+ let { console } = Cu.import("resource://gre/modules/Console.jsm", {});
console.error(ex);
return false;
}
if (!remoteDebuggingEnabled) {
let errorMsg = "Could not run chrome debugger! You need the following " +
"prefs to be set to true: " + kDebuggerPrefs.join(", ");
+ let { console } = Cu.import("resource://gre/modules/Console.jsm", {});
console.error(new Error(errorMsg));
// Dump as well, as we're doing this from a commandline, make sure people
// don't miss it:
@@ -190,7 +192,10 @@ DevToolsStartup.prototype = {
listener.open();
dump("Started debugger server on " + portOrPath + "\n");
} catch (e) {
- dump("Unable to start debugger server on " + portOrPath + ": " + e);
+ let _error = "Unable to start debugger server on " + portOrPath + ": "
+ + e;
+ Cu.reportError(_error);
+ dump(_error + "\n");
}
if (cmdLine.state == Ci.nsICommandLine.STATE_REMOTE_AUTO) {
@@ -199,12 +204,14 @@ DevToolsStartup.prototype = {
},
/* eslint-disable max-len */
- helpInfo: " --jsconsole Open the Browser Console.\n" +
- " --jsdebugger Open the Browser Toolbox.\n" +
- " --devtools Open DevTools on initial load.\n" +
- " --start-debugger-server [ws:][ <port> | <path> ] Start the debugger server on\n" +
- " a TCP port or Unix domain socket path. Defaults to TCP port\n" +
- " 6000. Use WebSocket protocol if ws: prefix is specified.\n",
+ helpInfo: " --browserconsole Open the Browser Console.\n" +
+ " --jsdebugger Open the Browser Toolbox.\n" +
+ " --devtools Open DevTools on initial load.\n" +
+ " --start-debugger-server [ws:][<port>|<path>] Start the debugger server on\n" +
+ " a TCP port or Unix domain socket path.\n" +
+ " Defaults to TCP port 6000.\n" +
+ " Use WebSocket protocol if ws: prefix\n" +
+ " is specified.\n",
/* eslint-disable max-len */
classID: Components.ID("{9e9a9283-0ce9-4e4a-8f1c-ba129a032c32}"),
diff --git a/devtools/client/framework/toolbox.js b/devtools/client/framework/toolbox.js
index 82d5d2915..926e30647 100644
--- a/devtools/client/framework/toolbox.js
+++ b/devtools/client/framework/toolbox.js
@@ -22,7 +22,7 @@ var {Task} = require("devtools/shared/task");
var {gDevTools} = require("devtools/client/framework/devtools");
var EventEmitter = require("devtools/shared/event-emitter");
var Telemetry = require("devtools/client/shared/telemetry");
-var HUDService = require("devtools/client/webconsole/hudservice");
+var { HUDService } = require("devtools/client/webconsole/hudservice");
var viewSource = require("devtools/client/shared/view-source");
var { attachThread, detachThread } = require("./attach-thread");
var Menu = require("devtools/client/framework/menu");
diff --git a/devtools/client/inspector/rules/models/rule.js b/devtools/client/inspector/rules/models/rule.js
index 1a3fa057a..4c978cb58 100644
--- a/devtools/client/inspector/rules/models/rule.js
+++ b/devtools/client/inspector/rules/models/rule.js
@@ -140,11 +140,18 @@ Rule.prototype = {
line, mediaText}) => {
let mediaString = mediaText ? " @" + mediaText : "";
let linePart = line > 0 ? (":" + line) : "";
+ let decodedHref = href;
+
+ if (decodedHref) {
+ try {
+ decodedHref = decodeURIComponent(href);
+ } catch (e) {}
+ }
let sourceStrings = {
- full: (href || CssLogic.l10n("rule.sourceInline")) + linePart +
+ full: (decodedHref || CssLogic.l10n("rule.sourceInline")) + linePart +
mediaString,
- short: CssLogic.shortSource({href: href}) + linePart + mediaString
+ short: CssLogic.shortSource({href: decodedHref}) + linePart + mediaString
};
return sourceStrings;
diff --git a/devtools/client/inspector/rules/test/browser_rules_style-editor-link.js b/devtools/client/inspector/rules/test/browser_rules_style-editor-link.js
index 927deb8ce..6a4fd1d66 100644
--- a/devtools/client/inspector/rules/test/browser_rules_style-editor-link.js
+++ b/devtools/client/inspector/rules/test/browser_rules_style-editor-link.js
@@ -10,10 +10,12 @@ thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Unknown sheet source");
// Test the links from the rule-view to the styleeditor
-const STYLESHEET_URL = "data:text/css," + encodeURIComponent(
- ["#first {",
- "color: blue",
- "}"].join("\n"));
+const STYLESHEET_DATA_URL_CONTENTS = ["#first {",
+ "color: blue",
+ "}"].join("\n");
+const STYLESHEET_DATA_URL =
+ `data:text/css,${encodeURIComponent(STYLESHEET_DATA_URL_CONTENTS)}`;
+const STYLESHEET_DECODED_DATA_URL = `data:text/css,${STYLESHEET_DATA_URL_CONTENTS}`;
const EXTERNAL_STYLESHEET_FILE_NAME = "doc_style_editor_link.css";
const EXTERNAL_STYLESHEET_URL = URL_ROOT + EXTERNAL_STYLESHEET_FILE_NAME;
@@ -31,7 +33,7 @@ const DOCUMENT_URL = "data:text/html;charset=utf-8," + encodeURIComponent(`
<style>
div { font-weight: bold; }
</style>
- <link rel="stylesheet" type="text/css" href="${STYLESHEET_URL}">
+ <link rel="stylesheet" type="text/css" href="${STYLESHEET_DATA_URL}">
<link rel="stylesheet" type="text/css" href="${EXTERNAL_STYLESHEET_URL}">
</head>
<body>
@@ -178,15 +180,28 @@ function* testDisabledStyleEditor(view, toolbox) {
}
function testRuleViewLinkLabel(view) {
- let link = getRuleViewLinkByIndex(view, 2);
+ info("Checking the data URL link label");
+
+ let link = getRuleViewLinkByIndex(view, 1);
let labelElem = link.querySelector(".ruleview-rule-source-label");
let value = labelElem.textContent;
let tooltipText = labelElem.getAttribute("title");
- is(value, EXTERNAL_STYLESHEET_FILE_NAME + ":1",
- "rule view stylesheet display value matches filename and line number");
- is(tooltipText, EXTERNAL_STYLESHEET_URL + ":1",
- "rule view stylesheet tooltip text matches the full URI path");
+ is(value, `${STYLESHEET_DATA_URL_CONTENTS}:1`,
+ "Rule view data URL stylesheet display value matches contents");
+ is(tooltipText, `${STYLESHEET_DECODED_DATA_URL}:1`,
+ "Rule view data URL stylesheet tooltip text matches the full URI path");
+
+ info("Checking the external link label");
+ link = getRuleViewLinkByIndex(view, 2);
+ labelElem = link.querySelector(".ruleview-rule-source-label");
+ value = labelElem.textContent;
+ tooltipText = labelElem.getAttribute("title");
+
+ is(value, `${EXTERNAL_STYLESHEET_FILE_NAME}:1`,
+ "Rule view external stylesheet display value matches filename and line number");
+ is(tooltipText, `${EXTERNAL_STYLESHEET_URL}:1`,
+ "Rule view external stylesheet tooltip text matches the full URI path");
}
function testUnselectableRuleViewLink(view, index) {
diff --git a/devtools/client/inspector/test/browser.ini b/devtools/client/inspector/test/browser.ini
index 65ad71c0c..499df25f0 100644
--- a/devtools/client/inspector/test/browser.ini
+++ b/devtools/client/inspector/test/browser.ini
@@ -113,6 +113,7 @@ subsuite = clipboard
[browser_inspector_infobar_01.js]
[browser_inspector_infobar_02.js]
[browser_inspector_infobar_03.js]
+[browser_inspector_infobar_04.js]
[browser_inspector_infobar_textnode.js]
[browser_inspector_initialization.js]
skip-if = (e10s && debug) # Bug 1250058 - Docshell leak on debug e10s
diff --git a/devtools/client/inspector/test/browser_inspector_infobar_04.js b/devtools/client/inspector/test/browser_inspector_infobar_04.js
new file mode 100644
index 000000000..f1b9eca49
--- /dev/null
+++ b/devtools/client/inspector/test/browser_inspector_infobar_04.js
@@ -0,0 +1,38 @@
+/* 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";
+
+// Check the position and text content of the highlighter nodeinfo bar under page zoom.
+
+const TEST_URI = URL_ROOT + "doc_inspector_infobar_01.html";
+
+add_task(function* () {
+ let {inspector, testActor} = yield openInspectorForURL(TEST_URI);
+ let testData = {
+ selector: "#top",
+ dims: "500" + " \u00D7 " + "100"
+ };
+
+ yield testInfobar(testData, inspector, testActor);
+ info("Change zoom page to level 2.");
+ yield testActor.zoomPageTo(2);
+ info("Testing again the infobar after zoom.");
+ yield testInfobar(testData, inspector, testActor);
+});
+
+function* testInfobar(test, inspector, testActor) {
+ info(`Testing ${test.selector}`);
+
+ yield selectAndHighlightNode(test.selector, inspector);
+
+ // Ensure the node is the correct one.
+ let id = yield testActor.getHighlighterNodeTextContent(
+ "box-model-infobar-id");
+ is(id, test.selector, `Node ${test.selector} selected.`);
+
+ let dims = yield testActor.getHighlighterNodeTextContent(
+ "box-model-infobar-dimensions");
+ is(dims, test.dims, "Node's infobar displays the right dimensions.");
+}
diff --git a/devtools/client/locales/en-US/netmonitor.properties b/devtools/client/locales/en-US/netmonitor.properties
index e6118ca9f..021b56a2b 100644
--- a/devtools/client/locales/en-US/netmonitor.properties
+++ b/devtools/client/locales/en-US/netmonitor.properties
@@ -143,12 +143,12 @@ networkMenu.sortedDesc=Sorted descending
# in the network table footer when there are no requests available.
networkMenu.empty=No requests
-# LOCALIZATION NOTE (networkMenu.summary): Semi-colon list of plural forms.
+# LOCALIZATION NOTE (networkMenu.summary2): Semi-colon list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# This label is displayed in the network table footer providing concise
# information about all requests. Parameters: #1 is the number of requests,
-# #2 is the size, #3 is the number of seconds.
-networkMenu.summary=One request, #2 KB, #3 s;#1 requests, #2 KB, #3 s
+# #2 is the size, #3 is the transferred size, #4 is the number of seconds.
+networkMenu.summary2=One request, #2 KB (transferred: #3 KB), #4 s;#1 requests, #2 KB (transferred: #3 KB), #4 s
# LOCALIZATION NOTE (networkMenu.sizeB): This is the label displayed
# in the network menu specifying the size of a request (in bytes).
@@ -221,10 +221,22 @@ tableChart.unavailable=No data available
# in pie or table charts specifying the size of a request (in kilobytes).
charts.sizeKB=%S KB
+# LOCALIZATION NOTE (charts.transferredSizeKB): This is the label displayed
+# in pie or table charts specifying the size of a transferred request (in kilobytes).
+charts.transferredSizeKB=%S KB
+
# LOCALIZATION NOTE (charts.totalS): This is the label displayed
# in pie or table charts specifying the time for a request to finish (in seconds).
charts.totalS=%S s
+# LOCALIZATION NOTE (charts.totalSize): This is the label displayed
+# in the performance analysis view for total requests size, in kilobytes.
+charts.totalSize=Size: %S KB
+
+# LOCALIZATION NOTE (charts.totalTranferredSize): This is the label displayed
+# in the performance analysis view for total transferred size, in kilobytes.
+charts.totalTransferredSize=Transferred Size: %S KB
+
# LOCALIZATION NOTE (charts.cacheEnabled): This is the label displayed
# in the performance analysis view for "cache enabled" charts.
charts.cacheEnabled=Primed cache
@@ -233,10 +245,6 @@ charts.cacheEnabled=Primed cache
# in the performance analysis view for "cache disabled" charts.
charts.cacheDisabled=Empty cache
-# LOCALIZATION NOTE (charts.totalSize): This is the label displayed
-# in the performance analysis view for total requests size, in kilobytes.
-charts.totalSize=Size: %S KB
-
# LOCALIZATION NOTE (charts.totalSeconds): Semi-colon list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# This is the label displayed in the performance analysis view for the
@@ -251,6 +259,23 @@ charts.totalCached=Cached responses: %S
# in the performance analysis view for total requests.
charts.totalCount=Total requests: %S
+# LOCALIZATION NOTE (charts.size): This is the label displayed
+# in the header column in the performance analysis view for size of the request.
+charts.size=Size
+
+# LOCALIZATION NOTE (charts.type): This is the label displayed
+# in the header column in the performance analysis view for type of request.
+charts.type=Type
+
+# LOCALIZATION NOTE (charts.transferred): This is the label displayed
+# in the header column in the performance analysis view for transferred
+# size of the request.
+charts.transferred=Transferred
+
+# LOCALIZATION NOTE (charts.time): This is the label displayed
+# in the header column in the performance analysis view for time of request.
+charts.time=Time
+
# LOCALIZATION NOTE (netRequest.headers): A label used for Headers tab
# This tab displays list of HTTP headers
netRequest.headers=Headers
@@ -556,6 +581,11 @@ netmonitor.timings.blocked=Blocked:
# in a "dns" state.
netmonitor.timings.dns=DNS resolution:
+# LOCALIZATION NOTE (netmonitor.timings.ssl): This is the label displayed
+# in the network details timings tab identifying the amount of time spent
+# in a "tls" handshake state.
+netmonitor.timings.ssl=TLS setup:
+
# LOCALIZATION NOTE (netmonitor.timings.connect): This is the label displayed
# in the network details timings tab identifying the amount of time spent
# in a "connect" state.
@@ -592,6 +622,37 @@ netmonitor.security.protocolVersion=Protocol version:
# in the security tab describing the cipher suite used to secure this connection.
netmonitor.security.cipherSuite=Cipher suite:
+# LOCALIZATION NOTE (netmonitor.security.keaGroup): This is the label displayed
+# in the security tab describing the key exchange group suite used to secure
+# this connection.
+netmonitor.security.keaGroup=Key Exchange Group:
+
+# LOCALIZATION NOTE (netmonitor.security.keaGroup.none): This is the label
+# displayed in the security tab describing the case when no group was used.
+netmonitor.security.keaGroup.none=none
+
+# LOCALIZATION NOTE (netmonitor.security.keaGroup.custom): This is the label
+# displayed in the security tab describing the case when a custom group was used.
+netmonitor.security.keaGroup.custom=custom
+
+# LOCALIZATION NOTE (netmonitor.security.keaGroup.unknown): This is the value
+# displayed in the security tab describing an unknown group.
+netmonitor.security.keaGroup.unknown=unknown group
+
+# LOCALIZATION NOTE (netmonitor.security.signatureScheme): This is the label
+# displayed in the security tab describing the signature scheme used by for
+# the server certificate in this connection.
+netmonitor.security.signatureScheme=Signature Scheme:
+
+# LOCALIZATION NOTE (netmonitor.security.signatureScheme.none): This is the
+# label displayed in the security tab describing the case when no signature
+# was used.
+netmonitor.security.signatureScheme.none=none
+
+# LOCALIZATION NOTE (netmonitor.security.signatureScheme.unknown): This is the
+# value displayed in the security tab describing an unknown signature scheme.
+netmonitor.security.signatureScheme.unknown=unknown signature scheme
+
# LOCALIZATION NOTE (netmonitor.security.hsts): This is the label displayed
# in the security tab describing the usage of HTTP Strict Transport Security.
netmonitor.security.hsts=HTTP Strict Transport Security:
diff --git a/devtools/client/menus.js b/devtools/client/menus.js
index 1aee85095..7e36839da 100644
--- a/devtools/client/menus.js
+++ b/devtools/client/menus.js
@@ -120,7 +120,7 @@ exports.menuitems = [
{ id: "menu_browserConsole",
l10nKey: "browserConsoleCmd",
oncommand() {
- let HUDService = require("devtools/client/webconsole/hudservice");
+ let {HUDService} = require("devtools/client/webconsole/hudservice");
HUDService.openBrowserConsoleOrFocus();
},
key: {
diff --git a/devtools/client/netmonitor/actions/index.js b/devtools/client/netmonitor/actions/index.js
index 3f7b0bd2f..2ce23448e 100644
--- a/devtools/client/netmonitor/actions/index.js
+++ b/devtools/client/netmonitor/actions/index.js
@@ -4,6 +4,7 @@
"use strict";
const filters = require("./filters");
-const sidebar = require("./sidebar");
+const requests = require("./requests");
+const ui = require("./ui");
-module.exports = Object.assign({}, filters, sidebar);
+module.exports = Object.assign({}, filters, requests, ui);
diff --git a/devtools/client/netmonitor/actions/moz.build b/devtools/client/netmonitor/actions/moz.build
index 477cafb41..d0ac61944 100644
--- a/devtools/client/netmonitor/actions/moz.build
+++ b/devtools/client/netmonitor/actions/moz.build
@@ -6,5 +6,6 @@
DevToolsModules(
'filters.js',
'index.js',
- 'sidebar.js',
+ 'requests.js',
+ 'ui.js',
)
diff --git a/devtools/client/netmonitor/actions/requests.js b/devtools/client/netmonitor/actions/requests.js
new file mode 100644
index 000000000..ae794a437
--- /dev/null
+++ b/devtools/client/netmonitor/actions/requests.js
@@ -0,0 +1,25 @@
+/* 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 {
+ UPDATE_REQUESTS,
+} = require("../constants");
+
+/**
+ * Update request items
+ *
+ * @param {array} requests - visible request items
+ */
+function updateRequests(items) {
+ return {
+ type: UPDATE_REQUESTS,
+ items,
+ };
+}
+
+module.exports = {
+ updateRequests,
+};
diff --git a/devtools/client/netmonitor/actions/sidebar.js b/devtools/client/netmonitor/actions/sidebar.js
deleted file mode 100644
index 7e8dca5c1..000000000
--- a/devtools/client/netmonitor/actions/sidebar.js
+++ /dev/null
@@ -1,49 +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 {
- DISABLE_TOGGLE_BUTTON,
- SHOW_SIDEBAR,
- TOGGLE_SIDEBAR,
-} = require("../constants");
-
-/**
- * Change ToggleButton disabled state.
- *
- * @param {boolean} disabled - expected button disabled state
- */
-function disableToggleButton(disabled) {
- return {
- type: DISABLE_TOGGLE_BUTTON,
- disabled: disabled,
- };
-}
-
-/**
- * Change sidebar visible state.
- *
- * @param {boolean} visible - expected sidebar visible state
- */
-function showSidebar(visible) {
- return {
- type: SHOW_SIDEBAR,
- visible: visible,
- };
-}
-
-/**
- * Toggle to show/hide sidebar.
- */
-function toggleSidebar() {
- return {
- type: TOGGLE_SIDEBAR,
- };
-}
-
-module.exports = {
- disableToggleButton,
- showSidebar,
- toggleSidebar,
-};
diff --git a/devtools/client/netmonitor/actions/ui.js b/devtools/client/netmonitor/actions/ui.js
new file mode 100644
index 000000000..c6df307ed
--- /dev/null
+++ b/devtools/client/netmonitor/actions/ui.js
@@ -0,0 +1,36 @@
++/* 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 {
+ OPEN_SIDEBAR,
+ TOGGLE_SIDEBAR,
+} = require("../constants");
+
+/**
+ * Change sidebar open state.
+ *
+ * @param {boolean} open - open state
+ */
+function openSidebar(open) {
+ return {
+ type: OPEN_SIDEBAR,
+ open,
+ };
+}
+
+/**
+ * Toggle sidebar open state.
+ */
+function toggleSidebar() {
+ return {
+ type: TOGGLE_SIDEBAR,
+ };
+}
+
+module.exports = {
+ openSidebar,
+ toggleSidebar,
+};
diff --git a/devtools/client/netmonitor/components/moz.build b/devtools/client/netmonitor/components/moz.build
index 47ef7f026..42459584d 100644
--- a/devtools/client/netmonitor/components/moz.build
+++ b/devtools/client/netmonitor/components/moz.build
@@ -6,5 +6,6 @@
DevToolsModules(
'filter-buttons.js',
'search-box.js',
+ 'summary-button.js',
'toggle-button.js',
)
diff --git a/devtools/client/netmonitor/components/summary-button.js b/devtools/client/netmonitor/components/summary-button.js
new file mode 100644
index 000000000..223552fbf
--- /dev/null
+++ b/devtools/client/netmonitor/components/summary-button.js
@@ -0,0 +1,61 @@
+/* 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/. */
+
+/* globals NetMonitorView */
+
+"use strict";
+
+const {
+ CONTENT_SIZE_DECIMALS,
+ REQUEST_TIME_DECIMALS,
+} = require("../constants");
+const { DOM, PropTypes } = require("devtools/client/shared/vendor/react");
+const { connect } = require("devtools/client/shared/vendor/react-redux");
+const { PluralForm } = require("devtools/shared/plural-form");
+const { L10N } = require("../l10n");
+const {
+ getDisplayedRequestsSummary
+} = require("../selectors/index");
+
+const { button, span } = DOM;
+
+function SummaryButton({
+ summary,
+ triggerSummary
+}) {
+ let { count, contentSize, transferredSize, millis } = summary;
+ const text = (count === 0) ? L10N.getStr("networkMenu.empty") :
+ PluralForm.get(count, L10N.getStr("networkMenu.summary2"))
+ .replace("#1", count)
+ .replace("#2", L10N.numberWithDecimals(contentSize / 1024,
+ CONTENT_SIZE_DECIMALS))
+ .replace("#3", L10N.numberWithDecimals(transferredSize / 1024,
+ CONTENT_SIZE_DECIMALS))
+ .replace("#4", L10N.numberWithDecimals(millis / 1000,
+ REQUEST_TIME_DECIMALS));
+
+ return button({
+ id: "requests-menu-network-summary-button",
+ className: "devtools-button",
+ title: count ? text : L10N.getStr("netmonitor.toolbar.perf"),
+ onClick: triggerSummary,
+ },
+ span({ className: "summary-info-icon" }),
+ span({ className: "summary-info-text" }, text));
+}
+
+SummaryButton.propTypes = {
+ summary: PropTypes.object.isRequired,
+};
+
+module.exports = connect(
+ (state) => ({
+ summary: getDisplayedRequestsSummary(state),
+ }),
+ (dispatch) => ({
+ triggerSummary: () => {
+ NetMonitorView.toggleFrontendMode();
+ },
+ })
+)(SummaryButton);
diff --git a/devtools/client/netmonitor/components/toggle-button.js b/devtools/client/netmonitor/components/toggle-button.js
index db546c55d..c9a59a76b 100644
--- a/devtools/client/netmonitor/components/toggle-button.js
+++ b/devtools/client/netmonitor/components/toggle-button.js
@@ -1,5 +1,3 @@
-/* -*- 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/. */
@@ -11,47 +9,43 @@ const { connect } = require("devtools/client/shared/vendor/react-redux");
const { L10N } = require("../l10n");
const Actions = require("../actions/index");
-// Shortcuts
const { button } = DOM;
-/**
- * Button used to toggle sidebar
- */
function ToggleButton({
disabled,
- onToggle,
- visible,
+ open,
+ triggerSidebar,
}) {
let className = ["devtools-button"];
- if (!visible) {
+ if (!open) {
className.push("pane-collapsed");
}
- let titleMsg = visible ? L10N.getStr("collapseDetailsPane") :
- L10N.getStr("expandDetailsPane");
+
+ const title = open ? L10N.getStr("collapseDetailsPane") :
+ L10N.getStr("expandDetailsPane");
return button({
id: "details-pane-toggle",
className: className.join(" "),
- title: titleMsg,
- disabled: disabled,
+ title,
+ disabled,
tabIndex: "0",
- onMouseDown: onToggle,
+ onMouseDown: triggerSidebar,
});
}
ToggleButton.propTypes = {
disabled: PropTypes.bool.isRequired,
- onToggle: PropTypes.func.isRequired,
- visible: PropTypes.bool.isRequired,
+ triggerSidebar: PropTypes.func.isRequired,
};
module.exports = connect(
(state) => ({
- disabled: state.sidebar.toggleButtonDisabled,
- visible: state.sidebar.visible,
+ disabled: state.requests.items.length === 0,
+ open: state.ui.sidebar.open,
}),
(dispatch) => ({
- onToggle: () => {
+ triggerSidebar: () => {
dispatch(Actions.toggleSidebar());
let requestsMenu = NetMonitorView.RequestsMenu;
diff --git a/devtools/client/netmonitor/constants.js b/devtools/client/netmonitor/constants.js
index a540d74b2..33bc71ea7 100644
--- a/devtools/client/netmonitor/constants.js
+++ b/devtools/client/netmonitor/constants.js
@@ -5,15 +5,17 @@
const general = {
FREETEXT_FILTER_SEARCH_DELAY: 200,
+ CONTENT_SIZE_DECIMALS: 2,
+ REQUEST_TIME_DECIMALS: 2,
};
const actionTypes = {
TOGGLE_FILTER_TYPE: "TOGGLE_FILTER_TYPE",
ENABLE_FILTER_TYPE_ONLY: "ENABLE_FILTER_TYPE_ONLY",
- TOGGLE_SIDEBAR: "TOGGLE_SIDEBAR",
- SHOW_SIDEBAR: "SHOW_SIDEBAR",
- DISABLE_TOGGLE_BUTTON: "DISABLE_TOGGLE_BUTTON",
SET_FILTER_TEXT: "SET_FILTER_TEXT",
+ OPEN_SIDEBAR: "OPEN_SIDEBAR",
+ TOGGLE_SIDEBAR: "TOGGLE_SIDEBAR",
+ UPDATE_REQUESTS: "UPDATE_REQUESTS",
};
module.exports = Object.assign({}, general, actionTypes);
diff --git a/devtools/client/netmonitor/moz.build b/devtools/client/netmonitor/moz.build
index 4b34b093b..2c9b32d3f 100644
--- a/devtools/client/netmonitor/moz.build
+++ b/devtools/client/netmonitor/moz.build
@@ -17,6 +17,7 @@ DevToolsModules(
'events.js',
'filter-predicates.js',
'l10n.js',
+ 'open-request-in-tab.js',
'panel.js',
'performance-statistics-view.js',
'prefs.js',
diff --git a/devtools/client/netmonitor/netmonitor-view.js b/devtools/client/netmonitor/netmonitor-view.js
index 68470f7a9..414b9ab8f 100644
--- a/devtools/client/netmonitor/netmonitor-view.js
+++ b/devtools/client/netmonitor/netmonitor-view.js
@@ -125,7 +125,6 @@ var NetMonitorView = {
if (!Prefs.statistics) {
$("#request-menu-context-perf").hidden = true;
$("#notice-perf-message").hidden = true;
- $("#requests-menu-network-summary-button").hidden = true;
}
},
@@ -171,10 +170,10 @@ var NetMonitorView = {
if (flags.visible) {
this._body.classList.remove("pane-collapsed");
- gStore.dispatch(Actions.showSidebar(true));
+ gStore.dispatch(Actions.openSidebar(true));
} else {
this._body.classList.add("pane-collapsed");
- gStore.dispatch(Actions.showSidebar(false));
+ gStore.dispatch(Actions.openSidebar(false));
}
if (tabIndex !== undefined) {
@@ -234,7 +233,7 @@ var NetMonitorView = {
// populating the statistics view.
// • The response mime type is used for categorization.
yield whenDataAvailable(requestsView, [
- "responseHeaders", "status", "contentSize", "mimeType", "totalTime"
+ "responseHeaders", "status", "contentSize", "transferredSize", "mimeType", "totalTime"
]);
} catch (ex) {
// Timed out while waiting for data. Continue with what we have.
@@ -964,7 +963,7 @@ NetworkDetailsView.prototype = {
if (!response) {
return;
}
- let { blocked, dns, connect, send, wait, receive } = response.timings;
+ let { blocked, dns, connect, ssl, send, wait, receive } = response.timings;
let tabboxWidth = $("#details-pane").getAttribute("width");
@@ -989,6 +988,11 @@ NetworkDetailsView.prototype = {
$("#timings-summary-connect .requests-menu-timings-total")
.setAttribute("value", L10N.getFormatStr("networkMenu.totalMS", connect));
+ $("#timings-summary-ssl .requests-menu-timings-box")
+ .setAttribute("width", ssl * scale);
+ $("#timings-summary-ssl .requests-menu-timings-total")
+ .setAttribute("value", L10N.getFormatStr("networkMenu.totalMS", ssl));
+
$("#timings-summary-send .requests-menu-timings-box")
.setAttribute("width", send * scale);
$("#timings-summary-send .requests-menu-timings-total")
@@ -1008,6 +1012,8 @@ NetworkDetailsView.prototype = {
.style.transform = "translateX(" + (scale * blocked) + "px)";
$("#timings-summary-connect .requests-menu-timings-box")
.style.transform = "translateX(" + (scale * (blocked + dns)) + "px)";
+ $("#timings-summary-ssl .requests-menu-timings-box")
+ .style.transform = "translateX(" + (scale * blocked) + "px)";
$("#timings-summary-send .requests-menu-timings-box")
.style.transform =
"translateX(" + (scale * (blocked + dns + connect)) + "px)";
@@ -1023,6 +1029,8 @@ NetworkDetailsView.prototype = {
.style.transform = "translateX(" + (scale * blocked) + "px)";
$("#timings-summary-connect .requests-menu-timings-total")
.style.transform = "translateX(" + (scale * (blocked + dns)) + "px)";
+ $("#timings-summary-ssl .requests-menu-timings-total")
+ .style.transform = "translateX(" + (scale * blocked) + "px)";
$("#timings-summary-send .requests-menu-timings-total")
.style.transform =
"translateX(" + (scale * (blocked + dns + connect)) + "px)";
@@ -1125,6 +1133,8 @@ NetworkDetailsView.prototype = {
setValue("#security-protocol-version-value",
securityInfo.protocolVersion);
setValue("#security-ciphersuite-value", securityInfo.cipherSuite);
+ setValue("#security-keagroup-value", securityInfo.keaGroupName);
+ setValue("#security-signaturescheme-value", securityInfo.signatureSchemeName);
// Host header
let domain = getUriHostPort(url);
diff --git a/devtools/client/netmonitor/netmonitor.xul b/devtools/client/netmonitor/netmonitor.xul
index bb580f7ad..aa5c4d848 100644
--- a/devtools/client/netmonitor/netmonitor.xul
+++ b/devtools/client/netmonitor/netmonitor.xul
@@ -28,9 +28,8 @@
id="react-filter-buttons-hook"/>
<spacer id="requests-menu-spacer"
flex="1"/>
- <toolbarbutton id="requests-menu-network-summary-button"
- class="devtools-toolbarbutton icon-and-text"
- data-localization="tooltiptext=netmonitor.toolbar.perf"/>
+ <html:div xmlns="http://www.w3.org/1999/xhtml"
+ id="react-summary-button-hook"/>
<html:div xmlns="http://www.w3.org/1999/xhtml"
id="react-search-box-hook"/>
<html:div xmlns="http://www.w3.org/1999/xhtml"
@@ -479,6 +478,14 @@
<hbox class="requests-menu-timings-box connect"/>
<label class="plain requests-menu-timings-total"/>
</hbox>
+ <hbox id="timings-summary-ssl"
+ class="tabpanel-summary-container"
+ align="center">
+ <label class="plain tabpanel-summary-label"
+ data-localization="content=netmonitor.timings.ssl"/>
+ <hbox class="requests-menu-timings-box ssl"/>
+ <label class="plain requests-menu-timings-total"/>
+ </hbox>
<hbox id="timings-summary-send"
class="tabpanel-summary-container"
align="center">
@@ -551,6 +558,26 @@
id="security-warning-cipher"
data-localization="tooltiptext=netmonitor.security.warning.cipher" />
</hbox>
+ <hbox id="security-keagroup"
+ class="tabpanel-summary-container"
+ align="baseline">
+ <label class="plain tabpanel-summary-label"
+ data-localization="content=netmonitor.security.keaGroup"/>
+ <textbox id="security-keagroup-value"
+ class="plain tabpanel-summary-value devtools-monospace cropped-textbox"
+ flex="1"
+ readonly="true"/>
+ </hbox>
+ <hbox id="security-signaturescheme"
+ class="tabpanel-summary-container"
+ align="baseline">
+ <label class="plain tabpanel-summary-label"
+ data-localization="content=netmonitor.security.signatureScheme"/>
+ <textbox id="security-signaturescheme-value"
+ class="plain tabpanel-summary-value devtools-monospace cropped-textbox"
+ flex="1"
+ readonly="true"/>
+ </hbox>
</vbox>
</vbox>
<vbox id="security-info-domain"
diff --git a/devtools/client/netmonitor/open-request-in-tab.js b/devtools/client/netmonitor/open-request-in-tab.js
new file mode 100644
index 000000000..aeb35dad0
--- /dev/null
+++ b/devtools/client/netmonitor/open-request-in-tab.js
@@ -0,0 +1,40 @@
+/* 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/. */
+/* eslint-disable mozilla/reject-some-requires */
+
+"use strict";
+
+let { Cc, Ci } = require("chrome");
+const Services = require("Services");
+const { gDevTools } = require("devtools/client/framework/devtools");
+
+/**
+ * Opens given request in a new tab.
+ */
+function openRequestInTab(request) {
+ let win = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
+ let rawData = request.requestPostData ? request.requestPostData.postData : null;
+ let postData;
+
+ if (rawData && rawData.text) {
+ let stringStream = getInputStreamFromString(rawData.text);
+ postData = Cc["@mozilla.org/network/mime-input-stream;1"]
+ .createInstance(Ci.nsIMIMEInputStream);
+ postData.addHeader("Content-Type", "application/x-www-form-urlencoded");
+ postData.setData(stringStream);
+ }
+
+ win.gBrowser.selectedTab = win.gBrowser.addTab(request.url, null, null, postData);
+}
+
+function getInputStreamFromString(data) {
+ let stringStream = Cc["@mozilla.org/io/string-input-stream;1"]
+ .createInstance(Ci.nsIStringInputStream);
+ stringStream.data = data;
+ return stringStream;
+}
+
+module.exports = {
+ openRequestInTab,
+};
diff --git a/devtools/client/netmonitor/performance-statistics-view.js b/devtools/client/netmonitor/performance-statistics-view.js
index c712c083d..38b98fb68 100644
--- a/devtools/client/netmonitor/performance-statistics-view.js
+++ b/devtools/client/netmonitor/performance-statistics-view.js
@@ -92,27 +92,35 @@ PerformanceStatisticsView.prototype = {
let string = L10N.numberWithDecimals(value / 1024, CONTENT_SIZE_DECIMALS);
return L10N.getFormatStr("charts.sizeKB", string);
},
+ transferredSize: value => {
+ let string = L10N.numberWithDecimals(value / 1024, CONTENT_SIZE_DECIMALS);
+ return L10N.getFormatStr("charts.transferredSizeKB", string);
+ },
time: value => {
let string = L10N.numberWithDecimals(value / 1000, REQUEST_TIME_DECIMALS);
return L10N.getFormatStr("charts.totalS", string);
}
},
_commonChartTotals: {
+ cached: total => {
+ return L10N.getFormatStr("charts.totalCached", total);
+ },
+ count: total => {
+ return L10N.getFormatStr("charts.totalCount", total);
+ },
size: total => {
let string = L10N.numberWithDecimals(total / 1024, CONTENT_SIZE_DECIMALS);
return L10N.getFormatStr("charts.totalSize", string);
},
+ transferredSize: total => {
+ let string = L10N.numberWithDecimals(total / 1024, CONTENT_SIZE_DECIMALS);
+ return L10N.getFormatStr("charts.totalTransferredSize", string);
+ },
time: total => {
let seconds = total / 1000;
let string = L10N.numberWithDecimals(seconds, REQUEST_TIME_DECIMALS);
return PluralForm.get(seconds,
L10N.getStr("charts.totalSeconds")).replace("#1", string);
- },
- cached: total => {
- return L10N.getFormatStr("charts.totalCached", total);
- },
- count: total => {
- return L10N.getFormatStr("charts.totalCount", total);
}
},
@@ -136,6 +144,14 @@ PerformanceStatisticsView.prototype = {
let chart = Chart.PieTable(document, {
diameter: NETWORK_ANALYSIS_PIE_CHART_DIAMETER,
title: L10N.getStr(title),
+ header: {
+ cached: "",
+ count: "",
+ label: L10N.getStr("charts.type"),
+ size: L10N.getStr("charts.size"),
+ transferredSize: L10N.getStr("charts.transferred"),
+ time: L10N.getStr("charts.time")
+ },
data: data,
strings: strings,
totals: totals,
@@ -161,13 +177,14 @@ PerformanceStatisticsView.prototype = {
* True if the cache is considered enabled, false for disabled.
*/
_sanitizeChartDataSource: function (items, emptyCache) {
- let data = [
+ const data = [
"html", "css", "js", "xhr", "fonts", "images", "media", "flash", "ws", "other"
- ].map(e => ({
+ ].map((type) => ({
cached: 0,
count: 0,
- label: e,
+ label: type,
size: 0,
+ transferredSize: 0,
time: 0
}));
@@ -211,6 +228,7 @@ PerformanceStatisticsView.prototype = {
if (emptyCache || !responseIsFresh(details)) {
data[type].time += details.totalTime || 0;
data[type].size += details.contentSize || 0;
+ data[type].transferredSize += details.transferredSize || 0;
} else {
data[type].cached++;
}
diff --git a/devtools/client/netmonitor/reducers/index.js b/devtools/client/netmonitor/reducers/index.js
index 58638a030..f36b1d91f 100644
--- a/devtools/client/netmonitor/reducers/index.js
+++ b/devtools/client/netmonitor/reducers/index.js
@@ -5,9 +5,11 @@
const { combineReducers } = require("devtools/client/shared/vendor/redux");
const filters = require("./filters");
-const sidebar = require("./sidebar");
+const requests = require("./requests");
+const ui = require("./ui");
module.exports = combineReducers({
filters,
- sidebar,
+ requests,
+ ui,
});
diff --git a/devtools/client/netmonitor/reducers/moz.build b/devtools/client/netmonitor/reducers/moz.build
index 477cafb41..d0ac61944 100644
--- a/devtools/client/netmonitor/reducers/moz.build
+++ b/devtools/client/netmonitor/reducers/moz.build
@@ -6,5 +6,6 @@
DevToolsModules(
'filters.js',
'index.js',
- 'sidebar.js',
+ 'requests.js',
+ 'ui.js',
)
diff --git a/devtools/client/netmonitor/reducers/requests.js b/devtools/client/netmonitor/reducers/requests.js
new file mode 100644
index 000000000..9ba888cad
--- /dev/null
+++ b/devtools/client/netmonitor/reducers/requests.js
@@ -0,0 +1,28 @@
+/* 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 I = require("devtools/client/shared/vendor/immutable");
+const {
+ UPDATE_REQUESTS,
+} = require("../constants");
+
+const Requests = I.Record({
+ items: [],
+});
+
+function updateRequests(state, action) {
+ return state.set("items", action.items || state.items);
+}
+
+function requests(state = new Requests(), action) {
+ switch (action.type) {
+ case UPDATE_REQUESTS:
+ return updateRequests(state, action);
+ default:
+ return state;
+ }
+}
+
+module.exports = requests;
diff --git a/devtools/client/netmonitor/reducers/sidebar.js b/devtools/client/netmonitor/reducers/sidebar.js
deleted file mode 100644
index eaa8b63df..000000000
--- a/devtools/client/netmonitor/reducers/sidebar.js
+++ /dev/null
@@ -1,43 +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 I = require("devtools/client/shared/vendor/immutable");
-const {
- DISABLE_TOGGLE_BUTTON,
- SHOW_SIDEBAR,
- TOGGLE_SIDEBAR,
-} = require("../constants");
-
-const SidebarState = I.Record({
- toggleButtonDisabled: true,
- visible: false,
-});
-
-function disableToggleButton(state, action) {
- return state.set("toggleButtonDisabled", action.disabled);
-}
-
-function showSidebar(state, action) {
- return state.set("visible", action.visible);
-}
-
-function toggleSidebar(state, action) {
- return state.set("visible", !state.visible);
-}
-
-function sidebar(state = new SidebarState(), action) {
- switch (action.type) {
- case DISABLE_TOGGLE_BUTTON:
- return disableToggleButton(state, action);
- case SHOW_SIDEBAR:
- return showSidebar(state, action);
- case TOGGLE_SIDEBAR:
- return toggleSidebar(state, action);
- default:
- return state;
- }
-}
-
-module.exports = sidebar;
diff --git a/devtools/client/netmonitor/reducers/ui.js b/devtools/client/netmonitor/reducers/ui.js
new file mode 100644
index 000000000..033b944f3
--- /dev/null
+++ b/devtools/client/netmonitor/reducers/ui.js
@@ -0,0 +1,40 @@
+/* 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 I = require("devtools/client/shared/vendor/immutable");
+const {
+ OPEN_SIDEBAR,
+ TOGGLE_SIDEBAR,
+} = require("../constants");
+
+const Sidebar = I.Record({
+ open: false,
+});
+
+const UI = I.Record({
+ sidebar: new Sidebar(),
+});
+
+function openSidebar(state, action) {
+ return state.setIn(["sidebar", "open"], action.open);
+}
+
+function toggleSidebar(state, action) {
+ return state.setIn(["sidebar", "open"], !state.sidebar.open);
+}
+
+function ui(state = new UI(), action) {
+ switch (action.type) {
+ case OPEN_SIDEBAR:
+ return openSidebar(state, action);
+ case TOGGLE_SIDEBAR:
+ return toggleSidebar(state, action);
+ default:
+ return state;
+ }
+}
+
+module.exports = ui;
diff --git a/devtools/client/netmonitor/request-list-context-menu.js b/devtools/client/netmonitor/request-list-context-menu.js
index 215296265..331a7bde3 100644
--- a/devtools/client/netmonitor/request-list-context-menu.js
+++ b/devtools/client/netmonitor/request-list-context-menu.js
@@ -10,6 +10,7 @@ const Services = require("Services");
const { Task } = require("devtools/shared/task");
const { Curl } = require("devtools/client/shared/curl");
const { gDevTools } = require("devtools/client/framework/devtools");
+const { openRequestInTab } = require("devtools/client/netmonitor/open-request-in-tab");
const Menu = require("devtools/client/framework/menu");
const MenuItem = require("devtools/client/framework/menu-item");
const { L10N } = require("./l10n");
@@ -186,8 +187,7 @@ RequestListContextMenu.prototype = {
*/
openRequestInTab() {
let win = Services.wm.getMostRecentWindow(gDevTools.chromeWindowType);
- let { url } = this.selectedItem.attachment;
- win.openUILinkIn(url, "tab", { relatedToCurrent: true });
+ openRequestInTab(this.selectedItem.attachment);
},
/**
diff --git a/devtools/client/netmonitor/request-utils.js b/devtools/client/netmonitor/request-utils.js
index ba54efb4f..647d71e7c 100644
--- a/devtools/client/netmonitor/request-utils.js
+++ b/devtools/client/netmonitor/request-utils.js
@@ -92,7 +92,7 @@ exports.getFormDataSections = Task.async(function* (headers, uploadHeaders, post
exports.formDataURI = function (mimeType, encoding, text) {
if (!encoding) {
encoding = "base64";
- text = btoa(text);
+ text = btoa(unescape(encodeURIComponent(text)));
}
return "data:" + mimeType + ";" + encoding + "," + text;
};
diff --git a/devtools/client/netmonitor/requests-menu-view.js b/devtools/client/netmonitor/requests-menu-view.js
index 6ea6381ec..4ee307145 100644
--- a/devtools/client/netmonitor/requests-menu-view.js
+++ b/devtools/client/netmonitor/requests-menu-view.js
@@ -19,7 +19,6 @@ const {setImageTooltip, getImageDimensions} =
const {Heritage, WidgetMethods, setNamedTimeout} =
require("devtools/client/shared/widgets/view-helpers");
const {CurlUtils} = require("devtools/client/shared/curl");
-const {PluralForm} = require("devtools/shared/plural-form");
const {Filters, isFreetextMatch} = require("./filter-predicates");
const {Sorters} = require("./sort-predicates");
const {L10N, WEBCONSOLE_L10N} = require("./l10n");
@@ -90,10 +89,11 @@ function storeWatcher(initialValue, reduceValue, onChange) {
let currentValue = initialValue;
return () => {
+ const oldValue = currentValue;
const newValue = reduceValue(currentValue);
- if (newValue !== currentValue) {
- onChange(newValue, currentValue);
+ if (newValue !== oldValue) {
currentValue = newValue;
+ onChange(newValue, oldValue);
}
};
}
@@ -129,8 +129,6 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
let widgetParentEl = $("#requests-menu-contents");
this.widget = new SideMenuWidget(widgetParentEl);
this._splitter = $("#network-inspector-view-splitter");
- this._summary = $("#requests-menu-network-summary-button");
- this._summary.setAttribute("label", L10N.getStr("networkMenu.empty"));
// Create a tooltip for the newly appended network request item.
this.tooltip = new HTMLTooltip(NetMonitorController._toolbox.doc, { type: "arrow" });
@@ -211,13 +209,10 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
if (NetMonitorController.supportsPerfStats) {
$("#requests-menu-perf-notice-button").addEventListener("command",
this._onContextPerfCommand, false);
- $("#requests-menu-network-summary-button").addEventListener("command",
- this._onContextPerfCommand, false);
$("#network-statistics-back-button").addEventListener("command",
this._onContextPerfCommand, false);
} else {
$("#notice-perf-message").hidden = true;
- $("#requests-menu-network-summary-button").hidden = true;
}
if (!NetMonitorController.supportsTransferredResponseSize) {
@@ -257,8 +252,6 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
this._onReloadCommand, false);
$("#requests-menu-perf-notice-button").removeEventListener("command",
this._onContextPerfCommand, false);
- $("#requests-menu-network-summary-button").removeEventListener("command",
- this._onContextPerfCommand, false);
$("#network-statistics-back-button").removeEventListener("command",
this._onContextPerfCommand, false);
@@ -406,9 +399,20 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
if (rawHeadersHidden) {
let selected = this.selectedItem.attachment;
let selectedRequestHeaders = selected.requestHeaders.headers;
- let selectedResponseHeaders = selected.responseHeaders.headers;
+ // display Status-Line above other response headers
+ let selectedStatusLine = selected.httpVersion
+ + " " + selected.status
+ + " " + selected.statusText
+ + "\n";
requestTextarea.value = writeHeaderText(selectedRequestHeaders);
- responseTextare.value = writeHeaderText(selectedResponseHeaders);
+ // sometimes it's empty
+ if (selected.responseHeaders) {
+ let selectedResponseHeaders = selected.responseHeaders.headers;
+ responseTextare.value = selectedStatusLine
+ + writeHeaderText(selectedResponseHeaders);
+ } else {
+ responseTextare.value = selectedStatusLine;
+ }
$("#raw-headers").hidden = false;
} else {
requestTextarea.value = null;
@@ -422,7 +426,7 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
*/
reFilterRequests: function () {
this.filterContents(this._filterPredicate);
- this.refreshSummary();
+ this.updateRequests();
this.refreshZebra();
},
@@ -541,7 +545,7 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
break;
}
- this.refreshSummary();
+ this.updateRequests();
this.refreshZebra();
},
@@ -552,41 +556,17 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
NetMonitorController.NetworkEventsHandler.clearMarkers();
NetMonitorView.Sidebar.toggle(false);
- this.store.dispatch(Actions.disableToggleButton(true));
$("#requests-menu-empty-notice").hidden = false;
this.empty();
- this.refreshSummary();
+ this.updateRequests();
},
/**
- * Refreshes the status displayed in this container's footer, providing
- * concise information about all requests.
+ * Update store request itmes and trigger related UI update
*/
- refreshSummary: function () {
- let visibleItems = this.visibleItems;
- let visibleRequestsCount = visibleItems.length;
- if (!visibleRequestsCount) {
- this._summary.setAttribute("label", L10N.getStr("networkMenu.empty"));
- return;
- }
-
- let totalBytes = this._getTotalBytesOfRequests(visibleItems);
- let totalMillis =
- this._getNewestRequest(visibleItems).attachment.endedMillis -
- this._getOldestRequest(visibleItems).attachment.startedMillis;
-
- // https://developer.mozilla.org/en-US/docs/Localization_and_Plurals
- let str = PluralForm.get(visibleRequestsCount,
- L10N.getStr("networkMenu.summary"));
-
- this._summary.setAttribute("label", str
- .replace("#1", visibleRequestsCount)
- .replace("#2", L10N.numberWithDecimals((totalBytes || 0) / 1024,
- CONTENT_SIZE_DECIMALS))
- .replace("#3", L10N.numberWithDecimals((totalMillis || 0) / 1000,
- REQUEST_TIME_DECIMALS))
- );
+ updateRequests: function () {
+ this.store.dispatch(Actions.updateRequests(this.visibleItems));
},
/**
@@ -865,7 +845,6 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
this._updateQueue = [];
this._addQueue = [];
- this.store.dispatch(Actions.disableToggleButton(!this.itemCount));
$("#requests-menu-empty-notice").hidden = !!this.itemCount;
// Make sure all the requests are sorted and filtered.
@@ -875,7 +854,7 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
// so this doesn't happen once per network event update).
this.sortContents();
this.filterContents();
- this.refreshSummary();
+ this.updateRequests();
this.refreshZebra();
// Rescale all the waterfalls so that everything is visible at once.
@@ -1131,7 +1110,7 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
*/
_createWaterfallView: function (item, timings, fromCache) {
let { target } = item;
- let sections = ["blocked", "dns", "connect", "send", "wait", "receive"];
+ let sections = ["blocked", "dns", "connect", "ssl", "send", "wait", "receive"];
// Skipping "blocked" because it doesn't work yet.
let timingsNode = $(".requests-menu-timings", target);
@@ -1559,59 +1538,6 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
},
/**
- * Gets the total number of bytes representing the cumulated content size of
- * a set of requests. Returns 0 for an empty set.
- *
- * @param array itemsArray
- * @return number
- */
- _getTotalBytesOfRequests: function (itemsArray) {
- if (!itemsArray.length) {
- return 0;
- }
-
- let result = 0;
- itemsArray.forEach(item => {
- let size = item.attachment.contentSize;
- result += (typeof size == "number") ? size : 0;
- });
-
- return result;
- },
-
- /**
- * Gets the oldest (first performed) request in a set. Returns null for an
- * empty set.
- *
- * @param array itemsArray
- * @return object
- */
- _getOldestRequest: function (itemsArray) {
- if (!itemsArray.length) {
- return null;
- }
- return itemsArray.reduce((prev, curr) =>
- prev.attachment.startedMillis < curr.attachment.startedMillis ?
- prev : curr);
- },
-
- /**
- * Gets the newest (latest performed) request in a set. Returns null for an
- * empty set.
- *
- * @param array itemsArray
- * @return object
- */
- _getNewestRequest: function (itemsArray) {
- if (!itemsArray.length) {
- return null;
- }
- return itemsArray.reduce((prev, curr) =>
- prev.attachment.startedMillis > curr.attachment.startedMillis ?
- prev : curr);
- },
-
- /**
* Gets the available waterfall width in this container.
* @return number
*/
diff --git a/devtools/client/netmonitor/selectors/index.js b/devtools/client/netmonitor/selectors/index.js
index f473149b5..60d6007cd 100644
--- a/devtools/client/netmonitor/selectors/index.js
+++ b/devtools/client/netmonitor/selectors/index.js
@@ -1,8 +1,78 @@
/* 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 { createSelector } = require("devtools/client/shared/vendor/reselect");
+
+/**
+ * Gets the total number of bytes representing the cumulated content size of
+ * a set of requests. Returns 0 for an empty set.
+ *
+ * @param {array} items - an array of request items
+ * @return {number} total bytes of requests
+ */
+function getContentSizeOfRequests(items) {
+ if (!items.length) {
+ return 0;
+ }
+
+ let result = 0;
+ items.forEach((item) => {
+ let size = item.attachment.contentSize;
+ result += (typeof size == "number") ? size : 0;
+ });
+
+ return result;
+}
+
+function getTransferredSizeOfRequests(items) {
+ if (!items.length) {
+ return 0;
+ }
+
+ let result = 0;
+ items.forEach((item) => {
+ let size = item.attachment.transferredSize;
+ result += (typeof size == "number") ? size : 0;
+ });
+
+ return result;
+}
+
+/**
+ * Gets the total milliseconds for all requests. Returns null for an
+ * empty set.
+ *
+ * @param {array} items - an array of request items
+ * @return {object} total milliseconds for all requests
+ */
+function getMillisOfRequests(items) {
+ if (!items.length) {
+ return null;
+ }
+
+ const oldest = items.reduce((prev, curr) =>
+ prev.attachment.startedMillis < curr.attachment.startedMillis ?
+ prev : curr);
+ const newest = items.reduce((prev, curr) =>
+ prev.attachment.startedMillis > curr.attachment.startedMillis ?
+ prev : curr);
+
+ return newest.attachment.endedMillis - oldest.attachment.startedMillis;
+}
+
+const getDisplayedRequestsSummary = createSelector(
+ (state) => state.requests.items,
+ (requests) => ({
+ count: requests.length,
+ contentSize: getContentSizeOfRequests(requests),
+ transferredSize: getTransferredSizeOfRequests(requests),
+ millis: getMillisOfRequests(requests),
+ })
+);
+
module.exports = {
- // selectors...
+ getDisplayedRequestsSummary,
};
diff --git a/devtools/client/netmonitor/test/browser_net_charts-03.js b/devtools/client/netmonitor/test/browser_net_charts-03.js
index c7d9b0c1a..4a655a8ca 100644
--- a/devtools/client/netmonitor/test/browser_net_charts-03.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-03.js
@@ -33,6 +33,10 @@ add_task(function* () {
totals: {
label1: value => "Hello " + L10N.numberWithDecimals(value, 2),
label2: value => "World " + L10N.numberWithDecimals(value, 2)
+ },
+ header: {
+ label1: "label1header",
+ label2: "label2header",
}
});
@@ -51,39 +55,48 @@ add_task(function* () {
is(title.getAttribute("value"), "Table title",
"The title node displays the correct text.");
- is(rows.length, 3, "There should be 3 table chart rows created.");
+ is(rows.length, 4, "There should be 3 table chart rows and a header created.");
- ok(rows[0].querySelector(".table-chart-row-box.chart-colored-blob"),
- "A colored blob exists for the firt row.");
is(rows[0].querySelectorAll("label")[0].getAttribute("name"), "label1",
- "The first column of the first row exists.");
+ "The first column of the header exists.");
is(rows[0].querySelectorAll("label")[1].getAttribute("name"), "label2",
+ "The second column of the header exists.");
+ is(rows[0].querySelectorAll("span")[0].textContent, "label1header",
+ "The first column of the header displays the correct text.");
+ is(rows[0].querySelectorAll("span")[1].textContent, "label2header",
+ "The second column of the header displays the correct text.");
+
+ ok(rows[1].querySelector(".table-chart-row-box.chart-colored-blob"),
+ "A colored blob exists for the firt row.");
+ is(rows[1].querySelectorAll("span")[0].getAttribute("name"), "label1",
+ "The first column of the first row exists.");
+ is(rows[1].querySelectorAll("span")[1].getAttribute("name"), "label2",
"The second column of the first row exists.");
- is(rows[0].querySelectorAll("label")[0].getAttribute("value"), "1",
+ is(rows[1].querySelectorAll("label")[0].getAttribute("value"), "1",
"The first column of the first row displays the correct text.");
- is(rows[0].querySelectorAll("label")[1].getAttribute("value"), "11.1foo",
+ is(rows[1].querySelectorAll("label")[1].getAttribute("value"), "11.1foo",
"The second column of the first row displays the correct text.");
- ok(rows[1].querySelector(".table-chart-row-box.chart-colored-blob"),
+ ok(rows[2].querySelector(".table-chart-row-box.chart-colored-blob"),
"A colored blob exists for the second row.");
- is(rows[1].querySelectorAll("label")[0].getAttribute("name"), "label1",
+ is(rows[2].querySelectorAll("label")[0].getAttribute("name"), "label1",
"The first column of the second row exists.");
- is(rows[1].querySelectorAll("label")[1].getAttribute("name"), "label2",
+ is(rows[2].querySelectorAll("label")[1].getAttribute("name"), "label2",
"The second column of the second row exists.");
- is(rows[1].querySelectorAll("label")[0].getAttribute("value"), "2",
+ is(rows[2].querySelectorAll("label")[0].getAttribute("value"), "2",
"The first column of the second row displays the correct text.");
- is(rows[1].querySelectorAll("label")[1].getAttribute("value"), "12.2bar",
+ is(rows[2].querySelectorAll("label")[1].getAttribute("value"), "12.2bar",
"The second column of the first row displays the correct text.");
- ok(rows[2].querySelector(".table-chart-row-box.chart-colored-blob"),
+ ok(rows[3].querySelector(".table-chart-row-box.chart-colored-blob"),
"A colored blob exists for the third row.");
- is(rows[2].querySelectorAll("label")[0].getAttribute("name"), "label1",
+ is(rows[3].querySelectorAll("label")[0].getAttribute("name"), "label1",
"The first column of the third row exists.");
- is(rows[2].querySelectorAll("label")[1].getAttribute("name"), "label2",
+ is(rows[3].querySelectorAll("label")[1].getAttribute("name"), "label2",
"The second column of the third row exists.");
- is(rows[2].querySelectorAll("label")[0].getAttribute("value"), "3",
+ is(rows[3].querySelectorAll("label")[0].getAttribute("value"), "3",
"The first column of the third row displays the correct text.");
- is(rows[2].querySelectorAll("label")[1].getAttribute("value"), "13.3baz",
+ is(rows[3].querySelectorAll("label")[1].getAttribute("value"), "13.3baz",
"The second column of the third row displays the correct text.");
is(sums.length, 2, "There should be 2 total summaries created.");
diff --git a/devtools/client/netmonitor/test/browser_net_charts-04.js b/devtools/client/netmonitor/test/browser_net_charts-04.js
index 0d150c409..921701ae5 100644
--- a/devtools/client/netmonitor/test/browser_net_charts-04.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-04.js
@@ -22,6 +22,10 @@ add_task(function* () {
totals: {
label1: value => "Hello " + L10N.numberWithDecimals(value, 2),
label2: value => "World " + L10N.numberWithDecimals(value, 2)
+ },
+ header: {
+ label1: "",
+ label2: ""
}
});
@@ -40,17 +44,17 @@ add_task(function* () {
is(title.getAttribute("value"), "Table title",
"The title node displays the correct text.");
- is(rows.length, 1, "There should be 1 table chart row created.");
+ is(rows.length, 2, "There should be 1 table chart row and a 1 header created.");
- ok(rows[0].querySelector(".table-chart-row-box.chart-colored-blob"),
+ ok(rows[1].querySelector(".table-chart-row-box.chart-colored-blob"),
"A colored blob exists for the firt row.");
- is(rows[0].querySelectorAll("label")[0].getAttribute("name"), "size",
+ is(rows[1].querySelectorAll("label")[0].getAttribute("name"), "size",
"The first column of the first row exists.");
- is(rows[0].querySelectorAll("label")[1].getAttribute("name"), "label",
+ is(rows[1].querySelectorAll("label")[1].getAttribute("name"), "label",
"The second column of the first row exists.");
- is(rows[0].querySelectorAll("label")[0].getAttribute("value"), "",
+ is(rows[1].querySelectorAll("label")[0].getAttribute("value"), "",
"The first column of the first row displays the correct text.");
- is(rows[0].querySelectorAll("label")[1].getAttribute("value"),
+ is(rows[1].querySelectorAll("label")[1].getAttribute("value"),
L10N.getStr("tableChart.loading"),
"The second column of the first row displays the correct text.");
diff --git a/devtools/client/netmonitor/test/browser_net_charts-05.js b/devtools/client/netmonitor/test/browser_net_charts-05.js
index 00445b132..c444d2c65 100644
--- a/devtools/client/netmonitor/test/browser_net_charts-05.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-05.js
@@ -33,6 +33,10 @@ add_task(function* () {
totals: {
size: value => "Hello " + L10N.numberWithDecimals(value, 2),
label: value => "World " + L10N.numberWithDecimals(value, 2)
+ },
+ header: {
+ label1: "",
+ label2: ""
}
});
@@ -53,9 +57,9 @@ add_task(function* () {
ok(node.querySelector(".table-chart-container"),
"A table chart was created successfully.");
- is(rows.length, 3, "There should be 3 pie chart slices created.");
- is(rows.length, 3, "There should be 3 table chart rows created.");
- is(sums.length, 2, "There should be 2 total summaries created.");
+ is(rows.length, 4, "There should be 3 pie chart slices and 1 header created.");
+ is(rows.length, 4, "There should be 3 table chart rows and 1 header created.");
+ is(sums.length, 2, "There should be 2 total summaries and 1 header created.");
yield teardown(monitor);
});
diff --git a/devtools/client/netmonitor/test/browser_net_charts-07.js b/devtools/client/netmonitor/test/browser_net_charts-07.js
index bb992e4eb..a655f258c 100644
--- a/devtools/client/netmonitor/test/browser_net_charts-07.js
+++ b/devtools/client/netmonitor/test/browser_net_charts-07.js
@@ -20,6 +20,10 @@ add_task(function* () {
totals: {
label1: value => "Hello " + L10N.numberWithDecimals(value, 2),
label2: value => "World " + L10N.numberWithDecimals(value, 2)
+ },
+ header: {
+ label1: "",
+ label2: ""
}
});
@@ -29,17 +33,17 @@ add_task(function* () {
let rows = grid.querySelectorAll(".table-chart-row");
let sums = node.querySelectorAll(".table-chart-summary-label");
- is(rows.length, 1, "There should be 1 table chart row created.");
+ is(rows.length, 2, "There should be 1 table chart row and 1 header created.");
- ok(rows[0].querySelector(".table-chart-row-box.chart-colored-blob"),
+ ok(rows[1].querySelector(".table-chart-row-box.chart-colored-blob"),
"A colored blob exists for the firt row.");
- is(rows[0].querySelectorAll("label")[0].getAttribute("name"), "size",
+ is(rows[1].querySelectorAll("label")[0].getAttribute("name"), "size",
"The first column of the first row exists.");
- is(rows[0].querySelectorAll("label")[1].getAttribute("name"), "label",
+ is(rows[1].querySelectorAll("label")[1].getAttribute("name"), "label",
"The second column of the first row exists.");
- is(rows[0].querySelectorAll("label")[0].getAttribute("value"), "",
+ is(rows[1].querySelectorAll("label")[0].getAttribute("value"), "",
"The first column of the first row displays the correct text.");
- is(rows[0].querySelectorAll("label")[1].getAttribute("value"),
+ is(rows[1].querySelectorAll("label")[1].getAttribute("value"),
L10N.getStr("tableChart.unavailable"),
"The second column of the first row displays the correct text.");
diff --git a/devtools/client/netmonitor/test/browser_net_curl-utils.js b/devtools/client/netmonitor/test/browser_net_curl-utils.js
index 7a5fc7926..0d35c6141 100644
--- a/devtools/client/netmonitor/test/browser_net_curl-utils.js
+++ b/devtools/client/netmonitor/test/browser_net_curl-utils.js
@@ -7,7 +7,7 @@
* Tests Curl Utils functionality.
*/
-const { CurlUtils } = require("devtools/client/shared/curl");
+const { Curl, CurlUtils } = require("devtools/client/shared/curl");
add_task(function* () {
let { tab, monitor } = yield initNetMonitor(CURL_UTILS_URL);
@@ -37,6 +37,8 @@ add_task(function* () {
data = yield createCurlData(requests.post.attachment, gNetwork);
testIsUrlEncodedRequest(data);
testWritePostDataTextParams(data);
+ testWriteEmptyPostDataTextParams(data);
+ testDataArgumentOnGeneratedCommand(data);
data = yield createCurlData(requests.multipart.attachment, gNetwork);
testIsMultipartRequest(data);
@@ -85,6 +87,18 @@ function testWritePostDataTextParams(data) {
"Should return a serialized representation of the request parameters");
}
+function testWriteEmptyPostDataTextParams(data) {
+ let params = CurlUtils.writePostDataTextParams(null);
+ is(params, "",
+ "Should return a empty string when no parameters provided");
+}
+
+function testDataArgumentOnGeneratedCommand(data) {
+ let curlCommand = Curl.generateCommand(data);
+ ok(curlCommand.includes("--data"),
+ "Should return a curl command with --data");
+}
+
function testGetMultipartBoundary(data) {
let boundary = CurlUtils.getMultipartBoundary(data);
ok(/-{3,}\w+/.test(boundary),
diff --git a/devtools/client/netmonitor/test/browser_net_footer-summary.js b/devtools/client/netmonitor/test/browser_net_footer-summary.js
index e484b2097..8faa8470b 100644
--- a/devtools/client/netmonitor/test/browser_net_footer-summary.js
+++ b/devtools/client/netmonitor/test/browser_net_footer-summary.js
@@ -10,13 +10,14 @@
add_task(function* () {
requestLongerTimeout(2);
+ let { getSummary } = require("devtools/client/netmonitor/selectors/index");
let { L10N } = require("devtools/client/netmonitor/l10n");
let { PluralForm } = require("devtools/shared/plural-form");
let { tab, monitor } = yield initNetMonitor(FILTERING_URL);
info("Starting test... ");
- let { $, NetMonitorView } = monitor.panelWin;
+ let { $, NetMonitorView, gStore } = monitor.panelWin;
let { RequestsMenu } = NetMonitorView;
RequestsMenu.lazyUpdate = false;
@@ -43,33 +44,27 @@ add_task(function* () {
yield teardown(monitor);
function testStatus() {
- let summary = $("#requests-menu-network-summary-button");
- let value = summary.getAttribute("label");
+ const { count, contentSize, transferredSize, millis } = getSummary(gStore.getState());
+ let value = $("#requests-menu-network-summary-button").textContent;
info("Current summary: " + value);
- let visibleItems = RequestsMenu.visibleItems;
- let visibleRequestsCount = visibleItems.length;
let totalRequestsCount = RequestsMenu.itemCount;
- info("Current requests: " + visibleRequestsCount + " of " + totalRequestsCount + ".");
+ info("Current requests: " + count + " of " + totalRequestsCount + ".");
- if (!totalRequestsCount || !visibleRequestsCount) {
+ if (!totalRequestsCount || !count) {
is(value, L10N.getStr("networkMenu.empty"),
"The current summary text is incorrect, expected an 'empty' label.");
return;
}
- let totalBytes = RequestsMenu._getTotalBytesOfRequests(visibleItems);
- let totalMillis =
- RequestsMenu._getNewestRequest(visibleItems).attachment.endedMillis -
- RequestsMenu._getOldestRequest(visibleItems).attachment.startedMillis;
+ info("Computed total bytes: " + contentSize);
+ info("Computed total millis: " + millis);
- info("Computed total bytes: " + totalBytes);
- info("Computed total millis: " + totalMillis);
-
- is(value, PluralForm.get(visibleRequestsCount, L10N.getStr("networkMenu.summary"))
- .replace("#1", visibleRequestsCount)
- .replace("#2", L10N.numberWithDecimals((totalBytes || 0) / 1024, 2))
- .replace("#3", L10N.numberWithDecimals((totalMillis || 0) / 1000, 2))
+ is(value, PluralForm.get(count, L10N.getStr("networkMenu.summary2"))
+ .replace("#1", count)
+ .replace("#2", L10N.numberWithDecimals((contentSize || 0) / 1024, 2))
+ .replace("#3", L10N.numberWithDecimals((transferredSize || 0) / 1024, 2))
+ .replace("#4", L10N.numberWithDecimals((millis || 0) / 1000, 2))
, "The current summary text is incorrect.");
}
});
diff --git a/devtools/client/netmonitor/test/browser_net_security-details.js b/devtools/client/netmonitor/test/browser_net_security-details.js
index 0a83b3ed9..61f39a414 100644
--- a/devtools/client/netmonitor/test/browser_net_security-details.js
+++ b/devtools/client/netmonitor/test/browser_net_security-details.js
@@ -66,6 +66,10 @@ add_task(function* () {
checkLabel("#security-cert-issuer-o", "Mozilla Testing");
checkLabel("#security-cert-issuer-ou", "<Not Available>");
+ // These two values can change. So only check they're not empty.
+ checkLabelNotEmpty("#security-keagroup-value");
+ checkLabelNotEmpty("#security-signaturescheme-value");
+
// Locale sensitive and varies between timezones. Cant't compare equality or
// the test fails depending on which part of the world the test is executed.
checkLabelNotEmpty("#security-cert-validity-begins");
diff --git a/devtools/client/netmonitor/test/browser_net_simple-request-data.js b/devtools/client/netmonitor/test/browser_net_simple-request-data.js
index 1b952bd71..f21318d7a 100644
--- a/devtools/client/netmonitor/test/browser_net_simple-request-data.js
+++ b/devtools/client/netmonitor/test/browser_net_simple-request-data.js
@@ -226,6 +226,8 @@ function test() {
"The eventTimings attachment has an incorrect |timings.blocked| property.");
is(typeof requestItem.attachment.eventTimings.timings.dns, "number",
"The eventTimings attachment has an incorrect |timings.dns| property.");
+ is(typeof requestItem.attachment.eventTimings.timings.ssl, "number",
+ "The eventTimings attachment has an incorrect |timings.ssl| property.");
is(typeof requestItem.attachment.eventTimings.timings.connect, "number",
"The eventTimings attachment has an incorrect |timings.connect| property.");
is(typeof requestItem.attachment.eventTimings.timings.send, "number",
diff --git a/devtools/client/netmonitor/toolbar-view.js b/devtools/client/netmonitor/toolbar-view.js
index 28c3cf99b..1834d3ee9 100644
--- a/devtools/client/netmonitor/toolbar-view.js
+++ b/devtools/client/netmonitor/toolbar-view.js
@@ -7,6 +7,7 @@ const Provider = createFactory(require("devtools/client/shared/vendor/react-redu
const FilterButtons = createFactory(require("./components/filter-buttons"));
const ToggleButton = createFactory(require("./components/toggle-button"));
const SearchBox = createFactory(require("./components/search-box"));
+const SummaryButton = createFactory(require("./components/summary-button"));
const { L10N } = require("./l10n");
// Shortcuts
@@ -28,8 +29,9 @@ ToolbarView.prototype = {
this._clearContainerNode = $("#react-clear-button-hook");
this._filterContainerNode = $("#react-filter-buttons-hook");
- this._toggleContainerNode = $("#react-details-pane-toggle-hook");
+ this._summaryContainerNode = $("#react-summary-button-hook");
this._searchContainerNode = $("#react-search-box-hook");
+ this._toggleContainerNode = $("#react-details-pane-toggle-hook");
// clear button
ReactDOM.render(button({
@@ -47,6 +49,12 @@ ToolbarView.prototype = {
FilterButtons()
), this._filterContainerNode);
+ // summary button
+ ReactDOM.render(Provider(
+ { store },
+ SummaryButton()
+ ), this._summaryContainerNode);
+
// search box
ReactDOM.render(Provider(
{ store },
@@ -68,8 +76,9 @@ ToolbarView.prototype = {
ReactDOM.unmountComponentAtNode(this._clearContainerNode);
ReactDOM.unmountComponentAtNode(this._filterContainerNode);
- ReactDOM.unmountComponentAtNode(this._toggleContainerNode);
+ ReactDOM.unmountComponentAtNode(this._summaryContainerNode);
ReactDOM.unmountComponentAtNode(this._searchContainerNode);
+ ReactDOM.unmountComponentAtNode(this._toggleContainerNode);
}
};
diff --git a/devtools/client/scratchpad/scratchpad.js b/devtools/client/scratchpad/scratchpad.js
index 306b635df..60221f39d 100644
--- a/devtools/client/scratchpad/scratchpad.js
+++ b/devtools/client/scratchpad/scratchpad.js
@@ -81,7 +81,7 @@ loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
loader.lazyRequireGetter(this, "DebuggerClient", "devtools/shared/client/main", true);
loader.lazyRequireGetter(this, "EnvironmentClient", "devtools/shared/client/main", true);
loader.lazyRequireGetter(this, "ObjectClient", "devtools/shared/client/main", true);
-loader.lazyRequireGetter(this, "HUDService", "devtools/client/webconsole/hudservice");
+loader.lazyRequireGetter(this, "HUDService", "devtools/client/webconsole/hudservice", true);
XPCOMUtils.defineLazyGetter(this, "REMOTE_TIMEOUT", () =>
Services.prefs.getIntPref("devtools.debugger.remote-timeout"));
@@ -1744,6 +1744,9 @@ var Scratchpad = {
this.editor.focus();
this.editor.setCursor({ line: lines.length, ch: lines.pop().length });
+ // Add the commands controller for the source-editor.
+ this.editor.insertCommandsController();
+
if (state)
this.dirty = !state.saved;
diff --git a/devtools/client/scratchpad/scratchpad.xul b/devtools/client/scratchpad/scratchpad.xul
index 3712f163d..8694c1bb5 100644
--- a/devtools/client/scratchpad/scratchpad.xul
+++ b/devtools/client/scratchpad/scratchpad.xul
@@ -121,7 +121,7 @@
<key id="sp-key-reloadAndRun"
key="&reloadAndRun.key;"
command="sp-cmd-reloadAndRun"
- modifiers="accel,alt"/>
+ modifiers="shift,alt"/>
<key id="sp-key-evalFunction"
key="&evalFunction.key;"
command="sp-cmd-evalFunction"
diff --git a/devtools/client/scratchpad/test/browser_scratchpad_open_error_console.js b/devtools/client/scratchpad/test/browser_scratchpad_open_error_console.js
index 4da2a2daf..418bdfb56 100644
--- a/devtools/client/scratchpad/test/browser_scratchpad_open_error_console.js
+++ b/devtools/client/scratchpad/test/browser_scratchpad_open_error_console.js
@@ -2,7 +2,7 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
-const HUDService = require("devtools/client/webconsole/hudservice");
+const {HUDService} = require("devtools/client/webconsole/hudservice");
function test()
{
diff --git a/devtools/client/shared/curl.js b/devtools/client/shared/curl.js
index 44465193f..45122be37 100644
--- a/devtools/client/shared/curl.js
+++ b/devtools/client/shared/curl.js
@@ -76,7 +76,8 @@ const Curl = {
// Create post data.
let postData = [];
- if (utils.isUrlEncodedRequest(data) || data.method == "PUT") {
+ if (utils.isUrlEncodedRequest(data) ||
+ ["PUT", "POST"].includes(data.method)) {
postDataText = data.postDataText;
postData.push("--data");
postData.push(escapeString(utils.writePostDataTextParams(postDataText)));
@@ -207,6 +208,9 @@ const CurlUtils = {
* Post data parameters.
*/
writePostDataTextParams: function (postDataText) {
+ if (!postDataText) {
+ return "";
+ }
let lines = postDataText.split("\r\n");
return lines[lines.length - 1];
},
diff --git a/devtools/client/shared/widgets/Chart.jsm b/devtools/client/shared/widgets/Chart.jsm
index 0894a62ca..0b7cb71fb 100644
--- a/devtools/client/shared/widgets/Chart.jsm
+++ b/devtools/client/shared/widgets/Chart.jsm
@@ -105,7 +105,7 @@ function PieTableChart(node, pie, table) {
* - "mouseout", when the mouse leaves a slice or a row
* - "click", when the mouse enters a slice or a row
*/
-function createPieTableChart(document, { title, diameter, data, strings, totals, sorted }) {
+function createPieTableChart(document, { title, diameter, data, strings, totals, sorted, header }) {
if (data && sorted) {
data = data.slice().sort((a, b) => +(a.size < b.size));
}
@@ -119,7 +119,8 @@ function createPieTableChart(document, { title, diameter, data, strings, totals,
title: title,
data: data,
strings: strings,
- totals: totals
+ totals: totals,
+ header: header,
});
let container = document.createElement("hbox");
@@ -338,7 +339,7 @@ function createPieChart(document, { data, width, height, centerX, centerY, radiu
* - "mouseout", when the mouse leaves a row
* - "click", when the mouse clicks a row
*/
-function createTableChart(document, { title, data, strings, totals }) {
+function createTableChart(document, { title, data, strings, totals, header }) {
strings = strings || {};
totals = totals || {};
let isPlaceholder = false;
@@ -371,6 +372,24 @@ function createTableChart(document, { title, data, strings, totals }) {
tableNode.className = "plain table-chart-grid";
container.appendChild(tableNode);
+ const headerNode = document.createElement("div");
+ headerNode.className = "table-chart-row";
+
+ const headerBoxNode = document.createElement("div");
+ headerBoxNode.className = "table-chart-row-box";
+ headerNode.appendChild(headerBoxNode);
+
+ for (let [key, value] of Object.entries(header)) {
+ let headerLabelNode = document.createElement("span");
+ headerLabelNode.className = "plain table-chart-row-label";
+ headerLabelNode.setAttribute("name", key);
+ headerLabelNode.textContent = value;
+
+ headerNode.appendChild(headerLabelNode);
+ }
+
+ tableNode.appendChild(headerNode);
+
for (let rowInfo of data) {
let rowNode = document.createElement("hbox");
rowNode.className = "table-chart-row";
diff --git a/devtools/client/sourceeditor/editor-commands-controller.js b/devtools/client/sourceeditor/editor-commands-controller.js
new file mode 100644
index 000000000..2587f9a1f
--- /dev/null
+++ b/devtools/client/sourceeditor/editor-commands-controller.js
@@ -0,0 +1,97 @@
+/* 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";
+
+/**
+ * The source editor exposes XUL commands that can be used when embedded in a XUL
+ * document. This controller drives the availability and behavior of the commands. When
+ * the editor input field is focused, this controller will update the matching menu item
+ * entries found in application menus or context menus.
+ */
+
+/**
+ * Returns a controller object that can be used for editor-specific commands:
+ * - find
+ * - find again
+ * - go to line
+ * - undo
+ * - redo
+ * - delete
+ * - select all
+ */
+function createController(ed) {
+ return {
+ supportsCommand: function (cmd) {
+ switch (cmd) {
+ case "cmd_find":
+ case "cmd_findAgain":
+ case "cmd_gotoLine":
+ case "cmd_undo":
+ case "cmd_redo":
+ case "cmd_delete":
+ case "cmd_selectAll":
+ return true;
+ }
+
+ return false;
+ },
+
+ isCommandEnabled: function (cmd) {
+ let cm = ed.codeMirror;
+
+ switch (cmd) {
+ case "cmd_find":
+ case "cmd_gotoLine":
+ case "cmd_selectAll":
+ return true;
+ case "cmd_findAgain":
+ return cm.state.search != null && cm.state.search.query != null;
+ case "cmd_undo":
+ return ed.canUndo();
+ case "cmd_redo":
+ return ed.canRedo();
+ case "cmd_delete":
+ return ed.somethingSelected();
+ }
+
+ return false;
+ },
+
+ doCommand: function (cmd) {
+ let cm = ed.codeMirror;
+
+ let map = {
+ "cmd_selectAll": "selectAll",
+ "cmd_find": "find",
+ "cmd_undo": "undo",
+ "cmd_redo": "redo",
+ "cmd_delete": "delCharAfter",
+ "cmd_findAgain": "findNext"
+ };
+
+ if (map[cmd]) {
+ cm.execCommand(map[cmd]);
+ return;
+ }
+
+ if (cmd == "cmd_gotoLine") {
+ ed.jumpToLine();
+ }
+ },
+
+ onEvent: function () {}
+ };
+}
+
+/**
+ * Create and insert a commands controller for the provided SourceEditor instance.
+ */
+function insertCommandsController(sourceEditor) {
+ let input = sourceEditor.codeMirror.getInputField();
+ let controller = createController(sourceEditor);
+ input.controllers.insertControllerAt(0, controller);
+}
+
+module.exports = { insertCommandsController };
diff --git a/devtools/client/sourceeditor/editor.js b/devtools/client/sourceeditor/editor.js
index ce2136afc..1b3c1d31a 100644
--- a/devtools/client/sourceeditor/editor.js
+++ b/devtools/client/sourceeditor/editor.js
@@ -489,6 +489,16 @@ Editor.prototype = {
},
/**
+ * The source editor can expose several commands linked from system and context menus.
+ * Kept for backward compatibility with scratchpad and styleeditor.
+ */
+ insertCommandsController: function () {
+ const { insertCommandsController } =
+ require("devtools/client/sourceeditor/editor-commands-controller");
+ insertCommandsController(this);
+ },
+
+ /**
* Returns text from the text area. If line argument is provided
* the method returns only that line.
*/
diff --git a/devtools/client/sourceeditor/moz.build b/devtools/client/sourceeditor/moz.build
index 5081325c5..765accb14 100644
--- a/devtools/client/sourceeditor/moz.build
+++ b/devtools/client/sourceeditor/moz.build
@@ -12,6 +12,7 @@ DevToolsModules(
'autocomplete.js',
'css-autocompleter.js',
'debugger.js',
+ 'editor-commands-controller.js',
'editor.js'
)
diff --git a/devtools/client/styleeditor/StyleEditorUI.jsm b/devtools/client/styleeditor/StyleEditorUI.jsm
index cdb267669..b2735b3fc 100644
--- a/devtools/client/styleeditor/StyleEditorUI.jsm
+++ b/devtools/client/styleeditor/StyleEditorUI.jsm
@@ -72,10 +72,18 @@ function StyleEditorUI(debuggee, target, panelDoc, cssProperties) {
this.editors = [];
this.selectedEditor = null;
this.savedLocations = {};
+ this._seenSheets = new Map();
+
+ // Don't add any style sheets that might arrive via events, until
+ // the call to initialize. Style sheets can arrive from the server
+ // at any time, for example if a new style sheet was added, or if
+ // the style sheet actor was just created and is walking the style
+ // sheets for the first time. In any case, in |initialize| we're
+ // going to fetch the list of sheets anyway.
+ this._suppressAdd = true;
this._onOptionsPopupShowing = this._onOptionsPopupShowing.bind(this);
this._onOptionsPopupHiding = this._onOptionsPopupHiding.bind(this);
- this._onStyleSheetCreated = this._onStyleSheetCreated.bind(this);
this._onNewDocument = this._onNewDocument.bind(this);
this._onMediaPrefChanged = this._onMediaPrefChanged.bind(this);
this._updateMediaList = this._updateMediaList.bind(this);
@@ -83,10 +91,13 @@ function StyleEditorUI(debuggee, target, panelDoc, cssProperties) {
this._onError = this._onError.bind(this);
this._updateOpenLinkItem = this._updateOpenLinkItem.bind(this);
this._openLinkNewTab = this._openLinkNewTab.bind(this);
+ this._addStyleSheet = this._addStyleSheet.bind(this);
this._prefObserver = new PrefObserver("devtools.styleeditor.");
this._prefObserver.on(PREF_ORIG_SOURCES, this._onNewDocument);
this._prefObserver.on(PREF_MEDIA_SIDEBAR, this._onMediaPrefChanged);
+
+ this._debuggee.on("stylesheet-added", this._addStyleSheet);
}
this.StyleEditorUI = StyleEditorUI;
@@ -165,7 +176,7 @@ StyleEditorUI.prototype = {
this._view = new SplitView(viewRoot);
wire(this._view.rootElement, ".style-editor-newButton", () =>{
- this._debuggee.addStyleSheet(null).then(this._onStyleSheetCreated);
+ this._debuggee.addStyleSheet(null);
});
wire(this._view.rootElement, ".style-editor-importButton", ()=> {
@@ -233,6 +244,7 @@ StyleEditorUI.prototype = {
* StyleSheet object for new sheet
*/
_onNewDocument: function () {
+ this._suppressAdd = true;
this._debuggee.getStyleSheets().then((styleSheets) => {
return this._resetStyleSheetList(styleSheets);
}).then(null, e => console.error(e));
@@ -246,6 +258,7 @@ StyleEditorUI.prototype = {
*/
_resetStyleSheetList: Task.async(function* (styleSheets) {
this._clear();
+ this._suppressAdd = false;
for (let sheet of styleSheets) {
try {
@@ -288,6 +301,10 @@ StyleEditorUI.prototype = {
this._view.removeAll();
this.selectedEditor = null;
+ // Here the keys are style sheet actors, and the values are
+ // promises that resolve to the sheet's editor. See |_addStyleSheet|.
+ this._seenSheets = new Map();
+ this._suppressAdd = true;
this._root.classList.add("loading");
},
@@ -298,46 +315,67 @@ StyleEditorUI.prototype = {
*
* @param {StyleSheetFront} styleSheet
* Style sheet to add to style editor
+ * @param {Boolean} isNew
+ * True if this style sheet was created by a call to the
+ * style sheets actor's @see addStyleSheet method.
+ * @return {Promise}
+ * A promise that resolves to the style sheet's editor when the style sheet has
+ * been fully loaded. If the style sheet has a source map, and source mapping
+ * is enabled, then the promise resolves to null.
*/
- _addStyleSheet: Task.async(function* (styleSheet) {
- let editor = yield this._addStyleSheetEditor(styleSheet);
-
- if (!Services.prefs.getBoolPref(PREF_ORIG_SOURCES)) {
- return;
+ _addStyleSheet: function (styleSheet, isNew) {
+ if (this._suppressAdd) {
+ return null;
}
- let sources = yield styleSheet.getOriginalSources();
- if (sources && sources.length) {
- let parentEditorName = editor.friendlyName;
- this._removeStyleSheetEditor(editor);
-
- for (let source of sources) {
- // set so the first sheet will be selected, even if it's a source
- source.styleSheetIndex = styleSheet.styleSheetIndex;
- source.relatedStyleSheet = styleSheet;
- source.relatedEditorName = parentEditorName;
- yield this._addStyleSheetEditor(source);
- }
+ if (!this._seenSheets.has(styleSheet)) {
+ let promise = (async () => {
+ let editor = await this._addStyleSheetEditor(styleSheet, isNew);
+
+ if (!Services.prefs.getBoolPref(PREF_ORIG_SOURCES)) {
+ return editor;
+ }
+
+ let sources = await styleSheet.getOriginalSources();
+ // A single generated sheet might map to multiple original
+ // sheets, so make editors for each of them.
+ if (sources && sources.length) {
+ let parentEditorName = editor.friendlyName;
+ this._removeStyleSheetEditor(editor);
+ editor = null;
+
+ for (let source of sources) {
+ // set so the first sheet will be selected, even if it's a source
+ source.styleSheetIndex = styleSheet.styleSheetIndex;
+ source.relatedStyleSheet = styleSheet;
+ source.relatedEditorName = parentEditorName;
+ await this._addStyleSheetEditor(source);
+ }
+ }
+
+ return editor;
+ })();
+ this._seenSheets.set(styleSheet, promise);
}
- }),
+ return this._seenSheets.get(styleSheet);
+ },
/**
* Add a new editor to the UI for a source.
*
* @param {StyleSheet} styleSheet
* Object representing stylesheet
- * @param {nsIfile} file
- * Optional file object that sheet was imported from
* @param {Boolean} isNew
* Optional if stylesheet is a new sheet created by user
* @return {Promise} that is resolved with the created StyleSheetEditor when
* the editor is fully initialized or rejected on error.
*/
- _addStyleSheetEditor: Task.async(function* (styleSheet, file, isNew) {
+ _addStyleSheetEditor: Task.async(function* (styleSheet, isNew) {
// recall location of saved file for this sheet after page reload
+ let file = null;
let identifier = this.getStyleSheetIdentifier(styleSheet);
let savedFile = this.savedLocations[identifier];
- if (savedFile && !file) {
+ if (savedFile) {
file = savedFile;
}
@@ -388,8 +426,16 @@ StyleEditorUI.prototype = {
NetUtil.readInputStreamToString(stream, stream.available());
stream.close();
+ this._suppressAdd = true;
this._debuggee.addStyleSheet(source).then((styleSheet) => {
- this._onStyleSheetCreated(styleSheet, selectedFile);
+ this._suppressAdd = false;
+ this._addStyleSheet(styleSheet, true).then(editor => {
+ if (editor) {
+ editor.savedFile = selectedFile;
+ }
+ // Just for testing purposes.
+ this.emit("test:editor-updated", editor);
+ });
});
});
};
@@ -398,14 +444,6 @@ StyleEditorUI.prototype = {
},
/**
- * When a new or imported stylesheet has been added to the document.
- * Add an editor for it.
- */
- _onStyleSheetCreated: function (styleSheet, file) {
- this._addStyleSheetEditor(styleSheet, file, true);
- },
-
- /**
* Forward any error from a stylesheet.
*
* @param {string} event
@@ -1013,6 +1051,9 @@ StyleEditorUI.prototype = {
this._clearStyleSheetEditors();
+ this._seenSheets = null;
+ this._suppressAdd = false;
+
let sidebar = this._panelDoc.querySelector(".splitview-controller");
let sidebarWidth = sidebar.getAttribute("width");
Services.prefs.setIntPref(PREF_NAV_WIDTH, sidebarWidth);
@@ -1025,5 +1066,7 @@ StyleEditorUI.prototype = {
this._prefObserver.off(PREF_ORIG_SOURCES, this._onNewDocument);
this._prefObserver.off(PREF_MEDIA_SIDEBAR, this._onMediaPrefChanged);
this._prefObserver.destroy();
+
+ this._debuggee.off("stylesheet-added", this._addStyleSheet);
}
};
diff --git a/devtools/client/styleeditor/StyleSheetEditor.jsm b/devtools/client/styleeditor/StyleSheetEditor.jsm
index 980e51974..832fcacde 100644
--- a/devtools/client/styleeditor/StyleSheetEditor.jsm
+++ b/devtools/client/styleeditor/StyleSheetEditor.jsm
@@ -468,6 +468,9 @@ StyleSheetEditor.prototype = {
sourceEditor.container.addEventListener("mousemove", this._onMouseMove);
}
+ // Add the commands controller for the source-editor.
+ sourceEditor.insertCommandsController();
+
this.emit("source-editor-load");
});
},
diff --git a/devtools/client/styleeditor/test/browser.ini b/devtools/client/styleeditor/test/browser.ini
index 1a85546af..4a84d45e6 100644
--- a/devtools/client/styleeditor/test/browser.ini
+++ b/devtools/client/styleeditor/test/browser.ini
@@ -60,6 +60,7 @@ support-files =
!/devtools/client/shared/test/test-actor-registry.js
!/devtools/client/shared/test/test-actor.js
+[browser_styleeditor_add_stylesheet.js]
[browser_styleeditor_autocomplete.js]
[browser_styleeditor_autocomplete-disabled.js]
[browser_styleeditor_bom.js]
diff --git a/devtools/client/styleeditor/test/browser_styleeditor_add_stylesheet.js b/devtools/client/styleeditor/test/browser_styleeditor_add_stylesheet.js
new file mode 100644
index 000000000..d8315d212
--- /dev/null
+++ b/devtools/client/styleeditor/test/browser_styleeditor_add_stylesheet.js
@@ -0,0 +1,37 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Test that a newly-added style sheet shows up in the style editor.
+
+const TESTCASE_URI = TEST_BASE_HTTPS + "simple.html";
+
+add_task(function* () {
+ let { ui } = yield openStyleEditorForURL(TESTCASE_URI);
+
+ is(ui.editors.length, 2, "Two sheets present after load.");
+
+ // We have to wait for the length to change, because we might still
+ // be seeing events from the initial open.
+ let added = new Promise(resolve => {
+ let handler = () => {
+ if (ui.editors.length === 3) {
+ ui.off("editor-added", handler);
+ resolve();
+ }
+ };
+ ui.on("editor-added", handler);
+ });
+
+ info("Adding a style sheet");
+ yield ContentTask.spawn(gBrowser.selectedBrowser, null, () => {
+ let document = content.document;
+ const style = document.createElement("style");
+ style.appendChild(document.createTextNode("div { background: #f06; }"));
+ document.head.appendChild(style);
+ });
+ yield added;
+
+ is(ui.editors.length, 3, "Three sheets present after new style sheet");
+});
diff --git a/devtools/client/styleeditor/test/browser_styleeditor_import.js b/devtools/client/styleeditor/test/browser_styleeditor_import.js
index f31f72ce7..2f42317b9 100644
--- a/devtools/client/styleeditor/test/browser_styleeditor_import.js
+++ b/devtools/client/styleeditor/test/browser_styleeditor_import.js
@@ -18,7 +18,7 @@ const SOURCE = "body{background:red;}";
add_task(function* () {
let { panel, ui } = yield openStyleEditorForURL(TESTCASE_URI);
- let added = ui.once("editor-added");
+ let added = ui.once("test:editor-updated");
importSheet(ui, panel.panelWindow);
info("Waiting for editor to be added for the imported sheet.");
diff --git a/devtools/client/themes/common.css b/devtools/client/themes/common.css
index 3d713ada7..133770150 100644
--- a/devtools/client/themes/common.css
+++ b/devtools/client/themes/common.css
@@ -2,7 +2,9 @@
* 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/. */
+
@import url("resource://devtools/client/themes/splitters.css");
+@namespace html url("http://www.w3.org/1999/xhtml");
:root {
font: message-box;
@@ -33,6 +35,10 @@
font-size: 80%;
}
+/* Override wrong system font from forms.css */
+html|button, html|select {
+ font: message-box;
+}
/* Autocomplete Popup */
diff --git a/devtools/client/themes/netmonitor.css b/devtools/client/themes/netmonitor.css
index fea634a0e..ccffb2acc 100644
--- a/devtools/client/themes/netmonitor.css
+++ b/devtools/client/themes/netmonitor.css
@@ -8,6 +8,7 @@
}
#react-clear-button-hook,
+#react-summary-button-hook,
#react-details-pane-toggle-hook {
display: flex;
}
@@ -47,7 +48,7 @@
#details-pane-toggle,
#details-pane.pane-collapsed,
.requests-menu-waterfall,
- #requests-menu-network-summary-button > .toolbarbutton-text {
+ #requests-menu-network-summary-button > .summary-info-text {
display: none;
}
}
@@ -58,6 +59,7 @@
--timing-blocked-color: rgba(235, 83, 104, 0.8);
--timing-dns-color: rgba(223, 128, 255, 0.8); /* pink */
+ --timing-ssl-color: rgba(217, 102, 41, 0.8); /* orange */
--timing-connect-color: rgba(217, 102, 41, 0.8); /* orange */
--timing-send-color: rgba(70, 175, 227, 0.8); /* light blue */
--timing-wait-color: rgba(94, 136, 176, 0.8); /* blue grey */
@@ -73,6 +75,7 @@
--timing-blocked-color: rgba(235, 83, 104, 0.8);
--timing-dns-color: rgba(223, 128, 255, 0.8); /* pink */
+ --timing-ssl-color: rgba(217, 102, 41, 0.8); /* orange */
--timing-connect-color: rgba(217, 102, 41, 0.8); /* orange */
--timing-send-color: rgba(0, 136, 204, 0.8); /* blue */
--timing-wait-color: rgba(95, 136, 176, 0.8); /* blue grey */
@@ -504,6 +507,10 @@
background-color: var(--timing-connect-color);
}
+.requests-menu-timings-box.ssl {
+ background-color: var(--timing-ssl-color);
+}
+
.requests-menu-timings-box.send {
background-color: var(--timing-send-color);
}
@@ -744,16 +751,35 @@
/* Performance analysis buttons */
#requests-menu-network-summary-button {
+ display: flex;
+ align-items: center;
background: none;
box-shadow: none;
border-color: transparent;
- list-style-image: url(images/profiler-stopwatch.svg);
padding-inline-end: 0;
cursor: pointer;
margin-inline-end: 1em;
min-width: 0;
}
+#requests-menu-network-summary-button > .summary-info-icon {
+ background-image: url(images/profiler-stopwatch.svg);
+ filter: var(--icon-filter);
+ width: 16px;
+ height: 16px;
+ opacity: 0.8;
+}
+
+#requests-menu-network-summary-button > .summary-info-text {
+ opacity: 0.8;
+ margin-inline-start: 0.5em;
+}
+
+#requests-menu-network-summary-button:hover > .summary-info-icon,
+#requests-menu-network-summary-button:hover > .summary-info-text {
+ opacity: 1;
+}
+
/* Performance analysis view */
#network-statistics-toolbar {
diff --git a/devtools/client/webconsole/hudservice.js b/devtools/client/webconsole/hudservice.js
index 46b4f2a13..3023b7bb3 100644
--- a/devtools/client/webconsole/hudservice.js
+++ b/devtools/client/webconsole/hudservice.js
@@ -51,6 +51,23 @@ HUD_SERVICE.prototype =
*/
consoles: null,
+ _browerConsoleSessionState: false,
+ storeBrowserConsoleSessionState() {
+ this._browerConsoleSessionState = !!this.getBrowserConsole();
+ },
+ getBrowserConsoleSessionState() {
+ return this._browerConsoleSessionState;
+ },
+
+ /**
+ * Restore the Browser Console as provided by SessionStore.
+ */
+ restoreBrowserConsoleSession: function HS_restoreBrowserConsoleSession() {
+ if (!HUDService.getBrowserConsole()) {
+ HUDService.toggleBrowserConsole();
+ }
+ },
+
/**
* Assign a function to this property to listen for every request that
* completes. Used by unit tests. The callback takes one argument: the HTTP
@@ -647,6 +664,9 @@ BrowserConsole.prototype = extend(WebConsole.prototype, {
return this._bc_init;
}
+ // Only add the shutdown observer if we've opened a Browser Console window.
+ ShutdownObserver.init();
+
this.ui._filterPrefsPrefix = BROWSER_CONSOLE_FILTER_PREFS_PREFIX;
let window = this.iframeWindow;
@@ -703,16 +723,32 @@ BrowserConsole.prototype = extend(WebConsole.prototype, {
});
const HUDService = new HUD_SERVICE();
+exports.HUDService = HUDService;
-(() => {
- let methods = ["openWebConsole", "openBrowserConsole",
- "toggleBrowserConsole", "getOpenWebConsole",
- "getBrowserConsole", "getHudByWindow",
- "openBrowserConsoleOrFocus", "getHudReferenceById"];
- for (let method of methods) {
- exports[method] = HUDService[method].bind(HUDService);
- }
+/**
+ * The ShutdownObserver listens for app shutdown and saves the current state
+ * of the Browser Console for session restore.
+ */
+var ShutdownObserver = {
+ _initialized: false,
+ init() {
+ if (this._initialized) {
+ return;
+ }
+
+ Services.obs.addObserver(this, "quit-application-granted", false);
+
+ this._initialized = true;
+ },
- exports.consoles = HUDService.consoles;
- exports.lastFinishedRequest = HUDService.lastFinishedRequest;
-})();
+ observe(message, topic) {
+ if (topic == "quit-application-granted") {
+ HUDService.storeBrowserConsoleSessionState();
+ this.uninit();
+ }
+ },
+
+ uninit() {
+ Services.obs.removeObserver(this, "quit-application-granted");
+ }
+};
diff --git a/devtools/client/webconsole/panel.js b/devtools/client/webconsole/panel.js
index 3e3a4f4b9..b692de681 100644
--- a/devtools/client/webconsole/panel.js
+++ b/devtools/client/webconsole/panel.js
@@ -8,7 +8,7 @@
const promise = require("promise");
-loader.lazyGetter(this, "HUDService", () => require("devtools/client/webconsole/hudservice"));
+loader.lazyRequireGetter(this, "HUDService", "devtools/client/webconsole/hudservice", true);
loader.lazyGetter(this, "EventEmitter", () => require("devtools/shared/event-emitter"));
/**
diff --git a/devtools/client/webconsole/test/browser.ini b/devtools/client/webconsole/test/browser.ini
index 918411182..1c7913835 100644
--- a/devtools/client/webconsole/test/browser.ini
+++ b/devtools/client/webconsole/test/browser.ini
@@ -182,6 +182,7 @@ subsuite = clipboard
[browser_console_optimized_out_vars.js]
[browser_console_private_browsing.js]
skip-if = e10s # Bug 1042253 - webconsole e10s tests
+[browser_console_restore.js]
[browser_console_server_logging.js]
[browser_console_variables_view.js]
[browser_console_variables_view_filter.js]
diff --git a/devtools/client/webconsole/test/browser_console.js b/devtools/client/webconsole/test/browser_console.js
index 7bd1ffdc2..4358ac0f1 100644
--- a/devtools/client/webconsole/test/browser_console.js
+++ b/devtools/client/webconsole/test/browser_console.js
@@ -22,7 +22,7 @@ const TEST_IMAGE = "http://example.com/browser/devtools/client/webconsole/" +
add_task(function* () {
yield loadTab(TEST_URI);
- let opened = waitForConsole();
+ let opened = waitForBrowserConsole();
let hud = HUDService.getBrowserConsole();
ok(!hud, "browser console is not open");
@@ -141,20 +141,3 @@ function consoleOpened(hud) {
],
});
}
-
-function waitForConsole() {
- let deferred = promise.defer();
-
- Services.obs.addObserver(function observer(aSubject) {
- Services.obs.removeObserver(observer, "web-console-created");
- aSubject.QueryInterface(Ci.nsISupportsString);
-
- let hud = HUDService.getBrowserConsole();
- ok(hud, "browser console is open");
- is(aSubject.data, hud.hudId, "notification hudId is correct");
-
- executeSoon(() => deferred.resolve(hud));
- }, "web-console-created", false);
-
- return deferred.promise;
-}
diff --git a/devtools/client/webconsole/test/browser_console_restore.js b/devtools/client/webconsole/test/browser_console_restore.js
new file mode 100644
index 000000000..fb08d9c70
--- /dev/null
+++ b/devtools/client/webconsole/test/browser_console_restore.js
@@ -0,0 +1,30 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Check that the browser console gets session state is set correctly, and that
+// it re-opens when restore is requested.
+
+"use strict";
+
+add_task(function* () {
+ is(HUDService.getBrowserConsoleSessionState(), false, "Session state false by default");
+ HUDService.storeBrowserConsoleSessionState();
+ is(HUDService.getBrowserConsoleSessionState(), false,
+ "Session state still not true even after setting (since Browser Console is closed)");
+
+ yield HUDService.toggleBrowserConsole();
+ HUDService.storeBrowserConsoleSessionState();
+ is(HUDService.getBrowserConsoleSessionState(), true,
+ "Session state true (since Browser Console is opened)");
+
+ info("Closing the browser console and waiting for the session restore to reopen it")
+ yield HUDService.toggleBrowserConsole();
+
+ let opened = waitForBrowserConsole();
+ HUDService.restoreBrowserConsoleSession();
+
+ info("Waiting for the console to open after session restore")
+ yield opened;
+});
diff --git a/devtools/client/webconsole/test/head.js b/devtools/client/webconsole/test/head.js
index 519cb78b0..2787933c4 100644
--- a/devtools/client/webconsole/test/head.js
+++ b/devtools/client/webconsole/test/head.js
@@ -12,7 +12,7 @@ Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtool
var {Utils: WebConsoleUtils} = require("devtools/client/webconsole/utils");
var {Messages} = require("devtools/client/webconsole/console-output");
const asyncStorage = require("devtools/shared/async-storage");
-const HUDService = require("devtools/client/webconsole/hudservice");
+const {HUDService} = require("devtools/client/webconsole/hudservice");
// Services.prefs.setBoolPref("devtools.debugger.log", true);
@@ -1842,3 +1842,18 @@ function getRenderedSource(root) {
column: location.getAttribute("data-column"),
} : null;
}
+
+function waitForBrowserConsole() {
+ return new Promise(resolve => {
+ Services.obs.addObserver(function observer(subject) {
+ Services.obs.removeObserver(observer, "web-console-created");
+ subject.QueryInterface(Ci.nsISupportsString);
+
+ let hud = HUDService.getBrowserConsole();
+ ok(hud, "browser console is open");
+ is(subject.data, hud.hudId, "notification hudId is correct");
+
+ executeSoon(() => resolve(hud));
+ }, "web-console-created");
+ });
+}
diff --git a/devtools/server/actors/highlighters/box-model.js b/devtools/server/actors/highlighters/box-model.js
index 35f201a04..ae4284424 100644
--- a/devtools/server/actors/highlighters/box-model.js
+++ b/devtools/server/actors/highlighters/box-model.js
@@ -15,7 +15,10 @@ const {
isNodeValid,
moveInfobar,
} = require("./utils/markup");
-const { setIgnoreLayoutChanges } = require("devtools/shared/layout/utils");
+const {
+ setIgnoreLayoutChanges,
+ getCurrentZoom,
+ } = require("devtools/shared/layout/utils");
const inspector = require("devtools/server/actors/inspector");
const nodeConstants = require("devtools/shared/dom-node-constants");
@@ -670,10 +673,14 @@ BoxModelHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
pseudos += ":" + pseudo;
}
- let rect = this._getOuterQuad("border").bounds;
- let dim = parseFloat(rect.width.toPrecision(6)) +
+ // We want to display the original `width` and `height`, instead of the ones affected
+ // by any zoom. Since the infobar can be displayed also for text nodes, we can't
+ // access the computed style for that, and this is why we recalculate them here.
+ let zoom = getCurrentZoom(this.win);
+ let { width, height } = this._getOuterQuad("border").bounds;
+ let dim = parseFloat((width / zoom).toPrecision(6)) +
" \u00D7 " +
- parseFloat(rect.height.toPrecision(6));
+ parseFloat((height / zoom).toPrecision(6));
this.getElement("infobar-tagname").setTextContent(displayName);
this.getElement("infobar-id").setTextContent(id);
diff --git a/devtools/server/actors/stylesheets.js b/devtools/server/actors/stylesheets.js
index f20634e6c..7fcbca8c4 100644
--- a/devtools/server/actors/stylesheets.js
+++ b/devtools/server/actors/stylesheets.js
@@ -13,7 +13,6 @@ const events = require("sdk/event/core");
const protocol = require("devtools/shared/protocol");
const {LongStringActor} = require("devtools/server/actors/string");
const {fetch} = require("devtools/shared/DevToolsUtils");
-const {listenOnce} = require("devtools/shared/async-utils");
const {originalSourceSpec, mediaRuleSpec, styleSheetSpec,
styleSheetsSpec} = require("devtools/shared/specs/stylesheets");
const {SourceMapConsumer} = require("source-map");
@@ -251,7 +250,7 @@ var StyleSheetActor = protocol.ActorClassWithSpec(styleSheetSpec, {
},
destroy: function () {
- if (this._transitionTimeout) {
+ if (this._transitionTimeout && this.window) {
this.window.clearTimeout(this._transitionTimeout);
removePseudoClassLock(
this.document.documentElement, TRANSITION_PSEUDO_CLASS);
@@ -801,6 +800,64 @@ var StyleSheetsActor = protocol.ActorClassWithSpec(styleSheetsSpec, {
protocol.Actor.prototype.initialize.call(this, null);
this.parentActor = tabActor;
+
+ this._onNewStyleSheetActor = this._onNewStyleSheetActor.bind(this);
+ this._onSheetAdded = this._onSheetAdded.bind(this);
+ this._onWindowReady = this._onWindowReady.bind(this);
+
+ events.on(this.parentActor, "stylesheet-added", this._onNewStyleSheetActor);
+ events.on(this.parentActor, "window-ready", this._onWindowReady);
+
+ // We listen for StyleSheetApplicableStateChanged rather than
+ // StyleSheetAdded, because the latter will be sent before the
+ // rules are ready. Using the former (with a check to ensure that
+ // the sheet is enabled) ensures that the sheet is ready before we
+ // try to make an actor for it.
+ this.parentActor.chromeEventHandler
+ .addEventListener("StyleSheetApplicableStateChanged", this._onSheetAdded, true);
+
+ // This is used when creating a new style sheet, so that we can
+ // pass the correct flag when emitting our stylesheet-added event.
+ // See addStyleSheet and _onNewStyleSheetActor for more details.
+ this._nextStyleSheetIsNew = false;
+ },
+
+ destroy: function () {
+ for (let win of this.parentActor.windows) {
+ // This flag only exists for devtools, so we are free to clear
+ // it when we're done.
+ win.document.styleSheetChangeEventsEnabled = false;
+ }
+
+ events.off(this.parentActor, "stylesheet-added", this._onNewStyleSheetActor);
+ events.off(this.parentActor, "window-ready", this._onWindowReady);
+
+ this.parentActor.chromeEventHandler.removeEventListener("StyleSheetAdded",
+ this._onSheetAdded, true);
+
+ protocol.Actor.prototype.destroy.call(this);
+ },
+
+ /**
+ * Event handler that is called when a the tab actor emits window-ready.
+ *
+ * @param {Event} evt
+ * The triggering event.
+ */
+ _onWindowReady: function (evt) {
+ this._addStyleSheets(evt.window);
+ },
+
+ /**
+ * Event handler that is called when a the tab actor emits stylesheet-added.
+ *
+ * @param {StyleSheetActor} actor
+ * The new style sheet actor.
+ */
+ _onNewStyleSheetActor: function (actor) {
+ // Forward it to the client side.
+ events.emit(this, "stylesheet-added", actor, this._nextStyleSheetIsNew);
+ this._nextStyleSheetIsNew = false;
},
/**
@@ -808,23 +865,11 @@ var StyleSheetsActor = protocol.ActorClassWithSpec(styleSheetsSpec, {
* all the style sheets in this document.
*/
getStyleSheets: Task.async(function* () {
- // Iframe document can change during load (bug 1171919). Track their windows
- // instead.
- let windows = [this.window];
let actors = [];
- for (let win of windows) {
+ for (let win of this.parentActor.windows) {
let sheets = yield this._addStyleSheets(win);
actors = actors.concat(sheets);
-
- // Recursively handle style sheets of the documents in iframes.
- for (let iframe of win.document.querySelectorAll("iframe, browser, frame")) {
- if (iframe.contentDocument && iframe.contentWindow) {
- // Sometimes, iframes don't have any document, like the
- // one that are over deeply nested (bug 285395)
- windows.push(iframe.contentWindow);
- }
- }
}
return actors;
}),
@@ -832,15 +877,13 @@ var StyleSheetsActor = protocol.ActorClassWithSpec(styleSheetsSpec, {
/**
* Check if we should be showing this stylesheet.
*
- * @param {Document} doc
- * Document for which we're checking
* @param {DOMCSSStyleSheet} sheet
* Stylesheet we're interested in
*
* @return boolean
* Whether the stylesheet should be listed.
*/
- _shouldListSheet: function (doc, sheet) {
+ _shouldListSheet: function (sheet) {
// Special case about:PreferenceStyleSheet, as it is generated on the
// fly and the URI is not registered with the about: handler.
// https://bugzilla.mozilla.org/show_bug.cgi?id=935803#c37
@@ -852,6 +895,22 @@ var StyleSheetsActor = protocol.ActorClassWithSpec(styleSheetsSpec, {
},
/**
+ * Event handler that is called when a new style sheet is added to
+ * a document. In particular, StyleSheetApplicableStateChanged is
+ * listened for, because StyleSheetAdded is sent too early, before
+ * the rules are ready.
+ *
+ * @param {Event} evt
+ * The triggering event.
+ */
+ _onSheetAdded: function (evt) {
+ let sheet = evt.stylesheet;
+ if (this._shouldListSheet(sheet)) {
+ this.parentActor.createStyleSheetActor(sheet);
+ }
+ },
+
+ /**
* Add all the stylesheets for the document in this window to the map and
* create an actor for each one if not already created.
*
@@ -865,24 +924,16 @@ var StyleSheetsActor = protocol.ActorClassWithSpec(styleSheetsSpec, {
{
return Task.spawn(function* () {
let doc = win.document;
- // readyState can be uninitialized if an iframe has just been created but
- // it has not started to load yet.
- if (doc.readyState === "loading" || doc.readyState === "uninitialized") {
- // Wait for the document to load first.
- yield listenOnce(win, "DOMContentLoaded", true);
-
- // Make sure we have the actual document for this window. If the
- // readyState was initially uninitialized, the initial dummy document
- // was replaced with the actual document (bug 1171919).
- doc = win.document;
- }
+ // We have to set this flag in order to get the
+ // StyleSheetApplicableStateChanged events. See Document.webidl.
+ doc.styleSheetChangeEventsEnabled = true;
let isChrome = Services.scriptSecurityManager.isSystemPrincipal(doc.nodePrincipal);
let styleSheets = isChrome ? DOMUtils.getAllStyleSheets(doc) : doc.styleSheets;
let actors = [];
for (let i = 0; i < styleSheets.length; i++) {
let sheet = styleSheets[i];
- if (!this._shouldListSheet(doc, sheet)) {
+ if (!this._shouldListSheet(sheet)) {
continue;
}
@@ -917,7 +968,7 @@ var StyleSheetsActor = protocol.ActorClassWithSpec(styleSheetsSpec, {
if (rule.type == Ci.nsIDOMCSSRule.IMPORT_RULE) {
// Associated styleSheet may be null if it has already been seen due
// to duplicate @imports for the same URL.
- if (!rule.styleSheet || !this._shouldListSheet(doc, rule.styleSheet)) {
+ if (!rule.styleSheet || !this._shouldListSheet(rule.styleSheet)) {
continue;
}
let actor = this.parentActor.createStyleSheetActor(rule.styleSheet);
@@ -948,6 +999,13 @@ var StyleSheetsActor = protocol.ActorClassWithSpec(styleSheetsSpec, {
* Object with 'styelSheet' property for form on new actor.
*/
addStyleSheet: function (text) {
+ // This is a bit convoluted. The style sheet actor may be created
+ // by a notification from platform. In this case, we can't easily
+ // pass the "new" flag through to createStyleSheetActor, so we set
+ // a flag locally and check it before sending an event to the
+ // client. See |_onNewStyleSheetActor|.
+ this._nextStyleSheetIsNew = true;
+
let parent = this.document.documentElement;
let style = this.document.createElementNS("http://www.w3.org/1999/xhtml", "style");
style.setAttribute("type", "text/css");
diff --git a/devtools/server/actors/webbrowser.js b/devtools/server/actors/webbrowser.js
index 1808895b1..dffe49b91 100644
--- a/devtools/server/actors/webbrowser.js
+++ b/devtools/server/actors/webbrowser.js
@@ -2501,7 +2501,11 @@ DebuggerProgressListener.prototype = {
if (isWindow && isStop) {
// Don't dispatch "navigate" event just yet when there is a redirect to
// about:neterror page.
- if (request.status != Cr.NS_OK) {
+ // Navigating to about:neterror will make `status` be something else than NS_OK.
+ // But for some error like NS_BINDING_ABORTED we don't want to emit any `navigate`
+ // event as the page load has been cancelled and the related page document is going
+ // to be a dead wrapper.
+ if (request.status != Cr.NS_OK && request.status != Cr.NS_BINDING_ABORTED) {
// Instead, listen for DOMContentLoaded as about:neterror is loaded
// with LOAD_BACKGROUND flags and never dispatches load event.
// That may be the same reason why there is no onStateChange event
diff --git a/devtools/shared/gcli/commands/cmd.js b/devtools/shared/gcli/commands/cmd.js
index 1777ed960..1b3cb2ecb 100644
--- a/devtools/shared/gcli/commands/cmd.js
+++ b/devtools/shared/gcli/commands/cmd.js
@@ -137,8 +137,6 @@ exports.items = [
return !prefBranch.prefHasUserValue(PREF_DIR);
},
exec: function(args, context) {
- gcli.load();
-
let dirName = prefBranch.getComplexValue(PREF_DIR,
Ci.nsISupportsString).data.trim();
return l10n.lookupFormat("cmdStatus3", [ dirName ]);
@@ -170,8 +168,6 @@ exports.items = [
supportsString.data = args.directory;
prefBranch.setComplexValue(PREF_DIR, Ci.nsISupportsString, supportsString);
- gcli.load();
-
return l10n.lookupFormat("cmdStatus3", [ args.directory ]);
}
}
diff --git a/devtools/shared/gcli/commands/cookie.js b/devtools/shared/gcli/commands/cookie.js
index f1680042f..203ac738c 100644
--- a/devtools/shared/gcli/commands/cookie.js
+++ b/devtools/shared/gcli/commands/cookie.js
@@ -100,7 +100,7 @@ exports.items = [
let cookies = [];
while (enm.hasMoreElements()) {
- let cookie = enm.getNext().QueryInterface(Ci.nsICookie);
+ let cookie = enm.getNext().QueryInterface(Ci.nsICookie2);
if (isCookieAtHost(cookie, host)) {
cookies.push({
host: cookie.host,
@@ -108,9 +108,10 @@ exports.items = [
value: cookie.value,
path: cookie.path,
expires: cookie.expires,
- secure: cookie.secure,
- httpOnly: cookie.httpOnly,
- sameDomain: cookie.sameDomain
+ isDomain: cookie.isDomain,
+ isHttpOnly: cookie.isHttpOnly,
+ isSecure: cookie.isSecure,
+ isSession: cookie.isSession,
});
}
}
@@ -169,11 +170,14 @@ exports.items = [
for (let cookie of cookies) {
cookie.expires = translateExpires(cookie.expires);
- let noAttrs = !cookie.secure && !cookie.httpOnly && !cookie.sameDomain;
- cookie.attrs = (cookie.secure ? "secure" : " ") +
- (cookie.httpOnly ? "httpOnly" : " ") +
- (cookie.sameDomain ? "sameDomain" : " ") +
- (noAttrs ? l10n.lookup("cookieListOutNone") : " ");
+ let noAttrs = !cookie.isDomain && !cookie.isHttpOnly &&
+ !cookie.isSecure && !cookie.isSession;
+ cookie.attrs = ((cookie.isDomain ? "isDomain " : "") +
+ (cookie.isHttpOnly ? "isHttpOnly " : "") +
+ (cookie.isSecure ? "isSecure " : "") +
+ (cookie.isSession ? "isSession " : "") +
+ (noAttrs ? l10n.lookup("cookieListOutNone") : ""))
+ .trim();
}
return context.createView({
diff --git a/devtools/shared/gcli/commands/screenshot.js b/devtools/shared/gcli/commands/screenshot.js
index e2f38b6d9..11bafcab9 100644
--- a/devtools/shared/gcli/commands/screenshot.js
+++ b/devtools/shared/gcli/commands/screenshot.js
@@ -277,6 +277,12 @@ function createScreenshotData(document, args) {
window.scrollTo(0,0);
width = window.innerWidth + window.scrollMaxX - window.scrollMinX;
height = window.innerHeight + window.scrollMaxY - window.scrollMinY;
+ let writingMode = "horizontal-tb";
+ if (window.getComputedStyle(document.documentElement)) {
+ writingMode = window.getComputedStyle(document.documentElement).writingMode;
+ }
+ let orientation = writingMode.substring(0, writingMode.indexOf("-")).toLowerCase();
+ left = ((orientation != "vertical") ? left : (-width + window.innerWidth));
filename = filename.replace(".png", "-fullpage.png");
}
else if (args.selector) {
diff --git a/devtools/shared/gcli/source/lib/gcli/commands/help.js b/devtools/shared/gcli/source/lib/gcli/commands/help.js
index 7d1cc9087..365c53380 100644
--- a/devtools/shared/gcli/source/lib/gcli/commands/help.js
+++ b/devtools/shared/gcli/source/lib/gcli/commands/help.js
@@ -275,7 +275,7 @@ exports.items = [
' <div if="${command.isParent}">\n' +
' <p class="gcli-help-header">${l10n.subCommands}:</p>\n' +
' <ul class="gcli-help-${subcommands}">\n' +
- ' <li if="${subcommands.length === 0}">${l10n.subcommandsNone}</li>\n' +
+ ' <li if="${subcommands.length === 0}">${l10n.subCommandsNone}</li>\n' +
' <li foreach="subcommand in ${subcommands}">\n' +
' ${subcommand.name}: ${subcommand.description}\n' +
' <span class="gcli-out-shortcut" data-command="help ${subcommand.name}"\n' +
@@ -321,7 +321,7 @@ exports.items = [
'\n' +
'<span if="${command.isParent}"># ${l10n.subCommands}:</span>\n' +
'\n' +
- '<span if="${subcommands.length === 0}">${l10n.subcommandsNone}</span>\n' +
+ '<span if="${subcommands.length === 0}">${l10n.subCommandsNone}</span>\n' +
'<loop foreach="subcommand in ${subcommands}">* ${subcommand.name}: ${subcommand.description}\n' +
'</loop>\n' +
'</div>\n',
diff --git a/devtools/shared/inspector/css-logic.js b/devtools/shared/inspector/css-logic.js
index c8cdd2fdb..901b7a189 100644
--- a/devtools/shared/inspector/css-logic.js
+++ b/devtools/shared/inspector/css-logic.js
@@ -30,6 +30,8 @@
"use strict";
+const MAX_DATA_URL_LENGTH = 40;
+
/**
* Provide access to the style information in a page.
* CssLogic uses the standard DOM API, and the Gecko inIDOMUtils API to access
@@ -103,6 +105,13 @@ exports.shortSource = function (sheet) {
return exports.l10n("rule.sourceInline");
}
+ // If the sheet is a data URL, return a trimmed version of it.
+ let dataUrl = sheet.href.trim().match(/^data:.*?,((?:.|\r|\n)*)$/);
+ if (dataUrl) {
+ return dataUrl[1].length > MAX_DATA_URL_LENGTH ?
+ `${dataUrl[1].substr(0, MAX_DATA_URL_LENGTH - 1)}…` : dataUrl[1];
+ }
+
// We try, in turn, the filename, filePath, query string, whole thing
let url = {};
try {
@@ -123,8 +132,7 @@ exports.shortSource = function (sheet) {
return url.query;
}
- let dataUrl = sheet.href.match(/^(data:[^,]*),/);
- return dataUrl ? dataUrl[1] : sheet.href;
+ return sheet.href;
};
const TAB_CHARS = "\t";
diff --git a/devtools/shared/locales/en-US/gcli.properties b/devtools/shared/locales/en-US/gcli.properties
index e5231e44a..59c344832 100644
--- a/devtools/shared/locales/en-US/gcli.properties
+++ b/devtools/shared/locales/en-US/gcli.properties
@@ -141,9 +141,10 @@ helpManual=Provide help either on a specific command (if a search string is prov
helpSearchDesc=Search string
helpSearchManual3=search string to use in narrowing down the displayed commands. Regular expressions not supported.
-# LOCALIZATION NOTE: These strings are displayed in the help page for a
-# command in the console.
+# LOCALIZATION NOTE (helpManSynopsis, helpManDescription): These strings are
+# displayed in the help page for a command in the console.
helpManSynopsis=Synopsis
+helpManDescription=Description
# LOCALIZATION NOTE: This message is displayed in the help page if the command
# has no parameters.
@@ -177,6 +178,10 @@ helpIntro=GCLI is an experiment to create a highly usable command line for web d
# sub-commands.
subCommands=Sub-Commands
+# LOCALIZATION NOTE: Text shown as part of the output of the 'help' command
+# when the command in question should have sub-commands but in fact has none.
+subCommandsNone=None
+
# LOCALIZATION NOTE: This error message is displayed when the command line is
# cannot find a match for the parse types.
commandParseError=Command line parsing error
diff --git a/devtools/shared/specs/stylesheets.js b/devtools/shared/specs/stylesheets.js
index c89a7c088..fc75281f8 100644
--- a/devtools/shared/specs/stylesheets.js
+++ b/devtools/shared/specs/stylesheets.js
@@ -105,6 +105,14 @@ exports.styleSheetSpec = styleSheetSpec;
const styleSheetsSpec = generateActorSpec({
typeName: "stylesheets",
+ events: {
+ "stylesheet-added": {
+ type: "stylesheetAdded",
+ sheet: Arg(0, "stylesheet"),
+ isNew: Arg(1, "boolean")
+ },
+ },
+
methods: {
getStyleSheets: {
request: {},
diff --git a/devtools/shared/webconsole/network-helper.js b/devtools/shared/webconsole/network-helper.js
index af6a2e55b..4e25fac26 100644
--- a/devtools/shared/webconsole/network-helper.js
+++ b/devtools/shared/webconsole/network-helper.js
@@ -63,6 +63,8 @@ const {components, Cc, Ci} = require("chrome");
loader.lazyImporter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm");
const DevToolsUtils = require("devtools/shared/DevToolsUtils");
const Services = require("Services");
+const { LocalizationHelper } = require("devtools/shared/l10n");
+const L10N = new LocalizationHelper("devtools/client/locales/netmonitor.properties");
// The cache used in the `nsIURL` function.
const gNSURLStore = new Map();
@@ -620,6 +622,31 @@ var NetworkHelper = {
// Cipher suite.
info.cipherSuite = SSLStatus.cipherName;
+ // Key exchange group name.
+ info.keaGroupName = SSLStatus.keaGroupName;
+ // Localise two special values.
+ if (info.keaGroupName == "none") {
+ info.keaGroupName = L10N.getStr("netmonitor.security.keaGroup.none");
+ }
+ if (info.keaGroupName == "custom") {
+ info.keaGroupName = L10N.getStr("netmonitor.security.keaGroup.custom");
+ }
+ if (info.keaGroupName == "unknown group") {
+ info.keaGroupName = L10N.getStr("netmonitor.security.keaGroup.unknown");
+ }
+
+ // Certificate signature scheme.
+ info.signatureSchemeName = SSLStatus.signatureSchemeName;
+ // Localise two special values.
+ if (info.signatureSchemeName == "none") {
+ info.signatureSchemeName =
+ L10N.getStr("netmonitor.security.signatureScheme.none");
+ }
+ if (info.signatureSchemeName == "unknown signature") {
+ info.signatureSchemeName =
+ L10N.getStr("netmonitor.security.signatureScheme.unknown");
+ }
+
// Protocol version.
info.protocolVersion =
this.formatSecurityProtocol(SSLStatus.protocolVersion);
diff --git a/devtools/shared/webconsole/network-monitor.js b/devtools/shared/webconsole/network-monitor.js
index 084493432..a55162f52 100644
--- a/devtools/shared/webconsole/network-monitor.js
+++ b/devtools/shared/webconsole/network-monitor.js
@@ -732,7 +732,9 @@ NetworkMonitor.prototype = {
0x804b0004: "STATUS_CONNECTED_TO",
0x804b0005: "STATUS_SENDING_TO",
0x804b000a: "STATUS_WAITING_FOR",
- 0x804b0006: "STATUS_RECEIVING_FROM"
+ 0x804b0006: "STATUS_RECEIVING_FROM",
+ 0x804b000c: "STATUS_TLS_STARTING",
+ 0x804b000d: "STATUS_TLS_ENDING"
},
httpDownloadActivities: [
@@ -1327,8 +1329,24 @@ NetworkMonitor.prototype = {
let response = {};
response.httpVersion = statusLineArray.shift();
- response.remoteAddress = httpActivity.channel.remoteAddress;
- response.remotePort = httpActivity.channel.remotePort;
+ // XXX:
+ // Sometimes, when using a proxy server (manual proxy configuration),
+ // throws an error:
+ // 0x80040111 (NS_ERROR_NOT_AVAILABLE)
+ // [nsIHttpChannelInternal.remoteAddress]
+ // Bug 1337791 is the suspect.
+ response.remoteAddress = null;
+ try {
+ response.remoteAddress = httpActivity.channel.remoteAddress;
+ } catch (e) {
+ Cu.reportError(e);
+ }
+ response.remotePort = null;
+ try {
+ response.remotePort = httpActivity.channel.remotePort;
+ } catch (e) {
+ Cu.reportError(e);
+ }
response.status = statusLineArray.shift();
response.statusText = statusLineArray.join(" ");
response.headersSize = extraStringData.length;
@@ -1390,6 +1408,7 @@ NetworkMonitor.prototype = {
timings: {
blocked: 0,
dns: 0,
+ ssl: 0,
connect: 0,
send: 0,
wait: 0,
@@ -1424,6 +1443,36 @@ NetworkMonitor.prototype = {
harTimings.connect = -1;
}
+ if (timings.STATUS_TLS_STARTING && timings.STATUS_TLS_ENDING) {
+ harTimings.ssl = timings.STATUS_TLS_ENDING.last -
+ timings.STATUS_TLS_STARTING.first;
+ } else {
+ harTimings.ssl = -1;
+ }
+
+ // sometimes the connection information events are attached to a speculative
+ // channel instead of this one, but necko might glue them back together in the
+ // nsITimedChannel interface used by Resource and Navigation Timing
+ let timedChannel = httpActivity.channel.QueryInterface(Ci.nsITimedChannel);
+
+ if ((harTimings.connect <= 0) && timedChannel) {
+ if (timedChannel.secureConnectionStartTime > timedChannel.connectStartTime) {
+ harTimings.connect =
+ timedChannel.secureConnectionStartTime - timedChannel.connectStartTime;
+ harTimings.ssl =
+ timedChannel.connectEndTime - timedChannel.secureConnectionStartTime;
+ } else {
+ harTimings.connect =
+ timedChannel.connectEndTime - timedChannel.connectStartTime;
+ harTimings.ssl = -1;
+ }
+ }
+
+ if ((harTimings.dns <= 0) && timedChannel) {
+ harTimings.dns =
+ timedChannel.domainLookupEndTime - timedChannel.domainLookupStartTime;
+ }
+
if (timings.STATUS_SENDING_TO) {
harTimings.send = timings.STATUS_SENDING_TO.last - timings.STATUS_SENDING_TO.first;
} else if (timings.REQUEST_HEADER && timings.REQUEST_BODY_SENT) {