Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Started work on bug 762996 - show errors count in dev toolbar

Basics are working, need to fix some use cases and interaction with the Web
Console. Then tests. Also need to show the error count in the UI.
  • Loading branch information...
commit 3847e63b8f3b7e58dd02529577b40ea806a4c761 1 parent 1c5897b
@mihaisucan authored
View
2  .gitignore
@@ -1,5 +1,7 @@
*
!/patches*
+!/patches*/*
+/patches*/*~
!/.gitignore
/patches*/status
/patches.queue
View
470 patches-webconsole/bug-762996
@@ -0,0 +1,470 @@
+# HG changeset patch
+# Parent ad2b6e81c01f0a76174579b7620607a2bc42ef28
+# User Mihai Sucan <mihai.sucan@gmail.com>
+# Date 1339184478 -10800
+
+Bug 762996 - Add errors count to the Web Console button in the developer toolbar
+
+diff --git a/browser/devtools/shared/DeveloperToolbar.jsm b/browser/devtools/shared/DeveloperToolbar.jsm
+--- a/browser/devtools/shared/DeveloperToolbar.jsm
++++ b/browser/devtools/shared/DeveloperToolbar.jsm
+@@ -3,16 +3,19 @@
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+ "use strict";
+
+ const EXPORTED_SYMBOLS = [ "DeveloperToolbar" ];
+
+ const NS_XHTML = "http://www.w3.org/1999/xhtml";
+
++const WEBCONSOLE_CONTENT_SCRIPT_URL =
++ "chrome://browser/content/devtools/HUDService-content.js";
++
+ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+ Components.utils.import("resource://gre/modules/Services.jsm");
+
+ XPCOMUtils.defineLazyGetter(this, "gcli", function() {
+ let obj = {};
+ Components.utils.import("resource:///modules/devtools/gcli.jsm", obj);
+ Components.utils.import("resource:///modules/devtools/GcliCommands.jsm", {});
+ return obj.gcli;
+@@ -31,16 +34,17 @@ function DeveloperToolbar(aChromeWindow,
+
+ this._element = aToolbarElement;
+ this._element.hidden = true;
+ this._doc = this._element.ownerDocument;
+
+ this._lastState = NOTIFICATIONS.HIDE;
+ this._pendingShowCallback = undefined;
+ this._pendingHide = false;
++ this._errorsCount = {};
+ }
+
+ /**
+ * Inspector notifications dispatched through the nsIObserverService
+ */
+ const NOTIFICATIONS = {
+ /** DeveloperToolbar.show() has been called, and we're working on it */
+ LOAD: "developer-toolbar-load",
+@@ -53,16 +57,20 @@ const NOTIFICATIONS = {
+ };
+
+ /**
+ * Attach notification constants to the object prototype so tests etc can
+ * use them without needing to import anything
+ */
+ DeveloperToolbar.prototype.NOTIFICATIONS = NOTIFICATIONS;
+
++DeveloperToolbar.prototype._contentMessageListeners =
++ ["WebConsole:CachedMessages", "WebConsole:PageError",
++ "WebConsole:LocationChange"];
++
+ /**
+ * Is the toolbar open?
+ */
+ Object.defineProperty(DeveloperToolbar.prototype, 'visible', {
+ get: function DT_visible() {
+ return !this._element.hidden;
+ },
+ enumerable: true
+@@ -147,19 +155,22 @@ DeveloperToolbar.prototype._onload = fun
+ scratchpad: null
+ });
+
+ this.display.onVisibilityChange.add(this.outputPanel._visibilityChanged, this.outputPanel);
+ this.display.onVisibilityChange.add(this.tooltipPanel._visibilityChanged, this.tooltipPanel);
+ this.display.onOutput.add(this.outputPanel._outputChanged, this.outputPanel);
+
+ this._chromeWindow.getBrowser().tabContainer.addEventListener("TabSelect", this, false);
++ this._chromeWindow.getBrowser().tabContainer.addEventListener("TabClose", this, false);
+ this._chromeWindow.getBrowser().addEventListener("load", this, true);
+ this._chromeWindow.addEventListener("resize", this, false);
+
++ this._initErrorsCount(this._chromeWindow.getBrowser().selectedTab);
++
+ this._element.hidden = false;
+ this._input.focus();
+
+ this._notify(NOTIFICATIONS.SHOW);
+ if (this._pendingShowCallback) {
+ this._pendingShowCallback.call();
+ this._pendingShowCallback = undefined;
+ }
+@@ -173,16 +184,64 @@ DeveloperToolbar.prototype._onload = fun
+ }
+
+ if (!DeveloperToolbar.introShownThisSession) {
+ this.display.maybeShowIntro();
+ DeveloperToolbar.introShownThisSession = true;
+ }
+ };
+
++DeveloperToolbar.prototype._initErrorsCount = function DT__initErrorsCount(aTab)
++{
++ let tabId = aTab.linkedPanel;
++ if (tabId in this._errorsCount) {
++ dump("DT__initErrorsCount tabId already tracked! " + tabId + "\n");
++ return;
++ }
++ dump("DT__initErrorsCount " + tabId + "\n");
++
++ let messageManager = aTab.linkedBrowser.messageManager;
++ messageManager.loadFrameScript(WEBCONSOLE_CONTENT_SCRIPT_URL, true);
++
++ this._errorsCount[tabId] = 0;
++
++ this._contentMessageListeners.forEach(function(aName) {
++ messageManager.addMessageListener(aName, this);
++ }, this);
++
++ let message = {
++ features: ["PageError", "LocationChange"],
++ cachedMessages: ["PageError"],
++ };
++
++ this.sendMessageToTab(aTab, "WebConsole:Init", message);
++ dump("DT__initErrorsCount end " + tabId + "\n");
++};
++
++DeveloperToolbar.prototype._stopErrorsCount = function DT__stopErrorsCount(aTab)
++{
++ let tabId = aTab.linkedPanel;
++ if (!(tabId in this._errorsCount)) {
++ dump("DT__stopErrorsCount tabId untracked! " + tabId + "\n");
++ return;
++ }
++ dump("DT__stopErrorsCount " + tabId + "\n");
++
++ let messageManager = aTab.linkedBrowser.messageManager;
++
++ this.sendMessageToTab(aTab, "WebConsole:Destroy");
++
++ this._contentMessageListeners.forEach(function(aName) {
++ messageManager.removeMessageListener(aName, this);
++ }, this);
++
++ delete this._errorsCount[tabId];
++ dump("DT__stopErrorsCount end " + tabId + "\n");
++};
++
+ /**
+ * Hide the developer toolbar.
+ */
+ DeveloperToolbar.prototype.hide = function DT_hide()
+ {
+ if (this._lastState == NOTIFICATIONS.HIDE) {
+ return;
+ }
+@@ -241,36 +300,105 @@ DeveloperToolbar.prototype._notify = fun
+ };
+
+ /**
+ * Update various parts of the UI when the current tab changes
+ * @param aEvent
+ */
+ DeveloperToolbar.prototype.handleEvent = function DT_handleEvent(aEvent)
+ {
++ dump("DT_handleEvent " + aEvent.type + "\n");
+ if (aEvent.type == "TabSelect" || aEvent.type == "load") {
+ this._chromeWindow.HUDConsoleUI.refreshCommand();
+ this._chromeWindow.DebuggerUI.refreshCommand();
+
+ if (this.visible) {
+ let contentDocument = this._chromeWindow.getBrowser().contentDocument;
+
+ this.display.reattach({
+ contentDocument: contentDocument,
+ chromeWindow: this._chromeWindow,
+ environment: {
+ chromeDocument: this._doc,
+ contentDocument: contentDocument
+ },
+ });
++
++ }
++
++ if (aEvent.type == "TabSelect" && this.visible) {
++ this._initErrorsCount(aEvent.target);
+ }
+ }
+ else if (aEvent.type == "resize") {
+ this.outputPanel._resize();
+ }
++ else if (aEvent.type == "TabClose") {
++ this._stopErrorsCount(aEvent.target);
++ }
++};
++
++DeveloperToolbar.prototype.receiveMessage = function DT_receiveMessage(aMessage)
++{
++ if (!aMessage.json ||
++ !(aMessage.json.hudId in this._errorsCount)) {
++ dump("received message from unknown ID " + aMessage + "\n");
++ return;
++ }
++
++ dump("receiveMessage " + aMessage.name + " " + aMessage.json.hudId + "\n");
++
++ switch (aMessage.name) {
++ case "WebConsole:PageError":
++ this._onPageError(aMessage.json.hudId, aMessage.json.pageError);
++ break;
++ case "WebConsole:CachedMessages":
++ this._onCachedMessages(aMessage.json.hudId, aMessage.json.messages);
++ break;
++ case "WebConsole:LocationChange":
++ this._onTabLocationChange(aMessage.json.hudId, aMessage.json);
++ break;
++ }
++
++ dump("receiveMessage end errors " + this._errorsCount[aMessage.json.hudId] + "\n");
++};
++
++DeveloperToolbar.prototype.sendMessageToTab =
++function DT_sendMessageToTab(aTab, aMessage)
++{
++ let tabId = aTab.linkedPanel;
++ aMessage.hudId = tabId;
++ if (!("id" in aMessage)) {
++ aMessage.id = "DevToolbar-" + this.sequenceId;
++ }
++
++ aTab.linkedBrowser.messageManager.sendAsyncMessage(aName, aMessage);
++};
++
++DeveloperToolbar.prototype._onPageError =
++function DT__onPageError(aTabId, aPageError)
++{
++ let isError = true;
++ if ((aPageError.flags & aPageError.warningFlag) ||
++ (aPageError.flags & aPageError.strictFlag)) {
++ isError = false;
++ }
++ this._errorsCount[aTabId]++;
++};
++
++DeveloperToolbar.prototype._onCachedMessages =
++function DT__onCachedMessages(aTabId, aMessages)
++{
++ aMessages.forEach(this._onPageError.bind(this, aTabId));
++};
++
++DeveloperToolbar.prototype._onTabLocationChange =
++function DT__onTabLocationChange(aTabId)
++{
++ this._errorsCount[aTabId] = 0;
+ };
+
+ /**
+ * Panel to handle command line output.
+ * @param aChromeDoc document from which we can pull the parts we need.
+ * @param aInput the input element that should get focus.
+ * @param aLoadCallback called when the panel is loaded properly.
+ */
+diff --git a/browser/devtools/webconsole/HUDService-content.js b/browser/devtools/webconsole/HUDService-content.js
+--- a/browser/devtools/webconsole/HUDService-content.js
++++ b/browser/devtools/webconsole/HUDService-content.js
+@@ -1281,39 +1281,57 @@ let ConsoleListener = {
+ */
+ observe: function CL_observe(aScriptError)
+ {
+ if (!_alive || !(aScriptError instanceof Ci.nsIScriptError) ||
+ !aScriptError.outerWindowID) {
+ return;
+ }
+
+- switch (aScriptError.category) {
+- // We ignore chrome-originating errors as we only care about content.
+- case "XPConnect JavaScript":
+- case "component javascript":
+- case "chrome javascript":
+- case "chrome registration":
+- case "XBL":
+- case "XBL Prototype Handler":
+- case "XBL Content Sink":
+- case "xbl javascript":
+- return;
++ if (!this.isCategoryAllowed(aScriptError.category)) {
++ return;
+ }
+
+ let errorWindow =
+ WebConsoleUtils.getWindowByOuterId(aScriptError.outerWindowID,
+ Manager.window);
+ if (!errorWindow || errorWindow.top != Manager.window) {
+ return;
+ }
+
+ Manager.sendMessage("WebConsole:PageError", { pageError: aScriptError });
+ },
+
++
++ /**
++ * Check if the given script error category is allowed to be tracked or not.
++ * We ignore chrome-originating errors as we only care about content.
++ *
++ * @param string aCategory
++ * The nsIScriptError category you want to check.
++ * @return boolean
++ * True if the category is allowed to be logged, false otherwise.
++ */
++ isCategoryAllowed: function CL_isCategoryAllowed(aCategory)
++ {
++ switch (aCategory) {
++ case "XPConnect JavaScript":
++ case "component javascript":
++ case "chrome javascript":
++ case "chrome registration":
++ case "XBL":
++ case "XBL Prototype Handler":
++ case "XBL Content Sink":
++ case "xbl javascript":
++ return false;
++ }
++
++ return true;
++ },
++
+ /**
+ * Get the cached page errors for the current inner window.
+ *
+ * @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.
+ */
+@@ -1321,24 +1339,25 @@ let ConsoleListener = {
+ {
+ let innerWindowId = WebConsoleUtils.getInnerWindowId(Manager.window);
+ let result = [];
+ let errors = {};
+ Services.console.getMessageArray(errors, {});
+
+ (errors.value || []).forEach(function(aError) {
+ if (!(aError instanceof Ci.nsIScriptError) ||
+- aError.innerWindowID != innerWindowId) {
++ aError.innerWindowID != innerWindowId ||
++ this.isCategoryAllowed(aError.category)) {
+ return;
+ }
+
+ let remoteMessage = WebConsoleUtils.cloneObject(aError);
+ remoteMessage._type = "PageError";
+ result.push(remoteMessage);
+- });
++ }, this);
+
+ return result;
+ },
+
+ /**
+ * Remove the nsIConsoleService listener.
+ */
+ destroy: function CL_destroy()
+diff --git a/browser/devtools/webconsole/HUDService.jsm b/browser/devtools/webconsole/HUDService.jsm
+--- a/browser/devtools/webconsole/HUDService.jsm
++++ b/browser/devtools/webconsole/HUDService.jsm
+@@ -1407,20 +1407,18 @@ HeadsUpDisplay.prototype = {
+ if (!aRemoteMessages.length) {
+ return;
+ }
+
+ aRemoteMessages.forEach(function(aMessage) {
+ switch (aMessage._type) {
+ case "PageError": {
+ let category = this.categoryForScriptError(aMessage.category);
+- if (category != -1) {
+- this.outputMessage(category, this.reportPageError,
+- [category, aMessage]);
+- }
++ this.outputMessage(category, this.reportPageError,
++ [category, aMessage]);
+ break;
+ }
+ case "ConsoleAPI":
+ this.outputMessage(CATEGORY_WEBDEV, this.logConsoleAPIMessage,
+ [aMessage]);
+ break;
+ }
+ }, this);
+@@ -2045,20 +2043,16 @@ HeadsUpDisplay.prototype = {
+ *
+ * @param nsIScriptError aScriptError
+ * The error message to report.
+ * @return nsIDOMElement|undefined
+ * The message element to display in the Web Console output.
+ */
+ reportPageError: function HUD_reportPageError(aCategory, aScriptError)
+ {
+- if (!aScriptError.outerWindowID) {
+- return;
+- }
+-
+ // Warnings and legacy strict errors become warnings; other types become
+ // errors.
+ let severity = SEVERITY_ERROR;
+ if ((aScriptError.flags & aScriptError.warningFlag) ||
+ (aScriptError.flags & aScriptError.strictFlag)) {
+ severity = SEVERITY_WARNING;
+ }
+
+@@ -2075,34 +2069,23 @@ HeadsUpDisplay.prototype = {
+ return node;
+ },
+
+ /**
+ * Determine the category of a given nsIScriptError.
+ *
+ * @param nsIScriptError aScriptError
+ * The script error you want to determine the category for.
+- * @return CATEGORY_JS|CATEGORY_CSS|-1
++ * @return CATEGORY_JS|CATEGORY_CSS
+ * Depending on the script error CATEGORY_JS or CATEGORY_CSS can be
+- * returned. If the category is unknown -1 is returned.
++ * returned.
+ */
+ categoryForScriptError: function HUD_categoryForScriptError(aScriptError)
+ {
+ switch (aScriptError.category) {
+- // We ignore chrome-originating errors as we only care about content.
+- case "XPConnect JavaScript":
+- case "component javascript":
+- case "chrome javascript":
+- case "chrome registration":
+- case "XBL":
+- case "XBL Prototype Handler":
+- case "XBL Content Sink":
+- case "xbl javascript":
+- return -1;
+-
+ case "CSS Parser":
+ case "CSS Loader":
+ return CATEGORY_CSS;
+
+ default:
+ return CATEGORY_JS;
+ }
+ },
+@@ -2297,20 +2280,18 @@ HeadsUpDisplay.prototype = {
+ this.jsterm.handleInspectObject(aMessage.json);
+ break;
+ case "WebConsole:ConsoleAPI":
+ this.outputMessage(CATEGORY_WEBDEV, this.logConsoleAPIMessage, [aMessage.json]);
+ break;
+ case "WebConsole:PageError": {
+ let pageError = aMessage.json.pageError;
+ let category = this.categoryForScriptError(pageError);
+- if (category != -1) {
+- this.outputMessage(category, this.reportPageError,
+- [category, pageError]);
+- }
++ this.outputMessage(category, this.reportPageError,
++ [category, pageError]);
+ break;
+ }
+ case "WebConsole:CachedMessages":
+ this._displayCachedConsoleMessages(aMessage.json.messages);
+ this._onInitComplete();
+ break;
+ case "WebConsole:NetworkActivity":
+ this.handleNetworkActivity(aMessage.json);
View
1  patches-webconsole/series
@@ -1,2 +1,3 @@
bug-761157
bug-761257
+bug-762996
Please sign in to comment.
Something went wrong with that request. Please try again.