Bug 402365 - "[Trunk] Tabs break calendar mode view" [r=philor,berend]
authorAndrew Sutherland <bugmail@asutherland.org>
Thu, 09 Oct 2008 10:11:34 +0100
changeset 560 85b938c0ab7727a5d3ca5e0b02340aba9b4ea0a3
parent 559 f794c3766d9b42f34c6c214bad0efc039d5a4e5e
child 561 0337948019136d5bd8a291c965b9aeb8aca79b12
push idunknown
push userunknown
push dateunknown
reviewersphilor, berend
bugs402365
Bug 402365 - "[Trunk] Tabs break calendar mode view" [r=philor,berend]
calendar/base/content/calendar-multiday-view.xml
calendar/base/content/calendar-task-view.js
calendar/base/content/calendar-task-view.xul
calendar/lightning/content/customize-toolbar.js
calendar/lightning/content/customize-toolbar.xul
calendar/lightning/content/lightning-migration.xul
calendar/lightning/content/lightning-scripts.inc
calendar/lightning/content/lightning-standalone.xul
calendar/lightning/content/lightning-today-pane.js
calendar/lightning/content/messenger-overlay-sidebar.js
calendar/lightning/content/messenger-overlay-sidebar.xul
calendar/lightning/content/messenger-overlay-toolbar.js
calendar/lightning/content/messenger-overlay-toolbar.xul
calendar/lightning/jar.mn
calendar/lightning/themes/pinstripe/lightning.css
calendar/lightning/themes/winstripe/lightning.css
mail/base/content/mailWindowOverlay.js
mail/base/content/messenger.xul
mail/base/content/tabmail.xml
mail/themes/pinstripe/mail/tabmailBindings.xml
mail/themes/qute/mail/tabmailBindings.xml
--- a/calendar/base/content/calendar-multiday-view.xml
+++ b/calendar/base/content/calendar-multiday-view.xml
@@ -2322,17 +2322,17 @@
           self.adjustWeekdayLength();
         ]]></body>
       </method>
 
       <field name="mScrollHandler">null</field>
       <method name="onScroll">
         <body><![CDATA[
           // workaround: deck changes shouldn't provoke onScroll events...
-          var displayDeck = document.getElementById("displayDeck");
+          var displayDeck = document.getElementById("calendarDisplayDeck");
           if (displayDeck && displayDeck.selectedPanel.id != "calendar-view-box") {
             return;
           }
           var panel = this.parentNode.parentNode.parentNode;
           var deck = panel.parentNode;
           if (panel.id != deck.selectedPanel.id) {
             return;
           }
--- a/calendar/base/content/calendar-task-view.js
+++ b/calendar/base/content/calendar-task-view.js
@@ -258,27 +258,29 @@ function sendMailToOrganizer() {
     }
 }
 
 function taskViewObserveDisplayDeckChange(event) {
     var deck = event.target;
 
     // Bug 309505: The 'select' event also fires when we change the selected
     // panel of calendar-view-box.  Workaround with this check.
-    if (deck.id != "displayDeck") {
+    if (deck.id != "calendarDisplayDeck") {
         return;
     }
 
     var id = null;
     try {
       id = deck.selectedPanel.id
     }
     catch (e) {}
 
     // In case we find that the task view has been made visible, we refresh the view.
     if (id == "calendar-task-box") {
         taskViewUpdate(
             document.getElementById("task-tree-filtergroup").value || "all");
     }
 }
 
-document.getElementById("displayDeck").
+document.addEventListener("load", function () {
+  document.getElementById("calendarDisplayDeck").
     addEventListener("select", taskViewObserveDisplayDeckChange, true);
+  }, true);
--- a/calendar/base/content/calendar-task-view.xul
+++ b/calendar/base/content/calendar-task-view.xul
@@ -48,17 +48,17 @@
 
 <overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <script type="application/javascript" src="chrome://calendar/content/calendar-task-tree.js"/>
   <script type="application/javascript" src="chrome://calendar/content/calendar-task-view.js"/>
   <script type="application/javascript" src="chrome://calendar/content/calendar-dialog-utils.js"/>
   <script type="application/javascript" src="chrome://calendar/content/calApplicationUtils.js"/>
   <script type="application/javascript" src="chrome://global/content/globalOverlay.js"/>
 
-  <vbox id="displayDeck">
+  <vbox id="calendarDisplayDeck">
     <vbox id="calendar-task-box" flex="1"
           onselect="taskDetailsView.onSelect(event);">
       <textbox id="view-task-edit-field"
                class="task-edit-field"
                onfocus="taskEdit.onFocus(event)"
                onblur="taskEdit.onBlur(event)"
                onkeypress="taskEdit.onKeyPress(event)"/>
       <vbox flex="1">
--- a/calendar/lightning/content/customize-toolbar.js
+++ b/calendar/lightning/content/customize-toolbar.js
@@ -1,362 +0,0 @@
-/* -*- Mode: javascript; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* ***** 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 Mozilla Communicator client code, released
- * March 31, 1998.
- *
- * The Initial Developer of the Original Code is
- * David Hyatt.
- * Portions created by the Initial Developer are Copyright (C) 2002
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   David Hyatt (hyatt@apple.com)
- *   Blake Ross (blaker@netscape.com)
- *   Joe Hewitt (hewitt@netscape.com)
- *   Michael Buettner <michael.buettner@sun.com>
- *
- * 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 ***** */
-
-/**
- * Global variables in addition to those specified in
- * mail/base/content/customizeToolbar.js
- */
-var gPreviousLocation = null;
-var gRepositionOnce = false;
-var gIsMainApplicationContext = (window.arguments[3] != null);
-
-/**
- * Since we want to 'inherit' several global functions
- * we retrieve the current values in order to call them
- * from their overriden version.
- */
-var gOnLoad = onLoad;
-var gInitDialog = initDialog;
-var gOnCancel = onCancel;
-
-/**
- * initDialog() gets called from the load handler and
- * is responsible for initializing global variables, etc.
- */
-initDialog = function() {
-
-    // Don't do any extra processing in case we're not
-    // customizing one of the main application toolbars.
-    // The customize toolbar dialog contains special features
-    // that don't apply in that case (location drop down, etc.).
-    if (gIsMainApplicationContext) {
-
-        // remember initial toolbar location and set the
-        // menulist accordingly. this applies to the mode toolbar only.
-        gPreviousLocation = gToolbox.getAttribute("location");
-        document.getElementById("location-list").value = gPreviousLocation;
-
-        // set the toolbar selection according to the current mode.
-        // this list allows to hop from one toolbar to another without
-        // leaving the customize dialog. if this feature is to be used outside
-        // of mail/news main application window, we detect this case and disable
-        // all relevant controls and stuff.
-        var selectorList = document.getElementById("selector-list");
-        selectorList.value = window.arguments[3];
-        if (selectorList.selectedItem.value != window.arguments[3]) {
-            document.getElementById("selector-container").collapsed = true;
-        }
-    }
-
-    // now call the original initDialog() function
-    gInitDialog();
-}
-
-/**
- * onCancel() is called if the customize toolbar dialog has been canceled.
- * we're returning to the previous state and discarding any changes made
- * to the current toolbar. please note that this applies to the current toolbar
- * only. in case we've been switched from one to another, only the changes
- * made to the most recent toolbar will be discarded.
- */
-onCancel = function() {
-
-    // Don't do any extra processing in case we're not
-    // customizing one of the main application toolbars.
-    // The customize toolbar dialog contains special features
-    // that don't apply in that case (location drop down, etc.).
-    if (gIsMainApplicationContext) {
-        updateToolbarLocation(gPreviousLocation);
-    }
-
-    gOnCancel();
-}
-
-/**
- * repositionDialog() to find a good position for the customize dialog.
- * it is called during initialization and subsequently if any option has been
- * altered that causes the toolbar to change size or location. we need to
- * override this function to add the ability to set the position *above*
- * the toolbar in question, since this is necessary for the mode toolbar
- * which is set at the lower left of the application window by default.
- */
-_repositionDialog = function() {
- 
-  // Christian said it's better to not make the dialog jump...
-  if (gRepositionOnce)
-    return;
-  gRepositionOnce = true;
-    
-  // Position the dialog touching the bottom of the toolbox and centered with 
-  // it. We must resize the window smaller first so that it is positioned properly. 
-  var screenX = gToolbox.boxObject.screenX + ((gToolbox.boxObject.width - kWindowWidth) / 2);
-  var screenY = gToolbox.boxObject.screenY + gToolbox.boxObject.height;
-
-  var newHeight = kWindowHeight;
-  if (newHeight >= screen.availHeight - screenY - kVSizeSlop) {
-    // the customize window doesn't fit below the toolbar. first try if there's
-    // enough space at the top of the toolbar. if neither works, shrink the height.
-    if(gToolbox.boxObject.screenY - newHeight - kVSizeSlop < 0) {
-      newHeight = screen.availHeight - screenY - kVSizeSlop;
-    } else {
-      screenY = gToolbox.boxObject.screenY - newHeight;
-    }
-  }
-  
-  if(screenX < 0) {
-    screenX = 0;
-  }
-  
-  window.resizeTo(kWindowWidth, newHeight);
-  window.moveTo(screenX, screenY);
-}
-
-/**
- * onLoad() is called by the load event handler. we need to override
- * this function to initialize the newly introduced controls.
- */
-onLoad = function() {
-
-    // remove the box containing all the relevant controls from
-    // the dom tree, we bring a new one to the table. this is necessary
-    // since those controls don't specify id's, so we need to override
-    // all of them.
-    var mainbox = document.getElementById("main-box");
-    var controlbox = document.getElementById("control-box");
-    mainbox.removeChild(controlbox.nextSibling);
-
-    // Don't do any extra processing in case we're not
-    // customizing one of the main application toolbars.
-    // The customize toolbar dialog contains special features
-    // that don't apply in that case (location drop down, etc.).
-    if (gIsMainApplicationContext) {
-
-        document.getElementById("selector-container")
-            .removeAttribute("collapsed");
-
-        // show the location option (place toolbar at top or bottom)
-        // if this feature has been requested.
-        updateLocationVisibility(window.arguments[3] == 'mode');
-    }
-
-    // now call the default implementation of the load handler, since
-    // this retrieves the toolbox element from the arguments.
-    gOnLoad();
-}
-
-/**
- * updateToolbarLocation() is called to handle a new location for
- * the toolbar (top or bottom). basically, we just set the appropriate
- * attribute on all customizable toolbars and rely on them intercepting
- * this modification and act accordingly.
- */
-_updateToolbarLocation = function(aLocation) {
-
-  // since the current toolbar will change its location in
-  // the dom tree (most probably), we need to unwrap the toolbar items
-  // and reset anything that could get in the way.
-  removeToolboxListeners();
-  unwrapToolbarItems(false);
-
-  var toolboxId = gToolbox.id;
-
-  // set the new location on the toolbox...
-  setAttribute(gToolbox, "location", aLocation);
-  gToolboxDocument.persist(gToolbox.id, "location");
-  
-  // ...and each customizable toolbar
-  for (var i = 0; i < gToolbox.childNodes.length; ++i) {
-    var toolbar = getToolbarAt(i);
-    if (isCustomizableToolbar(toolbar)) {
-      setAttribute(toolbar, "location", aLocation);
-      gToolboxDocument.persist(toolbar.id, "location");
-    }
-  }
-
-  gToolbox = gToolboxDocument.getElementById(toolboxId);
-  gToolboxDocument = gToolbox.ownerDocument;
-  
-  gToolbox.addEventListener("draggesture", onToolbarDragGesture, false);
-  gToolbox.addEventListener("dragover", onToolbarDragOver, false);
-  gToolbox.addEventListener("dragexit", onToolbarDragExit, false);
-  gToolbox.addEventListener("dragdrop", onToolbarDragDrop, false);
-
-  // Now re-wrap the items on the toolbar, but don't clobber previousset.
-  wrapToolbarItems(false);
-}
-
-/**
- * Handler that takes care of a new toolbar being selected for customization.
- */
-_updateToolbarSelection = function(aSelection) {
-
-  var callback = window.arguments[2];
-  if(callback) {
-
-    // first of all, we need to remove our listeners and unwrap
-    // the toolbar items. this is important to do first, before calling
-    // the outside world, since they possibly want to change those items...
-    removeToolboxListeners();
-    unwrapToolbarItems(true);
-    
-    // persist the current set of buttons in all
-    // customizable toolbars to localstore.    
-    persistCurrentSets();
-
-    // execute the supplied callback function. we expect to receive
-    // the toolbox we're supposed to customize as a result.
-    var toolbox = callback(aSelection);
-
-    // store some internal states in the window arguments
-    // since we're going to call initDialog() again...
-    window.arguments[0] = toolbox;
-    window.arguments[3] = aSelection;
-
-    // show or hide the location menu dependend
-    // on the toolbar we're switching to.
-    updateLocationVisibility(aSelection == 'mode');
-
-    // nothing has changed so far...
-    gToolboxChanged = false;
-
-    // now just call the default load handler in order to
-    // initialize the dialog for a fresh start.
-    gOnLoad();
-  }
-}
-
-/**
- * Show or hide the location selection, this option shouldn't be always visible
- */
-_updateLocationVisibility = function(aShow) {
-
-  var controls = document.getElementsByAttribute("location-option", "true");
-  for (var i=0;i<controls.length;i++) {
-    if (aShow) {
-      controls[i].removeAttribute("collapsed");
-    } else {
-      controls[i].setAttribute("collapsed","true");
-    }  
-  }
-}
-
-/**
- * Builds the palette of draggable items that are not yet in a toolbar.
- */
-_buildPalette = function() {
-
-  // Empty the palette first.
-  var paletteBox = document.getElementById("palette-box");
-  while (paletteBox.lastChild)
-    paletteBox.removeChild(paletteBox.lastChild);
-
-  var currentRow = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
-                                            "hbox");
-  currentRow.setAttribute("class", "paletteRow");
-
-  // Add the toolbar separator item.
-  var templateNode = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
-                                              "toolbarseparator");
-  templateNode.id = "separator";
-  wrapPaletteItem(templateNode, currentRow, null);
-
-  // Add the toolbar spring item.
-  templateNode = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
-                                              "toolbarspring");
-  templateNode.id = "spring";
-  templateNode.flex = 1;
-  wrapPaletteItem(templateNode, currentRow, null);
-
-  // Add the toolbar spacer item.
-  templateNode = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
-                                              "toolbarspacer");
-  templateNode.id = "spacer";
-  templateNode.flex = 1;
-  wrapPaletteItem(templateNode, currentRow, null);
-
-  var rowSlot = 3;
-
-  var currentItems = getCurrentItemIds();
-  templateNode = gToolbox.palette.firstChild;
-  while (templateNode) {
-    // Check if the item is already in a toolbar before adding it to the palette.
-    if (!(templateNode.id in currentItems)) {
-
-      var nodeMode = templateNode.getAttribute('mode');
-      if (!nodeMode)
-        nodeMode = 'mail';
-
-      if (nodeMode == window.arguments[3]) {
-
-        var paletteItem = templateNode.cloneNode(true);
-
-        if (rowSlot == kRowMax) {
-          // Append the old row.
-          paletteBox.appendChild(currentRow);
-
-          // Make a new row.
-          currentRow = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
-                                                "hbox");
-          currentRow.setAttribute("class", "paletteRow");
-          rowSlot = 0;
-        }
-
-        ++rowSlot;
-        wrapPaletteItem(paletteItem, currentRow, null);
-      }
-    }
-    
-    templateNode = templateNode.nextSibling;
-  }
-
-  if (currentRow) { 
-    fillRowWithFlex(currentRow);
-    paletteBox.appendChild(currentRow);
-  }
-}
-
-if (gIsMainApplicationContext) {
-    repositionDialog = _repositionDialog;
-    updateToolbarLocation = _updateToolbarLocation;
-    updateToolbarSelection = _updateToolbarSelection;
-    updateLocationVisibility = _updateLocationVisibility;
-    buildPalette = _buildPalette;
-}
--- a/calendar/lightning/content/customize-toolbar.xul
+++ b/calendar/lightning/content/customize-toolbar.xul
@@ -1,102 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!-- ***** 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 Mozilla Communicator client code, released
-   - March 31, 1998.
-   -
-   - The Initial Developer of the Original Code is
-   - Netscape Communications Corporation.
-   - Portions created by the Initial Developer are Copyright (C) 1998-1999
-   - the Initial Developer. All Rights Reserved.
-   -
-   - Contributor(s):
-   -   David Hyatt (hyatt@apple.com)
-   -   Blake Ross (blaker@netscape.com)
-   -   Michael Buettner <michael.buettner@sun.com>
-   -   Berend Cornelius <berend.cornelius@sun.com>
-   -
-   - 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 LGPL or the GPL. 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 ***** -->
-
-<!DOCTYPE dialog [
-  <!ENTITY % customizeToolbarDTD SYSTEM "chrome://global/locale/customizeToolbar.dtd">
-  %customizeToolbarDTD;
-  <!ENTITY % lightningDTD SYSTEM "chrome://lightning/locale/lightning.dtd">
-  %lightningDTD;
-]>
-
-<?xml-stylesheet href="chrome://lightning/skin/lightning.css" type="text/css"?>
-
-<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
-
-  <script type="application/javascript"
-          src="chrome://lightning/content/customize-toolbar.js"/>
-
-  <vbox id="main-box" flex="1" collapsed="true">
-    <box id="selector-container" orient="vertical" insertbefore="instructions" collapsed="true">
-      <box align="center" orient="horizontal">
-        <label value="&customize.toolbar.selector.label;" control="selector-list"/>
-        <menulist id="selector-list"
-                  value="mode"
-                  oncommand="updateToolbarSelection(this.value);">
-          <menupopup>
-            <menuitem value="mode" label="&customize.toolbar.selector.mode;"/>
-            <menuitem value="mail" label="&customize.toolbar.selector.mail;"/>
-            <menuitem value="calendar" label="&customize.toolbar.selector.calendar;"/>
-            <menuitem value="task" label="&customize.toolbar.selector.task;"/>
-          </menupopup>
-        </menulist>
-      </box>
-      <separator class="groove"/>
-    </box>
-    <box id="control-box" align="center" insertafter="palette-box">
-      <label value="&customize.toolbar.location.label;" control="location-list"
-             location-option="true"
-             collapsed="true"/>
-      <menulist id="location-list"
-                value="top"
-                location-option="true"
-                collapsed="true"
-                oncommand="updateToolbarLocation(this.value);">
-        <menupopup>
-          <menuitem value="top" label="&customize.toolbar.location.top;"/>
-          <menuitem value="bottom" label="&customize.toolbar.location.bottom;"/>
-        </menupopup>
-      </menulist>
-      <label value="&show.label;" control="modelist"/>
-      <menulist id="modelist" value="icons" oncommand="updateToolbarMode(this.value);">
-        <menupopup>
-          <menuitem value="full" label="&iconsAndText.label;"/>
-          <menuitem value="icons" label="&icons.label;"/>
-          <menuitem value="text" label="&text.label;"/>
-        </menupopup>
-      </menulist>
-      <checkbox id="smallicons" oncommand="updateIconSize(this.checked);" label="&useSmallIcons.label;"/>
-      <spacer flex="1"/>
-      <button label="&restoreDefaultSet.label;" oncommand="restoreDefaultSet();"/>
-    </box>
-  </vbox>
-
-</overlay>
--- a/calendar/lightning/content/lightning-migration.xul
+++ b/calendar/lightning/content/lightning-migration.xul
@@ -64,11 +64,11 @@
             if (!cals.length) {
                 // There are no calendars, so we are running for the first time
                 gDataMigrator.checkAndMigrate();
             }
         }
         document.addEventListener("load", checkOld, true);
     ]]></script>
 
