Merge autoland to mozilla-central. a=merge

This commit is contained in:
Andreea Pavel 2018-04-14 00:55:22 +03:00
commit bdf94120d1
542 changed files with 2943 additions and 36644 deletions

View File

@ -626,10 +626,18 @@ html|input.urlbar-input[textoverflow]:not([focused]) {
#PopupAutoCompleteRichResult > richlistbox > richlistitem > .ac-tags:not([empty]),
#PopupAutoCompleteRichResult > richlistbox > richlistitem > .ac-separator:not([type=keyword]),
#PopupAutoCompleteRichResult > richlistbox > richlistitem > .ac-url:not([actiontype]),
#PopupAutoCompleteRichResult > richlistbox > richlistitem > .ac-action[actiontype] {
#PopupAutoCompleteRichResult > richlistbox > richlistitem > .ac-action[actiontype],
#PopupAutoCompleteRichResult > richlistbox > richlistitem[selected] > .ac-url[actiontype=remotetab],
#PopupAutoCompleteRichResult > richlistbox > richlistitem:hover > .ac-url[actiontype=remotetab]
{
display: -moz-box;
}
#PopupAutoCompleteRichResult > richlistbox > richlistitem[selected] > .ac-action[actiontype=remotetab],
#PopupAutoCompleteRichResult > richlistbox > richlistitem:hover > .ac-action[actiontype=remotetab] {
display: none;
}
/* Only show the "Search with" label on hover or selection. */
#PopupAutoCompleteRichResult > richlistbox > richlistitem:not([selected]):not(:hover) > .ac-action[actiontype=searchengine],
#PopupAutoCompleteRichResult > richlistbox > richlistitem:not([selected]):not(:hover) > .ac-separator[actiontype=searchengine] {

View File

