From 7885fece64d024e91da835b5446a28c7173490eb Mon Sep 17 00:00:00 2001 From: Dave Camp Date: Thu, 19 Apr 2012 11:04:46 -0700 Subject: [PATCH] Bug 707809 - Refactor creation of registered sidebar tools iframes in InspectorUI. r=robcee --- browser/devtools/highlighter/TreePanel.jsm | 23 +- browser/devtools/highlighter/inspector.jsm | 1208 ++++++++--------- browser/devtools/highlighter/test/Makefile.in | 2 - .../test/browser_inspector_changes.js | 20 +- .../browser_inspector_duplicate_ruleview.js | 128 -- .../test/browser_inspector_editor.js | 2 +- .../test/browser_inspector_initialization.js | 32 +- .../browser_inspector_pseudoclass_lock.js | 26 +- .../test/browser_inspector_registertools.js | 254 ---- .../test/browser_inspector_ruleviewstore.js | 23 +- .../test/browser_inspector_sidebarstate.js | 19 +- .../test/browser_inspector_tab_switch.js | 38 +- browser/devtools/highlighter/test/head.js | 14 + .../devtools/styleinspector/CssHtmlTree.jsm | 29 +- browser/devtools/styleinspector/CssLogic.jsm | 4 + .../devtools/styleinspector/CssRuleView.jsm | 8 +- .../styleinspector/StyleInspector.jsm | 454 +++---- .../test/browser_bug589375_keybindings.js | 18 +- .../styleinspector/test/browser_bug683672.js | 17 +- ...r_bug722196_property_view_media_queries.js | 16 +- .../test/browser_bug_692400_element_style.js | 18 +- ...r_computedview_734259_style_editor_link.js | 14 +- ...mputedview_bug_703643_context_menu_copy.js | 14 +- ...owser_ruleview_734259_style_editor_link.js | 17 +- ...r_ruleview_bug_703643_context_menu_copy.js | 52 +- .../test/browser_ruleview_focus.js | 21 +- .../test/browser_styleinspector.js | 17 +- ...styleinspector_bug_672744_search_filter.js | 17 +- ...tyleinspector_bug_672746_default_styles.js | 18 +- ...ector_bug_689759_no_results_placeholder.js | 17 +- browser/devtools/styleinspector/test/head.js | 149 +- .../browser/devtools/inspector.properties | 6 - .../devtools/styleinspector.properties | 6 + 33 files changed, 1089 insertions(+), 1612 deletions(-) delete mode 100644 browser/devtools/highlighter/test/browser_inspector_duplicate_ruleview.js delete mode 100644 browser/devtools/highlighter/test/browser_inspector_registertools.js diff --git a/browser/devtools/highlighter/TreePanel.jsm b/browser/devtools/highlighter/TreePanel.jsm index dcf8c14f6b4..87b04779a3d 100644 --- a/browser/devtools/highlighter/TreePanel.jsm +++ b/browser/devtools/highlighter/TreePanel.jsm @@ -26,6 +26,7 @@ * Julian Viereck * Paul Rouget * Kyle Simpson + * Dave Camp * * 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 @@ -46,6 +47,7 @@ const Cu = Components.utils; Cu.import("resource:///modules/domplate.jsm"); Cu.import("resource:///modules/InsideOutBox.jsm"); Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource:///modules/inspector.jsm"); var EXPORTED_SYMBOLS = ["TreePanel", "DOMHelpers"]; @@ -122,8 +124,10 @@ TreePanel.prototype = { delete this.initializingTreePanel; Services.obs.notifyObservers(null, this.IUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY, null); - if (this.IUI.selection) - this.select(this.IUI.selection, true); + if (this.pendingSelection) { + this.select(this.pendingSelection.node, this.pendingSelection.scroll); + delete this.pendingSelection; + } }, /** @@ -219,6 +223,11 @@ TreePanel.prototype = { delete this.treeBrowserDocument; } + if (this.ioBox) { + this.ioBox.destroy(); + delete this.ioBox; + } + this.treeLoaded = false; }, @@ -572,8 +581,11 @@ TreePanel.prototype = { */ select: function TP_select(aNode, aScroll) { - if (this.ioBox) + if (this.ioBox) { this.ioBox.select(aNode, true, true, aScroll); + } else { + this.pendingSelection = { node: aNode, scroll: aScroll }; + } }, /////////////////////////////////////////////////////////////////////////// @@ -691,11 +703,6 @@ TreePanel.prototype = { parent.removeChild(this.treeIFrame); delete this.treeIFrame; } - - if (this.ioBox) { - this.ioBox.destroy(); - delete this.ioBox; - } } }; diff --git a/browser/devtools/highlighter/inspector.jsm b/browser/devtools/highlighter/inspector.jsm index 4f1f8d6de9e..bf3fdd84e56 100644 --- a/browser/devtools/highlighter/inspector.jsm +++ b/browser/devtools/highlighter/inspector.jsm @@ -29,6 +29,7 @@ * Johan Charlez * Mike Ratcliffe * Murali S R + * Dave Camp * * 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 @@ -54,7 +55,6 @@ var EXPORTED_SYMBOLS = ["InspectorUI"]; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource:///modules/TreePanel.jsm"); -Cu.import("resource:///modules/devtools/CssRuleView.jsm"); Cu.import("resource:///modules/highlighter.jsm"); Cu.import("resource:///modules/devtools/LayoutHelpers.jsm"); @@ -76,9 +76,6 @@ const INSPECTOR_NOTIFICATIONS = { // Fires when the Tree Panel is opened and initialized. TREEPANELREADY: "inspector-treepanel-ready", - // Fires when the CSS Rule View is opened and initialized. - RULEVIEWREADY: "inspector-ruleview-ready", - // Event notifications for the attribute-value editor EDITOR_OPENED: "inspector-editor-opened", EDITOR_CLOSED: "inspector-editor-closed", @@ -87,6 +84,186 @@ const INSPECTOR_NOTIFICATIONS = { const PSEUDO_CLASSES = [":hover", ":active", ":focus"]; +/** + * Represents an open instance of the Inspector for a tab. + * This is the object handed out to sidebars and other API consumers. + * + * Right now it's a thin layer over InspectorUI, but we will + * start moving per-tab state into this object soon, eventually + * replacing the per-winID InspectorStore objects. + * + * The lifetime of this object is also not yet correct. This object + * is currently destroyed when the inspector is torn down, either by user + * closing the inspector or by user switching the tab. This should + * only be destroyed when user closes the inspector. + */ +function Inspector(aIUI) +{ + this._IUI = aIUI; + this._winID = aIUI.winID; + this._listeners = {}; +} + +Inspector.prototype = { + /** + * True if the highlighter is locked on a node. + */ + get locked() { + return !this._IUI.inspecting; + }, + + /** + * The currently selected node in the highlighter. + */ + get selection() { + return this._IUI.selection; + }, + + /** + * Indicate that a tool has modified the state of the page. Used to + * decide whether to show the "are you sure you want to navigate" + * notification. + */ + markDirty: function Inspector_markDirty() + { + this._IUI.isDirty = true; + }, + + /** + * The chrome window the inspector lives in. + */ + get chromeWindow() { + return this._IUI.chromeWin; + }, + + /** + * Get a store object that will last for the lifetime + * of the current inspector. When the lifetime of this object is corrected, + * this won't be necessary; clients can just decorate the inspector object. + * See bug 740662. + * + * @param string aStoreID + * A string identification for the store. + */ + _getStore: function Inspector__getStore(aStoreID) + { + let storeID = "tool-store-" + aStoreID; + let store = this._getStoreValue(storeID); + if (!store) { + store = {}; + this._setStoreValue(storeID, store); + } + return store; + }, + + /** + * Set a single value in the store. + * + * When this object replaces InspectorStore this can probably just be + * dropped in favor of decorating Inspector, see bug 740662. + */ + _setStoreValue: function Inspector__setStoreValue(aID, aValue) + { + this._IUI.store.setValue(this._winID, aID, aValue); + }, + + /** + * Set a single value in the store. + * + * When this object replaces InspectorStore this can probably just be + * dropped in favor of decorating Inspector, see bug 740662. + */ + _getStoreValue: function Inspector__getStoreValue(aID) + { + return this._IUI.store.getValue(this._winID, aID); + }, + + /** + * Notify the inspector that the current selection has changed. + * + * @param string aContext + * An string that will be passed to the change event. Allows + * a tool to recognize when it sent a change notification itself + * to avoid unnecessary refresh. + */ + change: function Inspector_change(aContext) + { + this._IUI.nodeChanged(aContext); + }, + + /** + * Called by the InspectorUI when the inspector is being destroyed. + */ + _destroy: function Inspector__destroy() + { + delete this._IUI; + delete this._listeners; + }, + + /// Event stuff. Would like to refactor this eventually. + /// Emulates the jetpack event source, which has a nice API. + + /** + * Connect a listener to this object. + * + * @param string aEvent + * The event name to which we're connecting. + * @param function aListener + * Called when the event is fired. + */ + on: function Inspector_on(aEvent, aListener) + { + if (!(aEvent in this._listeners)) { + this._listeners[aEvent] = []; + } + this._listeners[aEvent].push(aListener); + }, + + /** + * Listen for the next time an event is fired. + * + * @param string aEvent + * The event name to which we're connecting. + * @param function aListener + * Called when the event is fired. Will be called at most one time. + */ + once: function Inspector_once(aEvent, aListener) + { + let handler = function() { + this.removeListener(aEvent, handler); + aListener(); + }.bind(this); + this.on(aEvent, handler); + }, + + /** + * Remove a previously-registered event listener. Works for events + * registered with either on or once. + * + * @param string aEvent + * The event name whose listener we're disconnecting. + * @param function aListener + * The listener to remove. + */ + removeListener: function Inspector_removeListener(aEvent, aListener) + { + this._listeners[aEvent] = this._listeners[aEvent].filter(function(l) aListener != l); + }, + + /** + * Emit an event on the inspector. All arguments to this method will + * be sent to listner functions. + */ + _emit: function Inspector__emit(aEvent) + { + if (!(aEvent in this._listeners)) + return; + for each (let listener in this._listeners[aEvent]) { + listener.apply(null, arguments); + } + } +} + /////////////////////////////////////////////////////////////////////////// //// InspectorUI @@ -99,6 +276,10 @@ const PSEUDO_CLASSES = [":hover", ":active", ":focus"]; */ function InspectorUI(aWindow) { + // Let style inspector tools register themselves. + let tmp = {}; + Cu.import("resource:///modules/devtools/StyleInspector.jsm", tmp); + this.chromeWin = aWindow; this.chromeDoc = aWindow.document; this.tabbrowser = aWindow.gBrowser; @@ -118,6 +299,19 @@ InspectorUI.prototype = { isDirty: false, store: null, + _currentInspector: null, + _sidebar: null, + + /** + * The Inspector object for the current tab. + */ + get currentInspector() this._currentInspector, + + /** + * The InspectorStyleSidebar for the current tab. + */ + get sidebar() this._sidebar, + /** * Toggle the inspector interface elements on or off. * @@ -191,95 +385,6 @@ InspectorUI.prototype = { }, - /** - * Show the Sidebar. - */ - showSidebar: function IUI_showSidebar() - { - this.sidebarBox.removeAttribute("hidden"); - this.sidebarSplitter.removeAttribute("hidden"); - this.stylingButton.checked = true; - - // If no tool is already selected, show the last-used sidebar if available, - // otherwise just show the first. - - if (!Array.some(this.sidebarToolbar.children, - function(btn) btn.hasAttribute("checked"))) { - - let activePanel = this.sidebarTools[0]; - let activeId = this.store.getValue(this.winID, "activeSidebar"); - if (activeId && this.tools[activeId]) { - activePanel = this.tools[activeId]; - } - this.activateSidebarPanel(activePanel.id); - } - - this.store.setValue(this.winID, "sidebarOpen", true); - Services.prefs.setBoolPref("devtools.inspector.sidebarOpen", true); - }, - - /** - * Tear down the sidebar. - */ - _destroySidebar: function IUI_destroySidebar() - { - this.sidebarBox.setAttribute("hidden", "true"); - this.sidebarSplitter.setAttribute("hidden", "true"); - this.stylingButton.checked = false; - }, - - /** - * Hide the sidebar. - */ - hideSidebar: function IUI_hideSidebar() - { - this._destroySidebar(); - this.store.setValue(this.winID, "sidebarOpen", false); - Services.prefs.setBoolPref("devtools.inspector.sidebarOpen", false); - }, - - /** - * Show or hide the sidebar. Called from the Styling button on the - * highlighter toolbar. - */ - toggleSidebar: function IUI_toggleSidebar() - { - if (!this.isSidebarOpen) { - this.showSidebar(); - } else { - this.hideSidebar(); - } - }, - - /** - * Activate a sidebar panel by id. - */ - activateSidebarPanel: function IUI_activateSidebarPanel(aID) - { - let buttonId = this.getToolbarButtonId(aID); - this.chromeDoc.getElementById(buttonId).click(); - }, - - get activeSidebarPanel() - { - for each (let tool in this.sidebarTools) { - if (this.sidebarDeck.selectedPanel == this.getToolIframe(tool)) { - return tool.id; - } - } - return null; - }, - - /** - * Getter to test if the Sidebar is open or not. - */ - get isSidebarOpen() - { - return this.stylingButton.checked && - !this.sidebarBox.hidden && - !this.sidebarSplitter.hidden; - }, - /** * Toggle the status of the inspector, starting or stopping it. Invoked * from the toolbar's Inspect button. @@ -298,6 +403,19 @@ InspectorUI.prototype = { } }, + /** + * Show or hide the sidebar. Called from the Styling button on the + * highlighter toolbar. + */ + toggleSidebar: function IUI_toggleSidebar() + { + if (!this.isSidebarOpen) { + this.showSidebar(); + } else { + this.hideSidebar(); + } + }, + /** * Toggle the TreePanel. */ @@ -379,21 +497,9 @@ InspectorUI.prototype = { this.inspectToolbutton = this.chromeDoc.getElementById("inspector-inspect-toolbutton"); - this.initTools(); this.chromeWin.Tilt.setup(); this.treePanel = new TreePanel(this.chromeWin, this); - - if (Services.prefs.getBoolPref("devtools.ruleview.enabled") && - !this.toolRegistered("ruleview")) { - this.registerRuleView(); - } - - if (Services.prefs.getBoolPref("devtools.styleinspector.enabled") && - !this.toolRegistered("styleinspector")) { - this.stylePanel = new StyleInspector(this.chromeWin, this); - } - this.toolbar.hidden = false; this.inspectMenuitem.setAttribute("checked", true); @@ -409,6 +515,19 @@ InspectorUI.prototype = { // initialize the highlighter this.highlighter = new Highlighter(this.chromeWin); + this._currentInspector = new Inspector(this); + + this._sidebar = new InspectorStyleSidebar({ + document: this.chromeDoc, + inspector: this._currentInspector, + }); + + // Create UI for any sidebars registered with + // InspectorUI.registerSidebar() + for each (let tool in InspectorUI._registeredSidebars) { + this._sidebar.addTool(tool); + } + this.setupNavigationKeys(); this.highlighterReady(); @@ -425,40 +544,6 @@ InspectorUI.prototype = { }, - /** - * Register the Rule View in the Sidebar. - */ - registerRuleView: function IUI_registerRuleView() - { - let isOpen = this.isRuleViewOpen.bind(this); - - this.ruleViewObject = { - id: "ruleview", - label: this.strings.GetStringFromName("ruleView.label"), - tooltiptext: this.strings.GetStringFromName("ruleView.tooltiptext"), - accesskey: this.strings.GetStringFromName("ruleView.accesskey"), - context: this, - get isOpen() isOpen(), - show: this.openRuleView, - hide: this.closeRuleView, - onSelect: this.selectInRuleView, - onChanged: this.changeInRuleView, - panel: null, - unregister: this.destroyRuleView, - sidebar: true, - }; - - this.registerTool(this.ruleViewObject); - }, - - /** - * Register and initialize any included tools. - */ - initTools: function IUI_initTools() - { - // Extras go here. - }, - /** * Initialize the InspectorStore. */ @@ -581,12 +666,11 @@ InspectorUI.prototype = { this.stopInspecting(); - this.toolsDo(function IUI_toolsHide(aTool) { - this.unregisterTool(aTool); - }.bind(this)); - // close the sidebar - this._destroySidebar(); + if (this._sidebar) { + this._sidebar.destroy(); + this._sidebar = null; + } if (this.highlighter) { this.highlighter.destroy(); @@ -608,6 +692,10 @@ InspectorUI.prototype = { delete this.treePanel; delete this.stylePanel; delete this.toolbar; + + this._currentInspector._destroy(); + delete this._currentInspector; + Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.CLOSED, null); if (!aKeepStore) Services.obs.notifyObservers(null, INSPECTOR_NOTIFICATIONS.DESTROYED, winId); @@ -627,8 +715,14 @@ InspectorUI.prototype = { this.inspectToolbutton.checked = true; this.inspecting = true; - this.toolsDim(true); this.highlighter.unlock(); + this._notifySelected(); + this._currentInspector._emit("unlocked"); + }, + + _notifySelected: function IUI__notifySelected(aFrom) + { + this._currentInspector._emit("select", aFrom); }, /** @@ -646,13 +740,14 @@ InspectorUI.prototype = { this.inspectToolbutton.checked = false; this.inspecting = false; - this.toolsDim(false); if (this.highlighter.getNode()) { this.select(this.highlighter.getNode(), true, !aPreventScroll); } else { this.select(null, true, true); } this.highlighter.lock(); + this._notifySelected(); + this._currentInspector._emit("locked"); }, /** @@ -680,7 +775,7 @@ InspectorUI.prototype = { if (aFrom != "breadcrumbs") { this.clearPseudoClassLocks(); } - + this.selection = aNode; if (!this.inspecting) { this.highlighter.highlight(this.selection); @@ -691,9 +786,9 @@ InspectorUI.prototype = { this.chromeWin.Tilt.update(aNode); this.treePanel.select(aNode, aScroll); - this.toolsSelect(aScroll); + this._notifySelected(aFrom); }, - + /** * Toggle the pseudo-class lock on the currently inspected element. If the * pseudo-class is :hover or :active, that pseudo-class will also be toggled @@ -716,7 +811,7 @@ InspectorUI.prototype = { node = node.parentNode; } while (hierarchical && node.parentNode) } - this.nodeChanged(); + this.nodeChanged("pseudoclass"); }, /** @@ -740,7 +835,7 @@ InspectorUI.prototype = { { this.highlighter.invalidateSize(); this.breadcrumbs.updateSelectors(); - this.toolsOnChanged(aUpdater); + this._currentInspector._emit("change", aUpdater); }, ///////////////////////////////////////////////////////////////////////// @@ -785,7 +880,7 @@ InspectorUI.prototype = { } if (this.store.getValue(this.winID, "sidebarOpen")) { - this.showSidebar(); + this._sidebar.show(); } Services.obs.notifyObservers({wrappedJSObject: this}, @@ -954,168 +1049,6 @@ InspectorUI.prototype = { this.inspectNode(parent); }, - ///////////////////////////////////////////////////////////////////////// - //// CssRuleView methods - - /** - * Is the cssRuleView open? - */ - isRuleViewOpen: function IUI_isRuleViewOpen() - { - return this.isSidebarOpen && this.ruleButton.hasAttribute("checked") && - (this.sidebarDeck.selectedPanel == this.getToolIframe(this.ruleViewObject)); - }, - - /** - * Convenience getter to retrieve the Rule Button. - */ - get ruleButton() - { - return this.chromeDoc.getElementById( - this.getToolbarButtonId(this.ruleViewObject.id)); - }, - - /** - * Open the CssRuleView. - */ - openRuleView: function IUI_openRuleView() - { - let iframe = this.getToolIframe(this.ruleViewObject); - if (iframe.getAttribute("src")) { - // We're already loading this tool, let it finish. - return; - } - - let boundLoadListener = function() { - iframe.removeEventListener("load", boundLoadListener, true); - let doc = iframe.contentDocument; - - let winID = this.winID; - let ruleViewStore = this.store.getValue(winID, "ruleView"); - if (!ruleViewStore) { - ruleViewStore = {}; - this.store.setValue(winID, "ruleView", ruleViewStore); - } - - this.ruleView = new CssRuleView(doc, ruleViewStore); - - // Add event handlers bound to this. - this.boundRuleViewChanged = this.ruleViewChanged.bind(this); - this.ruleView.element.addEventListener("CssRuleViewChanged", - this.boundRuleViewChanged); - this.cssRuleViewBoundCSSLinkClicked = this.ruleViewCSSLinkClicked.bind(this); - this.ruleView.element.addEventListener("CssRuleViewCSSLinkClicked", - this.cssRuleViewBoundCSSLinkClicked); - - doc.documentElement.appendChild(this.ruleView.element); - this.ruleView.highlight(this.selection); - Services.obs.notifyObservers(null, - INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, null); - }.bind(this); - - iframe.addEventListener("load", boundLoadListener, true); - - iframe.setAttribute("src", "chrome://browser/content/devtools/cssruleview.xul"); - }, - - /** - * Stub to Close the CSS Rule View. Does nothing currently because the - * Rule View lives in the sidebar. - */ - closeRuleView: function IUI_closeRuleView() - { - // do nothing for now - }, - - /** - * Update the selected node in the Css Rule View. - * @param {nsIDOMnode} the selected node. - */ - selectInRuleView: function IUI_selectInRuleView(aNode) - { - if (this.ruleView) - this.ruleView.highlight(aNode); - }, - - /** - * Update the rules for the current node in the Css Rule View. - */ - changeInRuleView: function IUI_selectInRuleView() - { - if (this.ruleView) - this.ruleView.nodeChanged(); - }, - - ruleViewChanged: function IUI_ruleViewChanged() - { - this.isDirty = true; - this.nodeChanged(this.ruleViewObject); - }, - - /** - * When a css link is clicked this method is called in order to either: - * 1. Open the link in view source (for element style attributes) - * 2. Open the link in the style editor - * - * Like the style editor, we only view stylesheets contained in - * document.styleSheets. - * - * @param aEvent The event containing the style rule to act on - */ - ruleViewCSSLinkClicked: function(aEvent) - { - if (!this.chromeWin) { - return; - } - - let rule = aEvent.detail.rule; - let styleSheet = rule.sheet; - let doc = this.chromeWin.content.document; - let styleSheets = doc.styleSheets; - let contentSheet = false; - let line = rule.ruleLine || 0; - - // Array.prototype.indexOf always returns -1 here so we loop through - // the styleSheets object instead. - for each (let sheet in styleSheets) { - if (sheet == styleSheet) { - contentSheet = true; - break; - } - } - - if (contentSheet) { - this.chromeWin.StyleEditor.openChrome(styleSheet, line); - } else { - let href = styleSheet ? styleSheet.href : ""; - if (rule.elementStyle.element) { - href = rule.elementStyle.element.ownerDocument.location.href; - } - let viewSourceUtils = this.chromeWin.gViewSourceUtils; - viewSourceUtils.viewSource(href, null, doc, line); - } - }, - - /** - * Destroy the rule view. - */ - destroyRuleView: function IUI_destroyRuleView() - { - if (this.ruleView) { - this.ruleView.element.removeEventListener("CssRuleViewChanged", - this.boundRuleViewChanged); - this.ruleView.element.removeEventListener("CssRuleViewCSSLinkClicked", - this.cssRuleViewBoundCSSLinkClicked); - delete this.boundRuleViewChanged; - delete this.cssRuleViewBoundCSSLinkClicked; - this.ruleView.destroy(); - delete this.ruleView; - } - - let iframe = this.getToolIframe(this.ruleViewObject); - iframe.parentNode.removeChild(iframe); - }, - ///////////////////////////////////////////////////////////////////////// //// Utility Methods @@ -1200,341 +1133,6 @@ InspectorUI.prototype = { return "inspector-" + anId + "-toolbutton"; }, - /** - * 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 - */ - bindToolEvent: function IUI_bindToolEvent(aWidget, aEvent, aCallback) - { - this.toolEvents[aWidget.id + "_" + aEvent] = aCallback; - aWidget.addEventListener(aEvent, aCallback, false); - }, - - /** - * Register an external tool with the inspector. - * - * aRegObj = { - * id: "toolname", - * context: myTool, - * label: "Button or tab label", - * icon: "chrome://somepath.png", - * tooltiptext: "Button tooltip", - * accesskey: "S", - * isOpen: object.property, (getter) returning true if tool is open. - * onSelect: 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, set if tool is in a separate panel, null otherwise. - * sidebar: boolean, true if tool lives in sidebar tab. - * } - * - * @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.toolRegistered(aRegObj.id)) { - return; - } - - this.tools[aRegObj.id] = aRegObj; - - let buttonContainer = this.chromeDoc.getElementById("inspector-tools"); - let btn; - - // if this is a sidebar tool, create the sidebar features for it and bail. - if (aRegObj.sidebar) { - this.createSidebarTool(aRegObj); - return; - } - - btn = this.chromeDoc.createElement("toolbarbutton"); - let buttonId = this.getToolbarButtonId(aRegObj.id); - btn.setAttribute("id", buttonId); - btn.setAttribute("class", "devtools-toolbarbutton"); - btn.setAttribute("label", aRegObj.label); - btn.setAttribute("tooltiptext", aRegObj.tooltiptext); - btn.setAttribute("accesskey", aRegObj.accesskey); - btn.setAttribute("image", aRegObj.icon || ""); - buttonContainer.insertBefore(btn, this.stylingButton); - - this.bindToolEvent(btn, "click", - function IUI_toolButtonClick(aEvent) { - if (btn.checked) { - this.toolHide(aRegObj); - } else { - this.toolShow(aRegObj); - } - }.bind(this)); - - // if the tool has a panel, register the popuphiding event - if (aRegObj.panel) { - this.bindToolEvent(aRegObj.panel, "popuphiding", - function IUI_toolPanelHiding() { - btn.checked = false; - }); - } - }, - - get sidebarBox() - { - return this.chromeDoc.getElementById("devtools-sidebar-box"); - }, - - get sidebarToolbar() - { - return this.chromeDoc.getElementById("devtools-sidebar-toolbar"); - }, - - get sidebarDeck() - { - return this.chromeDoc.getElementById("devtools-sidebar-deck"); - }, - - get sidebarSplitter() - { - return this.chromeDoc.getElementById("devtools-side-splitter"); - }, - - get stylingButton() - { - return this.chromeDoc.getElementById("inspector-style-button"); - }, - - /** - * Creates a tab and tabpanel for our tool to reside in. - * @param {Object} aRegObj the Registration Object for our tool. - */ - createSidebarTool: function IUI_createSidebarTab(aRegObj) - { - // toolbutton elements - let btn = this.chromeDoc.createElement("toolbarbutton"); - let buttonId = this.getToolbarButtonId(aRegObj.id); - - btn.id = buttonId; - btn.setAttribute("label", aRegObj.label); - btn.setAttribute("class", "devtools-toolbarbutton"); - btn.setAttribute("tooltiptext", aRegObj.tooltiptext); - btn.setAttribute("accesskey", aRegObj.accesskey); - btn.setAttribute("image", aRegObj.icon || ""); - btn.setAttribute("type", "radio"); - btn.setAttribute("group", "sidebar-tools"); - this.sidebarToolbar.appendChild(btn); - - // create tool iframe - let iframe = this.chromeDoc.createElement("iframe"); - iframe.id = "devtools-sidebar-iframe-" + aRegObj.id; - iframe.setAttribute("flex", "1"); - iframe.setAttribute("tooltip", "aHTMLTooltip"); - iframe.addEventListener("mousedown", iframe.focus); - this.sidebarDeck.appendChild(iframe); - - // wire up button to show the iframe - this.bindToolEvent(btn, "click", function showIframe() { - this.toolShow(aRegObj); - }.bind(this)); - }, - - /** - * Return the registered object's iframe. - * @param aRegObj see registerTool function. - * @return iframe or null - */ - getToolIframe: function IUI_getToolIFrame(aRegObj) - { - return this.chromeDoc.getElementById("devtools-sidebar-iframe-" + aRegObj.id); - }, - - /** - * Show the specified tool. - * @param aTool Object (see comment for IUI_registerTool) - */ - toolShow: function IUI_toolShow(aTool) - { - let btn = this.chromeDoc.getElementById(this.getToolbarButtonId(aTool.id)); - btn.setAttribute("checked", "true"); - if (aTool.sidebar) { - Services.prefs.setCharPref("devtools.inspector.activeSidebar", aTool.id); - this.store.setValue(this.winID, "activeSidebar", aTool.id); - this.sidebarDeck.selectedPanel = this.getToolIframe(aTool); - this.sidebarTools.forEach(function(other) { - if (other != aTool) - this.chromeDoc.getElementById( - this.getToolbarButtonId(other.id)).removeAttribute("checked"); - }.bind(this)); - } - - aTool.show.call(aTool.context, this.selection); - }, - - /** - * Hide the specified tool. - * @param aTool Object (see comment for IUI_registerTool) - */ - toolHide: function IUI_toolHide(aTool) - { - aTool.hide.call(aTool.context); - - let btn = this.chromeDoc.getElementById(this.getToolbarButtonId(aTool.id)); - btn.removeAttribute("checked"); - }, - - /** - * Unregister the events associated with the registered tool's widget. - * @param aWidget XUL:widget (toolbarbutton|panel). - * @param aEvent a DOM event. - */ - unbindToolEvent: function IUI_unbindToolEvent(aWidget, aEvent) - { - let toolEvent = aWidget.id + "_" + aEvent; - aWidget.removeEventListener(aEvent, this.toolEvents[toolEvent], false); - delete this.toolEvents[toolEvent] - }, - - /** - * 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) - { - // if this is a sidebar tool, use the sidebar unregistration method - if (aRegObj.sidebar) { - this.unregisterSidebarTool(aRegObj); - return; - } - - let button = this.chromeDoc.getElementById(this.getToolbarButtonId(aRegObj.id)); - let buttonContainer = this.chromeDoc.getElementById("inspector-tools"); - - // unbind click events on button - this.unbindToolEvent(button, "click"); - - // unbind panel popuphiding events if present. - if (aRegObj.panel) - this.unbindToolEvent(aRegObj.panel, "popuphiding"); - - // remove the button from its container - buttonContainer.removeChild(button); - - // call unregister callback and remove from collection - if (aRegObj.unregister) - aRegObj.unregister.call(aRegObj.context); - - delete this.tools[aRegObj.id]; - }, - - /** - * Unregister the registered sidebar tool, unbinding click events for the - * button. - * @param aRegObj Object - * The registration object used to register the tool. - */ - unregisterSidebarTool: function IUI_unregisterSidebarTool(aRegObj) - { - // unbind tool button click event - let buttonId = this.getToolbarButtonId(aRegObj.id); - let btn = this.chromeDoc.getElementById(buttonId); - this.unbindToolEvent(btn, "click"); - - // Remove focus listener - let iframe = this.getToolIframe(aRegObj); - iframe.removeEventListener("mousedown", iframe.focus); - - // remove sidebar buttons and tools - this.sidebarToolbar.removeChild(btn); - - // call unregister callback and remove from collection, this also removes - // the iframe. - if (aRegObj.unregister) - aRegObj.unregister.call(aRegObj.context); - - delete this.tools[aRegObj.id]; - }, - - /** - * 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_toolsDim(aTool) { - if ("dim" in aTool) { - aTool.dim.call(aTool.context, aState); - } - }); - }, - - /** - * Notify registered tools of changes to the highlighted element. - * - * @param object aUpdater - * The tool that triggered the update (if any), that tool's - * onChanged will not be called. - */ - toolsOnChanged: function IUI_toolsChanged(aUpdater) - { - this.toolsDo(function IUI_toolsOnChanged(aTool) { - if (("onChanged" in aTool) && aTool != aUpdater) { - aTool.onChanged.call(aTool.context); - } - }); - }, - - /** - * 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) - { - for each (let tool in this.tools) { - aFunction(tool); - } - }, - - /** - * Convenience getter to retrieve only the sidebar tools. - */ - get sidebarTools() - { - let sidebarTools = []; - for each (let tool in this.tools) - if (tool.sidebar) - sidebarTools.push(tool); - return sidebarTools; - }, - - /** - * 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. @@ -1825,6 +1423,312 @@ InspectorProgressListener.prototype = { }, }; +InspectorUI._registeredSidebars = []; + +/** + * Register an inspector sidebar template. + * Already running sidebars will not be affected, see bug 740665. + * + * @param aRegistration Object + * { + * id: "toolname", + * label: "Button or tab label", + * icon: "chrome://somepath.png", + * tooltiptext: "Button tooltip", + * accesskey: "S", + * contentURL: string URI, source of the tool's iframe content. + * load: Called when the sidebar has been created and the contentURL loaded. + * Passed an Inspector object and an iframe object. + * destroy: Called when the sidebar is destroyed by the inspector. + * Passed whatever was returned by the tool's create function. + * } + */ +InspectorUI.registerSidebar = function IUI_registerSidebar(aRegistration) +{ + // Only allow a given tool ID to be registered once. + if (InspectorUI._registeredSidebars.some(function(elt) elt.id == aRegistration.id)) + return false; + + InspectorUI._registeredSidebars.push(aRegistration); + + return true; +} + +/** + * Unregister a previously-registered inspector sidebar. + * Already running sidebars will not be affected, see bug 740665. + * + * @param aID string + */ +InspectorUI.unregisterSidebar = function IUI_unregisterSidebar(aID) +{ + InspectorUI._registeredSidebars = InspectorUI._registeredSidebars.filter(function(aReg) aReg.id != aID); +} + +/////////////////////////////////////////////////////////////////////////// +//// Style Sidebar + +/** + * Manages the UI and loading of registered sidebar tools. + * @param aOptions object + * Initialization information for the style sidebar, including: + * document: The chrome document in which the style sidebar + * should be created. + * inspector: The Inspector object tied to this sidebar. + */ +function InspectorStyleSidebar(aOptions) +{ + this._tools = {}; + this._chromeDoc = aOptions.document; + this._inspector = aOptions.inspector; +} + +InspectorStyleSidebar.prototype = { + + get visible() !this._box.hasAttribute("hidden"), + get activePanel() this._deck.selectedPanel._toolID, + + destroy: function ISS_destroy() + { + for each (let toolID in Object.getOwnPropertyNames(this._tools)) { + this.removeTool(toolID); + } + delete this._tools; + this._teardown(); + }, + + /** + * Called by InspectorUI to create the UI for a registered sidebar tool. + * Will create a toolbar button and an iframe for the tool. + * @param aRegObj object + * See the documentation for InspectorUI.registerSidebar(). + */ + addTool: function ISS_addTool(aRegObj) + { + if (aRegObj.id in this._tools) { + return; + } + + let btn = this._chromeDoc.createElement("toolbarbutton"); + btn.setAttribute("label", aRegObj.label); + btn.setAttribute("class", "devtools-toolbarbutton"); + btn.setAttribute("tooltiptext", aRegObj.tooltiptext); + btn.setAttribute("accesskey", aRegObj.accesskey); + btn.setAttribute("image", aRegObj.icon || ""); + btn.setAttribute("type", "radio"); + btn.setAttribute("group", "sidebar-tools"); + this._toolbar.appendChild(btn); + + // create tool iframe + let frame = this._chromeDoc.createElement("iframe"); + frame.setAttribute("flex", "1"); + frame._toolID = aRegObj.id; + this._deck.appendChild(frame); + + // wire up button to show the iframe + let onClick = function() { + this.activatePanel(aRegObj.id); + }.bind(this); + btn.addEventListener("click", onClick, true); + + this._tools[aRegObj.id] = { + id: aRegObj.id, + registration: aRegObj, + button: btn, + frame: frame, + loaded: false, + context: null, + onClick: onClick + }; + }, + + /** + * Remove a tool from the sidebar. + * + * @param aID string + * The string ID of the tool to remove. + */ + removeTool: function ISS_removeTool(aID) + { + if (!aID in this._tools) { + return; + } + let tool = this._tools[aID]; + delete this._tools[aID]; + + if (tool.loaded && tool.registration.destroy) { + tool.registration.destroy(tool.context); + } + + if (tool.onLoad) { + tool.frame.removeEventListener("load", tool.onLoad, true); + delete tool.onLoad; + } + + if (tool.onClick) { + tool.button.removeEventListener("click", tool.onClick, true); + delete tool.onClick; + } + + tool.button.parentNode.removeChild(tool.button); + tool.frame.parentNode.removeChild(tool.frame); + }, + + /** + * Hide or show the sidebar. + */ + toggle: function ISS_toggle() + { + if (!this.visible) { + this.show(); + } else { + this.hide(); + } + }, + + /** + * Shows the sidebar, updating the stored visibility pref. + */ + show: function ISS_show() + { + this._box.removeAttribute("hidden"); + this._splitter.removeAttribute("hidden"); + this._toggleButton.checked = true; + + this._showDefault(); + + this._inspector._setStoreValue("sidebarOpen", true); + Services.prefs.setBoolPref("devtools.inspector.sidebarOpen", true); + }, + + /** + * Hides the sidebar, updating the stored visiblity pref. + */ + hide: function ISS_hide() + { + this._teardown(); + this._inspector._setStoreValue("sidebarOpen", false); + Services.prefs.setBoolPref("devtools.inspector.sidebarOpen", false); + }, + + /** + * Hides the sidebar UI elements. + */ + _teardown: function ISS__teardown() + { + this._toggleButton.checked = false; + this._box.setAttribute("hidden", true); + this._splitter.setAttribute("hidden", true); + }, + + /** + * Sets the current sidebar panel. + * + * @param aID string + * The ID of the panel to make visible. + */ + activatePanel: function ISS_activatePanel(aID) { + let tool = this._tools[aID]; + Services.prefs.setCharPref("devtools.inspector.activeSidebar", aID); + this._inspector._setStoreValue("activeSidebar", aID); + this._deck.selectedPanel = tool.frame; + this._showContent(tool); + tool.button.setAttribute("checked", "true"); + let hasSelected = Array.forEach(this._toolbar.children, function(btn) { + if (btn != tool.button) { + btn.removeAttribute("checked"); + } + }); + }, + + /** + * Make the iframe content of a given tool visible. If this is the first + * time the tool has been shown, load its iframe content and call the + * registration object's load method. + * + * @param aTool object + * The tool object we're loading. + */ + _showContent: function ISS__showContent(aTool) + { + // If the current tool is already loaded, notify that we're + // showing this sidebar. + if (aTool.loaded) { + this._inspector._emit("sidebaractivated", aTool.id); + this._inspector._emit("sidebaractivated-" + aTool.id); + return; + } + + // If we're already loading, we're done. + if (aTool.onLoad) { + return; + } + + // This will be canceled in removeTool if necessary. + aTool.onLoad = function(evt) { + if (evt.target.location != aTool.registration.contentURL) { + return; + } + aTool.frame.removeEventListener("load", aTool.onLoad, true); + delete aTool.onLoad; + aTool.loaded = true; + aTool.context = aTool.registration.load(this._inspector, aTool.frame); + + this._inspector._emit("sidebaractivated", aTool.id); + this._inspector._emit("sidebaractivated-" + aTool.id); + }.bind(this); + aTool.frame.addEventListener("load", aTool.onLoad, true); + aTool.frame.setAttribute("src", aTool.registration.contentURL); + }, + + /** + * For testing purposes, mostly - return the tool-provided context + * for a given tool. Will only work after the tool has been loaded + * and instantiated. + */ + _toolContext: function ISS__toolContext(aID) { + return aID in this._tools ? this._tools[aID].context : null; + }, + + /** + * Also mostly for testing, return the list of tool objects stored in + * the sidebar. + */ + _toolObjects: function ISS__toolObjects() { + return [this._tools[i] for each (i in Object.getOwnPropertyNames(this._tools))]; + }, + + /** + * If no tool is already selected, show the last-used sidebar. If there + * was no last-used sidebar, just show the first one. + */ + _showDefault: function ISS__showDefault() + { + let hasSelected = Array.some(this._toolbar.children, + function(btn) btn.hasAttribute("checked")); + + // Make sure the selected panel is loaded... + this._showContent(this._tools[this.activePanel]); + + if (hasSelected) { + return; + } + + let activeID = this._inspector._getStoreValue("activeSidebar"); + if (!activeID || !(activeID in this._tools)) { + activeID = Object.getOwnPropertyNames(this._tools)[0]; + } + this.activatePanel(activeID); + }, + + // DOM elements + get _toggleButton() this._chromeDoc.getElementById("inspector-style-button"), + get _box() this._chromeDoc.getElementById("devtools-sidebar-box"), + get _splitter() this._chromeDoc.getElementById("devtools-side-splitter"), + get _toolbar() this._chromeDoc.getElementById("devtools-sidebar-toolbar"), + get _deck() this._chromeDoc.getElementById("devtools-sidebar-deck"), +}; + /////////////////////////////////////////////////////////////////////////// //// HTML Breadcrumbs @@ -2349,12 +2253,6 @@ XPCOMUtils.defineLazyGetter(InspectorUI.prototype, "strings", "chrome://browser/locale/devtools/inspector.properties"); }); -XPCOMUtils.defineLazyGetter(this, "StyleInspector", function () { - var obj = {}; - Cu.import("resource:///modules/devtools/StyleInspector.jsm", obj); - return obj.StyleInspector; -}); - XPCOMUtils.defineLazyGetter(this, "DOMUtils", function () { return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils); }); diff --git a/browser/devtools/highlighter/test/Makefile.in b/browser/devtools/highlighter/test/Makefile.in index 91ea4a37aae..ed37f77073e 100644 --- a/browser/devtools/highlighter/test/Makefile.in +++ b/browser/devtools/highlighter/test/Makefile.in @@ -54,7 +54,6 @@ _BROWSER_FILES = \ browser_inspector_treePanel_output.js \ browser_inspector_treePanel_input.html \ browser_inspector_treePanel_result.html \ - browser_inspector_registertools.js \ browser_inspector_bug_665880.js \ browser_inspector_bug_674871.js \ browser_inspector_editor.js \ @@ -69,7 +68,6 @@ _BROWSER_FILES = \ browser_inspector_bug_699308_iframe_navigation.js \ browser_inspector_changes.js \ browser_inspector_ruleviewstore.js \ - browser_inspector_duplicate_ruleview.js \ browser_inspector_invalidate.js \ browser_inspector_sidebarstate.js \ browser_inspector_treePanel_menu.js \ diff --git a/browser/devtools/highlighter/test/browser_inspector_changes.js b/browser/devtools/highlighter/test/browser_inspector_changes.js index cec5ac15f62..bb0d95e043b 100644 --- a/browser/devtools/highlighter/test/browser_inspector_changes.js +++ b/browser/devtools/highlighter/test/browser_inspector_changes.js @@ -50,7 +50,7 @@ function createDocument() function getInspectorProp(aName) { - for each (let view in InspectorUI.stylePanel.cssHtmlTree.propertyViews) { + for each (let view in computedViewTree().propertyViews) { if (view.name == aName) { return view; } @@ -79,18 +79,16 @@ function runInspectorTests() // Start up the style inspector panel... Services.obs.addObserver(stylePanelTests, "StyleInspector-populated", false); - executeSoon(function() { - InspectorUI.showSidebar(); - document.getElementById(InspectorUI.getToolbarButtonId("styleinspector")).click(); - }); + InspectorUI.sidebar.show(); + InspectorUI.sidebar.activatePanel("computedview"); } function stylePanelTests() { Services.obs.removeObserver(stylePanelTests, "StyleInspector-populated"); - ok(InspectorUI.isSidebarOpen, "Inspector Sidebar is open"); - ok(InspectorUI.stylePanel.cssHtmlTree, "Style Panel has a cssHtmlTree"); + ok(InspectorUI.sidebar.visible, "Inspector Sidebar is open"); + ok(computedViewTree(), "Style Panel has a cssHtmlTree"); let propView = getInspectorProp("font-size"); is(propView.value, "10px", "Style inspector should be showing the correct font size."); @@ -114,11 +112,13 @@ function stylePanelAfterChange() function stylePanelNotActive() { // Tests changes made while the style panel is not active. - InspectorUI.ruleButton.click(); + InspectorUI.sidebar.activatePanel("ruleview"); + executeSoon(function() { - testDiv.style.fontSize = "20px"; Services.obs.addObserver(stylePanelAfterSwitch, "StyleInspector-populated", false); - document.getElementById(InspectorUI.getToolbarButtonId("styleinspector")).click(); + testDiv.style.fontSize = "20px"; + InspectorUI.nodeChanged(); + InspectorUI.sidebar.activatePanel("computedview"); }); } diff --git a/browser/devtools/highlighter/test/browser_inspector_duplicate_ruleview.js b/browser/devtools/highlighter/test/browser_inspector_duplicate_ruleview.js deleted file mode 100644 index d5ab35345b6..00000000000 --- a/browser/devtools/highlighter/test/browser_inspector_duplicate_ruleview.js +++ /dev/null @@ -1,128 +0,0 @@ -/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ -/* Any copyright is dedicated to the Public Domain. - http://creativecommons.org/publicdomain/zero/1.0/ */ - - -let div; -let tab1; -let tab2; -let tab1window; - -function inspectorTabOpen1() -{ - ok(window.InspectorUI, "InspectorUI variable exists"); - ok(!InspectorUI.inspecting, "Inspector is not highlighting"); - ok(InspectorUI.store.isEmpty(), "Inspector.store is empty"); - - Services.obs.addObserver(inspectorUIOpen1, - InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); - InspectorUI.openInspectorUI(); -} - -function inspectorUIOpen1() -{ - Services.obs.removeObserver(inspectorUIOpen1, - InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false); - - // Make sure the inspector is open. - ok(InspectorUI.inspecting, "Inspector is highlighting"); - ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open"); - ok(!InspectorUI.isSidebarOpen, "Inspector Sidebar 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]; - InspectorUI.inspectNode(div); - is(InspectorUI.selection, div, "selection matches the div element"); - - Services.obs.addObserver(inspectorRuleViewOpened, - InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, false); - - InspectorUI.showSidebar(); - InspectorUI.openRuleView(); -} - -function inspectorRuleViewOpened() { - Services.obs.removeObserver(inspectorRuleViewOpened, - InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY); - - // Open the second tab. - tab2 = gBrowser.addTab(); - gBrowser.selectedTab = tab2; - - gBrowser.selectedBrowser.addEventListener("load", function(evt) { - gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, - true); - waitForFocus(inspectorTabOpen2, content); - }, true); - - content.location = "data:text/html,

