This commit is contained in:
Ryan VanderMeulen 2016-08-19 09:54:25 -04:00
Родитель 01c4e8cc1b d85ca4e057
Коммит ca004651b3
87 изменённых файлов: 1588 добавлений и 703 удалений

Просмотреть файл

@ -182,7 +182,7 @@ function makeNewWindow(domWindow, browserHint = false) {
return new Window(domWindow);
}
for (let domWindow of windows()) {
for (let domWindow of windows(null, {includePrivate: supportPrivateWindows})) {
let window = makeNewWindow(domWindow);
if (window instanceof BrowserWindow)
addListItem(browserWindows, window);

Просмотреть файл

@ -1,9 +1,8 @@
<?xml version="1.0"?>
# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
# 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/.
<!-- 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/. -->
<!DOCTYPE prefwindow SYSTEM "chrome://browser/locale/preferences/connection.dtd">
@ -15,16 +14,12 @@
dlgbuttons="accept,cancel,help"
onbeforeaccept="return gConnectionsDialog.beforeAccept();"
onload="gConnectionsDialog.checkForSystemProxy();"
ondialoghelp="openPrefsHelp()"
#ifdef XP_MACOSX
style="width: &window.macWidth; !important;">
#else
style="width: &window.width; !important;">
#endif
ondialoghelp="openPrefsHelp()">
<script type="application/javascript" src="chrome://browser/content/utilityOverlay.js"/>
<prefpane id="ConnectionsDialogPane"
class="largeDialogContainer"
helpTopic="prefs-connection-settings">
<preferences>
@ -59,7 +54,7 @@
<preference id="network.proxy.backup.socks" name="network.proxy.backup.socks" type="string"/>
<preference id="network.proxy.backup.socks_port" name="network.proxy.backup.socks_port" type="int"/>
</preferences>
<script type="application/javascript" src="chrome://browser/content/preferences/connection.js"/>
<stringbundle id="preferencesBundle" src="chrome://browser/locale/preferences/preferences.properties"/>
@ -156,7 +151,7 @@
oninput="gConnectionsDialog.updateReloadButton();"/>
<button id="autoReload" icon="refresh"
label="&reload.label;" accesskey="&reload.accesskey;"
oncommand="gConnectionsDialog.reloadPAC();"
oncommand="gConnectionsDialog.reloadPAC();"
preference="pref.advanced.proxies.disable_button.reload"/>
</hbox>
</radiogroup>
@ -171,4 +166,3 @@
<separator/>
</prefpane>
</prefwindow>

Просмотреть файл

@ -351,10 +351,7 @@ var gAdvancedPane = {
*/
showConnections: function ()
{
openDialog("chrome://browser/content/preferences/connection.xul",
"mozilla:connectionmanager",
"modal=yes",
null);
gSubDialog.open("chrome://browser/content/preferences/connection.xul");
},
// Retrieves the amount of space currently used by disk cache
@ -506,10 +503,8 @@ var gAdvancedPane = {
manageCapability : Components.interfaces.nsIPermissionManager.DENY_ACTION,
windowTitle : bundlePreferences.getString("offlinepermissionstitle"),
introText : bundlePreferences.getString("offlinepermissionstext") };
openDialog("chrome://browser/content/preferences/permissions.xul",
"Browser:Permissions",
"modal=yes",
params);
gSubDialog.open("chrome://browser/content/preferences/permissions.xul",
null, params);
},
// XXX: duplicated in browser.js
@ -779,9 +774,7 @@ var gAdvancedPane = {
*/
showSecurityDevices: function ()
{
openDialog("chrome://pippki/content/device_manager.xul",
"mozilla:devicemanager",
"modal=yes", null);
gSubDialog.open("chrome://pippki/content/device_manager.xul");
},
observe: function (aSubject, aTopic, aData) {

Просмотреть файл

@ -208,7 +208,7 @@ var gSearchPane = {
onTreeSelect: function() {
document.getElementById("removeEngineButton").disabled =
gEngineView.selectedIndex == -1 || gEngineView.lastIndex == 0;
!gEngineView.isEngineSelectedAndRemovable();
},
onTreeKeyPress: function(aEvent) {
@ -229,7 +229,9 @@ var gSearchPane = {
(!isMac && aEvent.keyCode == KeyEvent.DOM_VK_F2)) {
tree.startEditing(index, tree.columns.getLastColumn());
} else if (aEvent.keyCode == KeyEvent.DOM_VK_DELETE ||
isMac && aEvent.shiftKey && aEvent.keyCode == KeyEvent.DOM_VK_BACK_SPACE) {
(isMac && aEvent.shiftKey &&
aEvent.keyCode == KeyEvent.DOM_VK_BACK_SPACE &&
gEngineView.isEngineSelectedAndRemovable())) {
// Delete and Shift+Backspace (Mac) removes selected engine.
Services.search.removeEngine(gEngineView.selectedEngine.originalEngine);
}
@ -387,6 +389,10 @@ EngineStore.prototype = {
},
removeEngine: function ES_removeEngine(aEngine) {
if (this._engines.length == 1) {
throw new Error("Cannot remove last engine!");
}
let engineName = aEngine.name;
let index = this._engines.findIndex(element => element.name == engineName);
@ -490,6 +496,10 @@ EngineView.prototype = {
return column.id == "engineShown";
},
isEngineSelectedAndRemovable: function() {
return this.selectedIndex != -1 && this.lastIndex != 0;
},
// nsITreeView
get rowCount() {
return this._engineStore.engines.length;

Просмотреть файл

@ -4,6 +4,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/Task.jsm");
function test() {
waitForExplicitFinish();
@ -18,53 +19,30 @@ function test() {
});
let connectionURL = "chrome://browser/content/preferences/connection.xul";
let windowWatcher = Services.ww;
// instantApply must be true, otherwise connection dialog won't save
// when opened from in-content prefs
Services.prefs.setBoolPref("browser.preferences.instantApply", true);
// this observer is registered after the pref tab loads
let observer = {
observe: function(aSubject, aTopic, aData) {
if (aTopic == "domwindowopened") {
// when connection window loads, run tests and acceptDialog()
let win = aSubject.QueryInterface(Components.interfaces.nsIDOMWindow);
win.addEventListener("load", function winLoadListener() {
win.removeEventListener("load", winLoadListener, false);
if (win.location.href == connectionURL) {
ok(true, "connection window opened");
runConnectionTests(win);
win.document.documentElement.acceptDialog();
}
}, false);
} else if (aTopic == "domwindowclosed") {
// finish up when connection window closes
let win = aSubject.QueryInterface(Components.interfaces.nsIDOMWindow);
if (win.location.href == connectionURL) {
windowWatcher.unregisterNotification(observer);
ok(true, "connection window closed");
// runConnectionTests will have changed this pref - make sure it was
// sanitized correctly when the dialog was accepted
is(Services.prefs.getCharPref("network.proxy.no_proxies_on"),
".a.com,.b.com,.c.com", "no_proxies_on pref has correct value");
gBrowser.removeCurrentTab();
finish();
}
}
}
};
/*
The connection dialog alone won't save onaccept since it uses type="child",
so it has to be opened as a sub dialog of the main pref tab.
Open the main tab here.
*/
open_preferences(function tabOpened(aContentWindow) {
open_preferences(Task.async(function* tabOpened(aContentWindow) {
is(gBrowser.currentURI.spec, "about:preferences", "about:preferences loaded");
windowWatcher.registerNotification(observer);
gBrowser.contentWindow.gAdvancedPane.showConnections();
});
let dialog = yield openAndLoadSubDialog(connectionURL);
let dialogClosingPromise = waitForEvent(dialog.document.documentElement, "dialogclosing");
ok(dialog, "connection window opened");
runConnectionTests(dialog);
dialog.document.documentElement.acceptDialog();
let dialogClosingEvent = yield dialogClosingPromise;
ok(dialogClosingEvent, "connection window closed");
// runConnectionTests will have changed this pref - make sure it was
// sanitized correctly when the dialog was accepted
is(Services.prefs.getCharPref("network.proxy.no_proxies_on"),
".a.com,.b.com,.c.com", "no_proxies_on pref has correct value");
gBrowser.removeCurrentTab();
finish();
}));
}
// run a bunch of tests on the window containing connection.xul

Просмотреть файл

@ -3,25 +3,13 @@
* http://creativecommons.org/publicdomain/zero/1.0/ */
Components.utils.import("resource://gre/modules/Services.jsm");
// From browser/components/preferences/in-content/test/head.js
function open_preferences(aCallback) {
gBrowser.selectedTab = gBrowser.addTab("about:preferences");
let newTabBrowser = gBrowser.getBrowserForTab(gBrowser.selectedTab);
newTabBrowser.addEventListener("Initialized", function () {
newTabBrowser.removeEventListener("Initialized", arguments.callee, true);
aCallback(gBrowser.contentWindow);
}, true);
}
Components.utils.import("resource://gre/modules/Task.jsm");
function test() {
waitForExplicitFinish();
let connectionTests = runConnectionTestsGen();
connectionTests.next();
const connectionURL = "chrome://browser/content/preferences/connection.xul";
let closeable = false;
let finalTest = false;
let prefWin;
// The changed preferences need to be backed up and restored because this mochitest
// changes them setting from the default
@ -38,55 +26,34 @@ function test() {
Services.prefs.clearUserPref("network.proxy.backup." + proxyType);
Services.prefs.clearUserPref("network.proxy.backup." + proxyType + "_port");
}
try {
Services.ww.unregisterNotification(observer);
} catch (e) {
// Do nothing, if the test was successful the above line should fail silently.
}
});
// this observer is registered after the pref tab loads
let observer = {
observe: function(aSubject, aTopic, aData) {
if (aTopic == "domwindowopened") {
// when the connection window loads, proceed forward in test
let win = aSubject.QueryInterface(Components.interfaces.nsIDOMWindow);
win.addEventListener("load", function winLoadListener() {
win.removeEventListener("load", winLoadListener);
if (win.location.href == connectionURL) {
// If this is a connection window, run the next test
connectionTests.next(win);
}
});
} else if (aTopic == "domwindowclosed") {
// Check if the window should have closed, and respawn another window for further testing
let win = aSubject.QueryInterface(Components.interfaces.nsIDOMWindow);
if (win.location.href == connectionURL) {
ok(closeable, "Connection dialog closed");
// Last close event, don't respawn, and clean up
if (finalTest) {
Services.ww.unregisterNotification(observer);
gBrowser.removeCurrentTab();
finish();
return;
}
// Open another connection pane for the next test
gBrowser.contentWindow.gAdvancedPane.showConnections();
}
}
}
};
// The actual tests to run, in a generator
function* runConnectionTestsGen() {
let doc, connectionWin, proxyTypePref, sharePref, httpPref, httpPortPref, ftpPref, ftpPortPref;
/*
The connection dialog alone won't save onaccept since it uses type="child",
so it has to be opened as a sub dialog of the main pref tab.
Open the main tab here.
*/
open_preferences(Task.async(function* tabOpened(aContentWindow) {
let dialog, dialogClosingPromise;
let doc, proxyTypePref, sharePref, httpPref, httpPortPref, ftpPref, ftpPortPref;
// Convenient function to reset the variables for the new window
function setDoc(win) {
doc = win.document;
connectionWin = win;
function* setDoc() {
if (closeable) {
let dialogClosingEvent = yield dialogClosingPromise;
ok(dialogClosingEvent, "Connection dialog closed");
}
if (finalTest) {
gBrowser.removeCurrentTab();
finish();
return;
}
dialog = yield openAndLoadSubDialog(connectionURL);
dialogClosingPromise = waitForEvent(dialog.document.documentElement, "dialogclosing");
doc = dialog.document;
proxyTypePref = doc.getElementById("network.proxy.type");
sharePref = doc.getElementById("network.proxy.share_proxy_settings");
httpPref = doc.getElementById("network.proxy.http");
@ -96,7 +63,7 @@ function test() {
}
// This batch of tests should not close the dialog
setDoc(yield null);
yield setDoc();
// Testing HTTP port 0 with share on
proxyTypePref.value = 1;
@ -126,7 +93,7 @@ function test() {
doc.documentElement.acceptDialog();
// HTTP 80, FTP 0, with share on
setDoc(yield null);
yield setDoc();
proxyTypePref.value = 1;
sharePref.value = true;
ftpPref.value = "localhost";
@ -136,7 +103,7 @@ function test() {
doc.documentElement.acceptDialog();
// HTTP host empty, port 0 with share on
setDoc(yield null);
yield setDoc();
proxyTypePref.value = 1;
sharePref.value = true;
httpPref.value = "";
@ -144,7 +111,7 @@ function test() {
doc.documentElement.acceptDialog();
// HTTP 0, but in no proxy mode
setDoc(yield null);
yield setDoc();
proxyTypePref.value = 0;
sharePref.value = true;
httpPref.value = "localhost";
@ -153,16 +120,6 @@ function test() {
// This is the final test, don't spawn another connection window
finalTest = true;
doc.documentElement.acceptDialog();
yield null;
}
/*
The connection dialog alone won't save onaccept since it uses type="child",
so it has to be opened as a sub dialog of the main pref tab.
Open the main tab here.
*/
open_preferences(function tabOpened(aContentWindow) {
Services.ww.registerNotification(observer);
gBrowser.contentWindow.gAdvancedPane.showConnections();
});
yield setDoc();
}));
}

Просмотреть файл

@ -4,6 +4,7 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/Task.jsm");
function test() {
waitForExplicitFinish();
@ -27,11 +28,6 @@ function test() {
});
let connectionURL = "chrome://browser/content/preferences/connection.xul";
let windowWatcher = Services.ww;
// instantApply must be true, otherwise connection dialog won't save
// when opened from in-content prefs
Services.prefs.setBoolPref("browser.preferences.instantApply", true);
// Set a shared proxy and a SOCKS backup
Services.prefs.setIntPref("network.proxy.type", 1);
@ -43,46 +39,27 @@ function test() {
Services.prefs.setCharPref("network.proxy.backup.socks", "127.0.0.1");
Services.prefs.setIntPref("network.proxy.backup.socks_port", 9050);
// this observer is registered after the pref tab loads
let observer = {
observe: function(aSubject, aTopic, aData) {
if (aTopic == "domwindowopened") {
// when connection window loads, run tests and acceptDialog()
let win = aSubject.QueryInterface(Components.interfaces.nsIDOMWindow);
win.addEventListener("load", function winLoadListener() {
win.removeEventListener("load", winLoadListener, false);
if (win.location.href == connectionURL) {
ok(true, "connection window opened");
win.document.documentElement.acceptDialog();
}
}, false);
} else if (aTopic == "domwindowclosed") {
// finish up when connection window closes
let win = aSubject.QueryInterface(Components.interfaces.nsIDOMWindow);
if (win.location.href == connectionURL) {
windowWatcher.unregisterNotification(observer);
ok(true, "connection window closed");
// The SOCKS backup should not be replaced by the shared value
is(Services.prefs.getCharPref("network.proxy.backup.socks"), "127.0.0.1", "Shared proxy backup shouldn't be replaced");
is(Services.prefs.getIntPref("network.proxy.backup.socks_port"), 9050, "Shared proxy port backup shouldn't be replaced");
gBrowser.removeCurrentTab();
finish();
}
}
}
};
/*
The connection dialog alone won't save onaccept since it uses type="child",
so it has to be opened as a sub dialog of the main pref tab.
Open the main tab here.
*/
open_preferences(function tabOpened(aContentWindow) {
open_preferences(Task.async(function* tabOpened(aContentWindow) {
is(gBrowser.currentURI.spec, "about:preferences", "about:preferences loaded");
windowWatcher.registerNotification(observer);
gBrowser.contentWindow.gAdvancedPane.showConnections();
});
}
let dialog = yield openAndLoadSubDialog(connectionURL);
let dialogClosingPromise = waitForEvent(dialog.document.documentElement, "dialogclosing");
ok(dialog, "connection window opened");
dialog.document.documentElement.acceptDialog();
let dialogClosingEvent = yield dialogClosingPromise;
ok(dialogClosingEvent, "connection window closed");
// The SOCKS backup should not be replaced by the shared value
is(Services.prefs.getCharPref("network.proxy.backup.socks"), "127.0.0.1", "Shared proxy backup shouldn't be replaced");
is(Services.prefs.getIntPref("network.proxy.backup.socks_port"), 9050, "Shared proxy port backup shouldn't be replaced");
gBrowser.removeCurrentTab();
finish();
}));
}

Просмотреть файл

@ -10,7 +10,7 @@ browser.jar:
* content/browser/preferences/colors.xul
* content/browser/preferences/cookies.xul
content/browser/preferences/cookies.js
* content/browser/preferences/connection.xul
content/browser/preferences/connection.xul
content/browser/preferences/connection.js
content/browser/preferences/donottrack.xul
* content/browser/preferences/fonts.xul

Просмотреть файл

@ -4,8 +4,6 @@
<!ENTITY connectionsDialog.title "Connection Settings">
<!ENTITY window.width "37em">
<!ENTITY window.macWidth "39em">
<!ENTITY proxyTitle.label "Configure Proxies to Access the Internet">
<!ENTITY noProxyTypeRadio.label "No proxy">

Просмотреть файл

@ -77,15 +77,11 @@
background-color: -moz-Dialog;
}
#navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar):not(#nav-bar) {
padding-top: 1px;
padding-bottom: 1px;
}
#navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar):not(#nav-bar):not(#addon-bar) {
overflow: -moz-hidden-unscrollable;
max-height: 4em;
transition: min-height 170ms ease-out, max-height 170ms ease-out;
padding: 1px 4px;
}
#navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar):not(#nav-bar):not(#addon-bar)[collapsed=true] {

Просмотреть файл

@ -170,6 +170,7 @@
overflow: -moz-hidden-unscrollable;
max-height: 4em;
transition: min-height 170ms ease-out, max-height 170ms ease-out;
padding: 0 5px;
}
#navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar):not(#nav-bar):not(#addon-bar)[collapsed=true] {

Просмотреть файл

@ -5,6 +5,8 @@ support-files =
doc_inspector_add_node.html
doc_inspector_breadcrumbs.html
doc_inspector_breadcrumbs_visibility.html
doc_inspector_csp.html
doc_inspector_csp.html^headers^
doc_inspector_delete-selected-node-01.html
doc_inspector_delete-selected-node-02.html
doc_inspector_embed.html
@ -68,7 +70,9 @@ skip-if = os == "mac" # Full keyboard navigation on OSX only works if Full Keybo
[browser_inspector_highlighter-embed.js]
[browser_inspector_highlighter-eyedropper-clipboard.js]
subsuite = clipboard
[browser_inspector_highlighter-eyedropper-csp.js]
[browser_inspector_highlighter-eyedropper-events.js]
[browser_inspector_highlighter-eyedropper-label.js]
[browser_inspector_highlighter-eyedropper-show-hide.js]
[browser_inspector_highlighter-geometry_01.js]
[browser_inspector_highlighter-geometry_02.js]

Просмотреть файл

@ -14,7 +14,8 @@ add_task(function* () {
.then(getHighlighterHelperFor(HIGHLIGHTER_TYPE));
helper.prefix = ID;
let {show, finalize} = helper;
let {show, finalize,
waitForElementAttributeSet, waitForElementAttributeRemoved} = helper;
info("Show the eyedropper with the copyOnSelect option");
yield show("html", {copyOnSelect: true});
@ -36,29 +37,3 @@ add_task(function* () {
finalize();
});
function* waitForElementAttributeSet(id, name, {getElementAttribute}) {
yield poll(function* () {
let value = yield getElementAttribute(id, name);
return !!value;
}, `Waiting for element ${id} to have attribute ${name} set`);
}
function* waitForElementAttributeRemoved(id, name, {getElementAttribute}) {
yield poll(function* () {
let value = yield getElementAttribute(id, name);
return !value;
}, `Waiting for element ${id} to have attribute ${name} removed`);
}
function* poll(check, desc) {
info(desc);
for (let i = 0; i < 10; i++) {
if (yield check()) {
return;
}
yield new Promise(resolve => setTimeout(resolve, 200));
}
throw new Error(`Timeout while: ${desc}`);
}

Просмотреть файл

@ -0,0 +1,30 @@
/* 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";
// Test that the eyedropper opens correctly even when the page defines CSP headers.
const HIGHLIGHTER_TYPE = "EyeDropper";
const ID = "eye-dropper-";
const TEST_URI = URL_ROOT + "doc_inspector_csp.html";
add_task(function* () {
let helper = yield openInspectorForURL(TEST_URI)
.then(getHighlighterHelperFor(HIGHLIGHTER_TYPE));
helper.prefix = ID;
let {show, hide, finalize, isElementHidden, waitForElementAttributeSet} = helper;
info("Try to display the eyedropper");
yield show("html");
let hidden = yield isElementHidden("root");
ok(!hidden, "The eyedropper is now shown");
info("Wait until the eyedropper is done taking a screenshot of the page");
yield waitForElementAttributeSet("root", "drawn", helper);
ok(true, "The image data was retrieved successfully from the window");
yield hide();
finalize();
});

Просмотреть файл

@ -0,0 +1,115 @@
/* 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";
// Test the position of the eyedropper label.
// It should move around when the eyedropper is close to the edges of the viewport so as
// to always stay visible.
const HIGHLIGHTER_TYPE = "EyeDropper";
const ID = "eye-dropper-";
const HTML = `
<style>
html, body {height: 100%; margin: 0;}
body {background: linear-gradient(red, gold); display: flex; justify-content: center;
align-items: center;}
</style>
Eyedropper label position test
`;
const TEST_PAGE = "data:text/html;charset=utf-8," + encodeURI(HTML);
const TEST_DATA = [{
desc: "Move the mouse to the center of the screen",
getCoordinates: (width, height) => {
return {x: width / 2, y: height / 2};
},
expectedPositions: {top: false, right: false, left: false}
}, {
desc: "Move the mouse to the center left",
getCoordinates: (width, height) => {
return {x: 0, y: height / 2};
},
expectedPositions: {top: false, right: true, left: false}
}, {
desc: "Move the mouse to the center right",
getCoordinates: (width, height) => {
return {x: width, y: height / 2};
},
expectedPositions: {top: false, right: false, left: true}
}, {
desc: "Move the mouse to the bottom center",
getCoordinates: (width, height) => {
return {x: width / 2, y: height};
},
expectedPositions: {top: true, right: false, left: false}
}, {
desc: "Move the mouse to the bottom left",
getCoordinates: (width, height) => {
return {x: 0, y: height};
},
expectedPositions: {top: true, right: true, left: false}
}, {
desc: "Move the mouse to the bottom right",
getCoordinates: (width, height) => {
return {x: width, y: height};
},
expectedPositions: {top: true, right: false, left: true}
}, {
desc: "Move the mouse to the top left",
getCoordinates: (width, height) => {
return {x: 0, y: 0};
},
expectedPositions: {top: false, right: true, left: false}
}, {
desc: "Move the mouse to the top right",
getCoordinates: (width, height) => {
return {x: width, y: 0};
},
expectedPositions: {top: false, right: false, left: true}
}];
add_task(function* () {
let {inspector, testActor} = yield openInspectorForURL(TEST_PAGE);
let helper = yield getHighlighterHelperFor(HIGHLIGHTER_TYPE)({inspector, testActor});
helper.prefix = ID;
let {mouse, show, hide, finalize} = helper;
let {width, height} = yield testActor.getBoundingClientRect("html");
// This test fails in non-e10s windows if we use width and height. For some reasons, the
// mouse events can't be dispatched/handled properly when we try to move the eyedropper
// to the far right and/or bottom of the screen. So just removing 10px from each side
// fixes it.
width -= 10;
height -= 10;
info("Show the eyedropper on the page");
yield show("html");
info("Move the eyedropper around and check that the label appears at the right place");
for (let {desc, getCoordinates, expectedPositions} of TEST_DATA) {
info(desc);
let {x, y} = getCoordinates(width, height);
info(`Moving the mouse to ${x} ${y}`);
yield mouse.move(x, y);
yield checkLabelPositionAttributes(helper, expectedPositions);
}
info("Hide the eyedropper");
yield hide();
finalize();
});
function* checkLabelPositionAttributes(helper, positions) {
for (let position in positions) {
is((yield hasAttribute(helper, position)), positions[position],
`The label was ${positions[position] ? "" : "not "}moved to the ${position}`);
}
}
function* hasAttribute({getElementAttribute}, name) {
let value = yield getElementAttribute("root", name);
return value !== null;
}

Просмотреть файл

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<title>Inspector CSP Test</title>
<meta charset="utf-8">
</head>
<body>
This HTTP response has CSP headers.
</body>
</html>

Просмотреть файл

@ -0,0 +1,2 @@
Content-Type: text/html; charset=UTF-8
content-security-policy: default-src 'self'; connect-src 'self'; script-src 'self'; style-src 'self';

Просмотреть файл

@ -394,6 +394,28 @@ function* getNodeFrontForSelector(selector, inspector) {
return nodes[0];
}
/**
* A simple polling helper that executes a given function until it returns true.
* @param {Function} check A generator function that is expected to return true at some
* stage.
* @param {String} desc A text description to be displayed when the polling starts.
* @param {Number} attemptes Optional number of times we poll. Defaults to 10.
* @param {Number} timeBetweenAttempts Optional time to wait between each attempt.
* Defaults to 200ms.
*/
function* poll(check, desc, attempts = 10, timeBetweenAttempts = 200) {
info(desc);
for (let i = 0; i < attempts; i++) {
if (yield check()) {
return;
}
yield new Promise(resolve => setTimeout(resolve, timeBetweenAttempts));
}
throw new Error(`Timeout while: ${desc}`);
}
/**
* Encapsulate some common operations for highlighter's tests, to have
* the tests cleaner, without exposing directly `inspector`, `highlighter`, and
@ -460,6 +482,22 @@ const getHighlighterHelperFor = (type) => Task.async(
prefix + id, name, highlighter);
},
waitForElementAttributeSet: function* (id, name) {
yield poll(function* () {
let value = yield testActor.getHighlighterNodeAttribute(
prefix + id, name, highlighter);
return !!value;
}, `Waiting for element ${id} to have attribute ${name} set`);
},
waitForElementAttributeRemoved: function* (id, name) {
yield poll(function* () {
let value = yield testActor.getHighlighterNodeAttribute(
prefix + id, name, highlighter);
return !value;
}, `Waiting for element ${id} to have attribute ${name} removed`);
},
synthesizeMouse: function* (options) {
options = Object.assign({selector: ":root"}, options);
yield testActor.synthesizeMouse(options);

Просмотреть файл

@ -258,7 +258,14 @@ severity.error=Error
severity.warn=Warning
severity.info=Info
severity.log=Log
severity.debug=Debug
# LOCALIZATION NOTE (level.error, level.warn, level.info, level.log, level.debug):
# tooltip for icons next to console output
level.error=Error
level.warn=Warning
level.info=Info
level.log=Log
level.debug=Debug
# LOCALIZATION NOTE (webconsole.find.key)
# Key shortcut used to focus the search box on upper right of the console

Просмотреть файл

@ -69,19 +69,11 @@ define(function (require, exports, module) {
delim = (i == delimMax ? "" : ", ");
if (value === array) {
items.push(Reference({
key: i,
object: value,
delim: delim}
));
} else {
items.push(GripArrayItem(Object.assign({}, this.props, {
key: i,
object: value,
delim: delim}
)));
}
items.push(GripArrayItem(Object.assign({}, this.props, {
key: i,
object: value,
delim: delim}
)));
} catch (exc) {
items.push(GripArrayItem(Object.assign({}, this.props, {
object: exc,
@ -178,21 +170,6 @@ define(function (require, exports, module) {
}
}));
/**
* Renders cycle references in an array.
*/
let Reference = React.createFactory(React.createClass({
displayName: "Reference",
render: function () {
return (
span({title: "Circular reference"},
"[…]"
)
);
}
}));
function supportsObject(grip, type) {
if (!isGrip(grip)) {
return false;

Просмотреть файл

@ -161,9 +161,6 @@ window.onload = Task.async(function* () {
}
function testRecursiveArray() {
// @TODO This is not how this feature should actually work
// See Bug 1282465 - Reps: fix or remove recursive handling in grip-array
// Test array = `let a = []; a = [a]`
const testName = "testRecursiveArray";

Просмотреть файл

@ -543,7 +543,18 @@ var TestActor = exports.TestActor = protocol.ActorClass(testSpec, {
*/
getBoundingClientRect: function (selector) {
let node = this._querySelector(selector);
return node.getBoundingClientRect();
let rect = node.getBoundingClientRect();
// DOMRect can't be stringified directly, so return a simple object instead.
return {
x: rect.x,
y: rect.y,
width: rect.width,
height: rect.height,
top: rect.top,
right: rect.right,
bottom: rect.bottom,
left: rect.left
};
},
/**

Просмотреть файл

@ -16,15 +16,11 @@
--breakpoint-conditional-background: url("chrome://devtools/skin/images/breakpoint.svg#dark-conditional");
}
.errors {
.CodeMirror .errors {
width: 16px;
}
.hit-counts {
width: 6px;
}
.error {
.CodeMirror .error {
display: inline-block;
margin-left: 5px;
width: 12px;
@ -32,9 +28,15 @@
background-repeat: no-repeat;
background-position: center;
background-size: contain;
background-image: url("chrome://devtools/skin/images/editor-error.png");
opacity: 0.75;
}
.hit-count {
.CodeMirror .hit-counts {
width: 6px;
}
.CodeMirror .hit-count {
display: inline-block;
height: 12px;
border: solid rgba(0,0,0,0.2);
@ -45,11 +47,6 @@
pointer-events: none;
}
.error {
background-image: url("chrome://devtools/skin/images/editor-error.png");
opacity: 0.75;
}
.CodeMirror-linenumber:before {
content: " ";
display: block;

Просмотреть файл

@ -342,14 +342,14 @@ a {
}
.message[category=console] > .indent,
.message.console > .indent {
.message.console-api > .indent {
border-inline-end: solid #cbcbcb 6px;
}
.message[category=console][severity=error] > .icon::before,
.message[category=output][severity=error] > .icon::before,
.message[category=server][severity=error] > .icon::before,
.message.console.error > .icon::before,
.message.console-api.error > .icon::before,
.message.output.error > .icon::before,
.message.server.error > .icon::before {
background-position: -12px -36px;
@ -357,14 +357,14 @@ a {
.message[category=console][severity=warn] > .icon::before,
.message[category=server][severity=warn] > .icon::before,
.message.console.warn > .icon::before,
.message.console-api.warn > .icon::before,
.message.server.warn > .icon::before {
background-position: -24px -36px;
}
.message[category=console][severity=info] > .icon::before,
.message[category=server][severity=info] > .icon::before,
.message.console.info > .icon::before,
.message.console-api.info > .icon::before,
.message.server.info > .icon::before {
background-position: -36px -36px;
}
@ -560,7 +560,8 @@ a {
background-color: rgba(0, 0, 0, 0.5);
}
.message[open] .stacktrace {
.message[open] .stacktrace,
.message.open .stacktrace {
display: block;
}

Просмотреть файл

@ -13,7 +13,9 @@ const { IdGenerator } = require("devtools/client/webconsole/new-console-output/u
const {
MESSAGE_ADD,
MESSAGES_CLEAR
MESSAGES_CLEAR,
MESSAGE_OPEN,
MESSAGE_CLOSE,
} = require("../constants");
const defaultIdGenerator = new IdGenerator();
@ -36,5 +38,21 @@ function messagesClear() {
};
}
function messageOpen(id) {
return {
type: MESSAGE_OPEN,
id
};
}
function messageClose(id) {
return {
type: MESSAGE_CLOSE,
id
};
}
exports.messageAdd = messageAdd;
exports.messagesClear = messagesClear;
exports.messageOpen = messageOpen;
exports.messageClose = messageClose;

Просмотреть файл

@ -0,0 +1,42 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
// React & Redux
const {
createClass,
DOM: dom,
PropTypes,
} = require("devtools/client/shared/vendor/react");
const CollapseButton = createClass({
displayName: "CollapseButton",
propTypes: {
open: PropTypes.bool.isRequired,
title: PropTypes.string.isRequired,
},
render: function () {
const { title, open, onClick } = this.props;
let classes = ["theme-twisty"];
if (open) {
classes.push("open");
}
return dom.a({
className: classes.join(" "),
onClick: onClick,
title
});
}
});
module.exports.CollapseButton = CollapseButton;

Просмотреть файл

@ -12,16 +12,16 @@ const {
const ReactDOM = require("devtools/client/shared/vendor/react-dom");
const { connect } = require("devtools/client/shared/vendor/react-redux");
const { getAllMessages } = require("devtools/client/webconsole/new-console-output/selectors/messages");
const { getAllMessages, getAllMessagesUiById } = require("devtools/client/webconsole/new-console-output/selectors/messages");
const MessageContainer = createFactory(require("devtools/client/webconsole/new-console-output/components/message-container").MessageContainer);
const ConsoleOutput = createClass({
propTypes: {
jsterm: PropTypes.object.isRequired,
// This function is created in mergeProps
openVariablesView: PropTypes.func.isRequired,
messages: PropTypes.array.isRequired
messages: PropTypes.object.isRequired,
sourceMapService: PropTypes.object,
onViewSourceInDebugger: PropTypes.func.isRequired,
},
displayName: "ConsoleOutput",
@ -41,9 +41,24 @@ const ConsoleOutput = createClass({
},
render() {
let messageNodes = this.props.messages.map(function (message) {
let {
dispatch,
messages,
messagesUi,
sourceMapService,
onViewSourceInDebugger
} = this.props;
let messageNodes = messages.map(function (message) {
return (
MessageContainer({ message, key: message.id })
MessageContainer({
dispatch,
message,
key: message.id,
sourceMapService,
onViewSourceInDebugger,
open: messagesUi.includes(message.id)
})
);
});
return (
@ -61,7 +76,8 @@ function isScrolledToBottom(outputNode, scrollNode) {
function mapStateToProps(state) {
return {
messages: getAllMessages(state)
messages: getAllMessages(state),
messagesUi: getAllMessagesUiById(state)
};
}

Просмотреть файл

@ -97,6 +97,12 @@ const FilterBar = createClass({
label: "Info",
filterKey: MESSAGE_LEVEL.INFO,
dispatch
}),
FilterButton({
active: filter.debug,
label: "Debug",
filterKey: MESSAGE_LEVEL.DEBUG,
dispatch
})
)
);
@ -121,7 +127,7 @@ const FilterBar = createClass({
return (
dom.div({className: "webconsole-filteringbar-wrapper"},
children
...children
)
);
}

Просмотреть файл

@ -25,7 +25,11 @@ const { Grip } = require("devtools/client/shared/components/reps/grip");
GripMessageBody.displayName = "GripMessageBody";
GripMessageBody.propTypes = {
grip: PropTypes.object.isRequired,
grip: PropTypes.oneOfType([
PropTypes.string,
PropTypes.number,
PropTypes.object,
]).isRequired,
};
function GripMessageBody(props) {

Просмотреть файл

@ -30,17 +30,34 @@ const MessageContainer = createClass({
displayName: "MessageContainer",
propTypes: {
message: PropTypes.object.isRequired
message: PropTypes.object.isRequired,
sourceMapService: PropTypes.object,
onViewSourceInDebugger: PropTypes.func.isRequired,
open: PropTypes.bool.isRequired,
},
shouldComponentUpdate(nextProps, nextState) {
return this.props.message.repeat !== nextProps.message.repeat;
return this.props.message.repeat !== nextProps.message.repeat
|| this.props.open !== nextProps.open;
},
render() {
const { message } = this.props;
const {
dispatch,
message,
sourceMapService,
onViewSourceInDebugger,
open
} = this.props;
let MessageComponent = createFactory(getMessageComponent(message));
return MessageComponent({ message });
return MessageComponent({
dispatch,
message,
sourceMapService,
onViewSourceInDebugger,
open
});
}
});

Просмотреть файл

@ -16,13 +16,13 @@ const {l10n} = require("devtools/client/webconsole/new-console-output/utils/mess
MessageIcon.displayName = "MessageIcon";
MessageIcon.propTypes = {
severity: PropTypes.string.isRequired,
level: PropTypes.string.isRequired,
};
function MessageIcon(props) {
const { severity } = props;
const { level } = props;
const title = l10n.getStr("severity." + severity);
const title = l10n.getStr("level." + level);
return dom.div({
className: "icon",
title

Просмотреть файл

@ -12,51 +12,102 @@ const {
DOM: dom,
PropTypes
} = require("devtools/client/shared/vendor/react");
const FrameView = createFactory(require("devtools/client/shared/components/frame"));
const StackTrace = createFactory(require("devtools/client/shared/components/stack-trace"));
const GripMessageBody = createFactory(require("devtools/client/webconsole/new-console-output/components/grip-message-body").GripMessageBody);
const MessageRepeat = createFactory(require("devtools/client/webconsole/new-console-output/components/message-repeat").MessageRepeat);
const MessageIcon = createFactory(require("devtools/client/webconsole/new-console-output/components/message-icon").MessageIcon);
const CollapseButton = createFactory(require("devtools/client/webconsole/new-console-output/components/collapse-button").CollapseButton);
const {l10n} = require("devtools/client/webconsole/new-console-output/utils/messages");
const actions = require("devtools/client/webconsole/new-console-output/actions/messages");
ConsoleApiCall.displayName = "ConsoleApiCall";
ConsoleApiCall.propTypes = {
message: PropTypes.object.isRequired,
sourceMapService: PropTypes.object,
onViewSourceInDebugger: PropTypes.func.isRequired,
open: PropTypes.bool.isRequired,
};
function ConsoleApiCall(props) {
const { message } = props;
const {category, severity} = message;
const { dispatch, message, sourceMapService, onViewSourceInDebugger, open } = props;
const {source, level, stacktrace, type, frame } = message;
const messageBody = message.parameters ?
message.parameters.map((grip) => GripMessageBody({grip})) :
message.messageText;
let messageBody;
if (type === "trace") {
messageBody = dom.span({className: "cm-variable"}, "console.trace()");
} else if (message.parameters) {
messageBody = message.parameters.map((grip, key) => GripMessageBody({grip, key}));
} else {
messageBody = message.messageText;
}
const icon = MessageIcon({severity: severity});
const icon = MessageIcon({level});
const repeat = MessageRepeat({repeat: message.repeat});
let collapse = "";
let attachment = "";
if (stacktrace) {
attachment = dom.div({className: "stacktrace devtools-monospace"},
StackTrace({
stacktrace: stacktrace,
onViewSourceInDebugger: onViewSourceInDebugger
})
);
collapse = CollapseButton({
open: open,
title: l10n.getStr("messageToggleDetails"),
onClick: function () {
if (open) {
dispatch(actions.messageClose(message.id));
} else {
dispatch(actions.messageOpen(message.id));
}
},
});
}
const classes = ["message", "cm-s-mozilla"];
if (category) {
classes.push(category);
if (source) {
classes.push(source);
}
if (severity) {
classes.push(severity);
if (level) {
classes.push(level);
}
if (open === true) {
classes.push("open");
}
const shouldRenderFrame = frame && frame.source !== "debugger eval code";
return dom.div({
className: classes.join(" ")
},
// @TODO add timestamp
// @TODO add indent if necessary
icon,
collapse,
dom.span({className: "message-body-wrapper"},
dom.span({},
dom.span({className: "message-flex-body"},
dom.span({className: "message-body devtools-monospace"},
messageBody
),
repeat
)
repeat,
dom.span({ className: "message-location devtools-monospace" },
shouldRenderFrame ? FrameView({
frame,
onClick: onViewSourceInDebugger,
showEmptyPathAsHost: true,
sourceMapService
}) : null
)
),
attachment
)
)
);

Просмотреть файл

@ -26,18 +26,18 @@ ConsoleCommand.propTypes = {
*/
function ConsoleCommand(props) {
const { message } = props;
const {category, severity} = message;
const {source, level} = message;
const icon = MessageIcon({severity: severity});
const icon = MessageIcon({level});
const classes = ["message"];
if (category) {
classes.push(category);
if (source) {
classes.push(source);
}
if (severity) {
classes.push(severity);
if (level) {
classes.push(level);
}
return dom.div({

Просмотреть файл

@ -23,17 +23,17 @@ EvaluationResult.propTypes = {
function EvaluationResult(props) {
const { message } = props;
const {category, severity} = message;
const icon = MessageIcon({severity: severity});
const {source, level} = message;
const icon = MessageIcon({level});
const classes = ["message", "cm-s-mozilla"];
if (category) {
classes.push(category);
if (source) {
classes.push(source);
}
if (severity) {
classes.push(severity);
if (level) {
classes.push(level);
}
return dom.div({

Просмотреть файл

@ -23,19 +23,19 @@ PageError.propTypes = {
function PageError(props) {
const { message } = props;
const {category, severity} = message;
const {source, level} = message;
const repeat = MessageRepeat({repeat: message.repeat});
const icon = MessageIcon({severity: severity});
const icon = MessageIcon({level});
const classes = ["message"];
if (category) {
classes.push(category);
if (source) {
classes.push(source);
}
if (severity) {
classes.push(severity);
if (level) {
classes.push(level);
}
return dom.div({

Просмотреть файл

@ -8,6 +8,7 @@ DIRS += [
]
DevToolsModules(
'collapse-button.js',
'console-output.js',
'filter-bar.js',
'filter-button.js',

Просмотреть файл

@ -8,55 +8,14 @@
const actionTypes = {
MESSAGE_ADD: "MESSAGE_ADD",
MESSAGES_CLEAR: "MESSAGES_CLEAR",
MESSAGE_OPEN: "MESSAGE_OPEN",
MESSAGE_CLOSE: "MESSAGE_CLOSE",
FILTER_TOGGLE: "FILTER_TOGGLE",
FILTER_TEXT_SET: "FILTER_TEXT_SET",
FILTERS_CLEAR: "FILTERS_CLEAR",
FILTER_BAR_TOGGLE: "FILTER_BAR_TOGGLE",
};
const categories = {
CATEGORY_NETWORK: "network",
CATEGORY_CSS: "cssparser",
CATEGORY_JS: "exception",
CATEGORY_WEBDEV: "console",
CATEGORY_INPUT: "input",
CATEGORY_OUTPUT: "output",
CATEGORY_SECURITY: "security",
CATEGORY_SERVER: "server"
};
const severities = {
SEVERITY_ERROR: "error",
SEVERITY_WARNING: "warn",
SEVERITY_INFO: "info",
SEVERITY_LOG: "log"
};
// A mapping from the console API log event levels to the Web Console
// severities.
const levels = {
LEVELS: {
error: severities.SEVERITY_ERROR,
exception: severities.SEVERITY_ERROR,
assert: severities.SEVERITY_ERROR,
warn: severities.SEVERITY_WARNING,
info: severities.SEVERITY_INFO,
log: severities.SEVERITY_LOG,
clear: severities.SEVERITY_LOG,
trace: severities.SEVERITY_LOG,
table: severities.SEVERITY_LOG,
debug: severities.SEVERITY_LOG,
dir: severities.SEVERITY_LOG,
dirxml: severities.SEVERITY_LOG,
group: severities.SEVERITY_LOG,
groupCollapsed: severities.SEVERITY_LOG,
groupEnd: severities.SEVERITY_LOG,
time: severities.SEVERITY_LOG,
timeEnd: severities.SEVERITY_LOG,
count: severities.SEVERITY_LOG
}
};
const chromeRDPEnums = {
MESSAGE_SOURCE: {
XML: "xml",
@ -96,10 +55,5 @@ const chromeRDPEnums = {
}
};
const filterTypes = {
FILTER_TOGGLE: "FILTER_TOGGLE"
};
// Combine into a single constants object
module.exports = Object.assign({}, actionTypes, categories, severities, levels,
chromeRDPEnums, filterTypes);
module.exports = Object.assign({}, actionTypes, chromeRDPEnums);

Просмотреть файл

@ -18,7 +18,7 @@ const NewConsoleOutputWrapper = BrowserLoader({
baseURI: "resource://devtools/client/webconsole/new-console-output/",
window: this}).require("./new-console-output-wrapper");
this.NewConsoleOutput = function (parentNode, jsterm) {
this.NewConsoleOutput = function (parentNode, jsterm, toolbox) {
console.log("Creating NewConsoleOutput", parentNode, NewConsoleOutputWrapper);
return new NewConsoleOutputWrapper(parentNode, jsterm);
return new NewConsoleOutputWrapper(parentNode, jsterm, toolbox);
};

Просмотреть файл

@ -16,8 +16,17 @@ const FilterBar = React.createFactory(require("devtools/client/webconsole/new-co
const store = configureStore();
function NewConsoleOutputWrapper(parentNode, jsterm) {
let childComponent = ConsoleOutput({ jsterm });
function NewConsoleOutputWrapper(parentNode, jsterm, toolbox) {
const sourceMapService = toolbox ? toolbox._sourceMapService : null;
let childComponent = ConsoleOutput({
jsterm,
sourceMapService,
onViewSourceInDebugger: frame => toolbox.viewSourceInDebugger.call(
toolbox,
frame.url,
frame.line
)
});
let filterBar = FilterBar({});
let provider = React.createElement(
Provider,

Просмотреть файл

@ -9,11 +9,12 @@ const Immutable = require("devtools/client/shared/vendor/immutable");
const constants = require("devtools/client/webconsole/new-console-output/constants");
const FilterState = Immutable.Record({
debug: true,
error: true,
warn: true,
info: true,
log: true,
text: ""
text: "",
warn: true,
});
function filters(state = new FilterState(), action) {

Просмотреть файл

@ -8,26 +8,47 @@
const Immutable = require("devtools/client/shared/vendor/immutable");
const constants = require("devtools/client/webconsole/new-console-output/constants");
function messages(state = Immutable.List(), action) {
const MessageState = Immutable.Record({
messagesById: Immutable.List(),
messagesUiById: Immutable.List(),
});
function messages(state = new MessageState(), action) {
const messagesById = state.messagesById;
const messagesUiById = state.messagesUiById;
switch (action.type) {
case constants.MESSAGE_ADD:
let newMessage = action.message;
if (newMessage.type === "clear") {
return Immutable.List([newMessage]);
return state.set("messagesById", Immutable.List([newMessage]));
}
if (newMessage.allowRepeating && state.size > 0) {
let lastMessage = state.last();
if (newMessage.allowRepeating && messagesById.size > 0) {
let lastMessage = messagesById.last();
if (lastMessage.repeatId === newMessage.repeatId) {
return state.pop().push(
newMessage.set("repeat", lastMessage.repeat + 1)
);
return state.withMutations(function (record) {
record.set("messagesById", messagesById.pop().push(
newMessage.set("repeat", lastMessage.repeat + 1)
));
});
}
}
return state.push(newMessage);
return state.withMutations(function (record) {
record.set("messagesById", messagesById.push(newMessage));
if (newMessage.type === "trace") {
record.set("messagesUiById", messagesUiById.push(newMessage.id));
}
});
case constants.MESSAGES_CLEAR:
return Immutable.List();
return state.set("messagesById", Immutable.List());
case constants.MESSAGE_OPEN:
return state.set("messagesUiById", messagesUiById.push(action.id));
case constants.MESSAGE_CLOSE:
let index = state.messagesUiById.indexOf(action.id);
return state.deleteIn(["messagesUiById", index]);
}
return state;

Просмотреть файл

@ -7,23 +7,33 @@
const { getAllFilters } = require("devtools/client/webconsole/new-console-output/selectors/filters");
const { getLogLimit } = require("devtools/client/webconsole/new-console-output/selectors/prefs");
const {
MESSAGE_TYPE
} = require("devtools/client/webconsole/new-console-output/constants");
function getAllMessages(state) {
let messages = state.messages;
let messages = state.messages.messagesById;
let logLimit = getLogLimit(state);
let filters = getAllFilters(state);
return prune(
search(
filterSeverity(messages, filters),
filterLevel(messages, filters),
filters.text
),
logLimit
);
}
function filterSeverity(messages, filters) {
return messages.filter((message) => filters[message.level] === true);
function getAllMessagesUiById(state) {
return state.messages.messagesUiById;
}
function filterLevel(messages, filters) {
return messages.filter((message) => {
return filters[message.level] === true
|| [MESSAGE_TYPE.COMMAND, MESSAGE_TYPE.RESULT].includes(message.type);
});
}
function search(messages, text = "") {
@ -53,3 +63,4 @@ function prune(messages, logLimit) {
}
exports.getAllMessages = getAllMessages;
exports.getAllMessagesUiById = getAllMessagesUiById;

Просмотреть файл

@ -7,12 +7,9 @@ const {FilterState} = require("devtools/client/webconsole/new-console-output/red
const {PrefState} = require("devtools/client/webconsole/new-console-output/reducers/prefs");
const { combineReducers, createStore } = require("devtools/client/shared/vendor/redux");
const { reducers } = require("./reducers/index");
const Services = require("Services");
function configureStore(Services) {
if (!Services) {
Services = require("Services");
}
function configureStore() {
const initialState = {
prefs: new PrefState({
logLimit: Math.max(Services.prefs.getIntPref("devtools.hud.loglimit"), 1),

Просмотреть файл

@ -3,7 +3,7 @@
"use strict";
const { getRepeatId } = require("devtools/client/webconsole/new-console-output/utils/messages");
const { stubConsoleMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs");
const stubConsoleMessages = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
const { setupActions } = require("devtools/client/webconsole/new-console-output/test/helpers");
const constants = require("devtools/client/webconsole/new-console-output/constants");
@ -26,9 +26,9 @@ describe("Message actions:", () => {
"foobar",
"test"
],
"columnNumber": 1,
"columnNumber": 27,
"counter": null,
"filename": "file:///test.html",
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js",
"functionName": "",
"groupName": "",
"level": "log",
@ -47,12 +47,7 @@ describe("Message actions:", () => {
message: stubConsoleMessages.get("console.log('foobar', 'test')")
};
// Some values on the message are generated by prepareMessage. Manually set
// these on the expected message to match.
expected.message = expected.message.set("repeatId", getRepeatId(expected.message));
expected.message = expected.message.set("id", "1");
expect(action).toEqual(expected);
expect(action.message.toJS()).toEqual(expected.message.toJS());
});
});

Просмотреть файл

@ -2,19 +2,22 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const { stubConsoleMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs");
const { ConsoleApiCall } = require("devtools/client/webconsole/new-console-output/components/message-types/console-api-call");
// Test utils.
const expect = require("expect");
const { renderComponent } = require("devtools/client/webconsole/new-console-output/test/helpers");
const {
renderComponent
} = require("devtools/client/webconsole/new-console-output/test/helpers");
// Components under test.
const { ConsoleApiCall } = require("devtools/client/webconsole/new-console-output/components/message-types/console-api-call");
// Test fakes.
const stubConsoleMessages = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
const onViewSourceInDebugger = () => {};
describe("ConsoleAPICall component:", () => {
describe("console.log", () => {
it("renders string grips", () => {
const message = stubConsoleMessages.get("console.log('foobar', 'test')");
const rendered = renderComponent(ConsoleApiCall, {message});
const rendered = renderComponent(ConsoleApiCall, {message, onViewSourceInDebugger});
const messageBody = getMessageBody(rendered);
// @TODO should output: foobar test
@ -27,7 +30,7 @@ describe("ConsoleAPICall component:", () => {
const message =
stubConsoleMessages.get("console.log('foobar', 'test')")
.set("repeat", 107);
const rendered = renderComponent(ConsoleApiCall, {message});
const rendered = renderComponent(ConsoleApiCall, {message, onViewSourceInDebugger});
const repeatNode = getRepeatNode(rendered);
expect(repeatNode[0].textContent).toBe("107");
@ -37,7 +40,7 @@ describe("ConsoleAPICall component:", () => {
describe("console.count", () => {
it("renders", () => {
const message = stubConsoleMessages.get("console.count('bar')");
const rendered = renderComponent(ConsoleApiCall, {message});
const rendered = renderComponent(ConsoleApiCall, {message, onViewSourceInDebugger});
const messageBody = getMessageBody(rendered);
expect(messageBody.textContent).toBe(message.messageText);

Просмотреть файл

@ -2,7 +2,7 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const { stubConsoleMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs");
const stubConsoleMessages = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
const { EvaluationResult } = require("devtools/client/webconsole/new-console-output/components/message-types/evaluation-result");
const expect = require("expect");

Просмотреть файл

@ -2,8 +2,8 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Require helper is necessary to load certain modules.
require("devtools/client/webconsole/new-console-output/test/requireHelper")();
const expect = require("expect");
const sinon = require("sinon");
const { render, mount } = require("enzyme");
const { createFactory } = require("devtools/client/shared/vendor/react");
@ -19,9 +19,6 @@ const {
const { setupStore } = require("devtools/client/webconsole/new-console-output/test/helpers");
const expect = require("expect");
const sinon = require("sinon");
describe("FilterBar component:", () => {
it("initial render", () => {
const store = setupStore([]);
@ -65,13 +62,15 @@ describe("FilterBar component:", () => {
};
const logButton = FilterButton(Object.assign({}, buttonProps,
{ label: "Logs", filterKey: MESSAGE_LEVEL.LOG }));
const debugButton = FilterButton(Object.assign({}, buttonProps,
{ label: "Debug", filterKey: MESSAGE_LEVEL.DEBUG }));
const infoButton = FilterButton(Object.assign({}, buttonProps,
{ label: "Info", filterKey: MESSAGE_LEVEL.INFO }));
const warnButton = FilterButton(Object.assign({}, buttonProps,
{ label: "Warnings", filterKey: MESSAGE_LEVEL.WARN }));
const errorButton = FilterButton(Object.assign({}, buttonProps,
{ label: "Errors", filterKey: MESSAGE_LEVEL.ERROR }));
expect(wrapper.contains([errorButton, warnButton, logButton, infoButton])).toBe(true);
expect(wrapper.contains([errorButton, warnButton, logButton, infoButton, debugButton])).toBe(true);
});
it("fires MESSAGES_CLEAR action when clear button is clicked", () => {

Просмотреть файл

@ -2,8 +2,8 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Require helper is necessary to load certain modules.
require("devtools/client/webconsole/new-console-output/test/requireHelper")();
const expect = require("expect");
const sinon = require("sinon");
const { render, shallow } = require("enzyme");
const { createFactory } = require("devtools/client/shared/vendor/react");
@ -14,9 +14,6 @@ const {
MESSAGE_LEVEL
} = require("devtools/client/webconsole/new-console-output/constants");
const expect = require("expect");
const sinon = require("sinon");
describe("FilterButton component:", () => {
const props = {
active: true,

Просмотреть файл

@ -2,24 +2,27 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const { stubConsoleMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs");
const { MessageContainer } = require("devtools/client/webconsole/new-console-output/components/message-container");
const { ConsoleApiCall } = require("devtools/client/webconsole/new-console-output/components/message-types/console-api-call");
const { EvaluationResult } = require("devtools/client/webconsole/new-console-output/components/message-types/evaluation-result");
const { PageError } = require("devtools/client/webconsole/new-console-output/components/message-types/page-error");
// Test utils.
const expect = require("expect");
const {
renderComponent,
shallowRenderComponent
} = require("devtools/client/webconsole/new-console-output/test/helpers");
// Components under test.
const { MessageContainer } = require("devtools/client/webconsole/new-console-output/components/message-container");
const { ConsoleApiCall } = require("devtools/client/webconsole/new-console-output/components/message-types/console-api-call");
const { EvaluationResult } = require("devtools/client/webconsole/new-console-output/components/message-types/evaluation-result");
const { PageError } = require("devtools/client/webconsole/new-console-output/components/message-types/page-error");
// Test fakes.
const stubConsoleMessages = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
const onViewSourceInDebugger = () => {};
describe("MessageContainer component:", () => {
it("pipes data to children as expected", () => {
const message = stubConsoleMessages.get("console.log('foobar', 'test')");
const rendered = renderComponent(MessageContainer, {message});
const rendered = renderComponent(MessageContainer, {message, onViewSourceInDebugger});
expect(rendered.textContent.includes("foobar")).toBe(true);
});
@ -35,13 +38,17 @@ describe("MessageContainer component:", () => {
},
{
component: PageError,
message: stubConsoleMessages.get("ReferenceError")
message: stubConsoleMessages.get("ReferenceError: asdf is not defined")
}
];
messageTypes.forEach(info => {
const rendered = shallowRenderComponent(MessageContainer, {message: info.message});
expect(rendered.type).toBe(info.component);
const { component, message } = info;
const rendered = shallowRenderComponent(MessageContainer, {
message,
onViewSourceInDebugger,
});
expect(rendered.type).toBe(component);
});
});
});

Просмотреть файл

@ -3,7 +3,7 @@
"use strict";
const {
SEVERITY_ERROR,
MESSAGE_LEVEL,
} = require("devtools/client/webconsole/new-console-output/constants");
const { MessageIcon } = require("devtools/client/webconsole/new-console-output/components/message-icon");
@ -14,8 +14,8 @@ const {
} = require("devtools/client/webconsole/new-console-output/test/helpers");
describe("MessageIcon component:", () => {
it("renders icon based on severity", () => {
const rendered = renderComponent(MessageIcon, { severity: SEVERITY_ERROR });
it("renders icon based on level", () => {
const rendered = renderComponent(MessageIcon, { level: MESSAGE_LEVEL.ERROR });
expect(rendered.classList.contains("icon")).toBe(true);
expect(rendered.getAttribute("title")).toBe("Error");

Просмотреть файл

@ -2,7 +2,7 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const { stubConsoleMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs");
const stubConsoleMessages = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
const { PageError } = require("devtools/client/webconsole/new-console-output/components/message-types/page-error");
@ -14,7 +14,7 @@ const {
describe("PageError component:", () => {
it("renders a page error", () => {
const message = stubConsoleMessages.get("ReferenceError");
const message = stubConsoleMessages.get("ReferenceError: asdf is not defined");
const rendered = renderComponent(PageError, {message});
const messageBody = getMessageBody(rendered);

Просмотреть файл

@ -4,12 +4,14 @@
"use strict";
// @TODO Load the actual strings from webconsole.properties instead.
module.exports = {
getStr: str => {
class L10n {
getStr(str) {
switch (str) {
case "severity.error":
case "level.error":
return "Error";
}
return str;
}
};
}
module.exports = L10n;

Просмотреть файл

@ -0,0 +1,10 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const LocalizationHelper = require("devtools/client/webconsole/new-console-output/test/fixtures/L10n");
module.exports = {
LocalizationHelper
};

Просмотреть файл

@ -0,0 +1,14 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const L10n = require("devtools/client/webconsole/new-console-output/test/fixtures/L10n");
const Utils = {
L10n
};
module.exports = {
Utils
};

Просмотреть файл

@ -3,7 +3,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/.
DevToolsModules(
'l10n.js',
'stubs.js',
)
DIRS += [
'stub-generators'
]

Просмотреть файл

@ -0,0 +1,15 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
head.js
!/devtools/client/framework/test/shared-head.js
test-console-api.html
test-tempfile.js
[browser_webconsole_update_stubs_console_api.js]
skip-if=true # This is only used to update stubs. It is not an actual test.
[browser_webconsole_update_stubs_evaluation_result.js]
skip-if=true # This is only used to update stubs. It is not an actual test.
[browser_webconsole_update_stubs_page_error.js]
skip-if=true # This is only used to update stubs. It is not an actual test.

Просмотреть файл

@ -0,0 +1,40 @@
/* -*- 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/ */
"use strict";
Cu.import("resource://gre/modules/osfile.jsm");
const { consoleApi: snippets } = require("devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/stub-snippets.js");
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-console-api.html";
let stubs = [];
snippets.forEach((code, key) => {
add_task(function* () {
let tempFilePath = OS.Path.join(`${BASE_PATH}/stub-generators`, "test-tempfile.js");
OS.File.writeAtomic(tempFilePath, `function triggerPacket() {${code}}`);
let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
let hud = toolbox.getCurrentPanel().hud;
let {ui} = hud;
ok(ui.jsterm, "jsterm exists");
ok(ui.newConsoleOutput, "newConsoleOutput exists");
toolbox.target.client.addListener("consoleAPICall", (type, res) => {
stubs.push(formatStub(key, res));
if (stubs.length == snippets.size) {
let filePath = OS.Path.join(`${BASE_PATH}/stubs`, "consoleApi.js");
OS.File.writeAtomic(filePath, formatFile(stubs));
OS.File.writeAtomic(tempFilePath, "");
}
});
yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function() {
content.wrappedJSObject.triggerPacket();
});
});
});

Просмотреть файл

@ -0,0 +1,29 @@
/* -*- 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/ */
"use strict";
Cu.import("resource://gre/modules/osfile.jsm");
const TEST_URI = "data:text/html;charset=utf-8,stub generation";
const { evaluationResult: snippets} = require("devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/stub-snippets.js");
let stubs = [];
add_task(function* () {
let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
ok(true, "make the test not fail");
for (var [code,key] of snippets) {
const packet = yield new Promise(resolve => {
toolbox.target.activeConsole.evaluateJS(code, resolve);
});
stubs.push(formatStub(key, packet));
if (stubs.length == snippets.size) {
let filePath = OS.Path.join(`${BASE_PATH}/stubs`, "evaluationResult.js");
OS.File.writeAtomic(filePath, formatFile(stubs));
}
}
});

Просмотреть файл

@ -0,0 +1,45 @@
/* -*- 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/ */
"use strict";
Cu.import("resource://gre/modules/osfile.jsm");
const TEST_URI = "data:text/html;charset=utf-8,stub generation";
const { pageError: snippets} = require("devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/stub-snippets.js");
let stubs = [];
add_task(function* () {
let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
ok(true, "make the test not fail");
for (var [key,code] of snippets) {
let received = new Promise(resolve => {
toolbox.target.client.addListener("pageError", function onPacket(e, packet) {
toolbox.target.client.removeListener("pageError", onPacket);
info("Received page error:" + e + " " + JSON.stringify(packet, null, "\t"));
let message = prepareMessage(packet, {getNextId: () => 1});
stubs.push(formatStub(message.messageText, packet));
resolve();
});
});
info("Injecting script: " + code);
yield ContentTask.spawn(gBrowser.selectedBrowser, code, function(code) {
let container = content.document.createElement("script");
content.document.body.appendChild(container);
container.textContent = code;
content.document.body.removeChild(container);
});
yield received;
}
let filePath = OS.Path.join(`${BASE_PATH}/stubs`, "pageError.js");
OS.File.writeAtomic(filePath, formatFile(stubs));
});

Просмотреть файл

@ -0,0 +1,47 @@
/* -*- 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/ */
/* import-globals-from ../../../../framework/test/shared-head.js */
"use strict";
// shared-head.js handles imports, constants, and utility functions
// Load the shared-head file first.
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
this);
Services.prefs.setBoolPref("devtools.webconsole.new-frontend-enabled", true);
registerCleanupFunction(() => {
Services.prefs.clearUserPref("devtools.webconsole.new-frontend-enabled");
});
const { prepareMessage } = require("devtools/client/webconsole/new-console-output/utils/messages");
const BASE_PATH = "../../../../devtools/client/webconsole/new-console-output/test/fixtures";
function formatStub(key, message) {
let prepared = prepareMessage(message, {getNextId: () => "1"});
return `
stubConsoleMessages.set("${key}", new ConsoleMessage(${JSON.stringify(prepared, null, "\t")}));
`;
}
function formatFile(stubs) {
return `/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/*
* THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN TESTS IN FIXTURES/ TO UPDATE.
*/
const { ConsoleMessage } = require("devtools/client/webconsole/new-console-output/types");
let stubConsoleMessages = new Map();
${stubs.join("")}
module.exports = stubConsoleMessages`;
}

Просмотреть файл

@ -0,0 +1,8 @@
# 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(
'stub-snippets.js',
)

Просмотреть файл

@ -0,0 +1,58 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Console API
const consoleApiCommands = [
"console.log('foobar', 'test')",
"console.log(undefined)",
"console.warn('danger, will robinson!')",
"console.log(NaN)",
"console.log(null)",
"console.clear()",
"console.count('bar')",
];
let consoleApi = new Map(consoleApiCommands.map(cmd => [cmd, cmd]));
consoleApi.set("console.trace()",
`
function bar() {
console.trace()
}
function foo() {
bar()
}
foo()
`);
consoleApi.set("console.time()",
`
console.time()
console.timeEnd()
`);
// Evaluation Result
const evaluationResultCommands = [
"new Date(0)"
];
let evaluationResult = new Map(evaluationResultCommands.map(cmd => [cmd, cmd]));
// Page Error
const pageErrorCommands = [
"asdf()",
];
let pageError = new Map(pageErrorCommands.map(cmd => [cmd, cmd]));
module.exports = {
consoleApi,
evaluationResult,
pageError,
};

Просмотреть файл

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Stub generator</title>
</head>
<body>
<p>Stub generator</p>
<script src="test-tempfile.js"></script>
</body>
</html>

Просмотреть файл

Просмотреть файл

@ -1,168 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const {
MESSAGE_SOURCE,
MESSAGE_TYPE,
MESSAGE_LEVEL,
// Legacy
CATEGORY_WEBDEV,
SEVERITY_LOG,
} = require("devtools/client/webconsole/new-console-output/constants");
const { ConsoleMessage } = require("devtools/client/webconsole/new-console-output/types");
exports.stubConsoleMessages = new Map([
[
"console.log('foobar', 'test')",
new ConsoleMessage({
allowRepeating: true,
source: MESSAGE_SOURCE.CONSOLE_API,
type: MESSAGE_TYPE.LOG,
level: MESSAGE_LEVEL.LOG,
messageText: null,
parameters: ["foobar", "test"],
repeat: 1,
repeatId: null,
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
})
],
[
"console.warn('danger, will robinson!')",
new ConsoleMessage({
allowRepeating: true,
source: MESSAGE_SOURCE.CONSOLE_API,
type: MESSAGE_TYPE.LOG,
level: MESSAGE_LEVEL.WARN,
messageText: null,
parameters: ["danger, will robinson!"],
repeat: 1,
repeatId: null,
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
})
],
[
"console.log(undefined)",
new ConsoleMessage({
allowRepeating: true,
source: MESSAGE_SOURCE.CONSOLE_API,
type: MESSAGE_TYPE.LOG,
level: MESSAGE_LEVEL.LOG,
messageText: null,
parameters: [
{ type: "undefined" }
],
repeat: 1,
repeatId: null,
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
})
],
[
"console.log(NaN)",
new ConsoleMessage({
allowRepeating: true,
source: MESSAGE_SOURCE.CONSOLE_API,
type: MESSAGE_TYPE.LOG,
level: MESSAGE_LEVEL.LOG,
messageText: null,
parameters: [
{ type: "NaN" }
],
repeat: 1,
repeatId: null,
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
})
],
[
"console.log(null)",
new ConsoleMessage({
allowRepeating: true,
source: MESSAGE_SOURCE.CONSOLE_API,
type: MESSAGE_TYPE.LOG,
level: MESSAGE_LEVEL.LOG,
messageText: null,
parameters: [
{ type: "null" }
],
repeat: 1,
repeatId: null,
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
})
],
[
"console.clear()",
new ConsoleMessage({
allowRepeating: true,
source: MESSAGE_SOURCE.CONSOLE_API,
type: MESSAGE_TYPE.CLEAR,
level: MESSAGE_LEVEL.LOG,
messageText: null,
parameters: ["Console cleared."],
repeat: 1,
repeatId: null,
})
],
[
"console.count('bar')",
new ConsoleMessage({
allowRepeating: true,
source: MESSAGE_SOURCE.CONSOLE_API,
type: MESSAGE_TYPE.LOG,
level: MESSAGE_LEVEL.DEBUG,
messageText: "bar: 1",
parameters: null,
repeat: 1,
repeatId: null,
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
})
],
[
"new Date(0)",
new ConsoleMessage({
allowRepeating: true,
source: MESSAGE_SOURCE.JAVASCRIPT,
type: MESSAGE_TYPE.RESULT,
level: MESSAGE_LEVEL.LOG,
messageText: null,
parameters: {
"type": "object",
"class": "Date",
"actor": "server2.conn0.obj115",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 0,
"preview": {
"timestamp": 0
}
},
repeat: 1,
repeatId: null,
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
})
],
[
"ReferenceError",
new ConsoleMessage({
allowRepeating: true,
source: MESSAGE_SOURCE.JAVASCRIPT,
type: MESSAGE_TYPE.LOG,
level: MESSAGE_LEVEL.ERROR,
messageText: "ReferenceError: asdf is not defined",
parameters: null,
repeat: 1,
repeatId: null,
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
})
]
]);

Просмотреть файл

@ -0,0 +1,200 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/*
* THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN TESTS IN FIXTURES/ TO UPDATE.
*/
const { ConsoleMessage } = require("devtools/client/webconsole/new-console-output/types");
let stubConsoleMessages = new Map();
stubConsoleMessages.set("console.log('foobar', 'test')", new ConsoleMessage({
"id": "1",
"allowRepeating": true,
"source": "console-api",
"type": "log",
"level": "log",
"messageText": null,
"parameters": [
"foobar",
"test"
],
"repeat": 1,
"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"foobar\",\"test\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js\",\"line\":1,\"column\":27}}",
"stacktrace": null,
"frame": {
"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js",
"line": 1,
"column": 27
}
}));
stubConsoleMessages.set("console.log(undefined)", new ConsoleMessage({
"id": "1",
"allowRepeating": true,
"source": "console-api",
"type": "log",
"level": "log",
"messageText": null,
"parameters": [
{
"type": "undefined"
}
],
"repeat": 1,
"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"undefined\"}],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js\",\"line\":1,\"column\":27}}",
"stacktrace": null,
"frame": {
"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js",
"line": 1,
"column": 27
}
}));
stubConsoleMessages.set("console.warn('danger, will robinson!')", new ConsoleMessage({
"id": "1",
"allowRepeating": true,
"source": "console-api",
"type": "warn",
"level": "warn",
"messageText": null,
"parameters": [
"danger, will robinson!"
],
"repeat": 1,
"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"type\":\"warn\",\"level\":\"warn\",\"messageText\":null,\"parameters\":[\"danger, will robinson!\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js\",\"line\":1,\"column\":27}}",
"stacktrace": null,
"frame": {
"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js",
"line": 1,
"column": 27
}
}));
stubConsoleMessages.set("console.log(NaN)", new ConsoleMessage({
"id": "1",
"allowRepeating": true,
"source": "console-api",
"type": "log",
"level": "log",
"messageText": null,
"parameters": [
{
"type": "NaN"
}
],
"repeat": 1,
"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"NaN\"}],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js\",\"line\":1,\"column\":27}}",
"stacktrace": null,
"frame": {
"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js",
"line": 1,
"column": 27
}
}));
stubConsoleMessages.set("console.log(null)", new ConsoleMessage({
"id": "1",
"allowRepeating": true,
"source": "console-api",
"type": "log",
"level": "log",
"messageText": null,
"parameters": [
{
"type": "null"
}
],
"repeat": 1,
"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"type\":\"log\",\"level\":\"log\",\"messageText\":null,\"parameters\":[{\"type\":\"null\"}],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js\",\"line\":1,\"column\":27}}",
"stacktrace": null,
"frame": {
"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js",
"line": 1,
"column": 27
}
}));
stubConsoleMessages.set("console.clear()", new ConsoleMessage({
"id": "1",
"allowRepeating": true,
"source": "console-api",
"type": "clear",
"level": "log",
"messageText": null,
"parameters": [
"Console was cleared."
],
"repeat": 1,
"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"type\":\"clear\",\"level\":\"log\",\"messageText\":null,\"parameters\":[\"Console was cleared.\"],\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js\",\"line\":1,\"column\":27}}",
"stacktrace": null,
"frame": {
"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js",
"line": 1,
"column": 27
}
}));
stubConsoleMessages.set("console.count('bar')", new ConsoleMessage({
"id": "1",
"allowRepeating": true,
"source": "console-api",
"type": "log",
"level": "debug",
"messageText": "bar: 1",
"parameters": null,
"repeat": 1,
"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"type\":\"log\",\"level\":\"debug\",\"messageText\":\"bar: 1\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js\",\"line\":1,\"column\":27}}",
"stacktrace": null,
"frame": {
"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js",
"line": 1,
"column": 27
}
}));
stubConsoleMessages.set("console.trace()", new ConsoleMessage({
"id": "1",
"allowRepeating": true,
"source": "console-api",
"type": "trace",
"level": "log",
"messageText": null,
"parameters": [],
"repeat": 1,
"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"console-api\",\"type\":\"trace\",\"level\":\"log\",\"messageText\":null,\"parameters\":[],\"repeatId\":null,\"stacktrace\":[{\"columnNumber\":3,\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js\",\"functionName\":\"bar\",\"language\":2,\"lineNumber\":3},{\"columnNumber\":3,\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js\",\"functionName\":\"foo\",\"language\":2,\"lineNumber\":6},{\"columnNumber\":1,\"filename\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js\",\"functionName\":\"triggerPacket\",\"language\":2,\"lineNumber\":9}],\"frame\":{\"source\":\"http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js\",\"line\":3,\"column\":3}}",
"stacktrace": [
{
"columnNumber": 3,
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js",
"functionName": "bar",
"language": 2,
"lineNumber": 3
},
{
"columnNumber": 3,
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js",
"functionName": "foo",
"language": 2,
"lineNumber": 6
},
{
"columnNumber": 1,
"filename": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js",
"functionName": "triggerPacket",
"language": 2,
"lineNumber": 9
}
],
"frame": {
"source": "http://example.com/browser/devtools/client/webconsole/new-console-output/test/fixtures/stub-generators/test-tempfile.js",
"line": 3,
"column": 3
}
}));
module.exports = stubConsoleMessages

Просмотреть файл

@ -0,0 +1,40 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/*
* THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN TESTS IN FIXTURES/ TO UPDATE.
*/
const { ConsoleMessage } = require("devtools/client/webconsole/new-console-output/types");
let stubConsoleMessages = new Map();
stubConsoleMessages.set("new Date(0)", new ConsoleMessage({
"id": "1",
"allowRepeating": true,
"source": "javascript",
"type": "result",
"level": "log",
"messageText": null,
"parameters": {
"type": "object",
"actor": "server1.conn0.child1/obj29",
"class": "Date",
"extensible": true,
"frozen": false,
"sealed": false,
"ownPropertyLength": 0,
"preview": {
"timestamp": 0
}
},
"repeat": 1,
"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"type\":\"result\",\"level\":\"log\",\"messageText\":null,\"parameters\":{\"type\":\"object\",\"actor\":\"server1.conn0.child1/obj29\",\"class\":\"Date\",\"extensible\":true,\"frozen\":false,\"sealed\":false,\"ownPropertyLength\":0,\"preview\":{\"timestamp\":0}},\"repeatId\":null,\"stacktrace\":null,\"frame\":null}",
"stacktrace": null,
"frame": null
}));
module.exports = stubConsoleMessages

Просмотреть файл

@ -0,0 +1,22 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
let maps = [];
[
"consoleApi",
"evaluationResult",
"pageError",
].forEach((filename) => {
maps[filename] = require(`./${filename}`);
});
// Combine all the maps into a single map.
module.exports = new Map([
...maps.consoleApi,
...maps.evaluationResult,
...maps.pageError,
]);

Просмотреть файл

@ -0,0 +1,29 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
/*
* THIS FILE IS AUTOGENERATED. DO NOT MODIFY BY HAND. RUN TESTS IN FIXTURES/ TO UPDATE.
*/
const { ConsoleMessage } = require("devtools/client/webconsole/new-console-output/types");
let stubConsoleMessages = new Map();
stubConsoleMessages.set("ReferenceError: asdf is not defined", new ConsoleMessage({
"id": "1",
"allowRepeating": true,
"source": "javascript",
"type": "log",
"level": "error",
"messageText": "ReferenceError: asdf is not defined",
"parameters": null,
"repeat": 1,
"repeatId": "{\"id\":null,\"allowRepeating\":true,\"source\":\"javascript\",\"type\":\"log\",\"level\":\"error\",\"messageText\":\"ReferenceError: asdf is not defined\",\"parameters\":null,\"repeatId\":null,\"stacktrace\":null,\"frame\":null}",
"stacktrace": null,
"frame": null
}));
module.exports = stubConsoleMessages

Просмотреть файл

@ -3,8 +3,6 @@
"use strict";
require("devtools/client/webconsole/new-console-output/test/requireHelper")();
let ReactDOM = require("devtools/client/shared/vendor/react-dom");
let React = require("devtools/client/shared/vendor/react");
var TestUtils = React.addons.TestUtils;
@ -12,8 +10,7 @@ var TestUtils = React.addons.TestUtils;
const actions = require("devtools/client/webconsole/new-console-output/actions/messages");
const { configureStore } = require("devtools/client/webconsole/new-console-output/store");
const { IdGenerator } = require("devtools/client/webconsole/new-console-output/utils/id-generator");
const { stubConsoleMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs");
const Services = require("devtools/client/webconsole/new-console-output/test/fixtures/Services");
const stubConsoleMessages = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
/**
* Prepare actions for use in testing.
@ -35,8 +32,7 @@ function setupActions() {
* Prepare the store for use in testing.
*/
function setupStore(input) {
// Inject the Services stub.
const store = configureStore(Services);
const store = configureStore();
// Add the messages from the input commands to the store.
input.forEach((cmd) => {

Просмотреть файл

@ -0,0 +1,9 @@
[DEFAULT]
tags = devtools
subsuite = devtools
support-files =
head.js
!/devtools/client/framework/test/shared-head.js
test-console.html
[browser_webconsole_init.js]

Просмотреть файл

@ -0,0 +1,68 @@
/* -*- 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/ */
"use strict";
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/new-console-output/test/mochitest/test-console.html";
add_task(function* () {
let toolbox = yield openNewTabAndToolbox(TEST_URI, "webconsole");
let hud = toolbox.getCurrentPanel().hud;
let {ui} = hud;
ok(ui.jsterm, "jsterm exists");
ok(ui.newConsoleOutput, "newConsoleOutput exists");
// @TODO: fix proptype errors
let receievedMessages = waitForMessages({
hud,
messages: [{
text: '0',
}, {
text: '1',
}, {
text: '2',
}],
});
yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function() {
content.wrappedJSObject.doLogs(3);
});
yield receievedMessages;
});
/**
* Wait for messages in the web console output, resolving once they are receieved.
*
* @param object options
* - hud: the webconsole
* - messages: Array[Object]. An array of messages to match. Current supported options:
* - text: Exact text match in .message-body
*/
function waitForMessages({ hud, messages }) {
return new Promise(resolve => {
let numMatched = 0;
let receivedLog = hud.ui.on("new-messages", function messagesReceieved(e, newMessage) {
for (let message of messages) {
if (message.matched) {
continue;
}
if (newMessage.node.querySelector(".message-body").textContent == message.text) {
numMatched++;
message.matched = true;
info("Matched a message with text: " + message.text + ", still waiting for " + (messages.length - numMatched) + " messages");
}
if (numMatched === messages.length) {
hud.ui.off("new-messages", messagesReceieved);
resolve();
return;
}
}
});
});
}

Просмотреть файл

@ -0,0 +1,18 @@
/* -*- 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/ */
/* import-globals-from ../../../../framework/test/shared-head.js */
"use strict";
// shared-head.js handles imports, constants, and utility functions
// Load the shared-head file first.
Services.scriptloader.loadSubScript(
"chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js",
this);
Services.prefs.setBoolPref("devtools.webconsole.new-frontend-enabled", true);
registerCleanupFunction(() => {
Services.prefs.clearUserPref("devtools.webconsole.new-frontend-enabled");
});

Просмотреть файл

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Simple webconsole test page</title>
</head>
<body>
<p>Simple webconsole test page</p>
<script>
function doLogs(num) {
num = num || 1;
for (var i = 0; i < num; i++) {
console.log(i);
}
}
</script>
</body>
</html>

Просмотреть файл

@ -3,10 +3,15 @@
# 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 += [
'fixtures/stub-generators/browser.ini',
'mochitest/browser.ini',
]
DIRS += [
'fixtures'
]
MOCHITEST_CHROME_MANIFESTS += [
'chrome/chrome.ini'
'chrome/chrome.ini',
]

Просмотреть файл

@ -4,23 +4,31 @@
const requireHacker = require("require-hacker");
module.exports = () => {
try {
requireHacker.global_hook("default", path => {
switch (path) {
case "react-dom/server":
return `const React = require('react-dev'); module.exports = React`;
case "react-addons-test-utils":
return `const React = require('react-dev'); module.exports = React.addons.TestUtils`;
case "react":
return `const React = require('react-dev'); module.exports = React`;
case "devtools/client/shared/vendor/react":
return `const React = require('react-dev'); module.exports = React`;
case "devtools/client/shared/vendor/react.default":
return `const React = require('react-dev'); module.exports = React`;
}
});
} catch (e) {
// Do nothing. This means the hook is already registered.
requireHacker.global_hook("default", path => {
switch (path) {
// For Enzyme
case "react-dom/server":
return `const React = require('react-dev'); module.exports = React`;
case "react-addons-test-utils":
return `const React = require('react-dev'); module.exports = React.addons.TestUtils`;
// Use react-dev. This would be handled by browserLoader in Firefox.
case "react":
case "devtools/client/shared/vendor/react":
return `const React = require('react-dev'); module.exports = React`;
// For Rep's use of AMD
case "devtools/client/shared/vendor/react.default":
return `const React = require('react-dev'); module.exports = React`;
}
};
// Some modules depend on Chrome APIs which don't work in mocha. When such a module
// is required, replace it with a mock version.
switch (path) {
case "devtools/client/webconsole/utils":
return `module.exports = require("devtools/client/webconsole/new-console-output/test/fixtures/WebConsoleUtils")`;
case "devtools/client/shared/l10n":
return `module.exports = require("devtools/client/webconsole/new-console-output/test/fixtures/LocalizationHelper")`;
case "Services":
case "Services.default":
return `module.exports = require("devtools/client/webconsole/new-console-output/test/fixtures/Services")`;
}
});

Просмотреть файл

@ -14,27 +14,38 @@ const { setupStore } = require("devtools/client/webconsole/new-console-output/te
const { MESSAGE_LEVEL } = require("devtools/client/webconsole/new-console-output/constants");
describe("Filtering", () => {
const numMessages = 5;
const numMessages = 7;
const store = setupStore([
// Console API
"console.log('foobar', 'test')",
"console.warn('danger, will robinson!')",
"console.log(undefined)",
"ReferenceError"
"console.count('bar')",
// Evaluation Result
"new Date(0)",
// PageError
"ReferenceError: asdf is not defined"
]);
// Add a console command as well
// Console Command
store.dispatch(messageAdd(new ConsoleCommand({ messageText: `console.warn("x")` })));
beforeEach(() => {
store.dispatch(actions.filtersClear());
});
describe("Severity filter", () => {
describe("Level filter", () => {
it("filters log messages", () => {
store.dispatch(actions.filterToggle(MESSAGE_LEVEL.LOG));
let messages = getAllMessages(store.getState());
// @TODO It currently filters console command. This should be -2, not -3.
expect(messages.size).toEqual(numMessages - 3);
expect(messages.size).toEqual(numMessages - 2);
});
it("filters debug messages", () => {
store.dispatch(actions.filterToggle(MESSAGE_LEVEL.DEBUG));
let messages = getAllMessages(store.getState());
expect(messages.size).toEqual(numMessages - 1);
});
// @TODO add info stub
@ -61,8 +72,8 @@ describe("Filtering", () => {
let messages = getAllMessages(store.getState());
// @TODO figure out what this should filter
// This does not filter out PageErrors or console commands
expect(messages.size).toEqual(3);
// This does not filter out PageErrors, Evaluation Results or console commands
expect(messages.size).toEqual(5);
});
});
@ -81,6 +92,7 @@ describe("Clear filters", () => {
store.dispatch(actions.filterTextSet("foobar"));
let filters = getAllFilters(store.getState());
expect(filters.toJS()).toEqual({
"debug": true,
"error": false,
"info": true,
"log": true,
@ -92,6 +104,7 @@ describe("Clear filters", () => {
filters = getAllFilters(store.getState());
expect(filters.toJS()).toEqual({
"debug": true,
"error": true,
"info": true,
"log": true,

Просмотреть файл

@ -8,7 +8,7 @@ const {
setupActions,
setupStore
} = require("devtools/client/webconsole/new-console-output/test/helpers");
const { stubConsoleMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs");
const stubConsoleMessages = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
const expect = require("expect");
@ -78,7 +78,7 @@ describe("Message reducer:", () => {
const messages = getAllMessages(getState());
expect(messages.size).toBe(1);
expect(messages.first().parameters[0]).toBe("Console cleared.");
expect(messages.first().parameters[0]).toBe("Console was cleared.");
});
it("limits the number of messages displayed", () => {

Просмотреть файл

@ -3,7 +3,7 @@
"use strict";
const { getRepeatId } = require("devtools/client/webconsole/new-console-output/utils/messages");
const { stubConsoleMessages } = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs");
const stubConsoleMessages = require("devtools/client/webconsole/new-console-output/test/fixtures/stubs/index");
const expect = require("expect");

Просмотреть файл

@ -20,8 +20,6 @@ exports.ConsoleCommand = Immutable.Record({
source: MESSAGE_SOURCE.JAVASCRIPT,
type: MESSAGE_TYPE.COMMAND,
level: MESSAGE_LEVEL.LOG,
category: "input",
severity: MESSAGE_TYPE.LOG,
});
exports.ConsoleMessage = Immutable.Record({
@ -34,6 +32,6 @@ exports.ConsoleMessage = Immutable.Record({
parameters: null,
repeat: 1,
repeatId: null,
category: "output",
severity: "log",
stacktrace: null,
frame: null,
});

Просмотреть файл

@ -6,25 +6,14 @@
"use strict";
let l10n;
try {
const WebConsoleUtils = require("devtools/shared/webconsole/utils").Utils;
const STRINGS_URI = "chrome://devtools/locale/webconsole.properties";
l10n = new WebConsoleUtils.L10n(STRINGS_URI);
} catch (e) {
l10n = require("devtools/client/webconsole/new-console-output/test/fixtures/l10n");
}
const WebConsoleUtils = require("devtools/client/webconsole/utils").Utils;
const STRINGS_URI = "chrome://devtools/locale/webconsole.properties";
const l10n = new WebConsoleUtils.L10n(STRINGS_URI);
const {
MESSAGE_SOURCE,
MESSAGE_TYPE,
MESSAGE_LEVEL,
// Legacy
CATEGORY_JS,
CATEGORY_OUTPUT,
CATEGORY_WEBDEV,
LEVELS,
SEVERITY_LOG,
} = require("../constants");
const { ConsoleMessage } = require("../types");
@ -54,7 +43,7 @@ function transformPacket(packet) {
let parameters = message.arguments;
let type = message.level;
let level = LEVELS[type] || MESSAGE_TYPE.LOG;
let level = getLevelFromType(type);
let messageText = null;
// Special per-type conversion.
@ -66,7 +55,6 @@ function transformPacket(packet) {
case "count":
// Chrome RDP doesn't have a special type for count.
type = MESSAGE_TYPE.LOG;
level = MESSAGE_LEVEL.DEBUG;
let {counter} = message;
let label = counter.label ? counter.label : l10n.getStr("noCounterLabel");
messageText = `${label}: ${counter.count}`;
@ -74,14 +62,30 @@ function transformPacket(packet) {
break;
}
const frame = {
source: message.filename || null,
line: message.lineNumber || null,
column: message.columnNumber || null
};
return new ConsoleMessage({
source: MESSAGE_SOURCE.CONSOLE_API,
type,
level,
parameters,
messageText,
category: CATEGORY_WEBDEV,
severity: level,
stacktrace: message.stacktrace ? message.stacktrace : null,
frame
});
}
case "navigationMessage": {
let { message } = packet;
return new ConsoleMessage({
source: MESSAGE_SOURCE.CONSOLE_API,
type: MESSAGE_TYPE.LOG,
level: MESSAGE_LEVEL.LOG,
messageText: "Navigated to " + message.url,
});
}
@ -97,9 +101,8 @@ function transformPacket(packet) {
return new ConsoleMessage({
source: MESSAGE_SOURCE.JAVASCRIPT,
type: MESSAGE_TYPE.LOG,
level,
messageText: pageError.errorMessage,
category: CATEGORY_JS,
severity: level,
});
}
@ -112,8 +115,6 @@ function transformPacket(packet) {
type: MESSAGE_TYPE.RESULT,
level: MESSAGE_LEVEL.LOG,
parameters: result,
category: CATEGORY_OUTPUT,
severity: SEVERITY_LOG,
});
}
}
@ -127,8 +128,8 @@ function getRepeatId(message) {
}
function convertCachedPacket(packet) {
// The devtools server provides cached message packets in a different shape
// from those of consoleApiCalls, so we prepare them for preparation here.
// The devtools server provides cached message packets in a different shape, so we
// transform them here.
let convertPacket = {};
if (packet._type === "ConsoleAPI") {
convertPacket.message = packet;
@ -136,12 +137,52 @@ function convertCachedPacket(packet) {
} else if (packet._type === "PageError") {
convertPacket.pageError = packet;
convertPacket.type = "pageError";
} else if ("_navPayload" in packet) {
convertPacket.type = "navigationMessage";
convertPacket.message = packet;
} else {
throw new Error("Unexpected packet type");
}
return convertPacket;
}
/**
* Maps a Firefox RDP type to its corresponding level.
*/
function getLevelFromType(type) {
const levels = {
LEVEL_ERROR: "error",
LEVEL_WARNING: "warn",
LEVEL_INFO: "info",
LEVEL_LOG: "log",
LEVEL_DEBUG: "debug",
};
// A mapping from the console API log event levels to the Web Console levels.
const levelMap = {
error: levels.LEVEL_ERROR,
exception: levels.LEVEL_ERROR,
assert: levels.LEVEL_ERROR,
warn: levels.LEVEL_WARNING,
info: levels.LEVEL_INFO,
log: levels.LEVEL_LOG,
clear: levels.LEVEL_LOG,
trace: levels.LEVEL_LOG,
table: levels.LEVEL_LOG,
debug: levels.LEVEL_LOG,
dir: levels.LEVEL_LOG,
dirxml: levels.LEVEL_LOG,
group: levels.LEVEL_LOG,
groupCollapsed: levels.LEVEL_LOG,
groupEnd: levels.LEVEL_LOG,
time: levels.LEVEL_LOG,
timeEnd: levels.LEVEL_LOG,
count: levels.LEVEL_DEBUG,
};
return levelMap[type] || MESSAGE_TYPE.LOG;
}
exports.prepareMessage = prepareMessage;
// Export for use in testing.
exports.getRepeatId = getRepeatId;

Просмотреть файл

@ -14,6 +14,6 @@
"sinon": "^1.17.5"
},
"scripts": {
"test": "NODE_PATH=`pwd`/../../../:`pwd`/../../../devtools/client/shared/vendor/ mocha new-console-output/test/**/*.test.js --compilers js:babel-register -r jsdom-global/register"
"test": "NODE_PATH=`pwd`/../../../:`pwd`/../../../devtools/client/shared/vendor/ mocha new-console-output/test/**/*.test.js --compilers js:babel-register -r jsdom-global/register -r ./new-console-output/test/requireHelper.js"
}
}

Просмотреть файл

@ -539,6 +539,8 @@ WebConsoleFrame.prototype = {
this.jsterm = new JSTerm(this);
this.jsterm.init();
let toolbox = gDevTools.getToolbox(this.owner.target);
if (this.NEW_CONSOLE_OUTPUT_ENABLED) {
// @TODO Remove this once JSTerm is handled with React/Redux.
this.window.jsterm = this.jsterm;
@ -551,7 +553,7 @@ WebConsoleFrame.prototype = {
this.outputNode.parentNode.appendChild(this.experimentalOutputNode);
// @TODO Once the toolbox has been converted to React, see if passing
// in JSTerm is still necessary.
this.newConsoleOutput = new this.window.NewConsoleOutput(this.experimentalOutputNode, this.jsterm);
this.newConsoleOutput = new this.window.NewConsoleOutput(this.experimentalOutputNode, this.jsterm, toolbox);
console.log("Created newConsoleOutput", this.newConsoleOutput);
let filterToolbar = doc.querySelector(".hud-console-filter-toolbar");
@ -563,7 +565,6 @@ WebConsoleFrame.prototype = {
this.jsterm.on("sidebar-opened", this.resize);
this.jsterm.on("sidebar-closed", this.resize);
let toolbox = gDevTools.getToolbox(this.owner.target);
if (toolbox) {
toolbox.on("webconsole-selected", this._onPanelSelected);
}
@ -1929,8 +1930,14 @@ WebConsoleFrame.prototype = {
handleTabNavigated: function (event, packet) {
if (event == "will-navigate") {
if (this.persistLog) {
let marker = new Messages.NavigationMarker(packet, Date.now());
this.output.addMessage(marker);
if (this.NEW_CONSOLE_OUTPUT_ENABLED) {
// Add a _type to hit convertCachedPacket.
packet._type = true;
this.newConsoleOutput.dispatchMessageAdd(packet);
} else {
let marker = new Messages.NavigationMarker(packet, Date.now());
this.output.addMessage(marker);
}
} else {
this.jsterm.clearOutput();
}
@ -3221,6 +3228,21 @@ WebConsoleConnectionProxy.prototype = {
this.webConsoleFrame._onUpdateListeners();
},
/**
* Dispatch a message add on the new frontend and emit an event for tests.
*/
dispatchMessageAdd: function(packet) {
this.webConsoleFrame.newConsoleOutput.dispatchMessageAdd(packet);
// Return the last message in the DOM as the message that was just dispatched. This may not
// always be true in the case of filtered messages, but it's close enough for our tests.
let messageNodes = this.webConsoleFrame.experimentalOutputNode.querySelectorAll(".message");
this.webConsoleFrame.emit("new-messages", {
response: packet,
node: messageNodes[messageNodes.length - 1],
});
},
/**
* The "cachedMessages" response handler.
*
@ -3248,7 +3270,7 @@ WebConsoleConnectionProxy.prototype = {
if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
for (let packet of messages) {
this.webConsoleFrame.newConsoleOutput.dispatchMessageAdd(packet);
this.dispatchMessageAdd(packet);
}
} else {
this.webConsoleFrame.displayCachedMessages(messages);
@ -3274,7 +3296,7 @@ WebConsoleConnectionProxy.prototype = {
_onPageError: function (type, packet) {
if (this.webConsoleFrame && packet.from == this._consoleActor) {
if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
this.webConsoleFrame.newConsoleOutput.dispatchMessageAdd(packet);
this.dispatchMessageAdd(packet);
return;
}
this.webConsoleFrame.handlePageError(packet.pageError);
@ -3310,7 +3332,7 @@ WebConsoleConnectionProxy.prototype = {
_onConsoleAPICall: function (type, packet) {
if (this.webConsoleFrame && packet.from == this._consoleActor) {
if (this.webConsoleFrame.NEW_CONSOLE_OUTPUT_ENABLED) {
this.webConsoleFrame.newConsoleOutput.dispatchMessageAdd(packet);
this.dispatchMessageAdd(packet);
} else {
this.webConsoleFrame.handleConsoleAPICall(packet.message);
}

Просмотреть файл

@ -411,6 +411,7 @@
--magnifier-height: 96px;
/* Width accounts for all color formats (hsl being the longest) */
--label-width: 160px;
--label-height: 23px;
--color: #e0e0e0;
position: absolute;
@ -446,8 +447,45 @@
background-color: var(--color);
border-radius: 2px;
width: var(--label-width);
transform: translateX(calc((var(--magnifier-width) - var(--label-width)) / 2));
height: var(--label-height);
position: relative;
--label-horizontal-center:
translateX(calc((var(--magnifier-width) - var(--label-width)) / 2));
--label-horizontal-left:
translateX(calc((-1 * var(--label-width) + var(--magnifier-width) / 2)));
--label-horizontal-right:
translateX(calc(var(--magnifier-width) / 2));
--label-vertical-top:
translateY(calc((-1 * var(--magnifier-height)) - var(--label-height)));
/* By default the color label container sits below the canvas.
Here we just center it horizontally */
transform: var(--label-horizontal-center);
transition: transform .1s ease-in-out;
}
/* If there isn't enough space below the canvas, we move the label container to the top */
:-moz-native-anonymous .eye-dropper-root[top] .eye-dropper-color-container {
transform: var(--label-horizontal-center) var(--label-vertical-top);
}
/* If there isn't enough space right of the canvas to horizontally center the label
container, offset it to the left */
:-moz-native-anonymous .eye-dropper-root[left] .eye-dropper-color-container {
transform: var(--label-horizontal-left);
}
:-moz-native-anonymous .eye-dropper-root[left][top] .eye-dropper-color-container {
transform: var(--label-horizontal-left) var(--label-vertical-top);
}
/* If there isn't enough space left of the canvas to horizontally center the label
container, offset it to the right */
:-moz-native-anonymous .eye-dropper-root[right] .eye-dropper-color-container {
transform: var(--label-horizontal-right);
}
:-moz-native-anonymous .eye-dropper-root[right][top] .eye-dropper-color-container {
transform: var(--label-horizontal-right) var(--label-vertical-top);
}
:-moz-native-anonymous .eye-dropper-color-preview {

Просмотреть файл

@ -181,13 +181,13 @@ EyeDropper.prototype = {
},
prepareImageCapture() {
// Get the page as an image.
// Get the image data from the content window.
let imageData = getWindowAsImageData(this.win);
let image = new this.win.Image();
image.src = imageData;
// Wait for screenshot to load
image.onload = () => {
// We need to transform imageData to something drawWindow will consume. An ImageBitmap
// works well. We could have used an Image, but doing so results in errors if the page
// defines CSP headers.
this.win.createImageBitmap(imageData).then(image => {
this.pageImage = image;
// We likely haven't drawn anything yet (no mousemove events yet), so start now.
this.draw();
@ -195,7 +195,7 @@ EyeDropper.prototype = {
// Set an attribute on the root element to be able to run tests after the first draw
// was done.
this.getElement("root").setAttribute("drawn", "true");
};
});
},
/**
@ -338,7 +338,25 @@ EyeDropper.prototype = {
},
moveTo(x, y) {
this.getElement("root").setAttribute("style", `top:${y}px;left:${x}px;`);
let root = this.getElement("root");
root.setAttribute("style", `top:${y}px;left:${x}px;`);
// Move the label container to the top if the magnifier is close to the bottom edge.
if (y >= this.win.innerHeight - MAGNIFIER_HEIGHT) {
root.setAttribute("top", "");
} else {
root.removeAttribute("top");
}
// Also offset the label container to the right or left if the magnifier is close to
// the edge.
root.removeAttribute("left");
root.removeAttribute("right");
if (x <= MAGNIFIER_WIDTH) {
root.setAttribute("right", "");
} else if (x >= this.win.innerWidth - MAGNIFIER_WIDTH) {
root.setAttribute("left", "");
}
},
/**
@ -438,9 +456,9 @@ EyeDropper.prototype = {
exports.EyeDropper = EyeDropper;
/**
* Get a content window as image data-url.
* Draw the visible portion of the window on a canvas and get the resulting ImageData.
* @param {Window} win
* @return {String} The data-url
* @return {ImageData} The image data for the window.
*/
function getWindowAsImageData(win) {
let canvas = win.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
@ -456,7 +474,7 @@ function getWindowAsImageData(win) {
ctx.scale(scale, scale);
ctx.drawWindow(win, win.scrollX, win.scrollY, width, height, "#fff");
return canvas.toDataURL();
return ctx.getImageData(0, 0, canvas.width, canvas.height);
}
/**

Просмотреть файл

@ -136,7 +136,8 @@ exports.getFrameElement = getFrameElement;
function getFrameOffsets(boundaryWindow, node) {
let xOffset = 0;
let yOffset = 0;
let frameWin = node.ownerDocument.defaultView;
let frameWin = getWindowFor(node);
let scale = getCurrentZoom(node);
if (boundaryWindow === null) {
@ -614,13 +615,7 @@ exports.isShadowAnonymous = isShadowAnonymous;
* @return {Number}
*/
function getCurrentZoom(node) {
let win = null;
if (node instanceof Ci.nsIDOMNode) {
win = node.ownerDocument.defaultView;
} else if (node instanceof Ci.nsIDOMWindow) {
win = node;
}
let win = getWindowFor(node);
if (!win) {
throw new Error("Unable to get the zoom from the given argument.");
@ -629,3 +624,23 @@ function getCurrentZoom(node) {
return utilsFor(win).fullZoom;
}
exports.getCurrentZoom = getCurrentZoom;
/**
* Return the default view for a given node, where node can be:
* - a DOM node
* - the document node
* - the window itself
* @param {DOMNode|DOMWindow|DOMDocument} node The node to get the window for.
* @return {DOMWindow}
*/
function getWindowFor(node) {
if (node instanceof Ci.nsIDOMNode) {
if (node.nodeType === node.DOCUMENT_NODE) {
return node.defaultView;
}
return node.ownerDocument.defaultView;
} else if (node instanceof Ci.nsIDOMWindow) {
return node;
}
return null;
}

Просмотреть файл

@ -43,7 +43,7 @@ function run_test() {
"Common name is correct.");
equal(result.subject.organization, DUMMY_CERT.organization,
"Organization is correct.");
equal(result.subject.organizationalUnit, DUMMY_CERT.organizationalUnit,
equal(result.subject.organizationUnit, DUMMY_CERT.organizationUnit,
"Organizational unit is correct.");
// Issuer
@ -51,7 +51,7 @@ function run_test() {
"Common name of the issuer is correct.");
equal(result.issuer.organization, DUMMY_CERT.issuerOrganization,
"Organization of the issuer is correct.");
equal(result.issuer.organizationalUnit, DUMMY_CERT.issuerOrganizationalUnit,
equal(result.issuer.organizationUnit, DUMMY_CERT.issuerOrganizationUnit,
"Organizational unit of the issuer is correct.");
// Validity