-    <deck id="displayDeck"/>
+    <deck id="calendarDisplayDeck"/>
 
 </overlay>
--- a/calendar/lightning/content/lightning-scripts.inc
+++ b/calendar/lightning/content/lightning-scripts.inc
@@ -41,17 +41,16 @@
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK ***** -->
 
 <script type="application/javascript" src="chrome://lightning/content/lightning-utils.js"/>
 <script type="application/javascript" src="chrome://lightning/content/messenger-overlay-sidebar.js"/>
 <script type="application/javascript" src="chrome://lightning/content/messenger-overlay-toolbar.js"/>
 <script type="application/javascript" src="chrome://lightning/content/lightning-common-sets.js"/>
-<script type="application/javascript" src="chrome://lightning/content/lightning-today-pane.js"/>
 <script type="application/javascript" src="chrome://calendar/content/calendar-invitations-manager.js"/>
 <script type="application/javascript">
     var calendarmenulabel = "&lightning.calendar.label;";
     var calendarmenuaccesskey = "&lightning.calendar.accesskey;";
     var messagemenulabel = "&msgMenu.label;";
     var messagemenuaccesskey = "&msgMenu.accesskey;";
     var tasksmenulabel = "&lightning.tasks.label;";
     var tasksmenuaccesskey = "&lightning.tasks.accesskey;";
--- a/calendar/lightning/content/lightning-standalone.xul
+++ b/calendar/lightning/content/lightning-standalone.xul
@@ -43,16 +43,16 @@
 
 <?xml-stylesheet href="chrome://calendar/content/calendar-multiday-view.css" type="text/css"?>
 <?xml-stylesheet href="chrome://calendar/skin/widgets/minimonth.css" type="text/css"?>
 
 <window
   id="lightning"
   xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <!-- messenger-overlay-sidebar expects to overlay a vbox named "folderPaneBox" and a 
-     - deck named "displayDeck".  So we oblige.
+     - deck named "calendarDisplayDeck".  So we oblige.
     -->
   <hbox flex="1">
     <vbox id="folderPaneBox" flex="1"/>
     <splitter />
-    <deck id="displayDeck" flex="4"/>
+    <deck id="calendarDisplayDeck" flex="4"/>
   </hbox>
 </window>
