merge m-c to fx-team

This commit is contained in:
Tim Taubert 2013-05-26 15:22:27 +02:00
commit 528e894d58
30 changed files with 995 additions and 4245 deletions

View File

@ -57,6 +57,7 @@ MOCHITEST_BROWSER_FILES = \
browser_gcli_keyboard3.js \
browser_gcli_menu.js \
browser_gcli_node.js \
browser_gcli_remote.js \
browser_gcli_resource.js \
browser_gcli_scratchpad.js \
browser_gcli_spell.js \

View File

@ -0,0 +1,462 @@
/*
* Copyright 2012, Mozilla Foundation and contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// define(function(require, exports, module) {
// <INJECTED SOURCE:START>
// THIS FILE IS GENERATED FROM SOURCE IN THE GCLI PROJECT
// DO NOT EDIT IT DIRECTLY
var exports = {};
const TEST_URI = "data:text/html;charset=utf-8,<p id='gcli-input'>gcli-testRemote.js</p>";
function test() {
helpers.addTabWithToolbar(TEST_URI, function(options) {
return helpers.runTests(options, exports);
}).then(finish);
}
// <INJECTED SOURCE:END>
'use strict';
// var assert = require('test/assert');
// var helpers = require('gclitest/helpers');
// var mockCommands = require('gclitest/mockCommands');
exports.setup = function(options) {
mockCommands.setup();
};
exports.shutdown = function(options) {
mockCommands.shutdown();
};
exports.testRemote = function(options) {
return helpers.audit(options, [
{
skipRemainingIf: !options.isHttp,
setup: 'remote ',
check: {
input: 'remote ',
hints: '',
markup: 'EEEEEEV',
cursor: 7,
current: '__command',
status: 'ERROR',
options: [ ],
message: 'Can\'t use \'remote\'.',
predictions: [ ],
unassigned: [ ],
}
},
{
setup: 'connect remote',
check: {
input: 'connect remote',
hints: ' [options]',
markup: 'VVVVVVVVVVVVVV',
cursor: 14,
current: 'prefix',
status: 'VALID',
options: [ ],
message: '',
predictions: [ ],
unassigned: [ ],
args: {
command: { name: 'connect' },
prefix: { value: 'remote', arg: ' remote', status: 'VALID', message: '' },
host: { value: undefined, arg: '', status: 'VALID', message: '' },
port: { value: undefined, arg: '', status: 'VALID', message: '' },
}
},
exec: {
output: /^Added [0-9]* commands.$/,
completed: false,
type: 'string',
error: false
}
},
{
setup: 'remote ',
check: {
input: 'remote ',
hints: '',
markup: 'IIIIIIV',
cursor: 7,
current: '__command',
status: 'ERROR',
optionsIncludes: [
'remote', 'remote cd', 'remote context', 'remote echo',
'remote exec', 'remote exit', 'remote firefox', 'remote help',
'remote intro', 'remote make'
],
message: '',
predictions: [ 'remote' ],
unassigned: [ ],
}
},
{
setup: 'remote echo hello world',
check: {
input: 'remote echo hello world',
hints: '',
markup: 'VVVVVVVVVVVVVVVVVVVVVVV',
cursor: 23,
current: 'message',
status: 'VALID',
options: [ ],
message: '',
predictions: [ ],
unassigned: [ ],
args: {
command: { name: 'remote echo' },
message: {
value: 'hello world',
arg: ' hello world',
status: 'VALID',
message: ''
}
}
},
exec: {
output: 'hello world',
completed: false,
type: 'string',
error: false
}
},
{
setup: 'remote exec ls',
check: {
input: 'remote exec ls',
hints: '',
markup: 'VVVVVVVVVVVVVV',
cursor: 14,
current: 'command',
status: 'VALID',
options: [ ],
message: '',
predictions: [ ],
unassigned: [ ],
args: {
command: {
value: 'ls',
arg: ' ls',
status: 'VALID',
message: ''
}
}
},
exec: {
// output: '', We can't rely on the contents of the FS
completed: false,
type: 'string',
error: false
}
},
{
setup: 'remote sleep mistake',
check: {
input: 'remote sleep mistake',
hints: '',
markup: 'VVVVVVVVVVVVVEEEEEEE',
cursor: 20,
current: 'length',
status: 'ERROR',
options: [ ],
message: 'Can\'t convert "mistake" to a number.',
predictions: [ ],
unassigned: [ ],
args: {
command: { name: 'remote sleep' },
length: {
value: undefined,
arg: ' mistake',
status: 'ERROR',
message: 'Can\'t convert "mistake" to a number.'
}
}
}
},
{
setup: 'remote sleep 1',
check: {
input: 'remote sleep 1',
hints: '',
markup: 'VVVVVVVVVVVVVV',
cursor: 14,
current: 'length',
status: 'VALID',
options: [ ],
message: '',
predictions: [ ],
unassigned: [ ],
args: {
command: { name: 'remote sleep' },
length: { value: 1, arg: ' 1', status: 'VALID', message: '' }
}
},
exec: {
output: 'Done',
completed: false,
type: 'string',
error: false
}
},
{
setup: 'remote help ',
skipIf: true, // The help command is not remotable
check: {
input: 'remote help ',
hints: '[search]',
markup: 'VVVVVVVVVVVV',
cursor: 12,
current: 'search',
status: 'VALID',
options: [ ],
message: '',
predictions: [ ],
unassigned: [ ],
args: {
command: { name: 'remote help' },
search: {
value: undefined,
arg: '',
status: 'VALID',
message: ''
}
}
},
exec: {
output: '',
completed: false,
type: 'string',
error: false
}
},
{
setup: 'remote intro',
check: {
input: 'remote intro',
hints: '',
markup: 'VVVVVVVVVVVV',
cursor: 12,
current: '__command',
status: 'VALID',
options: [ ],
message: '',
predictions: [ ],
unassigned: [ ],
args: {
command: { name: 'remote intro' }
}
},
exec: {
output: [
/^This command line/,
/F1\/Escape/
],
completed: false,
type: 'intro',
error: false
}
},
{
setup: 'context remote',
check: {
input: 'context remote',
hints: '',
markup: 'VVVVVVVVVVVVVV',
cursor: 14,
current: 'prefix',
status: 'VALID',
optionsContains: [ 'remote', 'remote cd', 'remote echo', 'remote exec', 'remote exit', 'remote firefox', 'remote help', 'remote intro', 'remote make' ],
message: '',
predictionsContains: [ 'remote', 'remote cd', 'remote echo', 'remote exec', 'remote exit', 'remote firefox', 'remote help', 'remote intro', 'remote make', 'remote pref' ],
unassigned: [ ],
args: {
command: { name: 'context' },
prefix: {
/*value:[object Object],*/
arg: ' remote',
status: 'VALID',
message: ''
}
}
},
exec: {
output: 'Using remote as a command prefix',
completed: true,
type: 'string',
error: false
}
},
{
setup: 'exec ls',
check: {
input: 'exec ls',
hints: '',
markup: 'VVVVVVV',
cursor: 7,
current: 'command',
status: 'VALID',
options: [ ],
message: '',
predictions: [ ],
unassigned: [ ],
args: {
command: { value: 'ls', arg: ' ls', status: 'VALID', message: '' },
}
},
exec: {
// output: '', We can't rely on the contents of the filesystem
completed: false,
type: 'string',
error: false
}
},
{
setup: 'echo hello world',
check: {
input: 'echo hello world',
hints: '',
markup: 'VVVVVVVVVVVVVVVV',
cursor: 16,
current: 'message',
status: 'VALID',
options: [ ],
message: '',
predictions: [ ],
unassigned: [ ],
args: {
command: { name: 'remote echo' },
message: {
value: 'hello world',
arg: ' hello world',
status: 'VALID',
message: ''
}
}
},
exec: {
output: /^hello world$/,
type: 'string',
error: false
}
},
{
setup: 'context',
check: {
input: 'context',
hints: ' [prefix]',
markup: 'VVVVVVV',
cursor: 7,
current: '__command',
status: 'VALID',
optionsContains: [ 'remote', 'remote cd', 'remote echo', 'remote exec', 'remote exit', 'remote firefox', 'remote help', 'remote intro', 'remote make' ],
message: '',
predictions: [ ],
unassigned: [ ],
args: {
command: { name: 'context' },
prefix: { value: undefined, arg: '', status: 'VALID', message: '' }
}
},
exec: {
output: 'Command prefix is unset',
completed: true,
type: 'string',
error: false
}
},
{
setup: 'disconnect ',
check: {
input: 'disconnect ',
hints: 'remote',
markup: 'VVVVVVVVVVV',
cursor: 11,
current: 'prefix',
status: 'ERROR',
options: [ 'remote' ],
message: '',
predictions: [ 'remote' ],
unassigned: [ ],
args: {
command: { name: 'disconnect' },
prefix: {
value: undefined,
arg: '',
status: 'INCOMPLETE',
message: ''
}
}
}
},
{
setup: 'disconnect remote --force',
check: {
input: 'disconnect remote --force',
hints: '',
markup: 'VVVVVVVVVVVVVVVVVVVVVVVVV',
cursor: 25,
current: 'force',
status: 'VALID',
message: '',
unassigned: [ ],
args: {
command: { name: 'disconnect' },
prefix: {
value: function(connection) {
assert.is(connection.prefix, 'remote', 'disconnecting remote');
},
arg: ' remote',
status: 'VALID',
message: ''
}
}
},
exec: {
output: /^Removed [0-9]* commands.$/,
completed: true,
type: 'string',
error: false
}
},
{
setup: 'remote ',
check: {
input: 'remote ',
hints: '',
markup: 'EEEEEEV',
cursor: 7,
current: '__command',
status: 'ERROR',
options: [ ],
message: 'Can\'t use \'remote\'.',
predictions: [ ],
unassigned: [ ],
}
}
]);
};
// });

