Bug 596315 - Animate the opening and closing of the Web Console, r=dietrich, a=johnath
authorPatrick Walton <pwalton@mozilla.com>
Wed, 10 Nov 2010 11:10:26 -0500
changeset 57255 bdcaec24bbcb1c838f967208bf2301cfcecff67c
parent 57254 f9966f6545e0a34e51a05e981c54142e1189f1bc
child 57256 25df336746e4e8c7cae63288192c79e722e36543
push id16848
push userrcampbell@mozilla.com
push dateWed, 10 Nov 2010 16:11:14 +0000
treeherdermozilla-central@bdcaec24bbcb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdietrich, johnath
bugs596315
milestone2.0b8pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 596315 - Animate the opening and closing of the Web Console, r=dietrich, a=johnath
toolkit/components/console/hudservice/HUDService.jsm
toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_581231_close_button.js
toolkit/themes/gnomestripe/global/webConsole.css
toolkit/themes/pinstripe/global/webConsole.css
toolkit/themes/winstripe/global/webConsole.css
--- a/toolkit/components/console/hudservice/HUDService.jsm
+++ b/toolkit/components/console/hudservice/HUDService.jsm
@@ -110,16 +110,20 @@ const NEW_GROUP_DELAY = 5000;
 // search.
 const SEARCH_DELAY = 200;
 
 // The number of lines that are displayed in the console output by default.
 // The user can change this number by adjusting the hidden
 // "devtools.hud.loglimit" preference.
 const DEFAULT_LOG_LIMIT = 200;
 