--- a/calendar/lightning/content/lightning-today-pane.js
+++ b/calendar/lightning/content/lightning-today-pane.js
@@ -1,86 +0,0 @@
-/* ***** 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 Sun Microsystems code.
- *
- * The Initial Developer of the Original Code is Sun Microsystems.
- * Portions created by the Initial Developer are Copyright (C) 2008
- * the Initial Developer. All Rights Reserved.
- *
- * Contributor(s):
- *   Berend Cornelius <berend.cornelius@sun.com>
- *
- * 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 ***** */
-
-var ltnTodaypaneButton = "mail-show-todaypane-button";
-
-function ltnAddButtonToSetString(toolbarSetString) {
-    // by default the todaypane-button is to be placed before the first
-    // separator of the toolbar
-    var separatorindex = toolbarSetString.indexOf("separator");
-    if (separatorindex > -1) {
-        var firstSubString = toolbarSetString.substring(0, separatorindex);
-        var secondSubString = toolbarSetString.substring(separatorindex);
-        toolbarSetString = firstSubString + ltnTodaypaneButton + "," + secondSubString;
-    } else {
-        // in case there is no separator within the toolbar we append the
-        // todaypane-button
-        toolbarSetString += "," + ltnTodaypaneButton;
-    }
-    return toolbarSetString;
-}
-
-/**  ltnInitTodayPane()
-*    initializes TodayPane for Lightning and adds a toolbarbutton to mail-toolbar once
-*/
-function ltnInitTodayPane() {
-    // add toolbar-button to mail-toolbar
-    var mailToolbar = getMailBar();
-    var addToolbarbutton = false;
-    var defaultSetString = mailToolbar.getAttribute("defaultset");
-    if (defaultSetString.indexOf(ltnTodaypaneButton) == -1) {
-        defaultSetString = ltnAddButtonToSetString(defaultSetString);
-        mailToolbar.setAttribute("defaultset", defaultSetString);
-    }
-
-    // add the toolbarbutton to the toolbarpalette on first startup
-    var todaypanebox = document.getElementById("today-pane-panel");
-    if (todaypanebox.hasAttribute("addtoolbarbutton")) {
-        addToolbarbutton = (todaypanebox.getAttribute("addtoolbarbutton") == "true");
-    }
-    if (addToolbarbutton) {
-        var currentSetString = mailToolbar.getAttribute("currentset");
-        if (currentSetString.indexOf(ltnTodaypaneButton) == -1) {
-            if (currentSetString.length == 0) {
-                mailToolbar.currentSet = mailToolbar.getAttribute("defaultset");
-            } else {
-                mailToolbar.currentSet = ltnAddButtonToSetString(currentSetString);
-            }
-            mailToolbar.setAttribute("currentset", mailToolbar.currentSet);
-            document.persist(mailToolbar.id, "currentset");
-        }
-        todaypanebox.setAttribute("addtoolbarbutton", "false");
-    }
-}
\ No newline at end of file
--- a/calendar/lightning/content/messenger-overlay-sidebar.js
+++ b/calendar/lightning/content/messenger-overlay-sidebar.js
@@ -69,138 +69,72 @@ function nextMonth(dt)
     return d;
 }
 
 var gMiniMonthLoading = false;
 function ltnMinimonthPick(minimonth) {
     if (gMiniMonthLoading || gCurrentMode != "calendar") {
         return;
     }
-    if (document.getElementById("displayDeck").selectedPanel !=
+    if (document.getElementById("calendarDisplayDeck").selectedPanel !=
         document.getElementById("calendar-view-box")) {
         ltnShowCalendarView(gLastShownCalendarView);
     }
     var jsDate = minimonth.value;
     document.getElementById("ltnDateTextPicker").value = jsDate;
     var cdt = jsDateToDateTime(jsDate, currentView().timezone);
     cdt.isDate = true;
     currentView().goToDay(cdt);
 }
 
-function ltnOnLoad(event) {
-    // take the existing folderPaneBox (that's what thunderbird displays
-    // at the left side of the application window) and stuff that inside
-    // of the deck we're introducing with the contentPanel. this essentially
-    // rearranges the DOM tree and allows us to switch between content that
-    // lives inside of the left pane.
-    var folderPaneBox = document.getElementById("folderPaneBox");
-    var contentPanel = document.getElementById("contentPanel");
-    contentPanel.insertBefore(folderPaneBox, contentPanel.firstChild);
-
-    // we're taking care of the mode toolbar (that's the small toolbar on
-    // the lower left with the 'mail', 'calendar', 'task' buttons on it).
-    // since we want to have this particular toolbar displayed at the
-    // top or bottom inside the folderPaneBox (basically on top or bottom
-    // of the window) we need to go to great length in order to get this
-    // damned thing working. i decided to dynamically place the toolbox
-    // inside the DOM tree, that appears to be the most clean solution to
-    // the problem. unfortunately, it didn't work out that easy. as soon
-    // as we call insertBefore() to place the node somewhere different, the
-    // constructor of the appropriate binding gets called again. this has
-    // the nasty side-effect that the toolbar get a bit confused, since it
-    // thinks it is customized, which just isn't the case. that's why we need
-    // to carry some internal toolbar properties over. and that's what those
-    // functions retrieveToolbarProperties() and restoreToolbarProperties()
-    // are all about.
-    var retrieveToolbarProperties = function(toolbox)
-    {
-      var toolbars = {};
-      var toolbar = toolbox.firstChild;
-      while (toolbar) {
-        if (toolbar.localName == "toolbar") {
-          if (toolbar.getAttribute("customizable") == "true") {
-            if (!toolbar.hasAttribute("customindex")) {
-              var propertybag = {};
-              propertybag.firstPermanentChild = toolbar.firstPermanentChild;
-              propertybag.lastPermanentChild = toolbar.lastPermanentChild;
-              toolbars[toolbar.id] = propertybag;
-            }
-          }
-        }
-        toolbar = toolbar.nextSibling;
+var calendarTabType = {
+  name: "calendar",
+  panelId: "calendarTabPanel",
+  modes: {
+    calendar: {
+      type: "calendar",
+      maxTabs: 1,
+      openTab: function(aTab, aTitle) {
+        aTab.title = aTitle;
+        ltnSwitch2Calendar();
+      },
+      showTab: function(aTab) {
+        ltnSwitch2Calendar();
       }
-      return toolbars;
-    }
+    },
+    tasks: {
+      type: "tasks",
+      maxTabs: 1,
+      openTab: function(aTab, aTitle) {
+        aTab.title = aTitle;
+        ltnSwitch2Task();
+      },
+      showTab: function(aTab) {
+        ltnSwitch2Task();
+      }
+    },
+  },
 
-    var restoreToolbarProperties = function(toolbox,toolbars)
-    {
-      var toolbar = toolbox.firstChild;
-      while (toolbar) {
-        if (toolbar.localName == "toolbar") {
-          if (toolbar.getAttribute("customizable") == "true") {
-            if (!toolbar.hasAttribute("customindex")) {
-              var propertybag = toolbars[toolbar.id];
-              toolbar.firstPermanentChild = propertybag.firstPermanentChild;
-              toolbar.lastPermanentChild = propertybag.lastPermanentChild;
-            }
-          }
-        }
-        toolbar = toolbar.nextSibling;
-      }
-    }
+  /* because calendar does some direct menu manipulation, we need to change
+   *  to the mail mode to clean up after those hacks.
+   */
+  saveTabState: function(aTab) {
+    ltnSwitch2Mail();
+  },
+  closeTab: function(aTab) {
+    ltnSwitch2Mail();
+  },
+};
+window.addEventListener("load", function(e) { 
+  document.getElementById('tabmail').registerTabType(calendarTabType); }, false);
 
-    // DOMAttrModified handler that listens on the toolbox element
-    var onModified = function(aEvent) {
-      if(aEvent.attrName == "location") {
-        var modeToolbox = document.getElementById("mode-toolbox");
-        modeToolbox.removeEventListener("DOMAttrModified", onModified, false);
-        var onLocationHandler = function() {
-          var contentPanel = document.getElementById("contentPanel");
-          var palette = modeToolbox.palette;
-          var bag = retrieveToolbarProperties(modeToolbox);
-          if(aEvent.newValue == "top" && !aEvent.prevValue || aEvent.prevValue == "bottom") {
-            // place the mode toolbox at the top of the left pane
-            modeToolbox = contentPanel.parentNode.insertBefore(modeToolbox, contentPanel);
-          } else if(aEvent.newValue == "bottom" && aEvent.prevValue == "top") {
-            // place the mode toolbox at the bottom of the left pane
-            modeToolbox = contentPanel.parentNode.appendChild(modeToolbox);
-          }
-          restoreToolbarProperties(modeToolbox,bag);
-          modeToolbox.addEventListener("DOMAttrModified", onModified, false);
-          modeToolbox.palette = palette;
-        }
 
-        setTimeout(onLocationHandler,1);
-      }
-    }
-
-    // install the handler that listens for modified 'location' attribute
-    // on the toolbox. the value is changed by the toolbar customize dialog.
-    var modeToolbox = document.getElementById("mode-toolbox");
-    if(modeToolbox.getAttribute("location") != "bottom") {
-      var palette = modeToolbox.palette;
-      var bag = retrieveToolbarProperties(modeToolbox);
-      modeToolbox = contentPanel.parentNode.insertBefore(modeToolbox, contentPanel);
-      modeToolbox.palette = palette;
-      restoreToolbarProperties(modeToolbox,bag);
-    }
-    modeToolbox.addEventListener("DOMAttrModified", onModified, false);
-
-    // To make sure the folder pane doesn't disappear without any possibility
-    // to get it back, we need to reveal it when the mode box is collapsed.
-    function modeBoxAttrModified(event) {
-        if (event.attrName == "collapsed") {
-            document.getElementById("folderPaneBox")
-                    .removeAttribute("collapsed");
-        }
-    }
-
-    document.getElementById("ltnModeBox").addEventListener("DOMAttrModified",
-                                                           modeBoxAttrModified,
-                                                           true);
+function ltnOnLoad(event) {
+    document.getElementById("calendarDisplayDeck").
+      addEventListener("select", LtnObserveDisplayDeckChange, true);
 
     // Take care of common initialization
     commonInitCalendar();
 
     // nuke the onload, or we get called every time there's
     // any load that occurs
     document.removeEventListener("load", ltnOnLoad, true);
 
@@ -209,20 +143,27 @@ function ltnOnLoad(event) {
 
     // Add an unload function to the window so we don't leak any listeners
     window.addEventListener("unload", ltnFinish, false);
 
     // Set up invitations manager
     scheduleInvitationsUpdate(FIRST_DELAY_STARTUP);
     getCalendarManager().addObserver(gInvitationsCalendarManagerObserver);
 
+    // set up the calendar toolbox
+    var toolbox = document.getElementById("calendar-toolbox");
+    toolbox.customizeDone = function(aEvent) { MailToolboxCustomizeDone(aEvent, "CustomizeMailToolbar"); };
+
+    var toolbarset = document.getElementById('calendar-custom-toolbars');
+    toolbox.toolbarset = toolbarset;
+
+
     var filter = document.getElementById("task-tree-filtergroup");
     filter.value = filter.value || "all";
     document.getElementById("modeBroadcaster").setAttribute("mode", gCurrentMode);
-    ltnInitTodayPane();
 }
 
 /* Called at midnight to tell us to redraw date-specific widgets.  Do NOT call
  * this for normal refresh, since it also calls scheduleMidnightRefresh.
  */
 function refreshUIBits() {
     try {
         getMinimonth().refreshDisplay();
@@ -332,17 +273,17 @@ function ltnShowCalendarView(type)
  * - ltnSwitch2Calendar()
  * - ltnSwitch2Task()
  */
 function LtnObserveDisplayDeckChange(event) {
     var deck = event.target;
 
     // Bug 309505: The 'select' event also fires when we change the selected
     // panel of calendar-view-box.  Workaround with this check.
-    if (deck.id != "displayDeck") {
+    if (deck.id != "calendarDisplayDeck") {
         return;
     }
 
     var id = null;
     try { id = deck.selectedPanel.id } catch (e) { }
 
     // Switch back to mail mode in case we find that this
     // notification has been fired but we're still in calendar or task mode.
@@ -375,24 +316,16 @@ function findMailSearchBox() {
         return tb2Box;
     }
 
     // In later versions, it's possible that a user removed the search box from
     // the toolbar.
     return null;
 }
 
-var gSelectFolder = SelectFolder;
-var gSelectMessage = SelectMessage;
-
-SelectFolder = function(folderUri) {
-    document.getElementById("switch2mail").doCommand();
-    gSelectFolder(folderUri);
-}
-
 var calendarpopuplist = new Array();
 var taskpopuplist = new Array();
 var mailpopuplist = new Array();
 var menulist = new Array();
 #ifdef XP_MACOSX
     var quitMenu = null;
     var prefMenu = null;
 #endif
@@ -403,19 +336,17 @@ function ltnInitializeMenus(){
     prefMenu = document.getElementById("menu_preferences");
     prefMenu.setAttribute("mode", "system");
     quitMenu = document.getElementById("menu_FileQuitItem");
     quitMenu.setAttribute("mode", "system");
 #endif
     copyPopupMenus();
     ltnRemoveMailOnlyItems(calendarpopuplist, "calendar");
     ltnRemoveMailOnlyItems(taskpopuplist, "task");
-    var modeToolbar = document.getElementById("mode-toolbar");
-    var visible = !modeToolbar.hasAttribute("collapsed");
-    document.getElementById("modeBroadcaster").setAttribute("checked", visible);
+    document.getElementById("modeBroadcaster").setAttribute("checked", true);
     document.getElementById("calendar-toolbar").setAttribute("collapsed", gCurrentMode!="calendar");
     document.getElementById("task-toolbar").setAttribute("collapsed", gCurrentMode!="task");
 }
 
 function getMenuElementById(aElementId, aMenuPopup) {
         var element = null;
         var elements = aMenuPopup.getElementsByAttribute("id", aElementId);
         if (elements.length > 0) {
@@ -735,24 +666,16 @@ function openInvitationsDialog() {
     gInvitationsCalendarManagerObserver.mCount = 0;
     getInvitationsManager().openInvitationsDialog(
         gInvitationsOperationListener,
         function oiD_callback() {
             scheduleInvitationsUpdate(FIRST_DELAY_RESCHEDULE);
         });
 }
 
-SelectMessage = function(messageUri) {
-    document.getElementById("switch2mail").doCommand();
-    gSelectMessage(messageUri);
-}
-
-document.getElementById("displayDeck").
-    addEventListener("select", LtnObserveDisplayDeckChange, true);
-
 document.addEventListener("load", ltnOnLoad, true);
 
 /**
  * Sets up the message pane context menu. Even though the actual context menu
  * is in messenger-overlay-toolbar.xul, this needs to be in a file that
  * directly overlays messenger.xul or the functions will not be defined.
  */
 function calSetupMsgPaneContext() {
--- a/calendar/lightning/content/messenger-overlay-sidebar.xul
+++ b/calendar/lightning/content/messenger-overlay-sidebar.xul
@@ -349,53 +349,51 @@
    <menuitem id="ltnTaskToolbar"
              type="checkbox"
              label="&task.toolbar.label;"
              accesskey="&task.toolbar.accesskey;"
              mode="task"
              command="cmd_toggleTaskToolbar"
              observes="cmd_toggleTaskToolbar"
              position="2"/>
-   <menuitem id="ltnModeToolbar"
-             type="checkbox"
-             label="&mode.toolbar.label;"
-             accesskey="&mode.toolbar.accesskey;"
-             observes="modeBroadcaster"
-             command="cmd_toggleModeToolbar"
-             position="3"/>
   </menupopup>
 
 <window id="messengerWindow">
 
   <broadcasterset id="calendar_broadcasters">
     <broadcaster id="filterBroadcaster" value="all"/>
   </broadcasterset>
 
   <!-- Be sure to keep these sets, since they will be overlayed by
        calendar/base/content/calendar-common-sets.xul -->
   <commandset id="calendar_commands">
     <command id="agenda_delete_event_command" oncommand="agendaListbox.deleteSelectedItem(false);"/>
     <command id="agenda_edit_event_command" oncommand="agendaListbox.editSelectedItem(event);"/>
-    <command id="switch2mail" checked="true" oncommand="ltnSwitch2Mail()"/>
-    <command id="switch2calendar" oncommand="ltnSwitch2Calendar()"/>
-    <command id="switch2task" oncommand="ltnSwitch2Task()"/>
-    <command id="cmd_toggleTodayPane" oncommand="toggleTodayPaneinMailMode()"/>
-    <command id="cmd_toggleModeToolbar" oncommand="toggleToolbar('cmd_toggleModeToolbar', 'mode-toolbar')"/>
+    <command id="switch2mail" checked="true"
+      oncommand="document.getElementById('tabmail').selectTabByMode('folder')"/>
+    <command id="switch2calendar"
+      oncommand="document.getElementById('tabmail').openTab('calendar', document.getElementById('calendar-tab-button').getAttribute('tooltiptext'))"/>
+    <command id="switch2task"
+      oncommand="document.getElementById('tabmail').openTab('tasks', document.getElementById('task-tab-button').getAttribute('tooltiptext'))"/>
+    <command id="new_calendar_tab"
+      oncommand="document.getElementById('tabmail').openTab('calendar', document.getElementById('calendar-tab-button').getAttribute('tooltiptext'))"/>
+    <command id="new_task_tab"
+      oncommand="document.getElementById('tabmail').openTab('tasks', document.getElementById('task-tab-button').getAttribute('tooltiptext'))"/>
     <command id="cmd_toggleCalendarToolbar" oncommand="toggleControlinMode('cmd_toggleCalendarToolbar', 'calendar-toolbar')"/>
     <command id="cmd_toggleTaskToolbar" oncommand="toggleControlinMode('cmd_toggleTaskToolbar', 'task-toolbar')"/>
     <command id="calendar-delete-command" oncommand="ltnDeleteSelectedItem()" disabledwhennoeventsselected="true"/>
     <command id="calendar_new_todo_command" oncommand="createTodoWithDialog(getSelectedCalendar());"/>
 
     <command id="lightning_delete_item_command" oncommand="goDoCommand('lightning_delete_item_command');"/>
     <command id="lightning_modify_item_command" oncommand="goDoCommand('lightning_modify_item_command');"/>
   </commandset>
 
   <keyset id="calendar-keys">
-    <key id="openLightningKey" modifiers="accel" key="3" observes="switch2calendar"/>
-    <key id="openTasksKey" modifiers="accel" key="4" command="switch2task"/>
+    <key id="openLightningKey" modifiers="accel" key="3" observes="new_calendar_tab"/>
+    <key id="openTasksKey" modifiers="accel" key="4" command="new_task_tab"/>
     <key id="todaypanekey" command="calendar_toggle_todaypane_command" keycode="VK_F11"/>
     <key id="calendar-new-event-key" key="&lightning.keys.event.new;" modifiers="accel" command="calendar_new_event_command"/>
     <key id="calendar-new-todo-key" key="&lightning.keys.todo.new;" modifiers="accel" command="calendar_new_todo_command"/>
   </keyset>
 
   <popupset id="calendar-popupset">
     <menupopup id="calendar-GoPopupMenu">
        <menuitem id="ltnGoToToday"
@@ -468,26 +466,30 @@
                  accesskey="&calendar.properties.accesskey;"
                  command="calendar_edit_calendar_command"
                  observes="calendar_edit_calendar_command"/>
     </menupopup>
   </popupset>
 
 </window>
 
-<hbox id="messengerBox" flex="1">
-
-  <!-- this will replace 'folderPaneBox' -->
-  <vbox id="ltnModeBox" insertbefore="folderPaneBox" persist="collapsed width">
-
-    <deck id="contentPanel" flex="1" minwidth="100" width="200" persist="collapsed width">
-
+<box id="tabmail-buttons">
+  <toolbarbutton id="calendar-tab-button" command="new_calendar_tab"
+    tooltiptext="&lightning.toolbar.calendar.label;"/>
+  <toolbarbutton id="task-tab-button" command="new_task_tab"
+    tooltiptext="&lightning.toolbar.task.label;"/>
+</box>
+<tabpanels id="tabpanelcontainer">
+  <vbox id="calendarTabPanel" orientation="vertical">
+    <toolbox id="calendar-toolbox" class="toolbox-top"/>
+    <box id="calendarContent" orientation="horizontal" flex="1">
       <vbox id="ltnSidebar"
-            height="350"
-            persist="height"
+            minwidth="100"
+            width="200"
+            persist="collapsed width"
             ondraggesture="nsDragAndDrop.startDrag(event, calendarViewDNDObserver);"
             ondragover="nsDragAndDrop.dragOver(event, calendarViewDNDObserver);"
             ondragdrop="nsDragAndDrop.drop(event, calendarViewDNDObserver);">
         <modevbox id="minimonth-pane" mode="calendar,task" broadcaster="modeBroadcaster" refcontrol="calendar_toggle_minimonthpane_command">
           <vbox align="center">
             <hbox id="ltnMinimonthBox">
               <spacer flex="1"/>
               <minimonth id="ltnMinimonth" onchange="ltnMinimonthPick(this);" flex="2"/>
@@ -526,87 +528,35 @@
           <modevbox id="calendar-list-pane" flex="1" mode="calendar,task" broadcaster="modeBroadcaster"
                     refcontrol="calendar_toggle_calendarlist_command">
             <treenode-checkbox id="calendar-list-header"
                              checked="true"
                              class="treenode-checkbox"
                              label="&calendar.list.header.label;"/>
             <modevbox id="calendar-listtree-pane" flex="1" mode="calendar,task" broadcaster="modeBroadcaster"
                       refcontrol="calendar-list-header">
-              <!-- This will be overlayed by calendar-calendars-list.xul -->
+              
               <tree id="calendar-list-tree-widget"  class="task-tree-subpane"
                 flex="1"/>
             </modevbox>
         </modevbox>
         </vbox>
-      </vbox>
-    </deck>
-
-    <popup id="mode-toolbar-context-menu">
-      <menuitem id="customize-mode-toolbar"
-                label="&customizeToolbar.label;"
-                accesskey="&customizeToolbar.accesskey;"
-                oncommand="CustomizeApplicationToolbar('mode-toolbox');"/>
-    </popup>
-
-    <modehbox id="invitations-pane" mode="mail,calendar,task" 
+        <modehbox id="invitations-pane" mode="calendar,task"
               broadcaster="modeBroadcaster" refcontrol="calendar_toggle_invitations_command">
       <label id="invitations"
              class="text-link calendar-invitations-sidebar-label"
              value="&lightning.sidebar.invitations.label;"
              onclick="openInvitationsDialog();"/>
       <spacer flex="1"/>
     </modehbox>
+  </vbox>
 
-    <toolbox id="mode-toolbox" location="bottom" mode="full">
-      <toolbarpalette id="mode-toolbox-palette">
-        <toolbarbutton class="toolbarbutton-2"
-                       mode="mode"
-                       id="mail-switch-button"
-                       label="&lightning.toolbar.mail.label;"
-                       command="switch2mail"
-                       ondraggesture="nsDragAndDrop.startDrag(event, calendarMailButtonDNDObserver);"
-                       ondragover="nsDragAndDrop.dragOver(event, calendarMailButtonDNDObserver);"
-                       ondragdrop="nsDragAndDrop.drop(event, calendarMailButtonDNDObserver);"/>
-        <toolbarbutton class="toolbarbutton-2"
-                       mode="mode"
-                       id="calendar-switch-button"
-                       label="&lightning.toolbar.calendar.label;"
-                       command="switch2calendar"
-                       ondraggesture="nsDragAndDrop.startDrag(event, calendarCalendarButtonDNDObserver);"
-                       ondragover="nsDragAndDrop.dragOver(event, calendarCalendarButtonDNDObserver);"
-                       ondragdrop="nsDragAndDrop.drop(event, calendarCalendarButtonDNDObserver);"/>
-        <toolbarbutton class="toolbarbutton-2"
-                       mode="mode"
-                       id="task-switch-button"
-                       label="&lightning.toolbar.task.label;"
-                       command="switch2task"
-                       ondraggesture="nsDragAndDrop.startDrag(event, calendarTaskButtonDNDObserver);"
-                       ondragover="nsDragAndDrop.dragOver(event, calendarTaskButtonDNDObserver);"
-                       ondragdrop="nsDragAndDrop.drop(event, calendarTaskButtonDNDObserver);"/>
-      </toolbarpalette>
-      <toolbar id="mode-toolbar"
-               class="chromeclass-toolbar"
-               customizable="true"
-               defaultset="mail-switch-button,calendar-switch-button,task-switch-button,spring"
-               context="mode-toolbar-context-menu"
-               persist="collapsed"/>
-      <toolbarset id="custom-toolbars"/>
-    </toolbox>
-
-  </vbox>
-  <splitter id="today-splitter" collapse="after" resizebefore="closest" state="collapsed">
-      <grippy/>
-  </splitter>
-  <modevbox id="today-pane-panel"
-            addtoolbarbutton="true"
-            persist="width addtoolbarbutton"/>
-</hbox>
-
-<deck id="displayDeck">
+      <splitter id="calsidebar_splitter" collapse="before" persist="state"/>
+      
+      <deck id="calendarDisplayDeck" flex="1">
   <vbox id="calendar-view-box" context="calendar-view-context-menu">
     <deck flex="1"
           id="view-deck"
           persist="selectedIndex"
           ondraggesture="nsDragAndDrop.startDrag(event, calendarViewDNDObserver);"
           ondragover="nsDragAndDrop.dragOver(event, calendarViewDNDObserver);"
           ondragdrop="nsDragAndDrop.drop(event, calendarViewDNDObserver);">
       <calendar-decorated-day-view id="day-view" flex="1"
@@ -618,12 +568,21 @@
       <calendar-decorated-multiweek-view id="multiweek-view" flex="1"
                                          context="calendar-view-context-menu"
                                          item-context="calendar-item-context-menu"/>
       <calendar-decorated-month-view id="month-view" flex="1"
                                          context="calendar-view-context-menu"
                                          item-context="calendar-item-context-menu"/>
     </deck>
   </vbox>
-</deck>
+      </deck>
 
-<vbox id="messagepanebox"/>
+      <splitter id="today-splitter" collapse="after" resizebefore="closest" state="collapsed">
+          <grippy/>
+      </splitter>
+      <modevbox id="today-pane-panel"
+                addtoolbarbutton="true"
+                persist="width addtoolbarbutton"/>
+    </box>
+  </vbox>
+</tabpanels>
+
 </overlay>
--- a/calendar/lightning/content/messenger-overlay-toolbar.js
+++ b/calendar/lightning/content/messenger-overlay-toolbar.js
@@ -46,242 +46,129 @@ var gCustomizeId;
  *  - 'mode'
  *  - 'mail'
  *  - 'calendar'
  *  - 'task'
  */
 var gCurrentMode = 'mail';
 
 /**
- * Helper function to get the view deck in a neutral way, regardless of whether
- * we're in Thunderbird or SeaMonkey
- */
-function getMailBar() {
-  return document.getElementById("mail-bar2") ||
-         document.getElementById("msgToolbar");
-}
-
-/**
- * Ensure that switching to the messenger window also switches to mail mode.
- * We probably should also catch this from other windows (compose, addressbook),
- * but for now we'll keep it here. This function overrides the toMessengerWindow
- * function in /mail/base/content/mailCore.js.
- */
-var toMessengerWindow = function ltnToMessengerWindow() {
-    var wm = Components.classes['@mozilla.org/appshell/window-mediator;1']
-             .getService(Components.interfaces.nsIWindowMediator);
-
-    var topWindow = wm.getMostRecentWindow("mail:3pane");
-
-    if (topWindow) {
-        var tomail = topWindow.document.getElementById("switch2mail");
-        tomail.doCommand();
-        topWindow.focus();
-    } else {
-        window.open("chrome://messenger/content/messenger.xul",
-                    "_blank",
-                    "chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar");
-    }
-};
-
-/**
  * ltnSwitch2Mail() switches to the mail mode
  */
 
 function ltnSwitch2Mail() {
   if (gCurrentMode != 'mail') {
-
     var switch2mail = document.getElementById("switch2mail");
     var switch2calendar = document.getElementById("switch2calendar");
     var switch2task = document.getElementById("switch2task");
     switch2mail.setAttribute("checked", "true");
     switch2calendar.removeAttribute("checked");
     switch2task.removeAttribute("checked");
 
     gCurrentMode = 'mail';
     swapPopupMenus();
     document.getElementById("modeBroadcaster").setAttribute("mode", gCurrentMode);
 
-    var mailToolbarMenuItem = document.getElementById("menu_showMessengerToolbar");
-    if (mailToolbarMenuItem) {
-        if (mailToolbarMenuItem.getAttribute("checked") == "true") {
-            getMailBar().removeAttribute("collapsed");
-        }
-    } else {
-        // Bug 440700: Corresponding toolbar menu entry is currently not
-        // accessible in Thunderbird Trunk -> always restore the toolbar
-        getMailBar().removeAttribute("collapsed");
-    }
-
-    var calendarToolbar = document.getElementById("calendar-toolbar");
-    calendarToolbar.setAttribute("collapsed", "true");
-
-    var calendarToolbar = document.getElementById("task-toolbar");
-    calendarToolbar.setAttribute("collapsed", "true");
-
-    // the content panel should display the folder tree
-    var contentDeck = document.getElementById("contentPanel");
-    contentDeck.selectedPanel = document.getElementById("folderPaneBox");
-
-    // display the mail panel on the display deck
-    var viewBox = document.getElementById("calendar-view-box");
-    collapseElement(viewBox);
-
-    // tell thunderbird that it needs to refresh the mail list.
-    // basically, we fake a selection change by directly calling
-    // the appropriate handler while clearing out some internal
-    // variables in order to force a refresh of the mail views.
-    gMsgFolderSelected = null;
-    msgWindow.openFolder = null;
-    ShowThreadPane();
-    FolderPaneSelectionChange();
-
-    document.commandDispatcher.updateCommands('mail-toolbar');
     document.commandDispatcher.updateCommands('calendar_commands');
 
     // Disable the rotate view menuitem
     document.getElementById("calendar_toggle_orientation_command")
             .setAttribute("disabled", "true");
     window.setCursor("auto");
   }
 }
 
 /**
  * ltnSwitch2Calendar() switches to the calendar mode
  */
 
 function ltnSwitch2Calendar() {
   if (gCurrentMode != 'calendar') {
-
     var switch2mail = document.getElementById("switch2mail");
     var switch2calendar = document.getElementById("switch2calendar");
     var switch2task = document.getElementById("switch2task");
     switch2mail.removeAttribute("checked");
     switch2calendar.setAttribute("checked", "true");
     switch2task.removeAttribute("checked");
 
     gCurrentMode = 'calendar';    
     swapPopupMenus();
     document.getElementById("modeBroadcaster").setAttribute("mode", gCurrentMode);    
 
-    var mailToolbar = getMailBar();
-    mailToolbar.setAttribute("collapsed", "true");
-
     toggleControlDisplay("cmd_toggleCalendarToolbar", "calendar-toolbar", "calendar");
     toggleControlDisplay("cmd_toggleTaskToolbar", "task-toolbar", "task");
     var taskToolbar = document.getElementById("task-toolbar");
     taskToolbar.setAttribute("collapsed", "true");
 
-
-    // the content deck should display the calendar panel
-    var contentDeck = document.getElementById("contentPanel");
-    contentDeck.selectedPanel = document.getElementById("ltnSidebar");
-
     // display the calendar panel on the display deck
     var viewBox = document.getElementById("calendar-view-box");
     uncollapseElement(viewBox);
-    var deck = document.getElementById("displayDeck");
+    var deck = document.getElementById("calendarDisplayDeck");
     deck.selectedPanel = viewBox;
 
     // show the last displayed type of calendar view
     showCalendarView(gLastShownCalendarView);
 
-    document.commandDispatcher.updateCommands('mail-toolbar');
     document.commandDispatcher.updateCommands('calendar_commands');
 
     window.setCursor("auto");
   }
 }
 
 /**
  * ltnSwitch2Task() switches to the task mode
  */
 
 function ltnSwitch2Task() {
   if (gCurrentMode != 'task') {
-
     var switch2mail = document.getElementById("switch2mail");
     var switch2calendar = document.getElementById("switch2calendar");
     var switch2task = document.getElementById("switch2task");
     switch2mail.removeAttribute("checked");
     switch2calendar.removeAttribute("checked");
     switch2task.setAttribute("checked", "true");
+
     toggleControlDisplay("cmd_toggleCalendarToolbar", "calendar-toolbar", "calendar");
     toggleControlDisplay("cmd_toggleTaskToolbar", "task-toolbar", "task");
     gCurrentMode = 'task';
     swapPopupMenus();
     document.getElementById("modeBroadcaster").setAttribute("mode", gCurrentMode);    
-    var mailToolbar = getMailBar();
     var calendarToolbar = document.getElementById("calendar-toolbar");
-    mailToolbar.setAttribute("collapsed", "true");
     calendarToolbar.setAttribute("collapsed", "true");
 
-    // the content deck should display the calendar panel
-    var contentDeck = document.getElementById("contentPanel");
-    contentDeck.selectedPanel = document.getElementById("ltnSidebar");
-
     // display the task panel on the display deck
     var taskBox = document.getElementById("calendar-task-box");
     uncollapseElement(taskBox);
-    var deck = document.getElementById("displayDeck");
+    var deck = document.getElementById("calendarDisplayDeck");
     deck.selectedPanel = taskBox;
 
-    // change title to "Tasks"
-    document.title = ltnGetString("lightning", "taskModeApplicationTitle") + " - " + 
-                     calGetString("brand", "brandShortName", null, "branding");
-
-    document.commandDispatcher.updateCommands('mail-toolbar');
     document.commandDispatcher.updateCommands('calendar_commands');
 
     window.setCursor("auto");
   }
 }
 
 /**
  * CustomizeApplicationToolbar() is called to customize one of the toolbars.
  * the appropriate identifier is passed as argument to the function.
  */
 
 // this shadows CustomizeMailToolbar from mail/base/content/mailCore.js
 // but adds the specific bits and pieces for lightning.
-function CustomizeApplicationToolbar(id) {
+function CustomizeCalendarToolbar(id) {
   // the following code operates different whether
   // or not we're actually customizing the mode toolbar or
   // any other toolbar.
   gCustomizeId = id;
-  var isModeToolbox = (id == 'mode-toolbox');
-  var modeName = isModeToolbox ? 'mode' : gCurrentMode;
+  var modeName = gCurrentMode;
 
   // retrieve the toolbars from the tree
-  var mailbar = getMailBar();
-  var menubar = document.getElementById('mail-menubar');
+  var calendarbox = document.getElementById('calendar-toolbox');
   var calendarbar = document.getElementById('calendar-toolbar');
   var taskbar = document.getElementById('task-toolbar');
-  var mailbox = document.getElementById("mail-toolbox");
-  var modebar = document.getElementById('mode-toolbar');
-  var modebox = document.getElementById('mode-toolbox');
-
-  // install the callback that handles what needs to be
-  // done after a toolbar has been customized.
-  if (modebox) {
-    mailbox.customizeDone = ModeToolboxCustomizeDone;
-    modebox.customizeDone = ModeToolboxCustomizeDone;
-
-    // disable elements on the toolbars
-    if (isModeToolbox) {
-      EnableDisableHierarchy(menubar, true);
-      EnableDisableHierarchy(mailbar, true);
-      EnableDisableHierarchy(calendarbar, true);
-      EnableDisableHierarchy(taskbar, true);
-    } else {
-      EnableDisableHierarchy(modebar, true);
-    }
-  } else {
-    modeName = null;
-  }
 
   var customizePopup = document.getElementById("CustomizeMailToolbar");
   customizePopup.setAttribute("disabled", "true");
 
   var wintype = document.documentElement.getAttribute("windowtype");
   wintype = wintype.replace(/:/g, "");
 
   // lightning install a new dropdown list in the customize dialog
@@ -291,25 +178,22 @@ function CustomizeApplicationToolbar(id)
   // "calendar" or "task".
   var onModeSwitch = function switchHandler(aMode) {
 
     // assume that we're switching to the mode toolbar
     var toolbox = 'mode-toolbox';
 
     // check which toolbar is to be customized next
     // and possibly switch the the appropriate mode.
-    if(aMode == 'mail') {
-      ltnSwitch2Mail();
-      toolbox = 'mail-toolbox';
-    } else if(aMode == 'calendar') {
+    if (aMode == 'calendar') {
       ltnSwitch2Calendar();
-      toolbox = 'mail-toolbox';
+      toolbox = 'calendar-toolbox';
     } else if(aMode == 'task') {
       ltnSwitch2Task();
-      toolbox = 'mail-toolbox';
+      toolbox = 'calendar-toolbox';
     }
 
     // enable/disable all toolbar to reflect the new state
     var isMode = (aMode == 'mode');
     EnableDisableHierarchy(modebar, !isMode);
     EnableDisableHierarchy(menubar, isMode);
     EnableDisableHierarchy(mailbar, isMode);
     EnableDisableHierarchy(calendarbar, isMode);
--- a/calendar/lightning/content/messenger-overlay-toolbar.xul
+++ b/calendar/lightning/content/messenger-overlay-toolbar.xul
@@ -93,42 +93,48 @@
         <menuitem id="threadPaneContext-calendar-convert-task-menuitem"
                   label="&calendar.context.convertmenu.task.label;"
                   accesskey="&calendar.context.convertmenu.task.accesskey;"
                   oncommand="calendarTaskButtonDNDObserver.onDropMessage(messenger.msgHdrFromURI(GetFirstSelectedMessage()))"/>
       </menupopup>
     </menu>
   </popup>
 
-  <menuitem id="menu_customizeToolbar"
+  <window id="messengerWindow">
+    <popup id="calendar-toolbar-context-menu"
+           onpopupshowing="onViewToolbarsPopupShowing(event, 'calendar-toolbox');">
+      <menuitem id="CustomizeCalendarToolbar"
+                oncommand="CustomizeMailToolbar('calendar-toolbox', 'CustomizeMailToolbar');"
             label="&customizeToolbar.label;"
-            accesskey="&customizeToolbar.accesskey;"
-            oncommand="CustomizeApplicationToolbar('mail-toolbox');"/>
+                accesskey="&customizeToolbar.accesskey;"/>
+    </popup>
+  </window>
 
-  <toolbarpalette id="MailToolbarPalette">
-<!-- All toolbar buttons that messenger-overlay-toolbar.xul wishes to include
+  <toolbox id="calendar-toolbox">
+    <toolbarpalette id="CalendarToolbarPalette">
+  <!-- All toolbar buttons that messenger-overlay-toolbar.xul wishes to include
      *must* go either into the calendar-toolbar.inc file (all toolbar buttons
      shared with Sunbird) or lightning-toolbar.inc file (toolbar buttons
      relevant only for Lightning). -->
 #include ../../base/content/calendar-toolbar.inc
 #include lightning-toolbar.inc
   </toolbarpalette>
   
-  <toolbox id="mail-toolbox">
     <toolbar id="calendar-toolbar"
              class="chromeclass-toolbar"
              customizable="true"
-             context="toolbar-context-menu"
+             context="calendar-toolbar-context-menu"
              collapsed="true"
              collapsedinMode="false"
              defaultset="calendar-new-event-button,calendar-new-task-button,calendar-show-todaypane-button,separator,calendar-go-to-today-button,separator,calendar-day-view-button,calendar-week-view-button,calendar-multiweek-view-button,calendar-month-view-button,separator,calendar-unifinder-button,separator,calendar-delete-button,calendar-print-button,separator,calendar-remote-reload-button,spring"
              persist="collapsedinMode"/>
     <toolbar id="task-toolbar"
              class="chromeclass-toolbar"
              customizable="true"
-             context="toolbar-context-menu"
+             context="calendar-toolbar-context-menu"
              collapsed="true"
              collapsedinMode="false"
              defaultset="task-new-event-button,task-new-task-button,task-button-address,task-show-todaypane-button,separator,task-category-button,task-progress-button,task-priority-button,separator,task-delete-button,task-print-button,separator,task-remote-reload-button,spring"
              persist="collapsedinMode"/>
+    <toolbarset id="calendar-custom-toolbars"/>
   </toolbox>
 
 </overlay>
--- a/calendar/lightning/jar.mn
+++ b/calendar/lightning/jar.mn
@@ -11,37 +11,33 @@ lightning.jar:
 % overlay chrome://lightning/content/messenger-overlay-sidebar.xul chrome://lightning/content/imip-bar-overlay.xul
 % overlay chrome://messenger/content/preferences/preferences.xul chrome://lightning/content/messenger-overlay-preferences.xul
 % overlay chrome://messenger/content/preferences/preferences.xul chrome://calendar/content/preferences/alarms.xul
 % overlay chrome://messenger/content/preferences/preferences.xul chrome://calendar/content/preferences/categories.xul
 % overlay chrome://messenger/content/preferences/preferences.xul chrome://calendar/content/preferences/general.xul
 % overlay chrome://messenger/content/preferences/preferences.xul chrome://calendar/content/preferences/timezones.xul
 % overlay chrome://messenger/content/preferences/preferences.xul chrome://calendar/content/preferences/views.xul
 % overlay chrome://messenger/content/mailWindowOverlay.xul chrome://lightning/content/messenger-overlay-toolbar.xul
-% overlay chrome://global/content/customizeToolbar.xul chrome://lightning/content/customize-toolbar.xul
 % overlay chrome://lightning/content/messenger-overlay-sidebar.xul chrome://calendar/content/calendar-unifinder.xul
 % overlay chrome://lightning/content/messenger-overlay-sidebar.xul chrome://calendar/content/calendar-unifinder-todo.xul
 % overlay chrome://lightning/content/messenger-overlay-sidebar.xul chrome://calendar/content/calendar-task-view.xul
 % overlay chrome://lightning/content/messenger-overlay-sidebar.xul chrome://calendar/content/today-pane.xul
 % overlay chrome://lightning/content/lightning-standalone.xul chrome://lightning/content/messenger-overlay-sidebar.xul
 % overlay chrome://global/content/customizeToolbar.xul chrome://lightning/content/toolkit-overlay-custombar.xul
 % overlay chrome://calendar/content/calendarCreation.xul chrome://lightning/content/lightning-calendar-creation.xul
 % overlay chrome://calendar/content/calendar-properties-dialog.xul chrome://lightning/content/lightning-calendar-properties.xul
 % skin lightning classic/1.0 %skin/classic/lightning/
-  content/lightning/customize-toolbar.js                (content/customize-toolbar.js)
-  content/lightning/customize-toolbar.xul               (content/customize-toolbar.xul)
   content/lightning/imip-bar.js                         (content/imip-bar.js)
   content/lightning/imip-bar-overlay.xul                (content/imip-bar-overlay.xul)
   content/lightning/lightning-common-sets.js (content/lightning-common-sets.js)
   content/lightning/lightning-migration.xul             (content/lightning-migration.xul)
   content/lightning/lightning-standalone.xul		(content/lightning-standalone.xul)
   content/lightning/lightning-utils.js			(content/lightning-utils.js) 
   content/lightning/lightning-widgets.css		(content/lightning-widgets.css)
   content/lightning/lightning-widgets.xml		(content/lightning-widgets.xml)
-  content/lightning/lightning-today-pane.js		(content/lightning-today-pane.js)
   content/lightning/messenger-overlay-sidebar.css	(content/messenger-overlay-sidebar.css)
   content/lightning/messenger-overlay-accountCentral.xul (content/messenger-overlay-accountCentral.xul)
   content/lightning/messenger-overlay-messageWindow.xul (content/messenger-overlay-messageWindow.xul)
 * content/lightning/messenger-overlay-sidebar.js	(content/messenger-overlay-sidebar.js)
 * content/lightning/messenger-overlay-sidebar.xul	(content/messenger-overlay-sidebar.xul)
   content/lightning/messenger-overlay-preferences.js    (content/messenger-overlay-preferences.js)
   content/lightning/messenger-overlay-preferences.xul	(content/messenger-overlay-preferences.xul)
   content/lightning/messenger-overlay-toolbar.js        (content/messenger-overlay-toolbar.js)
--- a/calendar/lightning/themes/pinstripe/lightning.css
+++ b/calendar/lightning/themes/pinstripe/lightning.css
@@ -50,118 +50,66 @@ radio[pane=paneLightning] {
 }
 
 /* iMIP notification bar */
 #imip-bar > image {
     list-style-image: url("chrome://calendar/skin/cal-icon32.png");
     -moz-margin-end: 8px;
 }
 
-/* ::::: mode toolbar buttons ::::: */
-#mode-toolbar {
-  padding: 5px 2px;
-  border-top: 1px solid ThreeDShadow !important;
-}
-
-#mode-toolbar[location="top"] {
-  border-top: 0 !important;
-}
-
-.toolbarbutton-2 {
-  -moz-box-orient: vertical;
-  min-width: 0px;
-  list-style-image: url("chrome://calendar/skin/mode-switch-icons.png");
-}
-
-toolbar[mode="full"] .toolbarbutton-2 {
-  min-width: 55px;
-}
-
-.toolbarbutton-2[type="menu"] {
-  -moz-binding: url("chrome://global/content/bindings/toolbarbutton.xml#menu-vertical");
-}
-
-#mail-switch-button {
-  -moz-image-region: rect(0px 25px 25px 0px);
-}
-
-#mail-switch-button:hover:active {
-  -moz-image-region: rect(25px 25px 50px 0px);
-}
-
-#mail-switch-button[disabled] {
-  -moz-image-region: rect(50px 25px 75px 0px);
-}
+/* ::::: tabs ::::: */
 
-#calendar-switch-button {
-  -moz-image-region: rect(0px 50px 25px 25px);
-}
-
-#calendar-switch-button:hover:active {
-  -moz-image-region: rect(25px 50px 50px 25px);
-}
-
-#calendar-switch-button[disabled] {
-  -moz-image-region: rect(50px 50px 75px 25px);
-}
-
-#task-switch-button {
-  -moz-image-region: rect(0px 75px 25px 50px);
-}
-
-#task-switch-button:hover:active {
-  -moz-image-region: rect(25px 75px 50px 50px);
-}
-
-#task-switch-button[disabled] {
-  -moz-image-region: rect(50px 75px 75px 50px);
-}
-
-/* ::::: small mode toolbar buttons ::::: */
-toolbar[iconsize="small"] .toolbarbutton-2 {
- list-style-image: url("chrome://calendar/skin/mode-switch-icons.png");
-}
-
-toolbar[iconsize="small"] #mail-switch-button {
-  -moz-image-region: rect(0px 91px 16px 75px);
-}
-
-toolbar[iconsize="small"] #mail-switch-button:hover:active {
-  -moz-image-region: rect(16px 91px 32px 75px);
-}
-
-toolbar[iconsize="small"] #mail-switch-button[disabled] {
-  -moz-image-region: rect(32px 91px 48px 75px);
-}
-
-toolbar[iconsize="small"] #calendar-switch-button {
+/* ::: new tab buttons ::: */
+#calendar-tab-button {
+  list-style-image: url("chrome://calendar/skin/mode-switch-icons.png");
   -moz-image-region: rect(0px 107px 16px 91px);
 }
 