View File

@ -226,7 +226,7 @@ helpers._actual = {
.replace(/ $/, '');
};
var promisedJoin = util.promised(join);
var promisedJoin = Promise.promised(join);
return promisedJoin(templateData.directTabText,
templateData.emptyParameters,
templateData.arrowTabText);
@ -314,12 +314,12 @@ helpers._createDebugCheck = function(options) {
var hintsPromise = helpers._actual.hints(options);
var predictionsPromise = helpers._actual.predictions(options);
return util.all(hintsPromise, predictionsPromise).then(function(values) {
return Promise.all(hintsPromise, predictionsPromise).then(function(values) {
var hints = values[0];
var predictions = values[1];
var output = '';
output += 'helpers.audit(options, [\n';
output += 'return helpers.audit(options, [\n';
output += ' {\n';
if (cursor === input.length) {
@ -627,13 +627,14 @@ helpers._check = function(options, name, checks) {
Object.keys(checks.args).forEach(function(paramName) {
var check = checks.args[paramName];
var assignment;
if (paramName === 'command') {
// We allow an 'argument' called 'command' to be the command itself, but
// what if the command has a parameter called 'command' (for example, an
// 'exec' command)? We default to using the parameter because checking
// the command value is less useful
var assignment = requisition.getAssignment(paramName);
if (assignment == null && paramName === 'command') {
assignment = requisition.commandAssignment;
}
else {
assignment = requisition.getAssignment(paramName);
}
if (assignment == null) {
assert.ok(false, 'Unknown arg: ' + paramName + suffix);
@ -641,9 +642,19 @@ helpers._check = function(options, name, checks) {
}
if ('value' in check) {
assert.is(assignment.value,
check.value,
'arg.' + paramName + '.value' + suffix);
if (typeof check.value === 'function') {
try {
check.value(assignment.value);
}
catch (ex) {
assert.ok(false, '' + ex);
}
}
else {
assert.is(assignment.value,
check.value,
'arg.' + paramName + '.value' + suffix);
}
}
if ('name' in check) {
@ -689,7 +700,7 @@ helpers._check = function(options, name, checks) {
});
}
return util.all(outstanding).then(function() {
return Promise.all(outstanding).then(function() {
// Ensure the promise resolves to nothing
return undefined;
});
@ -707,7 +718,16 @@ helpers._exec = function(options, name, expected) {
return Promise.resolve({});
}
var output = options.display.requisition.exec({ hidden: true });
var output;
try {
output = options.display.requisition.exec({ hidden: true });
}
catch (ex) {
assert.ok(false, 'Failure executing \'' + name + '\': ' + ex);
util.errorHandler(ex);
return Promise.resolve({});
}
if ('completed' in expected) {
assert.is(output.completed,
@ -725,9 +745,6 @@ helpers._exec = function(options, name, expected) {
}
var checkOutput = function() {
var div = options.window.document.createElement('div');
var conversionContext = options.display.requisition.conversionContext;
if ('type' in expected) {
assert.is(output.type,
expected.type,
@ -740,20 +757,20 @@ helpers._exec = function(options, name, expected) {
'output.error for: ' + name);
}
var conversionContext = options.display.requisition.conversionContext;
var convertPromise = converters.convert(output.data, output.type, 'dom',
conversionContext);
return convertPromise.then(function(node) {
div.appendChild(node);
var actualOutput = div.textContent.trim();
var actualOutput = node.textContent.trim();
var doTest = function(match, against) {
if (match.test(against)) {
assert.ok(true, 'html output for ' + name + ' should match ' +
match.source);
assert.ok(true, 'html output for ' + name + ' should match /' +
match.source + '/');
} else {
assert.ok(false, 'html output for ' + name + ' should match ' +
assert.ok(false, 'html output for ' + name + ' should match /' +
match.source +
'. Actual textContent: "' + against + '"');
'/. Actual textContent: "' + against + '"');
}
};
@ -810,11 +827,13 @@ var totalResponseTime = 0;
var averageOver = 0;
var maxResponseTime = 0;
var maxResponseCulprit = undefined;
var start = undefined;
/**
* Restart the stats collection process
*/
helpers.resetResponseTimes = function() {
start = new Date().getTime();
totalResponseTime = 0;
averageOver = 0;
maxResponseTime = 0;
@ -849,6 +868,20 @@ Object.defineProperty(helpers, 'maxResponseCulprit', {
enumerable: true
});
/**
* Quick summary of the times
*/
Object.defineProperty(helpers, 'timingSummary', {
get: function() {
var elapsed = (new Date().getTime() - start) / 1000;
return 'Total ' + elapsed + 's, ' +
'ave response ' + helpers.averageResponseTime + 'ms, ' +
'max response ' + helpers.maxResponseTime + 'ms ' +
'from \'' + helpers.maxResponseCulprit + '\'';
},
enumerable: true
});
/**
* A way of turning a set of tests into something more declarative, this helps
* to allow tests to be asynchronous.

View File

@ -99,7 +99,6 @@ MOCHITEST_BROWSER_TESTS = \
browser_dbg_source_maps-01.js \
browser_dbg_source_maps-02.js \
head.js \
helpers.js \
$(NULL)
MOCHITEST_BROWSER_PAGES = \

View File

@ -24,7 +24,7 @@ let TargetFactory = devtools.TargetFactory;
// Import the GCLI test helper
let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
Services.scriptloader.loadSubScript(testDir + "/helpers.js", this);
Services.scriptloader.loadSubScript(testDir + "../../../commandline/test/helpers.js", this);
const EXAMPLE_URL = "http://example.com/browser/browser/devtools/debugger/test/";
const TAB1_URL = EXAMPLE_URL + "browser_dbg_tab1.html";

File diff suppressed because it is too large Load Diff

View File

@ -183,7 +183,6 @@ FontInspector.prototype = {
*/
let extraCSS = "* {padding:0;margin:0}";
extraCSS += ".theme-dark {color: white}";
extraCSS += "p {font-family: '" + name + "';}";
extraCSS += "p {font-size: 40px;line-height:60px;padding:0 10px;margin:0;}";
cssCode += extraCSS;
let src = "data:text/html;charset=utf-8,<!DOCTYPE HTML><head><base></base></head><style></style><p contenteditable>Abc</p>";
@ -194,6 +193,7 @@ FontInspector.prototype = {
// nightmare.
doc.querySelector("base").href = base;
doc.querySelector("style").textContent = cssCode;
doc.querySelector("p").style.fontFamily = name;
// Forward theme
doc.documentElement.className = document.documentElement.className;
}, true);

View File

@ -24,7 +24,7 @@
</div>
<div id="template" style="display:none">
<section class="font">
<iframe sandbox="allow-same-origin" class="font-preview"></iframe>
<iframe sandbox="" class="font-preview"></iframe>
<div class="font-info">
<h1 class="font-name"></h1>
<span class="font-is-local">&system;</span>

View File

@ -45,7 +45,6 @@ _BROWSER_FILES = \
browser_inspector_bug_835722_infobar_reappears.js \
browser_inspector_bug_840156_destroy_after_navigation.js \
head.js \
helpers.js \
$(NULL)
libs:: $(_BROWSER_FILES)

View File

@ -17,7 +17,7 @@ let console = tempScope.console;
// Import the GCLI test helper
let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
Services.scriptloader.loadSubScript(testDir + "/helpers.js", this);
Services.scriptloader.loadSubScript(testDir + "../../../commandline/test/helpers.js", this);
function openInspector(callback)
{

File diff suppressed because it is too large Load Diff

View File

@ -51,7 +51,6 @@ _BROWSER_FILES = \
browser_responsive_cmd.js \
browser_responsivecomputedview.js \
head.js \
helpers.js \
$(NULL)
libs:: $(_BROWSER_FILES)

View File

@ -8,7 +8,7 @@ let TargetFactory = devtools.TargetFactory;
// Import the GCLI test helper
let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
Services.scriptloader.loadSubScript(testDir + "/helpers.js", this);
Services.scriptloader.loadSubScript(testDir + "../../../commandline/test/helpers.js", this);
function openInspector(callback)
{

File diff suppressed because it is too large Load Diff

View File

@ -29,10 +29,8 @@ _BROWSER_TEST_FILES = \
browser_styleeditor_bug_851132_middle_click.js \
browser_styleeditor_nostyle.js \
head.js \
helpers.js \
four.html \
head.js \
helpers.js \
import.css \
import.html \
import2.css \

View File

@ -19,7 +19,7 @@ let cache = Cc["@mozilla.org/network/cache-service;1"]
// Import the GCLI test helper
let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
Services.scriptloader.loadSubScript(testDir + "/helpers.js", this);
Services.scriptloader.loadSubScript(testDir + "../../../commandline/test/helpers.js", this);
function cleanup()
{

File diff suppressed because it is too large Load Diff

View File

@ -130,6 +130,7 @@ MOCHITEST_BROWSER_FILES = \
browser_bug_862916_console_dir_and_filter_off.js \
browser_console_native_getters.js \
browser_bug_871156_ctrlw_close_tab.js \
browser_console_private_browsing.js \
head.js \
$(NULL)

View File

@ -0,0 +1,195 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Bug 874061: test for how the browser and web consoles display messages coming
// from private windows. See bug for description of expected behavior.
function test()
{
const TEST_URI = "data:text/html;charset=utf8,<p>hello world! bug 874061" +
"<button onclick='console.log(\"foobar bug 874061\");" +
"fooBazBaz.yummy()'>click</button>";
let ConsoleAPIStorage = Cu.import("resource://gre/modules/ConsoleAPIStorage.jsm", {}).ConsoleAPIStorage;
let privateWindow, privateBrowser, privateTab, privateContent;
let hud, expectedMessages, nonPrivateMessage;
start();
function start()
{
gBrowser.selectedTab = gBrowser.addTab("data:text/html;charset=utf8," +
"<p>hello world! I am not private!");
gBrowser.selectedBrowser.addEventListener("load", onLoadTab, true);
}
function onLoadTab()
{
gBrowser.selectedBrowser.removeEventListener("load", onLoadTab, true);
info("onLoadTab()");
// Make sure we have a clean state to start with.
Services.console.reset();
ConsoleAPIStorage.clearEvents();
// Add a non-private message to the browser console.
content.console.log("bug874061-not-private");
nonPrivateMessage = {
name: "console message from a non-private window",
text: "bug874061-not-private",
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
};
privateWindow = OpenBrowserWindow({ private: true });
ok(privateWindow, "new private window");
ok(PrivateBrowsingUtils.isWindowPrivate(privateWindow), "window is private");
whenDelayedStartupFinished(privateWindow, onPrivateWindowReady);
}
function onPrivateWindowReady()
{
info("private browser window opened");
privateBrowser = privateWindow.gBrowser;
privateTab = privateBrowser.selectedTab = privateBrowser.addTab(TEST_URI);
privateBrowser.selectedBrowser.addEventListener("load", function onLoad() {
info("private tab opened");
privateBrowser.selectedBrowser.removeEventListener("load", onLoad, true);
privateContent = privateBrowser.selectedBrowser.contentWindow;
ok(PrivateBrowsingUtils.isWindowPrivate(privateContent), "tab window is private");
openConsole(privateTab, consoleOpened);
}, true);
}
function addMessages()
{
let button = privateContent.document.querySelector("button");
ok(button, "button in page");
EventUtils.synthesizeMouse(button, 2, 2, {}, privateContent);
}
function consoleOpened(aHud)
{
hud = aHud;
ok(hud, "web console opened");
addMessages();
expectedMessages = [
{
name: "script error",
text: "fooBazBaz is not defined",
category: CATEGORY_JS,
severity: SEVERITY_ERROR,
},
{
name: "console message",
text: "foobar bug 874061",
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
},
];
// Make sure messages are displayed in the web console as they happen, even
// if this is a private tab.
waitForMessages({
webconsole: hud,
messages: expectedMessages,
}).then(testCachedMessages);
}
function testCachedMessages()
{
info("testCachedMessages()");
closeConsole(privateTab, () => {
info("web console closed");
openConsole(privateTab, consoleReopened);
});
}
function consoleReopened(aHud)
{
hud = aHud;
ok(hud, "web console reopened");
// Make sure that cached messages are displayed in the web console, even
// if this is a private tab.
waitForMessages({
webconsole: hud,
messages: expectedMessages,
}).then(testBrowserConsole);
}
function testBrowserConsole()
{
info("testBrowserConsole()");
closeConsole(privateTab, () => {
info("web console closed");
privateWindow.HUDConsoleUI.toggleBrowserConsole().then(onBrowserConsoleOpen);
});
}
// Make sure that the cached messages from private tabs are not displayed in
// the browser console.
function checkNoPrivateMessages()
{
let text = hud.outputNode.textContent;
is(text.indexOf("fooBazBaz"), -1, "no exception displayed");
is(text.indexOf("bug 874061"), -1, "no console message displayed");
}
function onBrowserConsoleOpen(aHud)
{
hud = aHud;
ok(hud, "browser console opened");
checkNoPrivateMessages();
addMessages();
expectedMessages.push(nonPrivateMessage);
// Make sure that live messages are displayed in the browser console, even
// from private tabs.
waitForMessages({
webconsole: hud,
messages: expectedMessages,
}).then(testPrivateWindowClose);
}
function testPrivateWindowClose()
{
info("close the private window and check if the private messages are removed");
hud.jsterm.once("private-messages-cleared", () => {
isnot(hud.outputNode.textContent.indexOf("bug874061-not-private"), -1,
"non-private messages are still shown after private window closed");
checkNoPrivateMessages();
info("close the browser console");
privateWindow.HUDConsoleUI.toggleBrowserConsole().then(() => {
info("reopen the browser console");
executeSoon(() =>
HUDConsoleUI.toggleBrowserConsole().then(onBrowserConsoleReopen));
});
});
privateWindow.BrowserTryToCloseWindow();
}
function onBrowserConsoleReopen(aHud)
{
hud = aHud;
ok(hud, "browser console reopened");
// Make sure that the non-private message is still shown after reopen.
waitForMessages({
webconsole: hud,
messages: [nonPrivateMessage],
}).then(() => {
// Make sure that no private message is displayed after closing the private
// window and reopening the Browser Console.
checkNoPrivateMessages();
executeSoon(finishTest);
});
}
}

View File

@ -1222,3 +1222,13 @@ function scrollOutputToNode(aNode)
let nsIScrollBoxObject = boxObject.QueryInterface(Ci.nsIScrollBoxObject);
nsIScrollBoxObject.ensureElementIsVisible(aNode);
}
function whenDelayedStartupFinished(aWindow, aCallback)
{
Services.obs.addObserver(function observer(aSubject, aTopic) {
if (aWindow == aSubject) {
Services.obs.removeObserver(observer, aTopic);
executeSoon(aCallback);
}
}, "browser-delayed-startup-finished", false);
}

View File

@ -1099,6 +1099,9 @@ WebConsoleFrame.prototype = {
let node = this.createMessageNode(CATEGORY_WEBDEV, LEVELS[level], body,
sourceURL, sourceLine, clipboardText,
level, aMessage.timeStamp);
if (aMessage.private) {
node.setAttribute("private", true);
}
if (objectActors.size > 0) {
node._objectActors = objectActors;
@ -1176,6 +1179,9 @@ WebConsoleFrame.prototype = {
aScriptError.sourceName,
aScriptError.lineNumber, null, null,
aScriptError.timeStamp);
if (aScriptError.private) {
node.setAttribute("private", true);
}
return node;
},
@ -1255,6 +1261,9 @@ WebConsoleFrame.prototype = {
let messageNode = this.createMessageNode(CATEGORY_NETWORK, severity,
msgNode, null, null, clipboardText);
if (networkInfo.private) {
messageNode.setAttribute("private", true);
}
messageNode._connectionId = aActorId;
messageNode.url = request.url;
@ -1380,6 +1389,7 @@ WebConsoleFrame.prototype = {
response: {},
timings: {},
updates: [], // track the list of network event updates
private: aActor.private,
};
this._networkRequests[aActor.actor] = networkInfo;
@ -3671,6 +3681,18 @@ JSTerm.prototype = {
}
},
/**
* Remove all of the private messages from the Web Console output.
*/
clearPrivateMessages: function JST_clearPrivateMessages()
{
let nodes = this.hud.outputNode.querySelectorAll("richlistitem[private]");
for (let node of nodes) {
this.hud.removeOutputMessage(node);
}
this.emit("private-messages-cleared");
},
/**
* Updates the size of the input field (command line) to fit its contents.
*
@ -4516,6 +4538,7 @@ function WebConsoleConnectionProxy(aWebConsole, aTarget)
this._onAttachConsole = this._onAttachConsole.bind(this);
this._onCachedMessages = this._onCachedMessages.bind(this);
this._connectionTimeout = this._connectionTimeout.bind(this);
this._onLastPrivateContextExited = this._onLastPrivateContextExited.bind(this);
}
WebConsoleConnectionProxy.prototype = {
@ -4616,6 +4639,7 @@ WebConsoleConnectionProxy.prototype = {
client.addListener("networkEvent", this._onNetworkEvent);
client.addListener("networkEventUpdate", this._onNetworkEventUpdate);
client.addListener("fileActivity", this._onFileActivity);
client.addListener("lastPrivateContextExited", this._onLastPrivateContextExited);
this.target.on("will-navigate", this._onTabNavigated);
this.target.on("navigate", this._onTabNavigated);
@ -4800,6 +4824,24 @@ WebConsoleConnectionProxy.prototype = {
}
},
/**
* The "lastPrivateContextExited" message type handler. When this message is
* received the Web Console UI is cleared.
*
* @private
* @param string aType
* Message type.
* @param object aPacket
* The message received from the server.
*/
_onLastPrivateContextExited:
function WCCP__onLastPrivateContextExited(aType, aPacket)
{
if (this.owner && aPacket.from == this._consoleActor) {
this.owner.jsterm.clearPrivateMessages();
}
},
/**
* The "will-navigate" and "navigate" event handlers. We redirect any message
* to the UI for displaying.
@ -4862,6 +4904,7 @@ WebConsoleConnectionProxy.prototype = {
this.client.removeListener("networkEvent", this._onNetworkEvent);
this.client.removeListener("networkEventUpdate", this._onNetworkEventUpdate);
this.client.removeListener("fileActivity", this._onFileActivity);
this.client.removeListener("lastPrivateContextExited", this._onLastPrivateContextExited);
this.target.off("will-navigate", this._onTabNavigated);
this.target.off("navigate", this._onTabNavigated);

View File

@ -306,10 +306,21 @@ disconnectManual=Connect to the server, creating local versions of the commands
# short as possible.
disconnectPrefixDesc=Parent prefix for imported commands
# LOCALIZATION NOTE (disconnectForceDesc): A short description of the 'force'
# parameter to the 'disconnect' command. This string is designed to be shown
# in a dialog with restricted space, which is why it should be as short as
# possible.
disconnectForceDesc=Ignore outstanding requests
# LOCALIZATION NOTE (disconnectReply): The output of the 'disconnect' command,
# telling the user what it's done.
disconnectReply=Removed %S commands.
# LOCALIZATION NOTE (disconnectOutstanding): An error message displayed when
# the user attempts to disconnect before all requests have completed. %1$S is
# a list of commands which are incomplete
disconnectOutstanding=Outstanding requests (%1$S)
# LOCALIZATION NOTE (prefDesc): A very short description of the 'pref'
# command. This string is designed to be shown in a menu alongside the command
# name, which is why it should be as short as possible. See prefManual for a

View File

@ -211,7 +211,7 @@ ConsoleAPI.prototype = {
{
let window = this._window.get();
let metaForCall = {
isPrivate: PrivateBrowsingUtils.isWindowPrivate(window),
private: PrivateBrowsingUtils.isWindowPrivate(window),
timeStamp: Date.now(),
stack: this.getStackTrace(aMethod != "trace" ? 1 : null),
};
@ -270,6 +270,7 @@ ConsoleAPI.prototype = {
functionName: frame.functionName,
timeStamp: meta.timeStamp,
arguments: args,
private: meta.private,
};
switch (method) {
@ -308,7 +309,7 @@ ConsoleAPI.prototype = {
return;
}
this.notifyObservers(method, consoleEvent, meta.isPrivate);
this.notifyObservers(method, consoleEvent);
},
/**
@ -319,18 +320,11 @@ ConsoleAPI.prototype = {
* @param object aConsoleEvent
* The console event object to send to observers for the given console
* API call.
* @param boolean aPrivate
* Tells whether the window is in private browsing mode.
*/
notifyObservers: function CA_notifyObservers(aLevel, aConsoleEvent, aPrivate)
notifyObservers: function CA_notifyObservers(aLevel, aConsoleEvent)
{
aConsoleEvent.wrappedJSObject = aConsoleEvent;
// Store non-private messages for which the inner window was not destroyed.
if (!aPrivate) {
ConsoleAPIStorage.recordEvent(this._innerID, aConsoleEvent);
}
ConsoleAPIStorage.recordEvent(this._innerID, aConsoleEvent);
Services.obs.notifyObservers(aConsoleEvent, "console-api-log-event",
this._outerID);
},

View File

@ -53,7 +53,8 @@ function test() {
aWindow.console.log("foo bar baz (private: " + aIsPrivateMode + ")");
}, true);
storageShouldOccur = !aIsPrivateMode;
// We expect that console API messages are always stored.
storageShouldOccur = true;
innerID = getInnerWindowId(aWindow);
beforeEvents = CSS.ConsoleAPIStorage.getEvents(innerID);
aWindow.gBrowser.selectedBrowser.loadURI(testURI);

View File

@ -174,6 +174,7 @@ const UnsolicitedNotifications = {
"consoleAPICall": "consoleAPICall",
"eventNotification": "eventNotification",
"fileActivity": "fileActivity",
"lastPrivateContextExited": "lastPrivateContextExited",
"networkEvent": "networkEvent",
"networkEventUpdate": "networkEventUpdate",
"newGlobal": "newGlobal",

View File

@ -104,7 +104,7 @@ var mozl10n = {};
})(mozl10n);
define('gcli/index', ['require', 'exports', 'module' , 'gcli/types/basic', 'gcli/types/selection', 'gcli/types/command', 'gcli/types/date', 'gcli/types/javascript', 'gcli/types/node', 'gcli/types/resource', 'gcli/types/setting', 'gcli/settings', 'gcli/ui/intro', 'gcli/ui/focus', 'gcli/ui/fields/basic', 'gcli/ui/fields/javascript', 'gcli/ui/fields/selection', 'gcli/commands/connect', 'gcli/commands/context', 'gcli/commands/help', 'gcli/commands/pref', 'gcli/canon', 'gcli/converters', 'gcli/ui/ffdisplay'], function(require, exports, module) {
define('gcli/index', ['require', 'exports', 'module' , 'gcli/types/basic', 'gcli/types/selection', 'gcli/types/command', 'gcli/types/date', 'gcli/types/javascript', 'gcli/types/node', 'gcli/types/resource', 'gcli/types/setting', 'gcli/settings', 'gcli/ui/intro', 'gcli/ui/focus', 'gcli/ui/fields/basic', 'gcli/ui/fields/javascript', 'gcli/ui/fields/selection', 'gcli/commands/connect', 'gcli/commands/context', 'gcli/commands/help', 'gcli/commands/pref', 'gcli/canon', 'gcli/converters', 'gcli/types', 'gcli/ui/ffdisplay'], function(require, exports, module) {
'use strict';
@ -143,6 +143,9 @@ define('gcli/index', ['require', 'exports', 'module' , 'gcli/types/basic', 'gcli
exports.removeCommand = require('gcli/canon').removeCommand;
exports.addConverter = require('gcli/converters').addConverter;
exports.removeConverter = require('gcli/converters').removeConverter;
exports.addType = require('gcli/types').addType;
exports.removeType = require('gcli/types').removeType;
exports.lookup = mozl10n.lookup;
exports.lookupFormat = mozl10n.lookupFormat;
@ -621,7 +624,7 @@ ArrayType.prototype.parse = function(arg, context) {
}.bind(this);
var conversionPromises = arg.getArguments().map(subArgParse);
return util.all(conversionPromises).then(function(conversions) {
return Promise.all(conversionPromises).then(function(conversions) {
return new ArrayConversion(conversions, arg);
});
};
@ -654,15 +657,17 @@ exports.ArrayType = ArrayType;
define('util/promise', ['require', 'exports', 'module' ], function(require, exports, module) {
'use strict';
'use strict';
var imported = {};
Components.utils.import("resource://gre/modules/commonjs/sdk/core/promise.js",
imported);
var imported = {};
Components.utils.import("resource://gre/modules/commonjs/sdk/core/promise.js",
imported);
exports.defer = imported.Promise.defer;
exports.resolve = imported.Promise.resolve;
exports.reject = imported.Promise.reject;
exports.defer = imported.Promise.defer;
exports.resolve = imported.Promise.resolve;
exports.reject = imported.Promise.reject;
exports.promised = imported.Promise.promised;
exports.all = imported.Promise.all;
});
/*
@ -903,65 +908,6 @@ exports.createEvent = function(name) {
var Promise = require('util/promise');
/**
* Implementation of 'promised', while we wait for bug 790195 to be fixed.
* @see Consuming promises in https://addons.mozilla.org/en-US/developers/docs/sdk/latest/modules/sdk/core/promise.html
* @see https://bugzilla.mozilla.org/show_bug.cgi?id=790195
* @see https://github.com/mozilla/addon-sdk/blob/master/packages/api-utils/lib/promise.js#L179
*/
exports.promised = (function() {
// Note: Define shortcuts and utility functions here in order to avoid
// slower property accesses and unnecessary closure creations on each
// call of this popular function.
var call = Function.call;
var concat = Array.prototype.concat;
// Utility function that does following:
// execute([ f, self, args...]) => f.apply(self, args)
function execute(args) { return call.apply(call, args); }
// Utility function that takes promise of `a` array and maybe promise `b`
// as arguments and returns promise for `a.concat(b)`.
function promisedConcat(promises, unknown) {
return promises.then(function(values) {
return Promise.resolve(unknown).then(function(value) {
return values.concat([ value ]);
});
});
}
return function promised(f, prototype) {
/**
Returns a wrapped `f`, which when called returns a promise that resolves to
`f(...)` passing all the given arguments to it, which by the way may be
promises. Optionally second `prototype` argument may be provided to be used
a prototype for a returned promise.
## Example
var promise = promised(Array)(1, promise(2), promise(3))
promise.then(console.log) // => [ 1, 2, 3 ]
**/
return function promised() {
// create array of [ f, this, args... ]
return concat.apply([ f, this ], arguments).
// reduce it via `promisedConcat` to get promised array of fulfillments
reduce(promisedConcat, Promise.resolve([], prototype)).
// finally map that to promise of `f.apply(this, args...)`
then(execute);
};
};
})();
/**
* Convert an array of promises to a single promise, which is resolved (with an
* array containing resolved values) only when all the component promises are
* resolved.
*/
exports.all = exports.promised(Array);
/**
* Utility to convert a resolved promise to a concrete value.
* Warning: This is something of an experiment. The alternative of mixing
@ -992,8 +938,10 @@ exports.synchronize = function(promise) {
};
/**
* promiseMap is roughly like Array.map except that the action is taken to be
* something that completes asynchronously, returning a promise.
* promiseEach is roughly like Array.forEach except that the action is taken to
* be something that completes asynchronously, returning a promise, so we wait
* for the action to complete for each array element before moving onto the
* next.
* @param array An array of objects to enumerate
* @param action A function to call for each member of the array
* @param scope Optional object to use as 'this' for the function calls
@ -1007,23 +955,26 @@ exports.promiseEach = function(array, action, scope) {
}
var deferred = Promise.defer();
var replies = [];
var callNext = function(index) {
var replies = [];
var promiseReply = action.call(scope, array[index]);
Promise.resolve(promiseReply).then(function(reply) {
var onSuccess = function(reply) {
replies[index] = reply;
var nextIndex = index + 1;
if (nextIndex >= array.length) {
if (index + 1 >= array.length) {
deferred.resolve(replies);
}
else {
callNext(nextIndex);
callNext(index + 1);
}
}).then(null, function(ex) {
};
var onFailure = function(ex) {
deferred.reject(ex);
});
};
var reply = action.call(scope, array[index], index, array);
Promise.resolve(reply).then(onSuccess).then(null, onFailure);
};
callNext(0);
@ -3864,6 +3815,10 @@ Canon.prototype.addProxyCommands = function(prefix, commandSpecs, remoter, to) {
names.forEach(function(name) {
var commandSpec = commandSpecs[name];
if (commandSpec.noRemote) {
return;
}
if (!commandSpec.isParent) {
commandSpec.exec = function(args, context) {
context.commandName = name;
@ -4955,32 +4910,38 @@ NodeListType.prototype.name = 'nodelist';
define('util/host', ['require', 'exports', 'module' ], function(require, exports, module) {
'use strict';
'use strict';
/**
* The chromeWindow as as required by Highlighter, so it knows where to
* create temporary highlight nodes.
*/
exports.chromeWindow = undefined;
/**
* The chromeWindow as as required by Highlighter, so it knows where to
* create temporary highlight nodes.
*/
exports.chromeWindow = undefined;
/**
* Helper to turn a set of nodes background another color for 0.5 seconds.
* There is likely a better way to do this, but this will do for now.
*/
exports.flashNodes = function(nodes, match) {
// Commented out until Bug 653545 is completed
/*
if (exports.chromeWindow == null) {
console.log('flashNodes has no chromeWindow. Skipping flash');
return;
}
/**
* See docs in lib/util/host.js:flashNodes
*/
exports.flashNodes = function(nodes, match) {
// Commented out until Bug 653545 is completed
/*
if (exports.chromeWindow == null) {
console.log('flashNodes has no chromeWindow. Skipping flash');
return;
}
var imports = {};
Components.utils.import("resource:///modules/highlighter.jsm", imports);
var imports = {};
Components.utils.import("resource:///modules/highlighter.jsm", imports);
imports.Highlighter.flashNodes(nodes, exports.chromeWindow, match);
*/
};
imports.Highlighter.flashNodes(nodes, exports.chromeWindow, match);
*/
};
/**
* See docs in lib/util/host.js:exec
*/
exports.exec = function(execSpec) {
throw new Error('Not supported');
};
});
@ -6261,6 +6222,10 @@ function Requisition(environment, doc, commandOutputManager) {
}
this.commandOutputManager = commandOutputManager || new CommandOutputManager();
this.shell = {
cwd: '/', // Where we store the current working directory
env: {} // Where we store the current environment
};
// The command that we are about to execute.
// @see setCommandConversion()
@ -6344,6 +6309,10 @@ Object.defineProperty(Requisition.prototype, 'executionContext', {
get: function() { return requisition.environment; },
enumerable: true
});
Object.defineProperty(this._executionContext, 'shell', {
get: function() { return requisition.shell; },
enumerable : true
});
/**
* This is a temporary property that will change and/or be removed.
@ -6760,7 +6729,7 @@ Requisition.prototype.complete = function(cursor, predictionChoice) {
outstanding.push(promise);
}
return util.all(outstanding).then(function() {
return Promise.all(outstanding).then(function() {
this.onTextChange();
this.onTextChange.resumeFire();
}.bind(this));
@ -7577,19 +7546,19 @@ Requisition.prototype._assign = function(args) {
if (!this.commandAssignment.value) {
this._addUnassignedArgs(args);
return util.all(outstanding);
return Promise.all(outstanding);
}
if (args.length === 0) {
this.setBlankArguments();
return util.all(outstanding);
return Promise.all(outstanding);
}
// Create an error if the command does not take parameters, but we have
// been given them ...
if (this.assignmentCount === 0) {
this._addUnassignedArgs(args);
return util.all(outstanding);
return Promise.all(outstanding);
}
// Special case: if there is only 1 parameter, and that's of type
@ -7599,7 +7568,7 @@ Requisition.prototype._assign = function(args) {
if (assignment.param.type.name === 'string') {
var arg = (args.length === 1) ? args[0] : new MergedArgument(args);
outstanding.push(this.setAssignment(assignment, arg, noArgUp));
return util.all(outstanding);
return Promise.all(outstanding);
}
}
@ -7706,7 +7675,7 @@ Requisition.prototype._assign = function(args) {
// What's left is can't be assigned, but we need to extract
this._addUnassignedArgs(args);
return util.all(outstanding);
return Promise.all(outstanding);
};
exports.Requisition = Requisition;
@ -9538,10 +9507,12 @@ var connect = {
createRemoter: function(prefix, connection) {
return function(cmdArgs, context) {
var typed = context.typed;
if (typed.indexOf(prefix) !== 0) {
throw new Error("Missing prefix");
// If we've been called using a 'context' then there will be no prefix
// otherwise we need to remove it
if (typed.indexOf(prefix) === 0) {
typed = typed.substring(prefix.length).replace(/^ */, "");
}
typed = typed.substring(prefix.length).replace(/^ */, "");
return connection.execute(typed, cmdArgs).then(function(reply) {
var typedData = context.typedData(reply.type, reply.data);
@ -9581,12 +9552,19 @@ var disconnect = {
name: 'prefix',
type: 'connection',
description: l10n.lookup('disconnectPrefixDesc'),
},
{
name: 'force',
type: 'boolean',
description: l10n.lookup('disconnectForceDesc'),
hidden: connector.disconnectSupportsForce,
option: true
}
],
returnType: 'string',
exec: function(args, context) {
return args.prefix.disconnect().then(function() {
return args.prefix.disconnect(args.force).then(function() {
var removed = canon.removeProxyCommands(args.prefix.prefix);
delete connections[args.prefix.prefix];
return l10n.lookupFormat('disconnectReply', [ removed.length ]);
@ -9630,13 +9608,15 @@ exports.shutdown = function() {
* limitations under the License.
*/
define('util/connect/connector', ['require', 'exports', 'module' ], function(require, exports, module) {
define('util/connect/connector', ['require', 'exports', 'module' , 'util/promise'], function(require, exports, module) {
'use strict';
var debuggerSocketConnect = Components.utils.import('resource://gre/modules/devtools/dbg-client.jsm', {}).debuggerSocketConnect;
var DebuggerClient = Components.utils.import('resource://gre/modules/devtools/dbg-client.jsm', {}).DebuggerClient;
var Promise = require('util/promise');
/**
* What port should we use by default?
*/
@ -9723,33 +9703,13 @@ Connection.prototype.getCommandSpecs = function() {
/**
* Send an execute request. Replies are handled by the setup in connect()
*/
Connection.prototype.execute = function(typed, cmdArgs) {
var deferred = Promise.defer();
var request = {
to: this.actor,
type: 'execute',
typed: typed,
args: cmdArgs
};
this.client.request(request, function(response) {
deferred.resolve(response.reply);
});
return deferred.promise;
};
/**
* Send an execute request.
*/
Connection.prototype.execute = function(typed, cmdArgs) {
var request = new Request(this.actor, typed, cmdArgs);
this.requests[request.json.id] = request;
this.requests[request.json.requestId] = request;
this.client.request(request.json, function(response) {
var request = this.requests[response.id];
delete this.requests[response.id];
var request = this.requests[response.requestId];
delete this.requests[response.requestId];
request.complete(response.error, response.type, response.data);
}.bind(this));
@ -9757,10 +9717,12 @@ Connection.prototype.execute = function(typed, cmdArgs) {
return request.promise;
};
exports.disconnectSupportsForce = false;
/**
* Kill this connection
*/
Connection.prototype.disconnect = function() {
Connection.prototype.disconnect = function(force) {
var deferred = Promise.defer();
this.client.close(function() {
@ -9780,7 +9742,7 @@ function Request(actor, typed, args) {
type: 'execute',
typed: typed,
args: args,
id: Request._nextRequestId++,
requestId: 'id-' + Request._nextRequestId++,
};
this._deferred = Promise.defer();
@ -9844,6 +9806,7 @@ var contextCmdSpec = {
}
],
returnType: 'string',
noRemote: true,
exec: function echo(args, context) {
// Do not copy this code
var requisition = context.__dlhjshfw;
@ -10197,6 +10160,17 @@ var terminalDomConverter = {
}
};
/**
* Convert a terminal object to a string
*/
var terminalStringConverter = {
from: 'terminal',
to: 'string',
exec: function(data, context) {
return Array.isArray(data) ? data.join('') : '' + data;
}
};
/**
* Several converters are just data.toString inside a 'p' element
*/
@ -10376,6 +10350,7 @@ exports.convert = function(data, from, to, conversionContext) {
exports.addConverter(viewDomConverter);
exports.addConverter(viewStringConverter);
exports.addConverter(terminalDomConverter);
exports.addConverter(terminalStringConverter);
exports.addConverter(stringDomConverter);
exports.addConverter(numberDomConverter);
exports.addConverter(booleanDomConverter);

View File

@ -55,15 +55,17 @@ GcliActor.prototype.execute = function(request) {
contentWindow.document);
let requisition = new Requisition(environment);
let outputPromise = requisition.updateExec(request.typed);
let output = util.synchronize(outputPromise);
return {
id: request.id,
data: output.data,
type: output.type,
error: output.error
};
requisition.updateExec(request.typed).then(output => {
return output.promise.then(() => {
this.connection.send({
from: this.actorID,
requestId: request.requestId,
data: output.data,
type: output.type,
error: output.error
});
});
}).then(null, console.error);
};
GcliActor.prototype.requestTypes = {

View File

@ -94,6 +94,10 @@ function WebConsoleActor(aConnection, aParentActor)
this._onObserverNotification = this._onObserverNotification.bind(this);
Services.obs.addObserver(this._onObserverNotification,
"inner-window-destroyed", false);
if (this._isGlobalActor) {
Services.obs.addObserver(this._onObserverNotification,
"last-pb-context-exited", false);
}
}
WebConsoleActor.prototype =
@ -243,6 +247,10 @@ WebConsoleActor.prototype =
this.conn.removeActorPool(this._actorPool);
Services.obs.removeObserver(this._onObserverNotification,
"inner-window-destroyed");
if (this._isGlobalActor) {
Services.obs.removeObserver(this._onObserverNotification,
"last-pb-context-exited");
}
this._actorPool = null;
this._protoChains.clear();
this._dbgGlobals.clear();
@ -499,22 +507,24 @@ WebConsoleActor.prototype =
switch (type) {
case "ConsoleAPI":
if (this.consoleAPIListener) {
let cache = this.consoleAPIListener.getCachedMessages();
cache.forEach(function(aMessage) {
let cache = this.consoleAPIListener
.getCachedMessages(!this._isGlobalActor);
cache.forEach((aMessage) => {
let message = this.prepareConsoleMessageForRemote(aMessage);
message._type = type;
messages.push(message);
}, this);
});
}
break;
case "PageError":
if (this.pageErrorListener) {
let cache = this.pageErrorListener.getCachedMessages();
cache.forEach(function(aMessage) {
let cache = this.pageErrorListener
.getCachedMessages(!this._isGlobalActor);
cache.forEach((aMessage) => {
let message = this.preparePageErrorForRemote(aMessage);
message._type = type;
messages.push(message);
}, this);
});
}
break;
}
@ -904,6 +914,7 @@ WebConsoleActor.prototype =
error: !!(aPageError.flags & aPageError.errorFlag),
exception: !!(aPageError.flags & aPageError.exceptionFlag),
strict: !!(aPageError.flags & aPageError.strictFlag),
private: aPageError.isFromPrivateWindow,
};
},
@ -990,6 +1001,8 @@ WebConsoleActor.prototype =
{
let result = WebConsoleUtils.cloneObject(aMessage);
delete result.wrappedJSObject;
delete result.ID;
delete result.innerID;
result.arguments = Array.map(aMessage.arguments || [], (aObj) => {
let dbgObj = this.makeDebuggeeValue(aObj, true);
@ -1029,12 +1042,25 @@ WebConsoleActor.prototype =
* @param object aSubject
* Notification subject - in this case it is the inner window ID that
* was destroyed.
* @param string aTopic
* Notification topic.
*/
_onObserverNotification: function WCA__onObserverNotification(aSubject)
_onObserverNotification: function WCA__onObserverNotification(aSubject, aTopic)
{
let windowId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
if (this._dbgGlobals.has(windowId)) {
this._dbgGlobals.delete(windowId);
switch (aTopic) {
case "inner-window-destroyed": {
let windowId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
if (this._dbgGlobals.has(windowId)) {
this._dbgGlobals.delete(windowId);
}
break;
}
case "last-pb-context-exited":
this.conn.send({
from: this.actorID,
type: "lastPrivateContextExited",
});
break;
}
},
};
@ -1088,6 +1114,7 @@ function NetworkEventActor(aNetworkEvent, aWebConsoleActor)
this._discardRequestBody = aNetworkEvent.discardRequestBody;
this._discardResponseBody = aNetworkEvent.discardResponseBody;
this._private = aNetworkEvent.private;
}
NetworkEventActor.prototype =
@ -1109,7 +1136,8 @@ NetworkEventActor.prototype =
startedDateTime: this._startedDateTime,
url: this._request.url,
method: this._request.method,
isXHR: this._isXHR
isXHR: this._isXHR,
private: this._private,
};
},

View File

@ -209,7 +209,10 @@ this.NetworkHelper =
{
try {
return this.getRequestLoadContext(aRequest).associatedWindow;
} catch (ex) { }
} catch (ex) {
// TODO: bug 802246 - getWindowForRequest() throws on b2g: there is no
// associatedWindow property.
}
return null;
},

View File

@ -24,6 +24,9 @@ XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
XPCOMUtils.defineLazyModuleGetter(this, "NetworkHelper",
"resource://gre/modules/devtools/NetworkHelper.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "gActivityDistributor",
"@mozilla.org/network/http-activity-distributor;1",
"nsIHttpActivityDistributor");
@ -976,23 +979,32 @@ PageErrorListener.prototype =
/**
* Get the cached page errors for the current inner window.
*
* @param boolean [aIncludePrivate=false]
* Tells if you want to also retrieve messages coming from private
* windows. Defaults to false.
* @return array
* The array of cached messages. Each element is an nsIScriptError
* with an added _type property so the remote Web Console instance can
* tell the difference between various types of cached messages.
* The array of cached messages.
*/
getCachedMessages: function PEL_getCachedMessages()
getCachedMessages: function PEL_getCachedMessages(aIncludePrivate = false)
{
let innerWindowId = this.window ?
WebConsoleUtils.getInnerWindowId(this.window) : null;
let errors = Services.console.getMessageArray() || [];
return errors.filter(function(aError) {
return aError instanceof Ci.nsIScriptError &&
(!innerWindowId ||
(aError.innerWindowID == innerWindowId &&
this.isCategoryAllowed(aError.category)));
}, this);
return errors.filter((aError) => {
if (!(aError instanceof Ci.nsIScriptError)) {
return false;
}
if (!aIncludePrivate && aError.isFromPrivateWindow) {
return false;
}
if (innerWindowId &&
(aError.innerWindowID != innerWindowId ||
!this.isCategoryAllowed(aError.category))) {
return false;
}
return true;
});
},
/**
@ -1090,15 +1102,22 @@ ConsoleAPIListener.prototype =
/**
* Get the cached messages for the current inner window.
*
* @param boolean [aIncludePrivate=false]
* Tells if you want to also retrieve messages coming from private
* windows. Defaults to false.
* @return array
* The array of cached messages. Each element is a Console API
* prepared to be sent to the remote Web Console instance.
* The array of cached messages.
*/
getCachedMessages: function CAL_getCachedMessages()
getCachedMessages: function CAL_getCachedMessages(aIncludePrivate = false)
{
let innerWindowId = this.window ?
WebConsoleUtils.getInnerWindowId(this.window) : null;
return ConsoleAPIStorage.getEvents(innerWindowId);
return ConsoleAPIStorage.getEvents(innerWindowId).filter((aMessage) => {
if (!aIncludePrivate && aMessage.private) {
return false;
}
return true;
});
},
/**
@ -1905,13 +1924,7 @@ NetworkMonitor.prototype = {
_onRequestHeader:
function NM__onRequestHeader(aChannel, aTimestamp, aExtraStringData)
{
let win = null;
try {
win = NetworkHelper.getWindowForRequest(aChannel);
}
catch (ex) {
// getWindowForRequest() throws on b2g.
}
let win = NetworkHelper.getWindowForRequest(aChannel);
// Try to get the source window of the request.
if (this.window && (!win || win.top !== this.window)) {
@ -1922,6 +1935,7 @@ NetworkMonitor.prototype = {
// see NM__onRequestBodySent()
httpActivity.charset = win ? win.document.characterSet : null;
httpActivity.private = win ? PrivateBrowsingUtils.isWindowPrivate(win) : false;
httpActivity.timings.REQUEST_HEADER = {
first: aTimestamp,
@ -1935,14 +1949,15 @@ NetworkMonitor.prototype = {
event.headersSize = aExtraStringData.length;
event.method = aChannel.requestMethod;
event.url = aChannel.URI.spec;
event.private = httpActivity.private;
// Determine if this is an XHR request.
try {
let callbacks = aChannel.notificationCallbacks;
let xhrRequest = callbacks ? callbacks.getInterface(Ci.nsIXMLHttpRequest) : null;
event.isXHR = !!xhrRequest;
httpActivity.isXHR = event.isXHR = !!xhrRequest;
} catch (e) {
event.isXHR = false;
httpActivity.isXHR = event.isXHR = false;
}
// Determine the HTTP version.