tab 2: the inspector should close now"; -} - -function inspectorTabOpen2() -{ - // Make sure the inspector is closed. - ok(!InspectorUI.inspecting, "Inspector is not highlighting"); - ok(!InspectorUI.treePanel, "Inspector Tree Panel is closed"); - ok(!InspectorUI.isSidebarOpen, "Inspector Sidebar is not open"); - is(InspectorUI.store.length, 1, "Inspector.store.length = 1"); - - Services.obs.addObserver(inspectorFocusTab1, - InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, false); - // Switch back to tab 1. - executeSoon(function() { - gBrowser.selectedTab = tab1; - }); -} - -function inspectorFocusTab1() -{ - Services.obs.removeObserver(inspectorFocusTab1, - InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, false); - Services.obs.addObserver(inspectorRuleTrap, - InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, false); - - // Make sure the inspector is open. - ok(InspectorUI.inspecting, "Inspector is highlighting"); - ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open"); - is(InspectorUI.store.length, 1, "Inspector.store.length = 1"); - is(InspectorUI.selection, div, "selection matches the div element"); - ok(InspectorUI.isSidebarOpen, "sidebar is open"); - ok(InspectorUI.isRuleViewOpen(), "rule view is open"); - - // The rule view element plus its popupSet - is(InspectorUI.ruleView.doc.documentElement.children.length, 2, "RuleView elements.length == 2"); - - requestLongerTimeout(4); - executeSoon(function() { - InspectorUI.closeInspectorUI(); - gBrowser.removeCurrentTab(); // tab 1 - gBrowser.removeCurrentTab(); // tab 2 - finish(); - }); -} - -function inspectorRuleTrap() -{ - Services.obs.removeObserver(inspectorRuleTrap, - InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, false); - is(InspectorUI.ruleView.doc.documentElement.children.length, 1, "RuleView elements.length == 1"); -} - -function test() -{ - waitForExplicitFinish(); - - tab1 = gBrowser.addTab(); - gBrowser.selectedTab = tab1; - gBrowser.selectedBrowser.addEventListener("load", function(evt) { - gBrowser.selectedBrowser.removeEventListener(evt.type, arguments.callee, - true); - waitForFocus(inspectorTabOpen1, content); - }, true); - - content.location = "data:text/html,