+// Possible directions that can be passed to HUDService.animate().
+const ANIMATE_OUT = 0;
+const ANIMATE_IN = 1;
+
 // Constants used for defining the direction of JSTerm input history navigation.
 const HISTORY_BACK = -1;
 const HISTORY_FORWARD = 1;
 
 // The maximum number of bytes a Network ResponseListener can hold.
 const RESPONSE_BODY_LIMIT = 1048576; // 1 MB
 
 const ERRORS = { LOG_MESSAGE_MISSING_ARGS:
@@ -2730,17 +2734,17 @@ HUD_SERVICE.prototype =
    *        The timestamp of the newest message in milliseconds.
    * @returns nsIDOMNode
    *          The group into which the next message should be written.
    */
   appendGroupIfNecessary:
   function HS_appendGroupIfNecessary(aConsoleNode, aTimestamp)
   {
     let hudBox = aConsoleNode;
-    while (hudBox != null && hudBox.getAttribute("class") !== "hud-box") {
+    while (hudBox && !hudBox.classList.contains("hud-box")) {
       hudBox = hudBox.parentNode;
     }
 
     let lastTimestamp = hudBox.lastTimestamp;
     let delta = aTimestamp - lastTimestamp;
     hudBox.lastTimestamp = aTimestamp;
     if (delta < NEW_GROUP_DELAY) {
       // No new group needed. Return the most recently-added group, if there is
@@ -3035,16 +3039,63 @@ HUD_SERVICE.prototype =
    * @returns void
    */
   createController: function HUD_createController(aWindow)
   {
     if (aWindow.commandController == null) {
       aWindow.commandController = new CommandController(aWindow);
       aWindow.controllers.insertControllerAt(0, aWindow.commandController);
     }
+  },
+
+  /**
+   * Animates the Console appropriately.
+   *
+   * @param string aHUDId The ID of the console.
+   * @param string aDirection Whether to animate the console appearing
+   *        (ANIMATE_IN) or disappearing (ANIMATE_OUT).
+   * @param function aCallback An optional callback, which will be called with
+   *        the "transitionend" event passed as a parameter once the animation
+   *        finishes.
+   */
+  animate: function HS_animate(aHUDId, aDirection, aCallback)
+  {
+    let hudBox = this.getOutputNodeById(aHUDId);
+    if (!hudBox.classList.contains("animated") && aCallback) {
+      aCallback();
+      return;
+    }
+
+    switch (aDirection) {
+      case ANIMATE_OUT:
+        hudBox.style.height = 0;
+        break;
+      case ANIMATE_IN:
+        var contentWindow = hudBox.ownerDocument.defaultView;
+        hudBox.style.height = Math.ceil(contentWindow.innerHeight / 3) + "px";
+        break;
+    }
+
+    if (aCallback) {
+      hudBox.addEventListener("transitionend", aCallback, false);
+    }
+  },
+
+  /**
+   * Disables all animation for a console, for unit testing. After this call,
+   * the console will instantly take on a reasonable height, and the close
+   * animation will not occur.
+   *
+   * @param string aHUDId The ID of the console.
+   */
+  disableAnimation: function HS_disableAnimation(aHUDId)
+  {
+    let hudBox = this.getOutputNodeById(aHUDId);
+    hudBox.classList.remove("animated");
+    hudBox.style.height = "300px";
   }
 };
 
 //////////////////////////////////////////////////////////////////////////
 // HeadsUpDisplay
 //////////////////////////////////////////////////////////////////////////
 
 /*
@@ -3259,21 +3310,18 @@ HeadsUpDisplay.prototype = {
    *
    * @returns nsIDOMNode
    */
   makeHUDNodes: function HUD_makeHUDNodes()
   {
     let self = this;
     this.HUDBox = this.makeXULNode("vbox");
     this.HUDBox.setAttribute("id", this.hudId);
-    this.HUDBox.setAttribute("class", "hud-box");
-
-    var height = Math.ceil((this.contentWindow.innerHeight * .33)) + "px";
-    var style = "height: " + height + ";";
-    this.HUDBox.setAttribute("style", style);
+    this.HUDBox.setAttribute("class", "hud-box animated");
+    this.HUDBox.style.height = 0;
 
     let outerWrap = this.makeXULNode("vbox");
     outerWrap.setAttribute("class", "hud-outer-wrapper");
     outerWrap.setAttribute("flex", "1");
 
     let consoleCommandSet = this.makeXULNode("commandset");
     outerWrap.appendChild(consoleCommandSet);
 
@@ -5020,22 +5068,32 @@ ConsoleUtils = {
 
 HeadsUpDisplayUICommands = {
   toggleHUD: function UIC_toggleHUD() {
     var window = HUDService.currentContext();
     var gBrowser = window.gBrowser;
     var linkedBrowser = gBrowser.selectedTab.linkedBrowser;
     var tabId = gBrowser.getNotificationBox(linkedBrowser).getAttribute("id");
     var hudId = "hud_" + tabId;
-    var hud = gBrowser.selectedTab.ownerDocument.getElementById(hudId);
+    var ownerDocument = gBrowser.selectedTab.ownerDocument;
+    var hud = ownerDocument.getElementById(hudId);
     if (hud) {
-      HUDService.deactivateHUDForContext(gBrowser.selectedTab);
+      HUDService.animate(hudId, ANIMATE_OUT, function() {
+        // If the user closes the console while the console is animating away,
+        // then these callbacks will queue up, but all the callbacks after the
+        // first will have no console to operate on. This test handles this
+        // case gracefully.
+        if (ownerDocument.getElementById(hudId)) {
+          HUDService.deactivateHUDForContext(gBrowser.selectedTab);
+        }
+      });
     }
     else {
       HUDService.activateHUDForContext(gBrowser.selectedTab);
+      HUDService.animate(hudId, ANIMATE_IN);
     }
   },
 
   toggleFilter: function UIC_toggleFilter(aButton) {
     var filter = aButton.getAttribute("buttonType");
     var hudId = aButton.getAttribute("hudId");
     var state = HUDService.getFilterState(hudId, filter);
     if (state) {
--- a/toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_581231_close_button.js
+++ b/toolkit/components/console/hudservice/tests/browser/browser_webconsole_bug_581231_close_button.js
@@ -20,23 +20,25 @@ function test() {
 function testCloseButton() {
   browser.removeEventListener("DOMContentLoaded", testCloseButton, false);
 
   openConsole();
 
   hudId = HUDService.displaysIndex()[0];
   hudBox = HUDService.getHeadsUpDisplay(hudId);
 
-  let closeButton = hudBox.querySelector(".jsterm-close-button");
-  ok(closeButton != null, "we have the close button");
+  HUDService.disableAnimation(hudId);
+  executeSoon(function() {
+    let closeButton = hudBox.querySelector(".jsterm-close-button");
+    ok(closeButton != null, "we have the close button");
 
 
   // XXX: ASSERTION: ###!!! ASSERTION: XPConnect is being called on a scope without a 'Components' property!: 'Error', file /home/ddahl/code/moz/mozilla-central/mozilla-central/js/src/xpconnect/src/xpcwrappednativescope.cpp, line 795
 
   EventUtils.synthesizeMouse(closeButton, 0, 0, {});
 
   executeSoon(function (){
-    ok(!(hudId in HUDService.windowRegistry), "the console is closed when the " +
-     "close button is pressed");
+    ok(!(hudId in HUDService.windowRegistry), "the console is closed when " +
+       "the close button is pressed");
     closeButton = null;
     finishTest();
   });
 }
--- a/toolkit/themes/gnomestripe/global/webConsole.css
+++ b/toolkit/themes/gnomestripe/global/webConsole.css
@@ -37,16 +37,20 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 .hud-box {
   border-bottom: 1px solid #aaa;
   text-shadow: none;
 }
 
+.hud-box.animated {
+  -moz-transition: height 350ms;
+}
+
 .hud-outer-wrapper {
   width: 100%;
   height: 100%;
 }
 
 .hud-console-wrapper {
   width: 100%;
   overflow: auto;
--- a/toolkit/themes/pinstripe/global/webConsole.css
+++ b/toolkit/themes/pinstripe/global/webConsole.css
@@ -38,16 +38,20 @@
 
 %include shared.inc
 
 .hud-box {
   border-bottom: 1px solid #aaa;
   text-shadow: none;
 }
 
+.hud-box.animated {
+  -moz-transition: height 350ms;
+}
+
 .hud-outer-wrapper {
   width: 100%;
   height: 100%;
 }
 
 .hud-console-wrapper {
   width: 100%;
   overflow: auto;
--- a/toolkit/themes/winstripe/global/webConsole.css
+++ b/toolkit/themes/winstripe/global/webConsole.css
@@ -36,16 +36,20 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 .hud-box {
   border-bottom: 1px solid #aaa;
   text-shadow: none;
 }
 
+.hud-box.animated {
+  -moz-transition: height 350ms;
+}
+
 .hud-outer-wrapper {
   width: 100%;
   height: 100%;
 }
 
 .hud-console-wrapper {
   width: 100%;
   overflow: auto;