-toolbar[iconsize="small"] #calendar-switch-button:hover:active {
+#calendar-tab-button:hover:active {
   -moz-image-region: rect(16px 107px 32px 91px);
 }
 
-toolbar[iconsize="small"] #calendar-switch-button[disabled] {
+#calendar-tab-button[disabled] {
   -moz-image-region: rect(32px 107px 48px 91px);
 }
 
-toolbar[iconsize="small"] #task-switch-button {
+#task-tab-button {
+ list-style-image: url("chrome://calendar/skin/mode-switch-icons.png");
   -moz-image-region: rect(0px 123px 16px 107px);
 }
 
-toolbar[iconsize="small"] #task-switch-button:hover:active {
+#task-tab-button:hover:active {
   -moz-image-region: rect(16px 123px 32px 107px);
 }
 
-toolbar[iconsize="small"] #task-switch-button[disabled] {
+#task-tab-button[disabled] {
   -moz-image-region: rect(32px 123px 48px 107px);
 }
 
-/* Lightining sidebar background in calendar and task mode */
+/* ::: tab icons ::: */
+.tabmail-tab[type="calendar"] .tab-icon-image {
+  list-style-image: url("chrome://calendar/skin/mode-switch-icons.png");
+  -moz-image-region: rect(0px 107px 16px 91px);
+}
+
+.tabmail-tab[type="calendar"][selected="true"] .tab-icon-image {
+  -moz-image-region: rect(16px 107px 32px 91px);
+}
+
+.tabmail-tab[type="tasks"] .tab-icon-image {
+  list-style-image: url("chrome://calendar/skin/mode-switch-icons.png");
+  -moz-image-region: rect(0px 123px 16px 107px);
+}
+
+.tabmail-tab[type="tasks"][selected="true"] .tab-icon-image {
+  -moz-image-region: rect(16px 123px 32px 107px);
+}
+
+
+/* Lightning sidebar background in calendar and task mode */
 #ltnSidebar {
   background-color: -moz-field;  
 }
 
 #invitations-pane {
   border-top: 1px solid ThreeDShadow !important;
   background-color: -moz-dialog;  
 }