@ -4,9 +4,6 @@
# 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/.
with Files("**"):
BUG_COMPONENT = ("Firefox", "General")
BROWSER_CHROME_MANIFESTS += [
'browser/browser.ini',
'browser/disable_app_update/browser.ini',

View File

@ -30,6 +30,7 @@
"memory",
"privacy",
"restyle",
"screenshots",
"stackwalk",
"tasktracer",
"threads",

View File

@ -483,7 +483,7 @@ var gSearchResultsPane = {
return null;
}
if (refAttr) {
let attr = msg.attrs.find(a => a.name === refAttr);
let attr = msg.attributes && msg.attributes.find(a => a.name === refAttr);
if (!attr) {
console.error(`Missing search l10n id "${refId}.${refAttr}"`);
return null;

View File

@ -458,7 +458,7 @@ var gMainPane = {
document.l10n.setAttributes(
document.getElementById("updateAppInfo"),
"update-application-info",
"update-application-version",
{ version }
);

View File

@ -442,7 +442,7 @@
<hbox align="center">
<vbox flex="1">
<description id="updateAppInfo">
<html:a id="releasenotes" class="learnMore text-link" hidden="true"/>
<html:a id="releasenotes" data-l10n-name="learn-more" class="learnMore text-link" hidden="true"/>
</description>
<description id="distribution" class="text-blurb" hidden="true"/>
<description id="distributionId" class="text-blurb" hidden="true"/>
@ -615,8 +615,8 @@
</menulist>
</hbox>
<description id="contentProcessCountEnabledDescription" class="tip-caption" data-l10n-id="performance-limit-content-process-enabled-desc"/>
<description id="contentProcessCountDisabledDescription" class="tip-caption" data-l10n-id="performance-limit-content-process-disabled-desc">
<html:a class="text-link" href="https://wiki.mozilla.org/Electrolysis"/>
<description id="contentProcessCountDisabledDescription" class="tip-caption" data-l10n-id="performance-limit-content-process-blocked-desc">
<html:a class="text-link" data-l10n-name="learn-more" href="https://wiki.mozilla.org/Electrolysis"/>
</description>
</vbox>
</groupbox>

View File

@ -281,8 +281,8 @@
<vbox>
<hbox align="start">
<vbox flex="1">
<description data-l10n-id="tracking-description">
<a id="trackingProtectionLearnMore" target="_blank" class="learnMore text-link"/>
<description data-l10n-id="tracking-desc">
<a id="trackingProtectionLearnMore" data-l10n-name="learn-more" target="_blank" class="learnMore text-link"/>
</description>
</vbox>
<spacer flex="1"/>

View File

@ -15,11 +15,11 @@
data-category="paneSearchResults"
hidden="true">
<vbox class="no-results-container">
<label id="sorry-message" data-l10n-id="search-results-sorry-message">
<html:span id="sorry-message-query"/>
<label id="sorry-message" data-l10n-id="search-results-empty-message">
<html:span data-l10n-name="query" id="sorry-message-query"/>
</label>
<label id="need-help" data-l10n-id="search-results-need-help">
<a class="text-link" target="_blank"></a>
<label id="need-help" data-l10n-id="search-results-help-link">
<a class="text-link" data-l10n-name="url" target="_blank"></a>
</label>
</vbox>
<vbox class="no-results-container" align="center">

View File

@ -39,7 +39,7 @@ add_task(async function() {
Assert.deepEqual(msg, {
value: null,
attrs: [
attributes: [
{name: "label", value: elem.getAttribute("label")}
]
});

View File

@ -5,6 +5,8 @@ const NONREPORTABLE_PAGE = "about:mozilla";
/* Test that the Report Site Issue button is enabled for http and https tabs,
on page load, or TabSelect, and disabled for everything else. */
add_task(async function test_button_state_disabled() {
await SpecialPowers.pushPrefEnv({set: [[PREF_WC_REPORTER_ENABLED, true]]});
let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, REPORTABLE_PAGE);
await openPageActions();
is(isButtonDisabled(), false, "Check that button is enabled for reportable schemes on tab load");
@ -25,6 +27,8 @@ add_task(async function test_button_state_disabled() {
/* Test that the button is enabled or disabled when we expected it to be, when
pinned to the URL bar. */
add_task(async function test_button_state_in_urlbar() {
await SpecialPowers.pushPrefEnv({set: [[PREF_WC_REPORTER_ENABLED, true]]});
pinToURLBar();
let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, REPORTABLE_PAGE);
is(isURLButtonEnabled(), true, "Check that button (in urlbar) is enabled for reportable schemes on tab load");

View File

@ -4,7 +4,10 @@ add_task(async function test_opened_page() {
requestLongerTimeout(2);
// ./head.js sets the value for PREF_WC_REPORTER_ENDPOINT
await SpecialPowers.pushPrefEnv({set: [[PREF_WC_REPORTER_ENDPOINT, NEW_ISSUE_PAGE]]});
await SpecialPowers.pushPrefEnv({set: [
[PREF_WC_REPORTER_ENABLED, true],
[PREF_WC_REPORTER_ENDPOINT, NEW_ISSUE_PAGE]
]});
let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_PAGE);

View File

@ -80,14 +80,14 @@ restart-later = Restart Later
search-results-header = Search Results
# `<span></span>` will be replaced by the search term.
search-results-sorry-message =
# `<span data-l10n-name="query"></span>` will be replaced by the search term.
search-results-empty-message =
{ PLATFORM() ->
[windows] Sorry! There are no results in Options for “<span></span>”.
*[other] Sorry! There are no results in Preferences for “<span></span>”.
[windows] Sorry! There are no results in Options for “<span data-l10n-name="query"></span>”.
*[other] Sorry! There are no results in Preferences for “<span data-l10n-name="query"></span>”.
}
search-results-need-help = Need help? Visit <a>{ -brand-short-name } Support</a>
search-results-help-link = Need help? Visit <a data-l10n-name="url">{ -brand-short-name } Support</a>
## General Section
@ -267,7 +267,7 @@ update-application-title = { -brand-short-name } Updates
update-application-description = Keep { -brand-short-name } up to date for the best performance, stability, and security.
update-application-info = Version { $version } <a>What's new</a>
update-application-version = Version { $version } <a data-l10n-name="learn-more">Whats new</a>
update-history =
.label = Show Update History…
@ -315,7 +315,7 @@ performance-limit-content-process-option = Content process limit
.accesskey = l
performance-limit-content-process-enabled-desc = Additional content processes can improve performance when using multiple tabs, but will also use more memory.
performance-limit-content-process-disabled-desc = Modifying the number of content processes is only possible with multiprocess { -brand-short-name }. <a>Learn how to check if multiprocess is enabled</a>
performance-limit-content-process-blocked-desc = Modifying the number of content processes is only possible with multiprocess { -brand-short-name }. <a data-l10n-name="learn-more">Learn how to check if multiprocess is enabled</a>
# Variables:
# $num - default value of the `dom.ipc.processCount` pref.
@ -698,7 +698,7 @@ addressbar-suggestions-settings = Change preferences for search engine suggestio
tracking-header = Tracking Protection
tracking-description = Tracking Protection blocks online trackers that collect your browsing data across multiple websites. <a>Learn more about Tracking Protection and your privacy</a>
tracking-desc = Tracking Protection blocks online trackers that collect your browsing data across multiple websites. <a data-l10n-name="learn-more">Learn more about Tracking Protection and your privacy</a>
tracking-mode-label = Use Tracking Protection to block known trackers

View File

@ -99,14 +99,8 @@ Tools.webConsole = {
id: "webconsole",
accesskey: l10n("webConsoleCmd.accesskey"),
ordinal: 2,
oldWebConsoleURL: "chrome://devtools/content/webconsole/old/webconsole.xul",
newWebConsoleURL: "chrome://devtools/content/webconsole/webconsole.html",
get browserConsoleURL() {
if (Services.prefs.getBoolPref("devtools.browserconsole.new-frontend-enabled")) {
return "chrome://devtools/content/webconsole/browserconsole.xul";
}
return Tools.webConsole.oldWebConsoleURL;
},
url: "chrome://devtools/content/webconsole/webconsole.html",
browserConsoleURL: "chrome://devtools/content/webconsole/browserconsole.xul",
icon: "chrome://devtools/skin/images/tool-webconsole.svg",
label: l10n("ToolboxTabWebconsole.label"),
menuLabel: l10n("MenuWebconsole.label"),
@ -136,19 +130,6 @@ Tools.webConsole = {
return new WebConsolePanel(iframeWindow, toolbox);
}
};
function switchWebconsole() {
if (Services.prefs.getBoolPref("devtools.webconsole.new-frontend-enabled")) {
Tools.webConsole.url = Tools.webConsole.newWebConsoleURL;
} else {
Tools.webConsole.url = Tools.webConsole.oldWebConsoleURL;
}
}
switchWebconsole();
Services.prefs.addObserver(
"devtools.webconsole.new-frontend-enabled",
{ observe: switchWebconsole }
);
Tools.jsdebugger = {
id: "jsdebugger",

View File

@ -104,6 +104,7 @@ skip-if = e10s # Bug 1069044 - destroyInspector may hang during shutdown
[browser_toolbox_split_console.js]
[browser_toolbox_target.js]
[browser_toolbox_tabsswitch_shortcuts.js]
[browser_toolbox_telemetry_close.js]
[browser_toolbox_textbox_context_menu.js]
[browser_toolbox_theme.js]
[browser_toolbox_theme_registration.js]

View File

@ -0,0 +1,81 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const { Toolbox } = require("devtools/client/framework/toolbox");
const URL = "data:text/html;charset=utf8,browser_toolbox_telemetry_close.js";
const OPTOUT = Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTOUT;
const { SIDE, BOTTOM } = Toolbox.HostType;
const DATA = [
{
timestamp: null,
category: "devtools.main",
method: "close",
object: "tools",
value: null,
extra: {
host: "side",
width: "1440"
}
},
{
timestamp: null,
category: "devtools.main",
method: "close",
object: "tools",
value: null,
extra: {
host: "bottom",
width: "1440"
}
}
];
add_task(async function() {
// Let's reset the counts.
Services.telemetry.clearEvents();
// Ensure no events have been logged
const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true);
ok(!snapshot.parent, "No events have been logged for the main process");
await openAndCloseToolbox("webconsole", SIDE);
await openAndCloseToolbox("webconsole", BOTTOM);
checkResults();
});
async function openAndCloseToolbox(toolId, host) {
const tab = await addTab(URL);
const target = TargetFactory.forTab(tab);
const toolbox = await gDevTools.showToolbox(target, toolId);
await toolbox.switchHost(host);
await toolbox.destroy();
}
function checkResults() {
const snapshot = Services.telemetry.snapshotEvents(OPTOUT, true);
const events = snapshot.parent.filter(event => event[1] === "devtools.main" &&
event[2] === "close" &&
event[3] === "tools" &&
event[4] === null
);
for (let i in DATA) {
const [ timestamp, category, method, object, value, extra ] = events[i];
const expected = DATA[i];
// ignore timestamp
ok(timestamp > 0, "timestamp is greater than 0");
is(category, expected.category, "category is correct");
is(method, expected.method, "method is correct");
is(object, expected.object, "object is correct");
is(value, expected.value, "value is correct");
is(extra.host, expected.extra.host, "host is correct");
ok(extra.width > 0, "width is greater than 0");
}
}

View File

@ -372,11 +372,6 @@ OptionsPanel.prototype = {
// Labels for these new buttons are nightly only and mostly intended for working on
// devtools.
let prefDefinitions = [{
pref: "devtools.webconsole.new-frontend-enabled",
label: L10N.getStr("toolbox.options.enableNewConsole.label"),
id: "devtools-new-webconsole",
parentId: "webconsole-options"
}, {
pref: "devtools.debugger.new-debugger-frontend",
label: L10N.getStr("toolbox.options.enableNewDebugger.label"),
id: "devtools-new-debugger",

View File

@ -102,7 +102,6 @@ function setPrefDefaults() {
// Bug 1225160 - Using source maps with browser debugging can lead to a crash
Services.prefs.setBoolPref("devtools.debugger.source-maps-enabled", false);
Services.prefs.setBoolPref("devtools.debugger.new-debugger-frontend", true);
Services.prefs.setBoolPref("devtools.webconsole.new-frontend-enabled", true);
Services.prefs.setBoolPref("devtools.preference.new-panel-enabled", false);
Services.prefs.setBoolPref("layout.css.emulate-moz-box-with-flex", false);
}

View File

@ -2760,6 +2760,10 @@ Toolbox.prototype = {
let win = this.win;
this._telemetry.toolClosed("toolbox");
this._telemetry.recordEvent("devtools.main", "close", "tools", null, {
host: this._getTelemetryHostString(),
width: Math.ceil(win.outerWidth / 50) * 50
});
this._telemetry.destroy();
// Finish all outstanding tasks (which means finish destroying panels and

View File

@ -25,10 +25,8 @@
<script>
"use strict";
var host = document.querySelector("#shadow");
if (host.createShadowRoot) {
var root = host.createShadowRoot();
root.innerHTML = "<h3>Shadow DOM</h3><select multiple></select>";
}
var root = host.attachShadow({ mode: "open" });
root.innerHTML = "<h3>Shadow DOM</h3><select multiple></select>";
</script>
</body>
</html>

View File

@ -10,7 +10,6 @@ devtools.jar:
content/shared/widgets/VariablesView.xul (shared/widgets/VariablesView.xul)
content/webconsole/webconsole.html (webconsole/webconsole.html)
content/webconsole/browserconsole.xul (webconsole/browserconsole.xul)
* content/webconsole/old/webconsole.xul (webconsole/old/webconsole.xul)
* content/scratchpad/scratchpad.xul (scratchpad/scratchpad.xul)
content/scratchpad/scratchpad.js (scratchpad/scratchpad.js)
content/shared/splitview.css (shared/splitview.css)

View File

@ -207,7 +207,3 @@ toolbox.sourceMapSourceFailure=Error while fetching an original source: %1$S\nSo
# checkbox to enable the new debugger frontend. Displayed only in Nightly and local
# builds.
toolbox.options.enableNewDebugger.label=Enable new debugger frontend
# LOCALIZATION NOTE (toolbox.options.enableNewConsole.label): Label of the options panel
# checkbox to enable the new console frontend. Displayed only in Nightly and local builds.
toolbox.options.enableNewConsole.label=Enable new console frontend

View File

@ -1,96 +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/. -->
<!-- LOCALIZATION NOTE : FILE The correct localization of this file might be to
- keep it in English, or another language commonly spoken among web developers.
- You want to make that choice consistent across the developer tools.
- A good criteria is the language in which you'd find the best
- documentation on web development on the web. -->
<!ENTITY window.title "Web Console">
<!-- LOCALIZATION NOTE (openURL.label): You can see this string in the Web
- Console context menu. -->
<!ENTITY openURL.label "Open URL in New Tab">
<!ENTITY openURL.accesskey "T">
<!-- LOCALIZATION NOTE (btnPageNet.label): This string is used for the menu
- button that allows users to toggle the network logging output.
- This string and the following strings toggle various kinds of output
- filters. -->
<!ENTITY btnPageNet.label "Net">
<!ENTITY btnPageNet.tooltip "Log network access">
<!ENTITY btnPageNet.accesskey "N">
<!-- LOCALIZATION NOTE (btnPageNet.accesskeyMacOSX): This string is used as
- access key for the menu button that allows users to toggle the network
- logging output. On MacOSX accesskeys are available with Ctrl-*. Please make
- sure you do not use the following letters: A, E, N and P. These are used
- for editing commands in text inputs. -->
<!ENTITY btnPageNet.accesskeyMacOSX "t">
<!ENTITY btnPageCSS.label "CSS">
<!ENTITY btnPageCSS.tooltip2 "Log CSS errors and warnings">
<!ENTITY btnPageCSS.accesskey "C">
<!ENTITY btnPageJS.label "JS">
<!ENTITY btnPageJS.tooltip "Log JavaScript exceptions">
<!ENTITY btnPageJS.accesskey "J">
<!ENTITY btnPageSecurity.label "Security">
<!ENTITY btnPageSecurity.tooltip "Log security errors and warnings">
<!ENTITY btnPageSecurity.accesskey "u">
<!-- LOCALIZATION NOTE (btnPageLogging): This is used as the text of the
- the toolbar. It shows or hides messages that the web developer inserted on
- the page for debugging purposes, using calls such console.log() and
- console.error(). -->
<!ENTITY btnPageLogging.label "Logging">
<!ENTITY btnPageLogging.tooltip "Log messages sent to the window.console object">
<!ENTITY btnPageLogging.accesskey3 "L">
<!ENTITY btnConsoleErrors "Errors">
<!ENTITY btnConsoleInfo "Info">
<!ENTITY btnConsoleWarnings "Warnings">
<!ENTITY btnConsoleLog "Log">
<!ENTITY btnConsoleXhr "XHR">
<!ENTITY btnConsoleReflows "Reflows">
<!-- LOCALIZATION NOTE (btnServerLogging): This is used as the text of the
- the toolbar. It shows or hides messages that the web developer inserted on
- the page for debugging purposes, using calls on the HTTP server. -->
<!ENTITY btnServerLogging.label "Server">
<!ENTITY btnServerLogging.tooltip "Log messages received from a web server">
<!ENTITY btnServerLogging.accesskey "S">
<!ENTITY btnServerErrors "Errors">
<!ENTITY btnServerInfo "Info">
<!ENTITY btnServerWarnings "Warnings">
<!ENTITY btnServerLog "Log">
<!-- LOCALIZATION NODE (btnConsoleSharedWorkers) the term "Shared Workers"
- should not be translated. -->
<!ENTITY btnConsoleSharedWorkers "Shared Workers">
<!-- LOCALIZATION NODE (btnConsoleServiceWorkers) the term "Service Workers"
- should not be translated. -->
<!ENTITY btnConsoleServiceWorkers "Service Workers">
<!-- LOCALIZATION NODE (btnConsoleWindowlessWorkers) the term "Workers"
- should not be translated. -->
<!ENTITY btnConsoleWindowlessWorkers "Add-on or Chrome Workers">
<!ENTITY filterOutput.placeholder "Filter output">
<!ENTITY btnClear.label "Clear">
<!ENTITY btnClear.tooltip "Clear the Web Console output">
<!ENTITY btnClear.accesskey "r">
<!ENTITY fullZoomEnlargeCmd.commandkey "+">
<!ENTITY fullZoomEnlargeCmd.commandkey2 "="> <!-- + is above this key on many keyboards -->
<!ENTITY fullZoomEnlargeCmd.commandkey3 "">
<!ENTITY fullZoomReduceCmd.commandkey "-">
<!ENTITY fullZoomReduceCmd.commandkey2 "">
<!ENTITY fullZoomResetCmd.commandkey "0">
<!ENTITY fullZoomResetCmd.commandkey2 "">
<!ENTITY copyURLCmd.label "Copy Link Location">
<!ENTITY copyURLCmd.accesskey "a">
<!ENTITY closeCmd.key "W">
<!ENTITY findCmd.key "F">
<!ENTITY clearOutputCtrl.key "L">
<!ENTITY openInVarViewCmd.label "Open in Variables View">
<!ENTITY openInVarViewCmd.accesskey "V">
<!ENTITY storeAsGlobalVar.label "Store as global variable">
<!ENTITY storeAsGlobalVar.accesskey "S">

View File

@ -14,43 +14,17 @@ browserConsole.title=Browser Console
# %2$02S = minutes, %3$02S = seconds, %4$03S = milliseconds.
timestampFormat=%02S:%02S:%02S.%03S
helperFuncUnsupportedTypeError=Cant call pprint on this type of object.
# LOCALIZATION NOTE (NetworkPanel.durationMS): this string is used to
# show the duration between two network events (e.g request and response
# header or response header and response body). Parameters: %S is the duration.
NetworkPanel.durationMS=%Sms
ConsoleAPIDisabled=The Web Console logging API (console.log, console.info, console.warn, console.error) has been disabled by a script on this page.
# LOCALIZATION NOTE (webConsoleXhrIndicator): the indicator displayed before
# a URL in the Web Console that was requested using an XMLHttpRequest.
# Should probably be the same as &btnConsoleXhr; in webConsole.dtd
webConsoleXhrIndicator=XHR
# LOCALIZATION NOTE (webConsoleMixedContentWarning): the message displayed
# after a URL in the Web Console that has been flagged for Mixed Content (i.e.
# http content in an https page).
webConsoleMixedContentWarning=Mixed Content
# LOCALIZATION NOTE (webConsoleMoreInfoLabel): the more info tag displayed
# after security related web console messages.
webConsoleMoreInfoLabel=Learn More
# LOCALIZATION NOTE (scratchpad.linkText): the text used in the right hand
# side of the Web Console command line when JavaScript is being entered, to
# indicate how to jump into scratchpad mode.
scratchpad.linkText=Shift+RETURN - Open in Scratchpad
# LOCALIZATION NOTE (reflow.*): the console displays reflow activity.
# We can get 2 kind of lines: with JS link or without JS link. It looks like
# that:
# reflow: 12ms
# reflow: 12ms function foobar, file.js line 42
# The 2nd line, from "function" to the end of the line, is a link to the
# JavaScript debugger.
reflow.messageWithNoLink=reflow: %Sms
reflow.messageWithLink=reflow: %Sms\u0020
reflow.messageLinkText=function %1$S, %2$S line %3$S
# LOCALIZATION NOTE (stacktrace.anonymousFunction): this string is used to
# display JavaScript functions that have no given name - they are said to be
# anonymous. Test console.trace() in the webconsole.
@ -61,10 +35,6 @@ stacktrace.anonymousFunction=<anonymous>
# %S is the "Async Cause" of the frame.
stacktrace.asyncStack=(Async: %S)
# LOCALIZATION NOTE (timerStarted): this string is used to display the result
# of the console.time() call. Parameters: %S is the name of the timer.
timerStarted=%S: timer started
# LOCALIZATION NOTE (timeEnd): this string is used to display the result of
# the console.timeEnd() call. Parameters: %1$S is the name of the timer, %2$S
# is the number of milliseconds.
@ -92,19 +62,6 @@ timerAlreadyExists=Timer “%S” already exists.
timerDoesntExist=Timer “%S” doesnt exist.
timerJSError=Failed to process the timer name.
# LOCALIZATION NOTE (maxCountersExceeded): Error message shown when the maximum
# number of console.count()-counters was exceeded.
maxCountersExceeded=The maximum allowed number of counters in this page was exceeded.
# LOCALIZATION NOTE (longStringEllipsis): the string displayed after a long
# string. This string is clickable such that the rest of the string is
# retrieved from the server.
longStringEllipsis=[…]
# LOCALIZATION NOTE (longStringTooLong): the string displayed after the user
# tries to expand a long string.
longStringTooLong=The string you are trying to view is too long to be displayed by the Web Console.
# LOCALIZATION NOTE (connectionTimeout): message displayed when the Remote Web
# Console fails to connect to the server due to a timeout.
connectionTimeout=Connection timeout. Check the Error Console on both ends for potential error messages. Reopen the Web Console to try again.
@ -154,15 +111,6 @@ messageToggleDetails=Show/hide message details.
# you hover the arrow for expanding/collapsing the messages of a group.
groupToggle=Show/hide group.
# LOCALIZATION NOTE (emptySlotLabel): the text is displayed when an Array
# with empty slots is printed to the console.
# This is a semi-colon list of plural forms.
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
# #1 number of empty slots
# example: 1 empty slot
# example: 5 empty slots
emptySlotLabel=#1 empty slot;#1 empty slots
# LOCALIZATION NOTE (table.index, table.iterationIndex, table.key, table.value):
# the column header displayed in the console table widget.
table.index=(index)
@ -170,13 +118,6 @@ table.iterationIndex=(iteration index)
table.key=Key
table.value=Values
# LOCALIZATION NOTE (severity.error, severity.warn, severity.info, severity.log):
# tooltip for icons next to console output
severity.error=Error
severity.warn=Warning
severity.info=Info
severity.log=Log
# LOCALIZATION NOTE (level.error, level.warn, level.info, level.log, level.debug):
# tooltip for icons next to console output
level.error=Error
@ -216,12 +157,6 @@ webconsole.menu.openURL.accesskey=T
webconsole.menu.openInNetworkPanel.label=Open in Network Panel
webconsole.menu.openInNetworkPanel.accesskey=N
# LOCALIZATION NOTE (webconsole.menu.openInVarView.label)
# Label used for a context-menu item displayed for object/variable logs. Clicking on it
# opens the webconsole variable view for the logged variable.
webconsole.menu.openInVarView.label=Open in Variables View
webconsole.menu.openInVarView.accesskey=V
# LOCALIZATION NOTE (webconsole.menu.storeAsGlobalVar.label)
# Label used for a context-menu item displayed for object/variable logs. Clicking on it
# creates a new global variable pointing to the logged variable.

View File

@ -3,9 +3,11 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
const { PureComponent } = require("devtools/client/shared/vendor/react");
const { div, button } = require("devtools/client/shared/vendor/react-dom-factories");
const { PureComponent, createFactory } = require("devtools/client/shared/vendor/react");
const { div, button, p, span, img } = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const PerfSettings = createFactory(require("devtools/client/performance-new/components/PerfSettings.js"));
const { openLink } = require("devtools/client/shared/link");
/**
* The recordingState is one of the following:
@ -32,6 +34,7 @@ const LOCKED_BY_PRIVATE_BROWSING = "locked-by-private-browsing";
class Perf extends PureComponent {
static get propTypes() {
return {
toolbox: PropTypes.object.isRequired,
perfFront: PropTypes.object.isRequired,
receiveProfile: PropTypes.func.isRequired
};
@ -52,6 +55,8 @@ class Perf extends PureComponent {
this.handleProfilerStopping = this.handleProfilerStopping.bind(this);
this.handlePrivateBrowsingStarting = this.handlePrivateBrowsingStarting.bind(this);
this.handlePrivateBrowsingEnding = this.handlePrivateBrowsingEnding.bind(this);
this.settingsComponentCreated = this.settingsComponentCreated.bind(this);
this.handleLinkClick = this.handleLinkClick.bind(this);
}
componentDidMount() {
@ -114,6 +119,15 @@ class Perf extends PureComponent {
}
}
/**
* Store a reference to the settings component. This gives the <Perf> component
* access to the `.getRecordingSettings()` method. At this time the recording panel
* is not doing much state management, so this avoid the overhead of redux.
*/
settingsComponentCreated(settings) {
this.settings = settings;
}
getRecordingStateForTesting() {
return this.state.recordingState;
}
@ -235,12 +249,21 @@ class Perf extends PureComponent {
}
startRecording() {
const settings = this.settings;
if (!settings) {
console.error("Expected the PerfSettings panel to be rendered and available.");
return;
}
this.setState({
recordingState: REQUEST_TO_START_RECORDING,
// Reset this error state since it's no longer valid.
recordingUnexpectedlyStopped: false,
});
this.props.perfFront.startProfiler();
this.props.perfFront.startProfiler(
// Pull out the recording settings from the child component. This approach avoids
// using Redux as a state manager.
settings.getRecordingSettings()
);
}
async getProfileAndStopProfiler() {
@ -256,24 +279,16 @@ class Perf extends PureComponent {
this.props.perfFront.stopProfilerAndDiscardProfile();
}
render() {
renderButton() {
const { recordingState, isSupportedPlatform } = this.state;
// Handle the cases of platform support.
switch (isSupportedPlatform) {
case null:
// We don't know yet if this is a supported platform, wait for a response.
return null;
case false:
return renderButton({
label: "Start recording",
disabled: true,
additionalMessage: "Your platform is not supported. The Gecko Profiler only " +
"supports Tier-1 platforms."
});
case true:
// Continue on and render the panel.
break;
if (!isSupportedPlatform) {
return renderButton({
label: "Start recording",
disabled: true,
additionalMessage: "Your platform is not supported. The Gecko Profiler only " +
"supports Tier-1 platforms."
});
}
// TODO - L10N all of the messages. Bug 1418056
@ -284,7 +299,14 @@ class Perf extends PureComponent {
case AVAILABLE_TO_RECORD:
return renderButton({
onClick: this.startRecording,
label: "Start recording",
label: span(
null,
img({
className: "perf-button-image",
src: "chrome://devtools/skin/images/tool-profiler.svg"
}),
"Start recording",
),
additionalMessage: this.state.recordingUnexpectedlyStopped
? div(null, "The recording was stopped by another tool.")
: null
@ -329,6 +351,58 @@ class Perf extends PureComponent {
throw new Error("Unhandled recording state");
}
}
handleLinkClick(event) {
openLink(event.target.value, this.props.toolbox);
}
render() {
const { isSupportedPlatform } = this.state;
if (isSupportedPlatform === null) {
// We don't know yet if this is a supported platform, wait for a response.
return null;
}
return div(
{ className: "perf" },
this.renderButton(),
PerfSettings({ ref: this.settingsComponentCreated }),
div(
{ className: "perf-description" },
p(null,
"This new recording panel is a bit different from the existing " +
"performance panel. It records the entire browser, and then opens up " +
"and shares the profile with ",
button(
// Implement links as buttons to avoid any risk of loading the link in the
// the panel.
{
className: "perf-external-link",
value: "https://perf-html.io",
onClick: this.handleLinkClick
},
"perf-html.io"
),
", a Mozilla performance analysis tool."
),
p(null,
"This is still a prototype. Join along or file bugs at: ",
button(
// Implement links as buttons to avoid any risk of loading the link in the
// the panel.
{
className: "perf-external-link",
value: "https://github.com/devtools-html/perf.html",
onClick: this.handleLinkClick
},
"github.com/devtools-html/perf.html"
),
"."
)
),
);
}
}
module.exports = Perf;
@ -338,7 +412,7 @@ function renderButton(props) {
const nbsp = "\u00A0";
return div(
{ className: "perf" },
{ className: "perf-button-container" },
div({ className: "perf-additional-message" }, additionalMessage || nbsp),
div(
null,

View File

@ -0,0 +1,409 @@
/* 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 { PureComponent, createFactory } = require("devtools/client/shared/vendor/react");
const { div, details, summary, label, input, span, h2, section } = require("devtools/client/shared/vendor/react-dom-factories");
const Range = createFactory(require("devtools/client/performance-new/components/Range"));
const { makeExponentialScale, formatFileSize, calculateOverhead } = require("devtools/client/performance-new/utils");
// sizeof(double) + sizeof(char)
// http://searchfox.org/mozilla-central/rev/e8835f52eff29772a57dca7bcc86a9a312a23729/tools/profiler/core/ProfileEntry.h#73
const PROFILE_ENTRY_SIZE = 9;
const NOTCHES = Array(22).fill("discrete-level-notch");
const threadColumns = [
[
{
name: "GeckoMain",
title: "The main processes for both the parent process, and content processes"
},
{
name: "Compositor",
title: "Composites together different painted elements on the page."
},
{
name: "DOM Worker",
title: "This handle both web workers and service workers"
},
{
name: "Renderer",
title: "When WebRender is enabled, the thread that executes OpenGL calls"
},
],
[
{
name: "RenderBackend",
title: "The WebRender RenderBackend thread"
},
{
name: "PaintWorker",
title: "When off-main-thread painting is enabled, the thread on which " +
"painting happens"
},
{
name: "StyleThread",
title: "Style computation is split into multiple threads"
},
{
name: "Socket Thread",
title: "The thread where networking code runs any blocking socket calls"
},
],
[
{
name: "StreamTrans",
title: "TODO"
},
{
name: "ImgDecoder",
title: "Image decoding threads"
},
{
name: "DNS Resolver",
title: "DNS resolution happens on this thread"
},
]
];
const featureCheckboxes = [
{
name: "Native Stacks",
value: "stackwalk",
title: "Record native stacks (C++ and Rust). This is not available on all platforms.",
recommended: true
},
{
name: "JavaScript",
value: "js",
title: "Record JavaScript stack information, and interleave it with native stacks.",
recommended: true
},
{
name: "Java",
value: "java",
title: "Profile Java code (Android only)."
},
{
name: "Native Leaf Stack",
value: "leaf",
title: "Record the native memory address of the leaf-most stack. This could be " +
"useful on platforms that do not support stack walking."
},
{
name: "Main Thread IO",
value: "mainthreadio",
title: "Record main thread I/O markers."
},
{
name: "Memory",
value: "memory",
title: "Add memory measurements to the samples, this includes resident set size " +
"(RSS) and unique set size (USS)."
},
{
name: "Privacy",
value: "privacy",
title: "Remove some potentially user-identifiable information."
},
{
name: "JIT Optimizations",
value: "trackopts",
title: "Track JIT optimizations in the JS engine."
},
{
name: "TaskTracer",
value: "tasktracer",
title: "Enable TaskTracer (Experimental, requires custom build.)"
},
];
/**
* This component manages the settings for recording a performance profile. In addition
* to rendering the UI, it also manages the state of the settings. In order to not
* introduce the complexity of adding Redux to a relatively simple UI, this
* component expects to be accessed via the `ref`, and then calling
* `settings.getRecordingSettings()` to get out the settings. If the recording panel
* takes on new responsibilities, then this decision should be revisited.
*/
class PerfSettings extends PureComponent {
static get propTypes() {
return {};
}
constructor(props) {
super(props);
// Right now the defaults are reset every time the panel is opened. These should
// be persisted between sessions. See Bug 1453014.
this.state = {
interval: 1,
entries: 10000000, // 90MB
features: {
js: true,
stackwalk: true,
},
threads: "GeckoMain,Compositor",
threadListFocused: false,
};
this._handleThreadCheckboxChange = this._handleThreadCheckboxChange.bind(this);
this._handleFeaturesCheckboxChange = this._handleFeaturesCheckboxChange.bind(this);
this._handleThreadTextChange = this._handleThreadTextChange.bind(this);
this._handleThreadTextCleanup = this._handleThreadTextCleanup.bind(this);
this._renderThreadsColumns = this._renderThreadsColumns.bind(this);
this._onChangeInterval = this._onChangeInterval.bind(this);
this._onChangeEntries = this._onChangeEntries.bind(this);
this._intervalExponentialScale = makeExponentialScale(0.01, 100);
this._entriesExponentialScale = makeExponentialScale(100000, 100000000);
}
getRecordingSettings() {
const features = [];
for (const [name, isSet] of Object.entries(this.state.features)) {
if (isSet) {
features.push(name);
}
}
return {
entries: this.state.entries,
interval: this.state.interval,
features,
threads: _threadStringToList(this.state.threads)
};
}
_renderNotches() {
const { interval, entries, features } = this.state;
const overhead = calculateOverhead(interval, entries, features);
const notchCount = 22;
const notches = [];
for (let i = 0; i < notchCount; i++) {
const active = i <= Math.round(overhead * (NOTCHES.length - 1))
? "active" : "inactive";
let level = "normal";
if (i > 16) {
level = "critical";
} else if (i > 10) {
level = "warning";
}
notches.push(
div({
key: i,
className:
`perf-settings-notch perf-settings-notch-${level} ` +
`perf-settings-notch-${active}`
})
);
}
return notches;
}
_handleThreadCheckboxChange(event) {
const { checked, value } = event.target;
this.setState(state => {
let threadsList = _threadStringToList(state.threads);
if (checked) {
if (!threadsList.includes(value)) {
threadsList.push(value);
}
} else {
threadsList = threadsList.filter(thread => thread !== value);
}
return { threads: threadsList.join(",") };
});
}
_handleFeaturesCheckboxChange(event) {
const { checked, value } = event.target;
this.setState(state => ({
features: {...state.features, [value]: checked}
}));
}
_handleThreadTextChange(event) {
this.setState({ threads: event.target.value });
}
_handleThreadTextCleanup() {
this.setState(state => {
const threadsList = _threadStringToList(state.threads);
return { threads: threadsList.join(",") };
});
}
_renderThreadsColumns(threads, index) {
return div(
{ className: "perf-settings-thread-column", key: index },
threads.map(({name, title}) => label(
{
className: "perf-settings-checkbox-label",
key: name,
title
},
input({
className: "perf-settings-checkbox",
type: "checkbox",
value: name,
checked: this.state.threads.includes(name),
onChange: this._handleThreadCheckboxChange
}),
name
))
);
}
_renderThreads() {
return details(
{ className: "perf-settings-details" },
summary({ className: "perf-settings-summary" }, "Threads:"),
// Contain the overflow of the slide down animation with the first div.
div(
{ className: "perf-settings-details-contents" },
// Provide a second <div> element for the contents of the slide down animation.
div(
{ className: "perf-settings-details-contents-slider" },
div(
{ className: "perf-settings-thread-columns" },
threadColumns.map(this._renderThreadsColumns),
),
div(
{ className: "perf-settings-row" },
label(
{
className: "perf-settings-text-label",
title: "These thread names are a comma separated list that is used to " +
"enable profiling of the threads in the profiler. The name needs to " +
"be only a partial match of the thread name to be included. It " +
"is whitespace sensitive."
},
div({}, "Add custom threads by name:"),
input({
className: "perf-settings-text-input",
type: "text",
value: this.state.threads,
onChange: this._handleThreadTextChange,
onBlur: this._handleThreadTextCleanup,
})
)
)
)
)
);
}
_renderFeatures() {
return details(
{ className: "perf-settings-details" },
summary({ className: "perf-settings-summary" }, "Features:"),
div(
{ className: "perf-settings-details-contents" },
div(
{ className: "perf-settings-details-contents-slider" },
featureCheckboxes.map(({name, value, title, recommended}) => label(
{
className: "perf-settings-checkbox-label perf-settings-feature-label",
key: value,
},
input({
className: "perf-settings-checkbox",
type: "checkbox",
value,
checked: this.state.features[value],
onChange: this._handleFeaturesCheckboxChange
}),
div({ className: "perf-settings-feature-name" }, name),
div(
{ className: "perf-settings-feature-title" },
title,
recommended
? span(
{ className: "perf-settings-subtext" },
" (Recommended on by default.)"
)
: null
)
))
)
)
);
}
_onChangeInterval(interval) {
this.setState({ interval });
}
_onChangeEntries(entries) {
this.setState({ entries });
}
render() {
return section(
{ className: "perf-settings" },
h2({ className: "perf-settings-title" }, "Recording Settings"),
div(
{ className: "perf-settings-row" },
label({ className: "perf-settings-label" }, "Overhead:"),
div(
{ className: "perf-settings-value perf-settings-notches" },
this._renderNotches()
)
),
Range({
label: "Sampling interval:",
value: this.state.interval,
id: "perf-range-interval",
scale: this._intervalExponentialScale,
display: _intervalTextDisplay,
onChange: this._onChangeInterval
}),
Range({
label: "Buffer size:",
value: this.state.entries,
id: "perf-range-entries",
scale: this._entriesExponentialScale,
display: _entriesTextDisplay,
onChange: this._onChangeEntries
}),
this._renderThreads(),
this._renderFeatures()
);
}
}
/**
* Clean up the thread list string into a list of values.
* @param string threads, comma separated values.
* @return Array list of thread names
*/
function _threadStringToList(threads) {
return threads
// Split on commas
.split(",")
// Clean up any extraneous whitespace
.map(string => string.trim())
// Filter out any blank strings
.filter(string => string);
}
/**
* Format the interval number for display.
* @param {number} value
* @return {string}
*/
function _intervalTextDisplay(value) {
return `${value} ms`;
}
/**
* Format the entries number for display.
* @param {number} value
* @return {string}
*/
function _entriesTextDisplay(value) {
return formatFileSize(value * PROFILE_ENTRY_SIZE);
}
module.exports = PerfSettings;

View File

@ -0,0 +1,69 @@
/* 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 { PureComponent } = require("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const { div, input, label } = require("devtools/client/shared/vendor/react-dom-factories");
class Range extends PureComponent {
static get propTypes() {
return {
value: PropTypes.number.isRequired,
label: PropTypes.string.isRequired,
id: PropTypes.string.isRequired,
scale: PropTypes.object.isRequired,
onChange: PropTypes.func.isRequired,
display: PropTypes.func.isRequired,
};
}
constructor(props) {
super(props);
this.handleInput = this.handleInput.bind(this);
}
handleInput(e) {
e.preventDefault();
const { scale, onChange } = this.props;
const frac = e.target.value / 100;
onChange(scale.fromFractionToSingleDigitValue(frac));
}
render() {
const { label: labelText, scale, id, value, display } = this.props;
return (
div(
{ className: "perf-settings-row" },
label(
{
className: "perf-settings-label",
htmlFor: id
},
labelText
),
div(
{ className: "perf-settings-value" },
div(
{ className: "perf-settings-range-input" },
input({
type: "range",
className: `perf-settings-range-input-el`,
min: "0",
max: "100",
value: scale.fromValueToFraction(value) * 100,
onChange: this.handleInput,
id,
})
),
div(
{ className: `perf-settings-range-value`},
display(value)
)
)
)
);
}
}
module.exports = Range;

View File

@ -5,4 +5,6 @@
DevToolsModules(
'Perf.js',
'PerfSettings.js',
'Range.js',
)

View File

@ -19,10 +19,12 @@ const { createElement } = require("devtools/client/shared/vendor/react");
/**
* Perform a simple initialization on the panel. Hook up event listeners.
*
* @param toolbox - The toolbox
* @param perfFront - The Perf actor's front. Used to start and stop recordings.
*/
function gInit(perfFront) {
function gInit(toolbox, perfFront) {
const props = {
toolbox,
perfFront,
receiveProfile: profile => {
// Open up a new tab and send a message with the profile.

View File

@ -9,6 +9,7 @@ DIRS += [
DevToolsModules(
'panel.js',
'utils.js',
)
MOCHITEST_CHROME_MANIFESTS += ['test/chrome/chrome.ini']

View File

@ -35,7 +35,7 @@ class PerformancePanel {
this.isReady = true;
this.emit("ready");
this.panelWin.gInit(perfFront);
this.panelWin.gInit(this.toolbox, perfFront);
return this;
}

View File

@ -0,0 +1,162 @@
/* 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 UNITS = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
/**
* Linearly interpolate between values.
* https://en.wikipedia.org/wiki/Linear_interpolation
*
* @param {number} frac - Value ranged 0 - 1 to interpolate between the range
* start and range end.
* @param {number} rangeState - The value to start from.
* @param {number} rangeEnd - The value to interpolate to.
* @returns {number}
*/
function lerp(frac, rangeStart, rangeEnd) {
return (1 - frac) * rangeStart + frac * rangeEnd;
}
/**
* Make sure a value is clamped between a min and max value.
*
* @param {number} val - The value to clamp.
* @param {number} min - The minimum value.
* @returns {number}
*/
function clamp(val, min, max) {
return Math.max(min, Math.min(max, val));
}
/**
* Formats a file size.
* @param {number} num - The number (in bytes) to format.
* @returns {string} e.g. "10 B", "100 MB"
*/
function formatFileSize(num) {
if (!Number.isFinite(num)) {
throw new TypeError(`Expected a finite number, got ${typeof num}: ${num}`);
}
const neg = num < 0;
if (neg) {
num = -num;
}
if (num < 1) {
return (neg ? "-" : "") + num + " B";
}
const exponent = Math.min(
Math.floor(Math.log(num) / Math.log(1000)),
UNITS.length - 1
);
const numStr = Number((num / Math.pow(1000, exponent)).toPrecision(3));
const unit = UNITS[exponent];
return (neg ? "-" : "") + numStr + " " + unit;
}
/**
* Creates numbers that scale exponentially.
*
* @param {number} rangeStart
* @param {number} rangeEnd
*/
function makeExponentialScale(rangeStart, rangeEnd) {
const startExp = Math.log(rangeStart);
const endExp = Math.log(rangeEnd);
const fromFractionToValue = frac =>
Math.exp((1 - frac) * startExp + frac * endExp);
const fromValueToFraction = value =>
(Math.log(value) - startExp) / (endExp - startExp);
const fromFractionToSingleDigitValue = frac => {
return +fromFractionToValue(frac).toPrecision(1);
};
return {
// Takes a number ranged 0-1 and returns it within the range.
fromFractionToValue,
// Takes a number in the range, and returns a value between 0-1
fromValueToFraction,
// Takes a number ranged 0-1 and returns a value in the range, but with
// a single digit value.
fromFractionToSingleDigitValue,
};
}
/**
* Scale a source range to a destination range, but clamp it within the
* destination range.
* @param {number} val - The source range value to map to the destination range,
* @param {number} sourceRangeStart,
* @param {number} sourceRangeEnd,
* @param {number} destRangeStart,
* @param {number} destRangeEnd
*/
function scaleRangeWithClamping(
val,
sourceRangeStart,
sourceRangeEnd,
destRangeStart,
destRangeEnd
) {
const frac = clamp(
(val - sourceRangeStart) / (sourceRangeEnd - sourceRangeStart),
0,
1
);
return lerp(frac, destRangeStart, destRangeEnd);
}
/**
* Use some heuristics to guess at the overhead of the recording settings.
* @param {number} interval
* @param {number} bufferSize
* @param {object} features - Map of the feature name to a boolean.
*/
function calculateOverhead(interval, bufferSize, features) {
const overheadFromSampling =
scaleRangeWithClamping(
Math.log(interval),
Math.log(0.05),
Math.log(1),
1,
0
) +
scaleRangeWithClamping(
Math.log(interval),
Math.log(1),
Math.log(100),
0.1,
0
);
const overheadFromBuffersize = scaleRangeWithClamping(
Math.log(bufferSize),
Math.log(10),
Math.log(1000000),
0,
0.1
);
const overheadFromStackwalk = features.stackwalk ? 0.05 : 0;
const overheadFromJavaScrpt = features.js ? 0.05 : 0;
const overheadFromTaskTracer = features.tasktracer ? 0.05 : 0;
return clamp(
overheadFromSampling +
overheadFromBuffersize +
overheadFromStackwalk +
overheadFromJavaScrpt +
overheadFromTaskTracer,
0,
1
);
}
module.exports = {
formatFileSize,
makeExponentialScale,
scaleRangeWithClamping,
calculateOverhead
};

View File

@ -485,9 +485,15 @@ class Telemetry {
// If the pending event has not been created add the property to the pending
// list.
if (!PENDING_EVENTS.has(sig)) {
PENDING_EVENT_PROPERTIES.set(sig, {
[pendingPropName]: pendingPropValue
});
let props = PENDING_EVENT_PROPERTIES.get(sig);
if (props) {
props[pendingPropName] = pendingPropValue;
} else {
PENDING_EVENT_PROPERTIES.set(sig, {
[pendingPropName]: pendingPropValue
});
}
return;
}

View File

@ -17,7 +17,198 @@
font-size: 120%;
}
.perf-button-image {
vertical-align: text-top;
padding-inline-end: 4px;
}
.perf-additional-message {
margin: 10px;
margin-top: 65px;
}
.perf > * {
max-width: 440px;
}
.perf-description {
line-height: 1.4;
}
.perf-external-link {
margin: 0;
padding: 0;
background: none;
border: none;
color: var(--blue-60);
text-decoration: underline;
white-space: nowrap;
cursor: pointer;
}
/* Settings */
.perf-settings {
width: 100%;
margin: 50px 0 25px;
}
.perf-settings-title {
padding: 5px 10px;
margin-bottom: 15px;
background-color: var(--grey-10);
border: var(--grey-30) 1px solid;
font-size: 11px;
font-weight: normal;
}
.perf-settings-row {
display: flex;
overflow: hidden;
line-height: 1.8;
}
.perf-settings-controls > .tree {
height: 100%;
}
.perf-settings-row.focused {
background-color: var(--theme-selection-background);
color: var(--theme-selection-color);
}
.perf-settings-label {
height: 30px;
min-width: 110px;
}
.perf-settings-value {
display: flex;
flex: 1;
}
.perf-settings-range-input {
flex: 1;
}
.perf-settings-range-input-el {
width: 100%;
}
.perf-settings-range-value {
min-width: 70px;
text-align: end;
}
.perf-settings-notches {
height: 14px;
margin: 5px 0 10px;
margin-inline-start: 0.7em;
display: flex;
}
.perf-settings-notch {
margin-right: 1px;
flex: 1;
border: 1px solid rgba(0,0,0,0.2);
border-radius: 2px;
}
.perf-settings-notch-normal.perf-settings-notch-active {
border-color: hsl(90, 90%, 40%);
background-color: hsla(90, 90%, 40%, 0.5);
}
.perf-settings-notch-warning.perf-settings-notch-active {
border-color: hsl(45, 100%, 49%);
background-color: hsla(45, 100%, 49%, 0.5);
}
.perf-settings-notch-critical.perf-settings-notch-active {
border-color: hsl(0, 90%, 40%);
background-color: hsla(0, 90%, 40%, 0.5);
}
.perf-settings-text-input {
width: 100%;
padding: 4px;
box-sizing: border-box;
}
.perf-settings-text-label {
flex: 1;
}
.perf-settings-details-contents {
overflow: hidden;
}
.perf-settings-details-contents-slider {
padding: 10px;
margin: 0 0 18px;
border: var(--grey-20) 1px solid;
background-color: var(--grey-10);
opacity: 0;
transform: translateY(-100px);
transition-duration: 250ms;
transition-timing-function: cubic-bezier(.07,.95,0,1);
transition-property: transform, opacity;
}
.perf-settings-details[open] .perf-settings-details-contents-slider {
opacity: 1;
transform: translateY(0);
}
.perf-settings-summary {
height: 30px;
cursor: default;
-moz-user-select: none;
}
.perf-settings-thread-columns {
margin-bottom: 20px;
display: flex;
line-height: 2;
}
.perf-settings-thread-column {
flex: 1;
}
.perf-settings-checkbox-label {
display: block;
}
.perf-settings-feature-label {
margin: 16px 0;
display: flex;
}
.perf-settings-feature-label {
margin: 16px 0;
display: flex;
}
.perf-settings-checkbox {
align-self: flex-start;
}
.perf-settings-feature-name {
width: 150px;
color: var(--blue-60);
}
.perf-settings-feature-title {
flex: 1;
}
.perf-settings-feature-name {
width: 130px;
color: var(--blue-60);
line-height: 1.6;
}
.perf-settings-subtext {
font-weight: bold;
}

View File

@ -11,7 +11,6 @@ loader.lazyRequireGetter(this, "TargetFactory", "devtools/client/framework/targe
loader.lazyRequireGetter(this, "gDevToolsBrowser", "devtools/client/framework/devtools-browser", true);
loader.lazyRequireGetter(this, "Tools", "devtools/client/definitions", true);
loader.lazyRequireGetter(this, "Telemetry", "devtools/client/shared/telemetry");
loader.lazyRequireGetter(this, "WebConsoleFrame", "devtools/client/webconsole/old/webconsole", true);
loader.lazyRequireGetter(this, "NewWebConsoleFrame", "devtools/client/webconsole/new-webconsole", true);
loader.lazyRequireGetter(this, "gDevTools", "devtools/client/framework/devtools", true);
loader.lazyRequireGetter(this, "DebuggerServer", "devtools/server/main", true);
@ -193,10 +192,6 @@ HUD_SERVICE.prototype =
win.document.title = l10n.getStr("browserConsole.title");
if (browserConsoleURL === Tools.webConsole.oldWebConsoleURL) {
return {iframeWindow: win, chromeWindow: win};
}
let iframe = win.document.querySelector("iframe");
await new Promise(resolve => {
iframe.addEventListener("DOMContentLoaded", resolve, {once: true});
@ -273,11 +268,7 @@ function WebConsole(target, iframeWindow, chromeWindow) {
if (element.getAttribute("windowtype") != gDevTools.chromeWindowType) {
this.browserWindow = HUDService.currentContext();
}
if (iframeWindow.location.href === Tools.webConsole.newWebConsoleURL) {
this.ui = new NewWebConsoleFrame(this);
} else {
this.ui = new WebConsoleFrame(this);
}
this.ui = new NewWebConsoleFrame(this);
}
WebConsole.prototype = {
iframeWindow: null,

View File

@ -7,7 +7,6 @@
DIRS += [
'actions',
'components',
'old',
'reducers',
'selectors',
'test',

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +0,0 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
DIRS += [
'net',
]
DevToolsModules(
'console-output.js',
'jsterm.js',
'webconsole.js',
)
with Files('**'):
BUG_COMPONENT = ('Firefox', 'Developer Tools: Console')

View File

@ -1,20 +0,0 @@
"use strict";
module.exports = {
"globals": {
"Locale": true,
"Document": true,
"document": true,
"Node": true,
"Element": true,
"MessageEvent": true,
"BrowserLoader": true,
"addEventListener": true,
"DOMParser": true,
"dispatchEvent": true,
"setTimeout": true
},
"rules": {
"no-unused-vars": ["error", {"args": "none"}],
}
};

View File

@ -1,72 +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 { Component, createFactory } = require("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const NetInfoGroupList = createFactory(require("./net-info-group-list"));
/**
* This template represents 'Cookies' tab displayed when the user
* expands network log in the Console panel. It's responsible for rendering
* sent and received cookies.
*/
class CookiesTab extends Component {
static get propTypes() {
return {
actions: PropTypes.shape({
requestData: PropTypes.func.isRequired
}),
data: PropTypes.object.isRequired,
};
}
componentDidMount() {
let { actions, data } = this.props;
let requestCookies = data.request.cookies;
let responseCookies = data.response.cookies;
// TODO: use async action objects as soon as Redux is in place
if (!requestCookies || !requestCookies.length) {
actions.requestData("requestCookies");
}
if (!responseCookies || !responseCookies.length) {
actions.requestData("responseCookies");
}
}
render() {
let { data: file } = this.props;
let requestCookies = file.request.cookies;
let responseCookies = file.response.cookies;
// The cookie panel displays two groups of cookies:
// 1) Response Cookies
// 2) Request Cookies
let groups = [{
key: "responseCookies",
name: Locale.$STR("responseCookies"),
params: responseCookies
}, {
key: "requestCookies",
name: Locale.$STR("requestCookies"),
params: requestCookies
}];
return (
dom.div({className: "cookiesTabBox"},
dom.div({className: "panelContent"},
NetInfoGroupList({
groups: groups
})
)
)
);
}
}
// Exports from this module
module.exports = CookiesTab;

View File

@ -1,77 +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 { Component, createFactory } = require("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const NetInfoGroupList = createFactory(require("./net-info-group-list"));
const Spinner = createFactory(require("./spinner"));
/**
* This template represents 'Headers' tab displayed when the user
* expands network log in the Console panel. It's responsible for rendering
* request and response HTTP headers.
*/
class HeadersTab extends Component {
static get propTypes() {
return {
actions: PropTypes.shape({
requestData: PropTypes.func.isRequired
}),
data: PropTypes.object.isRequired,
};
}
componentDidMount() {
let { actions, data } = this.props;
let requestHeaders = data.request.headers;
let responseHeaders = data.response.headers;
// Request headers if they are not available yet.
// TODO: use async action objects as soon as Redux is in place
if (!requestHeaders) {
actions.requestData("requestHeaders");
}
if (!responseHeaders) {
actions.requestData("responseHeaders");
}
}
render() {
let { data } = this.props;
let requestHeaders = data.request.headers;
let responseHeaders = data.response.headers;
// TODO: Another groups to implement:
// 1) Cached Headers
// 2) Headers from upload stream
let groups = [{
key: "responseHeaders",
name: Locale.$STR("responseHeaders"),
params: responseHeaders
}, {
key: "requestHeaders",
name: Locale.$STR("requestHeaders"),
params: requestHeaders
}];
// If response headers are not available yet, display a spinner
if (!responseHeaders || !responseHeaders.length) {
groups[0].content = Spinner();
}
return (
dom.div({className: "headersTabBox"},
dom.div({className: "panelContent"},
NetInfoGroupList({groups: groups})
)
)
);
}
}
// Exports from this module
module.exports = HeadersTab;

View File

@ -1,25 +0,0 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
DevToolsModules(
'cookies-tab.js',
'headers-tab.js',
'net-info-body.css',
'net-info-body.js',
'net-info-group-list.js',
'net-info-group.css',
'net-info-group.js',
'net-info-params.css',
'net-info-params.js',
'params-tab.js',
'post-tab.js',
'response-tab.css',
'response-tab.js',
'size-limit.css',
'size-limit.js',
'spinner.js',
'stacktrace-tab.js',
)

View File

@ -1,93 +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/. */
/******************************************************************************/
/* Network Info Body */
.netInfoBody {
margin: 10px 0 0 0;
width: 100%;
cursor: default;
display: block;
}
.netInfoBody *:focus {
outline: 0 !important;
}
.netInfoBody .panelContent {
word-break: break-all;
}
/******************************************************************************/
/* Network Info Body Tabs */
.netInfoBody > .tabs {
background-color: transparent;
background-image: none;
height: 100%;
}
.netInfoBody > .tabs .tabs-navigation {
border-bottom-color: var(--net-border);
background-color: transparent;
text-decoration: none;
padding-top: 3px;
padding-left: 7px;
padding-bottom: 1px;
border-bottom: 1px solid var(--net-border);
}
.netInfoBody > .tabs .tabs-menu {
display: table;
list-style: none;
padding: 0;
margin: 0;
}
/* This is the trick that makes the tab bottom border invisible */
.netInfoBody > .tabs .tabs-menu-item {
position: relative;
bottom: -2px;
float: left;
}
.netInfoBody > .tabs .tabs-menu-item a {
display: block;
border: 1px solid transparent;
text-decoration: none;
padding: 5px 8px 4px 8px;;
font-weight: bold;
color: var(--theme-body-color);
border-radius: 4px 4px 0 0;
}
.netInfoBody > .tabs .tab-panel {
background-color: var(--theme-body-background);
border: 1px solid transparent;
border-top: none;
padding: 10px;
overflow: auto;
height: calc(100% - 31px); /* minus the height of the tab bar */
}
.netInfoBody > .tabs .tab-panel > div,
.netInfoBody > .tabs .tab-panel > div > div {
height: 100%;
}
.netInfoBody > .tabs .tabs-menu-item.is-active a,
.netInfoBody > .tabs .tabs-menu-item.is-active a:focus,
.netInfoBody > .tabs .tabs-menu-item.is-active:hover a {
background-color: var(--theme-body-background);
border: 1px solid transparent;
border-bottom-color: var(--theme-highlight-bluegrey);
color: var(--theme-highlight-bluegrey);
}
.netInfoBody > .tabs .tabs-menu-item:hover a {
border: 1px solid transparent;
border-bottom: 1px solid var(--net-border);
background-color: var(--theme-body-background);
}

View File

@ -1,196 +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 { Component, createFactory } = require("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const { createFactories } = require("devtools/client/shared/react-utils");
const { Tabs, TabPanel } = createFactories(require("devtools/client/shared/components/tabs/Tabs"));
// Network
const HeadersTab = createFactory(require("./headers-tab"));
const ResponseTab = createFactory(require("./response-tab"));
const ParamsTab = createFactory(require("./params-tab"));
const CookiesTab = createFactory(require("./cookies-tab"));
const PostTab = createFactory(require("./post-tab"));
const StackTraceTab = createFactory(require("./stacktrace-tab"));
const NetUtils = require("../utils/net");
/**
* This template renders the basic Network log info body. It's not
* visible by default, the user needs to expand the network log
* to see it.
*
* This is the set of tabs displaying details about network events:
* 1) Headers - request and response headers
* 2) Params - URL parameters
* 3) Response - response body
* 4) Cookies - request and response cookies
* 5) Post - posted data
*/
class NetInfoBody extends Component {
static get propTypes() {
return {
tabActive: PropTypes.number.isRequired,
actions: PropTypes.object.isRequired,
data: PropTypes.shape({
request: PropTypes.object.isRequired,
response: PropTypes.object.isRequired
}),
// Service to enable the source map feature.
sourceMapService: PropTypes.object,
};
}
static get defaultProps() {
return {
tabActive: 0
};
}
constructor(props) {
super(props);
this.state = {
data: {
request: {},
response: {}
},
tabActive: props.tabActive,
};
this.onTabChanged = this.onTabChanged.bind(this);
this.hasCookies = this.hasCookies.bind(this);
this.hasStackTrace = this.hasStackTrace.bind(this);
this.getTabPanels = this.getTabPanels.bind(this);
}
onTabChanged(index) {
this.setState({tabActive: index});
}
hasCookies() {
let {request, response} = this.state.data;
return this.state.hasCookies ||
NetUtils.getHeaderValue(request.headers, "Cookie") ||
NetUtils.getHeaderValue(response.headers, "Set-Cookie");
}
hasStackTrace() {
let {cause} = this.state.data;
return cause && cause.stacktrace && cause.stacktrace.length > 0;
}
getTabPanels() {
let { actions, sourceMapService } = this.props;
let data = this.state.data;
let {request} = data;
// Flags for optional tabs. Some tabs are visible only if there
// are data to display.
let hasParams = request.queryString && request.queryString.length;
let hasPostData = request.bodySize > 0;
let panels = [];
// Headers tab
panels.push(
TabPanel({
id: "headers",
className: "headers",
key: "headers",
title: Locale.$STR("netRequest.headers")},
HeadersTab({data: data, actions: actions})
)
);
// URL parameters tab
if (hasParams) {
panels.push(
TabPanel({
id: "params",
className: "params",
key: "params",
title: Locale.$STR("netRequest.params")},
ParamsTab({data: data, actions: actions})
)
);
}
// Posted data tab
if (hasPostData) {
panels.push(
TabPanel({
id: "post",
className: "post",
key: "post",
title: Locale.$STR("netRequest.post")},
PostTab({data: data, actions: actions})
)
);
}
// Response tab
panels.push(
TabPanel({
id: "response",
className: "response",
key: "response",
title: Locale.$STR("netRequest.response")},
ResponseTab({data: data, actions: actions})
)
);
// Cookies tab
if (this.hasCookies()) {
panels.push(
TabPanel({
id: "cookies",
className: "cookies",
key: "cookies",
title: Locale.$STR("netRequest.cookies")},
CookiesTab({
data: data,
actions: actions
})
)
);
}
// Stacktrace tab
if (this.hasStackTrace()) {
panels.push(
TabPanel({
id: "stacktrace-tab",
className: "stacktrace-tab",
key: "stacktrace",
title: Locale.$STR("netRequest.callstack")},
StackTraceTab({
data: data,
actions: actions,
sourceMapService: sourceMapService,
})
)
);
}
return panels;
}
render() {
let tabActive = this.state.tabActive;
let tabPanels = this.getTabPanels();
return (
Tabs({
tabActive: tabActive,
onAfterChange: this.onTabChanged},
tabPanels
)
);
}
}
// Exports from this module
module.exports = NetInfoBody;

View File

@ -1,45 +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 { Component, createFactory } = require("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const NetInfoGroup = createFactory(require("./net-info-group"));
/**
* This template is responsible for rendering sections/groups inside tabs.
* It's used e.g to display Response and Request headers as separate groups.
*/
class NetInfoGroupList extends Component {
static get propTypes() {
return {
groups: PropTypes.array.isRequired,
};
}
render() {
let groups = this.props.groups;
// Filter out empty groups.
groups = groups.filter(group => {
return group && ((group.params && group.params.length) || group.content);
});
// Render groups
groups = groups.map(group => {
group.type = group.key;
return NetInfoGroup(group);
});
return (
dom.div({className: "netInfoGroupList"},
groups
)
);
}
}
// Exports from this module
module.exports = NetInfoGroupList;

View File

@ -1,68 +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/. */
/******************************************************************************/
/* Net Info Group */
.netInfoBody .netInfoGroup {
padding-bottom: 6px;
}
/* Last group doesn't need bottom padding */
.netInfoBody .netInfoGroup:last-child {
padding-bottom: 0;
}
.netInfoBody .netInfoGroup:last-child .netInfoGroupContent {
padding-bottom: 0;
}
.netInfoBody .netInfoGroupTitle {
cursor: pointer;
font-weight: bold;
-moz-user-select: none;
cursor: pointer;
padding-left: 3px;
}
.netInfoBody .netInfoGroupTwisty {
background-image: url("chrome://devtools/skin/images/controls.png");
background-size: 56px 28px;
background-position: 0 -14px;
background-repeat: no-repeat;
width: 14px;
height: 14px;
cursor: pointer;
display: inline-block;
vertical-align: middle;
}
.netInfoBody .netInfoGroup.opened .netInfoGroupTwisty {
background-position: -14px -14px;
}
/* Group content is expandable/collapsible by clicking on the title */
.netInfoBody .netInfoGroupContent {
padding-top: 7px;
margin-top: 3px;
padding-bottom: 14px;
border-top: 1px solid var(--net-border);
display: none;
}
/* Toggle group visibility */
.netInfoBody .netInfoGroup.opened .netInfoGroupContent {
display: block;
}
/******************************************************************************/
/* Themes */
.theme-dark .netInfoBody .netInfoGroup {
color: var(--theme-body-color);
}
.theme-dark .netInfoBody .netInfoGroup .netInfoGroupTwisty {
filter: invert(1);
}

View File

@ -1,82 +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 { Component, createFactory } = require("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const NetInfoParams = createFactory(require("./net-info-params"));
/**
* This template represents a group of data within a tab. For example,
* Headers tab has two groups 'Request Headers' and 'Response Headers'
* The Response tab can also have two groups 'Raw Data' and 'JSON'
*/
class NetInfoGroup extends Component {
static get propTypes() {
return {
type: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
params: PropTypes.array,
content: PropTypes.element,
open: PropTypes.bool
};
}
static get defaultProps() {
return {
open: true,
};
}
constructor(props) {
super(props);
this.state = {
open: props.open,
};
this.onToggle = this.onToggle.bind(this);
}
onToggle(event) {
this.setState({
open: !this.state.open
});
}
render() {
let content = this.props.content;
if (!content && this.props.params) {
content = NetInfoParams({
params: this.props.params
});
}
let open = this.state.open;
let className = open ? "opened" : "";
return (
dom.div({className: "netInfoGroup" + " " + className + " " +
this.props.type},
dom.span({
className: "netInfoGroupTwisty",
onClick: this.onToggle
}),
dom.span({
className: "netInfoGroupTitle",
onClick: this.onToggle},
this.props.name
),
dom.div({className: "netInfoGroupContent"},
content
)
)
);
}
}
// Exports from this module
module.exports = NetInfoGroup;

View File

@ -1,23 +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/. */
/******************************************************************************/
/* Net Info Params */
.netInfoBody .netInfoParamName {
padding: 0 10px 0 0;
font-weight: bold;
vertical-align: top;
text-align: right;
white-space: nowrap;
}
.netInfoBody .netInfoParamValue {
width: 100%;
word-wrap: break-word;
}
.netInfoBody .netInfoParamValue > code {
font-family: var(--monospace-font-family);
}

View File

@ -1,55 +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 { Component } = require("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
/**
* This template renders list of parameters within a group.
* It's essentially a list of name + value pairs.
*/
class NetInfoParams extends Component {
static get propTypes() {
return {
params: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string.isRequired,
value: PropTypes.string.isRequired
})).isRequired,
};
}
render() {
let params = this.props.params || [];
params.sort(function (a, b) {
return a.name > b.name ? 1 : -1;
});
let rows = [];
params.forEach((param, index) => {
rows.push(
dom.tr({key: index},
dom.td({className: "netInfoParamName"},
dom.span({title: param.name}, param.name)
),
dom.td({className: "netInfoParamValue"},
dom.code({}, param.value)
)
)
);
});
return (
dom.table({cellPadding: 0, cellSpacing: 0},
dom.tbody({},
rows
)
)
);
}
}
// Exports from this module
module.exports = NetInfoParams;

View File

@ -1,39 +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 { Component, createFactory } = require("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const NetInfoParams = createFactory(require("./net-info-params"));
/**
* This template represents 'Params' tab displayed when the user
* expands network log in the Console panel. It's responsible for
* displaying URL parameters (query string).
*/
class ParamsTab extends Component {
static get propTypes() {
return {
data: PropTypes.shape({
request: PropTypes.object.isRequired
})
};
}
render() {
let data = this.props.data;
return (
dom.div({className: "paramsTabBox"},
dom.div({className: "panelContent"},
NetInfoParams({params: data.request.queryString})
)
)
);
}
}
// Exports from this module
module.exports = ParamsTab;

View File

@ -1,290 +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 { Component, createFactory } = require("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const TreeView =
createFactory(require("devtools/client/shared/components/tree/TreeView"));
const { REPS, MODE, parseURLEncodedText } =
require("devtools/client/shared/components/reps/reps");
const { Rep } = REPS;
// Network
const NetInfoParams = createFactory(require("./net-info-params"));
const NetInfoGroupList = createFactory(require("./net-info-group-list"));
const Spinner = createFactory(require("./spinner"));
const SizeLimit = createFactory(require("./size-limit"));
const NetUtils = require("../utils/net");
const Json = require("../utils/json");
/**
* This template represents 'Post' tab displayed when the user
* expands network log in the Console panel. It's responsible for
* displaying posted data (HTTP post body).
*/
class PostTab extends Component {
static get propTypes() {
return {
data: PropTypes.shape({
request: PropTypes.object.isRequired
}),
actions: PropTypes.object.isRequired
};
}
constructor(props) {
super(props);
this.isJson = this.isJson.bind(this);
this.parseJson = this.parseJson.bind(this);
this.renderJson = this.renderJson.bind(this);
this.parseXml = this.parseXml.bind(this);
this.isXml = this.isXml.bind(this);
this.renderXml = this.renderXml.bind(this);
this.renderMultiPart = this.renderMultiPart.bind(this);
this.renderUrlEncoded = this.renderUrlEncoded.bind(this);
this.renderRawData = this.renderRawData.bind(this);
}
componentDidMount() {
let { actions, data: file } = this.props;
if (!file.request.postData) {
// TODO: use async action objects as soon as Redux is in place
actions.requestData("requestPostData");
}
}
isJson(file) {
let text = file.request.postData.text;
let value = NetUtils.getHeaderValue(file.request.headers, "content-type");
return Json.isJSON(value, text);
}
parseJson(file) {
let postData = file.request.postData;
if (!postData) {
return null;
}
let jsonString = new String(postData.text);
return Json.parseJSONString(jsonString);
}
/**
* Render JSON post data as an expandable tree.
*/
renderJson(file) {
let text = file.request.postData.text;
if (!text || isLongString(text)) {
return null;
}
if (!this.isJson(file)) {
return null;
}
let json = this.parseJson(file);
if (!json) {
return null;
}
return {
key: "json",
content: TreeView({
columns: [{id: "value"}],
object: json,
mode: MODE.TINY,
renderValue: props => Rep(Object.assign({}, props, {
cropLimit: 50,
})),
}),
name: Locale.$STR("jsonScopeName")
};
}
parseXml(file) {
let text = file.request.postData.text;
if (isLongString(text)) {
return null;
}
return NetUtils.parseXml({
mimeType: NetUtils.getHeaderValue(file.request.headers, "content-type"),
text: text,
});
}
isXml(file) {
if (isLongString(file.request.postData.text)) {
return false;
}
let value = NetUtils.getHeaderValue(file.request.headers, "content-type");
if (!value) {
return false;
}
return NetUtils.isHTML(value);
}
renderXml(file) {
let text = file.request.postData.text;
if (!text || isLongString(text)) {
return null;
}
if (!this.isXml(file)) {
return null;
}
let doc = this.parseXml(file);
if (!doc) {
return null;
}
// Proper component for rendering XML should be used (see bug 1247392)
return null;
}
/**
* Multipart post data are parsed and nicely rendered
* as an expandable tree of individual parts.
*/
renderMultiPart(file) {
let text = file.request.postData.text;
if (!text || isLongString(text)) {
return;
}
if (NetUtils.isMultiPartRequest(file)) {
// TODO: render multi part request (bug: 1247423)
}
}
/**
* URL encoded post data are nicely rendered as a list
* of parameters.
*/
renderUrlEncoded(file) {
let text = file.request.postData.text;
if (!text || isLongString(text)) {
return null;
}
if (!NetUtils.isURLEncodedRequest(file)) {
return null;
}
let lines = text.split("\n");
let params = parseURLEncodedText(lines[lines.length - 1]);
return {
key: "url-encoded",
content: NetInfoParams({params: params}),
name: Locale.$STR("netRequest.params")
};
}
renderRawData(file) {
let text = file.request.postData.text;
let group;
// The post body might reached the limit, so check if we are
// dealing with a long string.
if (typeof text == "object") {
group = {
key: "raw-longstring",
name: Locale.$STR("netRequest.rawData"),
content: dom.div({className: "netInfoResponseContent"},
sanitize(text.initial),
SizeLimit({
actions: this.props.actions,
data: file.request.postData,
message: Locale.$STR("netRequest.sizeLimitMessage"),
link: Locale.$STR("netRequest.sizeLimitMessageLink")
})
)
};
} else {
group = {
key: "raw",
name: Locale.$STR("netRequest.rawData"),
content: dom.div({className: "netInfoResponseContent"},
sanitize(text)
)
};
}
return group;
}
render() {
let { data: file } = this.props;
if (file.discardRequestBody) {
return dom.span({className: "netInfoBodiesDiscarded"},
Locale.$STR("netRequest.requestBodyDiscarded")
);
}
if (!file.request.postData) {
return (
Spinner()
);
}
// Render post body data. The right representation of the data
// is picked according to the content type.
let groups = [];
groups.push(this.renderUrlEncoded(file));
// TODO: render multi part request (bug: 1247423)
// groups.push(this.renderMultiPart(file));
groups.push(this.renderJson(file));
groups.push(this.renderXml(file));
groups.push(this.renderRawData(file));
// Filter out empty groups.
groups = groups.filter(group => group);
// The raw response is collapsed by default if a nice formatted
// version is available.
if (groups.length > 1) {
groups[groups.length - 1].open = false;
}
return (
dom.div({className: "postTabBox"},
dom.div({className: "panelContent"},
NetInfoGroupList({
groups: groups
})
)
)
);
}
}
// Helpers
/**
* Workaround for a "not well-formed" error that react
* reports when there's multipart data passed to render.
*/
function sanitize(text) {
text = JSON.stringify(text);
text = text.replace(/\\r\\n/g, "\r\n").replace(/\\"/g, "\"");
return text.slice(1, text.length - 1);
}
function isLongString(text) {
return typeof text == "object";
}
// Exports from this module
module.exports = PostTab;

View File

@ -1,21 +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/. */
/******************************************************************************/
/* Response Tab */
.netInfoBody .netInfoBodiesDiscarded {
font-style: italic;
color: gray;
}
.netInfoBody .netInfoResponseContent {
font-family: var(--monospace-font-family);
word-wrap: break-word;
}
.netInfoBody .responseTabBox img {
max-width: 300px;
max-height: 300px;
}

View File

@ -1,291 +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 { Component, createFactory } = require("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
// Reps
const TreeView = createFactory(require("devtools/client/shared/components/tree/TreeView"));
const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
const { Rep } = REPS;
// Network
const SizeLimit = createFactory(require("./size-limit"));
const NetInfoGroupList = createFactory(require("./net-info-group-list"));
const Spinner = createFactory(require("./spinner"));
const Json = require("../utils/json");
const NetUtils = require("../utils/net");
/**
* This template represents 'Response' tab displayed when the user
* expands network log in the Console panel. It's responsible for
* rendering HTTP response body.
*
* In case of supported response mime-type (e.g. application/json,
* text/xml, etc.), the response is parsed using appropriate parser
* and rendered accordingly.
*/
class ResponseTab extends Component {
static get propTypes() {
return {
data: PropTypes.shape({
request: PropTypes.object.isRequired,
response: PropTypes.object.isRequired
}),
actions: PropTypes.object.isRequired
};
}
constructor(props) {
super(props);
this.isJson = this.isJson.bind(this);
this.parseJson = this.parseJson.bind(this);
this.isImage = this.isImage.bind(this);
this.isXml = this.isXml.bind(this);
this.parseXml = this.parseXml.bind(this);
this.renderJson = this.renderJson.bind(this);
this.renderImage = this.renderImage.bind(this);
this.renderXml = this.renderXml.bind(this);
this.renderFormattedResponse = this.renderFormattedResponse.bind(this);
this.renderRawResponse = this.renderRawResponse.bind(this);
}
componentDidMount() {
let { actions, data: file } = this.props;
let content = file.response.content;
if (!content || typeof (content.text) == "undefined") {
// TODO: use async action objects as soon as Redux is in place
actions.requestData("responseContent");
}
}
// Response Types
isJson(content) {
if (isLongString(content.text)) {
return false;
}
return Json.isJSON(content.mimeType, content.text);
}
parseJson(file) {
let content = file.response.content;
if (isLongString(content.text)) {
return null;
}
let jsonString = new String(content.text);
return Json.parseJSONString(jsonString);
}
isImage(content) {
if (isLongString(content.text)) {
return false;
}
return NetUtils.isImage(content.mimeType);
}
isXml(content) {
if (isLongString(content.text)) {
return false;
}
return NetUtils.isHTML(content.mimeType);
}
parseXml(file) {
let content = file.response.content;
if (isLongString(content.text)) {
return null;
}
return NetUtils.parseXml(content);
}
// Rendering
renderJson(file) {
let content = file.response.content;
if (!this.isJson(content)) {
return null;
}
let json = this.parseJson(file);
if (!json) {
return null;
}
return {
key: "json",
content: TreeView({
columns: [{id: "value"}],
object: json,
mode: MODE.TINY,
renderValue: props => Rep(Object.assign({}, props, {
cropLimit: 50,
})),
}),
name: Locale.$STR("jsonScopeName")
};
}
renderImage(file) {
let content = file.response.content;
if (!this.isImage(content)) {
return null;
}
let dataUri = "data:" + content.mimeType + ";base64," + content.text;
return {
key: "image",
content: dom.img({src: dataUri}),
name: Locale.$STR("netRequest.image")
};
}
renderXml(file) {
let content = file.response.content;
if (!this.isXml(content)) {
return null;
}
let doc = this.parseXml(file);
if (!doc) {
return null;
}
// Proper component for rendering XML should be used (see bug 1247392)
return null;
}
/**
* If full response text is available, let's try to parse and
* present nicely according to the underlying format.
*/
renderFormattedResponse(file) {
let content = file.response.content;
if (typeof content.text == "object") {
return null;
}
let group = this.renderJson(file);
if (group) {
return group;
}
group = this.renderImage(file);
if (group) {
return group;
}
group = this.renderXml(file);
if (group) {
return group;
}
return null;
}
renderRawResponse(file) {
let group;
let content = file.response.content;
// The response might reached the limit, so check if we are
// dealing with a long string.
if (typeof content.text == "object") {
group = {
key: "raw-longstring",
name: Locale.$STR("netRequest.rawData"),
content: dom.div({className: "netInfoResponseContent"},
content.text.initial,
SizeLimit({
actions: this.props.actions,
data: content,
message: Locale.$STR("netRequest.sizeLimitMessage"),
link: Locale.$STR("netRequest.sizeLimitMessageLink")
})
)
};
} else {
group = {
key: "raw",
name: Locale.$STR("netRequest.rawData"),
content: dom.div({className: "netInfoResponseContent"},
content.text
)
};
}
return group;
}
/**
* The response panel displays two groups:
*
* 1) Formatted response (in case of supported format, e.g. JSON, XML, etc.)
* 2) Raw response data (always displayed if not discarded)
*/
render() {
let { data: file } = this.props;
// If response bodies are discarded (not collected) let's just
// display a info message indicating what to do to collect even
// response bodies.
if (file.discardResponseBody) {
return dom.span({className: "netInfoBodiesDiscarded"},
Locale.$STR("netRequest.responseBodyDiscarded")
);
}
// Request for the response content is done only if the response
// is not fetched yet - i.e. the `content.text` is undefined.
// Empty content.text` can also be a valid response either
// empty or not available yet.
let content = file.response.content;
if (!content || typeof (content.text) == "undefined") {
return (
Spinner()
);
}
// Render response body data. The right representation of the data
// is picked according to the content type.
let groups = [];
groups.push(this.renderFormattedResponse(file));
groups.push(this.renderRawResponse(file));
// Filter out empty groups.
groups = groups.filter(group => group);
// The raw response is collapsed by default if a nice formatted
// version is available.
if (groups.length > 1) {
groups[1].open = false;
}
return (
dom.div({className: "responseTabBox"},
dom.div({className: "panelContent"},
NetInfoGroupList({
groups: groups
})
)
)
);
}
}
// Helpers
function isLongString(text) {
return typeof text == "object";
}
// Exports from this module
module.exports = ResponseTab;

View File

@ -1,15 +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/. */
/******************************************************************************/
/* Response Size Limit */
.netInfoBody .netInfoSizeLimit {
font-weight: bold;
padding-top: 10px;
}
.netInfoBody .netInfoSizeLimit .objectLink {
color: var(--theme-highlight-blue);
}

View File

@ -1,65 +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 { Component } = require("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
/**
* This template represents a size limit notification message
* used e.g. in the Response tab when response body exceeds
* size limit. The message contains a link allowing the user
* to fetch the rest of the data from the backend (debugger server).
*/
class SizeLimit extends Component {
static get propTypes() {
return {
data: PropTypes.object.isRequired,
message: PropTypes.string.isRequired,
link: PropTypes.string.isRequired,
actions: PropTypes.shape({
resolveString: PropTypes.func.isRequired
}),
};
}
constructor(props) {
super(props);
this.onClickLimit = this.onClickLimit.bind(this);
}
// Event Handlers
onClickLimit(event) {
let actions = this.props.actions;
let content = this.props.data;
actions.resolveString(content, "text");
}
// Rendering
render() {
let message = this.props.message;
let link = this.props.link;
let reLink = /^(.*)\{\{link\}\}(.*$)/;
let m = message.match(reLink);
return (
dom.div({className: "netInfoSizeLimit"},
dom.span({}, m[1]),
dom.a({
className: "objectLink",
onClick: this.onClickLimit},
link
),
dom.span({}, m[2])
)
);
}
}
// Exports from this module
module.exports = SizeLimit;

View File

@ -1,22 +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 { Component } = require("devtools/client/shared/vendor/react");
const dom = require("devtools/client/shared/vendor/react-dom-factories");
/**
* This template represents a throbber displayed when the UI
* is waiting for data coming from the backend (debugging server).
*/
class Spinner extends Component {
render() {
return (
dom.div({className: "devtools-throbber"})
);
}
}
// Exports from this module
module.exports = Spinner;

View File

@ -1,33 +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 { Component, createFactory } = require("devtools/client/shared/vendor/react");
const PropTypes = require("devtools/client/shared/vendor/react-prop-types");
const StackTrace =
createFactory(require("devtools/client/shared/components/StackTrace"));
class StackTraceTab extends Component {
static get propTypes() {
return {
data: PropTypes.object.isRequired,
actions: PropTypes.shape({
onViewSourceInDebugger: PropTypes.func.isRequired
}),
// Service to enable the source map feature.
sourceMapService: PropTypes.object,
};
}
render() {
let { stacktrace } = this.props.data.cause;
let { actions, sourceMapService } = this.props;
let onViewSourceInDebugger = actions.onViewSourceInDebugger.bind(actions);
return StackTrace({ stacktrace, onViewSourceInDebugger, sourceMapService });
}
}
// Exports from this module
module.exports = StackTraceTab;

View File

@ -1,67 +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 promise = require("promise");
const defer = require("devtools/shared/defer");
/**
* Map of pending requests. Used mainly by tests to wait
* till things are ready.
*/
var promises = new Map();
/**
* This object is used to fetch network data from the backend.
* Communication with the chrome scope is based on message
* exchange.
*/
var DataProvider = {
hasPendingRequests: function () {
return promises.size > 0;
},
requestData: function (client, actor, method) {
let key = actor + ":" + method;
let p = promises.get(key);
if (p) {
return p;
}
let deferred = defer();
let realMethodName = "get" + method.charAt(0).toUpperCase() +
method.slice(1);
if (!client[realMethodName]) {
return null;
}
client[realMethodName](actor, response => {
promises.delete(key);
deferred.resolve(response);
});
promises.set(key, deferred.promise);
return deferred.promise;
},
resolveString: function (client, stringGrip) {
let key = stringGrip.actor + ":getString";
let p = promises.get(key);
if (p) {
return p;
}
p = client.getString(stringGrip).then(result => {
promises.delete(key);
return result;
});
promises.set(key, p);
return p;
},
};
// Exports from this module
module.exports = DataProvider;

View File

@ -1,95 +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";
/* global BrowserLoader */
// Initialize module loader and load all modules of the new inline
// preview feature. The entire code-base doesn't need any extra
// privileges and runs entirely in content scope.
const rootUrl = "resource://devtools/client/webconsole/old/net/";
const require = BrowserLoader({
baseURI: rootUrl,
window}).require;
const NetRequest = require("./net-request");
const { loadSheet } = require("devtools/shared/layout/utils");
// Localization
const {LocalizationHelper} = require("devtools/shared/l10n");
const L10N = new LocalizationHelper("devtools/client/locales/netmonitor.properties");
// Stylesheets
var styleSheets = [
"resource://devtools/client/jsonview/css/toolbar.css",
"resource://devtools/client/shared/components/tree/TreeView.css",
"resource://devtools/client/shared/components/reps.css",
"resource://devtools/client/webconsole/old/net/net-request.css",
"resource://devtools/client/webconsole/old/net/components/size-limit.css",
"resource://devtools/client/webconsole/old/net/components/net-info-body.css",
"resource://devtools/client/webconsole/old/net/components/net-info-group.css",
"resource://devtools/client/webconsole/old/net/components/net-info-params.css",
"resource://devtools/client/webconsole/old/net/components/response-tab.css"
];
// Load theme stylesheets into the Console frame. This should be
// done automatically by UI Components as soon as we have consensus
// on the right CSS strategy FIXME.
// It would also be nice to include them using @import.
styleSheets.forEach(url => {
loadSheet(window, url, "author");
});
// Localization API used by React components
// accessing strings from *.properties file.
// Example:
// let localizedString = Locale.$STR('string-key');
//
// Resources:
// http://l20n.org/
// https://github.com/yahoo/react-intl
this.Locale = {
$STR: key => {
try {
return L10N.getStr(key);
} catch (err) {
console.error(key + ": " + err);
}
return key;
}
};
// List of NetRequest instances represents the state.
// As soon as Redux is in place it should be maintained using a reducer.
var netRequests = new Map();
/**
* This function handles network events received from the backend. It's
* executed from within the webconsole.js
*/
function onNetworkEvent(log) {
// The 'from' field is set only in case of a 'networkEventUpdate' packet.
// The initial 'networkEvent' packet uses 'actor'.
// Check if NetRequest object is already created for this event actor and
// if there is none make sure to create one.
let response = log.response;
let netRequest = response.from ? netRequests.get(response.from) : null;
if (!netRequest && !log.update) {
netRequest = new NetRequest(log);
netRequests.set(response.actor, netRequest);
}
if (!netRequest) {
return;
}
if (log.update) {
netRequest.updateBody(response);
}
}
// Make the 'onNetworkEvent' accessible from chrome (see webconsole.js)
this.NetRequest = {
onNetworkEvent: onNetworkEvent
};

View File

@ -1,19 +0,0 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
DIRS += [
'components',
'utils'
]
DevToolsModules(
'data-provider.js',
'main.js',
'net-request.css',
'net-request.js',
)
XPCSHELL_TESTS_MANIFESTS += ['test/unit/xpcshell.ini']
BROWSER_CHROME_MANIFESTS += ['test/mochitest/browser.ini']

View File

@ -1,31 +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/. */
/******************************************************************************/
/* General */
:root {
--net-border: #d7d7d7;
}
:root.theme-dark {
--net-border: #5f7387;
}
/******************************************************************************/
/* Network log */
/* No background if a Net log is opened */
.netRequest.message.opened,
.netRequest.message.opened:hover {
background: transparent !important;
}
/******************************************************************************/
/* Themes */
.theme-dark .netRequest.opened:hover,
.theme-dark .netRequest.opened {
background: transparent;
}

View File

@ -1,324 +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";
// React
const React = require("devtools/client/shared/vendor/react");
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
// Reps
const { parseURLParams } = require("devtools/client/shared/components/reps/reps");
// Network
const { cancelEvent, isLeftClick } = require("./utils/events");
const NetInfoBody = React.createFactory(require("./components/net-info-body"));
const DataProvider = require("./data-provider");
// Constants
const XHTML_NS = "http://www.w3.org/1999/xhtml";
/**
* This object represents a network log in the Console panel (and in the
* Network panel in the future).
* It's associated with an existing log and so, also with an existing
* element in the DOM.
*
* The object neither render no request for more data by default. It only
* reqisters a click listener to the associated log entry (a network event)
* and changes the class attribute of the log entry, so a twisty icon
* appears to indicates that there are more details displayed if the
* log entry is expanded.
*
* When the user expands the log, data are requested from the backend
* and rendered directly within the Console iframe.
*/
function NetRequest(log) {
this.initialize(log);
}
NetRequest.prototype = {
initialize: function (log) {
this.client = log.consoleFrame.webConsoleClient;
this.owner = log.consoleFrame.owner;
// 'this.file' field is following HAR spec.
// http://www.softwareishard.com/blog/har-12-spec/
this.file = log.response;
this.parentNode = log.node;
this.file.request.queryString = parseURLParams(this.file.request.url);
this.hasCookies = false;
// Map of fetched responses (to avoid unnecessary RDP round trip).
this.cachedResponses = new Map();
let doc = this.parentNode.ownerDocument;
let twisty = doc.createElementNS(XHTML_NS, "a");
twisty.className = "theme-twisty";
twisty.href = "#";
let messageBody = this.parentNode.querySelector(".message-body-wrapper");
this.parentNode.insertBefore(twisty, messageBody);
this.parentNode.setAttribute("collapsible", true);
this.parentNode.classList.add("netRequest");
// Register a click listener.
this.addClickListener();
},
addClickListener: function () {
// Add an event listener to toggle the expanded state when clicked.
// The event bubbling is canceled if the user clicks on the log
// itself (not on the expanded body), so opening of the default
// modal dialog is avoided.
this.parentNode.addEventListener("click", (event) => {
if (!isLeftClick(event)) {
return;
}
// Clicking on the toggle button or the method expands/collapses
// the body with HTTP details.
let classList = event.originalTarget.classList;
if (!(classList.contains("theme-twisty") ||
classList.contains("method"))) {
return;
}
// Alright, the user is clicking fine, let's open HTTP details!
this.onToggleBody(event);
// Avoid the default modal dialog
cancelEvent(event);
}, true);
},
onToggleBody: function (event) {
let target = event.currentTarget;
let logRow = target.closest(".netRequest");
logRow.classList.toggle("opened");
let twisty = this.parentNode.querySelector(".theme-twisty");
if (logRow.classList.contains("opened")) {
twisty.setAttribute("open", true);
} else {
twisty.removeAttribute("open");
}
let isOpen = logRow.classList.contains("opened");
if (isOpen) {
this.renderBody();
} else {
this.closeBody();
}
},
updateCookies: function(method, response) {
// TODO: This code will be part of a reducer.
let result;
if (response.cookies > 0 &&
["requestCookies", "responseCookies"].includes(method)) {
this.hasCookies = true;
this.refresh();
}
},
/**
* Executed when 'networkEventUpdate' is received from the backend.
*/
updateBody: function (response) {
// 'networkEventUpdate' event indicates that there are new data
// available on the backend. The following logic checks the response
// cache and if this data has been already requested before they
// need to be updated now (re-requested).
let method = response.updateType;
this.updateCookies(method, response);
if (this.cachedResponses.get(method)) {
this.cachedResponses.delete(method);
this.requestData(method);
}
},
/**
* Close network inline preview body.
*/
closeBody: function () {
this.netInfoBodyBox.remove();
},
/**
* Render network inline preview body.
*/
renderBody: function () {
let messageBody = this.parentNode.querySelector(".message-body-wrapper");
// Create box for all markup rendered by ReactJS. Since we are
// rendering within webconsole.xul (i.e. XUL document) we need
// to explicitly specify XHTML namespace.
let doc = messageBody.ownerDocument;
this.netInfoBodyBox = doc.createElementNS(XHTML_NS, "div");
this.netInfoBodyBox.classList.add("netInfoBody");
messageBody.appendChild(this.netInfoBodyBox);
// As soon as Redux is in place state and actions will come from
// separate modules.
let body = NetInfoBody({
actions: this,
sourceMapService: this.owner.sourceMapURLService,
});
// Render net info body!
this.body = ReactDOM.render(body, this.netInfoBodyBox);
this.refresh();
},
/**
* Render top level ReactJS component.
*/
refresh: function () {
if (!this.netInfoBodyBox) {
return;
}
// TODO: As soon as Redux is in place there will be reducer
// computing a new state.
let newState = Object.assign({}, this.body.state, {
data: this.file,
hasCookies: this.hasCookies
});
this.body.setState(newState);
},
// Communication with the backend
requestData: function (method) {
// If the response has already been received bail out.
let response = this.cachedResponses.get(method);
if (response) {
return;
}
// Set an attribute indicating that this net log is waiting for
// data coming from the backend. Intended mainly for tests.
this.parentNode.setAttribute("loading", "true");
let actor = this.file.actor;
DataProvider.requestData(this.client, actor, method).then(args => {
this.cachedResponses.set(method, args);
this.onRequestData(method, args);
if (!DataProvider.hasPendingRequests()) {
this.parentNode.removeAttribute("loading");
// Fire an event indicating that all pending requests for
// data from the backend has finished. Intended for tests.
// Do it asynchronously so, it's done after all handlers
// for the current promise are executed.
setTimeout(() => {
let event = document.createEvent("Event");
event.initEvent("netlog-no-pending-requests", true, true);
this.parentNode.dispatchEvent(event);
});
}
});
},
onRequestData: function (method, response) {
// TODO: This code will be part of a reducer.
let result;
switch (method) {
case "requestHeaders":
result = this.onRequestHeaders(response);
break;
case "responseHeaders":
result = this.onResponseHeaders(response);
break;
case "requestCookies":
result = this.onRequestCookies(response);
break;
case "responseCookies":
result = this.onResponseCookies(response);
break;
case "responseContent":
result = this.onResponseContent(response);
break;
case "requestPostData":
result = this.onRequestPostData(response);
break;
}
result.then(() => {
this.refresh();
});
},
onRequestHeaders: function (response) {
this.file.request.headers = response.headers;
return this.resolveHeaders(this.file.request.headers);
},
onResponseHeaders: function (response) {
this.file.response.headers = response.headers;
return this.resolveHeaders(this.file.response.headers);
},
onResponseContent: function (response) {
let content = response.content;
for (let p in content) {
this.file.response.content[p] = content[p];
}
return Promise.resolve();
},
onRequestPostData: function (response) {
this.file.request.postData = response.postData;
return Promise.resolve();
},
onRequestCookies: function (response) {
this.file.request.cookies = response.cookies;
return this.resolveHeaders(this.file.request.cookies);
},
onResponseCookies: function (response) {
this.file.response.cookies = response.cookies;
return this.resolveHeaders(this.file.response.cookies);
},
onViewSourceInDebugger: function (frame) {
this.owner.viewSourceInDebugger(frame.source, frame.line);
},
resolveHeaders: function (headers) {
let promises = [];
for (let header of headers) {
if (typeof header.value == "object") {
promises.push(this.resolveString(header.value).then(value => {
header.value = value;
}));
}
}
return Promise.all(promises);
},
resolveString: function (object, propName) {
let stringGrip = object[propName];
if (typeof stringGrip == "object") {
DataProvider.resolveString(this.client, stringGrip).then(args => {
object[propName] = args;
this.refresh();
});
}
}
};
// Exports from this module
module.exports = NetRequest;

View File

@ -1,6 +0,0 @@
"use strict";
module.exports = {
// Extend from the shared list of defined globals for mochitests.
"extends": "../../../../../../.eslintrc.mochitests.js",
};

View File

@ -1,23 +0,0 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
head.js
page_basic.html
test.json
test.json^headers^
test-cookies.json
test-cookies.json^headers^
test.txt
test.xml
test.xml^headers^
!/devtools/client/shared/test/frame-script-utils.js
!/devtools/client/shared/test/shared-head.js
!/devtools/client/webconsole/old/test/head.js
[browser_net_basic.js]
[browser_net_cookies.js]
[browser_net_headers.js]
[browser_net_params.js]
[browser_net_post.js]
[browser_net_response.js]

View File

@ -1,33 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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";
const TEST_PAGE_URL = URL_ROOT + "page_basic.html";
const JSON_XHR_URL = URL_ROOT + "test.json";
/**
* Basic test that generates XHR in the content and
* checks the related log in the Console panel can
* be expanded.
*/
add_task(async function () {
info("Test XHR Spy basic started");
let {hud} = await addTestTab(TEST_PAGE_URL);
let netInfoBody = await executeAndInspectXhr(hud, {
method: "GET",
url: JSON_XHR_URL
});
ok(netInfoBody, "The network details must be available");
// There should be at least two tabs: Headers and Response
ok(netInfoBody.querySelector(".tabs .tabs-menu-item.headers"),
"Headers tab must be available");
ok(netInfoBody.querySelector(".tabs .tabs-menu-item.response"),
"Response tab must be available");
});

View File

@ -1,54 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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";
const TEST_PAGE_URL = URL_ROOT + "page_basic.html";
const JSON_XHR_URL = URL_ROOT + "test-cookies.json";
/**
* This test generates XHR requests in the page, expands
* networks details in the Console panel and checks that
* Cookies are properly displayed.
*/
add_task(async function () {
info("Test XHR Spy cookies started");
let {hud} = await addTestTab(TEST_PAGE_URL);
let netInfoBody = await executeAndInspectXhr(hud, {
method: "GET",
url: JSON_XHR_URL
});
// Select "Cookies" tab
let tabBody = await selectNetInfoTab(hud, netInfoBody, "cookies");
let requestCookieName = tabBody.querySelector(
".netInfoGroup.requestCookies .netInfoParamName > span[title='bar']");
// Verify request cookies (name and value)
ok(requestCookieName, "Request Cookie name must exist");
is(requestCookieName.textContent, "bar",
"The cookie name must have proper value");
let requestCookieValue = requestCookieName.parentNode.nextSibling;
ok(requestCookieValue, "Request Cookie value must exist");
is(requestCookieValue.textContent, "foo",
"The cookie value must have proper value");
let responseCookieName = tabBody.querySelector(
".netInfoGroup.responseCookies .netInfoParamName > span[title='test']");
// Verify response cookies (name and value)
ok(responseCookieName, "Response Cookie name must exist");
is(responseCookieName.textContent, "test",
"The cookie name must have proper value");
let responseCookieValue = responseCookieName.parentNode.nextSibling;
ok(responseCookieValue, "Response Cookie value must exist");
is(responseCookieValue.textContent, "abc",
"The cookie value must have proper value");
});

View File

@ -1,43 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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";
const TEST_PAGE_URL = URL_ROOT + "page_basic.html";
const JSON_XHR_URL = URL_ROOT + "test.json";
/**
* This test generates XHR requests in the page, expands
* networks details in the Console panel and checks that
* HTTP headers are there.
*/
add_task(async function () {
// Disable rcwn to make cache behavior deterministic.
await pushPref("network.http.rcwn.enabled", false);
info("Test XHR Spy headers started");
let {hud} = await addTestTab(TEST_PAGE_URL);
let netInfoBody = await executeAndInspectXhr(hud, {
method: "GET",
url: JSON_XHR_URL
});
// Select "Headers" tab
let tabBody = await selectNetInfoTab(hud, netInfoBody, "headers");
let paramName = tabBody.querySelector(
".netInfoParamName > span[title='content-type']");
// Verify "Content-Type" header (name and value)
ok(paramName, "Header name must exist");
is(paramName.textContent, "content-type",
"The header name must have proper value");
let paramValue = paramName.parentNode.nextSibling;
ok(paramValue, "Header value must exist");
is(paramValue.textContent, "application/json; charset=utf-8",
"The header value must have proper value");
});

View File

@ -1,69 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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";
const TEST_PAGE_URL = URL_ROOT + "page_basic.html";
const JSON_XHR_URL = URL_ROOT + "test.json";
/**
* This test generates XHR requests in the page, expands
* networks details in the Console panel and checks that
* HTTP parameters (query string) are there.
*/
add_task(async function () {
info("Test XHR Spy params started");
let {hud} = await addTestTab(TEST_PAGE_URL);
let netInfoBody = await executeAndInspectXhr(hud, {
method: "GET",
url: JSON_XHR_URL,
queryString: "?foo=bar"
});
// Check headers
let tabBody = await selectNetInfoTab(hud, netInfoBody, "params");
let paramName = tabBody.querySelector(
".netInfoParamName > span[title='foo']");
// Verify "Content-Type" header (name and value)
ok(paramName, "Header name must exist");
is(paramName.textContent, "foo",
"The param name must have proper value");
let paramValue = paramName.parentNode.nextSibling;
ok(paramValue, "param value must exist");
is(paramValue.textContent, "bar",
"The param value must have proper value");
});
/**
* Test URL parameters with the same name.
*/
add_task(async function () {
info("Test XHR Spy params started");
let {hud} = await addTestTab(TEST_PAGE_URL);
let netInfoBody = await executeAndInspectXhr(hud, {
method: "GET",
url: JSON_XHR_URL,
queryString: "?box[]=123&box[]=456"
});
// Check headers
let tabBody = await selectNetInfoTab(hud, netInfoBody, "params");
let params = tabBody.querySelectorAll(
".netInfoParamName > span[title='box[]']");
is(params.length, 2, "Two URI parameters must exist");
let values = tabBody.querySelectorAll(
".netInfoParamValue > code");
is(values[0].textContent, 123, "First value must match");
is(values[1].textContent, 456, "Second value must match");
});

View File

@ -1,88 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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";
const TEST_PAGE_URL = URL_ROOT + "page_basic.html";
const JSON_XHR_URL = URL_ROOT + "test.json";
const plainPostBody = "test-data";
const jsonData = "{\"bar\": \"baz\"}";
const jsonRendered = "bar\"baz\"";
const xmlPostBody = "<xml><name>John</name></xml>";
/**
* This test generates XHR requests in the page, expands
* networks details in the Console panel and checks that
* Post data are properly rendered.
*/
add_task(async function () {
info("Test XHR Spy post plain body started");
let {hud} = await addTestTab(TEST_PAGE_URL);
let netInfoBody = await executeAndInspectXhr(hud, {
method: "POST",
url: JSON_XHR_URL,
body: plainPostBody
});
// Check post body data
let tabBody = await selectNetInfoTab(hud, netInfoBody, "post");
let postContent = tabBody.querySelector(
".netInfoGroup.raw.opened .netInfoGroupContent");
is(postContent.textContent, plainPostBody,
"Post body must be properly rendered");
});
add_task(async function () {
info("Test XHR Spy post JSON body started");
let {hud} = await addTestTab(TEST_PAGE_URL);
let netInfoBody = await executeAndInspectXhr(hud, {
method: "POST",
url: JSON_XHR_URL,
body: jsonData,
requestHeaders: [{
name: "Content-Type",
value: "application/json"
}]
});
// Check post body data
let tabBody = await selectNetInfoTab(hud, netInfoBody, "post");
let postContent = tabBody.querySelector(
".netInfoGroup.json.opened .netInfoGroupContent");
is(postContent.textContent, jsonRendered,
"Post body must be properly rendered");
let rawPostContent = tabBody.querySelector(
".netInfoGroup.raw.opened .netInfoGroupContent");
ok(!rawPostContent, "Raw response group must be collapsed");
});
add_task(async function () {
info("Test XHR Spy post XML body started");
let {hud} = await addTestTab(TEST_PAGE_URL);
let netInfoBody = await executeAndInspectXhr(hud, {
method: "POST",
url: JSON_XHR_URL,
body: xmlPostBody,
requestHeaders: [{
name: "Content-Type",
value: "application/xml"
}]
});
// Check post body data
let tabBody = await selectNetInfoTab(hud, netInfoBody, "post");
let rawPostContent = tabBody.querySelector(
".netInfoGroup.raw.opened .netInfoGroupContent");
is(rawPostContent.textContent, xmlPostBody,
"Raw response group must not be collapsed");
});

View File

@ -1,86 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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";
const TEST_PAGE_URL = URL_ROOT + "page_basic.html";
const TEXT_XHR_URL = URL_ROOT + "test.txt";
const JSON_XHR_URL = URL_ROOT + "test.json";
const XML_XHR_URL = URL_ROOT + "test.xml";
const textResponseBody = "this is a response";
const jsonResponseBody = "name\"John\"";
// Individual tests below generate XHR request in the page, expand
// network details in the Console panel and checks various types
// of response bodies.
/**
* Validate plain text response
*/
add_task(async function () {
info("Test XHR Spy respone plain body started");
let {hud} = await addTestTab(TEST_PAGE_URL);
let netInfoBody = await executeAndInspectXhr(hud, {
method: "GET",
url: TEXT_XHR_URL,
});
// Check response body data
let tabBody = await selectNetInfoTab(hud, netInfoBody, "response");
let responseContent = tabBody.querySelector(
".netInfoGroup.raw.opened .netInfoGroupContent");
ok(responseContent.textContent.indexOf(textResponseBody) > -1,
"Response body must be properly rendered");
});
/**
* Validate XML response
*/
add_task(async function () {
info("Test XHR Spy response XML body started");
let {hud} = await addTestTab(TEST_PAGE_URL);
let netInfoBody = await executeAndInspectXhr(hud, {
method: "GET",
url: XML_XHR_URL,
});
// Check response body data
let tabBody = await selectNetInfoTab(hud, netInfoBody, "response");
let rawResponseContent = tabBody.querySelector(
".netInfoGroup.raw.opened .netInfoGroupContent");
ok(rawResponseContent, "Raw response group must not be collapsed");
});
/**
* Validate JSON response
*/
add_task(async function () {
info("Test XHR Spy response JSON body started");
let {hud} = await addTestTab(TEST_PAGE_URL);
let netInfoBody = await executeAndInspectXhr(hud, {
method: "GET",
url: JSON_XHR_URL,
});
// Check response body data
let tabBody = await selectNetInfoTab(hud, netInfoBody, "response");
let responseContent = tabBody.querySelector(
".netInfoGroup.json .netInfoGroupContent");
is(responseContent.textContent, jsonResponseBody,
"Response body must be properly rendered");
let rawResponseContent = tabBody.querySelector(
".netInfoGroup.raw.opened .netInfoGroupContent");
ok(!rawResponseContent, "Raw response group must be collapsed");
});

View File

@ -1,201 +0,0 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
/* eslint no-unused-vars: [2, {"vars": "local", "args": "none"}] */
/* import-globals-from ../../../test/head.js */
"use strict";
// Load Web Console head.js, it implements helper console test API
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/webconsole/old/test/head.js", this);
const NET_INFO_PREF = "devtools.webconsole.filter.networkinfo";
const NET_XHR_PREF = "devtools.webconsole.filter.netxhr";
// Enable XHR logging for the test
Services.prefs.setBoolPref(NET_INFO_PREF, true);
Services.prefs.setBoolPref(NET_XHR_PREF, true);
registerCleanupFunction(() => {
Services.prefs.clearUserPref(NET_INFO_PREF);
Services.prefs.clearUserPref(NET_XHR_PREF);
});
// Use the old webconsole since the new one doesn't yet support
// XHR spy. See Bug 1304794.
Services.prefs.setBoolPref("devtools.webconsole.new-frontend-enabled", false);
registerCleanupFunction(function() {
Services.prefs.clearUserPref("devtools.webconsole.new-frontend-enabled");
});
/**
* Add a new test tab in the browser and load the given url.
* @param {String} url The url to be loaded in the new tab
* @return a promise that resolves to the tab object when the url is loaded
*/
function addTestTab(url) {
info("Adding a new JSON tab with URL: '" + url + "'");
return (async function () {
let tab = await addTab(url);
// Load devtools/shared/test/frame-script-utils.js
loadFrameScriptUtils(tab.linkedBrowser);
// Open the Console panel
let hud = await openConsole();
return {
tab: tab,
browser: tab.linkedBrowser,
hud: hud
};
})();
}
/**
*
* @param hud
* @param options
*/
function executeAndInspectXhr(hud, options) {
hud.jsterm.clearOutput();
options.queryString = options.queryString || "";
// Execute XHR in the content scope.
performRequestsInContent({
method: options.method,
url: options.url + options.queryString,
body: options.body,
nocache: options.nocache,
requestHeaders: options.requestHeaders
});
return (async function () {
// Wait till the appropriate Net log appears in the Console panel.
let rules = await waitForMessages({
webconsole: hud,
messages: [{
text: options.url,
category: CATEGORY_NETWORK,
severity: SEVERITY_INFO,
isXhr: true,
}]
});
// The log is here, get its parent element (className: 'message').
let msg = [...rules[0].matched][0];
let body = msg.querySelector(".message-body");
// Open XHR HTTP details body and wait till the UI fetches
// all necessary data from the backend. All RPD requests
// needs to be finished before we can continue testing.
await synthesizeMouseClickSoon(hud, body);
await waitForBackend(msg);
let netInfoBody = body.querySelector(".netInfoBody");
ok(netInfoBody, "Net info body must exist");
return netInfoBody;
})();
}
/**
* Wait till XHR data are fetched from the backend (i.e. there are
* no pending RDP requests.
*/
function waitForBackend(element) {
if (!element.hasAttribute("loading")) {
return;
}
return once(element, "netlog-no-pending-requests", true);
}
/**
* Select specific tab in XHR info body.
*
* @param netInfoBody The main XHR info body
* @param tabId Tab ID (possible values: 'headers', 'cookies', 'params',
* 'post', 'response');
*
* @returns Tab body element.
*/
function selectNetInfoTab(hud, netInfoBody, tabId) {
let tab = netInfoBody.querySelector(".tabs-menu-item." + tabId);
ok(tab, "Tab must exist " + tabId);
// Click to select specified tab and wait till its
// UI is populated with data from the backend.
// There must be no pending RDP requests before we can
// continue testing the UI.
return (async function () {
await synthesizeMouseClickSoon(hud, tab);
let msg = getAncestorByClass(netInfoBody, "message");
await waitForBackend(msg);
let tabBody = netInfoBody.querySelector("." + tabId + "TabBox");
ok(tabBody, "Tab body must exist");
return tabBody;
})();
}
/**
* Return parent node with specified class.
*
* @param node A child element
* @param className Specified class name.
*
* @returns A parent element.
*/
function getAncestorByClass(node, className) {
for (let parent = node; parent; parent = parent.parentNode) {
if (parent.classList && parent.classList.contains(className)) {
return parent;
}
}
return null;
}
/**
* Synthesize asynchronous click event (with clean stack trace).
*/
function synthesizeMouseClickSoon(hud, element) {
return new Promise((resolve) => {
executeSoon(() => {
EventUtils.synthesizeMouse(element, 2, 2, {}, hud.iframeWindow);
resolve();
});
});
}
/**
* Execute XHR in the content scope.
*/
function performRequestsInContent(requests) {
info("Performing requests in the context of the content.");
return executeInContent("devtools:test:xhr", requests);
}
function executeInContent(name, data = {}, objects = {},
expectResponse = true) {
let mm = gBrowser.selectedBrowser.messageManager;
mm.sendAsyncMessage(name, data, objects);
if (expectResponse) {
return waitForContentMessage(name);
}
return Promise.resolve();
}
function waitForContentMessage(name) {
info("Expecting message " + name + " from content");
let mm = gBrowser.selectedBrowser.messageManager;
return new Promise((resolve) => {
mm.addMessageListener(name, function onMessage(msg) {
mm.removeMessageListener(name, onMessage);
resolve(msg.data);
});
});
}

View File

@ -1,14 +0,0 @@
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<!doctype html>
<html>
<head>
<meta charset="utf-8"/>
<title>XHR Spy test page</title>
</head>
<body>
<script type="text/javascript">
document.cookie = "bar=foo";
</script>
</body>
</html>

View File

@ -1 +0,0 @@
{"name":"Cookies Test"}

View File

@ -1,2 +0,0 @@
Content-Type: application/json; charset=utf-8
Set-Cookie: test=abc

View File

@ -1 +0,0 @@
{"name":"John"}

View File

@ -1 +0,0 @@
Content-Type: application/json; charset=utf-8

View File

@ -1 +0,0 @@
this is a response

View File

@ -1 +0,0 @@
<xml><name>John</name></xml>

View File

@ -1 +0,0 @@
Content-Type: application/xml; charset=utf-8

View File

@ -1,6 +0,0 @@
"use strict";
module.exports = {
// Extend from the common devtools xpcshell eslintrc config.
"extends": "../../../../../../.eslintrc.xpcshell.js"
};

View File

@ -1,44 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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";
const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const { parseJSONString, isJSON } = require("devtools/client/webconsole/old/net/utils/json");
// Test data
const simpleJson = '{"name":"John"}';
const jsonInFunc = 'someFunc({"name":"John"})';
const json1 = "{'a': 1}";
const json2 = " {'a': 1}";
const json3 = "\t {'a': 1}";
const json4 = "\n\n\t {'a': 1}";
const json5 = "\n\n\t ";
const textMimeType = "text/plain";
const jsonMimeType = "text/javascript";
const unknownMimeType = "text/unknown";
/**
* Testing API provided by webconsole/old/net/utils/json.js
*/
function run_test() {
// parseJSONString
equal(parseJSONString(simpleJson).name, "John");
equal(parseJSONString(jsonInFunc).name, "John");
// isJSON
equal(isJSON(textMimeType, json1), true);
equal(isJSON(textMimeType, json2), true);
equal(isJSON(jsonMimeType, json3), true);
equal(isJSON(jsonMimeType, json4), true);
equal(isJSON(unknownMimeType, json1), true);
equal(isJSON(textMimeType, json1), true);
equal(isJSON(unknownMimeType), false);
equal(isJSON(unknownMimeType, json5), false);
}

View File

@ -1,76 +0,0 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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";
const { require } = ChromeUtils.import("resource://devtools/shared/Loader.jsm", {});
const {
isImage,
isHTML,
getHeaderValue,
isURLEncodedRequest,
isMultiPartRequest
} = require("devtools/client/webconsole/old/net/utils/net");
// Test data
const imageMimeTypes = ["image/jpeg", "image/jpg", "image/gif",
"image/png", "image/bmp"];
const htmlMimeTypes = ["text/html", "text/xml", "application/xml",
"application/rss+xml", "application/atom+xml", "application/xhtml+xml",
"application/mathml+xml", "application/rdf+xml"];
const headers = [{name: "headerName", value: "value1"}];
const har1 = {
request: {
postData: {
text: "content-type: application/x-www-form-urlencoded"
}
}
};
const har2 = {
request: {
headers: [{
name: "content-type",
value: "application/x-www-form-urlencoded"
}]
}
};
const har3 = {
request: {
headers: [{
name: "content-type",
value: "multipart/form-data"
}]
}
};
/**
* Testing API provided by webconsole/old/net/utils/net.js
*/
function run_test() {
// isImage
imageMimeTypes.forEach(mimeType => {
ok(isImage(mimeType));
});
// isHTML
htmlMimeTypes.forEach(mimeType => {
ok(isHTML(mimeType));
});
// getHeaderValue
equal(getHeaderValue(headers, "headerName"), "value1");
// isURLEncodedRequest
ok(isURLEncodedRequest(har1));
ok(isURLEncodedRequest(har2));
// isMultiPartRequest
ok(isMultiPartRequest(har3));
}

View File

@ -1,8 +0,0 @@
[DEFAULT]
tags = devtools
head =
firefox-appdir = browser
skip-if = toolkit == 'android'
[test_json-utils.js]
[test_net-utils.js]

View File

@ -1,21 +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";
function isLeftClick(event, allowKeyModifiers) {
return event.button === 0 && (allowKeyModifiers || noKeyModifiers(event));
}
function noKeyModifiers(event) {
return !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey;
}
function cancelEvent(event) {
event.stopPropagation();
event.preventDefault();
}
// Exports from this module
exports.isLeftClick = isLeftClick;
exports.cancelEvent = cancelEvent;

View File

@ -1,234 +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";
// List of JSON content types.
const contentTypes = {
"text/plain": 1,
"text/javascript": 1,
"text/x-javascript": 1,
"text/json": 1,
"text/x-json": 1,
"application/json": 1,
"application/x-json": 1,
"application/javascript": 1,
"application/x-javascript": 1,
"application/json-rpc": 1
};
// Implementation
var Json = {};
/**
* Parsing JSON
*/
Json.parseJSONString = function (jsonString) {
if (!jsonString.length) {
return null;
}
let regex, matches;
let first = firstNonWs(jsonString);
if (first !== "[" && first !== "{") {
// This (probably) isn't pure JSON. Let's try to strip various sorts
// of XSSI protection/wrapping and see if that works better.
// Prototype-style secure requests
regex = /^\s*\/\*-secure-([\s\S]*)\*\/\s*$/;
matches = regex.exec(jsonString);
if (matches) {
jsonString = matches[1];
if (jsonString[0] === "\\" && jsonString[1] === "n") {
jsonString = jsonString.substr(2);
}
if (jsonString[jsonString.length - 2] === "\\" &&
jsonString[jsonString.length - 1] === "n") {
jsonString = jsonString.substr(0, jsonString.length - 2);
}
}
// Google-style (?) delimiters
if (jsonString.includes("&&&START&&&")) {
regex = /&&&START&&&([\s\S]*)&&&END&&&/;
matches = regex.exec(jsonString);
if (matches) {
jsonString = matches[1];
}
}
// while(1);, for(;;);, and )]}'
regex = /^\s*(\)\]\}[^\n]*\n|while\s*\(1\);|for\s*\(;;\);)([\s\S]*)/;
matches = regex.exec(jsonString);
if (matches) {
jsonString = matches[2];
}
// JSONP
regex = /^\s*([A-Za-z0-9_$.]+\s*(?:\[.*\]|))\s*\(([\s\S]*)\)/;
matches = regex.exec(jsonString);
if (matches) {
jsonString = matches[2];
}
}
try {
return JSON.parse(jsonString);
} catch (err) {
// eslint-disable-line no-empty
}
// Give up if we don't have valid start, to avoid some unnecessary overhead.
first = firstNonWs(jsonString);
if (first !== "[" && first !== "{" && isNaN(first) && first !== '"') {
return null;
}
// Remove JavaScript comments, quote non-quoted identifiers, and merge
// multi-line structures like |{"a": 1} \n {"b": 2}| into a single JSON
// object [{"a": 1}, {"b": 2}].
jsonString = pseudoJsonToJson(jsonString);
try {
return JSON.parse(jsonString);
} catch (err) {
// eslint-disable-line no-empty
}
return null;
};
function firstNonWs(str) {
for (let i = 0, len = str.length; i < len; i++) {
let ch = str[i];
if (ch !== " " && ch !== "\n" && ch !== "\t" && ch !== "\r") {
return ch;
}
}
return "";
}
function pseudoJsonToJson(json) {
let ret = "";
let at = 0, lasti = 0, lastch = "", hasMultipleParts = false;
for (let i = 0, len = json.length; i < len; ++i) {
let ch = json[i];
if (/\s/.test(ch)) {
continue;
}
if (ch === '"') {
// Consume a string.
++i;
while (i < len) {
if (json[i] === "\\") {
++i;
} else if (json[i] === '"') {
break;
}
++i;
}
} else if (ch === "'") {
// Convert an invalid string into a valid one.
ret += json.slice(at, i) + "\"";
at = i + 1;
++i;
while (i < len) {
if (json[i] === "\\") {
++i;
} else if (json[i] === "'") {
break;
}
++i;
}
if (i < len) {
ret += json.slice(at, i) + "\"";
at = i + 1;
}
} else if ((ch === "[" || ch === "{") &&
(lastch === "]" || lastch === "}")) {
// Multiple JSON messages in one... Make it into a single array by
// inserting a comma and setting the "multiple parts" flag.
ret += json.slice(at, i) + ",";
hasMultipleParts = true;
at = i;
} else if (lastch === "," && (ch === "]" || ch === "}")) {
// Trailing commas in arrays/objects.
ret += json.slice(at, lasti);
at = i;
} else if (lastch === "/" && lasti === i - 1) {
// Some kind of comment; remove it.
if (ch === "/") {
ret += json.slice(at, i - 1);
at = i + json.slice(i).search(/\n|\r|$/);
i = at - 1;
} else if (ch === "*") {
ret += json.slice(at, i - 1);
at = json.indexOf("*/", i + 1) + 2;
if (at === 1) {
at = len;
}
i = at - 1;
}
ch = "\0";
} else if (/[a-zA-Z$_]/.test(ch) && lastch !== ":") {
// Non-quoted identifier. Quote it.
ret += json.slice(at, i) + "\"";
at = i;
i = i + json.slice(i).search(/[^a-zA-Z0-9$_]|$/);
ret += json.slice(at, i) + "\"";
at = i;
}
lastch = ch;
lasti = i;
}
ret += json.slice(at);
if (hasMultipleParts) {
ret = "[" + ret + "]";
}
return ret;
}
Json.isJSON = function (contentType, data) {
// Workaround for JSON responses without proper content type
// Let's consider all responses starting with "{" as JSON. In the worst
// case there will be an exception when parsing. This means that no-JSON
// responses (and post data) (with "{") can be parsed unnecessarily,
// which represents a little overhead, but this happens only if the request
// is actually expanded by the user in the UI (Net & Console panels).
// Do a manual string search instead of checking (data.strip()[0] === "{")
// to improve performance/memory usage.
let len = data ? data.length : 0;
for (let i = 0; i < len; i++) {
let ch = data.charAt(i);
if (ch === "{") {
return true;
}
if (ch === " " || ch === "\t" || ch === "\n" || ch === "\r") {
continue;
}
break;
}
if (!contentType) {
return false;
}
contentType = contentType.split(";")[0];
contentType = contentType.trim();
return !!contentTypes[contentType];
};
// Exports from this module
module.exports = Json;

View File

@ -1,11 +0,0 @@
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# 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/.
DevToolsModules(
'events.js',
'json.js',
'net.js',
)

View File

@ -1,134 +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 mimeCategoryMap = {
"text/plain": "txt",
"application/octet-stream": "bin",
"text/html": "html",
"text/xml": "html",
"application/xml": "html",
"application/rss+xml": "html",
"application/atom+xml": "html",
"application/xhtml+xml": "html",
"application/mathml+xml": "html",
"application/rdf+xml": "html",
"text/css": "css",
"application/x-javascript": "js",
"text/javascript": "js",
"application/javascript": "js",
"text/ecmascript": "js",
"application/ecmascript": "js",
"image/jpeg": "image",
"image/jpg": "image",
"image/gif": "image",
"image/png": "image",
"image/bmp": "image",
"application/x-shockwave-flash": "plugin",
"application/x-silverlight-app": "plugin",
"video/x-flv": "media",
"audio/mpeg3": "media",
"audio/x-mpeg-3": "media",
"video/mpeg": "media",
"video/x-mpeg": "media",
"video/webm": "media",
"video/mp4": "media",
"video/ogg": "media",
"audio/ogg": "media",
"application/ogg": "media",
"application/x-ogg": "media",
"application/x-midi": "media",
"audio/midi": "media",
"audio/x-mid": "media",
"audio/x-midi": "media",
"music/crescendo": "media",
"audio/wav": "media",
"audio/x-wav": "media",
"application/x-woff": "font",
"application/font-woff": "font",
"application/x-font-woff": "font",
"application/x-ttf": "font",
"application/x-font-ttf": "font",
"font/ttf": "font",
"font/woff": "font",
"application/x-otf": "font",
"application/x-font-otf": "font"
};
var NetUtils = {};
NetUtils.isImage = function (contentType) {
if (!contentType) {
return false;
}
contentType = contentType.split(";")[0];
contentType = contentType.trim();
return mimeCategoryMap[contentType] == "image";
};
NetUtils.isHTML = function (contentType) {
if (!contentType) {
return false;
}
contentType = contentType.split(";")[0];
contentType = contentType.trim();
return mimeCategoryMap[contentType] == "html";
};
NetUtils.getHeaderValue = function (headers, name) {
if (!headers) {
return null;
}
name = name.toLowerCase();
for (let i = 0; i < headers.length; ++i) {
let headerName = headers[i].name.toLowerCase();
if (headerName == name) {
return headers[i].value;
}
}
};
NetUtils.parseXml = function (content) {
let contentType = content.mimeType.split(";")[0];
contentType = contentType.trim();
let parser = new DOMParser();
let doc = parser.parseFromString(content.text, contentType);
let root = doc.documentElement;
// Error handling
let nsURI = "http://www.mozilla.org/newlayout/xml/parsererror.xml";
if (root.namespaceURI == nsURI && root.nodeName == "parsererror") {
return null;
}
return doc;
};
NetUtils.isURLEncodedRequest = function (file) {
let mimeType = "application/x-www-form-urlencoded";
let postData = file.request.postData;
if (postData && postData.text) {
let text = postData.text.toLowerCase();
if (text.startsWith("content-type: " + mimeType)) {
return true;
}
}
let value = NetUtils.getHeaderValue(file.request.headers, "content-type");
return value && value.startsWith(mimeType);
};
NetUtils.isMultiPartRequest = function (file) {
let mimeType = "multipart/form-data";
let value = NetUtils.getHeaderValue(file.request.headers, "content-type");
return value && value.startsWith(mimeType);
};
// Exports from this module
module.exports = NetUtils;

View File

@ -1,6 +0,0 @@
"use strict";
module.exports = {
// Extend from the shared list of defined globals for mochitests.
"extends": "../../../../.eslintrc.mochitests.js"
};

View File

@ -1,409 +0,0 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
head.js
test-bug-585956-console-trace.html
test-bug-593003-iframe-wrong-hud-iframe.html
test-bug-593003-iframe-wrong-hud.html
test-bug-595934-canvas-css.html
test-bug-595934-canvas-css.js
test-bug-595934-css-loader.css
test-bug-595934-css-loader.css^headers^
test-bug-595934-css-loader.html
test-bug-595934-css-parser.css
test-bug-595934-css-parser.html
test-bug-595934-empty-getelementbyid.html
test-bug-595934-empty-getelementbyid.js
test-bug-595934-html.html
test-bug-595934-image.html
test-bug-595934-image.jpg
test-bug-595934-imagemap.html
test-bug-595934-malformedxml-external.html
test-bug-595934-malformedxml-external.xml
test-bug-595934-malformedxml.xhtml
test-bug-595934-svg.xhtml
test-bug-595934-workers.html
test-bug-595934-workers.js
test-bug-597136-external-script-errors.html
test-bug-597136-external-script-errors.js
test-bug-597756-reopen-closed-tab.html
test-bug-599725-response-headers.sjs
test-bug-600183-charset.html
test-bug-600183-charset.html^headers^
test-bug-601177-log-levels.html
test-bug-601177-log-levels.js
test-bug-603750-websocket.html
test-bug-603750-websocket.js
test-bug-613013-console-api-iframe.html
test-bug-618078-network-exceptions.html
test-bug-621644-jsterm-dollar.html
test-bug-630733-response-redirect-headers.sjs
test-bug-632275-getters.html
test-bug-632347-iterators-generators.html
test-bug-644419-log-limits.html
test-bug-646025-console-file-location.html
test-bug-658368-time-methods.html
test-bug-737873-mixedcontent.html
test-bug-752559-ineffective-iframe-sandbox-warning0.html
test-bug-752559-ineffective-iframe-sandbox-warning1.html
test-bug-752559-ineffective-iframe-sandbox-warning2.html
test-bug-752559-ineffective-iframe-sandbox-warning3.html
test-bug-752559-ineffective-iframe-sandbox-warning4.html
test-bug-752559-ineffective-iframe-sandbox-warning5.html
test-bug-752559-ineffective-iframe-sandbox-warning-inner.html
test-bug-752559-ineffective-iframe-sandbox-warning-nested1.html
test-bug-752559-ineffective-iframe-sandbox-warning-nested2.html
test-bug-762593-insecure-passwords-about-blank-web-console-warning.html
test-bug-762593-insecure-passwords-web-console-warning.html
test-bug-766001-console-log.js
test-bug-766001-js-console-links.html
test-bug-766001-js-errors.js
test-bug-782653-css-errors-1.css
test-bug-782653-css-errors-2.css
test-bug-782653-css-errors.html
test-bug-837351-security-errors.html
test-bug-859170-longstring-hang.html
test-bug-869003-iframe.html
test-bug-869003-top-window.html
test-closure-optimized-out.html
test-closures.html
test-console-assert.html
test-console-clear.html
test-console-count.html
test-console-count-external-file.js
test-console-extras.html
test-console-replaced-api.html
test-console-server-logging.sjs
test-console-server-logging-array.sjs
test-console-server-logging-backtrace.sjs
test-console.html
test-console-workers.html
test-console-table.html
test-console-output-02.html
test-console-output-03.html
test-console-output-04.html
test-console-output-dom-elements.html
test-console-output-events.html
test-console-column.html
test-consoleiframes.html
test-console-trace-async.html
test-certificate-messages.html
test-cu-reporterror.js
test-data.json
test-data.json^headers^
test-duplicate-error.html
test-encoding-ISO-8859-1.html
test-error.html
test-eval-in-stackframe.html
test-file-location.js
test-filter.html
test-for-of.html
test_hpkp-invalid-headers.sjs
test_hsts-invalid-headers.sjs
test-iframe-762593-insecure-form-action.html
test-iframe-762593-insecure-frame.html
test-iframe1.html
test-iframe2.html
test-iframe3.html
test-image.png
test-mixedcontent-securityerrors.html
test-mutation.html
test-network-request.html
test-network.html
test-observe-http-ajax.html
test-own-console.html
test-property-provider.html
test-repeated-messages.html
test-result-format-as-string.html
test-trackingprotection-securityerrors.html
test-webconsole-error-observer.html
test_bug_770099_violation.html
test_bug_770099_violation.html^headers^
test-autocomplete-in-stackframe.html
testscript.js
test-bug_923281_console_log_filter.html
test-bug_923281_test1.js
test-bug_923281_test2.js
test-bug_939783_console_trace_duplicates.html
test-bug-952277-highlight-nodes-in-vview.html
test-bug-609872-cd-iframe-parent.html
test-bug-609872-cd-iframe-child.html
test-bug-989025-iframe-parent.html
test-bug_1050691_click_function_to_source.html
test-bug_1050691_click_function_to_source.js
test-console-api-stackframe.html
test-exception-stackframe.html
test_bug_1010953_cspro.html^headers^
test_bug_1010953_cspro.html
test_bug1045902_console_csp_ignore_reflected_xss_message.html^headers^
test_bug1045902_console_csp_ignore_reflected_xss_message.html
test_bug1092055_shouldwarn.js^headers^
test_bug1092055_shouldwarn.js
test_bug1092055_shouldwarn.html
test_bug_1247459_violation.html
!/devtools/client/netmonitor/test/sjs_cors-test-server.sjs
!/devtools/client/shared/test/shared-head.js
!/image/test/mochitest/blue.png
[browser_bug1045902_console_csp_ignore_reflected_xss_message.js]
skip-if = (e10s && debug) || (e10s && os == 'win') # Bug 1221499 enabled these on windows
[browser_bug664688_sandbox_update_after_navigation.js]
[browser_bug_638949_copy_link_location.js]
subsuite = clipboard
skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
[browser_bug_862916_console_dir_and_filter_off.js]
skip-if = (e10s && (os == 'win' || os == 'mac')) # Bug 1243976
[browser_bug_865288_repeat_different_objects.js]
[browser_bug_865871_variables_view_close_on_esc_key.js]
[browser_bug_869003_inspect_cross_domain_object.js]
[browser_bug_871156_ctrlw_close_tab.js]
[browser_cached_messages.js]
[browser_console.js]
[browser_console_certificate_imminent_distrust.js]
[browser_console_clear_method.js]
[browser_console_clear_on_reload.js]
[browser_console_click_focus.js]
[browser_console_consolejsm_output.js]
[browser_console_copy_command.js]
subsuite = clipboard
skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
[browser_console_dead_objects.js]
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
[browser_console_copy_entire_message_context_menu.js]
subsuite = clipboard
skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
[browser_console_devtools_loader_exception.js]
[browser_console_error_source_click.js]
[browser_console_filters.js]
[browser_console_iframe_messages.js]
[browser_console_keyboard_accessibility.js]
[browser_console_log_inspectable_object.js]
[browser_console_native_getters.js]
[browser_console_navigation_marker.js]
[browser_console_netlogging.js]
[browser_console_nsiconsolemessage.js]
[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]
[browser_console_variables_view_dom_nodes.js]
[browser_console_variables_view_dont_sort_non_sortable_classes_properties.js]
[browser_console_variables_view_special_names.js]
[browser_console_variables_view_while_debugging.js]
[browser_console_variables_view_while_debugging_and_inspecting.js]
[browser_eval_in_debugger_stackframe.js]
[browser_eval_in_debugger_stackframe2.js]
[browser_jsterm_inspect.js]
skip-if = e10s && debug && (os == 'win' || os == 'mac') # Bug 1243966
[browser_longstring_hang.js]
[browser_output_breaks_after_console_dir_uninspectable.js]
[browser_output_longstring_expand.js]
[browser_repeated_messages_accuracy.js]
[browser_result_format_as_string.js]
[browser_warn_user_about_replaced_api.js]
[browser_webconsole_allow_mixedcontent_securityerrors.js]
tags = mcb
skip-if = (os == 'win' && bits == 64) # Bug 1390001
[browser_webconsole_script_errordoc_urls.js]
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
[browser_webconsole_assert.js]
[browser_webconsole_block_mixedcontent_securityerrors.js]
tags = mcb
skip-if = (os == 'win' && bits == 64) # Bug 1390001
[browser_webconsole_bug_579412_input_focus.js]
[browser_webconsole_bug_580001_closing_after_completion.js]
[browser_webconsole_bug_580030_errors_after_page_reload.js]
[browser_webconsole_bug_582201_duplicate_errors.js]
[browser_webconsole_bug_583816_No_input_and_Tab_key_pressed.js]
[browser_webconsole_bug_585237_line_limit.js]
[browser_webconsole_bug_585956_console_trace.js]
[browser_webconsole_bug_585991_autocomplete_keys.js]
[browser_webconsole_bug_585991_autocomplete_popup.js]
[browser_webconsole_bug_586388_select_all.js]
[browser_webconsole_bug_587617_output_copy.js]
subsuite = clipboard
skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
[browser_webconsole_bug_588342_document_focus.js]
[browser_webconsole_bug_588730_text_node_insertion.js]
[browser_webconsole_bug_588967_input_expansion.js]
[browser_webconsole_bug_589162_css_filter.js]
[browser_webconsole_bug_592442_closing_brackets.js]
[browser_webconsole_bug_593003_iframe_wrong_hud.js]
[browser_webconsole_bug_594497_history_arrow_keys.js]
[browser_webconsole_bug_595223_file_uri.js]
[browser_webconsole_bug_595350_multiple_windows_and_tabs.js]
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
[browser_webconsole_bug_595934_message_categories.js]
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
[browser_webconsole_bug_597103_deactivateHUDForContext_unfocused_window.js]
[browser_webconsole_bug_597136_external_script_errors.js]
[browser_webconsole_bug_597136_network_requests_from_chrome.js]
[browser_webconsole_bug_597460_filter_scroll.js]
[browser_webconsole_bug_597756_reopen_closed_tab.js]
[browser_webconsole_bug_599725_response_headers.js]
[browser_webconsole_bug_600183_charset.js]
[browser_webconsole_bug_601177_log_levels.js]
[browser_webconsole_bug_601352_scroll.js]
[browser_webconsole_bug_601667_filter_buttons.js]
[browser_webconsole_bug_603750_websocket.js]
[browser_webconsole_bug_611795.js]
[browser_webconsole_bug_613013_console_api_iframe.js]
[browser_webconsole_bug_613280_jsterm_copy.js]
subsuite = clipboard
skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
[browser_webconsole_bug_613642_maintain_scroll.js]
[browser_webconsole_bug_613642_prune_scroll.js]
[browser_webconsole_bug_614793_jsterm_scroll.js]
[browser_webconsole_bug_618078_network_exceptions.js]
[browser_webconsole_bug_621644_jsterm_dollar.js]
[browser_webconsole_bug_622303_persistent_filters.js]
[browser_webconsole_bug_623749_ctrl_a_select_all_winnt.js]
skip-if = os != "win"
[browser_webconsole_bug_630733_response_redirect_headers.js]
[browser_webconsole_bug_632275_getters_document_width.js]
[browser_webconsole_bug_632347_iterators_generators.js]
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
[browser_webconsole_bug_632817.js]
skip-if = true # Bug 1244707
[browser_webconsole_bug_642108_pruneTest.js]
[browser_webconsole_autocomplete_and_selfxss.js]
subsuite = clipboard
skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
[browser_webconsole_bug_644419_log_limits.js]
[browser_webconsole_bug_646025_console_file_location.js]
[browser_webconsole_bug_651501_document_body_autocomplete.js]
[browser_webconsole_bug_653531_highlighter_console_helper.js]
skip-if = true # Requires direct access to content nodes
[browser_webconsole_bug_658368_time_methods.js]
[browser_webconsole_bug_659907_console_dir.js]
[browser_webconsole_bug_660806_history_nav.js]
[browser_webconsole_bug_664131_console_group.js]
[browser_webconsole_bug_686937_autocomplete_JSTerm_helpers.js]
[browser_webconsole_bug_704295.js]
[browser_webconsole_bug_734061_No_input_change_and_Tab_key_pressed.js]
[browser_webconsole_bug_737873_mixedcontent.js]
tags = mcb
[browser_webconsole_bug_752559_ineffective_iframe_sandbox_warning.js]
skip-if = (os == 'win' && bits == 64) # Bug 1390001
[browser_webconsole_bug_762593_insecure_passwords_about_blank_web_console_warning.js]
[browser_webconsole_bug_762593_insecure_passwords_web_console_warning.js]
skip-if = true # Bug 1110500 - mouse event failure in test
[browser_webconsole_bug_764572_output_open_url.js]
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
[browser_webconsole_bug_766001_JS_Console_in_Debugger.js]
[browser_webconsole_bug_770099_violation.js]
skip-if = e10s && (os == 'win' || os == 'mac') # Bug 1243978
[browser_webconsole_bug_782653_CSS_links_in_Style_Editor.js]
[browser_webconsole_bug_804845_ctrl_key_nav.js]
skip-if = os != "mac"
[browser_webconsole_bug_817834_add_edited_input_to_history.js]
[browser_webconsole_bug_837351_securityerrors.js]
skip-if = (os == 'win' && bits == 64) # Bug 1390001
[browser_webconsole_filter_buttons_contextmenu.js]
[browser_webconsole_bug_1006027_message_timestamps_incorrect.js]
skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug intermittent)
[browser_webconsole_bug_1010953_cspro.js]
skip-if = e10s && (os == 'win' || os == 'mac') # Bug 1243967
[browser_webconsole_bug_1247459_violation.js]
skip-if = e10s && (os == 'win') # Bug 1264955
[browser_webconsole_certificate_messages.js]
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
[browser_webconsole_show_subresource_security_errors.js]
skip-if = e10s && (os == 'win' || os == 'mac') # Bug 1243987
[browser_webconsole_cached_autocomplete.js]
[browser_webconsole_chrome.js]
[browser_webconsole_clear_method.js]
[browser_webconsole_clickable_urls.js]
[browser_webconsole_closure_inspection.js]
[browser_webconsole_completion.js]
[browser_webconsole_console_extras.js]
[browser_webconsole_console_logging_api.js]
[browser_webconsole_console_logging_workers_api.js]
[browser_webconsole_console_trace_async.js]
[browser_webconsole_count.js]
[browser_webconsole_dont_navigate_on_doubleclick.js]
[browser_webconsole_execution_scope.js]
[browser_webconsole_for_of.js]
[browser_webconsole_history.js]
[browser_webconsole_hpkp_invalid-headers.js]
skip-if = (os == 'win' && bits == 64) # Bug 1390001
[browser_webconsole_hsts_invalid-headers.js]
skip-if = e10s # Bug 1042253 - webconsole e10s tests
[browser_webconsole_input_field_focus_on_panel_select.js]
[browser_webconsole_inspect-parsed-documents.js]
[browser_webconsole_js_input_expansion.js]
[browser_webconsole_jsterm.js]
skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug timeout)
[browser_webconsole_live_filtering_of_message_types.js]
[browser_webconsole_live_filtering_on_search_strings.js]
[browser_webconsole_message_node_id.js]
[browser_webconsole_multiline_input.js]
[browser_webconsole_netlogging.js]
skip-if = true # Bug 1298364
[browser_webconsole_netlogging_basic.js]
[browser_webconsole_netlogging_panel.js]
[browser_webconsole_netlogging_reset_filter.js]
[browser_webconsole_notifications.js]
[browser_webconsole_open-links-without-callback.js]
[browser_webconsole_promise.js]
[browser_webconsole_output_copy_newlines.js]
subsuite = clipboard
skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
[browser_webconsole_output_order.js]
[browser_webconsole_scratchpad_panel_link.js]
[browser_webconsole_split.js]
[browser_webconsole_split_escape_key.js]
[browser_webconsole_split_focus.js]
[browser_webconsole_split_persist.js]
[browser_webconsole_trackingprotection_errors.js]
tags = trackingprotection
skip-if = (os == 'win' && bits == 64) # Bug 1390001
[browser_webconsole_view_source.js]
skip-if = (os == 'win' && bits == 64) # Bug 1390001
[browser_webconsole_reflow.js]
[browser_webconsole_log_file_filter.js]
[browser_webconsole_expandable_timestamps.js]
[browser_webconsole_autocomplete_accessibility.js]
[browser_webconsole_autocomplete_in_debugger_stackframe.js]
[browser_webconsole_autocomplete_popup_close_on_tab_switch.js]
[browser_webconsole_autocomplete-properties-with-non-alphanumeric-names.js]
[browser_console_hide_jsterm_when_devtools_chrome_enabled_false.js]
[browser_console_history_persist.js]
[browser_webconsole_output_01.js]
[browser_webconsole_output_02.js]
[browser_webconsole_output_03.js]
[browser_webconsole_output_04.js]
[browser_webconsole_output_05.js]
[browser_webconsole_output_06.js]
[browser_webconsole_output_dom_elements_01.js]
[browser_webconsole_output_dom_elements_02.js]
skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug timeout)
[browser_webconsole_output_dom_elements_03.js]
skip-if = e10s # Bug 1241019
[browser_webconsole_output_dom_elements_04.js]
skip-if = e10s # Bug 1042253 - webconsole e10s tests (Linux debug timeout)
[browser_webconsole_output_dom_elements_05.js]
[browser_webconsole_output_events.js]
[browser_webconsole_output_regexp.js]
[browser_webconsole_output_table.js]
[browser_console_variables_view_highlighter.js]
[browser_webconsole_start_netmon_first.js]
[browser_webconsole_console_trace_duplicates.js]
[browser_webconsole_cd_iframe.js]
[browser_webconsole_autocomplete_crossdomain_iframe.js]
[browser_webconsole_console_custom_styles.js]
[browser_webconsole_console_api_stackframe.js]
[browser_webconsole_exception_stackframe.js]
[browser_webconsole_column_numbers.js]
[browser_console_open_or_focus.js]
[browser_webconsole_bug_922212_console_dirxml.js]
[browser_webconsole_shows_reqs_in_netmonitor.js]
[browser_netmonitor_shows_reqs_in_webconsole.js]
[browser_webconsole_bug_1050691_click_function_to_source.js]
[browser_webconsole_context_menu_open_in_var_view.js]
[browser_webconsole_context_menu_store_as_global.js]
[browser_webconsole_strict_mode_errors.js]

View File

@ -1,52 +0,0 @@
/* -*- 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/ */
// Tests that a file with an unsupported CSP directive ('reflected-xss filter')
// displays the appropriate message to the console.
"use strict";
const EXPECTED_RESULT = "Not supporting directive \u2018reflected-xss\u2019. " +
"Directive and values will be ignored.";
const TEST_FILE = "http://example.com/browser/devtools/client/webconsole/old/" +
"test/test_bug1045902_console_csp_ignore_reflected_xss_" +
"message.html";
var hud = undefined;
var TEST_URI = "data:text/html;charset=utf8,Web Console CSP ignoring " +
"reflected XSS (bug 1045902)";
add_task(function* () {
let { browser } = yield loadTab(TEST_URI);
hud = yield openConsole();
yield loadDocument(browser);
yield testViolationMessage();
hud = null;
});
function loadDocument(browser) {
hud.jsterm.clearOutput();
browser.loadURI(TEST_FILE);
return BrowserTestUtils.browserLoaded(browser);
}
function testViolationMessage() {
let aOutputNode = hud.outputNode;
return waitForSuccess({
name: "Confirming that CSP logs messages to the console when " +
"\u2018reflected-xss\u2019 directive is used!",
validator: function () {
console.log(aOutputNode.textContent);
let success = false;
success = aOutputNode.textContent.indexOf(EXPECTED_RESULT) > -1;
return success;
}
});
}

View File

@ -1,92 +0,0 @@
/* -*- 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/ */
// Tests if the JSTerm sandbox is updated when the user navigates from one
// domain to another, in order to avoid permission denied errors with a sandbox
// created for a different origin.
"use strict";
add_task(function* () {
const TEST_URI1 = "http://example.com/browser/devtools/client/webconsole/old/" +
"test/test-console.html";
const TEST_URI2 = "http://example.org/browser/devtools/client/webconsole/old/" +
"test/test-console.html";
yield loadTab(TEST_URI1);
let hud = yield openConsole();
hud.jsterm.clearOutput();
hud.jsterm.execute("window.location.href");
info("wait for window.location.href");
let msgForLocation1 = {
webconsole: hud,
messages: [
{
name: "window.location.href jsterm input",
text: "window.location.href",
category: CATEGORY_INPUT,
},
{
name: "window.location.href result is displayed",
text: TEST_URI1,
category: CATEGORY_OUTPUT,
},
],
};
yield waitForMessages(msgForLocation1);
// load second url
BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI2);
yield loadBrowser(gBrowser.selectedBrowser);
is(hud.outputNode.textContent.indexOf("Permission denied"), -1,
"no permission denied errors");
hud.jsterm.clearOutput();
hud.jsterm.execute("window.location.href");
info("wait for window.location.href after page navigation");
yield waitForMessages({
webconsole: hud,
messages: [
{
name: "window.location.href jsterm input",
text: "window.location.href",
category: CATEGORY_INPUT,
},
{
name: "window.location.href result is displayed",
text: TEST_URI2,
category: CATEGORY_OUTPUT,
},
],
});
is(hud.outputNode.textContent.indexOf("Permission denied"), -1,
"no permission denied errors");
// Navigation clears messages. Wait for that clear to happen before
// continuing the test or it might destroy messages we wait later on (Bug
// 1270234).
let cleared = hud.jsterm.once("messages-cleared");
gBrowser.goBack();
info("Waiting for messages to be cleared due to navigation");
yield cleared;
info("Messages cleared after navigation; checking location");
hud.jsterm.execute("window.location.href");
info("wait for window.location.href after goBack()");
yield waitForMessages(msgForLocation1);
is(hud.outputNode.textContent.indexOf("Permission denied"), -1,
"no permission denied errors");
});

View File

@ -1,109 +0,0 @@
/* -*- 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/ */
// Test for the "Copy link location" context menu item shown when you right
// click network requests in the output.
"use strict";
add_task(function* () {
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/old/" +
"test/test-console.html?_date=" + Date.now();
const COMMAND_NAME = "consoleCmd_copyURL";
const CONTEXT_MENU_ID = "#menu_copyURL";
registerCleanupFunction(() => {
Services.prefs.clearUserPref("devtools.webconsole.filter.networkinfo");
});
Services.prefs.setBoolPref("devtools.webconsole.filter.networkinfo", true);
yield loadTab(TEST_URI);
let hud = yield openConsole();
let output = hud.outputNode;
let menu = hud.iframeWindow.document.getElementById("output-contextmenu");
hud.jsterm.clearOutput();
yield ContentTask.spawn(gBrowser.selectedBrowser, null, function() {
content.console.log("bug 638949");
});
// Test that the "Copy Link Location" command is disabled for non-network
// messages.
let [result] = yield waitForMessages({
webconsole: hud,
messages: [{
text: "bug 638949",
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
}],
});
output.focus();
let message = [...result.matched][0];
goUpdateCommand(COMMAND_NAME);
ok(!isEnabled(), COMMAND_NAME + " is disabled");
// Test that the "Copy Link Location" menu item is hidden for non-network
// messages.
yield waitForContextMenu(menu, message, () => {
let isHidden = menu.querySelector(CONTEXT_MENU_ID).hidden;
ok(isHidden, CONTEXT_MENU_ID + " is hidden");
});
hud.jsterm.clearOutput();
// Reloading will produce network logging
gBrowser.reload();
// Test that the "Copy Link Location" command is enabled and works
// as expected for any network-related message.
// This command should copy only the URL.
[result] = yield waitForMessages({
webconsole: hud,
messages: [{
text: "test-console.html",
category: CATEGORY_NETWORK,
severity: SEVERITY_LOG,
}],
});
output.focus();
message = [...result.matched][0];
hud.ui.output.selectMessage(message);
goUpdateCommand(COMMAND_NAME);
ok(isEnabled(), COMMAND_NAME + " is enabled");
info("expected clipboard value: " + message.url);
let deferred = defer();
waitForClipboard((aData) => {
return aData.trim() == message.url;
}, () => {
goDoCommand(COMMAND_NAME);
}, () => {
deferred.resolve(null);
}, () => {
deferred.reject(null);
});
yield deferred.promise;
// Test that the "Copy Link Location" menu item is visible for network-related
// messages.
yield waitForContextMenu(menu, message, () => {
let isVisible = !menu.querySelector(CONTEXT_MENU_ID).hidden;
ok(isVisible, CONTEXT_MENU_ID + " is visible");
});
// Return whether "Copy Link Location" command is enabled or not.
function isEnabled() {
let controller = top.document.commandDispatcher
.getControllerForCommand(COMMAND_NAME);
return controller && controller.isCommandEnabled(COMMAND_NAME);
}
});

View File

@ -1,31 +0,0 @@
/* -*- 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 output for console.dir() works even if Logging filter is off.
"use strict";
const TEST_URI = "data:text/html;charset=utf8,<p>test for bug 862916";
add_task(function* () {
yield loadTab(TEST_URI);
let hud = yield openConsole();
ok(hud, "web console opened");
hud.setFilterState("log", false);
registerCleanupFunction(() => hud.setFilterState("log", true));
hud.jsterm.execute("window.fooBarz = 'bug862916'; " +
"console.dir(window)");
let varView = yield hud.jsterm.once("variablesview-fetched");
ok(varView, "variables view object");
yield findVariableViewProperties(varView, [
{ name: "fooBarz", value: "bug862916" },
], { webconsole: hud });
});

View File

@ -1,63 +0,0 @@
/* -*- 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/ */
// Test that makes sure messages are not considered repeated when console.log()
// is invoked with different objects, see bug 865288.
"use strict";
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/old/" +
"test/test-repeated-messages.html";
add_task(function* () {
yield loadTab(TEST_URI);
let hud = yield openConsole();
info("waiting for 3 console.log objects");
hud.jsterm.clearOutput(true);
hud.jsterm.execute("window.testConsoleObjects()");
let [result] = yield waitForMessages({
webconsole: hud,
messages: [{
name: "3 console.log messages",
text: "abba",
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
count: 3,
repeats: 1,
objects: true,
}],
});
let msgs = [...result.matched];
is(msgs.length, 3, "3 message elements");
for (let i = 0; i < msgs.length; i++) {
info("test message element #" + i);
let msg = msgs[i];
let clickable = msg.querySelector(".message-body a");
ok(clickable, "clickable object #" + i);
msg.scrollIntoView(false);
yield clickObject(clickable, i);
}
function* clickObject(obj, i) {
executeSoon(() => {
EventUtils.synthesizeMouse(obj, 2, 2, {}, hud.iframeWindow);
});
let varView = yield hud.jsterm.once("variablesview-fetched");
ok(varView, "variables view fetched #" + i);
yield findVariableViewProperties(varView, [
{ name: "id", value: "abba" + i },
], { webconsole: hud });
}
});

View File

@ -1,75 +0,0 @@
/* -*- 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 variables view sidebar can be closed by pressing Escape in the
// web console.
"use strict";
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/old/" +
"test/test-eval-in-stackframe.html";
function test() {
let hud;
Task.spawn(runner).then(finishTest);
function* runner() {
let {tab} = yield loadTab(TEST_URI);
hud = yield openConsole(tab);
let jsterm = hud.jsterm;
let result;
let vview;
let msg;
yield openSidebar("fooObj",
'testProp: "testValue"',
{ name: "testProp", value: "testValue" });
let prop = result.matchedProp;
ok(prop, "matched the |testProp| property in the variables view");
vview.window.focus();
let sidebarClosed = jsterm.once("sidebar-closed");
EventUtils.synthesizeKey("KEY_Escape");
yield sidebarClosed;
jsterm.clearOutput();
yield openSidebar("window.location",
"Location \u2192 http://example.com/browser/",
{ name: "host", value: "example.com" });
vview.window.focus();
msg.scrollIntoView();
sidebarClosed = jsterm.once("sidebar-closed");
EventUtils.synthesizeKey("KEY_Escape");
yield sidebarClosed;
function* openSidebar(objName, expectedText, expectedObj) {
msg = yield jsterm.execute(objName);
ok(msg, "output message found");
let anchor = msg.querySelector("a");
let body = msg.querySelector(".message-body");
ok(anchor, "object anchor");
ok(body, "message body");
ok(body.textContent.includes(expectedText), "message text check");
msg.scrollIntoView();
yield EventUtils.synthesizeMouse(anchor, 2, 2, {}, hud.iframeWindow);
let vviewVar = yield jsterm.once("variablesview-fetched");
vview = vviewVar._variablesView;
ok(vview, "variables view object exists");
[result] = yield findVariableViewProperties(vviewVar, [
expectedObj,
], { webconsole: hud });
}
}
}

View File

@ -1,83 +0,0 @@
/* -*- 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 users can inspect objects logged from cross-domain iframes -
// bug 869003.
"use strict";
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/old/" +
"test/test-bug-869003-top-window.html";
add_task(function* () {
// This test is slightly more involved: it opens the web console, then the
// variables view for a given object, it updates a property in the view and
// checks the result. We can get a timeout with debug builds on slower
// machines.
requestLongerTimeout(2);
yield loadTab("data:text/html;charset=utf8,<p>hello");
let hud = yield openConsole();
BrowserTestUtils.loadURI(gBrowser.selectedBrowser, TEST_URI);
let [result] = yield waitForMessages({
webconsole: hud,
messages: [{
name: "console.log message",
text: "foobar",
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
objects: true,
}],
});
let msg = [...result.matched][0];
ok(msg, "message element");
let body = msg.querySelector(".message-body");
ok(body, "message body");
ok(body.textContent.includes('{ hello: "world!",'), "message text check");
ok(body.textContent.includes('function func()'), "message text check");
yield testClickable(result.clickableElements[0], [
{ name: "hello", value: "world!" },
{ name: "bug", value: 869003 },
], hud);
yield testClickable(result.clickableElements[1], [
{ name: "hello", value: "world!" },
{ name: "name", value: "func" },
{ name: "length", value: 1 },
], hud);
});
function* testClickable(clickable, props, hud) {
ok(clickable, "clickable object found");
executeSoon(() => {
EventUtils.synthesizeMouse(clickable, 2, 2, {}, hud.iframeWindow);
});
let aVar = yield hud.jsterm.once("variablesview-fetched");
ok(aVar, "variables view fetched");
ok(aVar._variablesView, "variables view object");
let [result] = yield findVariableViewProperties(aVar, props, { webconsole: hud });
let prop = result.matchedProp;
ok(prop, "matched the |" + props[0].name + "| property in the variables view");
// Check that property value updates work.
aVar = yield updateVariablesViewProperty({
property: prop,
field: "value",
string: "'omgtest'",
webconsole: hud,
});
info("onFetchAfterUpdate");
props[0].value = "omgtest";
yield findVariableViewProperties(aVar, props, { webconsole: hud });
}

View File

@ -1,76 +0,0 @@
/* -*- 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 Ctrl-W closes the Browser Console and that Ctrl-W closes the
// current tab when using the Web Console - bug 871156.
"use strict";
add_task(function* () {
const TEST_URI = "data:text/html;charset=utf8,<title>bug871156</title>\n" +
"<p>hello world";
let firstTab = gBrowser.selectedTab;
Services.prefs.setBoolPref("toolkit.cosmeticAnimations.enabled", false);
registerCleanupFunction(() => {
Services.prefs.clearUserPref("toolkit.cosmeticAnimations.enabled");
});
yield loadTab(TEST_URI);
let hud = yield openConsole();
ok(hud, "Web Console opened");
let tabClosed = defer();
let toolboxDestroyed = defer();
let tabSelected = defer();
let target = TargetFactory.forTab(gBrowser.selectedTab);
let toolbox = gDevTools.getToolbox(target);
gBrowser.tabContainer.addEventListener("TabClose", function () {
info("tab closed");
tabClosed.resolve(null);
}, {once: true});
gBrowser.tabContainer.addEventListener("TabSelect", function () {
if (gBrowser.selectedTab == firstTab) {
info("tab selected");
tabSelected.resolve(null);
}
}, {once: true});
toolbox.once("destroyed", () => {
info("toolbox destroyed");
toolboxDestroyed.resolve(null);
});
// Get out of the web console initialization.
executeSoon(() => {
EventUtils.synthesizeKey("w", { accelKey: true });
});
yield promise.all([tabClosed.promise, toolboxDestroyed.promise,
tabSelected.promise]);
info("promise.all resolved. start testing the Browser Console");
hud = yield HUDService.toggleBrowserConsole();
ok(hud, "Browser Console opened");
let deferred = defer();
Services.obs.addObserver(function onDestroy() {
Services.obs.removeObserver(onDestroy, "web-console-destroyed");
ok(true, "the Browser Console closed");
deferred.resolve(null);
}, "web-console-destroyed");
waitForFocus(() => {
EventUtils.synthesizeKey("w", { accelKey: true }, hud.iframeWindow);
}, hud.iframeWindow);
yield deferred.promise;
});

View File

@ -1,59 +0,0 @@
/* -*- 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/ */
// Test to see if the cached messages are displayed when the console UI is
// opened.
"use strict";
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/old/" +
"test/test-webconsole-error-observer.html";
// On e10s, the exception is triggered in child process
// and is ignored by test harness
if (!Services.appinfo.browserTabsRemoteAutostart) {
expectUncaughtException();
}
function test() {
waitForExplicitFinish();
loadTab(TEST_URI).then(testOpenUI);
}
function testOpenUI(aTestReopen) {
openConsole().then((hud) => {
waitForMessages({
webconsole: hud,
messages: [
{
text: "log Bazzle",
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
},
{
text: "error Bazzle",
category: CATEGORY_WEBDEV,
severity: SEVERITY_ERROR,
},
{
text: "bazBug611032",
category: CATEGORY_JS,
severity: SEVERITY_ERROR,
},
{
text: "cssColorBug611032",
category: CATEGORY_CSS,
severity: SEVERITY_WARNING,
},
],
}).then(() => {
closeConsole(gBrowser.selectedTab).then(() => {
aTestReopen && info("will reopen the Web Console");
executeSoon(aTestReopen ? testOpenUI : finishTest);
});
});
});
}

View File

@ -1,246 +0,0 @@
/* -*- 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/ */
// Test the basic features of the Browser Console, bug 587757.
"use strict";
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/old/" +
"test/test-console.html?" + Date.now();
const TEST_FILE = "chrome://mochitests/content/browser/devtools/client/" +
"webconsole/old/test/test-cu-reporterror.js";
const TEST_XHR_ERROR_URI = `http://example.com/404.html?${Date.now()}`;
const TEST_IMAGE = "http://example.com/browser/devtools/client/webconsole/old/" +
"test/test-image.png";
const ObjectClient = require("devtools/shared/client/object-client");
add_task(function* () {
yield loadTab(TEST_URI);
let opened = waitForBrowserConsole();
let hud = HUDService.getBrowserConsole();
ok(!hud, "browser console is not open");
info("wait for the browser console to open with ctrl-shift-j");
EventUtils.synthesizeKey("j", { accelKey: true, shiftKey: true }, window);
hud = yield opened;
ok(hud, "browser console opened");
yield testMessages(hud);
yield testCPOWInspection(hud);
});
function testMessages(hud) {
hud.jsterm.clearOutput(true);
expectUncaughtException();
executeSoon(() => {
foobarExceptionBug587757();
});
// Add a message from a chrome window.
hud.iframeWindow.console.log("bug587757a");
// Check Cu.reportError stack.
// Use another js script to not depend on the test file line numbers.
Services.scriptloader.loadSubScript(TEST_FILE, hud.iframeWindow);
// Bug 1348885: test that error from nuked globals do not throw
let sandbox = new Cu.Sandbox(null, {
wantComponents: false,
wantGlobalProperties: ["URL", "URLSearchParams"],
});
let error = Cu.evalInSandbox(`
new Error("1348885");
`, sandbox);
Cu.reportError(error);
Cu.nukeSandbox(sandbox);
// Add a message from a content window.
gBrowser.contentWindowAsCPOW.console.log("bug587757b");
// Test eval.
hud.jsterm.execute("document.location.href");
// Test eval frame script
hud.jsterm.execute(`
gBrowser.selectedBrowser.messageManager.loadFrameScript('data:application/javascript,console.log("framescript-message")', false);
"framescript-eval";
`);
// Check for network requests.
let xhr = new XMLHttpRequest();
xhr.onload = () => console.log("xhr loaded, status is: " + xhr.status);
xhr.open("get", TEST_URI, true);
xhr.send();
// Check for xhr error.
let xhrErr = new XMLHttpRequest();
xhrErr.onload = () => {
console.log("xhr error loaded, status is: " + xhrErr.status);
};
xhrErr.open("get", TEST_XHR_ERROR_URI, true);
xhrErr.send();
// Check that Fetch requests are categorized as "XHR".
fetch(TEST_IMAGE).then(() => { console.log("fetch loaded"); });
return waitForMessages({
webconsole: hud,
messages: [
{
name: "chrome window console.log() is displayed",
text: "bug587757a",
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
},
{
name: "Cu.reportError is displayed",
text: "bug1141222",
category: CATEGORY_JS,
severity: SEVERITY_ERROR,
stacktrace: [{
file: TEST_FILE,
line: 2,
}, {
file: TEST_FILE,
line: 4,
},
// Ignore the rest of the stack,
// just assert Cu.reportError call site
// and consoleOpened call
]
},
{
name: "Error from nuked global works",
text: "1348885",
category: CATEGORY_JS,
severity: SEVERITY_ERROR,
},
{
name: "content window console.log() is displayed",
text: "bug587757b",
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
},
{
name: "jsterm eval result",
text: "browser.xul",
category: CATEGORY_OUTPUT,
severity: SEVERITY_LOG,
},
{
name: "jsterm eval result 2",
text: "framescript-eval",
category: CATEGORY_OUTPUT,
severity: SEVERITY_LOG,
},
{
name: "frame script message",
text: "framescript-message",
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
},
{
name: "exception message",
text: "foobarExceptionBug587757",
category: CATEGORY_JS,
severity: SEVERITY_ERROR,
},
{
name: "network message",
text: "test-console.html",
category: CATEGORY_NETWORK,
severity: SEVERITY_INFO,
isXhr: true,
},
{
name: "xhr error message",
text: "404.html",
category: CATEGORY_NETWORK,
severity: SEVERITY_ERROR,
isXhr: true,
},
{
name: "network message",
text: "test-image.png",
category: CATEGORY_NETWORK,
severity: SEVERITY_INFO,
isXhr: true,
},
],
});
}
function* testCPOWInspection(hud) {
// Directly request evaluation to get an actor for the selected browser.
// Note that this doesn't actually render a message, and instead allows us
// us to assert that inspecting an object doesn't throw in the server.
// This would be done in a mochitest-chrome suite, but that doesn't run in
// e10s, so it's harder to get ahold of a CPOW.
let cpowEval = yield hud.jsterm.requestEvaluation("gBrowser.selectedBrowser");
info("Creating an ObjectClient with: " + cpowEval.result.actor);
let objectClient = new ObjectClient(hud.jsterm.hud.proxy.client, {
actor: cpowEval.result.actor,
});
// Before the fix for Bug 1382833, this wouldn't resolve due to a CPOW error
// in the ObjectActor.
let prototypeAndProperties = yield objectClient.getPrototypeAndProperties();
// Just a sanity check to make sure a valid packet came back
is(prototypeAndProperties.prototype.class, "XBL prototype JSClass",
"Looks like a valid response");
// The CPOW is in the _contentWindow property.
let cpow = prototypeAndProperties.ownProperties._contentWindow.value;
// But it's only a CPOW in e10s.
let e10sCheck = yield hud.jsterm.requestEvaluation(
"Cu.isCrossProcessWrapper(gBrowser.selectedBrowser._contentWindow)");
if (!e10sCheck.result) {
is(cpow.class, "Window", "The object is not a CPOW.");
return;
}
is(cpow.class, "CPOW: Window", "The CPOW grip has the right class.");
// Check that various protocol request methods work for the CPOW.
let response, slice;
let objClient = new ObjectClient(hud.jsterm.hud.proxy.client, cpow);
response = yield objClient.getPrototypeAndProperties();
is(Reflect.ownKeys(response.ownProperties).length, 0, "No property was retrieved.");
is(response.ownSymbols.length, 0, "No symbol property was retrieved.");
is(response.prototype.type, "null", "The prototype is null.");
response = yield objClient.enumProperties({ignoreIndexedProperties: true});
slice = yield response.iterator.slice(0, response.iterator.count);
is(Reflect.ownKeys(slice.ownProperties).length, 0, "No property was retrieved.");
response = yield objClient.enumProperties({});
slice = yield response.iterator.slice(0, response.iterator.count);
is(Reflect.ownKeys(slice.ownProperties).length, 0, "No property was retrieved.");
response = yield objClient.getOwnPropertyNames();
is(response.ownPropertyNames.length, 0, "No property was retrieved.");
response = yield objClient.getProperty("x");
is(response.descriptor, undefined, "The property does not exist.");
response = yield objClient.enumSymbols();
slice = yield response.iterator.slice(0, response.iterator.count);
is(slice.ownSymbols.length, 0, "No symbol property was retrieved.");
response = yield objClient.getPrototype();
is(response.prototype.type, "null", "The prototype is null.");
response = yield objClient.getDisplayString();
is(response.displayString, "<cpow>", "The CPOW stringifies to <cpow>");
}

View File

@ -1,99 +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";
// Tests handling of certificates that will be imminently distrusted, and thus
// should emit a warning to the console.
//
// This test requires a cert to be created in build/pgo/certs.
//
// Change directories to build/pgo/certs:
// cd build/pgo/certs
//
// certutil -S -d . -n "imminently_distrusted" -s "CN=Imminently Distrusted End Entity" -c "pgo temporary ca" -t "P,," -k rsa -g 2048 -Z SHA256 -m 1519140221 -v 120 -8 "imminently-distrusted.example.com"
//
const TEST_URI = "data:text/html;charset=utf8,Browser Console imminent " +
"distrust warnings test";
const TEST_URI_PATH = "/browser/devtools/client/webconsole/old/test/" +
"test-certificate-messages.html";
var gWebconsoleTests = [
{url: "https://sha256ee.example.com" + TEST_URI_PATH,
name: "Imminent distrust warnings appropriately not present",
warning: [], nowarning: ["Upcoming_Distrust_Actions"]},
{url: "https://imminently-distrusted.example.com" +
TEST_URI_PATH,
name: "Imminent distrust warning displayed successfully",
warning: ["Upcoming_Distrust_Actions"], nowarning: []},
];
const TRIGGER_MSG = "If you haven't seen ssl warnings yet, you won't";
var gHud = undefined, gContentBrowser;
var gCurrentTest;
function test() {
registerCleanupFunction(function () {
gHud = gContentBrowser = null;
});
loadTab(TEST_URI).then(({browser}) => {
gContentBrowser = browser;
let opened = waitForBrowserConsole();
let hud = HUDService.getBrowserConsole();
ok(!hud, "browser console is not open");
HUDService.toggleBrowserConsole();
opened.then(function (hud) {
ok(hud, "browser console opened");
runTestLoop(hud);
});
});
}
function runTestLoop(theHud) {
gCurrentTest = gWebconsoleTests.shift();
if (!gCurrentTest) {
finishTest();
return;
}
if (!gHud) {
gHud = theHud;
}
gHud.jsterm.clearOutput();
BrowserTestUtils.browserLoaded(gContentBrowser).then(onLoad);
if (gCurrentTest.pref) {
SpecialPowers.pushPrefEnv({"set": gCurrentTest.pref},
function () {
BrowserTestUtils.loadURI(gBrowser.selectedBrowser, gCurrentTest.url);
});
} else {
BrowserTestUtils.loadURI(gBrowser.selectedBrowser, gCurrentTest.url);
}
}
function onLoad() {
waitForSuccess({
name: gCurrentTest.name,
validator: function () {
if (gHud.outputNode.textContent.includes(TRIGGER_MSG)) {
for (let warning of gCurrentTest.warning) {
if (!gHud.outputNode.textContent.includes(warning)) {
return false;
}
}
for (let nowarning of gCurrentTest.nowarning) {
if (gHud.outputNode.textContent.includes(nowarning)) {
return false;
}
}
return true;
}
}
}).then(runTestLoop);
}

Some files were not shown because too many files have changed in this diff Show More