Bug 587573 - Log image requests to the WebConsole r=sdwilsh a=blocking2.0

This commit is contained in:
Julian Viereck 2010-08-26 09:49:28 -07:00
parent 70f9bbef88
commit cbcaa3167f
5 changed files with 214 additions and 332 deletions

View File

@ -107,6 +107,110 @@ const ERRORS = { LOG_MESSAGE_MISSING_ARGS:
LOG_OUTPUT_FAILED: "Log Failure: Could not append messageNode to outputNode",
};
/**
* Helper object for networking stuff.
*
* All of the following functions have been taken from the Firebug source. They
* have been modified to match the Firefox coding rules.
*/
// FIREBUG CODE BEGIN.
/*
* Software License Agreement (BSD License)
*
* Copyright (c) 2007, Parakey Inc.
* All rights reserved.
*
* Redistribution and use of this software in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above
* copyright notice, this list of conditions and the
* following disclaimer.
*
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other
* materials provided with the distribution.
*
* * Neither the name of Parakey Inc. nor the names of its
* contributors may be used to endorse or promote products
* derived from this software without specific prior
* written permission of Parakey Inc.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
* IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Creator:
* Joe Hewitt
* Contributors
* John J. Barton (IBM Almaden)
* Jan Odvarko (Mozilla Corp.)
* Max Stepanov (Aptana Inc.)
* Rob Campbell (Mozilla Corp.)
* Hans Hillen (Paciello Group, Mozilla)
* Curtis Bartley (Mozilla Corp.)
* Mike Collins (IBM Almaden)
* Kevin Decker
* Mike Ratcliffe (Comartis AG)
* Hernan Rodríguez Colmeiro
* Austin Andrews
* Christoph Dorn
* Steven Roussey (AppCenter Inc, Network54)
*/
var NetworkHelper =
{
/**
* Gets the nsIDOMWindow that is associated with aRequest.
*
* @param nsIHttpChannel aRequest
* @returns nsIDOMWindow or null
*/
getWindowForRequest: function NH_getWindowForRequest(aRequest)
{
let loadContext = this.getRequestLoadContext(aRequest);
if (loadContext) {
return loadContext.associatedWindow;
}
return null;
},
/**
* Gets the nsILoadContext that is associated with aRequest.
*
* @param nsIHttpChannel aRequest
* @returns nsILoadContext or null
*/
getRequestLoadContext: function NH_getRequestLoadContext(aRequest)
{
if (aRequest && aRequest.notificationCallbacks) {
try {
return aRequest.notificationCallbacks.getInterface(Ci.nsILoadContext);
} catch (ex) { }
}
if (aRequest && aRequest.loadGroup
&& aRequest.loadGroup.notificationCallbacks) {
try {
return aRequest.loadGroup.notificationCallbacks.getInterface(Ci.nsILoadContext);
} catch (ex) { }
}
return null;
}
}
// FIREBUG CODE END.
function HUD_SERVICE()
{
// TODO: provide mixins for FENNEC: bug 568621
@ -179,16 +283,16 @@ HUD_SERVICE.prototype =
*/
displayRegistry: {},
/**
* Mapping of HUDIds to contentWindows.
*/
windowRegistry: {},
/**
* Mapping of URISpecs to HUDIds
*/
uriRegistry: {},
/**
* The nsILoadGroups being tracked
*/
loadGroups: {},
/**
* The sequencer is a generator (after initialization) that returns unique
* integers
@ -604,24 +708,25 @@ HUD_SERVICE.prototype =
* Register a new Heads Up Display
*
* @param string aHUDId
* @param string aURISpec
* @param nsIDOMWindow aContentWindow
* @returns void
*/
registerDisplay: function HS_registerDisplay(aHUDId, aURISpec)
registerDisplay: function HS_registerDisplay(aHUDId, aContentWindow)
{
// register a display DOM node Id and HUD uriSpec with the service
if (!aHUDId || !aURISpec){
if (!aHUDId || !aContentWindow){
throw new Error(ERRORS.MISSING_ARGS);
}
var URISpec = aContentWindow.document.location.href
this.filterPrefs[aHUDId] = this.defaultFilterPrefs;
this.displayRegistry[aHUDId] = aURISpec;
this.displayRegistry[aHUDId] = URISpec;
this._headsUpDisplays[aHUDId] = { id: aHUDId, };
this.registerActiveContext(aHUDId);
// init storage objects:
this.storage.createDisplay(aHUDId);
var huds = this.uriRegistry[aURISpec];
var huds = this.uriRegistry[URISpec];
var foundHUDId = false;
if (huds) {
@ -633,11 +738,19 @@ HUD_SERVICE.prototype =
}
}
if (!foundHUDId) {
this.uriRegistry[aURISpec].push(aHUDId);
this.uriRegistry[URISpec].push(aHUDId);
}
}
else {
this.uriRegistry[aURISpec] = [aHUDId];
this.uriRegistry[URISpec] = [aHUDId];
}
var windows = this.windowRegistry[aHUDId];
if (!windows) {
this.windowRegistry[aHUDId] = [aContentWindow];
}
else {
windows.push(aContentWindow);
}
},
@ -669,6 +782,9 @@ HUD_SERVICE.prototype =
this.deleteHeadsUpDisplay(aId);
// remove the related storage object
this.storage.removeDisplay(aId);
// remove the related window objects
delete this.windowRegistry[aId];
let displays = this.displays();
var uri = this.displayRegistry[aId];
@ -729,6 +845,24 @@ HUD_SERVICE.prototype =
}
},
/**
* Returns the hudId that is corresponding to the hud activated for the
* passed aContentWindow. If there is no matching hudId null is returned.
*
* @param nsIDOMWindow aContentWindow
* @returns string or null
*/
getHudIdByWindow: function HS_getHudIdByWindow(aContentWindow)
{
for (let hudId in this.windowRegistry) {
if (this.windowRegistry[hudId] &&
this.windowRegistry[hudId].indexOf(aContentWindow) != -1) {
return hudId;
}
}
return null;
},
/**
* Gets HUD DOM Node
* @param string id
@ -958,51 +1092,6 @@ HUD_SERVICE.prototype =
*/
applicationHooks: null,
/**
* Given an nsIChannel, return the corresponding nsILoadContext
*
* @param nsIChannel aChannel
* @returns nsILoadContext
*/
getLoadContext: function HS_getLoadContext(aChannel)
{
if (!aChannel) {
return null;
}
var loadContext;
var callbacks = aChannel.notificationCallbacks;
loadContext =
aChannel.notificationCallbacks.getInterface(Ci.nsILoadContext);
if (!loadContext) {
loadContext =
aChannel.QueryInterface(Ci.nsIRequest).loadGroup.notificationCallbacks.getInterface(Ci.nsILoadContext);
}
return loadContext;
},
/**
* Given an nsILoadContext, return the corresponding nsIDOMWindow
*
* @param nsILoadContext aLoadContext
* @returns nsIDOMWindow
*/
getWindowFromContext: function HS_getWindowFromContext(aLoadContext)
{
if (!aLoadContext) {
throw new Error("loadContext is null");
}
if (aLoadContext.isContent) {
if (aLoadContext.associatedWindow) {
return aLoadContext.associatedWindow;
}
else if (aLoadContext.topWindow) {
return aLoadContext.topWindow;
}
}
throw new Error("Cannot get window from " + aLoadContext);
},
getChromeWindowFromContentWindow:
function HS_getChromeWindowFromContentWindow(aContentWindow)
{
@ -1020,63 +1109,6 @@ HUD_SERVICE.prototype =
return win;
},
/**
* get the outputNode from the window object
*
* @param nsIDOMWindow aWindow
* @returns nsIDOMNode
*/
getOutputNodeFromWindow:
function HS_getOutputNodeFromWindow(aWindow)
{
var browser = gBrowser.getBrowserForDocument(aWindow.top.document);
var tabId = gBrowser.getNotificationBox(browser).getAttribute("id");
var hudId = "hud_" + tabId;
var displayNode = this.getHeadsUpDisplay(hudId);
return displayNode.querySelectorAll(".hud-output-node")[0];
},
/**
* Try to get the outputNode via the nsIRequest
* TODO: get node via request, see bug 552140
* @param nsIRequest aRequest
* @returns nsIDOMNode
*/
getOutputNodeFromRequest: function HS_getOutputNodeFromRequest(aRequest)
{
var context = this.getLoadContext(aRequest);
var window = this.getWindowFromContext(context);
return this.getOutputNodeFromWindow(window);
},
getLoadContextFromChannel: function HS_getLoadContextFromChannel(aChannel)
{
try {
return aChannel.QueryInterface(Ci.nsIChannel).notificationCallbacks.getInterface(Ci.nsILoadContext);
}
catch (ex) {
// noop, keep this output quiet. see bug 552140
}
try {
return aChannel.QueryInterface(Ci.nsIChannel).loadGroup.notificationCallbacks.getInterface(Ci.nsILoadContext);
}
catch (ex) {
// noop, keep this output quiet. see bug 552140
}
return null;
},
getWindowFromLoadContext:
function HS_getWindowFromLoadContext(aLoadContext)
{
if (aLoadContext.topWindow) {
return aLoadContext.topWindow;
}
else {
return aLoadContext.associatedWindow;
}
},
/**
* Begin observing HTTP traffic that we care about,
* namely traffic that originates inside any context that a Heads Up Display
@ -1094,55 +1126,43 @@ HUD_SERVICE.prototype =
var loadGroup;
if (aActivityType ==
activityDistributor.ACTIVITY_TYPE_HTTP_TRANSACTION) {
try {
var loadContext = self.getLoadContextFromChannel(aChannel);
// TODO: get image request data from the channel
// see bug 552140
var window = self.getWindowFromLoadContext(loadContext);
window = XPCNativeWrapper.unwrap(window);
var chromeWin = self.getChromeWindowFromContentWindow(window);
var vboxes =
chromeWin.document.getElementsByTagName("vbox");
var hudId;
for (var i = 0; i < vboxes.length; i++) {
if (vboxes[i].getAttribute("class") == "hud-box") {
hudId = vboxes[i].getAttribute("id");
}
}
loadGroup = self.getLoadGroup(hudId);
}
catch (ex) {
loadGroup = aChannel.QueryInterface(Ci.nsIChannel)
.QueryInterface(Ci.nsIRequest).loadGroup;
}
if (!loadGroup) {
return;
}
aChannel = aChannel.QueryInterface(Ci.nsIHttpChannel);
var transCodes = this.httpTransactionCodes;
var httpActivity = {
channel: aChannel,
loadGroup: loadGroup,
type: aActivityType,
subType: aActivitySubtype,
timestamp: aTimestamp,
extraSizeData: aExtraSizeData,
extraStringData: aExtraStringData,
stage: transCodes[aActivitySubtype],
};
if (aActivitySubtype ==
activityDistributor.ACTIVITY_SUBTYPE_REQUEST_HEADER ) {
// create a unique ID to track this transaction and be able to
// update the logged node with subsequent http transactions
httpActivity.httpId = self.sequenceId();
let loggedNode =
self.logActivity("network", aChannel.URI, httpActivity);
self.httpTransactions[aChannel] =
new Number(httpActivity.httpId);
// Try to get the source window of the request.
let win = NetworkHelper.getWindowForRequest(aChannel);
if (!win) {
return;
}
// Try to get the hudId that is associated to the window.
let hudId = self.getHudIdByWindow(win);
if (!hudId) {
return;
}
var httpActivity = {
channel: aChannel,
type: aActivityType,
subType: aActivitySubtype,
timestamp: aTimestamp,
extraSizeData: aExtraSizeData,
extraStringData: aExtraStringData,
stage: transCodes[aActivitySubtype],
hudId: hudId
};
// create a unique ID to track this transaction and be able to
// update the logged node with subsequent http transactions
httpActivity.httpId = self.sequenceId();
let loggedNode =
self.logActivity("network", aChannel.URI, httpActivity);
self.httpTransactions[aChannel] =
new Number(httpActivity.httpId);
}
}
},
@ -1173,22 +1193,11 @@ HUD_SERVICE.prototype =
*/
logNetActivity: function HS_logNetActivity(aType, aURI, aActivityObject)
{
var displayNode, outputNode, hudId;
var outputNode, hudId;
try {
displayNode =
this.getDisplayByLoadGroup(aActivityObject.loadGroup,
{URI: aURI}, aActivityObject);
if (!displayNode) {
return;
}
outputNode = displayNode.querySelectorAll(".hud-output-node")[0];
hudId = displayNode.getAttribute("id");
if (!outputNode) {
outputNode = this.getOutputNodeFromRequest(aActivityObject.request);
hudId = outputNode.ownerDocument.querySelectorAll(".hud-box")[0].
getAttribute("id");
}
hudId = aActivityObject.hudId;
outputNode = this.getHeadsUpDisplay(hudId).
querySelector(".hud-output-node");
// get an id to attach to the dom node for lookup of node
// when updating the log entry with additional http transactions
@ -1350,88 +1359,6 @@ HUD_SERVICE.prototype =
return groupNode;
},
/**
* update loadgroup when the window object is re-created
*
* @param string aId
* @param nsILoadGroup aLoadGroup
* @returns void
*/
updateLoadGroup: function HS_updateLoadGroup(aId, aLoadGroup)
{
if (this.loadGroups[aId] == undefined) {
this.loadGroups[aId] = { id: aId,
loadGroup: Cu.getWeakReference(aLoadGroup) };
}
else {
this.loadGroups[aId].loadGroup = Cu.getWeakReference(aLoadGroup);
}
},
/**
* gets the load group that corresponds to a HUDId
*
* @param string aId
* @returns nsILoadGroup
*/
getLoadGroup: function HS_getLoadGroup(aId)
{
try {
return this.loadGroups[aId].loadGroup.get();
}
catch (ex) {
return null;
}
},
/**
* gets outputNode for a specific heads up display by loadGroup
*
* @param nsILoadGroup aLoadGroup
* @returns nsIDOMNode
*/
getDisplayByLoadGroup:
function HS_getDisplayByLoadGroup(aLoadGroup, aChannel, aActivityObject)
{
if (!aLoadGroup) {
return null;
}
var trackedLoadGroups = this.getAllLoadGroups();
var len = trackedLoadGroups.length;
for (var i = 0; i < len; i++) {
try {
var unwrappedLoadGroup =
XPCNativeWrapper.unwrap(trackedLoadGroups[i].loadGroup);
if (aLoadGroup == unwrappedLoadGroup) {
return this.getOutputNodeById(trackedLoadGroups[i].hudId);
}
}
catch (ex) {
// noop
}
}
// TODO: also need to check parent loadGroup(s) incase of iframe activity?;
// see bug 568643
return null;
},
/**
* gets all nsILoadGroups that are being tracked by this service
* the loadgroups are matched to HUDIds in an object and an array is returned
* @returns array
*/
getAllLoadGroups: function HS_getAllLoadGroups()
{
var loadGroups = [];
for (var hudId in this.loadGroups) {
let loadGroupObj = { loadGroup: this.loadGroups[hudId].loadGroup.get(),
hudId: this.loadGroups[hudId].id,
};
loadGroups.push(loadGroupObj);
}
return loadGroups;
},
/**
* gets the DOM Node that maps back to what context/tab that
* activity originated via the URI
@ -1602,7 +1529,7 @@ HUD_SERVICE.prototype =
return;
}
this.registerDisplay(hudId, aContentWindow.document.location.href);
this.registerDisplay(hudId, aContentWindow);
// check if aContentWindow has a console Object
let _console = aContentWindow.wrappedJSObject.console;
@ -1865,20 +1792,6 @@ HeadsUpDisplay.prototype = {
}
},
/**
* Gets the loadGroup for the contentWindow
*
* @returns nsILoadGroup
*/
get loadGroup()
{
var loadGroup = this.contentWindow
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocumentLoader).loadGroup;
return loadGroup;
},
/**
* Shortcut to make XUL nodes
*
@ -2202,8 +2115,6 @@ function HUDConsole(aHeadsUpDisplay)
aHeadsUpDisplay._console = this;
HUDService.updateLoadGroup(hudId, hud.loadGroup);
let sendToHUDService = function console_send(aLevel, aArguments)
{
let ts = ConsoleUtils.timestamp();
@ -3337,7 +3248,7 @@ ConsoleUtils = {
/**
* Generates a formatted timestamp string for displaying in console messages.
*
* @param integer [ms] Optional, allows you to specify the timestamp in
* @param integer [ms] Optional, allows you to specify the timestamp in
* milliseconds since the UNIX epoch.
* @returns string The timestamp formatted for display.
*/