--- a/calendar/lightning/themes/winstripe/lightning.css
+++ b/calendar/lightning/themes/winstripe/lightning.css
@@ -50,118 +50,66 @@ radio[pane=paneLightning] {
 }
 
 /* iMIP notification bar */
 #imip-bar > image {
     list-style-image: url("chrome://calendar/skin/cal-icon32.png");
     -moz-image-region: rect(0px, 32px, 32px, 0px);
 }
 
-/* ::::: mode toolbar buttons ::::: */
-#mode-toolbar {
-  padding: 5px 2px;
-  border-top: 1px solid ThreeDShadow !important;
-}
-
-#mode-toolbar[location="top"] {
-  border-top: 0 !important;
-}
-
-.toolbarbutton-2 {
-  -moz-box-orient: vertical;
-  min-width: 0px;
-  list-style-image: url("chrome://calendar/skin/mode-switch-icons.png");
-}
-
-toolbar[mode="full"] .toolbarbutton-2 {
-  min-width: 55px;
-}
-
-.toolbarbutton-2[type="menu"] {
-  -moz-binding: url("chrome://global/content/bindings/toolbarbutton.xml#menu-vertical");
-}
-
-#mail-switch-button {
-  -moz-image-region: rect(0px 25px 25px 0px);
-}
-
-#mail-switch-button:hover:active {
-  -moz-image-region: rect(25px 25px 50px 0px);
-}
-
-#mail-switch-button[disabled] {
-  -moz-image-region: rect(50px 25px 75px 0px);
-}
+/* ::::: tabs ::::: */
 
-#calendar-switch-button {
-  -moz-image-region: rect(0px 50px 25px 25px);
-}
-
-#calendar-switch-button:hover:active {
-  -moz-image-region: rect(25px 50px 50px 25px);
-}
-
-#calendar-switch-button[disabled] {
-  -moz-image-region: rect(50px 50px 75px 25px);
-}
-
-#task-switch-button {
-  -moz-image-region: rect(0px 75px 25px 50px);
-}
-
-#task-switch-button:hover:active {
-  -moz-image-region: rect(25px 75px 50px 50px);
-}
-
-#task-switch-button[disabled] {
-  -moz-image-region: rect(50px 75px 75px 50px);
-}
-
-/* ::::: small mode toolbar buttons ::::: */
-toolbar[iconsize="small"] .toolbarbutton-2 {
- list-style-image: url("chrome://calendar/skin/mode-switch-icons.png");
-}
-
-toolbar[iconsize="small"] #mail-switch-button {
-  -moz-image-region: rect(0px 91px 16px 75px);
-}
-
-toolbar[iconsize="small"] #mail-switch-button:hover:active {
-  -moz-image-region: rect(16px 91px 32px 75px);
-}
-
-toolbar[iconsize="small"] #mail-switch-button[disabled] {
-  -moz-image-region: rect(32px 91px 48px 75px);
-}
-
-toolbar[iconsize="small"] #calendar-switch-button {
+/* ::: new tab buttons ::: */
+#calendar-tab-button {
+  list-style-image: url("chrome://calendar/skin/mode-switch-icons.png");
   -moz-image-region: rect(0px 107px 16px 91px);
 }
 
-toolbar[iconsize="small"] #calendar-switch-button:hover:active {
+#calendar-tab-button:hover:active {
   -moz-image-region: rect(16px 107px 32px 91px);
 }
 
-toolbar[iconsize="small"] #calendar-switch-button[disabled] {
+#calendar-tab-button[disabled] {
   -moz-image-region: rect(32px 107px 48px 91px);
 }
 
-toolbar[iconsize="small"] #task-switch-button {
+#task-tab-button {
+ list-style-image: url("chrome://calendar/skin/mode-switch-icons.png");
   -moz-image-region: rect(0px 123px 16px 107px);
 }
 
-toolbar[iconsize="small"] #task-switch-button:hover:active {
+#task-tab-button:hover:active {
   -moz-image-region: rect(16px 123px 32px 107px);
 }
 
-toolbar[iconsize="small"] #task-switch-button[disabled] {
+#task-tab-button[disabled] {
   -moz-image-region: rect(32px 123px 48px 107px);
 }
 
-/* Lightining sidebar background in calendar and task mode */
+/* ::: tab icons ::: */
+.tabmail-tab[type="calendar"] .tab-icon-image {
+  list-style-image: url("chrome://calendar/skin/mode-switch-icons.png");
+  -moz-image-region: rect(0px 107px 16px 91px);
+}
+
+.tabmail-tab[type="calendar"][selected="true"] .tab-icon-image {
+  -moz-image-region: rect(16px 107px 32px 91px);
+}
+
+.tabmail-tab[type="tasks"] .tab-icon-image {
+  list-style-image: url("chrome://calendar/skin/mode-switch-icons.png");
+  -moz-image-region: rect(0px 123px 16px 107px);
+}
+
+.tabmail-tab[type="tasks"][selected="true"] .tab-icon-image {
+  -moz-image-region: rect(16px 123px 32px 107px);
+}
+
+
+/* Lightning sidebar background in calendar and task mode */
 #ltnSidebar {
   background-color: -moz-field;  
 }
 
 #invitations-pane {
   border-top: 1px solid ThreeDShadow !important;
   background-color: -moz-dialog;  
 }
--- a/mail/base/content/mailWindowOverlay.js
+++ b/mail/base/content/mailWindowOverlay.js
@@ -25,16 +25,17 @@
 #   slucy@objectivesw.co.uk
 #   Håkan Waara <hwaara@chello.se>
 #   Jan Varga <varga@nixcorp.com>
 #   Seth Spitzer <sspitzer@netscape.com>
 #   David Bienvenu <bienvenu@nventure.com>
 #   Karsten Düsterloh <mnyromyr@tprac.de>
 #   Christopher Thomas <cst@yecc.com>
 #   Jeremy Morton <bugzilla@game-point.net>
+#   Andrew Sutherland <asutherland@asutherland.org>
 #
 # 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
@@ -1289,114 +1290,16 @@ function MsgOpenNewWindowForFolder(uri, 
     // highlighted. GetSelectedFolderURI() will return the message that is
     // highlighted.
     uriToOpen = GetSelectedFolderURI();
 
   if (uriToOpen)
     window.openDialog("chrome://messenger/content/", "_blank", "chrome,all,dialog=no", uriToOpen, keyToSelect);
 }
 
