diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index f276ddf5daaf..b28b9af113b1 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -173,6 +173,12 @@ XPCOMUtils.defineLazyGetter(this, "PopupNotifications", function () { } }); +XPCOMUtils.defineLazyGetter(this, "InspectorUI", function() { + let tmp = {}; + Cu.import("resource:///modules/inspector.jsm", tmp); + return new tmp.InspectorUI(window); +}); + let gInitialPages = [ "about:blank", "about:privatebrowsing", @@ -180,7 +186,6 @@ let gInitialPages = [ ]; #include browser-fullZoom.js -#include ../../devtools/highlighter/inspector.js #include browser-places.js #include browser-tabPreviews.js #include browser-tabview.js @@ -1676,7 +1681,8 @@ function delayedStartup(isLoadingBlank, mustLoadSidebar) { TabView.init(); // Enable Inspector? - if (InspectorUI.enabled) { + let enabled = gPrefService.getBoolPref("devtools.inspector.enabled"); + if (enabled) { document.getElementById("menu_pageinspect").hidden = false; document.getElementById("Tools:Inspect").removeAttribute("disabled"); #ifdef MENUBAR_CAN_AUTOHIDE @@ -1724,6 +1730,9 @@ function BrowserShutdown() { if (!gStartupRan) return; + if (!__lookupGetter__("InspectorUI")) + InspectorUI.destroy(); + // First clean up services initialized in BrowserStartup (or those whose // uninit methods don't depend on the services having been initialized). allTabs.uninit(); diff --git a/browser/base/content/browser.xul b/browser/base/content/browser.xul index 2251cdb944cc..dddbf04638d8 100644 --- a/browser/base/content/browser.xul +++ b/browser/base/content/browser.xul @@ -232,7 +232,6 @@ noautohide="true" titlebar="normal" close="true" - onpopuphiding="InspectorUI.closeInspectorUI();" label="&inspectPanelTitle.label;"> diff --git a/browser/devtools/highlighter/insideOutBox.js b/browser/devtools/highlighter/InsideOutBox.jsm similarity index 97% rename from browser/devtools/highlighter/insideOutBox.js rename to browser/devtools/highlighter/InsideOutBox.jsm index f0556a8d33cf..fd53647cef5e 100644 --- a/browser/devtools/highlighter/insideOutBox.js +++ b/browser/devtools/highlighter/InsideOutBox.jsm @@ -125,6 +125,9 @@ InsideOutBoxView = { * The box object containing the InsideOutBox. Required to add/remove * children during box manipulation (toggling opened or closed). */ + +var EXPORTED_SYMBOLS = ["InsideOutBox"]; + function InsideOutBox(aView, aBox) { this.view = aView; @@ -450,7 +453,7 @@ InsideOutBox.prototype = try { this.box.removeChild(this.rootObjectBox); } catch (exc) { - InspectorUI._log("this.box.removeChild(this.rootObjectBox) FAILS " + + this.view._log("this.box.removeChild(this.rootObjectBox) FAILS " + this.box + " must not contain " + this.rootObjectBox); } } @@ -643,4 +646,18 @@ InsideOutBox.prototype = return node; }, + + /** + * Clean up our mess. + */ + destroy: function IOBox_destroy() + { + delete this.view; + delete this.box; + delete this.rootObject; + delete this.rootObjectBox; + delete this.selectedObjectBox; + delete this.highlightedObjectBox; + delete this.scrollIntoView; + } }; diff --git a/browser/devtools/highlighter/Makefile.in b/browser/devtools/highlighter/Makefile.in index 907231d3cee9..333d3ea56de7 100644 --- a/browser/devtools/highlighter/Makefile.in +++ b/browser/devtools/highlighter/Makefile.in @@ -45,7 +45,10 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk EXTRA_JS_MODULES = \ + inspector.jsm \ domplate.jsm \ + InsideOutBox.jsm \ + TreePanel.jsm \ $(NULL) ifdef ENABLE_TESTS diff --git a/browser/devtools/highlighter/TreePanel.jsm b/browser/devtools/highlighter/TreePanel.jsm new file mode 100644 index 000000000000..3c1ad949f79a --- /dev/null +++ b/browser/devtools/highlighter/TreePanel.jsm @@ -0,0 +1,779 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (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.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Mozilla Tree Panel. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Rob Campbell (original author) + * Mihai Șucan + * Julian Viereck + * Paul Rouget + * Kyle Simpson + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +const Cu = Components.utils; + +Cu.import("resource:///modules/domplate.jsm"); +Cu.import("resource:///modules/InsideOutBox.jsm"); +Cu.import("resource:///modules/Services.jsm"); + +var EXPORTED_SYMBOLS = ["TreePanel"]; + +const INSPECTOR_URI = "chrome://browser/content/inspector.html"; + +/** + * TreePanel + * A container for the Inspector's HTML Tree Panel widget constructor function. + * @param aContext nsIDOMWindow (xulwindow) + * @param aIUI global InspectorUI object + */ +function TreePanel(aContext, aIUI) { + this._init(aContext, aIUI); +}; + +TreePanel.prototype = { + showTextNodesWithWhitespace: false, + id: "treepanel", // DO NOT LOCALIZE + openInDock: true, + + /** + * The tree panel container element. + * @returns xul:panel|xul:vbox|null + * xul:panel is returned when the tree panel is not docked, or + * xul:vbox when when the tree panel is docked. + * null is returned when no container is available. + */ + get container() + { + if (this.openInDock) { + return this.document.getElementById("inspector-tree-box"); + } + + return this.document.getElementById("inspector-tree-panel"); + }, + + /** + * Main TreePanel boot-strapping method. Initialize the TreePanel with the + * originating context and the InspectorUI global. + * @param aContext nsIDOMWindow (xulwindow) + * @param aIUI global InspectorUI object + */ + _init: function TP__init(aContext, aIUI) + { + this.IUI = aIUI; + this.window = aContext; + this.document = this.window.document; + + domplateUtils.setDOM(this.window); + + let isOpen = this.isOpen.bind(this); + + this.registrationObject = { + id: this.id, + label: this.IUI.strings.GetStringFromName("htmlPanel.label"), + tooltiptext: this.IUI.strings.GetStringFromName("htmlPanel.tooltiptext"), + accesskey: this.IUI.strings.GetStringFromName("htmlPanel.accesskey"), + context: this, + get isOpen() isOpen(), + show: this.open, + hide: this.close, + onSelect: this.select, + panel: this.openInDock ? null : this.container, + unregister: this.destroy, + }; + this.editingEvents = {}; + + if (!this.openInDock) { + this._boundClose = this.close.bind(this); + this.container.addEventListener("popuphiding", this._boundClose, false); + } + + // Register the HTML panel with the highlighter + this.IUI.registerTool(this.registrationObject); + }, + + /** + * Initialization function for the TreePanel. + */ + initializeIFrame: function TP_initializeIFrame() + { + if (!this.initializingTreePanel || this.treeLoaded) { + return; + } + this.treeBrowserDocument = this.treeIFrame.contentDocument; + this.treePanelDiv = this.treeBrowserDocument.createElement("div"); + this.treeBrowserDocument.body.appendChild(this.treePanelDiv); + this.treePanelDiv.ownerPanel = this; + this.ioBox = new InsideOutBox(this, this.treePanelDiv); + this.ioBox.createObjectBox(this.IUI.win.document.documentElement); + this.treeLoaded = true; + this.treeIFrame.addEventListener("click", this.onTreeClick.bind(this), false); + this.treeIFrame.addEventListener("dblclick", this.onTreeDblClick.bind(this), false); + delete this.initializingTreePanel; + Services.obs.notifyObservers(null, + this.IUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, null); + if (this.IUI.selection) + this.select(this.IUI.selection, true); + }, + + /** + * Open the inspector's tree panel and initialize it. + */ + open: function TP_open() + { + if (this.initializingTreePanel && !this.treeLoaded) { + return; + } + + this.initializingTreePanel = true; + if (!this.openInDock) + this.container.hidden = false; + + this.treeIFrame = this.document.getElementById("inspector-tree-iframe"); + if (!this.treeIFrame) { + this.treeIFrame = this.document.createElement("iframe"); + this.treeIFrame.setAttribute("id", "inspector-tree-iframe"); + this.treeIFrame.flex = 1; + this.treeIFrame.setAttribute("type", "content"); + } + + if (this.openInDock) { // Create vbox + this.openDocked(); + return; + } + + let resizerBox = this.document.getElementById("tree-panel-resizer-box"); + this.treeIFrame = this.container.insertBefore(this.treeIFrame, resizerBox); + + let boundLoadedInitializeTreePanel = function loadedInitializeTreePanel() + { + this.treeIFrame.removeEventListener("load", + boundLoadedInitializeTreePanel, true); + this.initializeIFrame(); + }.bind(this); + + let boundTreePanelShown = function treePanelShown() + { + this.container.removeEventListener("popupshown", + boundTreePanelShown, false); + + this.treeIFrame.addEventListener("load", + boundLoadedInitializeTreePanel, true); + + let src = this.treeIFrame.getAttribute("src"); + if (src != INSPECTOR_URI) { + this.treeIFrame.setAttribute("src", INSPECTOR_URI); + } else { + this.treeIFrame.contentWindow.location.reload(); + } + }.bind(this); + + this.container.addEventListener("popupshown", boundTreePanelShown, false); + + const panelWidthRatio = 7 / 8; + const panelHeightRatio = 1 / 5; + + let width = parseInt(this.IUI.win.outerWidth * panelWidthRatio); + let height = parseInt(this.IUI.win.outerHeight * panelHeightRatio); + let y = Math.min(this.document.defaultView.screen.availHeight - height, + this.IUI.win.innerHeight); + + this.container.openPopup(this.browser, "overlap", 0, 0, + false, false); + + this.container.moveTo(80, y); + this.container.sizeTo(width, height); + }, + + openDocked: function TP_openDocked() + { + let treeBox = null; + let toolbar = this.IUI.toolbar.nextSibling; // Addons bar, typically + let toolbarParent = + this.IUI.browser.ownerDocument.getElementById("browser-bottombox"); + treeBox = this.document.createElement("vbox"); + treeBox.id = "inspector-tree-box"; + treeBox.state = "open"; // for the registerTools API. + treeBox.minHeight = 10; + treeBox.flex = 1; + toolbarParent.insertBefore(treeBox, toolbar); + this.createResizer(); + treeBox.appendChild(this.treeIFrame); + + let boundLoadedInitializeTreePanel = function loadedInitializeTreePanel() + { + this.treeIFrame.removeEventListener("load", + boundLoadedInitializeTreePanel, true); + this.initializeIFrame(); + }.bind(this); + + this.treeIFrame.addEventListener("load", + boundLoadedInitializeTreePanel, true); + + let src = this.treeIFrame.getAttribute("src"); + if (src != INSPECTOR_URI) { + this.treeIFrame.setAttribute("src", INSPECTOR_URI); + } else { + this.treeIFrame.contentWindow.location.reload(); + } + }, + + /** + * Lame resizer on the toolbar. + */ + createResizer: function TP_createResizer() + { + let resizer = this.document.createElement("resizer"); + resizer.id = "inspector-horizontal-splitter"; + resizer.setAttribute("dir", "top"); + resizer.flex = 1; + resizer.setAttribute("element", "inspector-tree-box"); + resizer.height = 24; + this.IUI.toolbar.appendChild(resizer); + this.resizer = resizer; + }, + + /** + * Close the TreePanel. + */ + close: function TP_close() + { + if (this.openInDock) { + this.IUI.toolbar.removeChild(this.resizer); + let treeBox = this.container; + let treeBoxParent = treeBox.parentNode; + treeBoxParent.removeChild(treeBox); + } else { + this.container.hidePopup(); + } + + if (this.treePanelDiv) { + this.treePanelDiv.ownerPanel = null; + let parent = this.treePanelDiv.parentNode; + parent.removeChild(this.treePanelDiv); + delete this.treePanelDiv; + delete this.treeBrowserDocument; + } + + this.treeLoaded = false; + }, + + /** + * Is the TreePanel open? + * @returns boolean + */ + isOpen: function TP_isOpen() + { + if (this.openInDock) + return this.treeLoaded && this.container; + + return this.treeLoaded && this.container.state == "open"; + }, + + /** + * Create the ObjectBox for the given object. + * @param object nsIDOMNode + * @param isRoot boolean - Is this the root object? + * @returns InsideOutBox + */ + createObjectBox: function TP_createObjectBox(object, isRoot) + { + let tag = domplateUtils.getNodeTag(object); + if (tag) + return tag.replace({object: object}, this.treeBrowserDocument); + }, + + getParentObject: function TP_getParentObject(node) + { + let parentNode = node ? node.parentNode : null; + + if (!parentNode) { + // Documents have no parentNode; Attr, Document, DocumentFragment, Entity, + // and Notation. top level windows have no parentNode + if (node && node == this.window.Node.DOCUMENT_NODE) { + // document type + if (node.defaultView) { + let embeddingFrame = node.defaultView.frameElement; + if (embeddingFrame) + return embeddingFrame.parentNode; + } + } + // a Document object without a parentNode or window + return null; // top level has no parent + } + + if (parentNode.nodeType == this.window.Node.DOCUMENT_NODE) { + if (parentNode.defaultView) { + return parentNode.defaultView.frameElement; + } + // parent is document element, but no window at defaultView. + return null; + } + + if (!parentNode.localName) + return null; + + return parentNode; + }, + + getChildObject: function TP_getChildObject(node, index, previousSibling) + { + if (!node) + return null; + + if (node.contentDocument) { + // then the node is a frame + if (index == 0) { + return node.contentDocument.documentElement; // the node's HTMLElement + } + return null; + } + + if (node instanceof this.window.GetSVGDocument) { + let svgDocument = node.getSVGDocument(); + if (svgDocument) { + // then the node is a frame + if (index == 0) { + return svgDocument.documentElement; // the node's SVGElement + } + return null; + } + } + + let child = null; + if (previousSibling) // then we are walking + child = this.getNextSibling(previousSibling); + else + child = this.getFirstChild(node); + + if (this.showTextNodesWithWhitespace) + return child; + + for (; child; child = this.getNextSibling(child)) { + if (!domplateUtils.isWhitespaceText(child)) + return child; + } + + return null; // we have no children worth showing. + }, + + getFirstChild: function TP_getFirstChild(node) + { + this.treeWalker = node.ownerDocument.createTreeWalker(node, + this.window.NodeFilter.SHOW_ALL, null, false); + return this.treeWalker.firstChild(); + }, + + getNextSibling: function TP_getNextSibling(node) + { + let next = this.treeWalker.nextSibling(); + + if (!next) + delete this.treeWalker; + + return next; + }, + + ///////////////////////////////////////////////////////////////////// + // Event Handling + + /** + * Handle click events in the html tree panel. + * @param aEvent + * The mouse event. + */ + onTreeClick: function TP_onTreeClick(aEvent) + { + let node; + let target = aEvent.target; + let hitTwisty = false; + if (this.hasClass(target, "twisty")) { + node = this.getRepObject(aEvent.target.nextSibling); + hitTwisty = true; + } else { + node = this.getRepObject(aEvent.target); + } + + if (node) { + if (hitTwisty) { + this.ioBox.toggleObject(node); + } else { + if (this.IUI.inspecting) { + this.IUI.stopInspecting(true); + } else { + this.IUI.select(node, true, false); + this.IUI.highlighter.highlightNode(node); + } + } + } + }, + + /** + * Handle double-click events in the html tree panel. + * (double-clicking an attribute value allows it to be edited) + * @param aEvent + * The mouse event. + */ + onTreeDblClick: function TP_onTreeDblClick(aEvent) + { + // if already editing an attribute value, double-clicking elsewhere + // in the tree is the same as a click, which dismisses the editor + if (this.editingContext) + this.closeEditor(); + + let target = aEvent.target; + + if (this.hasClass(target, "nodeValue")) { + let repObj = this.getRepObject(target); + let attrName = target.getAttribute("data-attributeName"); + let attrVal = target.innerHTML; + + this.editAttributeValue(target, repObj, attrName, attrVal); + } + }, + + /** + * Starts the editor for an attribute value. + * @param aAttrObj + * The DOM object representing the attribute value in the HTML Tree + * @param aRepObj + * The original DOM (target) object being inspected/edited + * @param aAttrName + * The name of the attribute being edited + * @param aAttrVal + * The current value of the attribute being edited + */ + editAttributeValue: + function TP_editAttributeValue(aAttrObj, aRepObj, aAttrName, aAttrVal) + { + let editor = this.treeBrowserDocument.getElementById("attribute-editor"); + let editorInput = + this.treeBrowserDocument.getElementById("attribute-editor-input"); + let attrDims = aAttrObj.getBoundingClientRect(); + // figure out actual viewable viewport dimensions (sans scrollbars) + let viewportWidth = this.treeBrowserDocument.documentElement.clientWidth; + let viewportHeight = this.treeBrowserDocument.documentElement.clientHeight; + + // saves the editing context for use when the editor is saved/closed + this.editingContext = { + attrObj: aAttrObj, + repObj: aRepObj, + attrName: aAttrName + }; + + // highlight attribute-value node in tree while editing + this.addClass(aAttrObj, "editingAttributeValue"); + + // show the editor + this.addClass(editor, "editing"); + + // offset the editor below the attribute-value node being edited + let editorVeritcalOffset = 2; + + // keep the editor comfortably within the bounds of the viewport + let editorViewportBoundary = 5; + + // outer editor is sized based on the box inside it + editorInput.style.width = Math.min(attrDims.width, viewportWidth - + editorViewportBoundary) + "px"; + editorInput.style.height = Math.min(attrDims.height, viewportHeight - + editorViewportBoundary) + "px"; + let editorDims = editor.getBoundingClientRect(); + + // calculate position for the editor according to the attribute node + let editorLeft = attrDims.left + this.treeIFrame.contentWindow.scrollX - + // center the editor against the attribute value + ((editorDims.width - attrDims.width) / 2); + let editorTop = attrDims.top + this.treeIFrame.contentWindow.scrollY + + attrDims.height + editorVeritcalOffset; + + // but, make sure the editor stays within the visible viewport + editorLeft = Math.max(0, Math.min( + (this.treeIFrame.contentWindow.scrollX + + viewportWidth - editorDims.width), + editorLeft) + ); + editorTop = Math.max(0, Math.min( + (this.treeIFrame.contentWindow.scrollY + + viewportHeight - editorDims.height), + editorTop) + ); + + // position the editor + editor.style.left = editorLeft + "px"; + editor.style.top = editorTop + "px"; + + // set and select the text + editorInput.value = aAttrVal; + editorInput.select(); + + // listen for editor specific events + this.bindEditorEvent(editor, "click", function(aEvent) { + aEvent.stopPropagation(); + }); + this.bindEditorEvent(editor, "dblclick", function(aEvent) { + aEvent.stopPropagation(); + }); + this.bindEditorEvent(editor, "keypress", + this.handleEditorKeypress.bind(this)); + + // event notification + Services.obs.notifyObservers(null, this.IUI.INSPECTOR_NOTIFICATIONS.EDITOR_OPENED, + null); + }, + + /** + * Handle binding an event handler for the editor. + * (saves the callback for easier unbinding later) + * @param aEditor + * The DOM object for the editor + * @param aEventName + * The name of the event to listen for + * @param aEventCallback + * The callback to bind to the event (and also to save for later + * unbinding) + */ + bindEditorEvent: + function TP_bindEditorEvent(aEditor, aEventName, aEventCallback) + { + this.editingEvents[aEventName] = aEventCallback; + aEditor.addEventListener(aEventName, aEventCallback, false); + }, + + /** + * Handle unbinding an event handler from the editor. + * (unbinds the previously bound and saved callback) + * @param aEditor + * The DOM object for the editor + * @param aEventName + * The name of the event being listened for + */ + unbindEditorEvent: function TP_unbindEditorEvent(aEditor, aEventName) + { + aEditor.removeEventListener(aEventName, this.editingEvents[aEventName], + false); + this.editingEvents[aEventName] = null; + }, + + /** + * Handle keypress events in the editor. + * @param aEvent + * The keyboard event. + */ + handleEditorKeypress: function TP_handleEditorKeypress(aEvent) + { + if (aEvent.which == this.window.KeyEvent.DOM_VK_RETURN) { + this.saveEditor(); + } else if (aEvent.keyCode == this.window.KeyEvent.DOM_VK_ESCAPE) { + this.closeEditor(); + } + }, + + /** + * Close the editor and cleanup. + */ + closeEditor: function TP_closeEditor() + { + let editor = this.treeBrowserDocument.getElementById("attribute-editor"); + let editorInput = + this.treeBrowserDocument.getElementById("attribute-editor-input"); + + // remove highlight from attribute-value node in tree + this.removeClass(this.editingContext.attrObj, "editingAttributeValue"); + + // hide editor + this.removeClass(editor, "editing"); + + // stop listening for editor specific events + this.unbindEditorEvent(editor, "click"); + this.unbindEditorEvent(editor, "dblclick"); + this.unbindEditorEvent(editor, "keypress"); + + // clean up after the editor + editorInput.value = ""; + editorInput.blur(); + this.editingContext = null; + this.editingEvents = {}; + + // event notification + Services.obs.notifyObservers(null, this.IUI.INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED, + null); + }, + + /** + * Commit the edits made in the editor, then close it. + */ + saveEditor: function TP_saveEditor() + { + let editorInput = + this.treeBrowserDocument.getElementById("attribute-editor-input"); + + // set the new attribute value on the original target DOM element + this.editingContext.repObj.setAttribute(this.editingContext.attrName, + editorInput.value); + + // update the HTML tree attribute value + this.editingContext.attrObj.innerHTML = editorInput.value; + + this.IUI.isDirty = true; + + // event notification + Services.obs.notifyObservers(null, this.IUI.INSPECTOR_NOTIFICATIONS.EDITOR_SAVED, + null); + + this.closeEditor(); + }, + + /** + * Simple tree select method. + * @param aNode the DOM node in the content document to select. + * @param aScroll boolean scroll to the visible node? + */ + select: function TP_select(aNode, aScroll) + { + if (this.ioBox) + this.ioBox.select(aNode, true, true, aScroll); + }, + + /////////////////////////////////////////////////////////////////////////// + //// Utility functions + + /** + * Does the given object have a class attribute? + * @param aNode + * the DOM node. + * @param aClass + * The class string. + * @returns boolean + */ + hasClass: function TP_hasClass(aNode, aClass) + { + if (!(aNode instanceof this.window.Element)) + return false; + return aNode.classList.contains(aClass); + }, + + /** + * Add the class name to the given object. + * @param aNode + * the DOM node. + * @param aClass + * The class string. + */ + addClass: function TP_addClass(aNode, aClass) + { + if (aNode instanceof this.window.Element) + aNode.classList.add(aClass); + }, + + /** + * Remove the class name from the given object + * @param aNode + * the DOM node. + * @param aClass + * The class string. + */ + removeClass: function TP_removeClass(aNode, aClass) + { + if (aNode instanceof this.window.Element) + aNode.classList.remove(aClass); + }, + + /** + * Get the "repObject" from the HTML panel's domplate-constructed DOM node. + * In this system, a "repObject" is the Object being Represented by the box + * object. It is the "real" object that we're building our facade around. + * + * @param element + * The element in the HTML panel the user clicked. + * @returns either a real node or null + */ + getRepObject: function TP_getRepObject(element) + { + let target = null; + for (let child = element; child; child = child.parentNode) { + if (this.hasClass(child, "repTarget")) + target = child; + + if (child.repObject) { + if (!target && this.hasClass(child.repObject, "repIgnore")) + break; + else + return child.repObject; + } + } + return null; + }, + + /** + * Destructor function. Cleanup. + */ + destroy: function TP_destroy() + { + if (this.isOpen()) { + this.close(); + } + + domplateUtils.setDOM(null); + + delete this.resizer; + delete this.treeWalker; + + if (this.treePanelDiv) { + this.treePanelDiv.ownerPanel = null; + let parent = this.treePanelDiv.parentNode; + parent.removeChild(this.treePanelDiv); + delete this.treePanelDiv; + delete this.treeBrowserDocument; + } + + if (this.treeIFrame) { + this.treeIFrame.removeEventListener("dblclick", this.onTreeDblClick, false); + this.treeIFrame.removeEventListener("click", this.onTreeClick, false); + let parent = this.treeIFrame.parentNode; + parent.removeChild(this.treeIFrame); + delete this.treeIFrame; + } + + if (this.ioBox) { + this.ioBox.destroy(); + delete this.ioBox; + } + + if (!this.openInDock) { + this.container.removeEventListener("popuphiding", this._boundClose, false); + delete this._boundClose; + } + } +}; + diff --git a/browser/devtools/highlighter/inspector.js b/browser/devtools/highlighter/inspector.jsm similarity index 59% rename from browser/devtools/highlighter/inspector.js rename to browser/devtools/highlighter/inspector.jsm index e90bf6058c55..0e7433e6b251 100644 --- a/browser/devtools/highlighter/inspector.js +++ b/browser/devtools/highlighter/inspector.jsm @@ -1,6 +1,5 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ -#ifdef 0 /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -26,7 +25,7 @@ * Mihai Șucan * Julian Viereck * Paul Rouget - * Kyle Simpson + * Kyle Simpson * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -41,9 +40,15 @@ * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ -#endif -#include insideOutBox.js +const Cu = Components.utils; +const Ci = Components.interfaces; +const Cr = Components.results; + +var EXPORTED_SYMBOLS = ["InspectorUI"]; + +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); const INSPECTOR_INVISIBLE_ELEMENTS = { "head": true, @@ -72,6 +77,9 @@ const INSPECTOR_NOTIFICATIONS = { // Fires once the Inspector is closed. CLOSED: "inspector-closed", + // Fires when the Tree Panel is opened and initialized. + TREEPANELREADY: "inspector-treepanel-ready", + // Event notifications for the attribute-value editor EDITOR_OPENED: "inspector-editor-opened", EDITOR_CLOSED: "inspector-editor-closed", @@ -90,30 +98,32 @@ const INSPECTOR_NOTIFICATIONS = { * box inside it - editorInput.style.width = Math.min(attrDims.width, viewportWidth - - editorViewportBoundary) + "px"; - editorInput.style.height = Math.min(attrDims.height, viewportHeight - - editorViewportBoundary) + "px"; - let editorDims = editor.getBoundingClientRect(); - - // calculate position for the editor according to the attribute node - let editorLeft = attrDims.left + this.treeIFrame.contentWindow.scrollX - - // center the editor against the attribute value - ((editorDims.width - attrDims.width) / 2); - let editorTop = attrDims.top + this.treeIFrame.contentWindow.scrollY + - attrDims.height + editorVeritcalOffset; - - // but, make sure the editor stays within the visible viewport - editorLeft = Math.max(0, Math.min( - (this.treeIFrame.contentWindow.scrollX + - viewportWidth - editorDims.width), - editorLeft) - ); - editorTop = Math.max(0, Math.min( - (this.treeIFrame.contentWindow.scrollY + - viewportHeight - editorDims.height), - editorTop) - ); - - // position the editor - editor.style.left = editorLeft + "px"; - editor.style.top = editorTop + "px"; - - // set and select the text - editorInput.value = aAttrVal; - editorInput.select(); - - // listen for editor specific events - this.bindEditorEvent(editor, "click", function(aEvent) { - aEvent.stopPropagation(); - }); - this.bindEditorEvent(editor, "dblclick", function(aEvent) { - aEvent.stopPropagation(); - }); - this.bindEditorEvent(editor, "keypress", - this.handleEditorKeypress.bind(this)); - - // event notification - Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.EDITOR_OPENED, - null); - }, - - /** - * Handle binding an event handler for the editor. - * (saves the callback for easier unbinding later) - * @param aEditor - * The DOM object for the editor - * @param aEventName - * The name of the event to listen for - * @param aEventCallback - * The callback to bind to the event (and also to save for later - * unbinding) - */ - bindEditorEvent: - function IUI_bindEditorEvent(aEditor, aEventName, aEventCallback) - { - this.editingEvents[aEventName] = aEventCallback; - aEditor.addEventListener(aEventName, aEventCallback, false); - }, - - /** - * Handle unbinding an event handler from the editor. - * (unbinds the previously bound and saved callback) - * @param aEditor - * The DOM object for the editor - * @param aEventName - * The name of the event being listened for - */ - unbindEditorEvent: function IUI_unbindEditorEvent(aEditor, aEventName) - { - aEditor.removeEventListener(aEventName, this.editingEvents[aEventName], - false); - this.editingEvents[aEventName] = null; - }, - - /** - * Handle keypress events in the editor. - * @param aEvent - * The keyboard event. - */ - handleEditorKeypress: function IUI_handleEditorKeypress(aEvent) - { - if (aEvent.which == KeyEvent.DOM_VK_RETURN) { - this.saveEditor(); - } else if (aEvent.keyCode == KeyEvent.DOM_VK_ESCAPE) { - this.closeEditor(); - } - }, - - /** - * Close the editor and cleanup. - */ - closeEditor: function IUI_closeEditor() - { - let editor = this.treeBrowserDocument.getElementById("attribute-editor"); - let editorInput = - this.treeBrowserDocument.getElementById("attribute-editor-input"); - - // remove highlight from attribute-value node in tree - this.removeClass(this.editingContext.attrObj, "editingAttributeValue"); - - // hide editor - this.removeClass(editor, "editing"); - - // stop listening for editor specific events - this.unbindEditorEvent(editor, "click"); - this.unbindEditorEvent(editor, "dblclick"); - this.unbindEditorEvent(editor, "keypress"); - - // clean up after the editor - editorInput.value = ""; - editorInput.blur(); - this.editingContext = null; - this.editingEvents = {}; - - // event notification - Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED, - null); - }, - - /** - * Commit the edits made in the editor, then close it. - */ - saveEditor: function IUI_saveEditor() - { - let editorInput = - this.treeBrowserDocument.getElementById("attribute-editor-input"); - - // set the new attribute value on the original target DOM element - this.editingContext.repObj.setAttribute(this.editingContext.attrName, - editorInput.value); - - // update the HTML tree attribute value - this.editingContext.attrObj.innerHTML = editorInput.value; - - this.isDirty = true; - - // event notification - Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.EDITOR_SAVED, - null); - - this.closeEditor(); - }, - /** * Attach event listeners to content window and child windows to enable * highlighting and click to stop inspection. @@ -1420,7 +1038,7 @@ var InspectorUI = { { let node = aDocument.elementFromPoint(aX, aY); if (node && node.contentDocument) { - if (node instanceof HTMLIFrameElement) { + if (node instanceof Ci.nsIDOMHTMLIFrameElement) { let rect = node.getBoundingClientRect(); // Gap between the iframe and its content window. @@ -1434,8 +1052,8 @@ var InspectorUI = { return node; } } - if (node instanceof HTMLIFrameElement || - node instanceof HTMLFrameElement) { + if (node instanceof Ci.nsIDOMHTMLIFrameElement || + node instanceof Ci.nsIDOMHTMLFrameElement) { let subnode = this.elementFromPoint(node.contentDocument, aX, aY); if (subnode) { node = subnode; @@ -1475,47 +1093,6 @@ var InspectorUI = { return [borderTop + paddingTop, borderLeft + paddingLeft]; }, - /** - * Does the given object have a class attribute? - * @param aNode - * the DOM node. - * @param aClass - * The class string. - * @returns boolean - */ - hasClass: function IUI_hasClass(aNode, aClass) - { - if (!(aNode instanceof Element)) - return false; - return aNode.classList.contains(aClass); - }, - - /** - * Add the class name to the given object. - * @param aNode - * the DOM node. - * @param aClass - * The class string. - */ - addClass: function IUI_addClass(aNode, aClass) - { - if (aNode instanceof Element) - aNode.classList.add(aClass); - }, - - /** - * Remove the class name from the given object - * @param aNode - * the DOM node. - * @param aClass - * The class string. - */ - removeClass: function IUI_removeClass(aNode, aClass) - { - if (aNode instanceof Element) - aNode.classList.remove(aClass); - }, - /** * Retrieve the unique ID of a window object. * @@ -1538,32 +1115,6 @@ var InspectorUI = { return util.currentInnerWindowID; }, - /** - * Get the "repObject" from the HTML panel's domplate-constructed DOM node. - * In this system, a "repObject" is the Object being Represented by the box - * object. It is the "real" object that we're building our facade around. - * - * @param element - * The element in the HTML panel the user clicked. - * @returns either a real node or null - */ - getRepObject: function IUI_getRepObject(element) - { - let target = null; - for (let child = element; child; child = child.parentNode) { - if (this.hasClass(child, "repTarget")) - target = child; - - if (child.repObject) { - if (!target && this.hasClass(child.repObject, "repIgnore")) - break; - else - return child.repObject; - } - } - return null; - }, - /** * @param msg * text message to send to the log @@ -1592,6 +1143,19 @@ var InspectorUI = { this._log("END TRACE"); }, + /** + * Get the toolbar button name for a given id string. Used by the + * registerTools API to retrieve a consistent name for toolbar buttons + * based on the ID of the tool. + * @param anId String + * id of the tool to be buttonized + * @returns String + */ + getToolbarButtonId: function IUI_createButtonId(anId) + { + return "inspector-" + anId + "-toolbutton"; + }, + /** * Register an external tool with the inspector. * @@ -1602,93 +1166,187 @@ var InspectorUI = { * icon: "chrome://somepath.png", * tooltiptext: "Button tooltip", * accesskey: "S", + * isOpen: object.property, (getter) returning true if tool is open. * onSelect: object.method, - * onShow: object.method, - * onHide: object.method, + * show: object.method, called to show the tool when button is pressed. + * hide: object.method, called to hide the tool when button is pressed. + * dim: object.method, called to disable a tool during highlighting. + * unregister: object.method, called when tool should be destroyed. * panel: myTool.panel * } * - * @param aRegObj + * @param aRegObj Object + * The Registration Object used to register this tool described + * above. The tool should cache this object for later deregistration. */ - registerTool: function IUI_RegisterTool(aRegObj) { - if (this.tools[aRegObj.id]) { + registerTool: function IUI_registerTool(aRegObj) + { + if (this.toolRegistered(aRegObj.id)) { return; - } else { - let id = aRegObj.id; - let buttonId = "inspector-" + id + "-toolbutton"; - aRegObj.buttonId = buttonId; - - aRegObj.panel.addEventListener("popuphiding", - function IUI_toolPanelHiding() { - btn.setAttribute("checked", "false"); - }, false); - aRegObj.panel.addEventListener("popupshowing", - function IUI_toolPanelShowing() { - btn.setAttribute("checked", "true"); - }, false); - - this.tools[id] = aRegObj; } - let toolbox = document.getElementById("inspector-tools"); - let btn = document.createElement("toolbarbutton"); - btn.setAttribute("id", aRegObj.buttonId); + this.tools[aRegObj.id] = aRegObj; + + let buttonContainer = this.chromeDoc.getElementById("inspector-tools"); + let btn = this.chromeDoc.createElement("toolbarbutton"); + let buttonId = this.getToolbarButtonId(aRegObj.id); + btn.setAttribute("id", buttonId); btn.setAttribute("label", aRegObj.label); btn.setAttribute("tooltiptext", aRegObj.tooltiptext); btn.setAttribute("accesskey", aRegObj.accesskey); - btn.setAttribute("class", "toolbarbutton-text"); btn.setAttribute("image", aRegObj.icon || ""); - toolbox.appendChild(btn); + buttonContainer.appendChild(btn); - btn.addEventListener("click", - function IUI_ToolButtonClick(aEvent) { - if (btn.getAttribute("checked") == "true") { - aRegObj.onHide.apply(aRegObj.context); + /** + * Save a registered tool's callback for a specified event. + * @param aWidget xul:widget + * @param aEvent a DOM event name + * @param aCallback Function the click event handler for the button + */ + let toolEvents = this.toolEvents; + function bindToolEvent(aWidget, aEvent, aCallback) { + toolEvents[aWidget.id + "_" + aEvent] = aCallback; + aWidget.addEventListener(aEvent, aCallback, false); + } + + bindToolEvent(btn, "click", + function IUI_toolButtonClick(aEvent) { + if (btn.checked) { + this.toolHide(aRegObj); } else { - aRegObj.onShow.apply(aRegObj.context, [InspectorUI.selection]); - aRegObj.onSelect.apply(aRegObj.context, [InspectorUI.selection]); + this.toolShow(aRegObj); } - }, false); + }.bind(this)); + + if (aRegObj.panel) { + bindToolEvent(aRegObj.panel, "popuphiding", + function IUI_toolPanelHiding() { + btn.checked = false; + }); + } }, -/** - * Save a list of open tools to the inspector store. - * - * @param aWinID The ID of the window used to save the associated tools - */ + /** + * Show the specified tool. + * @param aTool Object (see comment for IUI_registerTool) + */ + toolShow: function IUI_toolShow(aTool) + { + aTool.show.call(aTool.context, this.selection); + this.chromeDoc.getElementById(this.getToolbarButtonId(aTool.id)).checked = true; + }, + + /** + * Hide the specified tool. + * @param aTool Object (see comment for IUI_registerTool) + */ + toolHide: function IUI_toolHide(aTool) + { + aTool.hide.call(aTool.context); + this.chromeDoc.getElementById(this.getToolbarButtonId(aTool.id)).checked = false; + }, + + /** + * Unregister the registered tool, unbinding click events for the buttons + * and showing and hiding events for the panel. + * @param aRegObj Object + * The registration object used to register the tool. + */ + unregisterTool: function IUI_unregisterTool(aRegObj) + { + let button = this.chromeDoc.getElementById(this.getToolbarButtonId(aRegObj.id)); + + /** + * Unregister the events associated with the registered tool's widget. + * @param aWidget XUL:widget (toolbarbutton|panel). + * @param aEvent a DOM event. + */ + let toolEvents = this.toolEvents; + function unbindToolEvent(aWidget, aEvent) { + let toolEvent = aWidget.id + "_" + aEvent; + aWidget.removeEventListener(aEvent, toolEvents[toolEvent], false); + delete toolEvents[toolEvent] + }; + + let buttonContainer = this.chromeDoc.getElementById("inspector-tools"); + unbindToolEvent(button, "click"); + + if (aRegObj.panel) + unbindToolEvent(aRegObj.panel, "popuphiding"); + + buttonContainer.removeChild(button); + + if (aRegObj.unregister) + aRegObj.unregister.call(aRegObj.context); + + delete this.tools[aRegObj.id]; + }, + + /** + * Save a list of open tools to the inspector store. + * + * @param aWinID The ID of the window used to save the associated tools + */ saveToolState: function IUI_saveToolState(aWinID) { let openTools = {}; this.toolsDo(function IUI_toolsSetId(aTool) { - if (aTool.panel.state == "open") { + if (aTool.isOpen) { openTools[aTool.id] = true; } }); - InspectorStore.setValue(aWinID, "openTools", openTools); + this.store.setValue(aWinID, "openTools", openTools); }, -/** - * Restore tools previously save using saveToolState(). - * - * @param aWinID The ID of the window to which the associated tools are to be - * restored. - */ + /** + * Restore tools previously save using saveToolState(). + * + * @param aWinID The ID of the window to which the associated tools are to be + * restored. + */ restoreToolState: function IUI_restoreToolState(aWinID) { - let openTools = InspectorStore.getValue(aWinID, "openTools"); - InspectorUI.selection = InspectorUI.selection; + let openTools = this.store.getValue(aWinID, "openTools"); if (openTools) { this.toolsDo(function IUI_toolsOnShow(aTool) { if (aTool.id in openTools) { - aTool.onShow.apply(aTool.context, [InspectorUI.selection]); + this.toolShow(aTool); } - }); + }.bind(this)); } }, - + + /** + * For each tool in the tools collection select the current node that is + * selected in the highlighter + * @param aScroll boolean + * Do you want to scroll the treepanel? + */ + toolsSelect: function IUI_toolsSelect(aScroll) + { + let selection = this.selection; + this.toolsDo(function IUI_toolsOnSelect(aTool) { + if (aTool.isOpen) { + aTool.onSelect.call(aTool.context, selection, aScroll); + } + }); + }, + + /** + * Dim or undim each tool in the tools collection + * @param aState true = dim, false = undim + */ + toolsDim: function IUI_toolsDim(aState) + { + this.toolsDo(function IUI_toolsOnSelect(aTool) { + if (aTool.isOpen && "dim" in aTool) { + aTool.dim.call(aTool.context, aState); + } + }); + }, + /** * Loop through all registered tools and pass each into the provided function - * * @param aFunction The function to which each tool is to be passed */ toolsDo: function IUI_toolsDo(aFunction) @@ -1697,13 +1355,42 @@ var InspectorUI = { aFunction(tool); } }, + + /** + * Check if a tool is registered? + * @param aId The id of the tool to check + */ + toolRegistered: function IUI_toolRegistered(aId) + { + return aId in this.tools; + }, + + /** + * Destroy the InspectorUI instance. This is called by the InspectorUI API + * "user", see BrowserShutdown() in browser.js. + */ + destroy: function IUI_destroy() + { + if (this.isInspectorOpen) { + this.closeInspectorUI(); + } + + delete this.store; + delete this.chromeDoc; + delete this.chromeWin; + delete this.tabbrowser; + }, }; /** * The Inspector store is used for storing data specific to each tab window. + * @constructor */ -var InspectorStore = { - store: {}, +function InspectorStore() +{ + this.store = {}; +} +InspectorStore.prototype = { length: 0, /** @@ -1832,14 +1519,24 @@ var InspectorStore = { * changes to the web page and he tries to navigate away, he is prompted to * confirm page navigation, such that he's given the chance to prevent the loss * of edits. + * + * @constructor + * @param object aInspector + * InspectorUI instance object. */ -var InspectorProgressListener = { +function InspectorProgressListener(aInspector) +{ + this.IUI = aInspector; + this.IUI.tabbrowser.addProgressListener(this); +} + +InspectorProgressListener.prototype = { onStateChange: function IPL_onStateChange(aProgress, aRequest, aFlag, aStatus) { // Remove myself if the Inspector is no longer open. - if (!InspectorUI.isTreePanelOpen) { - gBrowser.removeProgressListener(InspectorProgressListener); + if (!this.IUI.isInspectorOpen) { + this.destroy(); return; } @@ -1850,14 +1547,14 @@ var InspectorProgressListener = { // If the request is about to happen in a new window, we are not concerned // about the request. - if (aProgress.DOMWindow != InspectorUI.win) { + if (aProgress.DOMWindow != this.IUI.win) { return; } - if (InspectorUI.isDirty) { + if (this.IUI.isDirty) { this.showNotification(aRequest); } else { - InspectorUI.closeInspectorUI(); + this.IUI.closeInspectorUI(); } }, @@ -1873,7 +1570,7 @@ var InspectorProgressListener = { { aRequest.suspend(); - let notificationBox = gBrowser.getNotificationBox(InspectorUI.browser); + let notificationBox = this.IUI.tabbrowser.getNotificationBox(this.IUI.browser); let notification = notificationBox. getNotificationWithValue("inspector-page-navigation"); @@ -1898,29 +1595,29 @@ var InspectorProgressListener = { let buttons = [ { id: "inspector.confirmNavigationAway.buttonLeave", - label: InspectorUI.strings. + label: this.IUI.strings. GetStringFromName("confirmNavigationAway.buttonLeave"), - accessKey: InspectorUI.strings. + accessKey: this.IUI.strings. GetStringFromName("confirmNavigationAway.buttonLeaveAccesskey"), callback: function onButtonLeave() { if (aRequest) { aRequest.resume(); aRequest = null; - InspectorUI.closeInspectorUI(); + this.IUI.closeInspectorUI(); } - }, + }.bind(this), }, { id: "inspector.confirmNavigationAway.buttonStay", - label: InspectorUI.strings. + label: this.IUI.strings. GetStringFromName("confirmNavigationAway.buttonStay"), - accessKey: InspectorUI.strings. + accessKey: this.IUI.strings. GetStringFromName("confirmNavigationAway.buttonStayAccesskey"), callback: cancelRequest }, ]; - let message = InspectorUI.strings. + let message = this.IUI.strings. GetStringFromName("confirmNavigationAway.message"); notification = notificationBox.appendNotification(message, @@ -1931,17 +1628,38 @@ var InspectorProgressListener = { // transient notification removal. notification.persistence = -1; }, + + /** + * Destroy the progress listener instance. + */ + destroy: function IPL_destroy() + { + this.IUI.tabbrowser.removeProgressListener(this); + + let notificationBox = this.IUI.tabbrowser.getNotificationBox(this.IUI.browser); + let notification = notificationBox. + getNotificationWithValue("inspector-page-navigation"); + + if (notification) { + notificationBox.removeNotification(notification, true); + } + + delete this.IUI; + }, }; ///////////////////////////////////////////////////////////////////////// //// Initializers -XPCOMUtils.defineLazyGetter(InspectorUI, "inspectCmd", function () { - return document.getElementById("Tools:Inspect"); -}); - -XPCOMUtils.defineLazyGetter(InspectorUI, "strings", function () { - return Services.strings. - createBundle("chrome://browser/locale/inspector.properties"); +XPCOMUtils.defineLazyGetter(InspectorUI.prototype, "strings", + function () { + return Services.strings. + createBundle("chrome://browser/locale/inspector.properties"); + }); + +XPCOMUtils.defineLazyGetter(this, "StyleInspector", function () { + var obj = {}; + Cu.import("resource:///modules/devtools/StyleInspector.jsm", obj); + return obj.StyleInspector; }); diff --git a/browser/devtools/highlighter/test/Makefile.in b/browser/devtools/highlighter/test/Makefile.in index cf991dfa0f56..7fe6d5f7729e 100644 --- a/browser/devtools/highlighter/test/Makefile.in +++ b/browser/devtools/highlighter/test/Makefile.in @@ -61,5 +61,8 @@ _BROWSER_FILES = \ browser_inspector_bug_566084_location_changed.js \ $(NULL) +# Disabled due to constant failures +# browser_inspector_treePanel_click.js \ + libs:: $(_BROWSER_FILES) $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir) diff --git a/browser/devtools/highlighter/test/browser_inspector_bug_566084_location_changed.js b/browser/devtools/highlighter/test/browser_inspector_bug_566084_location_changed.js index e7abe3d3e3d4..71349374fcf5 100644 --- a/browser/devtools/highlighter/test/browser_inspector_bug_566084_location_changed.js +++ b/browser/devtools/highlighter/test/browser_inspector_bug_566084_location_changed.js @@ -5,19 +5,19 @@ let notificationBox = null; function startLocationTests() { ok(window.InspectorUI, "InspectorUI variable exists"); - Services.obs.addObserver(runInspectorTests, INSPECTOR_NOTIFICATIONS.OPENED, null); + Services.obs.addObserver(runInspectorTests, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, null); InspectorUI.toggleInspectorUI(); } function runInspectorTests() { - Services.obs.removeObserver(runInspectorTests, INSPECTOR_NOTIFICATIONS.OPENED, null); + Services.obs.removeObserver(runInspectorTests, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, null); let para = content.document.querySelector("p"); ok(para, "found the paragraph element"); is(para.textContent, "init", "paragraph content is correct"); ok(InspectorUI.inspecting, "Inspector is highlighting"); - ok(InspectorUI.isTreePanelOpen, "Inspector Panel is open"); + ok(InspectorUI.isInspectorOpen, "Inspector is open"); InspectorUI.isDirty = true; @@ -56,7 +56,7 @@ function onPageLoad() { is(para.textContent, "test2", "paragraph content is correct"); ok(!InspectorUI.inspecting, "Inspector is not highlighting"); - ok(!InspectorUI.isTreePanelOpen, "Inspector Panel is not open"); + ok(!InspectorUI.isInspectorOpen, "Inspector Panel is not open"); testEnd(); } @@ -68,7 +68,7 @@ function locationTest2() { is(para.textContent, "init", "paragraph content is correct"); ok(InspectorUI.inspecting, "Inspector is highlighting"); - ok(InspectorUI.isTreePanelOpen, "Inspector Panel is open"); + ok(InspectorUI.isInspectorOpen, "Inspector Panel is open"); notificationBox.addEventListener("AlertActive", alertActive2, false); @@ -102,7 +102,6 @@ function alertActive2() { function testEnd() { notificationBox = null; - InspectorUI.isDirty = false; gBrowser.removeCurrentTab(); executeSoon(finish); } diff --git a/browser/devtools/highlighter/test/browser_inspector_bug_665880.js b/browser/devtools/highlighter/test/browser_inspector_bug_665880.js index 29b2f17eae50..6ccdbaac2a0d 100644 --- a/browser/devtools/highlighter/test/browser_inspector_bug_665880.js +++ b/browser/devtools/highlighter/test/browser_inspector_bug_665880.js @@ -23,18 +23,18 @@ function test() objectNode = doc.querySelector("object"); ok(objectNode, "we have the object node"); Services.obs.addObserver(runObjectInspectionTest, - INSPECTOR_NOTIFICATIONS.OPENED, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); InspectorUI.toggleInspectorUI(); } function runObjectInspectionTest() { Services.obs.removeObserver(runObjectInspectionTest, - INSPECTOR_NOTIFICATIONS.OPENED); + InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED); executeSoon(function() { Services.obs.addObserver(performTestComparison, - INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); InspectorUI.inspectNode(objectNode); }); @@ -43,18 +43,18 @@ function test() function performTestComparison() { Services.obs.removeObserver(performTestComparison, - INSPECTOR_NOTIFICATIONS.HIGHLIGHTING); + InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING); is(InspectorUI.selection, objectNode, "selection matches node"); Services.obs.addObserver(finishUp, - INSPECTOR_NOTIFICATIONS.CLOSED, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false); InspectorUI.closeInspectorUI(); } function finishUp() { - Services.obs.removeObserver(finishUp, INSPECTOR_NOTIFICATIONS.CLOSED); + Services.obs.removeObserver(finishUp, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED); doc = objectNode = null; gBrowser.removeCurrentTab(); finish(); diff --git a/browser/devtools/highlighter/test/browser_inspector_bug_674871.js b/browser/devtools/highlighter/test/browser_inspector_bug_674871.js index e3b2e10227b5..c7cc2b2c72a2 100644 --- a/browser/devtools/highlighter/test/browser_inspector_bug_674871.js +++ b/browser/devtools/highlighter/test/browser_inspector_bug_674871.js @@ -47,18 +47,18 @@ function test() ok(iframeNode, "we have the iframe node"); ok(iframeBodyNode, "we have the body node"); Services.obs.addObserver(runTests, - INSPECTOR_NOTIFICATIONS.OPENED, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); InspectorUI.toggleInspectorUI(); } function runTests() { Services.obs.removeObserver(runTests, - INSPECTOR_NOTIFICATIONS.OPENED); + InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED); executeSoon(function() { Services.obs.addObserver(isTheIframeSelected, - INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); moveMouseOver(iframeNode, 1, 1); }); @@ -67,7 +67,7 @@ function test() function isTheIframeSelected() { Services.obs.removeObserver(isTheIframeSelected, - INSPECTOR_NOTIFICATIONS.HIGHLIGHTING); + InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING); is(InspectorUI.selection, iframeNode, "selection matches node"); iframeNode.style.marginBottom = doc.defaultView.innerHeight + "px"; @@ -75,7 +75,7 @@ function test() executeSoon(function() { Services.obs.addObserver(isTheIframeContentSelected, - INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); moveMouseOver(iframeNode, 40, 40); }); @@ -84,7 +84,7 @@ function test() function isTheIframeContentSelected() { Services.obs.removeObserver(isTheIframeContentSelected, - INSPECTOR_NOTIFICATIONS.HIGHLIGHTING); + InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING); is(InspectorUI.selection, iframeBodyNode, "selection matches node"); // 184 == 200 + 11(border) + 13(padding) - 40(scroll) @@ -92,12 +92,12 @@ function test() "highlighter height"); Services.obs.addObserver(finishUp, - INSPECTOR_NOTIFICATIONS.CLOSED, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false); InspectorUI.closeInspectorUI(); } function finishUp() { - Services.obs.removeObserver(finishUp, INSPECTOR_NOTIFICATIONS.CLOSED); + Services.obs.removeObserver(finishUp, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED); doc = iframeNode = iframeBodyNode = null; gBrowser.removeCurrentTab(); finish(); diff --git a/browser/devtools/highlighter/test/browser_inspector_editor.js b/browser/devtools/highlighter/test/browser_inspector_editor.js index ca2378f80c4e..6020fd09fe17 100644 --- a/browser/devtools/highlighter/test/browser_inspector_editor.js +++ b/browser/devtools/highlighter/test/browser_inspector_editor.js @@ -3,11 +3,11 @@ /* ***** BEGIN LICENSE BLOCK ***** /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ - * + * * Contributor(s): * Rob Campbell * Mihai Sucan - * Kyle Simpson + * Kyle Simpson * * ***** END LICENSE BLOCK ***** */ @@ -25,23 +25,30 @@ function setupEditorTests() div.setAttribute("id", "foobar"); div.setAttribute("class", "barbaz"); doc.body.appendChild(div); - - Services.obs.addObserver(runEditorTests, INSPECTOR_NOTIFICATIONS.OPENED, false); + + Services.obs.addObserver(setupHTMLPanel, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); InspectorUI.toggleInspectorUI(); } +function setupHTMLPanel() +{ + Services.obs.removeObserver(setupHTMLPanel, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED); + Services.obs.addObserver(runEditorTests, InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false); + InspectorUI.treePanel.open(); +} + function runEditorTests() { - Services.obs.removeObserver(runEditorTests, INSPECTOR_NOTIFICATIONS.OPENED, false); + Services.obs.removeObserver(runEditorTests, InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY); InspectorUI.stopInspecting(); - + // setup generator for async test steps editorTestSteps = doEditorTestSteps(); - + // add step listeners - Services.obs.addObserver(doNextStep, INSPECTOR_NOTIFICATIONS.EDITOR_OPENED, false); - Services.obs.addObserver(doNextStep, INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED, false); - Services.obs.addObserver(doNextStep, INSPECTOR_NOTIFICATIONS.EDITOR_SAVED, false); + Services.obs.addObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_OPENED, false); + Services.obs.addObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED, false); + Services.obs.addObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_SAVED, false); // start the tests doNextStep(); @@ -49,16 +56,17 @@ function runEditorTests() function doEditorTestSteps() { - let editor = InspectorUI.treeBrowserDocument.getElementById("attribute-editor"); - let editorInput = InspectorUI.treeBrowserDocument.getElementById("attribute-editor-input"); + let treePanel = InspectorUI.treePanel; + let editor = treePanel.treeBrowserDocument.getElementById("attribute-editor"); + let editorInput = treePanel.treeBrowserDocument.getElementById("attribute-editor-input"); // Step 1: grab and test the attribute-value nodes in the HTML panel, then open editor - let attrValNode_id = InspectorUI.treeBrowserDocument.querySelectorAll(".nodeValue.editable[data-attributeName='id']")[0]; - let attrValNode_class = InspectorUI.treeBrowserDocument.querySelectorAll(".nodeValue.editable[data-attributeName='class']")[0]; + let attrValNode_id = treePanel.treeBrowserDocument.querySelectorAll(".nodeValue.editable[data-attributeName='id']")[0]; + let attrValNode_class = treePanel.treeBrowserDocument.querySelectorAll(".nodeValue.editable[data-attributeName='class']")[0]; is(attrValNode_id.innerHTML, "foobar", "Step 1: we have the correct `id` attribute-value node in the HTML panel"); is(attrValNode_class.innerHTML, "barbaz", "we have the correct `class` attribute-value node in the HTML panel"); - + // double-click the `id` attribute-value node to open the editor executeSoon(function() { // firing 2 clicks right in a row to simulate a double-click @@ -69,7 +77,7 @@ function doEditorTestSteps() // Step 2: validate editing session, enter new attribute value into editor, and save input - ok(InspectorUI.editingContext, "Step 2: editor session started"); + ok(InspectorUI.treePanel.editingContext, "Step 2: editor session started"); let editorVisible = editor.classList.contains("editing"); ok(editorVisible, "editor popup visible"); @@ -88,13 +96,13 @@ function doEditorTestSteps() let attrValNodeHighlighted = attrValNode_id.classList.contains("editingAttributeValue"); ok(attrValNodeHighlighted, "`id` attribute-value node is editor-highlighted"); - is(InspectorUI.editingContext.repObj, div, "editor session has correct reference to div"); - is(InspectorUI.editingContext.attrObj, attrValNode_id, "editor session has correct reference to `id` attribute-value node in HTML panel"); - is(InspectorUI.editingContext.attrName, "id", "editor session knows correct attribute-name"); + is(treePanel.editingContext.repObj, div, "editor session has correct reference to div"); + is(treePanel.editingContext.attrObj, attrValNode_id, "editor session has correct reference to `id` attribute-value node in HTML panel"); + is(treePanel.editingContext.attrName, "id", "editor session knows correct attribute-name"); editorInput.value = "Hello World"; editorInput.focus(); - + // hit to save the inputted value executeSoon(function() { EventUtils.synthesizeKey("VK_RETURN", {}, attrValNode_id.ownerDocument.defaultView); @@ -106,7 +114,7 @@ function doEditorTestSteps() // Step 3: validate that the previous editing session saved correctly, then open editor on `class` attribute value - ok(!InspectorUI.editingContext, "Step 3: editor session ended"); + ok(!treePanel.editingContext, "Step 3: editor session ended"); editorVisible = editor.classList.contains("editing"); ok(!editorVisible, "editor popup hidden"); attrValNodeHighlighted = attrValNode_id.classList.contains("editingAttributeValue"); @@ -124,16 +132,16 @@ function doEditorTestSteps() // Step 4: enter value into editor, then hit to discard it - ok(InspectorUI.editingContext, "Step 4: editor session started"); + ok(treePanel.editingContext, "Step 4: editor session started"); editorVisible = editor.classList.contains("editing"); ok(editorVisible, "editor popup visible"); - - is(InspectorUI.editingContext.attrObj, attrValNode_class, "editor session has correct reference to `class` attribute-value node in HTML panel"); - is(InspectorUI.editingContext.attrName, "class", "editor session knows correct attribute-name"); + + is(treePanel.editingContext.attrObj, attrValNode_class, "editor session has correct reference to `class` attribute-value node in HTML panel"); + is(treePanel.editingContext.attrName, "class", "editor session knows correct attribute-name"); editorInput.value = "Hello World"; editorInput.focus(); - + // hit to discard the inputted value executeSoon(function() { EventUtils.synthesizeKey("VK_ESCAPE", {}, attrValNode_class.ownerDocument.defaultView); @@ -143,7 +151,7 @@ function doEditorTestSteps() // Step 5: validate that the previous editing session discarded correctly, then open editor on `id` attribute value again - ok(!InspectorUI.editingContext, "Step 5: editor session ended"); + ok(!treePanel.editingContext, "Step 5: editor session ended"); editorVisible = editor.classList.contains("editing"); ok(!editorVisible, "editor popup hidden"); is(div.getAttribute("class"), "barbaz", "`class` attribute-value *not* updated"); @@ -159,15 +167,15 @@ function doEditorTestSteps() // Step 6: validate that editor opened again, then test double-click inside of editor (should do nothing) - ok(InspectorUI.editingContext, "Step 6: editor session started"); + ok(treePanel.editingContext, "Step 6: editor session started"); editorVisible = editor.classList.contains("editing"); ok(editorVisible, "editor popup visible"); - + // double-click on the editor input box executeSoon(function() { // firing 2 clicks right in a row to simulate a double-click EventUtils.synthesizeMouse(editorInput, 2, 2, {clickCount: 2}, editorInput.ownerDocument.defaultView); - + // since the previous double-click is supposed to do nothing, // wait a brief moment, then move on to the next step executeSoon(function() { @@ -178,12 +186,12 @@ function doEditorTestSteps() yield; // End of Step 6 - // Step 7: validate that editing session is still correct, then enter a value and try a click + // Step 7: validate that editing session is still correct, then enter a value and try a click // outside of editor (should cancel the editing session) - ok(InspectorUI.editingContext, "Step 7: editor session still going"); + ok(treePanel.editingContext, "Step 7: editor session still going"); editorVisible = editor.classList.contains("editing"); ok(editorVisible, "editor popup still visible"); - + editorInput.value = "all your base are belong to us"; // single-click the `class` attribute-value node @@ -195,18 +203,18 @@ function doEditorTestSteps() // Step 8: validate that the editor was closed and that the editing was not saved - ok(!InspectorUI.editingContext, "Step 8: editor session ended"); + ok(!treePanel.editingContext, "Step 8: editor session ended"); editorVisible = editor.classList.contains("editing"); ok(!editorVisible, "editor popup hidden"); is(div.getAttribute("id"), "Hello World", "`id` attribute-value *not* updated"); is(attrValNode_id.innerHTML, "Hello World", "attribute-value node in HTML panel *not* updated"); - + // End of Step 8 // end of all steps, so clean up - Services.obs.removeObserver(doNextStep, INSPECTOR_NOTIFICATIONS.EDITOR_OPENED, false); - Services.obs.removeObserver(doNextStep, INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED, false); - Services.obs.removeObserver(doNextStep, INSPECTOR_NOTIFICATIONS.EDITOR_SAVED, false); + Services.obs.removeObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_OPENED, false); + Services.obs.removeObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_CLOSED, false); + Services.obs.removeObserver(doNextStep, InspectorUI.INSPECTOR_NOTIFICATIONS.EDITOR_SAVED, false); executeSoon(finishUp); } diff --git a/browser/devtools/highlighter/test/browser_inspector_highlighter.js b/browser/devtools/highlighter/test/browser_inspector_highlighter.js index 65fb0ca797d5..9d87098314ab 100644 --- a/browser/devtools/highlighter/test/browser_inspector_highlighter.js +++ b/browser/devtools/highlighter/test/browser_inspector_highlighter.js @@ -80,18 +80,18 @@ function setupHighlighterTests() h1 = doc.querySelectorAll("h1")[0]; ok(h1, "we have the header node"); Services.obs.addObserver(runSelectionTests, - INSPECTOR_NOTIFICATIONS.OPENED, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); InspectorUI.toggleInspectorUI(); } function runSelectionTests() { Services.obs.removeObserver(runSelectionTests, - INSPECTOR_NOTIFICATIONS.OPENED, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); executeSoon(function() { Services.obs.addObserver(performTestComparisons, - INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); EventUtils.synthesizeMouse(h1, 2, 2, {type: "mousemove"}, content); }); } @@ -99,7 +99,7 @@ function runSelectionTests() function performTestComparisons(evt) { Services.obs.removeObserver(performTestComparisons, - INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); InspectorUI.stopInspecting(); ok(InspectorUI.highlighter.isHighlighting, "highlighter is highlighting"); diff --git a/browser/devtools/highlighter/test/browser_inspector_iframeTest.js b/browser/devtools/highlighter/test/browser_inspector_iframeTest.js index afd9a933a828..7b53b64cf200 100644 --- a/browser/devtools/highlighter/test/browser_inspector_iframeTest.js +++ b/browser/devtools/highlighter/test/browser_inspector_iframeTest.js @@ -43,7 +43,6 @@ let div1; let div2; let iframe1; let iframe2; -let highlighterFrame; function createDocument() { @@ -87,28 +86,27 @@ function moveMouseOver(aElement) function setupIframeTests() { Services.obs.addObserver(runIframeTests, - INSPECTOR_NOTIFICATIONS.OPENED, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); InspectorUI.openInspectorUI(); } function runIframeTests() { Services.obs.removeObserver(runIframeTests, - INSPECTOR_NOTIFICATIONS.OPENED, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); Services.obs.addObserver(performTestComparisons1, - INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); - highlighterFrame = InspectorUI.highlighter.iframe; executeSoon(moveMouseOver.bind(this, div1)); } function performTestComparisons1() { Services.obs.removeObserver(performTestComparisons1, - INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); Services.obs.addObserver(performTestComparisons2, - INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); is(InspectorUI.selection, div1, "selection matches div1 node"); is(InspectorUI.highlighter.highlitNode, div1, "highlighter matches selection"); @@ -119,7 +117,7 @@ function performTestComparisons1() function performTestComparisons2() { Services.obs.removeObserver(performTestComparisons2, - INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); is(InspectorUI.selection, div2, "selection matches div2 node"); is(InspectorUI.highlighter.highlitNode, div2, "highlighter matches selection"); diff --git a/browser/devtools/highlighter/test/browser_inspector_initialization.js b/browser/devtools/highlighter/test/browser_inspector_initialization.js index 657d32f42aa9..63e77f09ef87 100644 --- a/browser/devtools/highlighter/test/browser_inspector_initialization.js +++ b/browser/devtools/highlighter/test/browser_inspector_initialization.js @@ -38,6 +38,7 @@ * ***** END LICENSE BLOCK ***** */ let doc; let salutation; +let closing; function createDocument() { @@ -60,24 +61,35 @@ function startInspectorTests() { ok(InspectorUI, "InspectorUI variable exists"); Services.obs.addObserver(runInspectorTests, - INSPECTOR_NOTIFICATIONS.OPENED, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); InspectorUI.toggleInspectorUI(); } function runInspectorTests() { Services.obs.removeObserver(runInspectorTests, - INSPECTOR_NOTIFICATIONS.OPENED, false); - Services.obs.addObserver(runContextMenuTest, - INSPECTOR_NOTIFICATIONS.CLOSED, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED); + Services.obs.addObserver(treePanelTests, + InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false); + ok(InspectorUI.toolbar, "we have the toolbar."); ok(!InspectorUI.toolbar.hidden, "toolbar is visible"); - let iframe = document.getElementById("inspector-tree-iframe"); - is(InspectorUI.treeIFrame, iframe, "Inspector IFrame matches"); ok(InspectorUI.inspecting, "Inspector is inspecting"); - ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open"); + ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open"); ok(InspectorUI.highlighter, "Highlighter is up"); + InspectorUI.treePanel.open(); +} + +function treePanelTests() +{ + Services.obs.removeObserver(treePanelTests, + InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY); + Services.obs.addObserver(runContextMenuTest, + InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false); + + ok(InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is open"); + executeSoon(function() { InspectorUI.closeInspectorUI(); }); @@ -85,8 +97,8 @@ function runInspectorTests() function runContextMenuTest() { - Services.obs.removeObserver(runContextMenuTest, INSPECTOR_NOTIFICATIONS.CLOSED, false); - Services.obs.addObserver(inspectNodesFromContextTest, INSPECTOR_NOTIFICATIONS.OPENED, false); + Services.obs.removeObserver(runContextMenuTest, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false); + Services.obs.addObserver(inspectNodesFromContextTest, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); salutation = doc.getElementById("salutation"); ok(salutation, "hello, context menu test!"); let eventDeets = { type : "contextmenu", button : 2 }; @@ -96,7 +108,7 @@ function runContextMenuTest() ok(contextInspectMenuItem, "we have the inspect context menu item"); EventUtils.synthesizeMouse(salutation, 2, 2, eventDeets); is(contextMenu.state, "showing", "context menu is open"); - is(contextInspectMenuItem.hidden, !InspectorUI.enabled, "is context menu item enabled?"); + is(!contextInspectMenuItem.hidden, gPrefService.getBoolPref("devtools.inspector.enabled"), "is context menu item enabled?"); contextMenu.hidePopup(); executeSoon(function() { InspectorUI.openInspectorUI(salutation); @@ -105,26 +117,65 @@ function runContextMenuTest() function inspectNodesFromContextTest() { - Services.obs.removeObserver(inspectNodesFromContextTest, INSPECTOR_NOTIFICATIONS.OPENED, false); - Services.obs.addObserver(finishInspectorTests, INSPECTOR_NOTIFICATIONS.CLOSED, false); + Services.obs.removeObserver(inspectNodesFromContextTest, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); + Services.obs.addObserver(openInspectorForContextTest, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false); ok(!InspectorUI.inspecting, "Inspector is not actively highlighting"); is(InspectorUI.selection, salutation, "Inspector is highlighting salutation"); - ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open"); + ok(!InspectorUI.isTreePanelOpen, "Inspector Tree Panel is closed"); // TODO: These tests depend on the style inspector patches. todo(InspectorUI.isStylePanelOpen, "Inspector Style Panel is open"); - todo(InspectorUI.isDOMPanelOpen, "Inspector DOM Panel is open"); - InspectorUI.closeInspectorUI(true); + executeSoon(function() { + InspectorUI.closeInspectorUI(true); + }); +} + +function openInspectorForContextTest() +{ + Services.obs.removeObserver(openInspectorForContextTest, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED); + Services.obs.addObserver(inspectNodesFromContextTestWhileOpen, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); + executeSoon(function() { + InspectorUI.openInspectorUI(salutation); + }); +} + +function inspectNodesFromContextTestWhileOpen() +{ + Services.obs.removeObserver(inspectNodesFromContextTestWhileOpen, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED); + Services.obs.addObserver(inspectNodesFromContextTestTrap, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); + Services.obs.addObserver(inspectNodesFromContextTestHighlight, InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); + is(InspectorUI.selection, salutation, "Inspector is highlighting salutation"); + closing = doc.getElementById("closing"); + ok(closing, "we have the closing statement"); + executeSoon(function() { + InspectorUI.openInspectorUI(closing); + }); +} + +function inspectNodesFromContextTestHighlight() +{ + Services.obs.removeObserver(inspectNodesFromContextTestHighlight, InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING); + Services.obs.addObserver(finishInspectorTests, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false); + is(InspectorUI.selection, closing, "InspectorUI.selection is header"); + executeSoon(function() { + InspectorUI.closeInspectorUI(true); + }); +} + +function inspectNodesFromContextTestTrap() +{ + Services.obs.removeObserver(inspectNodesFromContextTestTrap, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED); + ok(false, "Inspector UI has been opened again. We Should Not Be Here!"); } function finishInspectorTests() { Services.obs.removeObserver(finishInspectorTests, - INSPECTOR_NOTIFICATIONS.CLOSED, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED); ok(!InspectorUI.highlighter, "Highlighter is gone"); - ok(!InspectorUI.isTreePanelOpen, "Inspector Tree Panel is closed"); + ok(!InspectorUI.treePanel, "Inspector Tree Panel is closed"); ok(!InspectorUI.inspecting, "Inspector is not inspecting"); - ok(InspectorUI.toolbar.hidden, "toolbar is hidden"); + ok(!InspectorUI.toolbar, "toolbar is hidden"); gBrowser.removeCurrentTab(); finish(); diff --git a/browser/devtools/highlighter/test/browser_inspector_registertools.js b/browser/devtools/highlighter/test/browser_inspector_registertools.js index d8f24ed1973f..0f1f48412c56 100644 --- a/browser/devtools/highlighter/test/browser_inspector_registertools.js +++ b/browser/devtools/highlighter/test/browser_inspector_registertools.js @@ -39,16 +39,20 @@ let doc; let h1; +let p2; +let toolsLength = 0; +let toolEvents = 0; let tool1; let tool2; let tool3; +let initToolsMethod = InspectorUI.initTools; function createDocument() { let div = doc.createElement("div"); - let h1 = doc.createElement("h1"); + h1 = doc.createElement("h1"); let p1 = doc.createElement("p"); - let p2 = doc.createElement("p"); + p2 = doc.createElement("p"); let div2 = doc.createElement("div"); let p3 = doc.createElement("p"); doc.title = "Inspector Tree Selection Test"; @@ -79,73 +83,70 @@ function createDocument() function setupHighlighterTests() { - h1 = doc.querySelectorAll("h1")[0]; ok(h1, "we have the header node"); - Services.obs.addObserver(inspectorOpen, "inspector-opened", false); + Services.obs.addObserver(inspectorOpen, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); + registerTools(); InspectorUI.toggleInspectorUI(); } function inspectorOpen() { info("we received the inspector-opened notification"); - Services.obs.removeObserver(inspectorOpen, "inspector-opened", false); - Services.obs.addObserver(startToolTests, "inspector-highlighting", false); - let rect = h1.getBoundingClientRect(); - executeSoon(function() { - EventUtils.synthesizeMouse(h1, 2, 2, {type: "mousemove"}, content); - }); + Services.obs.removeObserver(inspectorOpen, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED); + toolsLength = InspectorUI.tools.length; + toolEvents = InspectorUI.toolEvents.length; + info("tools registered"); + Services.obs.addObserver(startToolTests, InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); + InspectorUI.inspectNode(h1); } function startToolTests(evt) { - info("we received the inspector-highlighting notification"); - Services.obs.removeObserver(startToolTests, "inspector-highlighting", false); + Services.obs.removeObserver(startToolTests, InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING); InspectorUI.stopInspecting(); - info("Getting InspectorUI.tools"); let tools = InspectorUI.tools; + tool1 = InspectorUI.tools["tool_1"]; tool2 = InspectorUI.tools["tool_2"]; tool3 = InspectorUI.tools["tool_3"]; info("Checking panel states 1"); - ok(tool1.context.panelIsClosed, "Panel 1 is closed"); - ok(tool2.context.panelIsClosed, "Panel 2 is closed"); - ok(tool3.context.panelIsClosed, "Panel 3 is closed"); + ok(!tool1.isOpen, "Panel 1 is closed"); + ok(!tool2.isOpen, "Panel 2 is closed"); + ok(!tool3.isOpen, "Panel 3 is closed"); info("Calling show method for all tools"); - tool1.onShow.apply(tool1.context, [h1]); - tool2.onShow.apply(tool2.context, [h1]); - tool3.onShow.apply(tool3.context, [h1]); + InspectorUI.toolShow(tool1); + InspectorUI.toolShow(tool2); + InspectorUI.toolShow(tool3); info("Checking panel states 2"); - ok(tool1.context.panelIsOpen, "Panel 1 is open"); - ok(tool2.context.panelIsOpen, "Panel 2 is open"); - ok(tool3.context.panelIsOpen, "Panel 3 is open"); + ok(tool1.isOpen, "Panel 1 is open"); + ok(tool2.isOpen, "Panel 2 is open"); + ok(tool3.isOpen, "Panel 3 is open"); - info("Calling selectNode method for all tools"); - tool1.onSelect.apply(tool1.context, [h1]); - tool2.onSelect.apply(tool2.context, [h1]); - tool3.onSelect.apply(tool3.context, [h1]); + info("Calling selectNode method for all tools, should see 3 selects"); + InspectorUI.inspectNode(p2); info("Calling hide method for all tools"); - tool1.onHide.apply(tool1.context, [h1]); - tool2.onHide.apply(tool2.context, [h1]); - tool3.onHide.apply(tool3.context, [h1]); - + InspectorUI.toolHide(tool1); + InspectorUI.toolHide(tool2); + InspectorUI.toolHide(tool3); + info("Checking panel states 3"); - ok(tool1.context.panelIsClosed, "Panel 1 is closed"); - ok(tool2.context.panelIsClosed, "Panel 2 is closed"); - ok(tool3.context.panelIsClosed, "Panel 3 is closed"); + ok(!tool1.isOpen, "Panel 1 is closed"); + ok(!tool2.isOpen, "Panel 2 is closed"); + ok(!tool3.isOpen, "Panel 3 is closed"); info("Showing tools 1 & 3"); - tool1.onShow.apply(tool1.context, [h1]); - tool3.onShow.apply(tool3.context, [h1]); + InspectorUI.toolShow(tool1); + InspectorUI.toolShow(tool3); info("Checking panel states 4"); - ok(tool1.context.panelIsOpen, "Panel 1 is open"); - ok(tool2.context.panelIsClosed, "Panel 2 is closed"); - ok(tool3.context.panelIsOpen, "Panel 3 is open"); + ok(tool1.isOpen, "Panel 1 is open"); + ok(!tool2.isOpen, "Panel 2 is closed"); + ok(tool3.isOpen, "Panel 3 is open"); gBrowser.selectedTab = gBrowser.addTab(); gBrowser.selectedBrowser.addEventListener("load", function() { @@ -160,24 +161,52 @@ function testSecondTab() { info("Opened second tab"); info("Checking panel states 5"); - ok(tool1.context.panelIsClosed, "Panel 1 is closed"); - ok(tool2.context.panelIsClosed, "Panel 2 is closed"); - ok(tool3.context.panelIsClosed, "Panel 3 is closed"); + + let tools = InspectorUI.tools; + ok(!(tool1 in tools), "Panel 1 not in tools"); + ok(!(tool2 in tools), "Panel 2 not in tools"); + ok(!(tool3 in tools), "Panel 3 not in tools"); info("Closing current tab"); + Services.obs.addObserver(testOriginalTab, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); gBrowser.removeCurrentTab(); +} +function testOriginalTab() +{ + Services.obs.removeObserver(testOriginalTab, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED); info("Checking panel states 6"); - ok(tool1.context.panelIsOpen, "Panel 1 is open"); - ok(tool2.context.panelIsClosed, "Panel 2 is closed"); - ok(tool3.context.panelIsOpen, "Panel 3 is open"); - executeSoon(finishUp); + info("Tools: " + InspectorUI.tools); + // reacquaint ourselves with our tools + tool1 = InspectorUI.tools["tool_1"]; + tool2 = InspectorUI.tools["tool_2"]; + tool3 = InspectorUI.tools["tool_3"]; + + ok(tool1.isOpen, "Panel 1 is open after reactivation"); + ok(!tool2.isOpen, "Panel 2 is closed after reactivation"); + ok(tool3.isOpen, "Panel 3 is open after reactivation"); + + Services.obs.addObserver(unregisterTools, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false); + InspectorUI.closeInspectorUI(true); +} + +function unregisterTools() +{ + Services.obs.removeObserver(unregisterTools, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED); + let tools = InspectorUI.tools; + + ok(!(tool1 in tools), "Tool 1 removed"); + ok(!(tool2 in tools), "Tool 2 removed"); + ok(!(tool3 in tools), "Tool 3 removed"); + is(tools.length, toolsLength, "Number of Registered Tools matches original"); + is(InspectorUI.toolEvents.length, toolEvents, "Number of tool events matches original"); + finishUp(); } function finishUp() { - InspectorUI.closeInspectorUI(true); gBrowser.removeCurrentTab(); + InspectorUI.initTools = initToolsMethod; finish(); } @@ -188,7 +217,7 @@ function test() gBrowser.selectedBrowser.addEventListener("load", function() { gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true); doc = content.document; - waitForFocus(registerTools, content); + waitForFocus(createDocument, content); }, true); content.location = "data:text/html,registertool tests for inspector"; @@ -196,10 +225,12 @@ function test() function registerTools() { - createDocument(); - registerTool(new testTool("tool_1", "Tool 1", "Tool 1 tooltip", "I")); - registerTool(new testTool("tool_2", "Tool 2", "Tool 2 tooltip", "J")); - registerTool(new testTool("tool_3", "Tool 3", "Tool 3 tooltip", "K")); + InspectorUI.initTools = function() { + info("(re)registering tools"); + registerTool(new testTool("tool_1", "Tool 1", "Tool 1 tooltip", "I")); + registerTool(new testTool("tool_2", "Tool 2", "Tool 2 tooltip", "J")); + registerTool(new testTool("tool_3", "Tool 3", "Tool 3 tooltip", "K")); + } } function registerTool(aTool) @@ -210,10 +241,11 @@ function registerTool(aTool) tooltiptext: aTool.tooltip, accesskey: aTool.accesskey, context: aTool, + get isOpen() aTool.isOpen(), onSelect: aTool.selectNode, - onShow: aTool.show, - onHide: aTool.hide, - panel: aTool.panel + show: aTool.show, + hide: aTool.hide, + unregister: aTool.destroy, }); } @@ -223,19 +255,13 @@ function testTool(aToolId, aLabel, aTooltip, aAccesskey) this.id = aToolId; this.label = aLabel; this.tooltip = aTooltip; - this.accesskey = aAccesskey - this.panel = this.createPanel(); + this.accesskey = aAccesskey; + this._isOpen = false; } testTool.prototype = { - get panelIsOpen() - { - return this.panel.state == "open" || this.panel.state == "showing"; - }, - - get panelIsClosed() - { - return this.panel.state == "closed" || this.panel.state == "hiding"; + isOpen: function BIR_isOpen() { + return this._isOpen; }, selectNode: function BIR_selectNode(aNode) { @@ -244,32 +270,23 @@ testTool.prototype = { }, show: function BIR_show(aNode) { - this.panel.openPopup(gBrowser.selectedBrowser, - "end_before", 0, 20, false, false); + this._isOpen = true; is(InspectorUI.selection, aNode, "show: currently selected node was passed: " + this.id); }, hide: function BIR_hide() { info(this.id + " hide"); - this.panel.hidePopup(); + this._isOpen = false; }, - createPanel: function BIR_createPanel() { - let popupSet = document.getElementById("mainPopupSet"); - let ns = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; - let panel = this.panel = document.createElementNS(ns, "panel"); - panel.setAttribute("orient", "vertical"); - panel.setAttribute("noautofocus", "true"); - panel.setAttribute("noautohide", "true"); - panel.setAttribute("titlebar", "normal"); - panel.setAttribute("close", "true"); - panel.setAttribute("label", "Panel for " + this.id); - panel.setAttribute("width", 200); - panel.setAttribute("height", 400); - popupSet.appendChild(panel); - - ok(panel.parentNode == popupSet, "Panel created and appended successfully"); - return panel; + destroy: function BIR_destroy() { + info("tool destroyed " + this.id); + if (this.isOpen()) + this.hide(); + delete this.id; + delete this.label; + delete this.tooltip; + delete this.accesskey; }, }; diff --git a/browser/devtools/highlighter/test/browser_inspector_scrolling.js b/browser/devtools/highlighter/test/browser_inspector_scrolling.js index 5f7e67ce8f2c..b395681edca4 100644 --- a/browser/devtools/highlighter/test/browser_inspector_scrolling.js +++ b/browser/devtools/highlighter/test/browser_inspector_scrolling.js @@ -65,16 +65,16 @@ function createDocument() function toggleInspector() { - Services.obs.addObserver(inspectNode, INSPECTOR_NOTIFICATIONS.OPENED, false); + Services.obs.addObserver(inspectNode, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); InspectorUI.toggleInspectorUI(); } function inspectNode() { Services.obs.removeObserver(inspectNode, - INSPECTOR_NOTIFICATIONS.OPENED, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); Services.obs.addObserver(performScrollingTest, - INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); executeSoon(function() { InspectorUI.inspectNode(div); @@ -84,7 +84,7 @@ function inspectNode() function performScrollingTest() { Services.obs.removeObserver(performScrollingTest, - INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); EventUtils.synthesizeMouseScroll(div, 10, 10, {axis:"vertical", delta:50, type:"MozMousePixelScroll"}, diff --git a/browser/devtools/highlighter/test/browser_inspector_store.js b/browser/devtools/highlighter/test/browser_inspector_store.js index 249962ede716..b3ee38da2e36 100644 --- a/browser/devtools/highlighter/test/browser_inspector_store.js +++ b/browser/devtools/highlighter/test/browser_inspector_store.js @@ -39,8 +39,8 @@ function test() { - ok(window.InspectorUI, "InspectorUI variable exists"); - ok(!InspectorUI.inspecting, "Inspector is not highlighting"); + let InspectorStore = InspectorUI.store; + is(InspectorStore.length, 0, "InspectorStore is empty"); ok(InspectorStore.isEmpty(), "InspectorStore is empty (confirmed)"); is(typeof InspectorStore.store, "object", diff --git a/browser/devtools/highlighter/test/browser_inspector_tab_switch.js b/browser/devtools/highlighter/test/browser_inspector_tab_switch.js index f40d430e5b33..9b09def410cd 100644 --- a/browser/devtools/highlighter/test/browser_inspector_tab_switch.js +++ b/browser/devtools/highlighter/test/browser_inspector_tab_switch.js @@ -47,23 +47,23 @@ function inspectorTabOpen1() { ok(window.InspectorUI, "InspectorUI variable exists"); ok(!InspectorUI.inspecting, "Inspector is not highlighting"); - ok(InspectorStore.isEmpty(), "InspectorStore is empty"); + ok(InspectorUI.store.isEmpty(), "Inspector.store is empty"); Services.obs.addObserver(inspectorUIOpen1, - INSPECTOR_NOTIFICATIONS.OPENED, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); InspectorUI.openInspectorUI(); } function inspectorUIOpen1() { Services.obs.removeObserver(inspectorUIOpen1, - INSPECTOR_NOTIFICATIONS.OPENED, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); // Make sure the inspector is open. ok(InspectorUI.inspecting, "Inspector is highlighting"); - ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open"); - ok(!InspectorStore.isEmpty(), "InspectorStore is not empty"); - is(InspectorStore.length, 1, "InspectorStore.length = 1"); + ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open"); + ok(!InspectorUI.store.isEmpty(), "InspectorUI.store is not empty"); + is(InspectorUI.store.length, 1, "Inspector.store.length = 1"); // Highlight a node. div = content.document.getElementsByTagName("div")[0]; @@ -87,13 +87,13 @@ function inspectorTabOpen2() { // Make sure the inspector is closed. ok(!InspectorUI.inspecting, "Inspector is not highlighting"); - ok(!InspectorUI.isPanelOpen, "Inspector Tree Panel is closed"); - is(InspectorStore.length, 1, "InspectorStore.length = 1"); + ok(!InspectorUI.treePanel, "Inspector Tree Panel is closed"); + is(InspectorUI.store.length, 1, "Inspector.store.length = 1"); // Activate the inspector again. executeSoon(function() { Services.obs.addObserver(inspectorUIOpen2, - INSPECTOR_NOTIFICATIONS.OPENED, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); InspectorUI.openInspectorUI(); }); } @@ -101,12 +101,12 @@ function inspectorTabOpen2() function inspectorUIOpen2() { Services.obs.removeObserver(inspectorUIOpen2, - INSPECTOR_NOTIFICATIONS.OPENED, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); // Make sure the inspector is open. ok(InspectorUI.inspecting, "Inspector is highlighting"); - ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open"); - is(InspectorStore.length, 2, "InspectorStore.length = 2"); + ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open"); + is(InspectorUI.store.length, 2, "Inspector.store.length = 2"); // Disable highlighting. InspectorUI.toggleInspection(); @@ -115,7 +115,7 @@ function inspectorUIOpen2() // Switch back to tab 1. executeSoon(function() { Services.obs.addObserver(inspectorFocusTab1, - INSPECTOR_NOTIFICATIONS.OPENED, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); gBrowser.selectedTab = tab1; }); } @@ -123,29 +123,78 @@ function inspectorUIOpen2() function inspectorFocusTab1() { Services.obs.removeObserver(inspectorFocusTab1, - INSPECTOR_NOTIFICATIONS.OPENED, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); // Make sure the inspector is still open. ok(InspectorUI.inspecting, "Inspector is highlighting"); - ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open"); - is(InspectorStore.length, 2, "InspectorStore.length = 2"); + ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open"); + is(InspectorUI.store.length, 2, "Inspector.store.length = 2"); + is(InspectorUI.selection, div, "selection matches the div element"); + + Services.obs.addObserver(inspectorOpenTreePanelTab1, + InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false); + + InspectorUI.treePanel.open(); +} + +function inspectorOpenTreePanelTab1() +{ + Services.obs.removeObserver(inspectorOpenTreePanelTab1, + InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY); + + ok(InspectorUI.inspecting, "Inspector is highlighting"); + ok(InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is open"); + is(InspectorUI.store.length, 2, "Inspector.store.length = 2"); is(InspectorUI.selection, div, "selection matches the div element"); // Switch back to tab 2. Services.obs.addObserver(inspectorFocusTab2, - INSPECTOR_NOTIFICATIONS.OPENED, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); gBrowser.selectedTab = tab2; } function inspectorFocusTab2() { Services.obs.removeObserver(inspectorFocusTab2, - INSPECTOR_NOTIFICATIONS.OPENED, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); // Make sure the inspector is still open. ok(!InspectorUI.inspecting, "Inspector is not highlighting"); - ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open"); - is(InspectorStore.length, 2, "InspectorStore.length = 2"); + ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open"); + is(InspectorUI.store.length, 2, "Inspector.store.length is 2"); + isnot(InspectorUI.selection, div, "selection does not match the div element"); + + // Switch back to tab 1. + Services.obs.addObserver(inspectorSecondFocusTab1, + InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false); + gBrowser.selectedTab = tab1; +} + +function inspectorSecondFocusTab1() +{ + Services.obs.removeObserver(inspectorSecondFocusTab1, + InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY); + + ok(InspectorUI.inspecting, "Inspector is highlighting"); + ok(InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is open"); + is(InspectorUI.store.length, 2, "Inspector.store.length = 2"); + is(InspectorUI.selection, div, "selection matches the div element"); + + // Switch back to tab 2. + Services.obs.addObserver(inspectorSecondFocusTab2, + InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); + gBrowser.selectedTab = tab2; +} + +function inspectorSecondFocusTab2() +{ + Services.obs.removeObserver(inspectorSecondFocusTab2, + InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED); + + // Make sure the inspector is still open. + ok(!InspectorUI.inspecting, "Inspector is not highlighting"); + ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open"); + is(InspectorUI.store.length, 2, "Inspector.store.length is 2"); isnot(InspectorUI.selection, div, "selection does not match the div element"); // Remove tab 1. @@ -161,8 +210,8 @@ function inspectorTabUnload1(evt) // Make sure the Inspector is still open and that the state is correct. ok(!InspectorUI.inspecting, "Inspector is not highlighting"); - ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open"); - is(InspectorStore.length, 1, "InspectorStore.length = 1"); + ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open"); + is(InspectorUI.store.length, 1, "Inspector.store.length = 1"); InspectorUI.closeInspectorUI(); gBrowser.removeCurrentTab(); diff --git a/browser/devtools/highlighter/test/browser_inspector_treePanel_click.js b/browser/devtools/highlighter/test/browser_inspector_treePanel_click.js index fb66981f9153..a886e90a2c67 100644 --- a/browser/devtools/highlighter/test/browser_inspector_treePanel_click.js +++ b/browser/devtools/highlighter/test/browser_inspector_treePanel_click.js @@ -17,43 +17,47 @@ function test() { waitForFocus(setupTest, content); }, true); - content.location = "data:text/html,

"; + content.location = 'data:text/html,

'; function setupTest() { node1 = doc.querySelector("div"); node2 = doc.querySelector("p"); - Services.obs.addObserver(runTests, INSPECTOR_NOTIFICATIONS.OPENED, false); + Services.obs.addObserver(runTests, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); InspectorUI.toggleInspectorUI(); } function runTests() { - Services.obs.removeObserver(runTests, INSPECTOR_NOTIFICATIONS.OPENED); - testNode1(); + Services.obs.removeObserver(runTests, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED); + Services.obs.addObserver(testNode1, InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false); + InspectorUI.select(node1, true, true, true); + InspectorUI.openTreePanel(); } function testNode1() { - let box = InspectorUI.ioBox.createObjectBox(node1); - box.click(); - executeSoon(function() { - is(InspectorUI.selection, node1, "selection matches node"); - is(InspectorUI.highlighter.node, node1, "selection matches node"); - testNode2(); - }); + dump("testNode1\n"); + Services.obs.removeObserver(testNode1, InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY); + is(InspectorUI.selection, node1, "selection matches node"); + is(InspectorUI.highlighter.node, node1, "selection matches node"); + testNode2(); } function testNode2() { - let box = InspectorUI.ioBox.createObjectBox(node2); - box.click(); - executeSoon(function() { - is(InspectorUI.selection, node2, "selection matches node"); - is(InspectorUI.highlighter.node, node2, "selection matches node"); - Services.obs.addObserver(finishUp, INSPECTOR_NOTIFICATIONS.CLOSED, false); - InspectorUI.closeInspectorUI(); - }); + dump("testNode2\n") + Services.obs.addObserver(testHighlightingNode2, InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); + InspectorUI.treePanelSelect("node2"); + } + + function testHighlightingNode2() { + dump("testHighlightingNode2\n") + Services.obs.removeObserver(testHighlightingNode2, InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING); + is(InspectorUI.selection, node2, "selection matches node"); + is(InspectorUI.highlighter.node, node2, "selection matches node"); + Services.obs.addObserver(finishUp, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false); + InspectorUI.closeInspectorUI(); } function finishUp() { - Services.obs.removeObserver(finishUp, INSPECTOR_NOTIFICATIONS.CLOSED); + Services.obs.removeObserver(finishUp, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED); doc = node1 = node2 = null; gBrowser.removeCurrentTab(); finish(); diff --git a/browser/devtools/highlighter/test/browser_inspector_treePanel_output.js b/browser/devtools/highlighter/test/browser_inspector_treePanel_output.js index 51a27fa7aab0..fb8595039927 100644 --- a/browser/devtools/highlighter/test/browser_inspector_treePanel_output.js +++ b/browser/devtools/highlighter/test/browser_inspector_treePanel_output.js @@ -64,17 +64,26 @@ function xhr_onReadyStateChange() { xhr = null; Services.obs.addObserver(inspectorOpened, - INSPECTOR_NOTIFICATIONS.OPENED, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); InspectorUI.openInspectorUI(); } function inspectorOpened() { Services.obs.removeObserver(inspectorOpened, - INSPECTOR_NOTIFICATIONS.OPENED, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED); + + Services.obs.addObserver(treePanelOpened, InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, false); + InspectorUI.treePanel.open(); +} + +function treePanelOpened() +{ + Services.obs.removeObserver(treePanelOpened, + InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY); ok(InspectorUI.inspecting, "Inspector is highlighting"); - ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open"); + ok(InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is open"); InspectorUI.stopInspecting(); ok(!InspectorUI.inspecting, "Inspector is not highlighting"); @@ -91,23 +100,23 @@ function inspectorOpened() ok(iframeDiv, "Found the div element inside the iframe"); InspectorUI.inspectNode(iframeDiv); - ok(InspectorUI.treePanelDiv, "InspectorUI.treePanelDiv is available"); - is(InspectorUI.treePanelDiv.innerHTML.replace(/^\s+|\s+$/mg, ''), + ok(InspectorUI.treePanel.treePanelDiv, "InspectorUI.treePanelDiv is available"); + is(InspectorUI.treePanel.treePanelDiv.innerHTML.replace(/^\s+|\s+$/mg, ''), expectedResult, "treePanelDiv.innerHTML is correct"); expectedResult = null; Services.obs.addObserver(inspectorClosed, - INSPECTOR_NOTIFICATIONS.CLOSED, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false); InspectorUI.closeInspectorUI(); } function inspectorClosed() { Services.obs.removeObserver(inspectorClosed, - INSPECTOR_NOTIFICATIONS.CLOSED, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false); ok(!InspectorUI.inspecting, "Inspector is not highlighting"); - ok(!InspectorUI.isTreePanelOpen, "Inspector Tree Panel is not open"); + ok(!InspectorUI.treePanel, "Inspector Tree Panel is not open"); gBrowser.removeCurrentTab(); finish(); diff --git a/browser/devtools/highlighter/test/browser_inspector_treeSelection.js b/browser/devtools/highlighter/test/browser_inspector_treeSelection.js index ceab34750904..5437f8b60ac4 100644 --- a/browser/devtools/highlighter/test/browser_inspector_treeSelection.js +++ b/browser/devtools/highlighter/test/browser_inspector_treeSelection.js @@ -68,16 +68,16 @@ function setupSelectionTests() h1 = doc.querySelectorAll("h1")[0]; ok(h1, "we have the header node"); Services.obs.addObserver(runSelectionTests, - INSPECTOR_NOTIFICATIONS.OPENED, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); InspectorUI.openInspectorUI(); } function runSelectionTests() { Services.obs.removeObserver(runSelectionTests, - INSPECTOR_NOTIFICATIONS.OPENED, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); Services.obs.addObserver(performTestComparisons, - INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); executeSoon(function() { InspectorUI.inspectNode(h1); }); @@ -86,7 +86,7 @@ function runSelectionTests() function performTestComparisons(evt) { Services.obs.removeObserver(performTestComparisons, - INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); is(h1, InspectorUI.selection, "selection matches node"); ok(InspectorUI.highlighter.isHighlighting, "highlighter is highlighting"); diff --git a/browser/devtools/jar.mn b/browser/devtools/jar.mn index 1c18a2b5273d..f7c7a7d6ef4a 100644 --- a/browser/devtools/jar.mn +++ b/browser/devtools/jar.mn @@ -1,4 +1,5 @@ browser.jar: +* content/browser/inspector.html (highlighter/inspector.html) content/browser/NetworkPanel.xhtml (webconsole/NetworkPanel.xhtml) * content/browser/scratchpad.xul (scratchpad/scratchpad.xul) * content/browser/scratchpad.js (scratchpad/scratchpad.js) diff --git a/browser/devtools/scratchpad/Makefile.in b/browser/devtools/scratchpad/Makefile.in index 3c073560ec60..bf57db4e4f2f 100644 --- a/browser/devtools/scratchpad/Makefile.in +++ b/browser/devtools/scratchpad/Makefile.in @@ -12,11 +12,11 @@ # for the specific language governing rights and limitations under the # License. # -# The Original Code is HUDService code. +# The Original Code is Scratchpad Build Code. # -# The Initial Developer of the Original Code is Mozilla Corporation. -# -# Portions created by the Initial Developer are Copyright (C) 2010 +# The Initial Developer of the Original Code is The Mozilla Foundation. +# +# Portions created by the Initial Developer are Copyright (C) 2011 # the Initial Developer. All Rights Reserved. # # Contributor(s): @@ -44,9 +44,7 @@ VPATH = @srcdir@ include $(DEPTH)/config/autoconf.mk ifdef ENABLE_TESTS -ifneq (mobile,$(MOZ_BUILD_APP)) DIRS += test endif -endif include $(topsrcdir)/config/rules.mk diff --git a/browser/devtools/styleinspector/CssHtmlTree.jsm b/browser/devtools/styleinspector/CssHtmlTree.jsm index 5e2a5dcc6ac6..7cf71d4c24f4 100644 --- a/browser/devtools/styleinspector/CssHtmlTree.jsm +++ b/browser/devtools/styleinspector/CssHtmlTree.jsm @@ -21,9 +21,10 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): - * Joe Walker (jwalker@mozilla.com) (original author) + * Joe Walker (jwalker@mozilla.com) (Original Author) * Mihai Șucan * Michael Ratcliffe + * Rob Campbell * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or diff --git a/browser/devtools/styleinspector/StyleInspector.jsm b/browser/devtools/styleinspector/StyleInspector.jsm index 34888f89b158..230a13253d6c 100644 --- a/browser/devtools/styleinspector/StyleInspector.jsm +++ b/browser/devtools/styleinspector/StyleInspector.jsm @@ -21,7 +21,8 @@ * the Initial Developer. All Rights Reserved. * * Contributor(s): - * Mike Ratcliffe + * Mike Ratcliffe (Original Author) + * Rob Campbell * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -109,8 +110,6 @@ var StyleInspector = { let iframeReady = false; function SI_iframeOnload() { iframe.removeEventListener("load", SI_iframeOnload, true); - panel.cssLogic = new CssLogic(); - panel.cssHtmlTree = new CssHtmlTree(iframe, panel.cssLogic, panel); iframeReady = true; if (panelReady) { SI_popupShown.call(panel); @@ -124,6 +123,10 @@ var StyleInspector = { function SI_popupShown() { panelReady = true; if (iframeReady) { + if (!this.cssLogic) { + this.cssLogic = new CssLogic(); + this.cssHtmlTree = new CssHtmlTree(iframe, this.cssLogic, this); + } let selectedNode = this.selectedNode || null; this.cssLogic.highlight(selectedNode); this.cssHtmlTree.highlight(selectedNode); @@ -162,12 +165,9 @@ var StyleInspector = { panel.selectNode = function SI_selectNode(aNode) { this.selectedNode = aNode; - if (this.isOpen()) { + if (this.isOpen() && !this.hasAttribute("dimmed")) { this.cssLogic.highlight(aNode); this.cssHtmlTree.highlight(aNode); - } else { - let win = Services.wm.getMostRecentWindow("navigator:browser"); - this.openPopup(win.gBrowser.selectedBrowser, "end_before", 0, 0, false, false); } }; @@ -176,6 +176,10 @@ var StyleInspector = { */ panel.destroy = function SI_destroy() { + if (!this.cssLogic) + return; + if (this.isOpen()) + this.hideTool(); this.cssLogic = null; this.cssHtmlTree = null; this.removeEventListener("popupshown", SI_popupShown); @@ -184,6 +188,37 @@ var StyleInspector = { Services.obs.notifyObservers(null, "StyleInspector-closed", null); }; + /** + * Dim or undim a panel by setting or removing a dimmed attribute. + * + * @param aState + * true = dim, false = undim + */ + panel.dimTool = function SI_dimTool(aState) + { + if (!this.isOpen()) + return; + + if (aState) { + this.setAttribute("dimmed", "true"); + } else if (this.hasAttribute("dimmed")) { + this.removeAttribute("dimmed"); + } + }; + + panel.showTool = function SI_showTool(aSelection) + { + this.selectNode(aSelection); + let win = Services.wm.getMostRecentWindow("navigator:browser"); + this.openPopup(win.gBrowser.selectedBrowser, "end_before", 0, 0, + false, false); + }; + + panel.hideTool = function SI_hideTool() + { + this.hidePopup(); + }; + /** * Is the Style Inspector initialized? * @returns {Boolean} true or false diff --git a/browser/devtools/styleinspector/test/browser/browser_styleinspector_webconsole.js b/browser/devtools/styleinspector/test/browser/browser_styleinspector_webconsole.js index aab2e2116307..a68f8915eacb 100644 --- a/browser/devtools/styleinspector/test/browser/browser_styleinspector_webconsole.js +++ b/browser/devtools/styleinspector/test/browser/browser_styleinspector_webconsole.js @@ -174,26 +174,18 @@ function styleInspectorClosedFromConsole1() { Services.obs.removeObserver(styleInspectorClosedFromConsole1, "StyleInspector-closed", false); - info("Style Inspector 1 closed"); - Services.obs.addObserver(styleInspectorClosedFromConsole2, - "StyleInspector-closed", false); -} - -function styleInspectorClosedFromConsole2() -{ - Services.obs.removeObserver(styleInspectorClosedFromConsole2, - "StyleInspector-closed", false); - info("Style Inspector 2 closed"); + info("Style Inspector 1 and 2 closed"); executeSoon(cleanUp); } function cleanUp() { - let popupSet = document.getElementById("mainPopupSet"); - ok(!popupSet.lastChild.hasAttribute("hudToolId"), + let panels = document.querySelector("panel[hudToolId]"); + ok(!panels, "all style inspector panels are now detached and ready for garbage collection"); info("cleaning up"); + doc = hudBox = stylePanels = jsterm = null; finishTest(); } diff --git a/browser/devtools/webconsole/HUDService.jsm b/browser/devtools/webconsole/HUDService.jsm index 394ac1f4d54d..4427c6abac00 100644 --- a/browser/devtools/webconsole/HUDService.jsm +++ b/browser/devtools/webconsole/HUDService.jsm @@ -165,7 +165,10 @@ const LEVELS = { info: SEVERITY_INFO, log: SEVERITY_LOG, trace: SEVERITY_LOG, - dir: SEVERITY_LOG + dir: SEVERITY_LOG, + group: SEVERITY_LOG, + groupCollapsed: SEVERITY_LOG, + groupEnd: SEVERITY_LOG }; // The lowest HTTP response code (inclusive) that is considered an error. @@ -252,6 +255,9 @@ const ERRORS = { LOG_MESSAGE_MISSING_ARGS: LOG_OUTPUT_FAILED: "Log Failure: Could not append messageNode to outputNode", }; +// The indent of a console group in pixels. +const GROUP_INDENT = 12; + /** * Implements the nsIStreamListener and nsIRequestObserver interface. Used * within the HS_httpObserverFactory function to get the response body of @@ -2005,6 +2011,20 @@ HUD_SERVICE.prototype = sourceLine = aMessage.lineNumber; break; + case "group": + case "groupCollapsed": + clipboardText = body = formatResult(args); + sourceURL = aMessage.filename; + sourceLine = aMessage.lineNumber; + hud.groupDepth++; + break; + + case "groupEnd": + if (hud.groupDepth > 0) { + hud.groupDepth--; + } + return; + default: Cu.reportError("Unknown Console API log level: " + level); return; @@ -2014,6 +2034,7 @@ HUD_SERVICE.prototype = CATEGORY_WEBDEV, LEVELS[level], body, + aHUDId, sourceURL, sourceLine, clipboardText, @@ -2076,7 +2097,8 @@ HUD_SERVICE.prototype = let chromeDocument = hud.HUDBox.ownerDocument; let message = stringBundle.GetStringFromName("ConsoleAPIDisabled"); let node = ConsoleUtils.createMessageNode(chromeDocument, CATEGORY_JS, - SEVERITY_WARNING, message); + SEVERITY_WARNING, message, + aHUDId); ConsoleUtils.outputMessageNode(node, aHUDId); }, @@ -2116,6 +2138,7 @@ HUD_SERVICE.prototype = aCategory, severity, aScriptError.errorMessage, + hudId, aScriptError.sourceName, aScriptError.lineNumber); @@ -2576,6 +2599,7 @@ HUD_SERVICE.prototype = CATEGORY_NETWORK, SEVERITY_LOG, msgNode, + hudId, null, null, clipboardText); @@ -3081,6 +3105,11 @@ HeadsUpDisplay.prototype = { consolePanel: null, + /** + * The nesting depth of the currently active console group. + */ + groupDepth: 0, + get mainPopupSet() { return this.chromeDocument.getElementById("mainPopupSet"); @@ -4438,7 +4467,7 @@ function JSTermHelper(aJSTerm) if (!errstr) { let stylePanel = StyleInspector.createPanel(); stylePanel.setAttribute("hudToolId", aJSTerm.hudId); - stylePanel.selectNode(aNode); + stylePanel.showTool(aNode); } else { aJSTerm.writeOutput(errstr + "\n", CATEGORY_OUTPUT, SEVERITY_ERROR); } @@ -4752,7 +4781,8 @@ JSTerm.prototype = { let node = ConsoleUtils.createMessageNode(this.parentNode.ownerDocument, CATEGORY_OUTPUT, SEVERITY_LOG, - aOutputString); + aOutputString, + this.hudId); let linkNode = node.querySelector(".webconsole-msg-body"); @@ -4799,7 +4829,7 @@ JSTerm.prototype = { { let node = ConsoleUtils.createMessageNode(this.parentNode.ownerDocument, aCategory, aSeverity, - aOutputMessage); + aOutputMessage, this.hudId); ConsoleUtils.outputMessageNode(node, this.hudId); }, @@ -5540,6 +5570,8 @@ ConsoleUtils = { * The severity of the message: one of the SEVERITY_* constants; * @param string|nsIDOMNode aBody * The body of the message, either a simple string or a DOM node. + * @param number aHUDId + * The HeadsUpDisplay ID. * @param string aSourceURL [optional] * The URL of the source file that emitted the error. * @param number aSourceLine [optional] @@ -5557,8 +5589,8 @@ ConsoleUtils = { */ createMessageNode: function ConsoleUtils_createMessageNode(aDocument, aCategory, aSeverity, - aBody, aSourceURL, aSourceLine, - aClipboardText, aLevel) { + aBody, aHUDId, aSourceURL, + aSourceLine, aClipboardText, aLevel) { if (aBody instanceof Ci.nsIDOMNode && aClipboardText == null) { throw new Error("HUDService.createMessageNode(): DOM node supplied " + "without any clipboard text"); @@ -5569,6 +5601,9 @@ ConsoleUtils = { // long multi-line messages. let iconContainer = aDocument.createElementNS(XUL_NS, "vbox"); iconContainer.classList.add("webconsole-msg-icon-container"); + // Apply the curent group by indenting appropriately. + let hud = HUDService.getHudReferenceById(aHUDId); + iconContainer.style.marginLeft = hud.groupDepth * GROUP_INDENT + "px"; // Make the icon node. It's sprited and the actual region of the image is // determined by CSS rules. @@ -6669,6 +6704,7 @@ ConsoleProgressListener.prototype = { CATEGORY_NETWORK, SEVERITY_LOG, msgNode, + this.hudId, null, null, uri.spec); diff --git a/browser/devtools/webconsole/test/browser/Makefile.in b/browser/devtools/webconsole/test/browser/Makefile.in index edad7719b014..29fb8618054a 100644 --- a/browser/devtools/webconsole/test/browser/Makefile.in +++ b/browser/devtools/webconsole/test/browser/Makefile.in @@ -145,6 +145,7 @@ _BROWSER_TEST_FILES = \ browser_webconsole_bug_653531_highlighter_console_helper.js \ browser_webconsole_bug_659907_console_dir.js \ browser_webconsole_bug_678816.js \ + browser_webconsole_bug_664131_console_group.js \ head.js \ $(NULL) diff --git a/browser/devtools/webconsole/test/browser/browser_webconsole_bug_642108_pruneTest.js b/browser/devtools/webconsole/test/browser/browser_webconsole_bug_642108_pruneTest.js index e580697e7eb4..e2ba415b8706 100644 --- a/browser/devtools/webconsole/test/browser/browser_webconsole_bug_642108_pruneTest.js +++ b/browser/devtools/webconsole/test/browser/browser_webconsole_bug_642108_pruneTest.js @@ -25,7 +25,8 @@ function populateConsoleRepeats(aHudRef) { let node = ConsoleUtils.createMessageNode(hud.ownerDocument, CATEGORY_CSS, SEVERITY_WARNING, - "css log x"); + "css log x", + aHudRef.hudId); ConsoleUtils.outputMessageNode(node, aHudRef.hudId); } } @@ -38,7 +39,8 @@ function populateConsole(aHudRef) { let node = ConsoleUtils.createMessageNode(hud.ownerDocument, CATEGORY_CSS, SEVERITY_WARNING, - "css log " + i); + "css log " + i, + aHudRef.hudId); ConsoleUtils.outputMessageNode(node, aHudRef.hudId); } } diff --git a/browser/devtools/webconsole/test/browser/browser_webconsole_bug_653531_highlighter_console_helper.js b/browser/devtools/webconsole/test/browser/browser_webconsole_bug_653531_highlighter_console_helper.js index a1a775e70a89..84c15450b275 100644 --- a/browser/devtools/webconsole/test/browser/browser_webconsole_bug_653531_highlighter_console_helper.js +++ b/browser/devtools/webconsole/test/browser/browser_webconsole_bug_653531_highlighter_console_helper.js @@ -82,18 +82,18 @@ function setupHighlighterTests() h1 = doc.querySelectorAll("h1")[0]; ok(h1, "we have the header node"); Services.obs.addObserver(runSelectionTests, - INSPECTOR_NOTIFICATIONS.OPENED, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); InspectorUI.toggleInspectorUI(); } function runSelectionTests() { Services.obs.removeObserver(runSelectionTests, - INSPECTOR_NOTIFICATIONS.OPENED, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); executeSoon(function() { Services.obs.addObserver(performTestComparisons, - INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); EventUtils.synthesizeMouse(h1, 2, 2, {type: "mousemove"}, content); }); } @@ -101,7 +101,7 @@ function runSelectionTests() function performTestComparisons(evt) { Services.obs.removeObserver(performTestComparisons, - INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); + InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false); InspectorUI.stopInspecting(); ok(InspectorUI.highlighter.isHighlighting, "highlighter is highlighting"); diff --git a/browser/devtools/webconsole/test/browser/browser_webconsole_bug_664131_console_group.js b/browser/devtools/webconsole/test/browser/browser_webconsole_bug_664131_console_group.js new file mode 100644 index 000000000000..b6fbdc5c8c30 --- /dev/null +++ b/browser/devtools/webconsole/test/browser/browser_webconsole_bug_664131_console_group.js @@ -0,0 +1,48 @@ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// Tests that console.group/groupEnd works as intended. +const GROUP_INDENT = 12; + +function test() { + addTab("data:text/html,Web Console test for bug 664131: Expand console " + + "object with group methods"); + browser.addEventListener("load", onLoad, true); +} + +function onLoad(aEvent) { + browser.removeEventListener(aEvent.type, arguments.callee, true); + + openConsole(); + let hudId = HUDService.getHudIdByWindow(content); + let hud = HUDService.hudReferences[hudId]; + outputNode = hud.outputNode; + + content.console.group("a"); + findLogEntry("a"); + let msg = outputNode.querySelectorAll(".webconsole-msg-icon-container"); + is(msg.length, 1, "one message node displayed"); + is(msg[0].style.marginLeft, GROUP_INDENT + "px", "correct group indent found"); + content.console.log("inside"); + findLogEntry("inside"); + let msg = outputNode.querySelectorAll(".webconsole-msg-icon-container"); + is(msg.length, 2, "two message nodes displayed"); + is(msg[1].style.marginLeft, GROUP_INDENT + "px", "correct group indent found"); + content.console.groupEnd("a"); + content.console.log("outside"); + findLogEntry("outside"); + let msg = outputNode.querySelectorAll(".webconsole-msg-icon-container"); + is(msg.length, 3, "three message nodes displayed"); + is(msg[2].style.marginLeft, "0px", "correct group indent found"); + content.console.groupCollapsed("b"); + findLogEntry("b"); + let msg = outputNode.querySelectorAll(".webconsole-msg-icon-container"); + is(msg.length, 4, "four message nodes displayed"); + is(msg[3].style.marginLeft, GROUP_INDENT + "px", "correct group indent found"); + + finishTest(); +} + diff --git a/browser/devtools/webconsole/test/browser/test-console-extras.html b/browser/devtools/webconsole/test/browser/test-console-extras.html index 706a1a2daaf8..ff23b6442111 100644 --- a/browser/devtools/webconsole/test/browser/test-console-extras.html +++ b/browser/devtools/webconsole/test/browser/test-console-extras.html @@ -10,9 +10,6 @@ console.assert() console.clear() console.dirxml() - console.group() - console.groupCollapsed() - console.groupEnd() console.profile() console.profileEnd() console.count() diff --git a/browser/locales/en-US/chrome/browser/inspector.properties b/browser/locales/en-US/chrome/browser/inspector.properties index 9629f3374e8a..ae1529ff0a01 100644 --- a/browser/locales/en-US/chrome/browser/inspector.properties +++ b/browser/locales/en-US/chrome/browser/inspector.properties @@ -7,3 +7,18 @@ confirmNavigationAway.buttonLeaveAccesskey=L confirmNavigationAway.buttonStay=Stay on Page confirmNavigationAway.buttonStayAccesskey=S +# LOCALIZATION NOTE (htmlPanel): Used in the Inspector tool's openInspectorUI +# method when registering the HTML panel. + +# LOCALIZATION NOTE (htmlPanel.label): This is a lable for a button that +# activates the Web Developer->Inspect UI's HTML Tree Panel. +htmlPanel.label=HTML + +# LOCALIZATION NOTE (htmlPanel.tooltiptext): The text that appears when a user +# hovers over the HTML panel's toolbar button. +htmlPanel.tooltiptext=HTML panel + +# LOCALIZATION NOTE (htmlPanel.accesskey): The key bound to the HTML panel's +# toolbar button. +htmlPanel.accesskey=H + diff --git a/browser/locales/en-US/chrome/browser/styleinspector.properties b/browser/locales/en-US/chrome/browser/styleinspector.properties index 1c4292b6043f..22d1bae9ad33 100644 --- a/browser/locales/en-US/chrome/browser/styleinspector.properties +++ b/browser/locales/en-US/chrome/browser/styleinspector.properties @@ -41,3 +41,9 @@ group.Positioning_and_Page_Flow=Positioning and Page Flow group.Borders=Borders group.Lists=Lists group.Effects_and_Other=Effects and Other + +# LOCALIZATION NOTE (style.highlighter.button): These strings are used inside +# html tree of the highlighter for the style inspector button +style.highlighter.button.label=Style +style.highlighter.accesskey=S +style.highlighter.button.tooltip=Inspect element styles diff --git a/browser/themes/gnomestripe/browser/browser.css b/browser/themes/gnomestripe/browser/browser.css index ba5b10038e50..530ff54a33a1 100644 --- a/browser/themes/gnomestripe/browser/browser.css +++ b/browser/themes/gnomestripe/browser/browser.css @@ -1942,3 +1942,12 @@ panel[dimmed="true"] { box-shadow: 0 0 0 1px black; outline-color: white; } + +/* + * need a "bumpy" background image for this! + */ +#inspector-horizontal-splitter { + background: none !important; + -moz-appearance: none; + cursor: n-resize; +} diff --git a/browser/themes/pinstripe/browser/browser.css b/browser/themes/pinstripe/browser/browser.css index 497999a5d012..d3c2fe68c815 100644 --- a/browser/themes/pinstripe/browser/browser.css +++ b/browser/themes/pinstripe/browser/browser.css @@ -2602,3 +2602,13 @@ panel[dimmed="true"] { #inspector-tools > toolbarbutton[checked]:hover:active { background-color: hsla(210,8%,5%,.2); } + +/* + * need a "bumpy" background image for this! + */ +#inspector-horizontal-splitter { + background: none !important; + -moz-appearance: none; + cursor: n-resize; +} + diff --git a/browser/themes/winstripe/browser/browser.css b/browser/themes/winstripe/browser/browser.css index 4e752e512c7a..7a5223175614 100644 --- a/browser/themes/winstripe/browser/browser.css +++ b/browser/themes/winstripe/browser/browser.css @@ -2558,3 +2558,13 @@ panel[dimmed="true"] { box-shadow: 0 0 0 1px black; outline-color: white; } + +/* + * need a "bumpy" background image for this! + */ +#inspector-horizontal-splitter { + background: none !important; + -moz-appearance: none; + cursor: n-resize; +} + diff --git a/dom/base/ConsoleAPI.js b/dom/base/ConsoleAPI.js index f0b80ac5d77a..146c6733d2a3 100644 --- a/dom/base/ConsoleAPI.js +++ b/dom/base/ConsoleAPI.js @@ -94,6 +94,15 @@ ConsoleAPI.prototype = { dir: function CA_dir() { self.notifyObservers(outerID, innerID, "dir", arguments); }, + group: function CA_group() { + self.notifyObservers(outerID, innerID, "group", self.beginGroup(arguments)); + }, + groupCollapsed: function CA_groupCollapsed() { + self.notifyObservers(outerID, innerID, "groupCollapsed", self.beginGroup(arguments)); + }, + groupEnd: function CA_groupEnd() { + self.notifyObservers(outerID, innerID, "groupEnd", arguments); + }, __exposedProps__: { log: "r", info: "r", @@ -101,7 +110,10 @@ ConsoleAPI.prototype = { error: "r", debug: "r", trace: "r", - dir: "r" + dir: "r", + group: "r", + groupCollapsed: "r", + groupEnd: "r" } }; @@ -120,6 +132,9 @@ ConsoleAPI.prototype = { debug: genPropDesc('debug'), trace: genPropDesc('trace'), dir: genPropDesc('dir'), + group: genPropDesc('group'), + groupCollapsed: genPropDesc('groupCollapsed'), + groupEnd: genPropDesc('groupEnd'), __noSuchMethod__: { enumerable: true, configurable: true, writable: true, value: function() {} }, __mozillaConsole__: { value: true } @@ -229,6 +244,13 @@ ConsoleAPI.prototype = { } return stack; + }, + + /** + * Begin a new group for logging output together. + **/ + beginGroup: function CA_beginGroup() { + return Array.prototype.join.call(arguments[0], " "); } }; diff --git a/dom/tests/browser/browser_ConsoleAPITests.js b/dom/tests/browser/browser_ConsoleAPITests.js index 9307e14c9855..a77ea6fbe42a 100644 --- a/dom/tests/browser/browser_ConsoleAPITests.js +++ b/dom/tests/browser/browser_ConsoleAPITests.js @@ -108,9 +108,52 @@ function testLocationData(aMessageObject) { is(aMessageObject.arguments[i], a, "correct arg " + i); }); - // Test finished - ConsoleObserver.destroy(); - finish(); + startGroupTest(); +} + +function startGroupTest() { + // Reset the observer function to cope with the fabricated test data. + ConsoleObserver.observe = function CO_observe(aSubject, aTopic, aData) { + try { + testConsoleGroup(aSubject.wrappedJSObject); + } catch (ex) { + // XXX Exceptions in this function currently aren't reported, because of + // some XPConnect weirdness, so report them manually + ok(false, "Exception thrown in CO_observe: " + ex); + } + }; + let button = gWindow.document.getElementById("test-groups"); + ok(button, "found #test-groups button"); + EventUtils.synthesizeMouse(button, 2, 2, {}, gWindow); +} + +function testConsoleGroup(aMessageObject) { + let messageWindow = getWindowByWindowId(aMessageObject.ID); + is(messageWindow, gWindow, "found correct window by window ID"); + + ok(aMessageObject.level == "group" || + aMessageObject.level == "groupCollapsed" || + aMessageObject.level == "groupEnd", + "expected level received"); + + is(aMessageObject.functionName, "testGroups", "functionName matches"); + ok(aMessageObject.lineNumber >= 32 && aMessageObject.lineNumber <= 34, + "lineNumber matches"); + if (aMessageObject.level == "groupCollapsed") { + ok(aMessageObject.arguments == "a group", "groupCollapsed arguments matches"); + } + else if (aMessageObject.level == "group") { + ok(aMessageObject.arguments == "b group", "group arguments matches"); + } + else if (aMessageObject.level == "groupEnd") { + ok(Array.prototype.join.call(aMessageObject.arguments, " ") == "b group", "groupEnd arguments matches"); + } + + if (aMessageObject.level == "groupEnd") { + // Test finished + ConsoleObserver.destroy(); + finish(); + } } function startTraceTest() { @@ -195,6 +238,9 @@ function consoleAPISanityTest() { ok(win.console.error, "console.error is here"); ok(win.console.trace, "console.trace is here"); ok(win.console.dir, "console.dir is here"); + ok(win.console.group, "console.group is here"); + ok(win.console.groupCollapsed, "console.groupCollapsed is here"); + ok(win.console.groupEnd, "console.groupEnd is here"); } var ConsoleObserver = { diff --git a/dom/tests/browser/test-console-api.html b/dom/tests/browser/test-console-api.html index 58dc264750f7..96066ae24773 100644 --- a/dom/tests/browser/test-console-api.html +++ b/dom/tests/browser/test-console-api.html @@ -27,6 +27,12 @@ console.warn(str); console.error(str); } + + function testGroups() { + console.groupCollapsed("a", "group"); + console.group("b", "group"); + console.groupEnd("b", "group"); + } @@ -34,5 +40,6 @@ + diff --git a/dom/tests/mochitest/general/test_consoleAPI.html b/dom/tests/mochitest/general/test_consoleAPI.html index 6ebc02fafbf7..a82ede0d7c2d 100644 --- a/dom/tests/mochitest/general/test_consoleAPI.html +++ b/dom/tests/mochitest/general/test_consoleAPI.html @@ -27,6 +27,9 @@ function doTest() { "debug": "function", "trace": "function", "dir": "function", + "group": "function", + "groupCollapsed": "function", + "groupEnd": "function", "__noSuchMethod__": "function" }; diff --git a/toolkit/mozapps/extensions/content/selectAddons.xml b/toolkit/mozapps/extensions/content/selectAddons.xml index 1813df6fe25b..2b93a7ec27a2 100644 --- a/toolkit/mozapps/extensions/content/selectAddons.xml +++ b/toolkit/mozapps/extensions/content/selectAddons.xml @@ -53,6 +53,7 @@ diff --git a/toolkit/themes/gnomestripe/mozapps/extensions/selectAddons.css b/toolkit/themes/gnomestripe/mozapps/extensions/selectAddons.css index 370474b7106f..d9666d36e529 100644 --- a/toolkit/themes/gnomestripe/mozapps/extensions/selectAddons.css +++ b/toolkit/themes/gnomestripe/mozapps/extensions/selectAddons.css @@ -114,6 +114,10 @@ display: none; } +.select-keep .addon-keep-checkbox:-moz-focusring { + outline: 1px dotted ThreeDDarkShadow; +} + .select-icon { width: 20px; } diff --git a/toolkit/themes/winstripe/mozapps/extensions/selectAddons.css b/toolkit/themes/winstripe/mozapps/extensions/selectAddons.css index 0933ee0af2d6..cbbbf2042272 100644 --- a/toolkit/themes/winstripe/mozapps/extensions/selectAddons.css +++ b/toolkit/themes/winstripe/mozapps/extensions/selectAddons.css @@ -124,6 +124,10 @@ width: 13px; } +.select-keep .addon-keep-checkbox:-moz-focusring { + outline: 1px dotted ThreeDDarkShadow; +} + .select-keep .checkbox-label-box { display: none; }