tab switching tests for inspector" + - "

tab 1
"; -} - diff --git a/browser/devtools/highlighter/test/browser_inspector_editor.js b/browser/devtools/highlighter/test/browser_inspector_editor.js index 6728ec4a038..3099bad1b08 100644 --- a/browser/devtools/highlighter/test/browser_inspector_editor.js +++ b/browser/devtools/highlighter/test/browser_inspector_editor.js @@ -77,8 +77,8 @@ function doEditorTestSteps() EventUtils.synthesizeMouse(attrValNode_id, 2, 2, {clickCount: 2}, attrValNode_id.ownerDocument.defaultView); }); - yield; // End of Step 1 + yield; // End of Step 1 // Step 2: validate editing session, enter new attribute value into editor, and save input ok(InspectorUI.treePanel.editingContext, "Step 2: editor session started"); diff --git a/browser/devtools/highlighter/test/browser_inspector_initialization.js b/browser/devtools/highlighter/test/browser_inspector_initialization.js index 6facbc436ce..2785ff020db 100644 --- a/browser/devtools/highlighter/test/browser_inspector_initialization.js +++ b/browser/devtools/highlighter/test/browser_inspector_initialization.js @@ -89,25 +89,21 @@ function treePanelTests() { Services.obs.removeObserver(treePanelTests, InspectorUI.INSPECTOR_NOTIFICATIONS.TREEPANELREADY); - Services.obs.addObserver(stylePanelTests, - "StyleInspector-opened", false); - ok(InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is open"); - executeSoon(function() { - InspectorUI.showSidebar(); - document.getElementById(InspectorUI.getToolbarButtonId("styleinspector")).click(); - }); + InspectorUI.sidebar.show(); + InspectorUI.currentInspector.once("sidebaractivated-computedview", + stylePanelTests) + InspectorUI.sidebar.activatePanel("computedview"); } function stylePanelTests() { - Services.obs.removeObserver(stylePanelTests, "StyleInspector-opened"); + ok(InspectorUI.sidebar.visible, "Inspector Sidebar is open"); + is(InspectorUI.sidebar.activePanel, "computedview", "Computed View is open"); + ok(computedViewTree(), "Computed view has a cssHtmlTree"); - ok(InspectorUI.isSidebarOpen, "Inspector Sidebar is open"); - ok(InspectorUI.stylePanel.cssHtmlTree, "Style Panel has a cssHtmlTree"); - - InspectorUI.ruleButton.click(); + InspectorUI.sidebar.activatePanel("ruleview"); executeSoon(function() { ruleViewTests(); }); @@ -118,8 +114,8 @@ function ruleViewTests() Services.obs.addObserver(runContextMenuTest, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false); - ok(InspectorUI.isRuleViewOpen(), "Rule View is open"); - ok(InspectorUI.ruleView, "InspectorUI has a cssRuleView"); + is(InspectorUI.sidebar.activePanel, "ruleview", "Rule View is open"); + ok(ruleView(), "InspectorUI has a cssRuleView"); executeSoon(function() { InspectorUI.closeInspectorUI(); @@ -152,8 +148,6 @@ function inspectNodesFromContextTest() 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.treePanel.isOpen(), "Inspector Tree Panel is closed"); - ok(!InspectorUI.stylePanel.isOpen(), "Inspector Style Panel is closed"); executeSoon(function() { InspectorUI.closeInspectorUI(true); }); @@ -207,11 +201,7 @@ function finishInspectorTests(subject, topic, aWinIdString) ok(!InspectorUI.highlighter, "Highlighter is gone"); ok(!InspectorUI.treePanel, "Inspector Tree Panel is closed"); ok(!InspectorUI.inspecting, "Inspector is not inspecting"); - ok(!InspectorUI.isSidebarOpen, "Inspector Sidebar is closed"); - ok(!InspectorUI.stylePanel, "Inspector Style Panel is gone"); - ok(!InspectorUI.ruleView, "Inspector Rule View is gone"); - is(InspectorUI.sidebarToolbar.children.length, 0, "No items in the Sidebar toolbar"); - is(InspectorUI.sidebarDeck.children.length, 0, "No items in the Sidebar deck"); + ok(!InspectorUI._sidebar, "Inspector Sidebar is closed"); ok(!InspectorUI.toolbar, "toolbar is hidden"); Services.obs.removeObserver(inspectNodesFromContextTestTrap, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED); diff --git a/browser/devtools/highlighter/test/browser_inspector_pseudoclass_lock.js b/browser/devtools/highlighter/test/browser_inspector_pseudoclass_lock.js index e5067896b3c..6dcfee24d91 100644 --- a/browser/devtools/highlighter/test/browser_inspector_pseudoclass_lock.js +++ b/browser/devtools/highlighter/test/browser_inspector_pseudoclass_lock.js @@ -23,7 +23,7 @@ function test() } function createDocument() -{ +{ div = doc.createElement("div"); div.textContent = "test div"; @@ -34,7 +34,7 @@ function createDocument() style.appendChild(rules); head.appendChild(style); doc.body.appendChild(div); - + setupTests(); } @@ -51,26 +51,22 @@ function selectNode() InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED); executeSoon(function() { - InspectorUI.highlighter.addListener("nodeselected", openRuleView); + InspectorUI.highlighter.addListener("locked", openRuleView); InspectorUI.inspectNode(div); + InspectorUI.stopInspecting(); }); } function openRuleView() { - Services.obs.addObserver(performTests, - InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, false); - - InspectorUI.showSidebar(); - InspectorUI.openRuleView(); + InspectorUI.sidebar.show(); + InspectorUI.currentInspector.once("sidebaractivated-ruleview", performTests); + InspectorUI.sidebar.activatePanel("ruleview"); } function performTests() { - Services.obs.removeObserver(performTests, - InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY); - - InspectorUI.highlighter.removeListener("nodeselected", performTests); + InspectorUI.highlighter.removeListener("locked", performTests); // toggle the class InspectorUI.highlighter.pseudoClassLockToggled(pseudo); @@ -107,10 +103,10 @@ function testAdded() is(pseudoClassesBox.textContent, pseudo, "pseudo-class in infobar selector"); // ruleview contains pseudo-class rule - is(InspectorUI.ruleView.element.children.length, 3, + is(ruleView().element.children.length, 3, "rule view is showing 3 rules for pseudo-class locked div"); - is(InspectorUI.ruleView.element.children[1]._ruleEditor.rule.selectorText, + is(ruleView().element.children[1]._ruleEditor.rule.selectorText, "div:hover", "rule view is showing " + pseudo + " rule"); } @@ -132,7 +128,7 @@ function testRemovedFromUI() is(pseudoClassesBox.textContent, "", "pseudo-class removed from infobar selector"); // ruleview no longer contains pseudo-class rule - is(InspectorUI.ruleView.element.children.length, 2, + is(ruleView().element.children.length, 2, "rule view is showing 2 rules after removing lock"); } diff --git a/browser/devtools/highlighter/test/browser_inspector_registertools.js b/browser/devtools/highlighter/test/browser_inspector_registertools.js deleted file mode 100644 index 7a178e4c103..00000000000 --- a/browser/devtools/highlighter/test/browser_inspector_registertools.js +++ /dev/null @@ -1,254 +0,0 @@ -/* -*- 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 Inspector Highlighter Tests. - * - * 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): - * Michael Ratcliffe - * - * 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 ***** */ - -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"); - h1 = doc.createElement("h1"); - let p1 = doc.createElement("p"); - p2 = doc.createElement("p"); - let div2 = doc.createElement("div"); - let p3 = doc.createElement("p"); - doc.title = "Inspector Tree Selection Test"; - h1.textContent = "Inspector Tree Selection Test"; - p1.textContent = "This is some example text"; - p2.textContent = "Lorem ipsum dolor sit amet, consectetur adipisicing " + - "elit, sed do eiusmod tempor incididunt ut labore et dolore magna " + - "aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco " + - "laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure " + - "dolor in reprehenderit in voluptate velit esse cillum dolore eu " + - "fugiat nulla pariatur. Excepteur sint occaecat cupidatat non " + - "proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; - p3.textContent = "Lorem ipsum dolor sit amet, consectetur adipisicing " + - "elit, sed do eiusmod tempor incididunt ut labore et dolore magna " + - "aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco " + - "laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure " + - "dolor in reprehenderit in voluptate velit esse cillum dolore eu " + - "fugiat nulla pariatur. Excepteur sint occaecat cupidatat non " + - "proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; - div.appendChild(h1); - div.appendChild(p1); - div.appendChild(p2); - div2.appendChild(p3); - doc.body.appendChild(div); - doc.body.appendChild(div2); - setupHighlighterTests(); -} - -function setupHighlighterTests() -{ - ok(h1, "we have the header node"); - 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, InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED); - toolsLength = InspectorUI.tools.length; - toolEvents = InspectorUI.toolEvents.length; - info("tools registered"); - InspectorUI.highlighter.addListener("nodeselected", startToolTests); - InspectorUI.inspectNode(h1); -} - -function startToolTests(evt) -{ - InspectorUI.highlighter.removeListener("nodeselected", startToolTests); - 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.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"); - InspectorUI.toolShow(tool1); - InspectorUI.toolShow(tool2); - InspectorUI.toolShow(tool3); - - info("Checking panel states 2"); - 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, should see 3 selects"); - InspectorUI.inspectNode(p2); - - info("Calling hide method for all tools"); - InspectorUI.toolHide(tool1); - InspectorUI.toolHide(tool2); - InspectorUI.toolHide(tool3); - - info("Checking panel states 3"); - 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"); - InspectorUI.toolShow(tool1); - InspectorUI.toolShow(tool3); - - info("Checking panel states 4"); - ok(tool1.isOpen, "Panel 1 is open"); - ok(!tool2.isOpen, "Panel 2 is closed"); - ok(tool3.isOpen, "Panel 3 is open"); - - 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() { - gBrowser.removeCurrentTab(); - InspectorUI.initTools = initToolsMethod; - finish(); -} - -function test() -{ - waitForExplicitFinish(); - ignoreAllUncaughtExceptions(); - gBrowser.selectedTab = gBrowser.addTab(); - gBrowser.selectedBrowser.addEventListener("load", function() { - gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true); - doc = content.document; - waitForFocus(createDocument, content); - }, true); - - content.location = "data:text/html,registertool tests for inspector"; -} - -function registerTools() -{ - 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) -{ - InspectorUI.registerTool({ - id: aTool.id, - label: aTool.label, - tooltiptext: aTool.tooltip, - accesskey: aTool.accesskey, - context: aTool, - get isOpen() aTool.isOpen(), - onSelect: aTool.selectNode, - show: aTool.show, - hide: aTool.hide, - unregister: aTool.destroy, - }); -} - -// Tool Object -function testTool(aToolId, aLabel, aTooltip, aAccesskey) -{ - this.id = aToolId; - this.label = aLabel; - this.tooltip = aTooltip; - this.accesskey = aAccesskey; - this._isOpen = false; -} - -testTool.prototype = { - isOpen: function BIR_isOpen() { - return this._isOpen; - }, - - selectNode: function BIR_selectNode(aNode) { - is(InspectorUI.selection, aNode, - "selectNode: currently selected node was passed: " + this.id); - }, - - show: function BIR_show(aNode) { - this._isOpen = true; - is(InspectorUI.selection, aNode, - "show: currently selected node was passed: " + this.id); - }, - - hide: function BIR_hide() { - info(this.id + " hide"); - this._isOpen = false; - }, - - 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_ruleviewstore.js b/browser/devtools/highlighter/test/browser_inspector_ruleviewstore.js index 72bb67fdc58..0eeb09898e3 100644 --- a/browser/devtools/highlighter/test/browser_inspector_ruleviewstore.js +++ b/browser/devtools/highlighter/test/browser_inspector_ruleviewstore.js @@ -48,18 +48,7 @@ let tab1; function waitForRuleView(aCallback) { - if (InspectorUI.ruleView) { - aCallback(); - return; - } - - let ruleViewFrame = InspectorUI.getToolIframe(InspectorUI.ruleViewObject); - ruleViewFrame.addEventListener("load", function(evt) { - ruleViewFrame.removeEventListener(evt.type, arguments.callee, true); - executeSoon(function() { - aCallback(); - }); - }, true); + InspectorUI.currentInspector.once("sidebaractivated-ruleview", aCallback); } function inspectorTabOpen1() @@ -77,17 +66,17 @@ function inspectorUIOpen1() // Highlight a node. div = content.document.getElementsByTagName("div")[0]; InspectorUI.inspectNode(div); + InspectorUI.stopInspecting(); // Open the rule view sidebar. waitForRuleView(ruleViewOpened1); - - InspectorUI.showSidebar(); - InspectorUI.ruleButton.click(); + InspectorUI.sidebar.show(); + InspectorUI.sidebar.activatePanel("ruleview"); } function ruleViewOpened1() { - let prop = InspectorUI.ruleView._elementStyle.rules[0].textProps[0]; + let prop = ruleView()._elementStyle.rules[0].textProps[0]; is(prop.name, "background-color", "First prop is the background color prop."); prop.setEnabled(false); @@ -124,7 +113,7 @@ function inspectorFocusTab1() function ruleViewOpened2() { - let prop = InspectorUI.ruleView._elementStyle.rules[0].textProps[0]; + let prop = ruleView()._elementStyle.rules[0].textProps[0]; is(prop.name, "background-color", "First prop is the background color prop."); ok(!prop.enabled, "First prop should be disabled."); diff --git a/browser/devtools/highlighter/test/browser_inspector_sidebarstate.js b/browser/devtools/highlighter/test/browser_inspector_sidebarstate.js index f5f137473df..4c0756beccd 100644 --- a/browser/devtools/highlighter/test/browser_inspector_sidebarstate.js +++ b/browser/devtools/highlighter/test/browser_inspector_sidebarstate.js @@ -8,22 +8,21 @@ function createDocument() doc.body.innerHTML = '

Sidebar state test

'; doc.title = "Sidebar State Test"; - // Open the sidebar and wait for the default view (the rule view) to show. - Services.obs.addObserver(inspectorRuleViewOpened, - InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, false); - InspectorUI.openInspectorUI(); - InspectorUI.showSidebar(); + + // Open the sidebar and wait for the default view (the rule view) to show. + InspectorUI.currentInspector.once("sidebaractivated-ruleview", inspectorRuleViewOpened); + + InspectorUI.sidebar.show(); + InspectorUI.sidebar.activatePanel("ruleview"); } function inspectorRuleViewOpened() { - Services.obs.removeObserver(inspectorRuleViewOpened, - InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY); - is(InspectorUI.activeSidebarPanel, "ruleview", "Rule View is selected by default"); + is(InspectorUI.sidebar.activePanel, "ruleview", "Rule View is selected by default"); // Select the computed view and turn off the inspector. - InspectorUI.activateSidebarPanel("styleinspector"); + InspectorUI.sidebar.activatePanel("computedview"); Services.obs.addObserver(inspectorClosed, InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false); @@ -46,7 +45,7 @@ function computedViewPopulated() { Services.obs.removeObserver(computedViewPopulated, "StyleInspector-populated"); - is(InspectorUI.activeSidebarPanel, "styleinspector", "Computed view is selected by default."); + is(InspectorUI.sidebar.activePanel, "computedview", "Computed view is selected by default."); finishTest(); } diff --git a/browser/devtools/highlighter/test/browser_inspector_tab_switch.js b/browser/devtools/highlighter/test/browser_inspector_tab_switch.js index 09126a3963e..8fc4d864e79 100644 --- a/browser/devtools/highlighter/test/browser_inspector_tab_switch.js +++ b/browser/devtools/highlighter/test/browser_inspector_tab_switch.js @@ -62,7 +62,7 @@ function inspectorUIOpen1() // Make sure the inspector is open. ok(InspectorUI.inspecting, "Inspector is highlighting"); ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open"); - ok(!InspectorUI.isSidebarOpen, "Inspector Sidebar is not open"); + ok(!InspectorUI.sidebar.visible, "Inspector Sidebar is not open"); ok(!InspectorUI.store.isEmpty(), "InspectorUI.store is not empty"); is(InspectorUI.store.length, 1, "Inspector.store.length = 1"); @@ -89,7 +89,6 @@ function inspectorTabOpen2() // Make sure the inspector is closed. ok(!InspectorUI.inspecting, "Inspector is not highlighting"); ok(!InspectorUI.treePanel, "Inspector Tree Panel is closed"); - ok(!InspectorUI.isSidebarOpen, "Inspector Sidebar is not open"); is(InspectorUI.store.length, 1, "Inspector.store.length = 1"); // Activate the inspector again. @@ -151,24 +150,25 @@ function inspectorOpenTreePanelTab1() is(InspectorUI.store.length, 2, "Inspector.store.length = 2"); is(InspectorUI.selection, div, "selection matches the div element"); - Services.obs.addObserver(inspectorSidebarStyleView1, "StyleInspector-opened", false); + InspectorUI.currentInspector.once("sidebaractivated-computedview", + inspectorSidebarStyleView1); executeSoon(function() { - InspectorUI.showSidebar(); - InspectorUI.activateSidebarPanel("styleinspector"); + InspectorUI.sidebar.show(); + InspectorUI.sidebar.activatePanel("computedview"); }); } function inspectorSidebarStyleView1() { - Services.obs.removeObserver(inspectorSidebarStyleView1, "StyleInspector-opened"); - ok(InspectorUI.isSidebarOpen, "Inspector Sidebar is open"); - ok(InspectorUI.stylePanel, "Inspector Has a Style Panel Instance"); - InspectorUI.sidebarTools.forEach(function(aTool) { - let btn = document.getElementById(InspectorUI.getToolbarButtonId(aTool.id)); + ok(InspectorUI.sidebar.visible, "Inspector Sidebar is open"); + ok(computedView(), "Inspector Has a computed view Instance"); + + InspectorUI.sidebar._toolObjects().forEach(function (aTool) { + let btn = aTool.button; is(btn.hasAttribute("checked"), - (aTool == InspectorUI.stylePanel.registrationObject), - "Button " + btn.id + " has correct checked attribute"); + (aTool.id == "computedview"), + "Button " + btn.label + " has correct checked attribute"); }); // Switch back to tab 2. @@ -185,7 +185,7 @@ function inspectorFocusTab2() // Make sure the inspector is still open. ok(!InspectorUI.inspecting, "Inspector is not highlighting"); ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open"); - ok(!InspectorUI.isSidebarOpen, "Inspector Sidebar is not open"); + ok(!InspectorUI.sidebar.visible, "Inspector Sidebar is not open"); is(InspectorUI.store.length, 2, "Inspector.store.length is 2"); isnot(InspectorUI.selection, div, "selection does not match the div element"); @@ -214,13 +214,13 @@ function inspectorSecondFocusTab1() is(InspectorUI.store.length, 2, "Inspector.store.length = 2"); is(InspectorUI.selection, div, "selection matches the div element"); - ok(InspectorUI.isSidebarOpen, "Inspector Sidebar is open"); - ok(InspectorUI.stylePanel, "Inspector Has a Style Panel Instance"); - InspectorUI.sidebarTools.forEach(function(aTool) { - let btn = document.getElementById(InspectorUI.getToolbarButtonId(aTool.id)); + ok(InspectorUI.sidebar.visible, "Inspector Sidebar is open"); + ok(computedView(), "Inspector Has a Style Panel Instance"); + InspectorUI.sidebar._toolObjects().forEach(function(aTool) { + let btn = aTool.button; is(btn.hasAttribute("checked"), - (aTool == InspectorUI.stylePanel.registrationObject), - "Button " + btn.id + " has correct checked attribute"); + (aTool.id == "computedview"), + "Button " + btn.label + " has correct checked attribute"); }); // Switch back to tab 2. diff --git a/browser/devtools/highlighter/test/head.js b/browser/devtools/highlighter/test/head.js index 791a05155aa..72ce49b50de 100644 --- a/browser/devtools/highlighter/test/head.js +++ b/browser/devtools/highlighter/test/head.js @@ -89,6 +89,20 @@ function midPoint(aPointA, aPointB) return pointC; } +function computedView() +{ + return InspectorUI.sidebar._toolContext("computedview"); +} + +function computedViewTree() +{ + return computedView().view; +} + +function ruleView() +{ + return InspectorUI.sidebar._toolContext("ruleview").view; +} function synthesizeKeyFromKeyTag(aKeyId) { let key = document.getElementById(aKeyId); isnot(key, null, "Successfully retrieved the node"); diff --git a/browser/devtools/styleinspector/CssHtmlTree.jsm b/browser/devtools/styleinspector/CssHtmlTree.jsm index e550a5b2802..d864fa32fb2 100644 --- a/browser/devtools/styleinspector/CssHtmlTree.jsm +++ b/browser/devtools/styleinspector/CssHtmlTree.jsm @@ -306,12 +306,10 @@ CssHtmlTree.prototype = { this._refreshProcess = new UpdateProcess(this.win, CssHtmlTree.propertyNames, { onItem: function(aPropertyName) { // Per-item callback. - if (this.viewedElement != aElement || !this.styleInspector.isOpen()) { - return false; - } let propView = new PropertyView(this, aPropertyName); fragment.appendChild(propView.buildMain()); fragment.appendChild(propView.buildSelectorContainer()); + if (propView.visible) { this.numVisibleProperties++; } @@ -324,7 +322,14 @@ CssHtmlTree.prototype = { this.propertyContainer.appendChild(fragment); this.noResults.hidden = this.numVisibleProperties > 0; this._refreshProcess = null; - Services.obs.notifyObservers(null, "StyleInspector-populated", null); + + // If a refresh was scheduled during the building, complete it. + if (this._needsRefresh) { + delete this._needsRefresh; + this.refreshPanel(); + } else { + Services.obs.notifyObservers(null, "StyleInspector-populated", null); + } }.bind(this)}); this._refreshProcess.schedule(); @@ -336,6 +341,15 @@ CssHtmlTree.prototype = { */ refreshPanel: function CssHtmlTree_refreshPanel() { + // If we're still in the process of creating the initial layout, + // leave it alone. + if (!this.htmlComplete) { + if (this._refreshProcess) { + this._needsRefresh = true; + } + return; + } + if (this._refreshProcess) { this._refreshProcess.cancel(); } @@ -355,7 +369,7 @@ CssHtmlTree.prototype = { }.bind(this), onDone: function() { this._refreshProcess = null; - this.noResults.hidden = this.numVisibleProperties > 0 + this.noResults.hidden = this.numVisibleProperties > 0; Services.obs.notifyObservers(null, "StyleInspector-populated", null); }.bind(this) }); @@ -1203,11 +1217,6 @@ SelectorView.prototype = { result = CssLogic.getShortName(source); } - aElement.parentNode.querySelector(".rule-link > a"). - addEventListener("click", function(aEvent) { - this.tree.styleInspector.selectFromPath(source); - aEvent.preventDefault(); - }.bind(this), false); result += ".style"; } diff --git a/browser/devtools/styleinspector/CssLogic.jsm b/browser/devtools/styleinspector/CssLogic.jsm index f3186913f28..3cdee5e21f8 100644 --- a/browser/devtools/styleinspector/CssLogic.jsm +++ b/browser/devtools/styleinspector/CssLogic.jsm @@ -648,6 +648,10 @@ CssLogic.prototype = { this._passId++; this._matchedRules = []; + if (!element) { + return; + } + do { let status = this.viewedElement === element ? CssLogic.STATUS.MATCHED : CssLogic.STATUS.PARENT_MATCH; diff --git a/browser/devtools/styleinspector/CssRuleView.jsm b/browser/devtools/styleinspector/CssRuleView.jsm index 24fa8f02468..df6399f0b5f 100644 --- a/browser/devtools/styleinspector/CssRuleView.jsm +++ b/browser/devtools/styleinspector/CssRuleView.jsm @@ -763,15 +763,15 @@ CssRuleView.prototype = { this.clear(); + if (this._elementStyle) { + delete this._elementStyle; + } + this._viewedElement = aElement; if (!this._viewedElement) { return; } - if (this._elementStyle) { - delete this._elementStyle.onChanged; - } - this._elementStyle = new ElementStyle(aElement, this.store); this._elementStyle.onChanged = function() { this._changed(); diff --git a/browser/devtools/styleinspector/StyleInspector.jsm b/browser/devtools/styleinspector/StyleInspector.jsm index 35a6876acc1..ef774f37326 100644 --- a/browser/devtools/styleinspector/StyleInspector.jsm +++ b/browser/devtools/styleinspector/StyleInspector.jsm @@ -23,6 +23,7 @@ * Contributor(s): * Mike Ratcliffe (Original Author) * Rob Campbell + * Dave Camp * * 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 @@ -43,316 +44,217 @@ const Ci = Components.interfaces; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource:///modules/devtools/CssRuleView.jsm"); +Cu.import("resource:///modules/inspector.jsm"); -var EXPORTED_SYMBOLS = ["StyleInspector"]; +// This module doesn't currently export any symbols directly, it only +// registers inspector tools. +var EXPORTED_SYMBOLS = []; /** - * StyleInspector Constructor Function. - * @param {window} aContext, the chrome window context we're calling from. - * @param {InspectorUI} aIUI (optional) An InspectorUI instance if called from the - * Highlighter. + * Lookup l10n string from a string bundle. + * @param {string} aName The key to lookup. + * @returns A localized version of the given key. */ -function StyleInspector(aContext, aIUI) +function l10n(aName) { - this._init(aContext, aIUI); + try { + return _strings.GetStringFromName(aName); + } catch (ex) { + Services.console.logStringMessage("Error reading '" + aName + "'"); + throw new Error("l10n error with " + aName); + } } -StyleInspector.prototype = { +function RegisterStyleTools() +{ + // Register the rules view + if (Services.prefs.getBoolPref("devtools.ruleview.enabled")) { + InspectorUI.registerSidebar({ + id: "ruleview", + label: l10n("ruleView.label"), + tooltiptext: l10n("ruleView.tooltiptext"), + accesskey: l10n("ruleView.accesskey"), + contentURL: "chrome://browser/content/devtools/cssruleview.xul", + load: function(aInspector, aFrame) new RuleViewTool(aInspector, aFrame), + destroy: function(aContext) aContext.destroy() + }); + } - /** - * Initialization method called from constructor. - * @param {window} aContext, the chrome window context we're calling from. - * @param {InspectorUI} aIUI (optional) An InspectorUI instance if called from - * the Highlighter. - */ - _init: function SI__init(aContext, aIUI) - { - this.window = aContext; - this.IUI = aIUI; - this.document = this.window.document; - this.cssLogic = new CssLogic(); - this.panelReady = false; - this.iframeReady = false; + // Register the computed styles view + if (Services.prefs.getBoolPref("devtools.styleinspector.enabled")) { + InspectorUI.registerSidebar({ + id: "computedview", + label: this.l10n("style.highlighter.button.label2"), + tooltiptext: this.l10n("style.highlighter.button.tooltip2"), + accesskey: this.l10n("style.highlighter.accesskey2"), + contentURL: "chrome://browser/content/devtools/csshtmltree.xul", + load: function(aInspector, aFrame) new ComputedViewTool(aInspector, aFrame), + destroy: function(aContext) aContext.destroy() + }); + } +} - // Were we invoked from the Highlighter? - if (this.IUI) { - this.openDocked = true; - let isOpen = this.isOpen.bind(this); +function RuleViewTool(aInspector, aFrame) +{ + this.inspector = aInspector; + this.chromeWindow = this.inspector.chromeWindow; + this.doc = aFrame.contentDocument; + this.view = new CssRuleView(this.doc, aInspector._getStore("ruleview")); + this.doc.documentElement.appendChild(this.view.element); - this.registrationObject = { - id: "styleinspector", - label: this.l10n("style.highlighter.button.label2"), - tooltiptext: this.l10n("style.highlighter.button.tooltip2"), - accesskey: this.l10n("style.highlighter.accesskey2"), - context: this, - get isOpen() isOpen(), - onSelect: this.selectNode, - onChanged: this.updateNode, - show: this.open, - hide: this.close, - dim: this.dimTool, - panel: null, - unregister: this.destroy, - sidebar: true, - }; + this._changeHandler = function() { + this.inspector.markDirty(); + this.inspector.change("ruleview"); + }.bind(this); - // Register the registrationObject with the Highlighter - this.IUI.registerTool(this.registrationObject); - this.createSidebarContent(true); - } - }, + this.view.element.addEventListener("CssRuleViewChanged", this._changeHandler) - /** - * Create the iframe in the IUI sidebar's tab panel. - * @param {Boolean} aPreserveOnHide Prevents destroy from being called. - */ - createSidebarContent: function SI_createSidebarContent(aPreserveOnHide) - { - this.preserveOnHide = !!aPreserveOnHide; + this._cssLinkHandler = function(aEvent) { + let rule = aEvent.detail.rule; + let styleSheet = rule.sheet; + let doc = this.chromeWindow.content.document; + let styleSheets = doc.styleSheets; + let contentSheet = false; + let line = rule.ruleLine || 0; - let boundIframeOnLoad = function loadedInitializeIframe() { - if (this.iframe && - this.iframe.getAttribute("src") == - "chrome://browser/content/devtools/csshtmltree.xul") { - let selectedNode = this.selectedNode || null; - this.cssHtmlTree = new CssHtmlTree(this); - this.cssLogic.highlight(selectedNode); - this.cssHtmlTree.highlight(selectedNode); - this.iframe.removeEventListener("load", boundIframeOnLoad, true); - this.iframeReady = true; - - // Now that we've loaded, select any node we were previously asked - // to show. - this.selectNode(this.selectedNode); - - Services.obs.notifyObservers(null, "StyleInspector-opened", null); + // Array.prototype.indexOf always returns -1 here so we loop through + // the styleSheets object instead. + for each (let sheet in styleSheets) { + if (sheet == styleSheet) { + contentSheet = true; + break; } - }.bind(this); - - this.iframe = this.IUI.getToolIframe(this.registrationObject); - - this.iframe.addEventListener("load", boundIframeOnLoad, true); - }, - - /** - * Factory method to create the actual style panel - * @param {Boolean} aPreserveOnHide Prevents destroy from being called - * onpopuphide. USE WITH CAUTION: When this value is set to true then you are - * responsible to manually call destroy from outside the style inspector. - * @param {function} aCallback (optional) callback to fire when ready. - */ - createPanel: function SI_createPanel(aPreserveOnHide, aCallback) - { - let popupSet = this.document.getElementById("mainPopupSet"); - let panel = this.document.createElement("panel"); - this.preserveOnHide = !!aPreserveOnHide; - - panel.setAttribute("class", "styleInspector"); - panel.setAttribute("orient", "vertical"); - panel.setAttribute("ignorekeys", "true"); - panel.setAttribute("noautofocus", "true"); - panel.setAttribute("noautohide", "true"); - panel.setAttribute("titlebar", "normal"); - panel.setAttribute("close", "true"); - panel.setAttribute("label", this.l10n("panelTitle")); - panel.setAttribute("width", 350); - panel.setAttribute("height", this.window.screen.height / 2); - - let iframe = this.document.createElement("iframe"); - let boundIframeOnLoad = function loadedInitializeIframe() - { - this.iframe.removeEventListener("load", boundIframeOnLoad, true); - this.iframeReady = true; - if (aCallback) - aCallback(this); - }.bind(this); - - iframe.flex = 1; - iframe.setAttribute("tooltip", "aHTMLTooltip"); - iframe.addEventListener("load", boundIframeOnLoad, true); - iframe.setAttribute("src", "chrome://browser/content/devtools/csshtmltree.xul"); - - panel.appendChild(iframe); - popupSet.appendChild(panel); - - this._boundPopupShown = this.popupShown.bind(this); - this._boundPopupHidden = this.popupHidden.bind(this); - panel.addEventListener("popupshown", this._boundPopupShown, false); - panel.addEventListener("popuphidden", this._boundPopupHidden, false); - - this.panel = panel; - this.iframe = iframe; - - return panel; - }, - - /** - * Event handler for the popupshown event. - */ - popupShown: function SI_popupShown() - { - this.panelReady = true; - if (this.iframeReady) { - this.cssHtmlTree = new CssHtmlTree(this); - let selectedNode = this.selectedNode || null; - this.cssLogic.highlight(selectedNode); - this.cssHtmlTree.highlight(selectedNode); - Services.obs.notifyObservers(null, "StyleInspector-opened", null); } - }, - /** - * Event handler for the popuphidden event. - * Hide the popup and conditionally destroy it - */ - popupHidden: function SI_popupHidden() - { - if (this.preserveOnHide) { - Services.obs.notifyObservers(null, "StyleInspector-closed", null); + if (contentSheet) { + this.chromeWindow.StyleEditor.openChrome(styleSheet, line); } else { - this.destroy(); - } - }, - - /** - * Check if the style inspector is open. - * @returns boolean - */ - isOpen: function SI_isOpen() - { - return this.openDocked ? this.IUI.isSidebarOpen && - (this.IUI.sidebarDeck.selectedPanel == this.iframe) : - this.panel && this.panel.state && this.panel.state == "open"; - }, - - isLoaded: function SI_isLoaded() - { - return this.openDocked ? this.iframeReady : this.iframeReady && this.panelReady; - }, - - /** - * Select from Path (via CssHtmlTree_pathClick) - * @param aNode The node to inspect. - */ - selectFromPath: function SI_selectFromPath(aNode) - { - if (this.IUI && this.IUI.selection) { - if (aNode != this.IUI.selection) { - this.IUI.inspectNode(aNode); + let href = styleSheet ? styleSheet.href : ""; + if (rule.elementStyle.element) { + href = rule.elementStyle.element.ownerDocument.location.href; } - } else { - this.selectNode(aNode); + let viewSourceUtils = this.chromeWindow.gViewSourceUtils; + viewSourceUtils.viewSource(href, null, doc, line); + } + }.bind(this); + + this.view.element.addEventListener("CssRuleViewCSSLinkClicked", + this._cssLinkHandler); + + this._onSelect = this.onSelect.bind(this); + this.inspector.on("select", this._onSelect); + + this._onChange = this.onChange.bind(this); + this.inspector.on("change", this._onChange); + + this.onSelect(); +} + +RuleViewTool.prototype = { + onSelect: function RVT_onSelect(aEvent, aFrom) { + let node = this.inspector.selection; + if (!node) { + this.view.highlight(null); + return; + } + + if (this.inspector.locked) { + this.view.highlight(node); } }, - /** - * Select a node to inspect in the Style Inspector panel - * @param aNode The node to inspect. - */ - selectNode: function SI_selectNode(aNode) - { - this.selectedNode = aNode; - if (this.isLoaded() && !this.dimmed) { - this.cssLogic.highlight(aNode); - this.cssHtmlTree.highlight(aNode); + onChange: function RVT_onChange(aEvent, aFrom) { + // We're not that good yet at refreshing, only + // refresh when we really need to. + if (aFrom != "pseudoclass") { + return; + } + + if (this.inspector.locked) { + this.view.nodeChanged(); } }, - /** - * Update the display for the currently-selected node. - */ - updateNode: function SI_updateNode() + destroy: function RVT_destroy() { + this.inspector.removeListener("select", this._onSelect); + this.inspector.removeListener("change", this._onChange); + this.view.element.removeEventListener("CssRuleViewChanged", + this._changeHandler); + this.view.element.removeEventListener("CssRuleViewCSSLinkClicked", + this._cssLinkHandler); + this.doc.documentElement.removeChild(this.view.element); + + this.view.destroy(); + + delete this._changeHandler; + delete this.view; + delete this.doc; + delete this.inspector; + } +} + +function ComputedViewTool(aInspector, aFrame) +{ + this.inspector = aInspector; + this.iframe = aFrame; + this.window = aInspector.chromeWindow; + this.document = this.window.document; + this.cssLogic = new CssLogic(); + this.view = new CssHtmlTree(this); + + this._onSelect = this.onSelect.bind(this); + this.inspector.on("select", this._onSelect); + + this._onChange = this.onChange.bind(this); + this.inspector.on("change", this._onChange); + + // Since refreshes of the computed view are non-destructive, + // refresh when the tab is changed so we can notice script-driven + // changes. + this.inspector.on("sidebaractivated", this._onChange); + + this.cssLogic.highlight(null); + this.view.highlight(null); + + this.onSelect(); +} + +ComputedViewTool.prototype = { + onSelect: function CVT_onSelect(aEvent) { - if (this.isLoaded() && !this.dimmed) { - this.cssLogic.highlight(this.selectedNode); - this.cssHtmlTree.refreshPanel(); + if (this.inspector.locked) { + this.cssLogic.highlight(this.inspector.selection); + this.view.highlight(this.inspector.selection); } }, - /** - * Dim or undim a panel by setting or removing a dimmed attribute. - * @param aState - * true = dim, false = undim - */ - dimTool: function SI_dimTool(aState) + onChange: function CVT_change(aEvent, aFrom) { - this.dimmed = aState; + if (aFrom == "computedview" || + this.inspector.selection != this.cssLogic.viewedElement) { + return; + } + + this.cssLogic.highlight(this.inspector.selection); + this.view.refreshPanel(); }, - /** - * Open the panel. - * @param {DOMNode} aSelection the (optional) DOM node to select. - */ - open: function SI_open(aSelection) + destroy: function CVT_destroy(aContext) { - this.selectNode(aSelection); - if (this.openDocked) { - if (!this.iframeReady) { - this.iframe.setAttribute("src", "chrome://browser/content/devtools/csshtmltree.xul"); - } - } else { - this.panel.openPopup(this.window.gBrowser.selectedBrowser, "end_before", 0, 0, - false, false); - } - }, - - /** - * Close the panel. - */ - close: function SI_close() - { - if (this.openDocked) { - Services.obs.notifyObservers(null, "StyleInspector-closed", null); - } else { - this.panel.hidePopup(); - } - }, - - /** - * Memoized lookup of a l10n string from a string bundle. - * @param {string} aName The key to lookup. - * @returns A localized version of the given key. - */ - l10n: function SI_l10n(aName) - { - try { - return _strings.GetStringFromName(aName); - } catch (ex) { - Services.console.logStringMessage("Error reading '" + aName + "'"); - throw new Error("l10n error with " + aName); - } - }, - - /** - * Destroy the style panel, remove listeners etc. - */ - destroy: function SI_destroy() - { - if (this.isOpen()) - this.close(); - if (this.cssHtmlTree) - this.cssHtmlTree.destroy(); - if (this.iframe) { - this.iframe.parentNode.removeChild(this.iframe); - delete this.iframe; - } + this.inspector.removeListener("select", this._onSelect); + this.inspector.removeListener("change", this._onChange); + this.inspector.removeListener("sidebaractivated", this._onChange); + this.view.destroy(); + delete this.view; delete this.cssLogic; delete this.cssHtmlTree; - if (this.panel) { - this.panel.removeEventListener("popupshown", this._boundPopupShown, false); - this.panel.removeEventListener("popuphidden", this._boundPopupHidden, false); - delete this._boundPopupShown; - delete this._boundPopupHidden; - this.panel.parentNode.removeChild(this.panel); - delete this.panel; - } - delete this.doc; - delete this.win; - delete CssHtmlTree.win; - Services.obs.notifyObservers(null, "StyleInspector-closed", null); - }, -}; + delete this.iframe; + delete this.window; + delete this.document; + } +} XPCOMUtils.defineLazyGetter(this, "_strings", function() Services.strings .createBundle("chrome://browser/locale/devtools/styleinspector.properties")); @@ -368,3 +270,5 @@ XPCOMUtils.defineLazyGetter(this, "CssHtmlTree", function() { Cu.import("resource:///modules/devtools/CssHtmlTree.jsm", tmp); return tmp.CssHtmlTree; }); + +RegisterStyleTools(); diff --git a/browser/devtools/styleinspector/test/browser_bug589375_keybindings.js b/browser/devtools/styleinspector/test/browser_bug589375_keybindings.js index a059185900c..0e8bacbfbd0 100644 --- a/browser/devtools/styleinspector/test/browser_bug589375_keybindings.js +++ b/browser/devtools/styleinspector/test/browser_bug589375_keybindings.js @@ -14,19 +14,12 @@ function createDocument() 'Some styled text' + ''; doc.title = "Style Inspector key binding test"; - stylePanel = new StyleInspector(window); - Services.obs.addObserver(runStyleInspectorTests, "StyleInspector-opened", false); - stylePanel.createPanel(false, function() { - stylePanel.open(doc.body); - }); + stylePanel = new ComputedViewPanel(window); + stylePanel.createPanel(doc.body, runStyleInspectorTests); } function runStyleInspectorTests() { - Services.obs.removeObserver(runStyleInspectorTests, "StyleInspector-opened", false); - - ok(stylePanel.isOpen(), "style inspector is open"); - Services.obs.addObserver(SI_test, "StyleInspector-populated", false); SI_inspectNode(); } @@ -66,8 +59,8 @@ function SI_test() testKey(iframe.contentWindow, "VK_RETURN", rulesTable); checkHelpLinkKeybinding(); - Services.obs.addObserver(finishUp, "StyleInspector-closed", false); - stylePanel.close(); + stylePanel.destroy(); + finishUp(); }); info("Adding focus event handler to search filter"); @@ -94,6 +87,7 @@ function getFirstVisiblePropertyView() propView = aPropView; return true; } + return false; }); return propView; @@ -127,8 +121,6 @@ function checkHelpLinkKeybinding() function finishUp() { - Services.obs.removeObserver(finishUp, "StyleInspector-closed", false); - ok(!stylePanel.isOpen(), "style inspector is closed"); doc = stylePanel = null; gBrowser.removeCurrentTab(); finish(); diff --git a/browser/devtools/styleinspector/test/browser_bug683672.js b/browser/devtools/styleinspector/test/browser_bug683672.js index a37df7a1871..fc0388cce2d 100644 --- a/browser/devtools/styleinspector/test/browser_bug683672.js +++ b/browser/devtools/styleinspector/test/browser_bug683672.js @@ -26,25 +26,18 @@ function tabLoaded() browser.removeEventListener("load", tabLoaded, true); doc = content.document; // ok(StyleInspector.isEnabled, "style inspector preference is enabled"); - stylePanel = new StyleInspector(window); - Services.obs.addObserver(runTests, "StyleInspector-opened", false); - stylePanel.createPanel(false, function() { - stylePanel.open(doc.body); - }); + stylePanel = new ComputedViewPanel(window); + stylePanel.createPanel(doc.body, runTests); } function runTests() { - Services.obs.removeObserver(runTests, "StyleInspector-opened", false); - - ok(stylePanel.isOpen(), "style inspector is open"); - testMatchedSelectors(); //testUnmatchedSelectors(); info("finishing up"); - Services.obs.addObserver(finishUp, "StyleInspector-closed", false); - stylePanel.close(); + stylePanel.destroy(); + finishUp(); } function testMatchedSelectors() @@ -97,8 +90,6 @@ function testUnmatchedSelectors() function finishUp() { - Services.obs.removeObserver(finishUp, "StyleInspector-closed", false); - ok(!stylePanel.isOpen(), "style inspector is closed"); doc = stylePanel = null; gBrowser.removeCurrentTab(); finish(); diff --git a/browser/devtools/styleinspector/test/browser_bug722196_property_view_media_queries.js b/browser/devtools/styleinspector/test/browser_bug722196_property_view_media_queries.js index fa1602b23ec..c2f94a24c69 100644 --- a/browser/devtools/styleinspector/test/browser_bug722196_property_view_media_queries.js +++ b/browser/devtools/styleinspector/test/browser_bug722196_property_view_media_queries.js @@ -22,19 +22,12 @@ function docLoaded() { browser.removeEventListener("load", docLoaded, true); doc = content.document; - stylePanel = new StyleInspector(window); - Services.obs.addObserver(checkSheets, "StyleInspector-opened", false); - stylePanel.createPanel(false, function() { - stylePanel.open(doc.body); - }); + stylePanel = new ComputedViewPanel(window); + stylePanel.createPanel(doc.body, checkSheets); } function checkSheets() { - Services.obs.removeObserver(checkSheets, "StyleInspector-opened", false); - - ok(stylePanel.isOpen(), "style inspector is open"); - var div = doc.querySelector("div"); ok(div, "captain, we have the div"); @@ -55,13 +48,12 @@ function checkSheets() is(cssLogic._matchedRules[1][0].source, source2, "rule.source gives correct output for rule 2"); - Services.obs.addObserver(finishUp, "StyleInspector-closed", false); - stylePanel.close(); + stylePanel.destroy(); + finishUp(); } function finishUp() { - Services.obs.removeObserver(finishUp, "StyleInspector-closed", false); doc = null; gBrowser.removeCurrentTab(); finish(); diff --git a/browser/devtools/styleinspector/test/browser_bug_692400_element_style.js b/browser/devtools/styleinspector/test/browser_bug_692400_element_style.js index 650b9b7fed0..56175d968d1 100644 --- a/browser/devtools/styleinspector/test/browser_bug_692400_element_style.js +++ b/browser/devtools/styleinspector/test/browser_bug_692400_element_style.js @@ -12,16 +12,14 @@ function createDocument() doc.body.innerHTML = "
"; doc.title = "Style Inspector Selector Text Test"; - stylePanel = new StyleInspector(window); + stylePanel = new ComputedViewPanel(window); + Services.obs.addObserver(SI_checkText, "StyleInspector-populated", false); - stylePanel.createPanel(false, function() { - Services.obs.addObserver(SI_checkText, "StyleInspector-populated", false); + let span = doc.querySelector("div"); + ok(span, "captain, we have the test div"); - let span = doc.querySelector("div"); - ok(span, "captain, we have the test div"); - stylePanel.open(span); - }); + stylePanel.createPanel(span); } function SI_checkText() @@ -34,6 +32,7 @@ function SI_checkText() propertyView = aView; return true; } + return false; }); ok(propertyView, "found PropertyView for color"); @@ -57,13 +56,12 @@ function SI_checkText() ok(false, "getting the selector text should not raise an exception"); } - Services.obs.addObserver(finishUp, "StyleInspector-closed", false); - stylePanel.close(); + stylePanel.destroy(); + finishUp(); } function finishUp() { - Services.obs.removeObserver(finishUp, "StyleInspector-closed", false); doc = stylePanel = null; gBrowser.removeCurrentTab(); finish(); diff --git a/browser/devtools/styleinspector/test/browser_computedview_734259_style_editor_link.js b/browser/devtools/styleinspector/test/browser_computedview_734259_style_editor_link.js index a35c4ffc469..3e6c40fbb6e 100644 --- a/browser/devtools/styleinspector/test/browser_computedview_734259_style_editor_link.js +++ b/browser/devtools/styleinspector/test/browser_computedview_734259_style_editor_link.js @@ -30,19 +30,15 @@ function createDocument() let span = doc.querySelector("span"); ok(span, "captain, we have the span"); - stylePanel = new StyleInspector(window); Services.obs.addObserver(testInlineStyle, "StyleInspector-populated", false); - stylePanel.createPanel(false, function() { - stylePanel.open(span); - }); + stylePanel = new ComputedViewPanel(window); + stylePanel.createPanel(span); } function testInlineStyle() { Services.obs.removeObserver(testInlineStyle, "StyleInspector-populated", false); - ok(stylePanel.isOpen(), "style inspector is open"); - info("expanding property"); expandProperty(0, function propertyExpanded() { Services.ww.registerNotification(function onWindow(aSubject, aTopic) { @@ -113,8 +109,8 @@ function validateStyleEditorSheet(aEditor) info("closing window"); win.close(); - Services.obs.addObserver(finishUp, "StyleInspector-closed", false); - stylePanel.close(); + stylePanel.destroy(); + finishUp(); } function expandProperty(aIndex, aCallback) @@ -138,8 +134,6 @@ function getLinkByIndex(aIndex) function finishUp() { - Services.obs.removeObserver(finishUp, "StyleInspector-closed", false); - ok(!stylePanel.isOpen(), "style inspector is closed"); doc = win = stylePanel = null; gBrowser.removeCurrentTab(); finish(); diff --git a/browser/devtools/styleinspector/test/browser_computedview_bug_703643_context_menu_copy.js b/browser/devtools/styleinspector/test/browser_computedview_bug_703643_context_menu_copy.js index b8e7000bf5e..4b3f1376d2a 100644 --- a/browser/devtools/styleinspector/test/browser_computedview_bug_703643_context_menu_copy.js +++ b/browser/devtools/styleinspector/test/browser_computedview_bug_703643_context_menu_copy.js @@ -35,19 +35,15 @@ function createDocument() let span = doc.querySelector("span"); ok(span, "captain, we have the span"); - stylePanel = new StyleInspector(window); + stylePanel = new ComputedViewPanel(window); Services.obs.addObserver(runStyleInspectorTests, "StyleInspector-populated", false); - stylePanel.createPanel(false, function() { - stylePanel.open(span); - }); + stylePanel.createPanel(span); } function runStyleInspectorTests() { Services.obs.removeObserver(runStyleInspectorTests, "StyleInspector-populated", false); - ok(stylePanel.isOpen(), "style inspector is open"); - cssHtmlTree = stylePanel.cssHtmlTree; let contentDocument = stylePanel.iframe.contentDocument; @@ -166,14 +162,12 @@ function failedClipboard(aExpectedPattern, aCallback) function closeStyleInspector() { - Services.obs.addObserver(finishUp, "StyleInspector-closed", false); - stylePanel.close(); + stylePanel.destroy(); + finishUp(); } function finishUp() { - Services.obs.removeObserver(finishUp, "StyleInspector-closed", false); - ok(!stylePanel.isOpen(), "style inspector is closed"); doc = stylePanel = cssHtmlTree = null; gBrowser.removeCurrentTab(); finish(); diff --git a/browser/devtools/styleinspector/test/browser_ruleview_734259_style_editor_link.js b/browser/devtools/styleinspector/test/browser_ruleview_734259_style_editor_link.js index 9ca43c8202b..098e45d68be 100644 --- a/browser/devtools/styleinspector/test/browser_ruleview_734259_style_editor_link.js +++ b/browser/devtools/styleinspector/test/browser_ruleview_734259_style_editor_link.js @@ -63,18 +63,14 @@ function inspectorUIOpen() InspectorUI.stopInspecting(); is(InspectorUI.selection, div, "selection matches the div element"); - Services.obs.addObserver(testInlineStyle, - InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, false); + InspectorUI.currentInspector.once("sidebaractivated-ruleview", testInlineStyle); - InspectorUI.showSidebar(); - InspectorUI.openRuleView(); + InspectorUI.sidebar.show(); + InspectorUI.sidebar.activatePanel("ruleview"); } function testInlineStyle() { - Services.obs.removeObserver(testInlineStyle, - InspectorUI.INSPECTOR_NOTIFICATIONS.RULEVIEWREADY, false); - executeSoon(function() { info("clicking an inline style"); @@ -149,16 +145,15 @@ function validateStyleEditorSheet(aEditor) function getLinkByIndex(aIndex) { - let ruleView = document.querySelector("#devtools-sidebar-iframe-ruleview"); - let contentDoc = ruleView.contentDocument; + let contentDoc = ruleView().doc; + contentWindow = contentDoc.defaultView; let links = contentDoc.querySelectorAll(".ruleview-rule-source"); - contentWindow = ruleView.contentWindow; return links[aIndex]; } function finishup() { - InspectorUI.hideSidebar(); + InspectorUI.sidebar.hide(); InspectorUI.closeInspectorUI(); gBrowser.removeCurrentTab(); doc = contentWindow = win = null; diff --git a/browser/devtools/styleinspector/test/browser_ruleview_bug_703643_context_menu_copy.js b/browser/devtools/styleinspector/test/browser_ruleview_bug_703643_context_menu_copy.js index fe392fee8b2..7e13280f23b 100644 --- a/browser/devtools/styleinspector/test/browser_ruleview_bug_703643_context_menu_copy.js +++ b/browser/devtools/styleinspector/test/browser_ruleview_bug_703643_context_menu_copy.js @@ -11,6 +11,11 @@ XPCOMUtils.defineLazyGetter(this, "osString", function() { return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS; }); +function ruleViewFrame() +{ + return InspectorUI.sidebar._tools["ruleview"].frame; +} + function createDocument() { doc.body.innerHTML = '' + 'Some styled text'; doc.title = "Tests that the no results placeholder works properly"; - stylePanel = new StyleInspector(window); - Services.obs.addObserver(runStyleInspectorTests, "StyleInspector-opened", false); - stylePanel.createPanel(false, function() { - stylePanel.open(doc.body); - }); + stylePanel = new ComputedViewPanel(window); + stylePanel.createPanel(doc.body, runStyleInspectorTests); } function runStyleInspectorTests() { - Services.obs.removeObserver(runStyleInspectorTests, "StyleInspector-opened", false); - - ok(stylePanel.isOpen(), "style inspector is open"); - Services.obs.addObserver(SI_AddFilterText, "StyleInspector-populated", false); let span = doc.querySelector("#matches"); @@ -90,14 +83,12 @@ function SI_checkPlaceholderHidden() is(display, "none", "placeholder is hidden"); - Services.obs.addObserver(finishUp, "StyleInspector-closed", false); - stylePanel.close(); + stylePanel.destroy(); + finishUp(); } function finishUp() { - Services.obs.removeObserver(finishUp, "StyleInspector-closed", false); - ok(!stylePanel.isOpen(), "style inspector is closed"); doc = stylePanel = null; gBrowser.removeCurrentTab(); finish(); diff --git a/browser/devtools/styleinspector/test/head.js b/browser/devtools/styleinspector/test/head.js index 5099b5c7564..01a1a616746 100644 --- a/browser/devtools/styleinspector/test/head.js +++ b/browser/devtools/styleinspector/test/head.js @@ -36,11 +36,13 @@ * ***** END LICENSE BLOCK ***** */ let tempScope = {}; -Cu.import("resource:///modules/devtools/StyleInspector.jsm", tempScope); +Cu.import("resource:///modules/devtools/CssLogic.jsm", tempScope); +Cu.import("resource:///modules/devtools/CssHtmlTree.jsm", tempScope); Cu.import("resource://gre/modules/HUDService.jsm", tempScope); -let StyleInspector = tempScope.StyleInspector; let HUDService = tempScope.HUDService; let ConsoleUtils = tempScope.ConsoleUtils; +let CssLogic = tempScope.CssLogic; +let CssHtmlTree = tempScope.CssHtmlTree; function log(aMsg) { @@ -192,6 +194,149 @@ function tearDown() tab = browser = hudId = hud = filterBox = outputNode = cs = null; } +/** + * Shows the computed view in its own panel. + */ +function ComputedViewPanel(aContext) +{ + this._init(aContext); +} + +ComputedViewPanel.prototype = { + _init: function CVP_init(aContext) + { + this.window = aContext; + this.document = this.window.document; + this.cssLogic = new CssLogic(); + this.panelReady = false; + this.iframeReady = false; + }, + + /** + * Factory method to create the actual style panel + * @param {function} aCallback (optional) callback to fire when ready. + */ + createPanel: function SI_createPanel(aSelection, aCallback) + { + let popupSet = this.document.getElementById("mainPopupSet"); + let panel = this.document.createElement("panel"); + + panel.setAttribute("class", "styleInspector"); + panel.setAttribute("orient", "vertical"); + panel.setAttribute("ignorekeys", "true"); + panel.setAttribute("noautofocus", "true"); + panel.setAttribute("noautohide", "true"); + panel.setAttribute("titlebar", "normal"); + panel.setAttribute("close", "true"); + panel.setAttribute("label", "Computed View"); + panel.setAttribute("width", 350); + panel.setAttribute("height", this.window.screen.height / 2); + + this._openCallback = aCallback; + this.selectedNode = aSelection; + + let iframe = this.document.createElement("iframe"); + let boundIframeOnLoad = function loadedInitializeIframe() + { + this.iframeReady = true; + this.iframe.removeEventListener("load", boundIframeOnLoad, true); + this.panel.openPopup(this.window.gBrowser.selectedBrowser, "end_before", 0, 0, false, false); + }.bind(this); + + iframe.flex = 1; + iframe.setAttribute("tooltip", "aHTMLTooltip"); + iframe.addEventListener("load", boundIframeOnLoad, true); + iframe.setAttribute("src", "chrome://browser/content/devtools/csshtmltree.xul"); + + panel.appendChild(iframe); + popupSet.appendChild(panel); + + this._boundPopupShown = this.popupShown.bind(this); + panel.addEventListener("popupshown", this._boundPopupShown, false); + + this.panel = panel; + this.iframe = iframe; + + return panel; + }, + + /** + * Event handler for the popupshown event. + */ + popupShown: function SI_popupShown() + { + this.panelReady = true; + this.cssHtmlTree = new CssHtmlTree(this); + let selectedNode = this.selectedNode || null; + this.cssLogic.highlight(selectedNode); + this.cssHtmlTree.highlight(selectedNode); + if (this._openCallback) { + this._openCallback(); + delete this._openCallback; + } + }, + + isLoaded: function SI_isLoaded() + { + return this.iframeReady && this.panelReady; + }, + + /** + * Select from Path (via CssHtmlTree_pathClick) + * @param aNode The node to inspect. + */ + selectFromPath: function SI_selectFromPath(aNode) + { + this.selectNode(aNode); + }, + + /** + * Select a node to inspect in the Style Inspector panel + * @param aNode The node to inspect. + */ + selectNode: function SI_selectNode(aNode) + { + this.selectedNode = aNode; + + if (this.isLoaded()) { + this.cssLogic.highlight(aNode); + this.cssHtmlTree.highlight(aNode); + } + }, + + /** + * Destroy the style panel, remove listeners etc. + */ + destroy: function SI_destroy() + { + this.panel.hidePopup(); + + if (this.cssHtmlTree) { + this.cssHtmlTree.destroy(); + delete this.cssHtmlTree; + } + + if (this.iframe) { + this.iframe.parentNode.removeChild(this.iframe); + delete this.iframe; + } + + delete this.cssLogic; + this.panel.removeEventListener("popupshown", this._boundPopupShown, false); + delete this._boundPopupShown; + this.panel.parentNode.removeChild(this.panel); + delete this.panel; + delete this.doc; + delete this.win; + delete CssHtmlTree.win; + }, +}; + +function ruleView() +{ + return InspectorUI.sidebar._toolContext("ruleview").view; +} + registerCleanupFunction(tearDown); waitForExplicitFinish(); diff --git a/browser/locales/en-US/chrome/browser/devtools/inspector.properties b/browser/locales/en-US/chrome/browser/devtools/inspector.properties index e0f53712a28..6eb67470c5a 100644 --- a/browser/locales/en-US/chrome/browser/devtools/inspector.properties +++ b/browser/locales/en-US/chrome/browser/devtools/inspector.properties @@ -20,12 +20,6 @@ breadcrumbs.siblings=Siblings # LOCALIZATION NOTE (htmlPanel): Used in the Inspector tool's openInspectorUI # method when registering the HTML panel. -# LOCALIZATION NOTE (ruleView.*): Button label, accesskey and tooltip text -# associated with the Highlighter's CSS Rule View in the Style Sidebar. -ruleView.label=Rules -ruleView.accesskey=R -ruleView.tooltiptext=View and Edit CSS - # LOCALIZATION NOTE (inspectButton.tooltiptext): # This button appears in the Inspector Toolbar. inspectButton is stateful, # if it's pressed users can select an element with the mouse. diff --git a/browser/locales/en-US/chrome/browser/devtools/styleinspector.properties b/browser/locales/en-US/chrome/browser/devtools/styleinspector.properties index 0371c767aa4..2c24d0bcac7 100644 --- a/browser/locales/en-US/chrome/browser/devtools/styleinspector.properties +++ b/browser/locales/en-US/chrome/browser/devtools/styleinspector.properties @@ -121,3 +121,9 @@ rule.contextmenu.copypropertyvalue=Copy property value # LOCALIZATION NOTE (rule.contextmenu.copypropertyvalue.accesskey): The rule # view's context menu copy property value access key. rule.contextmenu.copypropertyvalue.accesskey=U + +# LOCALIZATION NOTE (ruleView.*): Button label, accesskey and tooltip text +# associated with the Highlighter's CSS Rule View in the Style Sidebar. +ruleView.label=Rules +ruleView.accesskey=R +ruleView.tooltiptext=View and Edit CSS