From 624cd1f39603b15e743b158eeccff078dcc38dc0 Mon Sep 17 00:00:00 2001 From: Paul Rouget Date: Mon, 12 Mar 2012 14:39:56 +0100 Subject: [PATCH] Bug 683954 - [Layout] Implement an abstract view of the layout of the selected node. r=dcamp, r=jwalker --- browser/app/profile/firefox.js | 4 + browser/base/content/highlighter.css | 12 + browser/devtools/Makefile.in | 1 + browser/devtools/highlighter/inspector.jsm | 18 +- browser/devtools/jar.mn | 2 + browser/devtools/layoutview/LayoutView.jsm | 317 ++++++++++++++++++ browser/devtools/layoutview/Makefile.in | 53 +++ browser/devtools/layoutview/test/Makefile.in | 53 +++ .../layoutview/test/browser_layoutview.js | 134 ++++++++ browser/devtools/layoutview/view.css | 176 ++++++++++ browser/devtools/layoutview/view.xhtml | 149 ++++++++ .../chrome/browser/devtools/layoutview.dtd | 21 ++ browser/locales/jar.mn | 1 + .../devtools/layout-background.png | Bin 0 -> 3217 bytes .../gnomestripe/devtools/layout-buttons.png | Bin 0 -> 1237 bytes .../gnomestripe/devtools/layoutview.css | 87 +++++ browser/themes/gnomestripe/jar.mn | 3 + .../pinstripe/devtools/layout-background.png | Bin 0 -> 3217 bytes .../pinstripe/devtools/layout-buttons.png | Bin 0 -> 1237 bytes .../themes/pinstripe/devtools/layoutview.css | 88 +++++ browser/themes/pinstripe/jar.mn | 3 + .../winstripe/devtools/layout-background.png | Bin 0 -> 3217 bytes .../winstripe/devtools/layout-buttons.png | Bin 0 -> 1237 bytes .../themes/winstripe/devtools/layoutview.css | 88 +++++ browser/themes/winstripe/jar.mn | 6 + 25 files changed, 1215 insertions(+), 1 deletion(-) create mode 100644 browser/devtools/layoutview/LayoutView.jsm create mode 100644 browser/devtools/layoutview/Makefile.in create mode 100644 browser/devtools/layoutview/test/Makefile.in create mode 100644 browser/devtools/layoutview/test/browser_layoutview.js create mode 100644 browser/devtools/layoutview/view.css create mode 100644 browser/devtools/layoutview/view.xhtml create mode 100644 browser/locales/en-US/chrome/browser/devtools/layoutview.dtd create mode 100644 browser/themes/gnomestripe/devtools/layout-background.png create mode 100644 browser/themes/gnomestripe/devtools/layout-buttons.png create mode 100644 browser/themes/gnomestripe/devtools/layoutview.css create mode 100644 browser/themes/pinstripe/devtools/layout-background.png create mode 100644 browser/themes/pinstripe/devtools/layout-buttons.png create mode 100644 browser/themes/pinstripe/devtools/layoutview.css create mode 100644 browser/themes/winstripe/devtools/layout-background.png create mode 100644 browser/themes/winstripe/devtools/layout-buttons.png create mode 100644 browser/themes/winstripe/devtools/layoutview.css diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 0b68bb1c106..174c54b9997 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1049,6 +1049,10 @@ pref("devtools.inspector.htmlPanelOpen", false); pref("devtools.inspector.sidebarOpen", false); pref("devtools.inspector.activeSidebar", "ruleview"); +// Enable the Layout View +pref("devtools.layoutview.enabled", false); +pref("devtools.layoutview.open", false); + // Enable the Debugger pref("devtools.debugger.enabled", false); diff --git a/browser/base/content/highlighter.css b/browser/base/content/highlighter.css index a43c1dbf1ac..4475f7c767e 100644 --- a/browser/base/content/highlighter.css +++ b/browser/base/content/highlighter.css @@ -100,3 +100,15 @@ html|*#highlighter-nodeinfobar-tagname { .devtools-toolbarbutton:not([label]) > .toolbarbutton-text { display: none; } + +#inspector-layoutview-container > iframe { + -moz-transition-property: height; + -moz-transition-duration: 0.1s; + /* header size */ + height: 22px; +} + +#inspector-layoutview-container > iframe[open] { + /* header size + layout view size: 22px + 155px */ + height: 177px; +} diff --git a/browser/devtools/Makefile.in b/browser/devtools/Makefile.in index 67d1f34199b..e5b7ae07c24 100644 --- a/browser/devtools/Makefile.in +++ b/browser/devtools/Makefile.in @@ -55,6 +55,7 @@ DIRS = \ tilt \ scratchpad \ debugger \ + layoutview \ shared \ $(NULL) diff --git a/browser/devtools/highlighter/inspector.jsm b/browser/devtools/highlighter/inspector.jsm index b26d50d0024..ee6f5d389de 100644 --- a/browser/devtools/highlighter/inspector.jsm +++ b/browser/devtools/highlighter/inspector.jsm @@ -56,6 +56,7 @@ Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource:///modules/TreePanel.jsm"); Cu.import("resource:///modules/highlighter.jsm"); +Cu.import("resource:///modules/devtools/LayoutView.jsm"); Cu.import("resource:///modules/devtools/LayoutHelpers.jsm"); // Inspector notifications dispatched through the nsIObserverService. @@ -1405,6 +1406,12 @@ InspectorStyleSidebar.prototype = { destroy: function ISS_destroy() { + // close the Layout View + if (this._layoutview) { + this._layoutview.destroy(); + this._layoutview = null; + } + for each (let toolID in Object.getOwnPropertyNames(this._tools)) { this.removeTool(toolID); } @@ -1514,10 +1521,19 @@ InspectorStyleSidebar.prototype = { this._inspector._sidebarOpen = true; Services.prefs.setBoolPref("devtools.inspector.sidebarOpen", true); + + // Instantiate the Layout View if needed. + if (Services.prefs.getBoolPref("devtools.layoutview.enabled") + && !this._layoutview) { + this._layoutview = new LayoutView({ + document: this._chromeDoc, + inspector: this._inspector, + }); + } }, /** - * Hides the sidebar, updating the stored visiblity pref. + * Hides the sidebar, updating the stored visibility pref. */ hide: function ISS_hide() { diff --git a/browser/devtools/jar.mn b/browser/devtools/jar.mn index d2c29cba65d..38795a34a19 100644 --- a/browser/devtools/jar.mn +++ b/browser/devtools/jar.mn @@ -9,6 +9,8 @@ browser.jar: content/browser/devtools/csshtmltree.xul (styleinspector/csshtmltree.xul) content/browser/devtools/cssruleview.xul (styleinspector/cssruleview.xul) content/browser/devtools/styleinspector.css (styleinspector/styleinspector.css) +* content/browser/devtools/layoutview/view.xhtml (layoutview/view.xhtml) + content/browser/devtools/layoutview/view.css (layoutview/view.css) content/browser/orion.js (sourceeditor/orion/orion.js) * content/browser/source-editor-overlay.xul (sourceeditor/source-editor-overlay.xul) * content/browser/debugger.xul (debugger/debugger.xul) diff --git a/browser/devtools/layoutview/LayoutView.jsm b/browser/devtools/layoutview/LayoutView.jsm new file mode 100644 index 00000000000..b243c61438e --- /dev/null +++ b/browser/devtools/layoutview/LayoutView.jsm @@ -0,0 +1,317 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ft=javascript ts=2 et sw=2 tw=80: */ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Mozilla Layout Module. + * + * The Initial Developer of the Original Code is + * The Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2012 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Paul Rouget (original author) + * + * 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 ***** */ + +"use strict"; + +const Cu = Components.utils; +Cu.import("resource://gre/modules/Services.jsm"); +Cu.import("resource:///modules/inspector.jsm"); +Cu.import("resource:///modules/devtools/LayoutHelpers.jsm"); + +var EXPORTED_SYMBOLS = ["LayoutView"]; + +function LayoutView(aOptions) +{ + this.chromeDoc = aOptions.document; + this.inspector = aOptions.inspector; + this.browser = this.inspector.chromeWindow.gBrowser; + + this.init(); +} + +LayoutView.prototype = { + init: function LV_init() { + + this.update = this.update.bind(this); + this.onMessage = this.onMessage.bind(this); + + this.isOpen = false; + this.documentReady = false; + + // Is the layout view was open before? + if (!("_layoutViewIsOpen" in this.inspector)) { + this.inspector._layoutViewIsOpen = + Services.prefs.getBoolPref("devtools.layoutview.open"); + } + + // We update the values when: + // a node is locked + // we get the MozAfterPaint event and the node is locked + function onLock() { + this.undim(); + this.update(); + // We make sure we never add 2 listeners. + if (!this.trackingPaint) { + this.browser.addEventListener("MozAfterPaint", this.update, true); + this.trackingPaint = true; + } + } + + function onUnlock() { + this.browser.removeEventListener("MozAfterPaint", this.update, true); + this.trackingPaint = false; + this.dim(); + } + + this.onLock = onLock.bind(this); + this.onUnlock = onUnlock.bind(this); + this.inspector.on("locked", this.onLock); + this.inspector.on("unlocked", this.onUnlock); + + // Build the layout view in the sidebar. + this.buildView(); + + // Get messages from the iframe. + this.inspector.chromeWindow.addEventListener("message", this.onMessage, true); + + // Store for the different dimensions of the node. + // 'selector' refers to the element that holds the value in view.xhtml; + // 'property' is what we are measuring; + // 'value' is the computed dimension, computed in update(). + this.map = { + marginTop: {selector: ".margin.top > span", + property: "margin-top", + value: undefined}, + marginBottom: {selector: ".margin.bottom > span", + property: "margin-bottom", + value: undefined}, + marginLeft: {selector: ".margin.left > span", + property: "margin-left", + value: undefined}, + marginRight: {selector: ".margin.right > span", + property: "margin-right", + value: undefined}, + paddingTop: {selector: ".padding.top > span", + property: "padding-top", + value: undefined}, + paddingBottom: {selector: ".padding.bottom > span", + property: "padding-bottom", + value: undefined}, + paddingLeft: {selector: ".padding.left > span", + property: "padding-left", + value: undefined}, + paddingRight: {selector: ".padding.right > span", + property: "padding-right", + value: undefined}, + borderTop: {selector: ".border.top > span", + property: "border-top-width", + value: undefined}, + borderBottom: {selector: ".border.bottom > span", + property: "border-bottom-width", + value: undefined}, + borderLeft: {selector: ".border.left > span", + property: "border-left-width", + value: undefined}, + borderRight: {selector: ".border.right > span", + property: "border-right-width", + value: undefined}, + }; + }, + + /** + * Destroy the nodes. Remove listeners. + */ + destroy: function LV_destroy() { + this.inspector.removeListener("locked", this.onLock); + this.inspector.removeListener("unlocked", this.onUnlock); + this.browser.removeEventListener("MozAfterPaint", this.update, true); + this.inspector.chromeWindow.removeEventListener("message", this.onMessage, true); + this.close(); + this.iframe = null; + this.view.parentNode.removeChild(this.view); + }, + + /** + * Build the Layout container: + * + * + *