View File

@ -58,6 +58,7 @@ _BROWSER_TEST_PAGES = \
test-property-provider.html \
test-error.html \
test-duplicate-error.html \
test-image.png \
$(NULL)
libs:: $(_BROWSER_TEST_FILES)

View File

@ -64,8 +64,6 @@ const TEST_HTTP_URI = "http://example.com/browser/toolkit/components/console/hud
const TEST_NETWORK_URI = "http://example.com/browser/toolkit/components/console/hudservice/tests/browser/test-network.html";
const TEST_FILTER_URI = "http://example.com/browser/toolkit/components/console/hudservice/tests/browser/test-filter.html";
const TEST_PROPERTY_PROVIDER_URI = "http://example.com/browser/toolkit/components/console/hudservice/tests/browser/test-property-provider.html";
const TEST_ERROR_URI = "http://example.com/browser/toolkit/components/console/hudservice/tests/browser/test-error.html";
@ -159,48 +157,6 @@ function getAllHUDS() {
ok(idx.length > 0, "idx.length > 0: " + len);
}
function testGetDisplayByLoadGroup() {
var outputNode = HUDService.getDisplayByURISpec(TEST_URI);
var hudId = outputNode.getAttribute("id");
var loadGroup = HUDService.getLoadGroup(hudId);
var display = HUDService.getDisplayByLoadGroup(loadGroup);
ok(display.getAttribute("id") == hudId, "got display by loadGroup");
content.location = TEST_HTTP_URI;
executeSoon(function () {
let id = HUDService.displaysIndex()[0];
let domLogEntries =
outputNode.childNodes;
let count = outputNode.childNodes.length;
ok(count > 0, "LogCount: " + count);
let klasses = ["hud-network"];
function verifyClass(klass) {
let len = klasses.length;
for (var i = 0; i < len; i++) {
if (klass == klasses[i]) {
return true;
}
}
return false;
}
for (var i = 0; i < count; i++) {
let klass = domLogEntries[i].getAttribute("class");
if (klass != "hud-network") {
continue;
}
ok(verifyClass(klass),
"Log Node network class verified");
}
});
}
function testUnregister() {
HUDService.deactivateHUDForContext(tab);
ok(HUDService.displays()[0] == undefined,
@ -285,21 +241,29 @@ function testNet()
HUDService.setFilterState(hudId, "network", true);
setStringFilter("");
browser.addEventListener("DOMContentLoaded", function onTestNetLoad () {
browser.removeEventListener("DOMContentLoaded", onTestNetLoad, false);
let HUD = HUDService.hudWeakReferences[hudId].get();
let jsterm = HUD.jsterm;
let outputNode = jsterm.outputNode;
jsterm.clearOutput();
var successMsg =
"Found the loggged network message referencing a js file";
var errMsg = "Could not get logged network message for js file";
browser.addEventListener("load", function onTestNetLoad () {
browser.removeEventListener("load", onTestNetLoad, true);
var display = HUDService.getDisplayByURISpec(TEST_NETWORK_URI);
var outputNode = display.querySelectorAll(".hud-output-node")[0];
let group = outputNode.querySelector(".hud-group");
is(group.childNodes.length, 5, "Four children in output");
let outputChildren = group.childNodes;
testLogEntry(outputNode, "Network:",
{ success: successMsg, err: errMsg });
isnot(outputChildren[1].textContent.indexOf("test-network.html"), -1,
"html page is logged");
isnot(outputChildren[2].textContent.indexOf("testscript.js"), -1,
"javascript is logged");
isnot(outputChildren[3].textContent.indexOf("test-image.png"), -1,
"image is logged");
isnot(outputChildren[4].textContent.
indexOf("running network console logging tests"), -1, "log() is logged");
testPageReload();
}, false);
testLiveFilteringForMessageTypes();
}, true);
content.location = TEST_NETWORK_URI;
}
@ -341,6 +305,8 @@ function testLiveFilteringForMessageTypes() {
HUDService.setFilterState(hudId, "network", true);
isnot(countNetworkNodes(), 0, "the network nodes reappear when the " +
"corresponding filter is switched on");
testLiveFilteringForSearchStrings();
});
}
@ -387,6 +353,8 @@ function testLiveFilteringForSearchStrings()
setStringFilter("foo\"bar'baz\"boo'");
is(countNetworkNodes(), 0, "the network nodes are hidden when searching " +
"for the string \"foo\"bar'baz\"boo'\"");
testPageReload();
});
}
@ -804,7 +772,7 @@ function testHUDGetters()
}
function testPageReload() {
// see bug 578437 - The HUD console fails to re-attach the window.console
// see bug 578437 - The HUD console fails to re-attach the window.console
// object after page reload.
browser.addEventListener("DOMContentLoaded", function onDOMLoad () {
@ -990,11 +958,8 @@ function test() {
getAllHUDS();
getHUDById();
testInputFocus();
testGetDisplayByLoadGroup();
testGetContentWindowFromHUDId();
content.location.href = TEST_FILTER_URI;
testConsoleLoggingAPI("log");
testConsoleLoggingAPI("info");
testConsoleLoggingAPI("warn");
@ -1016,9 +981,13 @@ function test() {
testPropertyProvider();
testJSInputExpand();
testPropertyPanel();
// NOTE: Put any sync test above this comment.
//
// There is a set of async tests that have to run one after another.
// Currently, when one test is done the next one is called from within the
// test function until testEnd() is called that terminates the test.
testNet();
testLiveFilteringForMessageTypes();
testLiveFilteringForSearchStrings();
});
}, false);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 580 B

View File

@ -5,5 +5,6 @@
</head>
<body>
<h1>Heads Up Display Network Test Page</h1>
<img src="test-image.png"></img>
</body>
</html>