-/** 
-  *saveMailTabInfo - a private helper routine shared by message and folder tab owners to save
-  *                                 the local state of a mail tab
-  * @param aMailTabOwner
-  */
-function saveMailTabInfo(aMailTabOwner)
-{
-  if (aMailTabOwner)
-  {
-    aMailTabOwner.messenger = messenger;
-    aMailTabOwner.dbView = gDBView;
-    aMailTabOwner.searchSession = gSearchSession;
-    var indices = GetSelectedIndices(gDBView);
-    if (indices)
-    {
-      aMailTabOwner.selectedKeys = new Array(indices.length);
-      aMailTabOwner.selectedFolders = new Array(indices.length);
-    }
-    if (gDBView.currentlyDisplayedMessage != -1)
-    {
-      try // there may not be a selected message.
-      {
-          var curMsgHdr = gDBView.hdrForFirstSelectedMessage;
-          aMailTabOwner.selectedMsgId = curMsgHdr.messageId;
-          aMailTabOwner.msgSelectedFolder = curMsgHdr.folder;
-      }
-      catch (ex) {aMailTabOwner.msgSelectedFolder = gMsgFolderSelected};
-    }
-    else
-    {
-      aMailTabOwner.selectedMsgId = null;
-      aMailTabOwner.msgSelectedFolder = null;
-    }
-    if (indices)
-    {
-      for (var i = 0; i < indices.length; i++)
-      {
-        aMailTabOwner.selectedKeys[i] = gDBView.getKeyAt(i);
-        aMailTabOwner.selectedFolders[i] = gDBView.getFolderForViewIndex(i);
-      }
-    }
-  }
-}
-
-/**
-  * setMailTabState - a private helper routine shared by message and folder tab owners for setting up various
-                                     global variables based on the current tab.
-  * @param aMailTabOwner 
-  */
-function setMailTabState(aMailTabOwner)
-{
-  messenger = aMailTabOwner.messenger;
-  gDBView = aMailTabOwner.dbView;
-  gSearchSession = aMailTabOwner.searchSession;
-  if (gDBView)
-  {
-    var folderTree = GetFolderTree();
-    var row = EnsureFolderIndex(folderTree.builderView, gDBView.msgFolder);
-    
-    var folderTreeBoxObj = folderTree.treeBoxObject;
-    var folderTreeSelection = folderTreeBoxObj.view.selection;
-    // make sure that row.value is valid so that it doesn't mess up
-    // the call to ensureRowIsVisible().
-    if((row >= 0) && !folderTreeSelection.isSelected(row))
-    {
-      gMsgFolderSelected = gDBView.msgFolder;
-      folderTreeSelection.selectEventsSuppressed = true;
-      folderTreeSelection.select(row);
-      folderTreeBoxObj.ensureRowIsVisible(row);
-      folderTreeSelection.selectEventsSuppressed = false;
-    }
-    // this sets the thread pane tree's view to the gDBView view.
-    UpdateSortIndicators(gDBView.sortType, gDBView.sortOrder);
-    RerootThreadPane();
-    // we need to restore the selection to what it was when we switched away from this tab.
-    // we need to remember the selected keys, instead of the selected indices, since the view
-    // might have changed. But maybe the selectedIndices adjust as items are added/removed from
-    // the (hidden) view.
-    ClearThreadPaneSelection(); 
-    try
-    {
-      if (aMailTabOwner.selectedMsgId && aMailTabOwner.msgSelectedFolder)
-      {
-        var msgDB = aMailTabOwner.msgSelectedFolder.getMsgDatabase(msgWindow);
-        var msgHdr = msgDB.getMsgHdrForMessageID(aMailTabOwner.selectedMsgId);
-        setTimeout(gDBView.selectFolderMsgByKey, 0, aMailTabOwner.msgSelectedFolder, msgHdr.messageKey);
-      }
-    }
-    catch (ex) {dump(ex);}
-  }
-  else
-  {
-    var tree = GetThreadTree();
-    tree.boxObject.QueryInterface(Components.interfaces.nsITreeBoxObject).view = null;
-    ClearMessagePane();
-  }
-}
-
 function CreateToolbarTooltip(document, event)
 {
   event.stopPropagation();
   var tn = document.tooltipNode;
   if (tn.localName != "tab")
     return false; // Not a tab, so cancel the tooltip
   if ("mOverCloseButton" in tn && tn.mOverCloseButton) {
      event.target.setAttribute("label", tn.getAttribute("closetabtext"));
@@ -1431,146 +1334,216 @@ function DisplayFolderAndThreadPane(show
     document.getElementById("folder-location-container").collapsed = collapse;
   } catch (ex) {}
   try {
     document.getElementById("mailviews-container").collapsed = collapse;
   } catch (ex) {}
 }
 
 /**
-  * openFolderTab - a private helper method used by folder and message tab owners.
-  * @param aMailTabOwner
+ * mailTabType provides both "folder" and "message" tab modes.  Under the
+ *  previous TabOwner framework, their logic was separated into two 'classes'
+ *  which called common helper methods and had similar boilerplate logic.
   */
-function openFolderTab(aMailTabOwner)
-{
+let mailTabType = {
+  name: "mail",
+  panelId: "mailContent",
+  modes: {
+    folder: {
+      isDefault: true,
+      type: "folder",
+      openTab: function(aTab, aFolderUri) {
+        aTab.uriToOpen = aFolderUri;
+
+        this.openTab(aTab); // call superclass logic
+      },
+      showTab: function(aTab) {
+        this.folderAndThreadPaneVisible = true;
+        ClearMessagePane();
+
+        this.showTab(aTab);
+      },
+      onTitleChanged: function(aTab, aTabNode) {
+        aTab.title = gMsgFolderSelected.prettyName;
+        // the user may have changed folders, triggering our onTitleChanged callback.
+        // update the appropriate attributes on the tab.
+        aTabNode.setAttribute('SpecialFolder', getSpecialFolderString(gMsgFolderSelected));
+        aTabNode.setAttribute('ServerType', gMsgFolderSelected.server.type);
+      }
+    },
+    message: {
+      type: "message",
+      openTab: function(aTab, aFolderUri, aMsgHdr) {
+        aTab.uriToOpen = aFolderUri;
+        aTab.hdr = aMsgHdr;
+
+        aTab.title = aTab.hdr.mime2DecodedSubject;
+
+        this.openTab(aTab); // call superclass logic
+
+        gCurrentlyDisplayedMessage = nsMsgViewIndex_None;
   ClearThreadPaneSelection(); 
+        setTimeout(gDBView.selectFolderMsgByKey, 0, aTab.hdr.folder,
+                   aTab.hdr.messageKey);
+
+        // let's try hiding the thread pane and folder pane
+        this.folderAndThreadPaneVisible = false;
+      },
+      showTab: function(aTab) {
+        this.folderAndThreadPaneVisible = false;
+        // ClearMessagePane();
+
+        this.showTab(aTab);
+      }
+    }
+  },
+  /**
+   * Create the new tab's state, which engenders some side effects.  Part of our
+   *  contract is that we leave the tab in the selected state.
+   */
+  openTab: function(aTab) {
+    ClearThreadPaneSelection();
+
+    // each tab gets its own messenger instance; I assume this is so each one
+    //  gets its own undo/redo stack?
   messenger = Components.classes["@mozilla.org/messenger;1"]
                         .createInstance(Components.interfaces.nsIMessenger);
   messenger.setWindow(window, msgWindow);
-  aMailTabOwner.msgSelectedFolder = gMsgFolderSelected;
+    aTab.messenger = messenger;
+
+    aTab.msgSelectedFolder = gMsgFolderSelected;
+
   // clear selection, because context clicking on a folder and opening in a new
   // tab needs to have SelectFolder think the selection has changed.
   // We also need to clear these globals to subvert the code that prevents
   // folder loads when things haven't changed.
   GetFolderTree().view.selection.clearSelection();
   GetFolderTree().view.selection.currentIndex = -1;
   gMsgFolderSelected = null;
   msgWindow.openFolder = null;
+
   // clear thread pane selection - otherwise, the tree tries to impose 
   // the current selection on the new view.
   gDBView = null; // clear gDBView so we won't try to close it.
-  SelectFolder(aMailTabOwner.uriToOpen);
-  aMailTabOwner.dbView = gDBView;
-}
-
-function folderTabOwner()
-{
-  saveMailTabInfo(this);
-}
-
-folderTabOwner.prototype =
-{
-  open : function ()
-  {
-    openFolderTab(this);
+    SelectFolder(aTab.uriToOpen);
+    aTab.dbView = gDBView;
   },
   
-  close : function () 
-  {
-    if (this.dbView)
-      this.dbView.close();
-    if (this.messenger)
-      this.messenger.setWindow(null, null);
+  closeTab: function(aTab) {
+    if (aTab.dbView)
+      aTab.dbView.close();
+    if (aTab.messenger)
+      aTab.messenger.setWindow(null, null);
   },
 
-  saveCurrentInfo : function()
+  saveTabState: function(aTab) {
+    aTab.messenger = messenger;
+    aTab.dbView = gDBView;
+    aTab.searchSession = gSearchSession;
+
+    if (gDBView.currentlyDisplayedMessage != nsMsgViewIndex_None)
   {
-    saveMailTabInfo(this);
-  },
-
-  onSelect : function(aPreviousTabOwner)
+      try // there may not be a selected message.
+    {
+        var curMsgHdr = gDBView.hdrForFirstSelectedMessage;
+        aTab.selectedMsgId = curMsgHdr.messageId;
+        aTab.msgSelectedFolder = curMsgHdr.folder;
+    }
+      catch (ex)
   {
-    if (aPreviousTabOwner.type != this.type)
-    {
-      ClearMessagePane();
-      DisplayFolderAndThreadPane(true);
+        aTab.msgSelectedFolder = gMsgFolderSelected
+      }
+    }
+    else
+  {
+      aTab.selectedMsgId = null;
+      aTab.msgSelectedFolder = null;
+    }
+  },
+
+  _folderAndThreadPaneVisible: true,
+  get folderAndThreadPaneVisible() { return this._folderAndThreadPaneVisible; },
+  set folderAndThreadPaneVisible(aDesiredVisible) {
+    if (aDesiredVisible != this._folderAndThreadPaneVisible) {
+      DisplayFolderAndThreadPane(aDesiredVisible);
+      this._folderAndThreadPaneVisible = aDesiredVisible;
     }
-    setMailTabState(this);
   },
 
-  onTitleChanged: function(aTab)
+  showTab: function(aTab) {
+    // restore globals
+    messenger = aTab.messenger;
+    gDBView = aTab.dbView;
+    gSearchSession = aTab.searchSession;
+
+    // restore view state if we had one
+    if (gDBView)
   {
-    // the user may have changed folders, triggering our onTitleChanged callback.
-    // update the appropriate attributes on the tab.
-    aTab.setAttribute('SpecialFolder', getSpecialFolderString(gMsgFolderSelected));
-    aTab.setAttribute('ServerType', gMsgFolderSelected.server.type);
-    return;
-  },
-
-  get title()
+      var folderTree = GetFolderTree();
+      var row = EnsureFolderIndex(folderTree.builderView, gDBView.msgFolder);
+
+      var folderTreeBoxObj = folderTree.treeBoxObject;
+      var folderTreeSelection = folderTreeBoxObj.view.selection;
+      // make sure that row.value is valid so that it doesn't mess up
+      // the call to ensureRowIsVisible().
+      if ((row >= 0) && !folderTreeSelection.isSelected(row))
+    {
+        gMsgFolderSelected = gDBView.msgFolder;
+        folderTreeSelection.selectEventsSuppressed = true;
+        folderTreeSelection.select(row);
+        folderTreeBoxObj.ensureRowIsVisible(row);
+        folderTreeSelection.selectEventsSuppressed = false;
+    }
+      // this sets the thread pane tree's view to the gDBView view.
+      UpdateSortIndicators(gDBView.sortType, gDBView.sortOrder);
+      RerootThreadPane();
+      // we need to restore the selection to what it was when we switched away
+      // from this tab. we need to remember the selected keys, instead of the
+      // selected indices, since the view might have changed. But maybe the
+      // selectedIndices adjust as items are added/removed from the (hidden)
+      // view.
+      try
+  {
+        if (aTab.selectedMsgId && aTab.msgSelectedFolder)
   {
-    // this only works if we are the owner of the current tab...
-    return gMsgFolderSelected.prettyName;
-  },
-
-  type : "folder",
+          // we clear the selection in order to generate an event when we
+          //  re-select our message.
+          ClearThreadPaneSelection();
+
+          var msgDB = aTab.msgSelectedFolder.getMsgDatabase(msgWindow);
+          var msgHdr = msgDB.getMsgHdrForMessageID(aTab.selectedMsgId);
+          setTimeout(gDBView.selectFolderMsgByKey, 0, aTab.msgSelectedFolder,
+                     msgHdr.messageKey);
+        }
+        // (we do not clear the selection if there was more than one message
+        //  displayed.  this leaves our selection intact. there was originally
+        //  some claim that the selection might lose synchronization with the
+        //  view, but this is unsubstantiated.  said comment came from the
+        //  original code that stored information on the selected rows, but
+        //  then failed to do anything with it, probably because there is no
+        //  existing API call that accomplishes it.)
+      }
+      catch (ex) {dump(ex);}
+    }
+    // make sure the folder tree knows that we don't have a view
+    else
+    {
+      var tree = GetThreadTree();
+      tree.boxObject.QueryInterface(Ci.nsITreeBoxObject).view = null;
+      ClearMessagePane();
+    }
+  }
 };
-
-function messageTabOwner()
-{
-  saveMailTabInfo(this);
-}
-
-messageTabOwner.prototype =
-{
-  open : function ()
-  {
-    openFolderTab(this);
-    gCurrentlyDisplayedMessage = -1;
-    ClearThreadPaneSelection();
-    setTimeout(gDBView.selectFolderMsgByKey, 0, this.hdr.folder, this.hdr.messageKey);
-    // let's try hiding the thread pane and folder pane
-    DisplayFolderAndThreadPane(false);
-  },
-
-  close : function () 
-  {
-    if (this.dbView)
-      this.dbView.close();
-    if (this.messenger)
-      this.messenger.setWindow(null, null);
-  },
-
-  saveCurrentInfo : function()
-  {
-    saveMailTabInfo(this);
-  },
-
-  onSelect : function(aPreviousTabOwner)
-  {
-    if (aPreviousTabOwner.type != this.type)
-    {
-      ClearMessagePane();
-      DisplayFolderAndThreadPane(false);
+window.addEventListener("load", function(e) {
+    let tabmail = document.getElementById('tabmail');
+    if (tabmail) {
+      tabmail.registerTabType(mailTabType);
+      tabmail.openFirstTab();
     }
-    setMailTabState(this);
-  },
-
-  onTitleChanged: function(aTab)
-  {
-    return;
-  },
-
-  get title()
-  {
-    return this.hdr.mime2DecodedSubject;
-  },
-
-  type : "message",
-};
+  }, false);
 
 function MsgOpenNewTabForFolder(uri, key)
 {
   var uriToOpen = uri;
   var keyToSelect = key;
   
   if (!uriToOpen)
     // use GetSelectedFolderURI() to find out which message to open instead of
@@ -1579,19 +1552,17 @@ function MsgOpenNewTabForFolder(uri, key
     // highlighted. GetSelectedFolderURI() will return the message that is
     // highlighted.
     uriToOpen = GetSelectedFolderURI();
   
   // set up the first tab, which was previously invisible.
   // This assumes the first tab is always a 3-pane ui, which
   // may not be right, especially if we have the ability
   // to persist your tab setup.
-  var newTab = new folderTabOwner();
-  newTab.uriToOpen = uriToOpen;
-  document.getElementById('tabmail').addTab(newTab);
+  document.getElementById('tabmail').openTab("folder", uriToOpen);
 }
 
 function MsgOpenNewTabForMessage(messageKey, folderUri)
 {
   if (!messageKey)
     messageKey = gDBView.keyForFirstSelectedMessage;
 
   var hdr = gDBView.hdrForFirstSelectedMessage;
@@ -1600,20 +1571,17 @@ function MsgOpenNewTabForMessage(message
     // in its real folder, which is needed if the msg wouldn't be in a new
     // view with the same terms - e.g., it's read and the view is unread only.
     // If we cloned the view, we wouldn't have to do this.
     folderUri = hdr.folder.URI;
   
   // fix it so we won't try to load the previously loaded message.
   hdr.folder.lastMessageLoaded = nsMsgKey_None;
   
-  tab = new messageTabOwner();
-  tab.uriToOpen = folderUri;
-  tab.hdr = hdr;
-  document.getElementById('tabmail').addTab(tab);
+  document.getElementById('tabmail').openTab("message", folderUri, hdr);
 }
 
 // passing in the view, so this will work for search and the thread pane
 function MsgOpenSelectedMessages()
 {
   var dbView = GetDBView();
 
   var indices = GetSelectedIndices(dbView);
@@ -2314,17 +2282,17 @@ function SendUnsentMessages()
 function CoalesceGetMsgsForPop3ServersByDestFolder(currentServer, pop3DownloadServersArray, localFoldersToDownloadTo)
 {
   var outNumFolders = new Object();
   var inboxFolder = currentServer.rootMsgFolder.getFolderWithFlags(0x1000); 
   // coalesce the servers that download into the same folder...
   var index = localFoldersToDownloadTo.GetIndexOf(inboxFolder);
   if (index == -1)
   {
-    if(inboxFolder) 
+    if (inboxFolder)
     {
       inboxFolder.biffState =  Components.interfaces.nsIMsgFolder.nsMsgBiffState_NoMail;
       inboxFolder.clearNewMessages();
     }
     localFoldersToDownloadTo.AppendElement(inboxFolder);
     index = pop3DownloadServersArray.length
     pop3DownloadServersArray[index] = Components.classes["@mozilla.org/supports-array;1"].createInstance(Components.interfaces.nsISupportsArray);
   }
--- a/mail/base/content/messenger.xul
+++ b/mail/base/content/messenger.xul
@@ -168,17 +168,19 @@
 <popup id="messageIdContext"/>
 
 <tooltip id="folderpopup" class="folderSummaryPopup"/>
 
 <popup id="messagePaneContext"/>
 
   <toolbox id="mail-toolbox" class="toolbox-top">
   </toolbox>
-  <tabmail id="tabmail" flex="1">
+  <tabmail id="tabmail" flex="1" panelcontainer="tabpanelcontainer">
+  <box id="tabmail-buttons" orientation="horizontal"/>
+  <tabpanels id="tabpanelcontainer" flex="1" class="plain" selectedIndex="0">
     <box id="mailContent" orient="vertical" flex="1">
     <box id="messengerBox" orient="horizontal" flex="1" minheight="100" height="100" persist="height">
     <vbox id="folderPaneBox" minwidth="100" width="200" persist="collapsed width">
       <label id="folderColumnLabel" hidden="true" value="&folderColumn.label;"/>
       <sidebarheader id="folderPaneHeader" align="center">
         <label id="folderpane-title"/>
         <spacer flex="1"/>
         <toolbarbutton id="folderview-cycler-left"  class="folderview-cycler"
@@ -426,17 +428,17 @@
           <splitter id="attachment-splitter" collapse="after" resizebefore="closest" resizeafter="closest" collapsed="true"/>
           <hbox id="attachmentView"/>
           <findbar id="FindToolbar" browserid="messagepane"/>
         </vbox>
       </box>
     </vbox>
   </box>
   </box> <!-- end of mailContent -->
-
+  </tabpanels>
     </tabmail>
 #ifdef TOOLBAR_CUSTOMIZATION_SHEET
   <panel id="customizeToolbarSheetPopup" noautohide="true">
     <iframe id="customizeToolbarSheetIFrame"
             style="&dialog.style;"
             hidden="true"/>
   </panel>
 #endif
--- a/mail/base/content/tabmail.xml
+++ b/mail/base/content/tabmail.xml
@@ -16,16 +16,17 @@
 #
 # The Initial Developer of the Original Code is
 #   David Bienvenu <bienvenu@nventure.com>.
 # Portions created by the Initial Developer are Copyright (C) 2007
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #  Scott MacGregor <mscott@mozilla.org>
+#  Andrew Sutherland <asutherland@asutherland.org>
 #
 # 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
@@ -45,196 +46,483 @@
  %globalDTD;
 ]>
 
 <bindings id="tabmailBindings"
    xmlns="http://www.mozilla.org/xbl"
    xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
    xmlns:xbl="http://www.mozilla.org/xbl">
 
+  <!-- Thunderbird's tab UI mechanism.
+    -
+    - We expect to be instantiated with the following children:
+    - * One "tabpanels" child element whose id must be placed in the
+    -   "panelcontainer" attribute on the element we are being bound to. We do
+    -   this because it is important to allow overlays to contribute panels.
+    -   When we attempted to have the immediate children of the bound element
+    -   be propagated through use of the "children" tag, we found that children
+    -   contributed by overlays did not propagate.
+    - * Any children you want added to the right side of the tab bar.  This is
+    -   primarily intended to allow for "open a BLANK tab" buttons, namely
+    -   calendar and tasks.  For reasons similar to the tabpanels case, we
+    -   expect the instantiating element to provide a child hbox for overlays
+    -   to contribute buttons to.
+    -
+    - From a javascript perspective, there are three types of code that we
+    -  expect to interact with:
+    - 1) Code that wants to open new tabs.
+    - 2) Code that wants to contribute one or more varieties of tabs.
+    - 3) Code that wants to monitor to know when the active tab changes.
+    -
+    - Consumer code should use the following methods:
+    - * openTab(aTabModeName, arguments...): Open a tab of the given "mode",
+    -   passing the provided arguments.  The tab type author should tell you
+    -   the modes they implement and the required/optional arguments.
+    - * setTabTitle([aOptionalTabNode]): Tells us that the title of the current
+    -   tab (if no argument is provided) or provided tab needs to be updated.
+    -   This will result in a call to the tab mode's logic to update the title.
+    -   In the event this is not for the current tab, the caller is responsible
+    -   for ensuring that the underlying tab mode is capable of providing a tab
+    -   title when it is in the background.  (The is currently not the case for
+    -   "folder" and "mail" modes because of their implementation.)
+    - * removeCurrentTab(): Close the current tab.
+    - * removeTabByNode(aTabElement): Close the tab whose tabmail-tab bound
+    -   element is passed in.
+    - Changing the currently displayed tab is accomplished by changing
+    -  tabmail.tabContainer's selectedIndex or selectedItem property.
+    -
+    - Tab contributing code should define a tab type object and register it
+    -  with us by calling registerTabType.  Each tab type can provide multiple
+    -  tab modes.  The rationale behind this organization is that Thunderbird
+    -  historically/currently uses a single 3-pane view to display both
+    -  three-pane folder browsing and single message browsing across multiple
+    -  tabs.  Each tab type has the ability to use a single tab panel for all
+    -  of its display needs.  So Thunderbird's "mail" tab type covers both the
+    -  "folder" (3-pane folder-based browsing)  and "message" (just a single
+    -  message) tab modes.  Likewise, calendar/lightning currently displays both
+    -  its calendar and tasks in the same panel.  A tab type can also create a
+    -  new tabpanel for each tab as it is created.  In that case, the tab type 
+    -  should probably only have a single mode unless there are a number of
+    -  similar modes that can gain from code sharing.
+    - The tab type definition should include the following attributes:
+    - * name: The name of the tab-type, mainly to aid in debugging.
+    - * panelId or perTabPanel: If using a single tab panel, the id of the
+    -     panel must be provided in panelId.  If using one tab panel per tab,
+    -     perTabPanel should be the XUL element name that should be created for
+    -     each tab.  If a reason arises, in the future we might support
+    -     this being a helper function to create and return the element.
+    - * modes: An object whose attributes are mode names (which are
+    -     automatically propagated to a 'name' attribute for debugging) and
+    -     values are objects with the following attributes...
+    - * any of the openTab/closeTab/saveTabState/showTab/onTitleChanged
+    -     functions as described on the mode definitions.  These will only be
+    -     called if the mode does not provide the functions.  Note that because
+    -     the 'this' variable passed to the functions will always reference the
+    -     tab type definition (rather than the mode definition), the mode
+    -     functions can defer to the tab type functions by calling 
+    -     this.functionName().  (This should prove convenient.)
+    - Mode definition attributes:
+    - * type: The "type" attribute to set on the displayed tab for CSS purposes.
+    -     Generally, this would be the same as the mode name, but you can do as
+    -     you please.
+    - * isDefault: This should only be present and should be true for the tab
+    -     mode that is the tab displayed automatically on startup.
+    - * maxTabs: The maximum number of this mode that can be opened at a time.
+    -     If this limit is reached, any additional calls to openTab for this
+    -     mode will simply result in the first existing tab of this mode being
+    -     displayed.
+    - * openTab(aTab, arguments...): Called when a tab of the given mode is
+    -     in the process of being opened.  aTab will have its "mode" attribute
+    -     set to the mode definition of the tab mode being opened.  You should
+    -     set the "title" attribute on it, and may set any other attributes
+    -     you wish for your own use in subsequent functions.  Note that 'this'
+    -     points to the tab type definition, not the mode definition as you
+    -     might expect.  This allows you to place common logic code on the
+    -     tab type for use by multiple modes and to defer to it.  Any arguments
+    -     provided to the caller of tabmail.openTab will be passed to your
+    -     function as well.
+    - * closeTab(aTab): Called when aTab is being closed.  The tab need not be
+    -     currently displayed.  You are responsible for properly cleaning up
+    -     any state you preserved in aTab.
+    - * saveTabState(aTab): Called when aTab is being switched away from so that
+    -     you can preserve its state on aTab.  This is primarily for single
+    -     tab panel implementations; you may not have much state to save if your
+    -     tab has its own tab panel.
+    - * showTab(aTab): Called when aTab is being displayed and you should
+    -     restore its state (if required).
+    - * onTitleChanged(aTab): Called when someone calls tabmail.setTabTitle() to
+    -     hint that the tab's title needs to be updated.  This function should
+    -     update aTab.title if it can.
+    -
+    - Tab monitoring code is expected to be used for widgets on the screen
+    -  outside of the tab box that need to update themselves as the active tab
+    -  changes.  This is primarily intended to be used for the ThunderBar; if
+    -  you are not the ThunderBar and this sounds appealing to you, please
+    -  solicit discussion on your needs on the mozilla.dev.apps.thunderbird
+    -  newsgroup.
+    - Tab monitoring code (un)registers itself via (un)registerTabMonitor.
+    -  The following functions should be provided on the monitor object:
+    - * onTabTitleChanged(aTab): Called when the tab's title changes.
+    - * onTabSwitched(aTab, aOldTab): Called when a new tab is made active.  If
+    -     this is the first tab ever, aOldTab will be null, otherwise aOldTab
+    -     will be the previously active tab.
+    -->
   <binding id="tabmail">
     <resources>
       <stylesheet src="chrome://messenger/skin/tabmail.css"/>
     </resources>
     <content>
       <xul:tabbox anonid="tabbox" flex="1" eventnode="document" xbl:inherits="handleCtrlPageUpDown"
-                  onselect="if (!('updateCurrentMailTab' in this.parentNode) || event.target.localName != 'tabs')
-                            return; this.parentNode.updateCurrentMailTab();">
+                  onselect="if (!('updateCurrentTab' in this.parentNode) || event.target.localName != 'tabs')
+                            return; this.parentNode.updateCurrentTab();">
         <xul:hbox class="tab-drop-indicator-bar">
           <xul:hbox class="tab-drop-indicator" mousethrough="always"/>
         </xul:hbox>
-        <xul:hbox class="tabmail-strip" collapsed="true" tooltip="_child" context="_child"
+        <xul:hbox class="tabmail-strip" collapsed="false" tooltip="_child" context="_child"
                   anonid="strip"
                   ondraggesture="nsDragAndDrop.startDrag(event, this.parentNode.parentNode); event.stopPropagation();"
                   ondragover="nsDragAndDrop.dragOver(event, this.parentNode.parentNode); event.stopPropagation();"
                   ondragdrop="nsDragAndDrop.drop(event, this.parentNode.parentNode); event.stopPropagation();"
                   ondragexit="nsDragAndDrop.dragExit(event, this.parentNode.parentNode); event.stopPropagation();">
           <xul:tooltip onpopupshowing="return CreateToolbarTooltip(document, event);"/>
           <xul:menupopup anonid="tabContextMenu">
             <xul:menuitem label="&closeTabCmd.label;" accesskey="&closeTabCmd.accesskey;"
                           oncommand="var tabmail = this.parentNode.parentNode.parentNode.parentNode;
-                                     tabmail.removeTab(document.popupNode);"/>
+                                     tabmail.removeTabByNode(document.popupNode);"/>
           </xul:menupopup>
           <xul:tabs class="tabmail-tabs" flex="1"
                     anonid="tabcontainer"
                     setfocus="false"
                     onclick="this.parentNode.parentNode.parentNode.onTabClick(event);">
             <xul:tab selected="true" validate="never" type="folder"
                      maxwidth="250" width="0" minwidth="100" flex="100"
-                     class="tabmail-tab tabmail-tab" crop="end"/>
+                     class="tabmail-tab" crop="end"/>
+            <children/>
           </xul:tabs>
         </xul:hbox>
-        <xul:tabpanels flex="1" class="plain" selectedIndex="0" anonid="panelcontainer">
-          <children/>
-        </xul:tabpanels>
+        <!-- Remember, user of this binding, you need to provide tabpanels!  -->
+        <children includes="tabpanels"/>
       </xul:tabbox>
     </content>
 
     <implementation>
-      <field name="currentTabOwner">
-        0;
+      <field name="currentTabInfo">
+        null
+      </field>
+      <field name="tabTypes" readonly="true">
+        new Object()
       </field>
-      <field name="tabOwners" readonly="true">
-        new Array();
+      <field name="tabModes" readonly="true">
+        new Object()
+      </field>
+      <field name="defaultTabMode">
+        null
+      </field>
+      <field name="tabInfo" readonly="true">
+        new Array()
       </field>
       <field name="tabStrip" readonly="true">
         document.getAnonymousElementByAttribute(this, "anonid", "strip");
       </field>
       <field name="tabContainer" readonly="true">
         document.getAnonymousElementByAttribute(this, "anonid", "tabcontainer");
       </field>
-      <method name="addTab">
-        <parameter name="aTabOwner"/>
-        <body>
-          <![CDATA[
-            var t = document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
+      <field name="panelContainer" readonly="true">
+        document.getElementById(this.getAttribute("panelcontainer"));
+      </field>
+      <field name="tabMonitors" readonly="true">
+        new Array()
+      </field>
+      <method name="registerTabType">
+        <parameter name="aTabType"/>
+        <body><![CDATA[
+          if (aTabType.name in this.tabTypes)
+            return;
+        
+          this.tabTypes[aTabType.name] = aTabType;
+          for (let [modeName, modeDetails] in Iterator(aTabType.modes)) {
+            modeDetails.name = modeName;
+            modeDetails.tabType = aTabType;
+            modeDetails.tabs = [];
+            this.tabModes[modeName] = modeDetails;
+            if (modeDetails.isDefault)
+              this.defaultTabMode = modeDetails;
+          }
+          
+          aTabType.panel = document.getElementById(aTabType.panelId);
+        ]]></body>
+      </method>
+      <method name="registerTabMonitor">
+        <parameter name="aTabMonitor"/>
+        <body><![CDATA[
+          if (this.tabMonitors.indexOf(aTabMonitor) == -1)
+            this.tabMonitors.push(aTabMonitor);
+        ]]></body>
+      </method>
+      <method name="unregisterTabMonitor">
+        <parameter name="aTabMonitor"/>
+        <body><![CDATA[
+          if (this.tabMonitors.indexOf(aTabMonitor) >= 0)
+            this.tabMonitors.splice(this.tabMonitors.indexOf(aTabMonitor), 1);
+        ]]></body>
+      </method>
+      <method name="openFirstTab">
+        <body><![CDATA[
+          // From the moment of creation, our XBL binding already has a visible
+          //  tab.  We need to create a tab information structure for this tab.
+          //  In the process we also generate a synthetic tab title changed
+          //  event to ensure we have an accurate title.  We assume the tab
+          //  contents will set themselves up correctly.
+          if (this.tabInfo.length == 0) {
+            let firstTab = {mode: this.defaultTabMode, canClose: false};
+            let firstTabNode = this.tabContainer.firstChild;
+            firstTab.mode.tabs.push(firstTab);
+           
+            this.tabInfo[0] = this.currentTabInfo = firstTab;
+            this.setTabTitle(firstTabNode);
+
+            if (this.tabMonitors.length) {
+              for each (let [i, tabMonitor] in Iterator(this.tabMonitors))
+                tabMonitor.onTabSwitched(tab, null);
+            }
+          }
+        ]]></body>
+      </method>
+      <method name="openTab">
+        <parameter name="aTabModeName"/>
+        <body><![CDATA[
+          let tabMode = this.tabModes[aTabModeName];
+          // if we are already at our limit for this mode, show an existing one
+          if (tabMode.tabs.length == tabMode.maxTabs) {
+            let desiredTab = tabMode.tabs[0];
+            this.tabContainer.selectedIndex = this.tabInfo.indexOf(desiredTab);
+            return;
+          }
+          
+          // we need to save the state before it gets corrupted
+          this.saveCurrentTabState();
+
+          let tab = {mode: tabMode, canClose: true};
+          tabMode.tabs.push(tab);
+
+          var t = document.createElementNS(
+            "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
                                              "tab");
             t.setAttribute("crop", "end");
-            t.maxWidth = 250;
-            //    t.minWidth = this.tabContainer.mTabMinWidth;
+          t.maxWidth = this.tabContainer.mTabMaxWidth;
+          t.minWidth = this.tabContainer.mTabMinWidth;
             t.width = 0;
             t.setAttribute("flex", "100");
             t.setAttribute("validate", "never");
-            t.className = "tabmail-tab tabmail-tab";
-            if (!this.tabOwners.length)
-            {
-              // set up the first tab, which was previously invisible.
-              this.tabOwners[0] = new folderTabOwner();
-              this.setTabTitle(this.tabContainer.firstChild);
-            }
+          t.className = "tabmail-tab";
             this.tabContainer.appendChild(t);
             if (this.tabStrip.collapsed)
             {
               this.tabStrip.collapsed = false;
               this.tabStrip.setAttribute("closebuttons", "alltabs");
             }
-            this.saveCurrentTabInfo(); // save off the state of the old tab
+          
+          let oldTab = this.currentTabInfo;
 
             // the order of the following statements is important
-            this.currentTabOwner = aTabOwner;
-            this.tabOwners[this.tabContainer.childNodes.length - 1] = aTabOwner;
-            this.tabContainer.selectedIndex = this.tabContainer.childNodes.length - 1; // this has a side effect of calling updateCurrentMailTab
-            this.setTabTitle(t);
+          this.currentTabInfo = tab;
+          this.tabInfo[this.tabContainer.childNodes.length - 1] = tab;
+          // this has a side effect of calling updateCurrentTab, but our
+          //  setting currentTabInfo above will cause it to take no action.
+          this.tabContainer.selectedIndex =
+            this.tabContainer.childNodes.length - 1;
+
+          // make sure we are on the right panel
+          if (tab.mode.tabType.perTabPanel) {
+            // should we create the element for them, or will they do it?
+            if (typeof(tab.mode.tabType.perTabPanel) == "string") {
+              tab.panel = document.createElementNS(
+                "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
+                tab.mode.tabType.perTabPanel);
+            }
+            else {
+              tab.panel = tab.mode.tabType.perTabPanel(tab);
+            }
+            this.panelContainer.appendChild(tab.panel);
+            this.panelContainer.selectedPanel = tab.panel;
+          }
+          else
+            this.panelContainer.selectedPanel = tab.mode.tabType.panel;
+            
+          let tabOpenFunc = tab.mode.openTab || tab.mode.tabType.openTab;
+          let args = [tab].concat(Array.prototype.slice.call(arguments, 1));
+          tabOpenFunc.apply(tab.mode.tabType, args);
+          
+          if (this.tabMonitors.length) {
+            for each (let [i, tabMonitor] in Iterator(this.tabMonitors))
+              tabMonitor.onTabSwitched(tab, oldTab);
+          }
+          
+          t.setAttribute("label", tab.title);
+          
+          let docTitle = tab.title;
+#ifndef XP_MACOSX
+          docTitle += " - " + gBrandBundle.getString("brandFullName");
+#endif
+          document.title = docTitle;
+          
             // for styling purposes, apply the type to the tab...
-            t.setAttribute('type', aTabOwner.type);
-            this.currentTabOwner.open();
-          ]]>
-        </body>
+          t.setAttribute('type', tab.mode.type);
+        ]]></body>
+      </method>
+      <method name="selectTabByMode">
+        <parameter name="aTabModeName"/>
+        <body><![CDATA[
+          let tabMode = this.tabModes[aTabModeName];
+          if (tabMode.tabs.length) {
+            let desiredTab = tabMode.tabs[0];
+            this.tabContainer.selectedIndex = this.tabInfo.indexOf(desiredTab);
+          }
+        ]]></body>
       </method>
       <method name="closeTabs">
         <body>
           <![CDATA[
-            for (var i = 0; i < this.tabOwners.length; i++)
-              this.tabOwners[i].close();
+            for (var i = 0; i < this.tabInfo.length; i++) {
+              let tab = this.tabInfo[i];
+              
+              let tabCloseFunc = tab.mode.closeTab || tab.mode.tabType.closeTab;
+              tabCloseFunc.call(tab.mode.tabType, tab);
+            }
           ]]>
         </body>
       </method>
-      <method name="removeTab">
-        <parameter name="aTab"/>
+      <method name="removeTabByNode">
+        <parameter name="aTabNode"/>
         <body>
           <![CDATA[
-            var numTabs = this.tabContainer.childNodes.length;
-            if (numTabs < 3)
-            {
-              // hide the tab bar
-              this.tabStrip.collapsed = true;
-              if (numTabs == 1) // can this happen?
-                return;
-            }
-            var i;
             // Find and locate the tab in our list.
-            for (i = 0; i < numTabs; i++)
-              if (this.tabContainer.childNodes[i] == aTab)
+            let iTab, numTabs = this.tabContainer.childNodes.length;
+            for (iTab = 0; iTab < numTabs; iTab++)
+              if (this.tabContainer.childNodes[iTab] == aTabNode)
                 break;
-            var tabOwner = this.tabOwners[i];
-            tabOwner.close(); // inform the owner the tab is being closed
-            this.tabOwners.splice(i, 1);
-            this.tabContainer.removeChild(aTab);
+            let tab = this.tabInfo[iTab];
+
+            if (!tab.canClose)
+              return;
+            
+            let closeFunc = tab.mode.closeTab || tab.mode.tabType.closeTab;
+            closeFunc.call(tab.mode.tabType, tab);
+             
+            this.tabInfo.splice(iTab, 1);
+            tab.mode.tabs.splice(tab.mode.tabs.indexOf(tab), 1);
+            this.tabContainer.removeChild(aTabNode);
             if (this.tabContainer.selectedIndex == -1)
-              this.tabContainer.selectedIndex = (i == --numTabs) ? i - 1 : i;
-            if (this.currentTabOwner == tabOwner)
-              this.updateCurrentMailTab();
-          ]]>
-        </body>
-      </method>
-      <!--  UpdateCurrentMailTab - called in response to changing the current tab -->
-      <method name="updateCurrentMailTab">
-        <body>
-          <![CDATA[
-            if (this.currentTabOwner != this.tabOwners[this.tabContainer.selectedIndex])
-            {
-              this.saveCurrentTabInfo(); // save the old tab state before we change the current tab
-              // if this isn't set, then this is the first time we've switched tabs, so the
-              // old tab must be the 0th tab.
-              // the tab owner is responsible for actually setting up the UI for the tab.
-              var oldTabOwner = this.currentTabOwner;
-              this.currentTabOwner = this.tabOwners[this.tabContainer.selectedIndex];
-              this.currentTabOwner.onSelect(oldTabOwner);
+              this.tabContainer.selectedIndex = (iTab == --numTabs) ?
+                iTab - 1 : iTab;
+            if (this.currentTabInfo == tab)
+              this.updateCurrentTab();
+              
+            if (tab.panel) {
+              this.panelContainer.removeChild(tab.panel);
+              delete tab.panel;
             }
           ]]>
         </body>
       </method>
-      <method name="saveCurrentTabInfo">
+      <method name="removeCurrentTab">
+        <body><![CDATA[
+          this.removeTabByNode(
+            this.tabContainer.childNodes[this.tabContainer.selectedIndex]);
+        ]]></body>
+      </method>
+      <!--  UpdateCurrentTab - called in response to changing the current tab -->
+      <method name="updateCurrentTab">
         <body>
           <![CDATA[
-            if (!this.currentTabOwner)
-              this.currentTabOwner = this.tabOwners[0];
-            this.currentTabOwner.saveCurrentInfo();
+            if (this.currentTabInfo != this.tabInfo[this.tabContainer.selectedIndex])
+            {
+              if (this.currentTabInfo)
+                this.saveCurrentTabState();
+              
+              let oldTab = this.currentTabInfo;
+              let tab = this.currentTabInfo =
+                this.tabInfo[this.tabContainer.selectedIndex];
+
+              this.panelContainer.selectedPanel = tab.panel ||
+                                                  tab.mode.tabType.panel;
+
+              let showTabFunc = tab.mode.showTab || tab.mode.tabType.showTab;
+              showTabFunc.call(tab.mode.tabType, tab);
+
+              if (this.tabMonitors.length) {
+                for each (let [i, tabMonitor] in Iterator(this.tabMonitors))
+                  tabMonitor.onTabSwitched(tab, oldTab);
+              }
+
+
+              let docTitle = tab.title;
+#ifndef XP_MACOSX
+              docTitle += " - " + gBrandBundle.getString("brandFullName");
+#endif
+              document.title = docTitle;
+            }
+          ]]>
+        </body>
+      </method>
+      <method name="saveCurrentTabState">
+        <body>
+          <![CDATA[
+            if (!this.currentTabInfo)
+              this.currentTabInfo = this.tabInfo[0];
+            let tab = this.currentTabInfo;
+            
+            // save the old tab state before we change the current tab
+            let saveTabFunc = tab.mode.saveTabState ||
+                              tab.mode.tabType.saveTabState;
+            saveTabFunc.call(tab.mode.tabType, tab);
           ]]>
         </body>
       </method>
       <method name="onTabClick">
         <parameter name="event"/>
         <body>
           <![CDATA[
             // a middle mouse button click on a tab is a short cut for closing a tab
             if (event.button != 1 || event.target.localName != 'tab')
               return;
-            this.removeTab(event.target);
+            this.removeTabByNode(event.target);
             event.stopPropagation();
           ]]>
         </body>
       </method>
       <method name="setTabTitle">
-        <parameter name="aTab"/>
+        <parameter name="aTabNode"/>
         <body>
           <![CDATA[
-            if (!aTab)
-              aTab = this.tabContainer.childNodes[this.tabContainer.selectedIndex];
+            if (!aTabNode)
+              aTabNode =
+                this.tabContainer.childNodes[this.tabContainer.selectedIndex];
+                
             // get the owner for the tab...
             var i;
             var numTabs = this.tabContainer.childNodes.length;
             for (i = 0; i < numTabs; i++)
-              if (this.tabContainer.childNodes[i] == aTab)
+              if (this.tabContainer.childNodes[i] == aTabNode)
                 break;
             // on startup, we may not have a tab...
-            if (this.tabOwners[i])
+            let tab = this.tabInfo[i];
+            if (tab)
             {
-              aTab.setAttribute("label", this.tabOwners[i].title);
-              this.tabOwners[i].onTitleChanged(aTab);
+              let titleChangeFunc = tab.mode.onTitleChanged ||
+                                    tab.mode.tabType.onTitleChanged;
+              if (titleChangeFunc)
+                titleChangeFunc.call(tab.mode.tabType, tab, aTabNode);
+
+              if (this.tabMonitors.length) {
+                for each (let [i, tabMonitor] in Iterator(this.tabMonitors))
+                  tabMonitor.onTabTitleChanged(tab);
+              }
+                                    
+              aTabNode.setAttribute("label", tab.title);
             }
           ]]>
         </body>
       </method>
     </implementation>
   </binding>
   
  <binding id="tabmail-tab" display="xul:box"
@@ -358,16 +646,17 @@
 
   <binding id="tabmail-tabs"
            extends="chrome://global/content/bindings/tabbox.xml#tabs">
     <content>
       <xul:arrowscrollbox anonid="arrowscrollbox" class="tabmail-arrowscrollbox" flex="1"
                           xbl:inherits="smoothscroll" orient="horizontal" style="min-width: 1px;">
         <children includes="tab"/>
       </xul:arrowscrollbox>
+      <children/>
       <xul:stack align="center" pack="end" class="tabs-alltabs-stack">
         <xul:hbox flex="1" class="tabs-alltabs-box" anonid="alltabs-box"/>
         <xul:hbox flex="1" class="tabs-alltabs-box-animate" 
                   anonid="alltabs-box-animate"/>
         <xul:toolbarbutton class="tabs-alltabs-button" type="menu"
                            anonid="alltabs-button"
                            tooltipstring="&listAllTabs.label;">
           <xul:menupopup class="tabs-alltabs-popup"
@@ -896,17 +1185,17 @@
              * on an in-tab close button is when the tab was opened with a
              * double click on the tabbar. (bug 378344)
              * In both cases, it is most likely that the close button area has
              * been accidentally clicked, therefore we do not close the tab.
              */
             if (event.detail > 1)
               return;
 
-            tabbedBrowser.removeTab(bindingParent);
+            tabbedBrowser.removeTabByNode(bindingParent);
             tabbedBrowser._blockDblClick = true;
 
             /* XXXmano hack (see bug 343628):
              * Since we're removing the event target, if the user
              * double-clicks this button, the dblclick event will be dispatched
              * with the tabbar as its event target (and explicit/originalTarget),
              * which treats that as a mouse gesture for opening a new tab.
              * In this context, we're manually blocking the dblclick event
--- a/mail/themes/pinstripe/mail/tabmailBindings.xml
+++ b/mail/themes/pinstripe/mail/tabmailBindings.xml
@@ -72,18 +72,19 @@
           <xul:hbox class="tabs-bottom" align="center"/>
         </xul:vbox>
         <xul:vbox>
           <xul:hbox>
             <xul:stack>
               <xul:spacer class="tabs-left"/>
             </xul:stack>
             <xul:arrowscrollbox anonid="arrowscrollbox" orient="horizontal" flex="1" style="min-width: 1px;" class="tabmail-arrowscrollbox">
-              <children/>
+              <children includes="tab"/>
             </xul:arrowscrollbox>
+            <children/>
             <xul:stack align="center" pack="end">
               <xul:hbox flex="1" class="tabs-alltabs-box" 
                         anonid="alltabs-box"/>
               <xul:hbox flex="1" class="tabs-alltabs-box-animate" 
                         anonid="alltabs-box-animate"/>
               <xul:toolbarbutton class="tabs-alltabs-button"
                                  type="menu"
                                  anonid="alltabs-button"
--- a/mail/themes/qute/mail/tabmailBindings.xml
+++ b/mail/themes/qute/mail/tabmailBindings.xml
@@ -77,18 +77,19 @@
               <xul:spacer class="tabs-left"/>
             </xul:stack>
             <xul:arrowscrollbox anonid="arrowscrollbox"
                                 orient="horizontal"
                                 flex="1"
                                 style="min-width: 1px;"
                                 chromedir="&locale.dir;"
                                 class="tabmail-arrowscrollbox">
-              <children/>
+              <children includes="tab"/>
             </xul:arrowscrollbox>
+            <children/>
             <xul:stack align="center" pack="end" chromedir="&locale.dir;">
               <xul:hbox flex="1"
                         class="tabs-alltabs-box"
                         anonid="alltabs-box"/>
               <xul:hbox flex="1" class="tabs-alltabs-box-animate"
                         anonid="alltabs-box-animate"/>
               <xul:toolbarbutton class="tabs-alltabs-button" type="menu"
                                  anonid="alltabs-button"