[Pkg-mozext-commits] [tabmixplus] 01/05: Imported Upstream version 0.4.1.2~130918a

David Prévot taffit at alioth.debian.org
Wed Sep 18 19:57:51 UTC 2013


This is an automated email from the git hooks/post-receive script.

taffit pushed a commit to branch master
in repository tabmixplus.

commit dc250c13b3fb670e726d14af387f8783807defa7
Author: David Prévot <taffit at debian.org>
Date:   Wed Sep 18 14:42:33 2013 -0400

    Imported Upstream version 0.4.1.2~130918a
---
 chrome/content/changecode.js                       |  231 ++++++++++++--------
 chrome/content/click/click.js                      |   15 +-
 chrome/content/extensions/extensions.js            |    4 +-
 chrome/content/links/contentLinks.js               |   26 ++-
 chrome/content/links/newTab.xul                    |   24 +-
 chrome/content/links/setup.js                      |  103 ++++-----
 chrome/content/minit/minit.js                      |   11 +-
 chrome/content/minit/tabView.js                    |    6 -
 chrome/content/minit/tablib.js                     |  115 ++++++----
 chrome/content/overlay/browsr.css                  |   59 +++--
 chrome/content/places/places.js                    |  178 +++------------
 chrome/content/preferences/appearance.js           |   12 +-
 chrome/content/preferences/appearance.xul          |    2 +-
 chrome/content/preferences/mouse.js                |    5 +
 chrome/content/preferences/mouse.xul               |   28 ++-
 chrome/content/preferences/session.js              |    2 +
 chrome/content/preferences/shortcuts.xml           |    2 +-
 chrome/content/session/session.js                  |   81 +++++--
 chrome/content/session/sessionStore.js             |   27 ++-
 chrome/content/tab/scrollbox.xml                   |    2 +-
 chrome/content/tab/tab.js                          |  192 ++++++++++------
 chrome/content/tab/tabbrowser_4.xml                |   89 +++++---
 chrome/content/tabmix.js                           |  203 ++++++++++++-----
 chrome/content/utils.js                            |   63 +++---
 chrome/skin/app_version/4.0/linux/browser.css      |    2 +-
 chrome/skin/app_version/4.0/mac/browser.css        |    2 +-
 chrome/skin/app_version/4.0/win/browser.css        |    7 +-
 chrome/skin/tab.css                                |    8 +-
 defaults/preferences/tabmix.js                     |    6 +
 install.rdf                                        |    4 +-
 modules/AutoReload.jsm                             |    3 +-
 modules/MergeWindows.jsm                           |    3 +-
 modules/Places.jsm                                 |  173 +++++++++++++++
 modules/RenameTab.jsm                              |    6 +-
 modules/Services.jsm                               |   38 +++-
 modules/Shortcuts.jsm                              |    7 +-
 .../{SessionManagerAddon.jsm => AddonManager.jsm}  |   97 +++++---
 modules/extensions/TabGroupsManager.jsm            |    7 +-
 modules/log.jsm                                    |  146 +++++++++----
 39 files changed, 1244 insertions(+), 745 deletions(-)

diff --git a/chrome/content/changecode.js b/chrome/content/changecode.js
index 9979388..edda63c 100644
--- a/chrome/content/changecode.js
+++ b/chrome/content/changecode.js
@@ -1,112 +1,157 @@
+Tabmix._eval = function(name, code) name ? eval(name + " = " + code) : eval("(" + code + ")");
+// Tabmix._eval is on the first line to help us get the right line number
+
 // don't use strict for this file
 // so we don't evaluat all code as strict mode code
 
-function Tabmix_ChangeCode(aParams) {
-  this.obj = aParams.obj;
-  this.fnName = aParams.fnName;
-  this.fullName = aParams.fullName;
+// aOptions can be: getter, setter or forceUpdate
+Tabmix.changeCode = function(aParent, aName, aOptions) {
+  let console = TabmixSvc.console;
+  let debugMode = Tabmix._debugMode;
+
+  function ChangeCode(aParams) {
+    this.obj = aParams.obj;
+    this.fnName = aParams.fnName;
+    this.fullName = aParams.fullName;
 
-  let options = aParams.options;
-  this.needUpdate = options && options.forceUpdate || false;
+    let options = aParams.options;
+    this.needUpdate = options && options.forceUpdate || false;
 
-  if (options && (options.setter || options.getter)) {
-    let type = options.setter ? "__lookupSetter__" : "__lookupGetter__";
-    this.value = this.obj[type](this.fnName).toString();
+    if (options && (options.setter || options.getter)) {
+      this.type = options.setter ? "__lookupSetter__" : "__lookupGetter__";
+      this.value = this.obj[this.type](this.fnName).toString();
+    }
+    else if (typeof this.obj[this.fnName] == "function")
+      this.value = this.obj[this.fnName].toString();
+    else
+      this.errMsg = "\n" + this.fullName + " is undefined.";
+    this.notFound = [];
   }
-  else if (typeof this.obj[this.fnName] == "function")
-    this.value = this.obj[this.fnName].toString();
-  else
-    this.errMsg = "\n" + this.fullName + " is undefined.";
-  this.notFound = [];
-}
 
-Tabmix_ChangeCode.prototype = {
-  value: "", errMsg: "",
-  _replace: function TMP_utils__replace(substr ,newString, aParams) {
-    var silent;
-    if (typeof aParams != "undefined") {
-      let doReplace, flags;
-      if (typeof aParams == "object") {
-        doReplace = "check" in aParams ? aParams.check : true;
-        flags = aParams.flags;
-        silent = aParams.silent
+  ChangeCode.prototype = {
+    value: "", errMsg: "",
+    _replace: function TMP_utils__replace(substr ,newString, aParams) {
+      var silent;
+      if (typeof aParams != "undefined") {
+        let doReplace, flags;
+        if (typeof aParams == "object") {
+          doReplace = "check" in aParams ? aParams.check : true;
+          flags = aParams.flags;
+          silent = aParams.silent
+        }
+        else if (typeof aParams == "boolean") {
+          doReplace = aParams;
+        }
+        if (!doReplace)
+          return this;
+        if (flags && typeof substr == "string")
+          substr = new RegExp(substr.replace(/[{[(\\^.$|?*+\/)\]}]/g, "\\$&"), flags);
       }
-      else if (typeof aParams == "boolean") {
-        doReplace = aParams;
+
+      var exist = typeof(substr) == "string" ? this.value.indexOf(substr) > -1 : substr.test(this.value);
+      if (exist) {
+        this.value = this.value.replace(substr, newString);
+        this.needUpdate = true;
       }
-      if (!doReplace)
-        return this;
-      if (flags && typeof substr == "string")
-        substr = new RegExp(substr.replace(/[{[(\\^.$|?*+\/)\]}]/g, "\\$&"), flags);
-    }
+      else if (!silent)
+        this.notFound.push(substr);
+      return this;
+    },
 
-    var exist = typeof(substr) == "string" ? this.value.indexOf(substr) > -1 : substr.test(this.value);
-    if (exist) {
-      this.value = this.value.replace(substr, newString);
-      this.needUpdate = true;
-    }
-    else if (!silent)
-      this.notFound.push(substr);
-    return this;
-  },
-
-  toCode: function TMP_utils_toCode(aShow, aObj, aName) {
-    try {
-      if (Tabmix._debugMode) {
-        this.value = this.value.replace("{", "{try {") +
-            ' catch (ex) {Tabmix.assert(ex, "outer try-catch in ' + (aName || this.fullName) + '");}}';
+    toCode: function TMP_utils_toCode(aShow, aObj, aName) {
+      try {
+        // list of function that we don't warp with try-catch
+        let dontDebug = ["gBrowser.tabContainer._animateTabMove"];
+        if (debugMode && dontDebug.indexOf(this.fullName) == -1) {
+          let excludeReturn = ["TabsInTitlebar._update", "gBrowser._blurTab"];
+          let addReturn = "", re = new RegExp("//.*", "g");
+          if (excludeReturn.indexOf(this.fullName) == -1 &&
+              /return\s.+/.test(this.value.replace(re, "")))
+            addReturn = "\nreturn null\n";
+          this.value = this.value.replace("{", "{try {") +
+            ' catch (ex) {' +
+            '   TabmixSvc.console.assert(ex, "outer try-catch in ' + (aName || this.fullName) + '");}' +
+            addReturn +
+            ' }';
+        }
+        let [obj, fnName] = [aObj || this.obj, aName || this.fnName];
+        if (this.isValidToChange(fnName)) {
+          if (obj)
+            Tabmix.setNewFunction(obj, fnName, Tabmix._eval(null, this.value));
+          else
+            Tabmix._eval(fnName, this.value);
+        }
+        if (aShow)
+          this.show(obj, fnName);
+      } catch (ex) {
+        Components.utils.reportError("Tabmix " + console.callerName() + " failed to change " + this.fullName + "\nError: " + ex.message);
       }
+    },
+
+    defineProperty: function(aObj, aName, aCode) {
+      if (!this.type)
+        throw "Tabmix:\n" +  this.fullName + " don't have setter or getter";
+
       let [obj, fnName] = [aObj || this.obj, aName || this.fnName];
-      if (this.isValidToChange(fnName))
-        Tabmix.toCode(obj, fnName, this.value);
-      if (aShow)
-        this.show(obj, fnName);
-    } catch (ex) {
-      Components.utils.reportError("Tabmix " + Tabmix.callerName() + " failed to change " + this.fullName + "\nError: " + ex.message);
-    }
-  },
-
-  show: function(aObj, aName) {
-    if (aObj && aName in aObj)
-      Tabmix.show({obj: aObj, name: aName, fullName: this.fullName});
-    else if (this.fullName != null)
-      Tabmix.show(this.fullName);
-  },
-
-  isValidToChange: function(aName) {
-    var notFoundCount = this.notFound.length;
-    if (this.needUpdate && !notFoundCount)
-      return true;
-    var caller = Tabmix._getCallerNameByIndex(2);
-    if (notFoundCount) {
-      let str = (notFoundCount > 1 ? "s" : "") + "\n    ";
-      Tabmix.clog(caller + " was unable to change " + aName + "."
-        + (this.errMsg || "\ncan't find string" + str + this.notFound.join("\n    "))
-        + "\n\nTry Tabmix latest development version from tmp.garyr.net/tab_mix_plus-dev-build.xpi,"
-        + "\nReport about this to Tabmix developer at http://tmp.garyr.net/forum/");
-      if (Tabmix._debugMode)
-        Tabmix.clog(caller + "\nfunction " + aName + " = " + this.value);
+      let descriptor = {enumerable: true, configurable: true}
+
+      let setDescriptor = function(type) {
+        let fnType = "__lookup#ter__".replace("#", type);
+        type = type.toLowerCase();
+        let code = aCode && aCode[type + "ter"] ||
+                   this.type == fnType ? this.value : obj[fnType](fnName);
+
+        if (typeof code == "string")
+          descriptor[type] = Tabmix._eval(null, code);
+        else if (typeof code != "undefined")
+          descriptor[type] = code;
+      }.bind(this);
+
+      setDescriptor("Get");
+      setDescriptor("Set");
+
+      Object.defineProperty(obj, fnName, descriptor);
+    },
+
+    show: function(aObj, aName) {
+      if (aObj && aName in aObj)
+        console.show({obj: aObj, name: aName, fullName: this.fullName});
+      else if (this.fullName != null) {
+        let win = typeof window != "undefined" ? window : undefined;
+        console.show(this.fullName, 500, win);
+      }
+    },
+
+    isValidToChange: function(aName) {
+      var notFoundCount = this.notFound.length;
+      if (this.needUpdate && !notFoundCount)
+        return true;
+      var caller = console.getCallerNameByIndex(2);
+      if (notFoundCount) {
+        let str = (notFoundCount > 1 ? "s" : "") + "\n    ";
+        console.clog(caller + " was unable to change " + aName + "."
+          + (this.errMsg || "\ncan't find string" + str + this.notFound.join("\n    "))
+          + "\n\nTry Tabmix latest development version from tmp.garyr.net/tab_mix_plus-dev-build.xpi,"
+          + "\nReport about this to Tabmix developer at http://tmp.garyr.net/forum/");
+        if (debugMode)
+          console.clog(caller + "\nfunction " + aName + " = " + this.value);
+      }
+      else if (!this.needUpdate && debugMode)
+        console.clog(caller + " no update needed to " + aName);
+      return false;
     }
-    else if (!this.needUpdate && Tabmix._debugMode)
-      Tabmix.clog(caller + " no update needed to " + aName);
-    return false;
   }
-}
 
-Tabmix.defineProperty = function(aObj, aName, aCode) {
-  for (let [type, val] in Iterator(aCode)) {
-    if (typeof val == "string")
-      aCode[type] = eval("(" + val + ")");
+  let fnName = aName.split(".").pop();
+  try {
+    return new ChangeCode({obj: aParent, fnName: fnName,
+      fullName: aName, options: aOptions});
+  } catch (ex) {
+    console.clog(console.callerName() + " failed to change " + aName + "\nError: " + ex.message);
+    if (debugMode)
+      console.obj(aObject, "aObject");
   }
-  Object.defineProperty(aObj, aName, {get: aCode.getter, set: aCode.setter,
-                        enumerable: true, configurable: true});
-}
-
-Tabmix.toCode = function(aObj, aName, aCodeString) {
-  if (aObj)
-    this.setNewFunction(aObj, aName, eval("(" + aCodeString + ")"));
-  else
-    eval(aName + " = " + aCodeString);
+  return null;
 }
 
 Tabmix.setNewFunction = function(aObj, aName, aCode) {
diff --git a/chrome/content/click/click.js b/chrome/content/click/click.js
index 44f2655..c54e9af 100644
--- a/chrome/content/click/click.js
+++ b/chrome/content/click/click.js
@@ -20,7 +20,7 @@ var TabmixTabClickOptions = {
     var target = aEvent.originalTarget;
     var anonid = target.getAttribute("anonid");
     this._blockDblClick = aEvent.button == 0 && anonid == "tmp-close-button" ||
-                              target == gBrowser.tabContainer.mTabsNewtabButton;
+                              target.classList.contains("tabs-newtab-button");
 
     // don't do anything if user left click on close tab button , or on any other button on tab or tabbar
     if (aEvent.button == 0 && (anonid == "tmp-close-button" || target.localName == "toolbarbutton"))
@@ -290,7 +290,7 @@ var TabmixContext = {
       this._closeRightTabs = "context_closeTabsToTheEnd";
     }
 
-    if (Tabmix.isVersion(250)) {
+    if (Tabmix._restoreMultipleTabs) {
       let multipletablabel = $id("context_undoCloseTab").getAttribute("multipletablabel")
       let undoCloseTabMenu = $id("tm-content-undoCloseTab");
       undoCloseTabMenu.setAttribute("singletablabel", undoCloseTabMenu.label);
@@ -575,9 +575,11 @@ var TabmixContext = {
       var undoClose = Tabmix.prefs.getBoolPref("undoClose");
       Tabmix.showItem(undoCloseTabMenu, !contentClick && !gContextMenu.isTextSelected && undoClose && !closeTabsEmpty &&
                      Tabmix.prefs.getBoolPref("undoCloseTabContent"));
-      let closedTabCount = Tabmix.isVersion(250) ? TabmixSvc.ss.getNumberOfTabsClosedLast(window) : 1;
-      let visibleLabel = closedTabCount <= 1 ? "singletablabel" : "multipletablabel";
-      undoCloseTabMenu.setAttribute("label", undoCloseTabMenu.getAttribute(visibleLabel));
+      if (Tabmix._restoreMultipleTabs) {
+        let closedTabCount = TabmixSvc.ss.getNumberOfTabsClosedLast(window);
+        let visibleLabel = closedTabCount <= 1 ? "singletablabel" : "multipletablabel";
+        undoCloseTabMenu.setAttribute("label", undoCloseTabMenu.getAttribute(visibleLabel));
+      }
 
       var undoCloseListMenu = document.getElementById("tm-content-undoCloseList");
       Tabmix.showItem(undoCloseListMenu, !contentClick && !gContextMenu.isTextSelected && undoClose && !closeTabsEmpty &&
@@ -674,7 +676,8 @@ var TabmixContext = {
       if (links_urlSecurityCheck(url)) {
         if (check)
           return false;
-        urls.push(url);
+        if (urls.indexOf(url) == -1)
+          urls.push(url);
       }
       nextEpisode = treeWalker.nextNode();
     }
diff --git a/chrome/content/extensions/extensions.js b/chrome/content/extensions/extensions.js
index cea15a5..d74d3f0 100644
--- a/chrome/content/extensions/extensions.js
+++ b/chrome/content/extensions/extensions.js
@@ -280,14 +280,14 @@ var TMP_extensionsCompatibility = {
       ).toCode();
     }
 
-    // fix bug in superDargandGo
+    // fix bug in superDargandGo https://addons.mozilla.org/he/firefox/addon/super-dragandgo/
     try {
       if ("superDrag" in window && "contentAreaDNDObserver" in window) {
         Tabmix.changeCode(contentAreaDNDObserver, "contentAreaDNDObserver.onDrop")._replace(
           'document.firstChild.getAttribute("windowtype")',
           'window.document.documentElement.getAttribute("windowtype")'
         )._replace(
-          'preventBubble()',
+          'preventBubble()', /* fix bug in superDargandGo */
           'stopPropagation()'
         ).toCode();
       }
diff --git a/chrome/content/links/contentLinks.js b/chrome/content/links/contentLinks.js
index f8fe997..13de2ad 100644
--- a/chrome/content/links/contentLinks.js
+++ b/chrome/content/links/contentLinks.js
@@ -154,6 +154,11 @@ Tabmix.contentAreaClick = {
         return ["current", true];
     }
 
+    // check this after we check for suppressTabsOnFileDownload
+    // for the case the link have a matche in our list
+    if (typeof event.tabmix_openLinkWithHistory == "boolean")
+      return ["current"];
+
     // don't mess with links that have onclick inside iFrame
     let onClickInFrame = this._data.onclick && linkNode.ownerDocument.defaultView.frameElement;
 
@@ -209,6 +214,9 @@ Tabmix.contentAreaClick = {
   },
 
   contentLinkClick: function TMP_contentLinkClick(aEvent) {
+    if (typeof aEvent.tabmix_openLinkWithHistory == "boolean")
+      return;
+
     if (aEvent.button != 0 || aEvent.shiftKey || aEvent.ctrlKey || aEvent.altKey || aEvent.metaKey)
       return;
 
@@ -246,17 +254,15 @@ Tabmix.contentAreaClick = {
     if (isGmail)
       return;
 
-    // don't interrupt with noscript
-    if ("className" in linkNode && linkNode.className.indexOf("__noscriptPlaceholder__") > -1)
-      return;
-
-    // fix donwload button on page - http://get.adobe.com/reader/
-    if ("className" in linkNode && /download.button/.test(linkNode.className))
-      return;
+    if ("className" in linkNode) {
+      // don't interrupt with noscript
+      if (linkNode.className.indexOf("__noscriptPlaceholder__") > -1)
+        return;
 
-    // need to find a way to work here only on links
-    if ("className" in linkNode && /button/.test(linkNode.className.toLowerCase()))
-      return;
+      // need to find a way to work here only on links
+      if (/button/.test(linkNode.className.toLowerCase()))
+        return;
+    }
 
     // don't interrupt with fastdial links
     if ("ownerDocument" in linkNode && Tabmix.isNewTabUrls(linkNode.ownerDocument.documentURI))
diff --git a/chrome/content/links/newTab.xul b/chrome/content/links/newTab.xul
index 017e8e9..856f878 100644
--- a/chrome/content/links/newTab.xul
+++ b/chrome/content/links/newTab.xul
@@ -11,14 +11,15 @@
       if (!gGrid.cells)
         return;
       let win = Tabmix.getTopWin();
+      if (win)
+        win.TMP_Places._titlefrombookmark = Tabmix.prefs.getBoolPref("titlefrombookmark");
       gGrid.cells.forEach(function (cell) {
         let site = cell.site;
         if (!site)
           return;
         let url = site.url;
         let title = site.title || url;
-        if (Tabmix.prefs.getBoolPref("titlefrombookmark"))
-          title = win.TMP_Places.getTitleFromBookmark(url, title);
+        title = win.TMP_Places.getTitleFromBookmark(url, title);
         let tooltip = (title == url ? title : title + "\n" + url);
         let link = site._querySelector(".newtab-link");
         link.setAttribute("title", tooltip);
@@ -26,15 +27,16 @@
       });
     }
 
-    XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", "resource://gre/modules/PlacesUtils.jsm");
-    Services.prefs.addObserver("extensions.tabmix.titlefrombookmark", updateTitle, false);
-    window.addEventListener("unload", function TMP_removeObserver(aEvent) {
-      aEvent.currentTarget.removeEventListener("unload", TMP_removeObserver, false);
-      Services.prefs.removeObserver("extensions.tabmix.titlefrombookmark", updateTitle);
-    }, false);
-
-    if (Tabmix.prefs.getBoolPref("titlefrombookmark"))
-      updateTitle();
+    let win = Tabmix.getTopWin()
+    if (win && win.Tabmix && win.Tabmix.initialization.onWindowOpen.initialized) {
+      Services.prefs.addObserver("extensions.tabmix.titlefrombookmark", updateTitle, false);
+      window.addEventListener("unload", function TMP_removeObserver(aEvent) {
+        aEvent.currentTarget.removeEventListener("unload", TMP_removeObserver, false);
+        Services.prefs.removeObserver("extensions.tabmix.titlefrombookmark", updateTitle);
+      }, false);
+      if (Tabmix.prefs.getBoolPref("titlefrombookmark"))
+        updateTitle();
+    }
   ]]>
   </script>
 
diff --git a/chrome/content/links/setup.js b/chrome/content/links/setup.js
index 3216b24..c75839d 100644
--- a/chrome/content/links/setup.js
+++ b/chrome/content/links/setup.js
@@ -45,37 +45,34 @@ Tabmix.linkHandling_init = function TMP_TBP_init(aWindowType) {
 }
 
 /**
- * @brief Force-call the window observer at least one time.
- *
- * @returns  Nothing.
- *
  * @Theme Vista-aero 3.0.0.91 and BlueSky 3.0.0.91 use TMP_TBP_Startup in stylesheet
  *        window[onload="TMP_TBP_Startup()"]
  */
 function TMP_TBP_Startup() {
-  // don't start Tabmix at all if our tabbrowser_4.xml didn't start
-  // when ImTranslator extension installed
-  if (!Tabmix.initialized) {
-    Tabmix.initialized = true;
-    if (Tabmix.isVersion(160) && "gBrowserInit" in window)
-      gBrowserInit.onLoad();
-    else
-      BrowserStartup();
-    return;
-  }
+  let onLoad = Tabmix.initialization.run("beforeBrowserInitOnLoad");
+  if (onLoad)
+    onLoad();
+  else if (Tabmix.isVersion(160) && "gBrowserInit" in window)
+    gBrowserInit.onLoad();
+  else
+    BrowserStartup();
+}
 
-  TabmixSvc.windowStartup.init(window);
+Tabmix.beforeBrowserInitOnLoad = function() {
+  try {
+    TabmixSvc.windowStartup.init(window);
+  } catch (ex) {this.assert(ex);}
 
   try {
     // replace old Settings.
     // we must call this before any other tabmix function
     gTMPprefObserver.updateSettings();
     gTMPprefObserver.init();
-  } catch (ex) {Tabmix.assert(ex);}
+  } catch (ex) {this.assert(ex);}
 
   try {
     var SM = TabmixSessionManager;
-    if (Tabmix.isVersion(200)) {
+    if (this.isVersion(200)) {
       SM.globalPrivateBrowsing = PrivateBrowsingUtils.permanentPrivateBrowsing;
       SM.isWindowPrivate = function SM_isWindowPrivate(aWindow) PrivateBrowsingUtils.isWindowPrivate(aWindow);
       // isPrivateWindow is boolean property of this window, user can't change private status of a window
@@ -101,11 +98,11 @@ function TMP_TBP_Startup() {
     var fnContainer, TMP_BrowserStartup;
     if ("__ezsidebar__BrowserStartup" in window) // need to test this on firefox 16+
       [fnContainer, TMP_BrowserStartup] = [window, "__ezsidebar__BrowserStartup"];
-    else if (Tabmix.isVersion(160) && "gBrowserInit" in window)
+    else if (this.isVersion(160) && "gBrowserInit" in window)
       [fnContainer, TMP_BrowserStartup] = [gBrowserInit, "onLoad"];
     else
       [fnContainer, TMP_BrowserStartup] = [window, "BrowserStartup"];
-    var bowserStartup = Tabmix.changeCode(fnContainer, TMP_BrowserStartup);
+    var bowserStartup = this.changeCode(fnContainer, TMP_BrowserStartup);
 
     // Bug 756313 - Don't load homepage URI before first paint
     // moved this code from gBrowserInit.onLoad to gBrowserInit._delayedStartup
@@ -119,24 +116,24 @@ function TMP_TBP_Startup() {
       '   gBrowser.tabContainer.adjustTabstrip(true, url);' +
       '   $&' +
       ' }'
-    if (!Tabmix.isVersion(190))
+    if (!this.isVersion(190))
       bowserStartup = bowserStartup._replace(swapOldCode, swapNewCode);
 
-    var firstWindow = Tabmix.firstWindowInSession || SM.firstNonPrivateWindow;
+    var firstWindow = this.firstWindowInSession || SM.firstNonPrivateWindow;
     var disAllow = SM.isPrivateWindow || TMP_SessionStore.isSessionStoreEnabled() ||
-                   Tabmix.extensions.sessionManager ||
-                   Tabmix.isWindowAfterSessionRestore;
-    var sessionManager = Tabmix.prefs.getBoolPref("sessions.manager");
+                   this.extensions.sessionManager ||
+                   this.isWindowAfterSessionRestore;
+    var sessionManager = this.prefs.getBoolPref("sessions.manager");
     var resumeSession  = sessionManager &&
-                         Tabmix.prefs.getIntPref("sessions.onStart") < 2;
-    var recoverSession = Tabmix.prefs.getBoolPref("sessions.crashRecovery") &&
-                         Tabmix.prefs.prefHasUserValue("sessions.crashed");
+                         this.prefs.getIntPref("sessions.onStart") < 2;
+    var recoverSession = this.prefs.getBoolPref("sessions.crashRecovery") &&
+                         this.prefs.prefHasUserValue("sessions.crashed");
 
     SM.doRestore = !disAllow && firstWindow && (recoverSession || resumeSession);
     if (SM.doRestore) {
       // Prevent the default homepage from loading if we're going to restore a session
-      if (Tabmix.isVersion(250)) {
-        Tabmix.changeCode(gBrowserInit, "gBrowserInit._getUriToLoad")._replace(
+      if (this.isVersion(240)) {
+        this.changeCode(gBrowserInit, "gBrowserInit._getUriToLoad")._replace(
           'sessionStartup.willOverrideHomepage', 'true'
         ).toCode();
       }
@@ -159,14 +156,14 @@ function TMP_TBP_Startup() {
         '  }' +
         '$&'
 
-      if (!Tabmix.isVersion(190)) {
+      if (!this.isVersion(190)) {
         bowserStartup = bowserStartup._replace(
           'if (window.opener && !window.opener.closed', loadOnStartup
         );
       }
     }
     // All-in-One Sidebar 0.7.14 brake Firefox 12.0
-    if (Tabmix.isVersion(120) && typeof aios_dominitSidebar == "function") {
+    if (this.isVersion(120) && typeof aios_dominitSidebar == "function") {
       bowserStartup = bowserStartup._replace(
         'TabsOnTop.syncCommand();',
         'TabsOnTop.init();', {silent: true}
@@ -175,12 +172,12 @@ function TMP_TBP_Startup() {
     bowserStartup.toCode();
 
     // At the moment we must init TabmixSessionManager before sessionStore.init
-    var [obj, fn] = Tabmix.isVersion(160) && "gBrowserInit" in window ?
+    var [obj, fn] = this.isVersion(160) && "gBrowserInit" in window ?
           [gBrowserInit, "gBrowserInit._delayedStartup"] :
           [window, "delayedStartup"];
 
-    Tabmix.changeCode(obj, fn)._replace(
-      'Services.obs.addObserver', loadOnStartup, {check: Tabmix.isVersion(190) && !!loadOnStartup}
+    this.changeCode(obj, fn)._replace(
+      'Services.obs.addObserver', loadOnStartup, {check: this.isVersion(190) && !!loadOnStartup}
     )._replace(
       'Services.obs.addObserver',
       'try {' +
@@ -188,14 +185,14 @@ function TMP_TBP_Startup() {
       '} catch (ex) {Tabmix.assert(ex);}' +
       '$&'
     )._replace(
-      swapOldCode, swapNewCode, {check: Tabmix.isVersion(190)}
+      swapOldCode, swapNewCode, {check: this.isVersion(190)}
     )._replace(
       'SessionStore.canRestoreLastSession',
-      'TabmixSessionManager.canRestoreLastSession', {check: Tabmix.isVersion(250) && sessionManager}
+      'TabmixSessionManager.canRestoreLastSession', {check: this.isVersion(250) && sessionManager}
     ).toCode();
 
     // look for installed extensions that are incompatible with tabmix
-    if (Tabmix.firstWindowInSession && Tabmix.prefs.getBoolPref("disableIncompatible")) {
+    if (this.firstWindowInSession && this.prefs.getBoolPref("disableIncompatible")) {
       setTimeout(function checkCompatibility(aWindow) {
         let tmp = { };
         Components.utils.import("resource://tabmixplus/extensions/CompatibilityCheck.jsm", tmp);
@@ -206,26 +203,18 @@ function TMP_TBP_Startup() {
     // add tabmix menu item to tab context menu before menumanipulator and MenuEdit initialize
     TabmixContext.buildTabContextMenu();
 
-    TMP_BrowserStartup = fnContainer[TMP_BrowserStartup].bind(fnContainer);
-    TMP_BrowserStartup();
+    return fnContainer[TMP_BrowserStartup].bind(fnContainer);
 
-  } catch (ex) {Tabmix.assert(ex);}
+  } catch (ex) {this.assert(ex);}
+
+  return null;
 }
 
 // this must run before all
-Tabmix.initialized = false;
 Tabmix.beforeStartup = function TMP_beforeStartup(tabBrowser, aTabContainer) {
-    this.singleWindowMode = this.prefs.getBoolPref("singleWindow");
-    if (this.singleWindowMode) {
-      let tmp = { };
-      Components.utils.import("resource://tabmixplus/SingleWindowModeUtils.jsm", tmp);
-      // don't initialize Tabmix functions for a window that is about to
-      // close by SingleWindowModeUtils
-      if (tmp.SingleWindowModeUtils.newWindow(window))
-        return;
-    }
+    if (typeof tabBrowser == "undefined")
+      tabBrowser = gBrowser;
 
-    this.initialized = true;
     // return true if all tabs in the window are blank
     tabBrowser.isBlankWindow = function() {
        for (var i = 0; i < this.tabs.length; i++) {
@@ -274,7 +263,6 @@ Tabmix.beforeStartup = function TMP_beforeStartup(tabBrowser, aTabContainer) {
     var tabContainer = aTabContainer || tabBrowser.tabContainer ||
                        document.getAnonymousElementByAttribute(tabBrowser, "anonid", "tabcontainer");
 
-    TMP_eventListener.init(tabContainer);
     // Firefox sessionStore and session manager extension start to add tab before our onWindowOpen run
     // so we initialize this before start
     // mTabMaxWidth not exist from firefox 4.0
@@ -310,11 +298,6 @@ Tabmix.beforeStartup = function TMP_beforeStartup(tabBrowser, aTabContainer) {
       TMP_SessionStore.afterSwitchThemes = true;
 
     TMP_extensionsCompatibility.preInit();
-
-    if (this.prefs.prefHasUserValue("enableDebug") &&
-        this.prefs.getBoolPref("enableDebug")) {
-      this._debugMode = true;
-    }
 }
 
 Tabmix.adjustTabstrip = function tabContainer_adjustTabstrip(skipUpdateScrollStatus, aUrl) {
@@ -389,3 +372,9 @@ Tabmix.adjustTabstrip = function tabContainer_adjustTabstrip(skipUpdateScrollSta
     TabmixTabbar.updateBeforeAndAfter();
   }
 }
+
+// temporary property for Aurora 25.0a2 users with version before 2013-09-12
+// replace it with Tabmix.isVersion(260) once Firefox 25.0 is out
+XPCOMUtils.defineLazyGetter(Tabmix, "_restoreMultipleTabs", function() {
+  return typeof TabmixSvc.ss.setNumberOfTabsClosedLast == "function"
+});
diff --git a/chrome/content/minit/minit.js b/chrome/content/minit/minit.js
index 8f00308..6032c60 100644
--- a/chrome/content/minit/minit.js
+++ b/chrome/content/minit/minit.js
@@ -1178,10 +1178,15 @@ Tabmix.navToolbox = {
       // update physical position
       let useTabmixButtons = TabmixTabbar.scrollButtonsMode > TabmixTabbar.SCROLL_BUTTONS_LEFT_RIGHT;
       TabmixTabbar.setScrollButtonBox(useTabmixButtons, true, true);
-      if (useTabmixButtons && document.getElementById("TabsToolbar").hasAttribute("tabstripoverflow")) {
-        let tabStrip = gBrowser.tabContainer.mTabstrip;
-        tabStrip._scrollButtonUp.collapsed = tabStrip._scrollButtonDown.collapsed = false;
+      let tabBar = gBrowser.tabContainer;
+      if (useTabmixButtons && tabBar.overflow) {
+        tabBar.mTabstrip._scrollButtonUp.collapsed = false;
+        tabBar.mTabstrip._scrollButtonDown.collapsed = false;
       }
     }
+
+    // reset tabsNewtabButton and afterTabsButtonsWidth
+    if (typeof privateTab == "object")
+      TMP_eventListener.updateMultiRow(true);
   }
 }
diff --git a/chrome/content/minit/tabView.js b/chrome/content/minit/tabView.js
index 70050be..96cbe77 100644
--- a/chrome/content/minit/tabView.js
+++ b/chrome/content/minit/tabView.js
@@ -187,12 +187,6 @@
 
   /* ............... TabmixSessionManager TabView Data ............... */
 
-  TabmixSessionManager._sendWindowStateEvent = function SM__sendWindowStateEvent(aType) {
-    let event = document.createEvent("Events");
-    event.initEvent("SSWindowState" + aType, true, false);
-    window.dispatchEvent(event);
-  }
-
   // aWindow: rdfNodeWindow to read from
   TabmixSessionManager._setWindowStateBusy = function SM__setWindowStateBusy(aWindow) {
     TMP_SessionStore.initService();
diff --git a/chrome/content/minit/tablib.js b/chrome/content/minit/tablib.js
index 93bcea9..5e5a678 100644
--- a/chrome/content/minit/tablib.js
+++ b/chrome/content/minit/tablib.js
@@ -19,8 +19,7 @@ var tablib = {
     // we update this value in TabmixProgressListener.listener.onStateChange
     aBrowser.tabmix_allowLoad = !TabmixTabbar.lockallTabs;
     Tabmix.changeCode(aBrowser, "browser.loadURIWithFlags")._replace(
-      '{',
-      '$&' +
+      'if (!aURI)',
       '  var newURI, allowLoad = this.tabmix_allowLoad != false || aURI.match(/^javascript:/);' +
       '  try  {' +
       '    if (!allowLoad) {' +
@@ -39,9 +38,14 @@ var tablib = {
       '    browser.stop();' +
       '    browser.tabmix_allowLoad = true;' +
       '    browser.loadURIWithFlags(aURI, aFlags, aReferrerURI, aCharset, aPostData);' +
-      '    return;' +
+      '    return newTab;' +
       '  }' +
-      '  this.tabmix_allowLoad = aURI == "about:blank" || !isLockedTab;'
+      '  this.tabmix_allowLoad = aURI == "about:blank" || !isLockedTab;\n' +
+      '  $&'
+    )._replace(
+      /(\})(\)?)$/,
+      'return null;\
+      $1$2'
     )._replace(
       'this.webNavigation.LOAD_FLAGS_FROM_EXTERNAL',
       'Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL', {check: "loadTabsProgressively" in window }
@@ -77,7 +81,7 @@ var tablib = {
        dontMove = params.dontMove || null;'
     )._replace(
       't.setAttribute("label", aURI);',
-      't.setAttribute("label", TabmixTabbar.widthFitTitle ? this.mStringBundle.getString("tabs.connecting") : aURI);'
+      't.setAttribute("label", TabmixTabbar.widthFitTitle && aURI.indexOf("about") != 0 ? this.mStringBundle.getString("tabs.connecting") : aURI);'
     )._replace(
       't.className = "tabbrowser-tab";',
       '$&\
@@ -191,7 +195,11 @@ var tablib = {
 
     Tabmix.changeCode(gBrowser, "gBrowser.getWindowTitleForBrowser")._replace(
       'if (!docTitle)',
-      'docTitle = TMP_Places.getTabTitle(this.getTabForBrowser(aBrowser), aBrowser.currentURI.spec, docTitle);\
+      'let tab = this.getTabForBrowser(aBrowser);\
+       if (tab.hasAttribute("tabmix_changed_label"))\
+         docTitle = tab.getAttribute("tabmix_changed_label");\
+       else\
+         docTitle = TMP_Places.getTabTitle(tab, aBrowser.currentURI.spec, docTitle);\
        $&'
     ).toCode();
 
@@ -222,6 +230,11 @@ var tablib = {
       'if (aTab.label == title',
       'if (aTab.hasAttribute("mergeselected"))\
          title = "(*) " + title;\
+       if (aTab.hasAttribute("tabmix_changed_label")) {\
+         aTab.removeAttribute("tabmix_changed_label");\
+         if (aTab.label == title && aTab.crop == crop)\
+           tablib.onTabTitleChanged(aTab, title == urlTitle);\
+       }\
        $&'
     )._replace(
       'aTab.crop = crop;',
@@ -236,10 +249,16 @@ var tablib = {
     ).toCode();
 
     // Follow up bug 887515 - add ability to restore multiple tabs
-    if (Tabmix.isVersion(250)) {
+    // bug 914258 backout 887515 changes from Firefox 25
+    if (Tabmix._restoreMultipleTabs) {
       Tabmix.changeCode(gBrowser, "gBrowser.removeTabsToTheEndFrom")._replace(
-        'ss.setNumberOfTabsClosedLast(window, numberOfTabsToClose);',
-        'this.setNumberOfTabsClosedLast();'
+        'let tabs = this.getTabsToTheEndFrom(aTab);',
+        '$&\n'+
+        '              Tabmix.startCountingClosedTabs();'
+      )._replace(
+        '#1.setNumberOfTabsClosedLast(window, numberOfTabsToClose);'.
+         replace("#1", Tabmix.isVersion(260) ? "SessionStore" : "ss"),
+        'Tabmix.setNumberOfTabsClosedLast();'
       ).toCode();
     }
   },
@@ -390,7 +409,7 @@ var tablib = {
       '{if(!Tabmix.prefs.getBoolPref("selectTabOnMouseDown") && Tabmix.isCallerInList("' + callerName + '")) return;'
     ).toCode();
 
-    let _setter = Tabmix.changeCode(tabBar,  "gBrowser.tabContainer.visible", {setter: true})._replace(
+    Tabmix.changeCode(tabBar,  "gBrowser.tabContainer.visible", {setter: true})._replace(
       'this._container.collapsed = !val;',
       '  if (TabmixTabbar.hideMode == 2)' +
       '    val = false;' +
@@ -400,11 +419,7 @@ var tablib = {
       '    bottomToolbox.collapsed = !val;' +
       '    gTMPprefObserver.updateTabbarBottomPosition();' +
       '  }'
-    );
-    Tabmix.defineProperty(tabBar, "visible", {
-      getter: tabBar.__lookupGetter__("visible"),
-      setter: _setter.value
-    });
+    ).defineProperty();
 
   },
 
@@ -641,7 +656,8 @@ var tablib = {
     // if user changed mode to single window mode while having closed window
     // make sure that undoCloseWindow will open the closed window in the most recent non-private window
     Tabmix.changeCode(window, "undoCloseWindow")._replace(
-      'window = ss.undoCloseWindow(aIndex || 0);',
+      'window = #1.undoCloseWindow(aIndex || 0);'.
+       replace("#1", Tabmix.isVersion(260) ? "SessionStore" : "ss"),
       '{if (Tabmix.singleWindowMode) {\
          window = TabmixSvc.version(200) ?\
             Tabmix.RecentWindow.getMostRecentBrowserWindow({private: false}) :\
@@ -650,13 +666,13 @@ var tablib = {
        if (window) {\
         window.focus();\
         let index = aIndex || 0;\
-        let closedWindows = TabmixSvc.JSON.parse(ss.getClosedWindowData());\
-        ss.forgetClosedWindow(index);\
+        let closedWindows = TabmixSvc.JSON.parse(#1.getClosedWindowData());\
+        #1.forgetClosedWindow(index);\
         let state = closedWindows.splice(index, 1).shift();\
         state = TabmixSvc.JSON.stringify({windows: [state]});\
-        ss.setWindowState(window, state, false);\
+        #1.setWindowState(window, state, false);\
        }\
-       else $&}'
+       else $&}'.replace("#1", Tabmix.isVersion(260) ? "SessionStore" : "ss", "g")
     )._replace(
       'return window;',
       'TabmixSessionManager.notifyClosedWindowsChanged();\
@@ -692,11 +708,8 @@ var tablib = {
     ).toCode();
 
     Tabmix.changeCode(HistoryMenu.prototype, "HistoryMenu.prototype.populateUndoWindowSubmenu")._replace(
-      'JSON.parse(this._ss.getClosedWindowData());',
-      '"parse" in JSON ? JSON.parse(this._ss.getClosedWindowData()) : TabmixSvc.JSON.parse(this._ss.getClosedWindowData());'
-    )._replace(
       'this._ss',
-      'TabmixSvc.ss', {flags: "g"}
+      'TabmixSvc.ss', {check: !Tabmix.isVersion(260), flags: "g"}
     )._replace(
       'this._rootElt.getElementsByClassName("recentlyClosedWindowsMenu")[0];',
       'this._rootElt ? this._rootElt.getElementsByClassName("recentlyClosedWindowsMenu")[0] : document.getElementById(arguments[0]);'
@@ -737,6 +750,9 @@ var tablib = {
       popup.setAttribute("context", "tm_undocloseWindowContextMenu");
 
     Tabmix.changeCode(window, "switchToTabHavingURI")._replace(
+      'function switchIfURIInWindow',
+      'let switchIfURIInWindow = $&', {check: Tabmix._debugMode}
+    )._replace(
       'gBrowser.selectedBrowser.loadURI(aURI.spec);',
       '{$& \
        gBrowser.ensureTabIsVisible(gBrowser.selectedTab);}'
@@ -773,7 +789,7 @@ var tablib = {
 
       // sessionstore duplicateTab failed
       if (!newTab)
-        retuen;
+        return null;
 
       this.selectedBrowser.focus();
 
@@ -847,7 +863,8 @@ var tablib = {
       else {
         aTab._tabmixCopyToWindow = {data: aTabData};
         // replaceTabWithWindow not working if there is only one tab in the the window
-        window.openDialog(getBrowserURL(), "_blank", "chrome,dialog=no,all", aTab);
+        window.openDialog("chrome://browser/content/browser.xul",
+            "_blank", "chrome,dialog=no,all", aTab);
       }
     }
 
@@ -888,8 +905,9 @@ var tablib = {
           }
 
           var event = document.createEvent("Events");
-          event.initEvent("click", true, true);
-          event.getPreventDefault = function () { return false; }
+          event.initEvent("click", true, false);
+          event.tabmix_openLinkWithHistory = true;
+          event.button = 0;
           gContextMenu.target.dispatchEvent(event);
 
           newTab = aTab;
@@ -934,12 +952,12 @@ var tablib = {
         // remove current tab last
         if (!this.mCurrentTab.pinned)
           tabs.unshift(tabs.splice(tabs.indexOf(this.mCurrentTab), 1)[0]);
-        this.startCountingClosedTabs();
+        Tabmix.startCountingClosedTabs();
         tabs.reverse().forEach(function TMP_removeTab(tab) {
           if (!tab.pinned)
             this.removeTab(tab, {animate: false});
         }, this);
-        this.setNumberOfTabsClosedLast();
+        Tabmix.setNumberOfTabsClosedLast();
         // _handleTabSelect will call mTabstrip.ensureElementIsVisible
       }
     }
@@ -954,7 +972,7 @@ var tablib = {
 
       if (this.warnAboutClosingTabs(this.closingTabsEnum.GROUP, null, aDomain)) {
         var childNodes = this.visibleTabs;
-        this.startCountingClosedTabs();
+        Tabmix.startCountingClosedTabs();
         for (var i = childNodes.length - 1; i > -1; --i) {
           if (childNodes[i] != aTab && !childNodes[i].pinned &&
               this.getBrowserForTab(childNodes[i]).currentURI.spec.indexOf(aDomain) != -1)
@@ -964,7 +982,7 @@ var tablib = {
           this.removeTab(aTab, {animate: true});
           this.ensureTabIsVisible(this.selectedTab);
         }
-        this.setNumberOfTabsClosedLast();
+        Tabmix.setNumberOfTabsClosedLast();
       }
     }
 
@@ -992,12 +1010,12 @@ var tablib = {
 
         let childNodes = this.visibleTabs;
         let tabPos = childNodes.indexOf(aTab);
-        this.startCountingClosedTabs();
+        Tabmix.startCountingClosedTabs();
         for (let i = childNodes.length - 1; i > tabPos; i--) {
           if (!childNodes[i].pinned)
             this.removeTab(childNodes[i]);
         }
-        this.setNumberOfTabsClosedLast();
+        Tabmix.setNumberOfTabsClosedLast();
       }
     }
 
@@ -1013,12 +1031,12 @@ var tablib = {
 
         let childNodes = this.visibleTabs;
         let tabPos = childNodes.indexOf(aTab);
-        this.startCountingClosedTabs();
+        Tabmix.startCountingClosedTabs();
         for (let i = tabPos - 1; i >= 0; i--) {
           if (!childNodes[i].pinned)
             this.removeTab(childNodes[i]);
         }
-        this.setNumberOfTabsClosedLast();
+        Tabmix.setNumberOfTabsClosedLast();
       }
     }
 
@@ -1033,12 +1051,12 @@ var tablib = {
         var childNodes = this.visibleTabs;
         if (TabmixTabbar.visibleRows > 1)
           this.tabContainer.updateVerticalTabStrip(true)
-        this.startCountingClosedTabs();
+        Tabmix.startCountingClosedTabs();
         for (var i = childNodes.length - 1; i >= 0; --i) {
           if (childNodes[i] != aTab && !childNodes[i].pinned)
             this.removeTab(childNodes[i]);
         }
-        this.setNumberOfTabsClosedLast();
+        Tabmix.setNumberOfTabsClosedLast();
       }
     });
 
@@ -1513,17 +1531,18 @@ var tablib = {
     }
 
     // Follow up bug 887515 - add ability to restore multiple tabs
-    if (Tabmix.isVersion(250)) {
-      gBrowser.startCountingClosedTabs = function() {
+    // bug 914258 backout 887515 changes from Firefox 25
+    if (Tabmix._restoreMultipleTabs) {
+      Tabmix.startCountingClosedTabs = function() {
         this.shouldCountClosedTabs = true;
         this.numberOfTabsClosedLast = 0;
       }
-      gBrowser.setNumberOfTabsClosedLast = function(aNum) {
+      Tabmix.setNumberOfTabsClosedLast = function(aNum) {
         TabmixSvc.ss.setNumberOfTabsClosedLast(window, aNum || this.numberOfTabsClosedLast);
         this.shouldCountClosedTabs = false;
         this.numberOfTabsClosedLast = 0;
       }
-      gBrowser.countClosedTabs = function(aTab) {
+      Tabmix.countClosedTabs = function(aTab) {
         if (!this.shouldCountClosedTabs ||
             Services.prefs.getIntPref("browser.sessionstore.max_tabs_undo") == 0)
           return;
@@ -1537,9 +1556,9 @@ var tablib = {
       }
     }
     else {
-      gBrowser.startCountingClosedTabs = function() { }
-      gBrowser.setNumberOfTabsClosedLast = function() { }
-      gBrowser.countClosedTabs = function() { }
+      Tabmix.startCountingClosedTabs = function() { }
+      Tabmix.setNumberOfTabsClosedLast = function() { }
+      Tabmix.countClosedTabs = function() { }
     }
 
     /** DEPRECATED **/
@@ -1576,12 +1595,12 @@ var tablib = {
       let width = aTab.boxObject.width;
       aTab.removeAttribute("width");
       if (width != aTab.boxObject.width)
-        TMP_Places.afterTabTitleChanged();
+        TMP_Places.afterTabTitleChanged(true);
       if (aTab.hasAttribute("newtab"))
         aTab.removeAttribute("newtab");
     }
     else if (aTab.hasAttribute("fadein"))
-      TMP_Places.afterTabTitleChanged();
+      TMP_Places.afterTabTitleChanged(true);
     // don't keep unnecessary reference to current tab
     if (!TMP_Places.inUpdateBatch)
       TMP_Places.currentTab = null;
@@ -1688,7 +1707,7 @@ var tablib = {
     // we always show our prompt on Mac
     var showPrompt = Tabmix.isPlatform("Mac") || !isAfterFirefoxPrompt();
     // get caller caller name and make sure we are not on restart
-    var quitType = Tabmix._getCallerNameByIndex(2);
+    var quitType = Tabmix.getCallerNameByIndex(2);
     var askBeforSave = quitType != "restartApp" && quitType != "restart";
     var isLastWindow = Tabmix.isLastBrowserWindow;
     var result = TabmixSessionManager.deinit(isLastWindow, askBeforSave);
diff --git a/chrome/content/overlay/browsr.css b/chrome/content/overlay/browsr.css
index 6b774d8..8d04792 100644
--- a/chrome/content/overlay/browsr.css
+++ b/chrome/content/overlay/browsr.css
@@ -116,21 +116,18 @@ label.text-link, label[onclick] {
   visibility: collapse !important;
 }
 
-#TabsToolbar:not([tabstripoverflow]) > #tabmixScrollBox,
+#tabbrowser-tabs:not([overflow="true"]) ~ #tabmixScrollBox,
 #tabmixScrollBox[flowing="singlebar"],
 #tabmixScrollBox[defaultScrollButtons] {
   visibility: collapse;
 }
 
-/*** hide tabs-newtab-button that we don't use
- *
- * we set newtab_side only for left ot right side afterlast position rules are set by Firefox
- *
- ***/
-#tabbrowser-tabs:not([tabmix-visible])[overflow="true"] > .tabbrowser-arrowscrollbox > .tabs-newtab-button,
-#TabsToolbar:not([tabmix-visible])[newTabButton=false] .tabbrowser-arrowscrollbox > .tabs-newtab-button,
-#TabsToolbar:not([tabmix-visible])[disAllowNewtabbutton] .tabbrowser-arrowscrollbox > .tabs-newtab-button,
-#TabsToolbar:not([tabmix-visible])[newtab_side] .tabbrowser-arrowscrollbox > .tabs-newtab-button {
+/*** hide tabs-newtab-button that we don't use ***/
+#TabsToolbar:not([currentset*="privateTab-toolbar-openNewPrivateTab"]) #privateTab-afterTabs-openNewPrivateTab,
+#tabbrowser-tabs[overflow="true"] > .tabbrowser-arrowscrollbox > .tabs-newtab-button,
+#TabsToolbar:not([tabmix-show-newtabbutton]) .tabbrowser-arrowscrollbox > .tabs-newtab-button[command="cmd_newNavigatorTab"],
+#TabsToolbar[tabmix-show-newtabbutton*="side"] .tabbrowser-arrowscrollbox > .tabs-newtab-button {
+  /* using visibility: hidden !important; don't collapse the width and don't collapse padding and border height */
   visibility: collapse;
   height: 0px !important;
   width: 0px;
@@ -139,27 +136,51 @@ label.text-link, label[onclick] {
   vertical-align: bottom;
 }
 
-#TabsToolbar:not([newTabButton=false]):not([disAllowNewtabbutton]):not([newtab_side]) >
-   #tabbrowser-tabs:not([overflow="true"]) > .tabbrowser-arrowscrollbox > .tabs-newtab-button {
+#TabsToolbar[tabmix-show-newtabbutton="aftertabs"]:not([customizing="true"]) >
+   #tabbrowser-tabs:not([overflow="true"]) > .tabbrowser-arrowscrollbox > .tabs-newtab-button[command="cmd_newNavigatorTab"] {
   visibility: visible;
 }
 
-#TabsToolbar[tabmix-visible] .tabbrowser-arrowscrollbox > .tabs-newtab-button {  /* look in tabmix.js */
+#TabsToolbar[tabmix-show-newtabbutton="afterTabs-force"] .tabbrowser-arrowscrollbox > .tabs-newtab-button {  /* look in tabmix.js */
   visibility: visible !important;
 }
 
-#TabsToolbar[newTabButton=true][tabstripoverflow] > #new-tab-button,
-#TabsToolbar[newTabButton=true][disAllowNewtabbutton]:not([tabstripoverflow]):not([newtab_side]) > #new-tab-button,
-#TabsToolbar[newTabButton=true][newtab_side] > #new-tab-button {
+#TabsToolbar[tabmix-show-newtabbutton*="side"] > #new-tab-button {
   visibility: visible;
 }
 
 /* In vertical tabbar the default rules from chrome://browser/content/browser.css are sufficient */
-#TabsToolbar:not([orient="vertical"]):not([disAllowNewtabbutton]):not([tabstripoverflow]):not([newtab_side]) > #new-tab-button,
-#TabsToolbar[newTabButton=false] > #new-tab-button {
+#TabsToolbar:not([orient="vertical"])[tabmix-show-newtabbutton*="aftertabs"] > #new-tab-button,
+#TabsToolbar:not([tabmix-show-newtabbutton]) > #new-tab-button {
   visibility: collapse;
 }
 
+/*** Private-tab compatibility ***/
+#privateTab-afterTabs-openNewPrivateTab {
+  vertical-align: bottom;
+}
+
+#TabsToolbar[currentset*="privateTab-toolbar-openNewPrivateTab"]:not([tabmix-show-newtabbutton])
+    > #tabbrowser-tabs:not([overflow="true"]) > .tabbrowser-arrowscrollbox > .tabs-newtab-button[command="cmd_newNavigatorTab"] {
+  display: none; /* override privateTab visibility: visible !important; */
+}
+
+#TabsToolbar[currentset*="privateTab-toolbar-openNewPrivateTab"][tabmix-show-newtabbutton*="side"]
+    > #tabbrowser-tabs:not([overflow="true"]) > .tabbrowser-arrowscrollbox > .tabs-newtab-button {
+  display: none; /* override privateTab visibility: visible !important; */
+}
+
+#TabsToolbar[currentset*="privateTab-toolbar-openNewPrivateTab"][tabmix-show-newtabbutton*="aftertabs"]
+    > #privateTab-toolbar-openNewPrivateTab {
+  visibility: collapse; /* missing rule form privateTab */
+}
+
+#TabsToolbar[currentset*="privateTab-toolbar-openNewPrivateTab"][tabmix-show-newtabbutton*="aftertabs"]:not([customizing="true"])
+    > #tabbrowser-tabs > .tabbrowser-arrowscrollbox > #privateTab-afterTabs-openNewPrivateTab,
+#TabsToolbar[tabmix-show-newtabbutton*="side"] > #privateTab-toolbar-openNewPrivateTab {
+  visibility: visible;
+}
+
 /***  Tab opening animation  ***/
 .tab-progress-container > .tab-progress:not([fadein]):not([pinned]),
 .tab-icon:not([fadein]):not([pinned]) > .tab-protect-icon,
@@ -169,7 +190,7 @@ label.text-link, label[onclick] {
    display: none !important;
 }
 
-/* we have display: -moz-box !important; in browsroverlay_base.css
+/* we use display: -moz-box !important;
 so display: none !important; is not hide the button */
 .tab-close-button:not([fadein]) {
   opacity: 0;
diff --git a/chrome/content/places/places.js b/chrome/content/places/places.js
index 6823a6d..aea94e8 100644
--- a/chrome/content/places/places.js
+++ b/chrome/content/places/places.js
@@ -24,7 +24,6 @@ var TMP_Places = {
    },
 
    init: function TMP_PC_init() {
-      this.initPlacesUIUtils();
       this.contextMenu.toggleEventListener(true);
 
       // use tab label for bookmark name when user renamed the tab
@@ -35,12 +34,11 @@ var TMP_Places = {
             'TMP_Places.getTabTitle(gBrowser.getTabForBrowser(aBrowser), url.spec) || $&'
          ).toCode();
 
-         let getter = Tabmix.changeCode(PlacesCommandHook, "uniqueCurrentPages", {getter: true})._replace(
+         Tabmix.changeCode(PlacesCommandHook, "uniqueCurrentPages", {getter: true})._replace(
            'URIs.push(tab.linkedBrowser.currentURI);',
            'let uri = tab.linkedBrowser.currentURI; \
             URIs.push({uri: uri, title: TMP_Places.getTabTitle(tab, uri.spec)});'
-         );
-         Tabmix.defineProperty(PlacesCommandHook, "uniqueCurrentPages", {getter: getter.value});
+         ).defineProperty();
       }
 
       if ("PlacesViewBase" in window && PlacesViewBase.prototype) {
@@ -60,7 +58,7 @@ var TMP_Places = {
 
       // fix small bug when the event is not mouse event
       // inverse focus of middle/ctrl/meta clicked bookmarks/history
-      // when we are in single window mode set the function to retuen "tab"
+      // when we are in single window mode set the function to return "tab"
       Tabmix.changeCode(window, "whereToOpenLink")._replace(
         'var middle = !ignoreButton && e.button == 1;',
         'var middle = e instanceof MouseEvent && !ignoreButton && e.button == 1;'
@@ -78,9 +76,9 @@ var TMP_Places = {
       Tabmix.changeCode(window, "openUILinkIn")._replace(
         'params.fromChrome = true;',
         '$&' +
+        /* only in use when openUILinkIn called from searchbar.doSearch */
         ' var tabmixArg = arguments.length > 5 ? arguments[5] : null;' +
         ' if (tabmixArg) {' +
-        '   params.bookMarkId = "bookMarkId" in tabmixArg && tabmixArg.bookMarkId > -1 ? tabmixArg.bookMarkId : null;' +
         '   if ("backgroundPref" in tabmixArg)' +
         '     params.inBackground = getBoolPref(tabmixArg.backgroundPref);' +
         ' }' +
@@ -95,8 +93,7 @@ var TMP_Places = {
       Tabmix.changeCode(fnObj, fnName)._replace(
         /aRelatedToCurrent\s*= params.relatedToCurrent;/,
         '$& \
-         var bookMarkId = params.bookMarkId; \
-         var backgroundPref = params.backgroundPref;'
+         var bookMarkId = params.bookMarkId;'
       )._replace(
         'where == "current" && w.gBrowser.selectedTab.pinned',
         '$& && !params.suppressTabsOnFileDownload'
@@ -107,9 +104,6 @@ var TMP_Places = {
       )._replace(
         /Services.ww.openWindow[^;]*;/,
         'let newWin = $&\n    if (newWin && bookMarkId)\n        newWin.bookMarkIds = bookMarkId;'
-      )._replace( // we probably don't need this since Firefox 10
-        'aFromChrome ?',
-        '$& getBoolPref(backgroundPref) ||'
       )._replace(
         'w.gBrowser.loadURIWithFlags(url, flags, aReferrerURI, null, aPostData);',
         '$&\
@@ -133,141 +127,10 @@ var TMP_Places = {
       }
    },
 
-   functions: ["_openTabset", "openURINodesInTabs", "openContainerNodeInTabs", "openNodeWithEvent", "_openNodeIn"],
-   initPlacesUIUtils: function TMP_PC_initPlacesUIUtils(forceInit) {
-      var treeStyleTab = "TreeStyleTabBookmarksService" in window;
-      // we enter getURLsForContainerNode into TMP_Places to prevent leakes from PlacesUtils
-      if (!this.getURLsForContainerNode && !treeStyleTab) {
-        Tabmix.changeCode(PlacesUtils, "PlacesUtils.getURLsForContainerNode")._replace(
-          '{uri: child.uri,',
-          '{id: child.itemId, uri: child.uri,', {flags: "g"}
-        )._replace(
-          'this.',  'PlacesUtils.', {flags: "g"}
-        ).toCode(false, TMP_Places, "getURLsForContainerNode");
-      }
-
-      try {
-        let test = PlacesUIUtils._openTabset.toString();
-      } catch (ex) {
-        if (window.document.documentElement.getAttribute("windowtype") == "navigator:browser") {
-          Tabmix.log("Starting with Firefox 21 Imacros 8.3.0 break toString on PlacesUIUtils functions."
-            + "\nTabmix can't update PlacesUIUtils to work according to Tabmix preferences, use Imacros 8.3.1 and up.");
-        }
-        return;
-      }
-
-      if (!forceInit) {
-         if (PlacesUIUtils.tabmix_inited) {
-            PlacesUIUtils.tabmix_inited++;
-            return;
-         }
-         PlacesUIUtils.tabmix_inited = 1;
-      }
-      this._first_instance = true;
-      this.functions.forEach(function(aFn) {
-        PlacesUIUtils["tabmix_" + aFn] = PlacesUIUtils[aFn];
-      });
-
-      var treeStyleTab = "TreeStyleTabBookmarksService" in window;
-      function updateOpenTabset() {
-        var loadTabsCode = "browserWindow.$1.loadTabs(urls, loadInBackground, false);"
-        loadTabsCode = loadTabsCode.replace("$1", "gBrowser");
-        var openGroup = "browserWindow.TMP_Places.openGroup(urls, ids, where$1);"
-        Tabmix.changeCode(PlacesUIUtils, "PlacesUIUtils._openTabset")._replace(
-          'urls = []',
-          'behavior, $&', {check: treeStyleTab}
-        )._replace(
-          'var urls = [];',
-          '$& var ids = [];', {check: !treeStyleTab}
-        )._replace(
-          'urls.push(item.uri);',
-          '$& ids.push(item.id);', {check: !treeStyleTab}
-        )._replace(
-          '"chrome,dialog=no,all", args);',
-          '$&\
-           browserWindow.bookMarkIds = ids.join("|");'
-        )._replace(
-          /let openGroupBookmarkBehavior =|TSTOpenGroupBookmarkBehavior =/,
-          '$& behavior =', {check: treeStyleTab}
-        )._replace(
-          loadTabsCode,
-          'var changeWhere = where == "tabshifted" && aEvent.target.localName != "menuitem"; \
-           if (changeWhere) where = "current";'
-           + openGroup.replace("$1", treeStyleTab ? ", behavior" : "")
-        ).toCode();
-      }
-      if (treeStyleTab) {
-        window.setTimeout(function () {updateOpenTabset();}, 0);
-      }
-      else { // TreeStyleTab not installed
-        updateOpenTabset();
-
-        Tabmix.changeCode(PlacesUIUtils, "PlacesUIUtils.openURINodesInTabs")._replace(
-          'push({uri: aNodes[i].uri,',
-          'push({id: aNodes[i].itemId, uri: aNodes[i].uri,'
-        ).toCode();
-
-        Tabmix.changeCode(PlacesUIUtils, "PlacesUIUtils.openContainerNodeInTabs")._replace(
-          'PlacesUtils.getURLsForContainerNode(aNode)',
-          'TMP_Places.getURLsForContainerNode(aNode)'
-        ).toCode();
-      }
-
-      Tabmix.changeCode(PlacesUIUtils, "PlacesUIUtils.openNodeWithEvent")._replace(
-         /whereToOpenLink\(aEvent[,\s\w]*\), window/, '$&, aEvent'
-      ).toCode();
-
-      // Don't change "current" when user click context menu open (callee is PC_doCommand and aWhere is current)
-      // we disable the open menu when the tab is lock
-      // the 2nd check for aWhere == "current" is for non Firefox code that may call this function
-      Tabmix.changeCode(PlacesUIUtils, "PlacesUIUtils._openNodeIn")._replace(
-        /(function[^\(]*\([^\)]+)(\))/,
-        '$1, TMP_Event$2'
-      )._replace(
-        'aWindow.openUILinkIn',
-        'if (TMP_Event) aWhere = TMP_Places.isBookmarklet(aNode.uri) ? "current" : '
-                     + 'aWindow.TMP_Places.fixWhereToOpen(TMP_Event, aWhere); \
-         else if (aWhere == "current" && !TMP_Places.isBookmarklet(aNode.uri)) {\
-           let caller = Tabmix._getCallerNameByIndex(2);\
-           if (caller != "PC_doCommand")\
-             aWhere = aWindow.TMP_Places.fixWhereToOpen(null, aWhere);\
-         }\
-         if (aWhere == "current") Tabmix.getTopWin().gBrowser.mCurrentBrowser.tabmix_allowLoad = true;\
-         $&'
-      )._replace(
-        'inBackground:',
-        'bookMarkId: aNode.itemId, initiatingDoc: null,\
-         $&'
-      ).toCode();
-   },
-
    deinit: function TMP_PC_deinit() {
-      let placesCount = --PlacesUIUtils.tabmix_inited;
-      if (this._first_instance || placesCount == 0) {
-        this.functions.forEach(function(aFn) {
-           PlacesUIUtils[aFn] = PlacesUIUtils["tabmix_" + aFn];
-           delete PlacesUIUtils["tabmix_" + aFn];
-        });
-        this.getURLsForContainerNode = null;
-        delete PlacesUIUtils.tabmix_inited;
-      }
-
-      // when we close the window from which we run eval on PlacesUIUtils
-      // we need to do it again on the new window, or else PlacesUtils will be undefined in PlacesUIUtils
-      if (this._first_instance) {
-        let win = Tabmix.getTopWin() || Services.wm.getMostRecentWindow("Places:Organizer");
-        if (win) {
-          PlacesUIUtils.tabmix_inited = placesCount;
-          win.TMP_Places.initPlacesUIUtils(true);
-        }
-      }
-
       this.stopObserver();
    },
 
-   getURLsForContainerNode: null,
-   _first_instance: false,
-
   // update compatibility with X-notifier(aka WebMail Notifier) 2.9.13+
   // object name wmn replace with xnotifier for version 3.0+
   getXnotifierFunction: function(aName) {
@@ -483,11 +346,13 @@ var TMP_Places = {
     if (title != aTab.label) {
       aTab.label = title;
       aTab.crop = title != aUrl || aUrl == "about:blank" ? "end" : "center";
+      aTab.setAttribute("tabmix_changed_label", title);
       gBrowser._tabAttrModified(aTab);
       if (aTab.selected)
         gBrowser.updateTitlebar();
       if (!aTab.hasAttribute("faviconized"))
         aTab.removeAttribute("width");
+      this._tabTitleChanged = true;
       return true;
     }
     return false;
@@ -569,11 +434,14 @@ var TMP_Places = {
     return false;
   },
 
-  afterTabTitleChanged: function TMP_PC_afterTabTitleChanged() {
+  afterTabTitleChanged: function TMP_PC_afterTabTitleChanged(aChanged) {
+    if (!aChanged && !this._tabTitleChanged)
+      return;
     if (this.inUpdateBatch) {
       this._tabTitleChanged = true;
       return;
     }
+    this._tabTitleChanged = false;
     TabmixTabbar.updateScrollStatus();
     TabmixTabbar.updateBeforeAndAfter();
     if (this.currentTab) {
@@ -648,6 +516,7 @@ var TMP_Places = {
       }, this);
       this.stopObserver();
     }
+    this.afterTabTitleChanged();
   },
 
   _hasBookmarksObserver: false,
@@ -673,12 +542,15 @@ var TMP_Places = {
     let getIndex = function(url) aUrl.indexOf(url) + 1;
     Array.forEach(gBrowser.tabs, function(tab) {
       let url = tab.linkedBrowser.currentURI.spec;
+      if (this.isUserRenameTab(tab, url))
+        return;
       let index = this.applyCallBackOnUrl(url, getIndex);
-      if (index && !this.isUserRenameTab(tab, url)) {
+      if (index) {
         tab.setAttribute("tabmix_bookmarkId", aItemId[index-1]);
         this.setTabTitle(tab, url);
       }
     }, this);
+    this.afterTabTitleChanged();
   },
 
   updateTitleonTabs: function TMP_PC_updateTitleonTabs(aItemId, aRemoved) {
@@ -691,7 +563,7 @@ var TMP_Places = {
     let tabs = gBrowser.tabContainer.getElementsByAttribute(ID, batch ? "*" : aItemId);
     Array.slice(tabs).forEach(function(tab) {
       if (!batch ||
-          aItemId.indexOf(parseInt(tab.getAttribute(ID)))) {
+          aItemId.indexOf(parseInt(tab.getAttribute(ID))) > -1) {
         tab.removeAttribute(ID);
         let url = tab.linkedBrowser.currentURI.spec;
         if (!this.isUserRenameTab(tab, url)) {
@@ -702,6 +574,7 @@ var TMP_Places = {
         }
       }
     }, this);
+    this.afterTabTitleChanged();
   },
 
   onItemAdded: function TMP_PC_onItemAdded(aItemId, aFolder, aIndex, aItemType, aURI) {
@@ -749,12 +622,8 @@ var TMP_Places = {
 
     this._batchData = {updateIDs:[], add:{ids:[], urls:[]}};
 
-    if (this._tabTitleChanged) {
-      this._tabTitleChanged = false;
-      this.afterTabTitleChanged(this.currentTab);
-    }
-    else
-      this.currentTab = null;
+    this.afterTabTitleChanged();
+    this.currentTab = null;
   },
 
   onItemVisited: function () {},
@@ -817,3 +686,10 @@ TMP_Places.contextMenu = {
     }
   }
 }
+
+/** DEPRECATED **/
+TMP_Places.getTabFixedTitle = function(aBrowser, aUri) {
+  let win = aBrowser.ownerDocument.defaultView;
+  Tabmix.log("TMP_Places.getTabFixedTitle was deprecated\nuse TMP_Places.getTabTitle(tab, url)")
+  return win.TMP_Places.getTabTitle(win.gBrowser.getTabForBrowser(aBrowser), aUri);
+}
diff --git a/chrome/content/preferences/appearance.js b/chrome/content/preferences/appearance.js
index 6e2814e..bd083f9 100644
--- a/chrome/content/preferences/appearance.js
+++ b/chrome/content/preferences/appearance.js
@@ -50,8 +50,10 @@ var gAppearancePane = {
       hbox.setAttribute("align","center");
     }
 
-    this.toolbarButtons(browserWindow);
     gPrefWindow.initPane("paneAppearance");
+    // call this function after initPane
+    // we update some broadcaster that initPane may reset
+    this.toolbarButtons(browserWindow);
   },
 
   tabCloseButtonChanged: function() {
@@ -108,14 +110,14 @@ var gAppearancePane = {
     onPlate.childNodes[1].hidden = onPlate.childNodes.length > 2;
 
     // Display > Tab bar
-    function updateDisabledState(buttonID, itemID) {
+    function updateDisabledState(buttonID, itemID, aEnable) {
       let button = aWindow.document.getElementById(buttonID);
       let enablePosition =  button && button.parentNode == aWindow.gBrowser.tabContainer._container;
       gPrefWindow.setDisabled(itemID, !enablePosition || null);
-      gPrefWindow.setDisabled("obs_" + itemID, !enablePosition || null);
+      gPrefWindow.setDisabled("obs_" + itemID, !aEnable || !enablePosition || null);
     }
-    updateDisabledState("new-tab-button", "newTabButton");
-    updateDisabledState("alltabs-button", "hideAllTabsButton");
+    updateDisabledState("new-tab-button", "newTabButton", $("pref_newTabButton").value);
+    updateDisabledState("alltabs-button", "hideAllTabsButton", true);
 
     if (this._tabmixCustomizeToolbar) {
       delete this._tabmixCustomizeToolbar;
diff --git a/chrome/content/preferences/appearance.xul b/chrome/content/preferences/appearance.xul
index 6458dd9..b98a281 100644
--- a/chrome/content/preferences/appearance.xul
+++ b/chrome/content/preferences/appearance.xul
@@ -150,7 +150,7 @@
               <textbox id="maxrow" size="2" maxlength="2" preference="pref_maxrow" type="number" min="2"/>
             </hbox>
             <checkbox_tmp id="offsetAmountToScroll" label="&offsetAmountToScroll.label;"
-                      preference="pref_offsetAmountToScroll" style="height: 22px; margin: 2px;"/>
+                      preference="pref_offsetAmountToScroll" style="height: 26px;"/>
             <checkbox_tmp id="smoothScroll" label="&smoothScroll.label;" preference="pref_smoothScroll"/>
             <hbox align="center" id="clickToScroll.scrollDelay">
               <label value="&scrolldelay.label;" TSTdisabled="true" observes="obs_smoothScroll"/>
diff --git a/chrome/content/preferences/mouse.js b/chrome/content/preferences/mouse.js
index 8e4e8a5..12f3e07 100644
--- a/chrome/content/preferences/mouse.js
+++ b/chrome/content/preferences/mouse.js
@@ -33,6 +33,7 @@ var gMousePane = {
     this.clickTabbar = $("ClickTabbar");
     this.clickTabbar.appendChild(this.clickTab.firstChild.cloneNode(true));
     this.updatePanelPrefs($("tabclick").selectedIndex);
+    this.updateBroadcaster($("pref_tabbarscrolling").value);
 
     gPrefWindow.initPane("paneMouse");
   },
@@ -64,6 +65,10 @@ var gMousePane = {
   ensureElementIsVisible: function (aPopup) {
     var scrollBox = document.getAnonymousElementByAttribute(aPopup, "class", "popup-internal-box");
     scrollBox.ensureElementIsVisible(aPopup.parentNode.selectedItem);
+  },
+
+  updateBroadcaster: function (val) {
+    Tabmix.setItem('obs_tabbarscrolling', 'disabled', val == 2 || null);
   }
 
 }
diff --git a/chrome/content/preferences/mouse.xul b/chrome/content/preferences/mouse.xul
index 1f2dfe8..0360772 100644
--- a/chrome/content/preferences/mouse.xul
+++ b/chrome/content/preferences/mouse.xul
@@ -28,7 +28,8 @@
                   name="extensions.tabmix.mouseOverSelectDelay" type="int"/>
       <preference id="pref_tabFlip"           name="extensions.tabmix.tabFlip"            type="bool"/>
       <preference id="pref_tabFlipDelay"      name="extensions.tabmix.tabFlipDelay"       type="int"/>
-      <preference id="pref_tabbarscrolling"   name="extensions.tabmix.enableScrollSwitch" type="bool"/>
+      <preference id="pref_tabbarscrolling"   name="extensions.tabmix.scrollTabs"         type="int"
+                  onchange="gMousePane.updateBroadcaster(this.value)"/>
       <preference id="pref_reversescrolling"  name="extensions.tabmix.reversedScroll"     type="bool"/>
       <preference id="pref_mouseDownSelect"   name="extensions.tabmix.selectTabOnMouseDown"
                   type="bool" inverted="true"/>
@@ -98,16 +99,25 @@
           </groupbox>
           <!-- Tabbar Scrolling -->
           <groupbox flex="1">
-            <caption label="&tabbarscrolling.caption;"/>
+            <caption>
+               <checkbox_tmp id="enableTabsScroll" label="&tabbarscrolling.caption;"
+                             preference="pref_tabbarscrolling"
+                             onsyncfrompreference="return $('pref_tabbarscrolling').value != 2"
+                             onsynctopreference="var val = this.checked ? this.tabbarscrolling : 2;
+                                this.tabbarscrolling = $('tabbarscrolling').value;
+                                return val;"/>
+            </caption>
             <separator class="thin"/>
-            <label value="&tabbarscrolling.holdShift.label;"/>
-            <radiogroup id="tabbarscrolling" boolean="true" orient="horizontal"
+            <label value="&tabbarscrolling.holdShift.label;" observes="obs_tabbarscrolling"/>
+            <radiogroup id="tabbarscrolling" orient="horizontal"
                         preference="pref_tabbarscrolling">
-              <radio value="true" id="tabbarscrolling-select" label="&tabbarscrolling.selectTab.label;"/>
-              <radio value="false" id="tabbarscrolling-default" label="&tabbarscrolling.scrollAllTabs.label;"/>
+              <radio value="1" id="tabbarscrolling-select" label="&tabbarscrolling.selectTab.label;"
+                     observes="obs_tabbarscrolling"/>
+              <radio value="0" id="tabbarscrolling-default" label="&tabbarscrolling.scrollAllTabs.label;"
+                     observes="obs_tabbarscrolling"/>
             </radiogroup>
             <checkbox_tmp id="reversescrolling" label="&tabbarscrolling.inverse.label;"
-                      preference="pref_reversescrolling"/>
+                      preference="pref_reversescrolling" observes="obs_tabbarscrolling"/>
           </groupbox>
         </tabpanel>
         <tabpanel align="start">
@@ -188,5 +198,9 @@
       <broadcaster id="obs_tabFlip"/>
     </broadcasterset>
 
+    <broadcasterset>
+      <broadcaster id="obs_tabbarscrolling"/>
+    </broadcasterset>
+
   </prefpane>
 </overlay>
diff --git a/chrome/content/preferences/session.js b/chrome/content/preferences/session.js
index bf9dd22..b237ca9 100644
--- a/chrome/content/preferences/session.js
+++ b/chrome/content/preferences/session.js
@@ -71,6 +71,7 @@ var gSessionPane = {
       updatePrefs("browserStartupPage", useSessionManager ? 1 : 3);
     }
 
+    TabmixSvc.sm.settingPreference = true;
     if (useSessionManager) {
       sessionstorePrefs();
       sessionPrefs();
@@ -79,6 +80,7 @@ var gSessionPane = {
       sessionPrefs();
       sessionstorePrefs()
     }
+    TabmixSvc.sm.settingPreference = false;
 
     if (instantApply)
       Services.prefs.savePrefFile(null);
diff --git a/chrome/content/preferences/shortcuts.xml b/chrome/content/preferences/shortcuts.xml
index 2d5841d..ab02459 100644
--- a/chrome/content/preferences/shortcuts.xml
+++ b/chrome/content/preferences/shortcuts.xml
@@ -133,7 +133,7 @@
 
           if (!key.modifiers) {
             let ns = Ci.nsIDOMKeyEvent;
-            // Retuen and Esc blur the edit box
+            // Return and Esc blur the edit box
             if (event.keyCode == ns.DOM_VK_RETURN ||
                 event.keyCode == ns.DOM_VK_ESCAPE) {
               this.editBox.blur();
diff --git a/chrome/content/session/session.js b/chrome/content/session/session.js
index d4cfbc4..f6674fe 100644
--- a/chrome/content/session/session.js
+++ b/chrome/content/session/session.js
@@ -335,7 +335,7 @@ var TabmixSessionManager = {
         SessionStore.promiseInitialized.then(initializeSM);
       }
       else {
-        let forceInit = !Tabmix.isVersion(250) && this.doRestore
+        let forceInit = !Tabmix.isVersion(250) && this.doRestore;
         // make sure sessionstore initialize without restoring pinned tabs
         // for Firefox 25+ we block new tab in gbrowser.addtab
         if (forceInit)
@@ -446,8 +446,10 @@ var TabmixSessionManager = {
             return;
          }
 
-         if (Tabmix.isWindowAfterSessionRestore)
-            setTimeout(function(){this.onSessionRestored()}.bind(this), 0);
+         if (Tabmix.isWindowAfterSessionRestore) {
+            let self = this;
+            setTimeout(function(){self.onSessionRestored()}, 0);
+         }
          else {
            // remove extra tab that was opened by SessionStore if last session
            // contained pinned tab(s).
@@ -456,7 +458,8 @@ var TabmixSessionManager = {
               this.resetTab(tab);
               gBrowser.removeTab(tab);
               try {
-                 TabmixSvc.ss.forgetClosedTab(window, 0);
+                 if (TMP_ClosedTabs.count)
+                   TabmixSvc.ss.forgetClosedTab(window, 0);
               } catch(ex) {}
            }
            if (TabmixSvc.sm.crashed && this.enableBackup)
@@ -1174,10 +1177,11 @@ if (container == "error") { Tabmix.log("wrapContainer error path " + path + "\n"
                this.removeAllClosedWindows();
                this.saveState();
             }
+            let slef = this;
             setTimeout(function(){
               TMP_ClosedTabs.setButtonDisableState();
-              this.toggleRecentlyClosedWindowsButton();
-            }.bind(this), 0);
+              slef.toggleRecentlyClosedWindowsButton();
+            }, 0);
             break;
          case "private-browsing-change-granted":
             // Whether we restore the session upon resume will be determined by the
@@ -1495,7 +1499,7 @@ if (container == "error") { Tabmix.log("wrapContainer error path " + path + "\n"
 
    isValidtoSave: function() {
       if ( !this.enableManager ) return false;
-      if (gBrowser.isBlankWindow()) {
+      if (!this.isWindowValidtoSave()) {
          var title = TabmixSvc.getSMString("sm.title");
          var msg = TabmixSvc.getSMString("sm.dontSaveBlank.msg");
          var buttons = ["", TabmixSvc.setLabel("sm.button.continue")].join("\n");
@@ -1505,6 +1509,13 @@ if (container == "error") { Tabmix.log("wrapContainer error path " + path + "\n"
       return true;
    },
 
+   isWindowValidtoSave: function() {
+     if (gBrowser.isBlankWindow())
+       return false;
+      return typeof privateTab != "object" ||
+        Array.some(gBrowser.tabs, function(tab) !privateTab.isTabPrivate(tab));
+   },
+
    saveOneOrAll: function(action, path, saveClosedTabs) {
       if (this.isPrivateWindow)
         return false;
@@ -2474,7 +2485,7 @@ try{
 
    saveOneWindow: function SM_saveOneWindow(path, caller, overwriteWindow, saveClosedTabs) {
       // don't save private window or window without any tab
-      if (this.isPrivateWindow || gBrowser.isBlankWindow())
+      if (this.isPrivateWindow || !this.isWindowValidtoSave())
         return 0;
       if (!path) path = this.gSessionPath[0];
       if (!caller) caller = "";
@@ -2571,7 +2582,8 @@ try{
    // xxx need to fix this to save only history, image and history index
    // and save the rest when tab added
    tabLoaded: function SM_tabLoaded(aTab) {
-      if (!this._inited || !this.enableBackup || aTab.hasAttribute("inrestore"))
+      if (!this._inited || !this.enableBackup ||
+           aTab.hasAttribute("inrestore") || this.isTabPrivate(aTab))
         return;
       if (gBrowser.isBlankTab(aTab)) return;
       // if this window is not in the container add it to the last place
@@ -2587,7 +2599,7 @@ try{
       if (!add0_1) add0_1 = 0;
       for (var i = aTab._tPos + add0_1; i < gBrowser.tabs.length; i++) {
          tab = gBrowser.tabs[i];
-         node = (typeof(label) == "undefined") ? this.getNodeForTab(tab) : label + "/" + tab.linkedPanel;
+         node = (typeof(label) != "string") ? this.getNodeForTab(tab) : label + "/" + tab.linkedPanel;
          this.setIntLiteral(node, "tabPos", tab._tPos);
       }
    },
@@ -2600,8 +2612,10 @@ try{
       var tabContainer = this.initContainer(this.gThisWinTabs);
       var panelPath = this.getNodeForTab(aTab);
       var nodeToClose = this.RDFService.GetResource(panelPath);
-      this.updateTabPos(aTab); // update _tPos for the tab right to the deleted tab
-      if (this.saveClosedtabs) {
+      var privateTab = this.isTabPrivate(aTab);
+      // update _tPos for the tab right to the deleted tab
+      this.updateTabPos(aTab, null, privateTab ? 1 : 0);
+      if (!privateTab && this.saveClosedtabs) {
          // move closedtabs to closedtabs container
          var closedTabContainer = this.initContainer(this.gThisWinClosedtabs);
          var tabExist = true;
@@ -2630,15 +2644,18 @@ try{
       // we dont need this function to run before sessionmanager init
       if (!this._inited || !this.enableBackup || aTab.hasAttribute("inrestore"))
         return;
-      if (gBrowser.isBlankTab(aTab))
-        return; // dont write blank tab to the file
+      // dont write private or blank tab to the file
+      if (this.isTabPrivate(aTab) || gBrowser.isBlankTab(aTab))
+        return;
       this.initSession(this.gSessionPath[0], this.gThisWin);
       this.setLiteral(this.getNodeForTab(aTab), "properties", TabmixSessionData.getTabProperties(aTab, true));
       this.saveStateDelayed();
    },
 
    tabMoved: function SM_tabMoved(aTab, oldPos, newPos) {
-      if (!this.enableBackup || aTab.hasAttribute("inrestore")) return;
+      if (!this.enableBackup || aTab.hasAttribute("inrestore") ||
+            this.isTabPrivate(aTab))
+         return;
       this.initSession(this.gSessionPath[0], this.gThisWin);
       // can't use aTab._tPos after group of tab delete
       // we pass old position and new position from TabMove event
@@ -2661,7 +2678,9 @@ try{
 
    // xxx need to find the right event to trigger this function..
    tabScrolled: function SM_tabScrolled(aTab) {
-      if (!this.enableBackup || aTab.hasAttribute("inrestore")) return;
+      if (!this.enableBackup || aTab.hasAttribute("inrestore") ||
+            this.isTabPrivate(aTab))
+         return;
       var aBrowser = gBrowser.getBrowserForTab(aTab);
       if (gBrowser.isBlankBrowser(aBrowser)) return;
       var bContent = aBrowser.contentWindow;
@@ -2670,7 +2689,11 @@ try{
    },
 
    tabSelected: function(needFlush) {
-      if (!this.enableBackup || gBrowser.mCurrentTab.hasAttribute("inrestore")) return;
+      if (!this.enableBackup)
+         return;
+      let tab = gBrowser.mCurrentTab;
+      if (tab.hasAttribute("inrestore") || this.isTabPrivate(tab))
+         return;
       if (typeof(needFlush) == "undefined") needFlush = false;
       this.initSession(this.gSessionPath[0], this.gThisWin);
       this.setTabsScroll(); // until i find proper event to update tab scroll do it from here
@@ -2694,6 +2717,22 @@ try{
       return this.gThisWinTabs + "/" + aTab.linkedPanel;
    },
 
+   isTabPrivate: function(aTab) {
+     return typeof privateTab == "object" && privateTab.isTabPrivate(aTab);
+   },
+
+   privateTabChanged: function(aEvent) {
+     if (!this.enableBackup || typeof privateTab != "object")
+        return;
+
+     // aEvent.detail: 1 == private, 0 == non-private
+     let tab = aEvent.target;
+     if (aEvent.detail)
+        this.tabClosed(tab);
+     else
+        this.tabLoaded(tab);
+      },
+
    saveAllTab: function SM_saveAllTab(winPath, offset, saveBusy) {
       var savedTabs = 0 ;
       var rdfNodeTabs = this.getResource(winPath, "tabs");
@@ -2712,6 +2751,8 @@ try{
    // call from tabloaded, tabClosed, saveAllTab
 // xxx add flag what to save : all, history, property, scrollPosition
    saveTab: function SM_saveTab(aTab, rdfLabelTabs, tabContainer, needToAppend, offset) {
+      if (this.isTabPrivate(aTab))
+         return false;
       var aBrowser = gBrowser.getBrowserForTab(aTab);
       if (gBrowser.isBlankBrowser(aBrowser)) return false;
 
@@ -2752,6 +2793,9 @@ try{
    },
 
    saveTabviewTab: function SM_saveTabviewTab(aNode, aTab) {
+      if (!this.enableBackup || aTab.hasAttribute("inrestore") ||
+            this.isTabPrivate(aTab))
+         return;
       let data = TabmixSessionData.getTabValue(aTab, "tabview-tab");
       if (data != "" && data != "{}")
         this.setLiteral(aNode, "tabview-tab", data);
@@ -2932,7 +2976,8 @@ try{
       var features = "chrome,all,dialog=no";
       if (Tabmix.isVersion(200))
          features += aPrivate ? ",private" : ",non-private";
-      var newWindow = window.openDialog( getBrowserURL(), "_blank", features, null);
+      var newWindow = window.openDialog("chrome://browser/content/browser.xul",
+          "_blank", features, null);
       newWindow.tabmixdata = { path: aPath, caller: aCaller };
    },
 
diff --git a/chrome/content/session/sessionStore.js b/chrome/content/session/sessionStore.js
index 58cd1fd..855ee84 100644
--- a/chrome/content/session/sessionStore.js
+++ b/chrome/content/session/sessionStore.js
@@ -51,8 +51,7 @@ var TMP_SessionStore = {
 
    getTitleFromTabState: function(aTab) {
      let tabData = TabmixSvc.JSON.parse(TabmixSvc.ss.getTabState(aTab));
-     let activePageData = this.getActiveEntryData(tabData);
-     return activePageData.title || activePageData.url;
+     return this.getActiveEntryData(tabData).title;
    },
 
    /**
@@ -99,8 +98,8 @@ var TMP_SessionStore = {
     *
     * @returns       Nothing.
     */
-   setService: function TMP_ss_setSessionService(msgNo, start, win) {
-      if ("tabmix_setSession" in window || Tabmix.prefs.prefHasUserValue("setDefault"))
+   setService: function TMP_ss_setSessionService(msgNo, start) {
+      if (TabmixSvc.sm.settingPreference || Tabmix.prefs.prefHasUserValue("setDefault"))
          return;
      /*
       * From 2008-03-10 we don't set browser.sessionstore.enabled to false anymore
@@ -120,7 +119,7 @@ var TMP_SessionStore = {
          return;
       }
 
-      window.tabmix_setSession = true;
+      TabmixSvc.sm.settingPreference = true;
       // if session manager extension is install disable TMP session manager
       if (msgNo == -1 || Tabmix.extensions.sessionManager) {
          // update session manager settings accourding to current tabmix settings
@@ -151,7 +150,7 @@ var TMP_SessionStore = {
             Services.prefs.setBoolPref(TMP_SS_CRASHRECOVERY, false);
             Services.prefs.setBoolPref("browser.sessionstore.resume_from_crash", true);
          }
-         delete window.tabmix_setSession;
+         TabmixSvc.sm.settingPreference = false;
       }
       else if (this.isSessionStoreEnabled()) {
          // ask the user to choose between TMP session manager and sessionstore
@@ -181,10 +180,10 @@ var TMP_SessionStore = {
               Services.prefs.setBoolPref(TMP_SS_MANAGER, false);
               Services.prefs.setBoolPref(TMP_SS_CRASHRECOVERY, false);
            }
-           delete window.tabmix_setSession;
+           TabmixSvc.sm.settingPreference = false;
          }
          let result = Tabmix.promptService([Tabmix.BUTTON_OK, Tabmix.HIDE_MENUANDTEXT, Tabmix.HIDE_CHECKBOX],
-               [title, msg, "", "", buttons], win || window, start ? callBack : null);
+               [title, msg, "", "", buttons], window, start ? callBack : null);
          if (!start)
            callBack(result);
       }
@@ -194,7 +193,7 @@ var TMP_SessionStore = {
          if (!Tabmix.isVersion(200))
            Services.prefs.setBoolPref("browser.warnOnRestart", false);
          Services.prefs.setBoolPref("browser.warnOnQuit", false);
-         delete window.tabmix_setSession;
+         TabmixSvc.sm.settingPreference = false;
       }
    },
 
@@ -309,14 +308,14 @@ var TMP_ClosedTabs = {
     * Get closed tabs count
     */
    get count() {
-      return TabmixSvc.ss.getClosedTabCount(window);
+      return window.__SSi ? TabmixSvc.ss.getClosedTabCount(window) : 0;
    },
 
   /**
     * Get closed tabs data
     */
    get getClosedTabData() {
-      return TabmixSvc.JSON.parse(TabmixSvc.ss.getClosedTabData(window));
+      return window.__SSi ? TabmixSvc.JSON.parse(TabmixSvc.ss.getClosedTabData(window)) : {};
    },
 
    getUrl: function ct_getUrl(aTabData) {
@@ -454,7 +453,7 @@ var TMP_ClosedTabs = {
       }
 
       // Reset the number of tabs closed last time to the default.
-      gBrowser.setNumberOfTabsClosedLast(1);
+      Tabmix.setNumberOfTabsClosedLast(1);
    },
 
    removeAllClosedTabs: function () {
@@ -576,7 +575,7 @@ var TMP_ClosedTabs = {
       let index = Number(aIndex);
       if (isNaN(index)) {
         index = 0;
-        if (Tabmix.isVersion(250))
+        if (Tabmix._restoreMultipleTabs)
           numberOfTabsToUndoClose = TabmixSvc.ss.getNumberOfTabsClosedLast(window);
       } else if (0 > index || index >= this.count)
         return null;
@@ -588,7 +587,7 @@ var TMP_ClosedTabs = {
         tab = this.SSS_undoCloseTab(index, aWhere || "original", true);
 
       // Reset the number of tabs closed last time to the default.
-      gBrowser.setNumberOfTabsClosedLast(1);
+      Tabmix.setNumberOfTabsClosedLast(1);
       return tab;
    }
 
diff --git a/chrome/content/tab/scrollbox.xml b/chrome/content/tab/scrollbox.xml
index e38e9ef..7f358e0 100644
--- a/chrome/content/tab/scrollbox.xml
+++ b/chrome/content/tab/scrollbox.xml
@@ -427,7 +427,7 @@
         }
 
         var tabs = document.getBindingParent(this);
-        if (tabs.hasAttribute("multibar")) {
+        if (tabs.hasAttribute("multibar") && tabs.overflow) {
           //XXX don't do anything on Linux when hovering last tab and
           // we show close button on tab on hover
           if (!Tabmix.isPlatform("Linux") || TabmixTabbar.visibleRows == 1 ||
diff --git a/chrome/content/tab/tab.js b/chrome/content/tab/tab.js
index eee14c6..49593fd 100644
--- a/chrome/content/tab/tab.js
+++ b/chrome/content/tab/tab.js
@@ -17,6 +17,16 @@ var TabmixTabbar = {
     return gBrowser.tabContainer.getAttribute("flowing") == "multibar";
   },
 
+  isButtonOnTabsToolBar: function(button) {
+    return button && button.parentNode == gBrowser.tabContainer._container;
+  },
+
+  // get privateTab-toolbar-openNewPrivateTab, when the button is on the tabbar
+  newPrivateTabButton: function() {
+    let button = document.getElementById("privateTab-toolbar-openNewPrivateTab");
+    return this.isButtonOnTabsToolBar(button) ? button : null;
+  },
+
   updateSettings: function TMP_updateSettings(start) {
     if (!gBrowser || Tabmix.prefs.prefHasUserValue("setDefault"))
       return;
@@ -132,20 +142,14 @@ var TabmixTabbar = {
       tabBar.adjustTabstrip(true);
 
     // show on tabbar
-    var showNewTabButton = Tabmix.prefs.getBoolPref("newTabButton");
-    let toolBar = gBrowser.tabContainer._container;
     let tabstripClosebutton = document.getElementById("tabs-closebutton");
-    if (tabstripClosebutton && tabstripClosebutton.parentNode == toolBar)
+    if (this.isButtonOnTabsToolBar(tabstripClosebutton))
       tabstripClosebutton.collapsed = Tabmix.prefs.getBoolPref("hideTabBarButton");
     let allTabsButton = document.getElementById("alltabs-button");
-    if (allTabsButton && allTabsButton.parentNode == toolBar)
+    if (this.isButtonOnTabsToolBar(allTabsButton))
       allTabsButton.collapsed = Tabmix.prefs.getBoolPref("hideAllTabsButton");
-
-    let newTabButton = document.getElementById("new-tab-button");
-    showNewTabButton =  showNewTabButton && newTabButton && newTabButton.parentNode == toolBar;
-    Tabmix.setItem("TabsToolbar", "newTabButton", showNewTabButton || false);
     Tabmix.setItem(tabBar, "tabBarSpace", Tabmix.prefs.getBoolPref("tabBarSpace") || null);
-    tabBar._checkNewtabButtonVisibility = isMultiRow && showNewTabButton && Tabmix.prefs.getIntPref("newTabButton.position") == 2;
+    this.setShowNewTabButtonAttr();
 
     var self = this;
     if (start)
@@ -160,6 +164,21 @@ var TabmixTabbar = {
     }, 50, currentVisible);
   },
 
+  setShowNewTabButtonAttr: function() {
+    let newTabButton = document.getElementById("new-tab-button");
+    let showNewTabButton = Tabmix.prefs.getBoolPref("newTabButton") &&
+        this.isButtonOnTabsToolBar(newTabButton);
+    let position = Tabmix.prefs.getIntPref("newTabButton.position");
+    gTMPprefObserver.setShowNewTabButtonAttr(showNewTabButton, position);
+  },
+
+  showNewTabButtonOnSide: function(aCondition, aValue) {
+    if (Tabmix._show_newtabbutton) {
+      Tabmix.setItem("TabsToolbar", "tabmix-show-newtabbutton",
+        aCondition ? aValue : Tabmix._show_newtabbutton);
+    }
+  },
+
   setScrollButtonBox: function TMP_setScrollButtonBox(useTabmixButtons, insertAfterTabs, update) {
     let newBox, box = document.getElementById("tabmixScrollBox");
     if (useTabmixButtons && !box) {
@@ -873,8 +892,16 @@ var gTMPprefObserver = {
   },
 
   dynamicRules: {},
+  insertRule: function(cssText, name) {
+    let index = this.tabStyleSheet.insertRule(cssText,
+        this.tabStyleSheet.cssRules.length);
+    if (name)
+      this.dynamicRules[name] = this.tabStyleSheet.cssRules[index];
+    return index;
+  },
+
   createColorRules: function TMP_PO_createColorRules() {
-    var bottomBorder, ss = this.tabStyleSheet;
+    var bottomBorder;
     this.gradients = { };
     if (Tabmix.isVersion(160)) {
       this.gradients.body = "linear-gradient(#colorCode, #colorCode)";
@@ -946,39 +973,30 @@ var gTMPprefObserver = {
         "{background-image: " + this.gradients.body + " !important;}";
     }
 
-    // Charter Toolbar extension add Object.prototype.toJSONString
-    // that break the use      "for (var rule in styleRules)"
-    var rules = ["currentTab", "unloadedTab", "unreadTab", "otherTab", "progressMeter"];
-    for (let j = 0; j < rules.length; j++) {
-      let rule = rules[j];
+    for (let rule in Iterator(styleRules, true)) {
       this.setTabStyles("extensions.tabmix.styles." + rule, true);
       var prefValues = this.tabStylePrefs[rule];
       if (!prefValues)
         continue;
 
-      var newRule, index;
       if (rule !=  "progressMeter") {
-        newRule = styleRules[rule].text.replace("#colorCode",prefValues.textColor);
-        index = ss.insertRule(newRule, ss.cssRules.length);
-        this.dynamicRules[rule] = ss.cssRules[index];
+        let newRule = styleRules[rule].text.replace("#colorCode",prefValues.textColor);
+        this.insertRule(newRule, rule);
       }
-      newRule = styleRules[rule].bg.replace(/#colorCode/g,prefValues.bgColor);
-      index = ss.insertRule(newRule, ss.cssRules.length);
-      this.dynamicRules[rule + "bg"] = ss.cssRules[index];
+      let newRule = styleRules[rule].bg.replace(/#colorCode/g,prefValues.bgColor);
+      this.insertRule(newRule, rule + "bg");
       if (rule != "progressMeter")
         this.toggleTabStyles(rule);
     }
     if ("bgTabsontop" in styleRules.currentTab) {
       // bottom border for selected tab on top is diffrent
       let newRule = styleRules.currentTab.bgTabsontop.replace(/#colorCode/g, this.tabStylePrefs["currentTab"].bgColor);
-      let index = ss.insertRule(newRule, ss.cssRules.length);
-      this.dynamicRules["currentTab" + "bgTabsontop"] = ss.cssRules[index];
+      this.insertRule(newRule, "currentTab" + "bgTabsontop");
     }
 
     // rule for controling moz-margin-start when we have pinned tab in multi-row
     let marginStart = '#tabbrowser-tabs[positionpinnedtabs] > .tabbrowser-tab[tabmix-firstTabInRow="true"]{-moz-margin-start: 0px;}';
-    let index = ss.insertRule(marginStart, ss.cssRules.length);
-    this.dynamicRules["tabmix-firstTabInRow"] = ss.cssRules[index];
+    this.insertRule(marginStart, "tabmix-firstTabInRow");
 
     // rule for progress-bar background-image
     // move it back to css file when we drop support for Firefox 11-15
@@ -993,7 +1011,7 @@ var gTMPprefObserver = {
       backgroundImage = backgroundImage.replace("#2", Tabmix.ltr ? "left" : "rigth").
           replace("linear-gradient", "-moz-linear-gradient");
     }
-    ss.insertRule(backgroundImage, ss.cssRules.length);
+    this.insertRule(backgroundImage);
 
     // for ColorfulTabs 8.0+
     // add new rule to adjust selected tab bottom margin
@@ -1002,8 +1020,8 @@ var gTMPprefObserver = {
       let padding = Tabmix.getStyle(gBrowser.tabs[0], "paddingBottom");
       let newRule = '#tabbrowser-tabs[flowing="multibar"] > .tabbrowser-tab[selected=true]' +
                     ' {margin-bottom: -1px !important; padding-bottom: ' + (padding + 1) + 'px !important;}';
-      let index = ss.insertRule(newRule, ss.cssRules.length);
-      newRule = ss.cssRules[index];
+      let index = this.insertRule(newRule);
+      newRule = this._tabStyleSheet.cssRules[index];
       gBrowser.tabContainer.addEventListener("TabOpen", function TMP_addStyleRule(aEvent) {
         gBrowser.tabContainer.removeEventListener("TabOpen", TMP_addStyleRule, true);
         let padding = Tabmix.getStyle(aEvent.target, "paddingBottom");
@@ -1013,7 +1031,6 @@ var gTMPprefObserver = {
   },
 
   setTabIconMargin: function TMP_PO_setTabIconMargin() {
-    var ss = this.tabStyleSheet;
     var [sMarginStart, sMarginEnd] = Tabmix.rtl ? ["margin-right", "margin-left"] : ["margin-left", "margin-right"];
     var icon = document.getAnonymousElementByAttribute(gBrowser.mCurrentTab, "class", "tab-icon-image");
     if (!icon)
@@ -1038,7 +1055,7 @@ var gTMPprefObserver = {
                            selector + '.tab-reload-icon,' +
                            selector + '.tab-lock-icon {' +
                            '-moz-margin-start: %S; -moz-margin-end: %S;}'.replace("%S", marginStart).replace("%S", marginEnd);
-    ss.insertRule(iconRule, ss.cssRules.length);
+    this.insertRule(iconRule);
     icon.setAttribute("pinned", true);
     let _marginStart = style.getPropertyValue(sMarginStart);
     let _marginEnd = style.getPropertyValue(sMarginEnd);
@@ -1047,7 +1064,7 @@ var gTMPprefObserver = {
                          _selector + '.tab-reload-icon,' +
                          _selector + '.tab-lock-icon {' +
                          '-moz-margin-start: %S; -moz-margin-end: %S;}'.replace("%S", _marginStart).replace("%S", _marginEnd);
-    ss.insertRule(_iconRule, ss.cssRules.length);
+    this.insertRule(_iconRule);
     if (!pinned)
       icon.removeAttribute("pinned");
 
@@ -1060,9 +1077,9 @@ var gTMPprefObserver = {
 
     function tabmix_setRule(aRule) {
       let newRule = aRule.replace(/%S/g, "tab-icon-image").replace("%PX", marginEnd);
-      ss.insertRule(newRule, ss.cssRules.length);
+      this.insertRule(newRule);
       newRule = aRule.replace(/%S/g, "tab-lock-icon").replace("%PX", marginEnd);
-      ss.insertRule(newRule, ss.cssRules.length);
+      this.insertRule(newRule);
     }
     iconRule = '.tabbrowser-tabs%favhideclose%[closebuttons-side="left"][closebuttons="alltabs"] > .tabbrowser-tab:not([pinned]):not([protected])%faviconized% .%S ,' +
                       '.tabbrowser-tabs%favhideclose%[closebuttons-side="left"][closebuttons="activetab"] > .tabbrowser-tab:not([pinned]):not([protected])[selected="true"]%faviconized% .%S {'+
@@ -1080,7 +1097,6 @@ var gTMPprefObserver = {
   },
 
   setCloseButtonMargin: function TMP_PO_setCloseButtonMargin() {
-    var ss = this.tabStyleSheet;
     var [sMarginStart, sMarginEnd] = Tabmix.rtl ? ["margin-right", "margin-left"] : ["margin-left", "margin-right"];
     var icon = document.getAnonymousElementByAttribute(gBrowser.mCurrentTab, "button_side", "right") ||
                document.getAnonymousElementByAttribute(gBrowser.mCurrentTab, "class", "tab-close-button close-icon always-right");
@@ -1095,7 +1111,7 @@ var gTMPprefObserver = {
       let newRule = '.tab-close-button[button_side="left"] {' +
                     '-moz-margin-start: %PX !important;'.replace("%PX", marginEnd) +
                     '-moz-margin-end: %PX !important;}'.replace("%PX", marginStart);
-      ss.insertRule(newRule, ss.cssRules.length);
+      this.insertRule(newRule);
     }
     // set right margin to text stack when close button is not right to it
     // on default theme the margin is zero, so we set the end margin to be the same as the start margin
@@ -1108,25 +1124,32 @@ var gTMPprefObserver = {
                             '-moz-margin-end: %PX !important;}'.replace("%PX", textMarginEnd);
     if ("faviconize" in window) {
       let newRule = iconRule.replace(/%favhideclose%/g, ':not([favhideclose="true"])').replace(/%faviconized%/g, '').replace(/%faviconized1%/g, ':not([faviconized="true"])');
-      ss.insertRule(newRule, ss.cssRules.length);
+      this.insertRule(newRule);
       newRule = iconRule.replace(/%favhideclose%/g, '[favhideclose="true"]').replace(/%faviconized%/g, ':not([faviconized="true"])').replace(/%faviconized1%/g, ':not([faviconized="true"])');
-      ss.insertRule(newRule, ss.cssRules.length);
+      this.insertRule(newRule);
       newRule = '.tabbrowser-tab[faviconized="true"][protected]:not([pinned]) {max-width: 36px !important;}';
-      ss.insertRule(newRule, ss.cssRules.length);
+      this.insertRule(newRule);
     }
     else {
       let newRule = iconRule.replace(/%favhideclose%/g, '').replace(/%faviconized%/g, '').replace(/%faviconized1%/g, '');
-      ss.insertRule(newRule, ss.cssRules.length);
+      this.insertRule(newRule);
     }
   },
 
   miscellaneousRules: function TMP_PO_miscellaneousRules() {
     // height shrink to actual size when the tabbar is in display: block (multi-row)
     let newHeight = gBrowser.tabContainer.visibleTabsFirstChild.getBoundingClientRect().height;
-    let newRule = '#TabsToolbar:not([newTabButton=false]):not([disAllowNewtabbutton]):not([newtab_side]) >' +
+    let newRule = '#TabsToolbar[tabmix-show-newtabbutton*="aftertabs"] >' +
                   '#tabbrowser-tabs:not([overflow="true"]) > .tabbrowser-arrowscrollbox[flowing="multibar"]' +
-                  ' > .tabs-newtab-button {height: #px;}'.replace("#", newHeight);
-    this.tabStyleSheet.insertRule(newRule, this.tabStyleSheet.cssRules.length);
+                  ' > .tabs-newtab-button[command="cmd_newNavigatorTab"] {height: #px;}'.replace("#", newHeight);
+    this.insertRule(newRule);
+
+    if (!Tabmix.isPlatform("Mac") && !Tabmix.isPlatform("Linux")) {
+      let newRule = '#TabsToolbar[multibar] > .toolbarbutton-1,' +
+                    '#TabsToolbar[multibar] > #tabs-closebutton {' +
+                    '  height: #px;}'.replace("#", newHeight);
+      this.insertRule(newRule);
+    }
   },
 
   addWidthRules: function TMP_PO_addWidthRules() {
@@ -1135,9 +1158,7 @@ var gTMPprefObserver = {
     let _max = Services.prefs.getIntPref("browser.tabs.tabMaxWidth");
     let _min = Services.prefs.getIntPref("browser.tabs.tabMinWidth");
     newRule = newRule.replace("#1" ,_min).replace("#2" ,_max);
-    let ss = this.tabStyleSheet;
-    let index = ss.insertRule(newRule, ss.cssRules.length);
-    this.dynamicRules["width"] = ss.cssRules[index];
+    this.insertRule(newRule, "width");
   },
 
   defaultStylePrefs: {    currentTab: {italic:false,bold:false,underline:false,text:true,textColor:'rgba(0,0,0,1)',bg:false,bgColor:'rgba(236,233,216,1)'},
@@ -1402,26 +1423,23 @@ var gTMPprefObserver = {
   },
 
   changeNewTabButtonSide: function(aPosition) {
-    var tabBar = gBrowser.tabContainer;
-    tabBar._checkNewtabButtonVisibility = false;
-    let newTabButton = document.getElementById("new-tab-button");
-    if (newTabButton && newTabButton.parentNode == gBrowser.tabContainer._container) {
-      let sideChanged, tabsToolbar = document.getElementById("TabsToolbar");
+    function $(id) document.getElementById(id);
+    let newTabButton = $("new-tab-button");
+    if (TabmixTabbar.isButtonOnTabsToolBar(newTabButton)) {
+      let sideChanged, tabsToolbar = $("TabsToolbar");
       let toolBar = Array.slice(tabsToolbar.childNodes);
       let buttonIndex = toolBar.indexOf(newTabButton);
       let tabsIndex = toolBar.indexOf(gBrowser.tabContainer);
       if (aPosition == 0) {
-        Tabmix.setItem(tabsToolbar, "newtab_side", "left");
         if (buttonIndex > tabsIndex) {
           newTabButton.parentNode.insertBefore(newTabButton, gBrowser.tabContainer);
           sideChanged = true;
         }
       }
       else {
-        Tabmix.setItem(tabsToolbar, "newtab_side", aPosition == 1 ? "right" : null);
         if (buttonIndex < tabsIndex) {
           let before = gBrowser.tabContainer.nextSibling;
-          if (document.getElementById("tabmixScrollBox")) {
+          if ($("tabmixScrollBox")) {
             before = before.nextSibling;
             tabsIndex++;
           }
@@ -1436,16 +1454,54 @@ var gTMPprefObserver = {
         tabsToolbar.setAttribute("currentset", cSet.join(","));
         document.persist("TabsToolbar", "currentset");
       }
-      tabBar._checkNewtabButtonVisibility = TabmixTabbar.isMultiRow && Tabmix.prefs.getBoolPref("newTabButton") && aPosition == 2;
-      Tabmix.setItem("TabsToolbar", "newTabButton", Tabmix.prefs.getBoolPref("newTabButton"));
-      tabBar._rightNewTabButton = newTabButton;
+      let showNewTabButton = Tabmix.prefs.getBoolPref("newTabButton");
+      this.setShowNewTabButtonAttr(showNewTabButton, aPosition);
+      Tabmix.sideNewTabButton = newTabButton;
     }
     else {
-      Tabmix.setItem("TabsToolbar", "newTabButton", false);
-      tabBar._rightNewTabButton = null;
+      this.setShowNewTabButtonAttr(false);
+      Tabmix.sideNewTabButton = null;
     }
   },
 
+  setShowNewTabButtonAttr: function(aShow, aPosition) {
+    // check new tab button visibility when we are in multi-row and the
+    // preference is to show new-tab-button after last tab
+    gBrowser.tabContainer._checkNewtabButtonVisibility =
+                  TabmixTabbar.isMultiRow && ((aShow && aPosition == 2) ||
+                  !!TabmixTabbar.newPrivateTabButton());
+
+   /** values for tabmix-show-newtabbutton to show tabs-newtab-button are:
+    *  aftertabs       - show the button after tabs
+    *  aftertabs-force - force the button after tabs to be visible, only to
+    *                  - make it possible to catch the button width
+    *                   (Tabmix.getAfterTabsButtonsWidth)
+    *  temporary-right-side
+    *                  - show the button on right side when there is no place
+    *                    for the button aftertabs in multi-row mode
+    *  rigth-side      - show the button on right side
+    *  left-side       - show the button on left side
+    */
+    let attrValue;
+    if (!aShow)
+      attrValue = null;
+    else if (aPosition == 0)
+      attrValue = "left-side";
+    else if (aPosition == 1)
+      attrValue = "right-side";
+    else
+      attrValue = "aftertabs";
+    // we use this value in disAllowNewtabbutton and overflow setters
+    Tabmix._show_newtabbutton = attrValue;
+    if (aShow) {
+      if (gBrowser.tabContainer.overflow)
+        attrValue = "right-side";
+      else if (gBrowser.tabContainer.disAllowNewtabbutton)
+        attrValue = "temporary-right-side";
+    }
+    Tabmix.setItem("TabsToolbar", "tabmix-show-newtabbutton", attrValue);
+  },
+
   tabBarPositionChanged: function(aPosition) {
     if (aPosition > 1 || (aPosition != 0 && Tabmix.extensions.verticalTabBar)) {
       Tabmix.prefs.setIntPref("tabBarPosition", 0);
@@ -1792,6 +1848,12 @@ try {
         shortcuts.toggleFLST = "VK_F9";
       Tabmix.prefs.setCharPref("shortcuts", TabmixSvc.JSON.stringify(shortcuts));
     }
+    // 2013-09-04
+    if (Tabmix.prefs.prefHasUserValue("enableScrollSwitch")) {
+      // enableScrollSwitch non-default value was true that is now 1
+      Tabmix.prefs.setIntPref("scrollTabs", 1);
+      Tabmix.prefs.clearUserPref("enableScrollSwitch");
+    }
 
     // verify valid value
     if (Tabmix.prefs.prefHasUserValue("tabs.closeButtons")) {
@@ -1802,8 +1864,7 @@ try {
     // 2011-01-22 - verify sessionstore enabled
     Services.prefs.clearUserPref("browser.sessionstore.enabled");
 
-    let getVersion = function _getVersion(extensions) {
-      let currentVersion = extensions.get("{dc572301-7619-498c-a57d-39143191b318}").version;
+    let getVersion = function _getVersion(currentVersion) {
       let oldVersion = Tabmix.prefs.prefHasUserValue("version") ? Tabmix.prefs.getCharPref("version") : "";
 
       let vCompare = function(a, b) Services.vc.compare(a, b) <= 0;
@@ -1833,13 +1894,12 @@ try {
         // noting more to do at the moment
       }
     }
-    const Application = Cc["@mozilla.org/fuel/application;1"].getService(Ci.fuelIApplication);
-    let wrappedGetVersion = function(extensions) {
+    AddonManager.getAddonByID("{dc572301-7619-498c-a57d-39143191b318}", function(aAddon) {
       try {
-        getVersion(extensions);
+        getVersion(aAddon.version);
       } catch (ex) {Tabmix.assert(ex);}
-    }
-    Application.getExtensions(wrappedGetVersion);
+    })
+
     // block item in tabclicking options that are not in use
     this.blockedValues = [];
     if (!("SessionSaver" in window && window.SessionSaver.snapBackTab))
diff --git a/chrome/content/tab/tabbrowser_4.xml b/chrome/content/tab/tabbrowser_4.xml
index ed0a074..1d64b34 100644
--- a/chrome/content/tab/tabbrowser_4.xml
+++ b/chrome/content/tab/tabbrowser_4.xml
@@ -318,10 +318,6 @@
         document.getAnonymousElementByAttribute(this.mTabstrip._scrollbox, "class", "box-inherit scrollbox-innerbox");
       </field>
 
-      <field name="mTabsNewtabButton">
-        document.getAnonymousElementByAttribute(this, "class", "tabs-newtab-button");
-      </field>
-
       <constructor>
         <![CDATA[
           // add dragover event handler to TabsToolbar to capture dragging over
@@ -364,7 +360,10 @@
            * if we set this in field[s] they will reset each time the tabbar binding construct
            * by tabbar position change
            */
-          this._newTabButtonWidth = 22;
+          Tabmix.afterTabsButtonsWidth = [22];
+          Tabmix.tabsNewtabButton =
+            document.getAnonymousElementByAttribute(this, "command", "cmd_newNavigatorTab");
+          Tabmix._show_newtabbutton = "aftertabs";
 
           let tab = this.firstChild;
           tab.setAttribute("flst_id", new Date().getTime());
@@ -382,12 +381,15 @@
           if (Tabmix.prefs.getBoolPref("extraIcons.locked"))
             this.setAttribute("extraIcons-locked", true);
 
+          if (Tabmix.prefs.prefHasUserValue("enableDebug") &&
+                Tabmix.prefs.getBoolPref("enableDebug")) {
+            Tabmix._debugMode = true;
+          }
+
           if ("linkedBrowser" in tab)
             tablib.setLoadURIWithFlags(tab.linkedBrowser);
 
-          try {
-            Tabmix.beforeStartup(tabbrowser, this);
-          } catch (ex) {Tabmix.assert(ex);}
+          Tabmix.initialization.run("beforeStartup", tabbrowser, this);
         ]]>
       </constructor>
 
@@ -435,6 +437,7 @@
           this._inUpdateVerticalTabStrip = true;
           // we must adjustNewtabButtonvisibility before get lastTabRowNumber
           this.adjustNewtabButtonvisibility();
+          let visibleRows = TabmixTabbar.visibleRows;
           // this.lastTabRowNumber is null when we hide the tabbar
           let rows = aReset || this.childNodes.length == 1 ? 1 : (this.lastTabRowNumber || 1);
 
@@ -468,15 +471,32 @@
           if (this.mTabstrip.orient == "vertical")
             this.overflow = multibar == "scrollbar";
 
-          // prevent new-tab-button on the right from flickering when new tabs animate is on.
-          if (this.disAllowNewtabbutton && Services.prefs.getBoolPref("browser.tabs.animate")) {
-            this.disAllowNewtabbutton = false;
-            let self = this;
-            // after 300ms new tab is fully opened
-            setTimeout(function() {self.adjustNewtabButtonvisibility();}, 300);
+          if (!this.overflow) {
+            // prevent new-tab-button on the right from flickering when new tabs animate is on.
+            if (this.disAllowNewtabbutton && Services.prefs.getBoolPref("browser.tabs.animate")) {
+              // after 250ms new tab is fully opened
+              if (!this.adjustNewtabButtonTimeout) {
+                let timeout = 250, callerName = Tabmix.callerName();
+                if (callerName == "onxbloverflow") {
+                  let timeFromLastTabOpenedTime = Date.now() - Tabmix._lastTabOpenedTime;
+                  if (timeFromLastTabOpenedTime < 250)
+                    timeout = 0;
+                }
+                // Don't reset adjustNewtabButtonvisibility if multibar or rows
+                // didn't changed or when we get here from _enterVerticalMode
+                if (callerName != "_enterVerticalMode" &&
+                     (multibar != currentMultibar || rows != visibleRows))
+                  this.disAllowNewtabbutton = false;
+                let self = this;
+                this.adjustNewtabButtonTimeout = setTimeout(function() {
+                  self.adjustNewtabButtonvisibility();
+                  self.adjustNewtabButtonTimeout = null;
+                }, timeout);
+              }
+            }
+            else
+              this.adjustNewtabButtonvisibility();
           }
-          else
-            this.adjustNewtabButtonvisibility();
 
           this._inUpdateVerticalTabStrip = false;
           return multibar;
@@ -509,7 +529,7 @@
               this.setAttribute("overflow", "true");
             else
               this.removeAttribute("overflow");
-            Tabmix.setItem("TabsToolbar", "tabstripoverflow", val || null);
+            TabmixTabbar.showNewTabButtonOnSide(val, "right-side");
           }
           return val;
         ]]></setter>
@@ -565,22 +585,24 @@
             return;
 
           if (!this._checkNewtabButtonVisibility) {
-            this.disAllowNewtabbutton = this.getAttribute("flowing") == "singlebar" &&
-                Tabmix.prefs.getBoolPref("newTabButton") &&
-                Tabmix.prefs.getIntPref("newTabButton.position") == 2 &&
-                this.overflow;
+            TabmixTabbar.showNewTabButtonOnSide(this.overflow, "right-side");
             return;
           }
 
+          // when Private-tab enabled/disabled we need to reset
+          // tabsNewtabButton and afterTabsButtonsWidth
+          if (!Tabmix.tabsNewtabButton)
+            Tabmix.getAfterTabsButtonsWidth();
+
          var lastTab = this.visibleTabsLastChild;
          // button is visible
          //         A: last tab and the button are in the same row - check if we have room for the button in this row
          //         B: last tab and the button are NOT in the same row  - NG - hide the button
          if (!this.disAllowNewtabbutton) {
-           let sameRow = TabmixTabbar.inSameRow(lastTab, this.mTabsNewtabButton);
+           let sameRow = TabmixTabbar.inSameRow(lastTab, Tabmix.tabsNewtabButton);
            if (sameRow) {
              let tabstripEnd = this.mTabstrip.scrollBoxObject.screenX + this.mTabstrip.scrollBoxObject.width;
-             let buttonEnd = this.mTabsNewtabButton.boxObject.screenX + this.mTabsNewtabButton.boxObject.width
+             let buttonEnd = Tabmix.tabsNewtabButton.boxObject.screenX + Tabmix.tabsNewtabButton.boxObject.width
              this.disAllowNewtabbutton = buttonEnd > tabstripEnd;
            }
            else
@@ -598,10 +620,20 @@
              this.disAllowNewtabbutton = false;
              return;
            }
+
+           // buttons that are not on TabsToolbar or not visible are null
+           let newTabButtonWidth = function(aOnSide) {
+             let width = 0, privatTabButton = TabmixTabbar.newPrivateTabButton();
+             if (privatTabButton)
+               width += aOnSide ? privatTabButton.boxObject.width : Tabmix.afterTabsButtonsWidth[1];
+             if (Tabmix.sideNewTabButton)
+               width += aOnSide ? Tabmix.sideNewTabButton.boxObject.width : Tabmix.afterTabsButtonsWidth[0];
+             return width;
+           }
            let tsbo = this.mTabstrip.scrollBoxObject;
-           let tsboEnd = tsbo.screenX + tsbo.width + (this._rightNewTabButton ? this._rightNewTabButton.boxObject.width: 0);
+           let tsboEnd = tsbo.screenX + tsbo.width + newTabButtonWidth(true);
            if (TabmixTabbar.inSameRow(lastTab, previousTab)) {
-             let buttonEnd = lastTab.boxObject.screenX + lastTab.boxObject.width + this._newTabButtonWidth;
+             let buttonEnd = lastTab.boxObject.screenX + lastTab.boxObject.width + newTabButtonWidth();
              this.disAllowNewtabbutton = buttonEnd > tsboEnd;
              return;
            }
@@ -612,7 +644,7 @@
              if (lastTabEnd > tsboEnd)
                this.disAllowNewtabbutton = false;
              else
-               this.disAllowNewtabbutton = lastTabEnd + this._newTabButtonWidth > tsboEnd;
+               this.disAllowNewtabbutton = lastTabEnd + newTabButtonWidth() > tsboEnd;
              return;
            }
          }
@@ -621,11 +653,10 @@
 
       <property name="disAllowNewtabbutton">
         <getter><![CDATA[
-          return document.getElementById("TabsToolbar").hasAttribute("disAllowNewtabbutton");
+          return document.getElementById("TabsToolbar").getAttribute("tabmix-show-newtabbutton") == "temporary-right-side";
         ]]></getter>
         <setter><![CDATA[
-          if (val != this.disAllowNewtabbutton)
-            Tabmix.setItem("TabsToolbar", "disAllowNewtabbutton", val || null);
+          TabmixTabbar.showNewTabButtonOnSide(val, "temporary-right-side");
           return val;
         ]]></setter>
       </property>
diff --git a/chrome/content/tabmix.js b/chrome/content/tabmix.js
index 7135ea9..afd7e0f 100644
--- a/chrome/content/tabmix.js
+++ b/chrome/content/tabmix.js
@@ -58,10 +58,10 @@ Tabmix.startup = function TMP_startup() {
 Tabmix.beforeSessionStoreInit = function TMP_beforeSessionStoreInit() {
   if (this.isFirstWindow) {
     let tmp = {};
-    Cu.import("resource://tabmixplus/extensions/SessionManagerAddon.jsm", tmp);
+    Cu.import("resource://tabmixplus/extensions/AddonManager.jsm", tmp);
     TMP_SessionStore.setService(1, true);
   }
-  this.getNewTabButtonWidth();
+  this.getAfterTabsButtonsWidth();
   TabmixSessionManager.init();
 }
 
@@ -121,17 +121,34 @@ Tabmix.sessionInitialized = function() {
 // we call this at the start of gBrowserInit._delayedStartup
 // if we call it erlier we get this warning:
 // XUL box for _moz_generated_content_before element contained an inline #text child
-Tabmix.getNewTabButtonWidth = function TMP_getNewTabButtonWidth() {
+Tabmix.getAfterTabsButtonsWidth = function TMP_getAfterTabsButtonsWidth() {
   if (gBrowser.tabContainer.orient == "horizontal") {
     let tabBar = gBrowser.tabContainer;
     let stripIsHidden = TabmixTabbar.hideMode != 0 && !tabBar.visible;
     if (stripIsHidden)
       tabBar.visible = true;
-    this.setItem("TabsToolbar", "tabmix-visible", true);
-    // save mTabsNewtabButton width
+    let tabsToolbar = document.getElementById("TabsToolbar");
+    let showButton = tabsToolbar.getAttribute("tabmix-show-newtabbutton");
+    this.setItem(tabsToolbar, "tabmix-show-newtabbutton", "aftertabs-force");
+    // save tabsNewtabButton width
     let lwtheme = document.getElementById("main-window").getAttribute("lwtheme");
-    tabBar._newTabButtonWidth = lwtheme ? 31 : tabBar.mTabsNewtabButton.getBoundingClientRect().width;
-    this.setItem("TabsToolbar", "tabmix-visible", null);
+    this.tabsNewtabButton =
+      document.getAnonymousElementByAttribute(tabBar, "command", "cmd_newNavigatorTab");
+    let openNewTabRect = this.tabsNewtabButton.getBoundingClientRect();
+    this.afterTabsButtonsWidth = [];
+    this.afterTabsButtonsWidth.push(lwtheme ? 31 : openNewTabRect.width);
+    // when privateTab extension installed add its new tab button width
+    // for the use of adjustNewtabButtonvisibility set tabsNewtabButton to be
+    // the right button
+    let openNewPrivateTab = document.getElementById("privateTab-afterTabs-openNewPrivateTab");
+    if (openNewPrivateTab) {
+      let openNewPrivateTabRect = openNewPrivateTab.getBoundingClientRect();
+      this.afterTabsButtonsWidth.push(openNewPrivateTabRect.width);
+      if (openNewPrivateTabRect.right > openNewTabRect.right)
+        this.tabsNewtabButton = openNewPrivateTab;
+    }
+
+    this.setItem(tabsToolbar, "tabmix-show-newtabbutton", showButton);
     if (stripIsHidden)
       tabBar.visible = false;
   }
@@ -197,14 +214,7 @@ Tabmix.delayedStartup = function TMP_delayedStartup() {
 var TMP_eventListener = {
   init: function TMP_EL_init() {
     window.addEventListener("DOMContentLoaded", this, false);
-  },
-
-  browserDelayedStartupFinished: function TMP_EL_browserDelayedStartupFinished() {
-    // master password dialog can popup before startup when Gmail-manager try to login
-    // it can cause load event to fire late, so we get here before onWindowOpen run
-    if (!TMP_eventListener._windowInitialized)
-      TMP_eventListener.onWindowOpen();
-    Tabmix.delayedStartup();
+    window.addEventListener("load", this, false);
   },
 
   handleEvent: function TMP_EL_handleEvent(aEvent) {
@@ -231,14 +241,8 @@ var TMP_eventListener = {
         this.onTabBarScroll(aEvent);
         break;
       case "DOMContentLoaded":
-        try {
-          this.onContentLoaded(aEvent);
-        } catch (ex) {Tabmix.assert(ex);}
-        break;
       case "load":
-        try {
-          this.onWindowOpen(aEvent);
-        } catch (ex) {Tabmix.assert(ex);}
+        this._onLoad(aEvent.type);
         break;
       case "unload":
         this.onWindowClose(aEvent);
@@ -246,6 +250,9 @@ var TMP_eventListener = {
       case "fullscreen":
         this.onFullScreen(!window.fullScreen);
         break;
+      case "PrivateTab:PrivateChanged":
+        TabmixSessionManager.privateTabChanged(aEvent);
+        break;
     }
   },
 
@@ -257,20 +264,18 @@ var TMP_eventListener = {
     }, handler);
   },
 
- /*
-  *  we use this event to run this code before load event
-  *  until TMP version 0.3.8.3 we used to run this code from Tabmix.beforeStartup
-  *  that called from tabcontainer constractur
-  */
-  onContentLoaded: function TMP_EL_onContentLoaded() {
-    window.removeEventListener("DOMContentLoaded", this, false);
-    // don't load tabmix into undock sidebar opened by ezsidebar extension
-    var wintype = window.document.documentElement.getAttribute("windowtype");
-    if (wintype == "mozilla:sidebar")
-      return;
-
-    window.addEventListener("load", this, false);
+  // ignore non-browser windows
+  _onLoad: function TMP_EL_onContentLoaded(aType) {
+    window.removeEventListener(aType, this, false);
+    let wintype = window.document.documentElement.getAttribute("windowtype");
+    if (wintype == "navigator:browser")
+      Tabmix.initialization.run(aType == "load" ? "onWindowOpen" :
+                                              "onContentLoaded");
+    else if (aType != "load")
+      window.removeEventListener("load", this, false);
+  },
 
+  onContentLoaded: function TMP_EL_onContentLoaded() {
     Tabmix.isFirstWindow = Tabmix.numberOfWindows() == 1;
     Tabmix.isWindowAfterSessionRestore = TMP_SessionStore._isAfterSessionRestored();
 
@@ -286,7 +291,7 @@ var TMP_eventListener = {
       Tabmix.lazy_import(TabmixSessionManager, "_decode", "Decode", "Decode");
     } catch (ex) {Tabmix.assert(ex);}
 
-    this._tabEvents = ["SSTabRestoring",
+    this._tabEvents = ["SSTabRestoring", "PrivateTab:PrivateChanged",
       "TabOpen", "TabClose", "TabSelect", "TabMove", "TabUnpinned"];
     this.toggleEventListener(gBrowser.tabContainer, this._tabEvents, true);
 
@@ -312,6 +317,9 @@ var TMP_eventListener = {
     if ("_update" in TabsInTitlebar) {
       // set option to Prevent double click on Tab-bar from changing window size.
       Tabmix.changeCode(TabsInTitlebar, "TabsInTitlebar._update")._replace(
+        'function $(id)',
+        'let $ = $&', {check: Tabmix._debugMode}
+      )._replace(
         'this._dragBindingAlive',
         '$& && Tabmix.prefs.getBoolPref("dblClickTabbar_changesize")'
       )._replace(
@@ -361,14 +369,7 @@ var TMP_eventListener = {
     }
   },
 
-  _windowInitialized: false,
   onWindowOpen: function TMP_EL_onWindowOpen() {
-    if (this._windowInitialized)
-      return;
-
-    this._windowInitialized = true;
-    window.removeEventListener("load", this, false);
-
     window.addEventListener("unload", this, false);
     window.addEventListener("fullscreen", this, true);
 
@@ -452,7 +453,7 @@ var TMP_eventListener = {
         Tabmix.setItem(tabsToolbar, "classic40", version);
         platform = "xp40";
         // check if australis tab shape is implemented in window (bug 738491)
-        let australis = document.getElementById("tab-clip-path-outer");
+        let australis = document.getElementById("tab-curve-clip-path-start");
         if (australis)
           tabBar.setAttribute("australis", true);
       }
@@ -660,7 +661,9 @@ var TMP_eventListener = {
     }
   },
 
-  updateMultiRow: function () {
+  updateMultiRow: function (aReset) {
+    if (aReset)
+      Tabmix.tabsNewtabButton = null;
     if (TabmixTabbar.isMultiRow) {
       gBrowser.tabContainer.updateVerticalTabStrip();
       gBrowser.tabContainer.setFirstTabInRow();
@@ -671,6 +674,7 @@ var TMP_eventListener = {
   // Function to catch when new tabs are created and update tab icons if needed
   // In addition clicks and doubleclick events are trapped.
   onTabOpen: function TMP_EL_onTabOpen(aEvent) {
+    Tabmix._lastTabOpenedTime = Date.now();
     var tab = aEvent.target;
     this.setTabAttribute(tab);
     TMP_LastTab.tabs = null;
@@ -686,20 +690,22 @@ var TMP_eventListener = {
   // when more the one tabs opened at once
   lastTimeTabOpened: 0,
   onTabOpen_delayUpdateTabBar: function TMP_EL_onTabOpen_delayUpdateTabBar(aTab) {
-    let tabBar = gBrowser.tabContainer;
-    let self = this, newTime = new Date().getTime();
-    if (tabBar.overflow || newTime - this.lastTimeTabOpened > 200) {
+    let newTime = Date.now();
+    if (gBrowser.tabContainer.overflow || newTime - this.lastTimeTabOpened > 200) {
       this.onTabOpen_updateTabBar(aTab);
       this.lastTimeTabOpened = newTime;
     }
-    else if (!tabBar.TMP_onOpenTimeout) {
-      tabBar.TMP_onOpenTimeout = window.setTimeout( function TMP_onOpenTimeout(tab) {
-        if (tabBar.TMP_onOpenTimeout) {
-          clearTimeout(tabBar.TMP_onOpenTimeout);
-          tabBar.TMP_onOpenTimeout = null;
+    else if (!this._onOpenTimeout) {
+      let self = this;
+      let timeout = gBrowser.tabContainer.disAllowNewtabbutton &&
+          Services.prefs.getBoolPref("browser.tabs.animate") ? 0 : 200;
+      this._onOpenTimeout = window.setTimeout( function TMP_onOpenTimeout(tab) {
+        if (self._onOpenTimeout) {
+          clearTimeout(self._onOpenTimeout);
+          self._onOpenTimeout = null;
         }
         self.onTabOpen_updateTabBar(tab);
-      }, 200, aTab);
+      }, timeout, aTab);
     }
   },
 
@@ -760,7 +766,7 @@ var TMP_eventListener = {
     if (updateNow)
       this.onTabClose_updateTabBar(tab);
 
-    gBrowser.countClosedTabs(tab);
+    Tabmix.countClosedTabs(tab);
   },
 
   // TGM extension use it
@@ -799,16 +805,16 @@ var TMP_eventListener = {
     // and mTabstrip.ensureElementIsVisible
     // this class change tab height (by changing the borders)
     if (typeof colorfulTabs == "object" && colorfulTabs.standout &&
-        tab.className.indexOf("standout") == -1) {
+        !tab.classList.contains("standout")) {
       for (let i = 0; i < gBrowser.tabs.length; i++) {
         let _tab = gBrowser.tabs[i];
-        if (_tab.className.indexOf("standout") > -1) {
-          _tab.className = _tab.className.replace(" standout", "");
+        if (_tab.classList.contains("standout")) {
+          _tab.classList.remove("standout");
           break;
         }
 
       }
-      tab.className = tab.className + " standout";
+      tab.classList.add("standout");
     }
 
     // update this functions after new tab select
@@ -863,10 +869,16 @@ var TMP_eventListener = {
   },
 
   onTabBarScroll: function TMP_EL_onTabBarScroll(aEvent) {
+    var scrollTabs = Tabmix.prefs.getIntPref("scrollTabs");
+    if (scrollTabs > 1) {
+      aEvent.stopPropagation();
+      aEvent.preventDefault();
+      return;
+    }
     var tabBar = gBrowser.tabContainer;
     tabBar.removeShowButtonAttr();
 
-    var shouldMoveFocus = Tabmix.prefs.getBoolPref("enableScrollSwitch");
+    let shouldMoveFocus = scrollTabs == 1;
     if (aEvent.shiftKey)
       shouldMoveFocus = !shouldMoveFocus;
     var direction = aEvent.detail;
@@ -1039,3 +1051,74 @@ var TMP_eventListener = {
   }
 
 }
+
+/**
+ * other extensions can cause delay to some of the events Tabmix uses for
+ * initialization, for each phase call all previous phases that are not
+ * initialized yet
+ */
+Tabmix.initialization = {
+  beforeStartup:           {id: 0, obj: "Tabmix"},
+  onContentLoaded:         {id: 1, obj: "TMP_eventListener"},
+  beforeBrowserInitOnLoad: {id: 2, obj: "Tabmix"},
+  onWindowOpen:            {id: 3, obj: "TMP_eventListener"},
+  delayedStartup:          {id: 4, obj: "Tabmix"},
+
+  get isValidWindow() {
+    /**
+      * don't initialize Tabmix functions on this window if one of this is true:
+      *  - the window is a popup window
+      *  - the window is about to close by SingleWindowModeUtils
+      *  - tabbrowser-tabs binding didn't start (i onlly saw it happened
+      *       when ImTranslator extension installed)
+      */
+    let chromehidden = document.documentElement.getAttribute("chromehidden");
+    let stopInitialization = chromehidden.indexOf("extrachrome") > -1 ||
+                             chromehidden.indexOf("toolbar") > -1;
+    Tabmix.singleWindowMode = Tabmix.prefs.getBoolPref("singleWindow");
+    if (!stopInitialization && Tabmix.singleWindowMode) {
+      let tmp = { };
+      Components.utils.import("resource://tabmixplus/SingleWindowModeUtils.jsm", tmp);
+      stopInitialization = tmp.SingleWindowModeUtils.newWindow(window);
+    }
+
+    if (!stopInitialization) {
+      let tabBrowser = arguments.length > 1 ? arguments[1] : gBrowser;
+      stopInitialization = typeof tabBrowser.tabContainer.setFirstTabInRow != "function";
+    }
+
+    if (stopInitialization) {
+      this.run = function() {}
+      window.removeEventListener("DOMContentLoaded", TMP_eventListener, false);
+      window.removeEventListener("load", TMP_eventListener, false);
+    }
+
+    delete this.isValidWindow;
+    Object.defineProperty(this, "run", {enumerable: false});
+    Object.defineProperty(this, "isValidWindow", {value: !stopInitialization,
+                                                  enumerable: false});
+    return this.isValidWindow;
+  },
+
+  run: function tabmix_initialization_run(aPhase) {
+    if (!this.isValidWindow)
+      return null;
+    let result, currentPhase = this[aPhase].id;
+    for (let [name, phase] in Iterator(this)) {
+      if (phase.id > currentPhase)
+        break;
+      if (!phase.initialized) {
+        phase.initialized = true;
+        try {
+          let obj = window[phase.obj];
+          result = obj[name].apply(obj, Array.slice(arguments, 1));
+        } catch (ex) {
+          Tabmix.assert(ex, phase.obj + "." + name + " failed");
+        }
+      }
+    }
+    return result;
+  }
+}
+
+TMP_eventListener.init();
diff --git a/chrome/content/utils.js b/chrome/content/utils.js
index f29a58d..d3f6ce8 100644
--- a/chrome/content/utils.js
+++ b/chrome/content/utils.js
@@ -1,20 +1,6 @@
 "use strict";
 
 var Tabmix = {
-  // aOptions can be: getter, setter or forceUpdate
-  changeCode: function(aParent, aName, aOptions) {
-    let fnName = aName.split(".").pop();
-    try {
-      return new Tabmix_ChangeCode({obj: aParent, fnName: fnName,
-        fullName: aName, options: aOptions});
-    } catch (ex) {
-      this.clog(Tabmix.callerName() + " failed to change " + aName + "\nError: " + ex.message);
-      if (Tabmix._debugMode)
-        this.obj(aObject, "aObject");
-    }
-    return null;
-  },
-
   get prefs() {
     delete this.prefs;
     return this.prefs = Services.prefs.getBranch("extensions.tabmix.");
@@ -66,7 +52,7 @@ var Tabmix = {
     // so we can open new window
     if (!this.getTopWin())
       return false;
-    return Tabmix.prefs.getBoolPref("singleWindow");
+    return this.prefs.getBoolPref("singleWindow");
   },
 
   isNewWindowAllow: function(isPrivate) {
@@ -97,14 +83,18 @@ var Tabmix = {
       return;
 
     var self = this;
-    XPCOMUtils.defineLazyGetter(aObject, aOldName, function() {
-      self.informAboutChangeInTabmix(aOldName, aNewName);
-      return self.getObject(window, aNewName);
+    Object.defineProperty(aObject, aOldName, {
+      get: function () {
+        self.informAboutChangeInTabmix(aOldName, aNewName);
+        delete aObject[aOldName];
+        return aObject[aOldName] = self.getObject(window, aNewName);
+      },
+      configurable: true
     });
   },
 
   informAboutChangeInTabmix: function(aOldName, aNewName) {
-    let err = Error(aOldName + " is deprecated in Tabmix since version 0.3.8.5pre.110123a use " + aNewName + " instead.");
+    let err = Error(aOldName + " is deprecated in Tabmix, use " + aNewName + " instead.");
     // cut off the first lines, we looking for the function that trigger the getter.
     let stack = Error().stack.split("\n").slice(3);
     let file = stack[0] ? stack[0].split(":") : null;
@@ -200,6 +190,30 @@ var Tabmix = {
   compare: function TMP_utils_compare(a, b, lessThan) {return lessThan ? a < b : a > b;},
   itemEnd: function TMP_utils_itemEnd(item, end) {return item.boxObject.screenX + (end ? item.getBoundingClientRect().width : 0);},
 
+  show: function(aMethod, aDelay, aWindow) {
+    TabmixSvc.console.show(aMethod, aDelay, aWindow || window);
+  },
+
+  // console._removeInternal use this function name to remove it from
+  // caller list
+  __noSuchMethod__: function TMP_console_wrapper(id, args) {
+    if (["changeCode", "setNewFunction", "nonStrictMode"].indexOf(id) > -1) {
+      this.installeChangecode;
+      return this[id].apply(this, args);
+    }
+    if (typeof TabmixSvc.console[id] == "function") {
+      return TabmixSvc.console[id].apply(TabmixSvc.console, args);
+    }
+    TabmixSvc.console.trace("unexpected method " + id);
+    return null;
+  },
+
+  get installeChangecode() {
+    delete this.installeChangecode;
+    Services.scriptloader.loadSubScript("chrome://tabmixplus/content/changecode.js", window);
+    return this.installeChangecode = true;
+  },
+
   _init: function() {
     Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
     Components.utils.import("resource://gre/modules/Services.jsm");
@@ -210,17 +224,10 @@ var Tabmix = {
                  resource + "modules/RecentWindow.jsm");
     }
 
-    let tmp = {};
-    Components.utils.import("resource://tabmixplus/log.jsm", tmp);
-    for (let [fnName, value] in Iterator(tmp.log))
-      this[fnName] = typeof value == "function" ? value.bind(this) : value;
-
     window.addEventListener("unload", function tabmix_destroy() {
       window.removeEventListener("unload", tabmix_destroy, false);
       this.destroy();
     }.bind(this), false);
-
-    Services.scriptloader.loadSubScript("chrome://tabmixplus/content/changecode.js", window);
   },
 
   originalFunctions: {},
@@ -228,10 +235,6 @@ var Tabmix = {
     this.toCode = null;
     this.originalFunctions = null;
     delete this.window;
-    for (let [id, timer] in Iterator(this._timers)) {
-      timer.cancel();
-      delete this._timers[id];
-    }
   }
 }
 
diff --git a/chrome/skin/app_version/4.0/linux/browser.css b/chrome/skin/app_version/4.0/linux/browser.css
index 1c16a13..08989c9 100644
--- a/chrome/skin/app_version/4.0/linux/browser.css
+++ b/chrome/skin/app_version/4.0/linux/browser.css
@@ -26,7 +26,7 @@
 }
 
 /* set on linux we need to set vertical alogn even if the button is hidden*/
-.tabbrowser-tabs[multibar] .tabbrowser-arrowscrollbox > .tabs-newtab-button {
+.tabbrowser-tabs[multibar] .tabbrowser-arrowscrollbox > toolbarbutton {
   vertical-align: bottom;
 }
 
diff --git a/chrome/skin/app_version/4.0/mac/browser.css b/chrome/skin/app_version/4.0/mac/browser.css
index af44236..d926529 100644
--- a/chrome/skin/app_version/4.0/mac/browser.css
+++ b/chrome/skin/app_version/4.0/mac/browser.css
@@ -31,7 +31,7 @@
   margin-top: 1px !important;
 }
 
-.tabbrowser-tabs[multibar=true] .tabbrowser-arrowscrollbox > .tabs-newtab-button {
+.tabbrowser-tabs[multibar=true] .tabbrowser-arrowscrollbox > toolbarbutton {
   vertical-align: bottom;
 }
 
diff --git a/chrome/skin/app_version/4.0/win/browser.css b/chrome/skin/app_version/4.0/win/browser.css
index 47142c0..98d93bc 100644
--- a/chrome/skin/app_version/4.0/win/browser.css
+++ b/chrome/skin/app_version/4.0/win/browser.css
@@ -21,12 +21,12 @@
 }
 
 /*  new tab button after last tab on firefox 4.0  aero */
-#TabsToolbar[tabmix_aero] .tabbrowser-tabs[multibar=true] .tabbrowser-arrowscrollbox > .tabs-newtab-button {
+#TabsToolbar[tabmix_aero] .tabbrowser-tabs[multibar=true] .tabbrowser-arrowscrollbox > toolbarbutton {
   vertical-align: bottom;
 }
 
 /* enable this rule after bug 738491 australis-tabs-win lands, and move it to diffrent file
-#tabbrowser-tabs[flowing="multibar"]:not([multibar]) > .tabbrowser-arrowscrollbox > .tabs-newtab-button {
+#tabbrowser-tabs[flowing="multibar"]:not([multibar]) > .tabbrowser-arrowscrollbox > toolbarbutton {
   vertical-align: bottom;
 }
 */
@@ -36,9 +36,8 @@
   padding-bottom: 3px;
 }
 
-#TabsToolbar[multibar] > .toolbarbutton-1,
 #TabsToolbar[multibar] > #tabs-closebutton {
-  height: 26px;
+  padding-top: 5px;
 }
 
 /* for TabsToolbar on bottom with windows 7 */
diff --git a/chrome/skin/tab.css b/chrome/skin/tab.css
index 315f93f..48d4cdb 100644
--- a/chrome/skin/tab.css
+++ b/chrome/skin/tab.css
@@ -323,14 +323,14 @@ toolbar[iconsize=small] #btn_sessionmanager[disabled=true], .sessionmanager-icon
 }
 
 /* CrystalFox_Qute-BigRedBrent */
-.tabbrowser-tabs[tabmix_skin="CrystalFox"]:not([multibar])[inline=true] .tabbrowser-arrowscrollbox > .tabs-newtab-button,
-.tabbrowser-tabs[tabmix_skin="CrystalFox"][multibar] .tabbrowser-arrowscrollbox > .tabs-newtab-button {
+.tabbrowser-tabs[tabmix_skin="CrystalFox"]:not([multibar])[inline=true] .tabbrowser-arrowscrollbox > toolbarbutton,
+.tabbrowser-tabs[tabmix_skin="CrystalFox"][multibar] .tabbrowser-arrowscrollbox > toolbarbutton {
   vertical-align: top;
 }
 
 /* BlackFox_V1-Blue */
-.tabbrowser-tabs[tabmix_skin="BlackFox"]:not([multibar])[inline=true] .tabbrowser-arrowscrollbox > .tabs-newtab-button,
-.tabbrowser-tabs[tabmix_skin="BlackFox"][multibar] .tabbrowser-arrowscrollbox > .tabs-newtab-button {
+.tabbrowser-tabs[tabmix_skin="BlackFox"]:not([multibar])[inline=true] .tabbrowser-arrowscrollbox > toolbarbutton,
+.tabbrowser-tabs[tabmix_skin="BlackFox"][multibar] .tabbrowser-arrowscrollbox > toolbarbutton {
   vertical-align: top;
 }
 
diff --git a/defaults/preferences/tabmix.js b/defaults/preferences/tabmix.js
index d513c95..13b7824 100644
--- a/defaults/preferences/tabmix.js
+++ b/defaults/preferences/tabmix.js
@@ -106,7 +106,13 @@ pref("extensions.tabmix.tabs.closeButtons.delay", 50);
 
 pref("extensions.tabmix.moveTabOnDragging", true);
 pref("extensions.tabmix.useFirefoxDragmark", true);
+/*
 pref("extensions.tabmix.enableScrollSwitch", false);
+ replaced by scrollTabs: 0 - scrool tabbar on overflow - default
+                         1 - scroll change selected tab
+                         2 - disable scroll over tabs
+*/
+pref("extensions.tabmix.scrollTabs", 0);
 pref("extensions.tabmix.reversedScroll", false);
 
 pref("extensions.tabmix.dblClickTab", 0);
diff --git a/install.rdf b/install.rdf
index 0f9ea38..20d6c27 100644
--- a/install.rdf
+++ b/install.rdf
@@ -5,7 +5,7 @@
   <RDF:Description RDF:about="urn:mozilla:install-manifest"
                    NS1:id="{dc572301-7619-498c-a57d-39143191b318}"
                    NS1:name="Tab Mix Plus"
-                   NS1:version="0.4.1.1pre.130821b"
+                   NS1:version="0.4.1.2pre.130918a"
                    NS1:type="2"
                    NS1:description="Tab browsing with an added boost."
                    NS1:iconURL="chrome://tabmixplus/skin/tmp.png"
@@ -19,5 +19,5 @@
   <RDF:Description RDF:about="rdf:#$n83In3"
                    NS1:id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"
                    NS1:minVersion="11.0"
-                   NS1:maxVersion="26.0a1" />
+                   NS1:maxVersion="27.0a1" />
 </RDF:RDF>
diff --git a/modules/AutoReload.jsm b/modules/AutoReload.jsm
index 0d995b7..56f4333 100644
--- a/modules/AutoReload.jsm
+++ b/modules/AutoReload.jsm
@@ -260,7 +260,8 @@ function  _observe(aSubject, aTopic, aData) {
   if (aTopic == "common-dialog-loaded") {
     TabmixSvc.obs.removeObserver(_observe, "common-dialog-loaded");
     let icon = aSubject.document.getElementById("info.icon");
-    icon.className = icon.className.replace("question-icon" ,"alert-icon");
+    icon.classList.add("alert-icon");
+    icon.classList.remove("question-icon");
   }
 }
 
diff --git a/modules/MergeWindows.jsm b/modules/MergeWindows.jsm
index cf8f139..916bbf9 100644
--- a/modules/MergeWindows.jsm
+++ b/modules/MergeWindows.jsm
@@ -102,7 +102,8 @@ let MergeWindows = {
     var features = "chrome,all,dialog=no";
     if (TabmixSvc.version(200))
         features += aPrivate ? ",private" : ",non-private";
-    var newWindow = aWindows[0].openDialog(aWindows[0].getBrowserURL(), "_blank", features, null);
+    var newWindow = aWindows[0].openDialog("chrome://browser/content/browser.xul",
+        "_blank", features, null);
     let mergePopUps = function _mergePopUps(aEvent) {
       newWindow.removeEventListener("SSWindowStateReady", _mergePopUps, false);
       this.concatTabsAndMerge(newWindow, aWindows);
diff --git a/modules/Places.jsm b/modules/Places.jsm
new file mode 100644
index 0000000..0b4a20b
--- /dev/null
+++ b/modules/Places.jsm
@@ -0,0 +1,173 @@
+"use strict";
+
+var EXPORTED_SYMBOLS = ["TabmixPlacesUtils"];
+
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "Services",
+  "resource://gre/modules/Services.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
+  "resource://gre/modules/PluralForm.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
+  "resource://gre/modules/PrivateBrowsingUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "PlacesUIUtils",
+  "resource:///modules/PlacesUIUtils.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "PlacesUtils", function() {
+  Cu.import("resource://gre/modules/PlacesUtils.jsm");
+  return PlacesUtils;
+});
+
+XPCOMUtils.defineLazyModuleGetter(this,
+  "TabmixSvc", "resource://tabmixplus/Services.jsm");
+
+this.TabmixPlacesUtils = {
+  init: function(aWindow) {
+    PlacesUtilsInternal.init(aWindow);
+  },
+
+  onQuitApplication: function() {
+    PlacesUtilsInternal.onQuitApplication();
+  }
+}
+Object.freeze(TabmixPlacesUtils);
+
+let global = this; // for Firefox 11-14
+let Tabmix = { }
+
+let PlacesUtilsInternal = {
+  _timer: null,
+  _initialized: false,
+
+  init: function(aWindow) {
+    if (this._initialized)
+      return;
+    this._initialized = true;
+
+    Tabmix._debugMode = aWindow.Tabmix._debugMode;
+    Services.scriptloader.loadSubScript("chrome://tabmixplus/content/changecode.js", global);
+
+    this.initPlacesUIUtils(aWindow);
+  },
+
+  onQuitApplication: function () {
+    if (this._timer)
+      this._timer.clear();
+
+    this.functions.forEach(function(aFn) {
+      PlacesUIUtils[aFn] = PlacesUIUtils["tabmix_" + aFn];
+      delete PlacesUIUtils["tabmix_" + aFn];
+    });
+    delete PlacesUIUtils.tabmix_getURLsForContainerNode;
+  },
+
+  functions: ["_openTabset", "openURINodesInTabs", "openContainerNodeInTabs", "openNodeWithEvent", "_openNodeIn"],
+  initPlacesUIUtils: function TMP_PC_initPlacesUIUtils(aWindow) {
+    try {
+      let test = PlacesUIUtils._openTabset.toString();
+    } catch (ex) {
+      if (aWindow.document.documentElement.getAttribute("windowtype") == "navigator:browser") {
+        TabmixSvc.console.log("Starting with Firefox 21 Imacros 8.3.0 break toString on PlacesUIUtils functions."
+          + "\nTabmix can't update PlacesUIUtils to work according to Tabmix preferences, use Imacros 8.3.1 and up.");
+      }
+      return;
+    }
+
+    this.functions.forEach(function(aFn) {
+      PlacesUIUtils["tabmix_" + aFn] = PlacesUIUtils[aFn];
+    });
+
+    var treeStyleTab = "TreeStyleTabBookmarksService" in aWindow;
+    function updateOpenTabset() {
+      let openGroup = "    browserWindow.TMP_Places.openGroup(urls, ids, where$1);"
+      Tabmix.changeCode(PlacesUIUtils, "PlacesUIUtils._openTabset")._replace(
+        'urls = []',
+        'behavior, $&', {check: treeStyleTab}
+      )._replace(
+        'var urls = []',
+        '$&, ids = []', {check: !treeStyleTab}
+      )._replace(
+        'urls.push(item.uri);',
+        '$&\n' +
+        '      ids.push(item.id);', {check: !treeStyleTab}
+      )._replace(
+        '"chrome,dialog=no,all", args);',
+        '$&\n' +
+        '      browserWindow.bookMarkIds = ids.join("|");'
+      )._replace(
+        /let openGroupBookmarkBehavior =|TSTOpenGroupBookmarkBehavior =/,
+        '$& behavior =', {check: treeStyleTab}
+      )._replace(
+        'browserWindow.gBrowser.loadTabs(urls, loadInBackground, false);',
+        'var changeWhere = where == "tabshifted" && aEvent.target.localName != "menuitem";\n' +
+        '    if (changeWhere)\n' +
+        '      where = "current";\n' +
+        openGroup.replace("$1", treeStyleTab ? ", behavior" : "")
+      ).toCode();
+    };
+    if (treeStyleTab) {
+      let timer = this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+      timer.initWithCallback(function() {
+        timer.cancel();
+        this._timer = null;
+        updateOpenTabset();
+      }.bind(this), 50, Ci.nsITimer.TYPE_ONE_SHOT);
+    }
+    else { // TreeStyleTab not installed
+      updateOpenTabset();
+
+      Tabmix.changeCode(PlacesUIUtils, "PlacesUIUtils.openURINodesInTabs")._replace(
+        'push({uri: aNodes[i].uri,',
+        'push({id: aNodes[i].itemId, uri: aNodes[i].uri,'
+      ).toCode();
+
+      // we enter getURLsForContainerNode into PlacesUIUtils to prevent leakes from PlacesUtils
+      Tabmix.changeCode(PlacesUtils, "PlacesUtils.getURLsForContainerNode")._replace(
+        '{uri: child.uri,',
+        '{id: child.itemId, uri: child.uri,', {flags: "g"}
+      )._replace(
+        'this.',  'PlacesUtils.', {flags: "g"}
+      ).toCode(false, PlacesUIUtils, "tabmix_getURLsForContainerNode");
+
+      Tabmix.changeCode(PlacesUIUtils, "PlacesUIUtils.openContainerNodeInTabs")._replace(
+        'PlacesUtils.getURLsForContainerNode(aNode)',
+        'PlacesUIUtils.tabmix_getURLsForContainerNode(aNode)'
+      ).toCode();
+    }
+
+    Tabmix.changeCode(PlacesUIUtils, "PlacesUIUtils.openNodeWithEvent")._replace(
+      /whereToOpenLink\(aEvent[,\s\w]*\), window/, '$&, aEvent'
+    ).toCode();
+
+    // Don't change "current" when user click context menu open (callee is PC_doCommand and aWhere is current)
+    // we disable the open menu when the tab is lock
+    // the 2nd check for aWhere == "current" is for non Firefox code that may call this function
+    Tabmix.changeCode(PlacesUIUtils, "PlacesUIUtils._openNodeIn")._replace(
+      /(function[^\(]*\([^\)]+)(\))/,
+      '$1, TMP_Event$2' /* event argument exist when this function called from openNodeWithEvent */
+    )._replace(
+      'aWindow.openUILinkIn',
+      'let browserWindow = this._getTopBrowserWin();\n' +
+      '      if (browserWindow && typeof browserWindow.TMP_Places == "object") {\n' +
+      '        if (TMP_Event) aWhere = browserWindow.TMP_Places.isBookmarklet(aNode.uri) ? "current" :\n' +
+      '                       browserWindow.TMP_Places.fixWhereToOpen(TMP_Event, aWhere);\n' +
+      '        else if (aWhere == "current" && !browserWindow.TMP_Places.isBookmarklet(aNode.uri)) {\n' +
+      '          let caller = browserWindow.Tabmix.getCallerNameByIndex(2);\n' +
+      '          if (caller != "PC_doCommand")\n' +
+      '            aWhere = browserWindow.TMP_Places.fixWhereToOpen(null, aWhere);\n' +
+      '        }\n' +
+      '      }\n' +
+      '      if (browserWindow && aWhere == "current") browserWindow.gBrowser.mCurrentBrowser.tabmix_allowLoad = true;\n' +
+      '      $&'
+    )._replace(
+      'inBackground:',
+      'bookMarkId: aNode.itemId, initiatingDoc: null,\n' +
+      '        $&'
+    ).toCode();
+  }
+}
diff --git a/modules/RenameTab.jsm b/modules/RenameTab.jsm
index 1949f35..b345388 100644
--- a/modules/RenameTab.jsm
+++ b/modules/RenameTab.jsm
@@ -23,9 +23,11 @@ let RenameTab = {
           this.window.TMP_SessionStore.getTitleFromTabState(aTab) :
           browser.contentDocument.title;
     this.data.url = browser.currentURI.spec;
-    docTitle = this.window.TMP_Places.getTitleFromBookmark(this.data.url,
+    this.data.docTitle = this.window.TMP_Places.getTitleFromBookmark(this.data.url,
         docTitle, null, aTab);
-    this.data.docTitle = docTitle || gBrowser.mStringBundle.getString("tabs.emptyTabTitle");
+    if (!this.data.docTitle)
+      this.data.docTitle = this.window.isBlankPageURL(this.data.url) ?
+        gBrowser.mStringBundle.getString("tabs.emptyTabTitle") : this.data.url;
     this.data.modified = aTab.getAttribute("label-uri") || null;
     if (this.data.modified == this.data.url || this.data.modified == "*")
       this.data.value = aTab.getAttribute("fixed-label");
diff --git a/modules/Services.jsm b/modules/Services.jsm
index 93851eb..247d3d2 100644
--- a/modules/Services.jsm
+++ b/modules/Services.jsm
@@ -73,6 +73,22 @@ let TabmixSvc = {
     return this.direct2dDisabled = false;
   },
 
+  /**
+   * call a callback for all currently opened browser windows
+   * (might miss the most recent one)
+   * @param aFunc
+   *        Callback each window is passed to
+   */
+  forEachBrowserWindow: function(aFunc) {
+    let windowsEnum = Services.wm.getEnumerator("navigator:browser");
+    while (windowsEnum.hasMoreElements()) {
+      let window = windowsEnum.getNext();
+      if (!window.closed) {
+        aFunc.call(null, window);
+      }
+    }
+  },
+
   // some extensions override native JSON so we use nsIJSON
   JSON: {
     nsIJSON: null,
@@ -97,7 +113,8 @@ let TabmixSvc = {
   },
 
   windowStartup: {
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference]),
+    QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
+                                           Ci.nsISupportsWeakReference]),
     _initialized: false,
     init: function(aWindow) {
       // windowStartup must only be called once for each window
@@ -109,14 +126,23 @@ let TabmixSvc = {
       this._initialized = true;
 
       Services.obs.addObserver(this, "browser-delayed-startup-finished", true);
+      Services.obs.addObserver(this, "quit-application", true);
+
+      Cu.import("resource://tabmixplus/Places.jsm");
+      TabmixPlacesUtils.init(aWindow);
     },
 
     observe: function(aSubject, aTopic, aData) {
       switch (aTopic) {
+        case "quit-application":
+          TabmixPlacesUtils.onQuitApplication();
+          for (let [id, timer] in Iterator(TabmixSvc.console._timers))
+            timer.cancel();
+          break;
         case "browser-delayed-startup-finished":
           try {
-            aSubject.TMP_eventListener.browserDelayedStartupFinished();
-          } catch (ex) {log.assert(ex);}
+            aSubject.Tabmix.initialization.run("delayedStartup");
+          } catch (ex) {this.console.assert(ex);}
           break;
       }
     }
@@ -131,7 +157,8 @@ let TabmixSvc = {
       delete this.sanitized;
       return this.sanitized = TabmixSvc.prefBranch.prefHasUserValue("sessions.sanitized");
     },
-    private: true
+    private: true,
+    settingPreference: false,
   }
 }
 
@@ -145,7 +172,6 @@ XPCOMUtils.defineLazyGetter(TabmixSvc.JSON, "nsIJSON", function() {
  */
 XPCOMUtils.defineLazyGetter(TabmixSvc, "prefs", function () {return Services.prefs});
 XPCOMUtils.defineLazyGetter(TabmixSvc, "io", function () {return Services.io});
-XPCOMUtils.defineLazyGetter(TabmixSvc, "console", function () {return Services.console});
 XPCOMUtils.defineLazyGetter(TabmixSvc, "wm", function () {return Services.wm});
 XPCOMUtils.defineLazyGetter(TabmixSvc, "obs", function () {return Services.obs});
 XPCOMUtils.defineLazyGetter(TabmixSvc, "prompt", function () {return Services.prompt});
@@ -167,5 +193,5 @@ XPCOMUtils.defineLazyServiceGetter(TabmixSvc, "ss", "@mozilla.org/browser/sessio
 XPCOMUtils.defineLazyModuleGetter(TabmixSvc, "FileUtils",
   "resource://gre/modules/FileUtils.jsm");
 
-XPCOMUtils.defineLazyModuleGetter(this, "log",
+XPCOMUtils.defineLazyModuleGetter(TabmixSvc, "console",
   "resource://tabmixplus/log.jsm");
diff --git a/modules/Shortcuts.jsm b/modules/Shortcuts.jsm
index f1af0fb..9550c14 100644
--- a/modules/Shortcuts.jsm
+++ b/modules/Shortcuts.jsm
@@ -6,7 +6,6 @@ const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 const NS_XUL = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://tabmixplus/Services.jsm");
-Cu.import("resource://tabmixplus/log.jsm");
 
 let Shortcuts = {
   keys: {
@@ -138,7 +137,7 @@ try {
     }
 
     this.updatingShortcuts = false;
-} catch (ex) {log.assert(ex);}
+} catch (ex) {TabmixSvc.console.assert(ex);}
   },
 
   /* ........ Window Event Handlers .............. */
@@ -159,7 +158,7 @@ try {
     let win = aKey.ownerDocument.defaultView;
     let command = this.keys[aKey._id].command;
     win.TabmixTabClickOptions.doCommand(command, win.gBrowser.selectedTab);
-} catch (ex) {log.assert(ex);}
+} catch (ex) {TabmixSvc.console.assert(ex);}
   },
 
   onUnload: function TMP_SC_onUnload(aWindow) {
@@ -269,7 +268,7 @@ try {
       shortcuts = JSON.parse(this.prefs.getCharPref("shortcuts"));
     } catch (ex) {}
     if (shortcuts == null) {
-      log.log("failed to read shortcuts preference.\nAll shortcuts was resets to default");
+      TabmixSvc.console.log("failed to read shortcuts preference.\nAll shortcuts was resets to default");
       shortcuts = {};
       updatePreference = true;
     }
diff --git a/modules/extensions/SessionManagerAddon.jsm b/modules/extensions/AddonManager.jsm
similarity index 53%
rename from modules/extensions/SessionManagerAddon.jsm
rename to modules/extensions/AddonManager.jsm
index 1ec79d9..29aa72a 100644
--- a/modules/extensions/SessionManagerAddon.jsm
+++ b/modules/extensions/AddonManager.jsm
@@ -4,32 +4,59 @@
 
 "use strict";
 
-var EXPORTED_SYMBOLS = ["SessionManagerAddon"];
+var EXPORTED_SYMBOLS = ["TabmixAddonManager"];
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/AddonManager.jsm");
 Cu.import("resource://tabmixplus/Services.jsm");
 
-let TabmixListener = {
+function log(msg) {
+  TabmixSvc.console.log(msg);
+}
+
+// https://addons.mozilla.org/en-US/firefox/addon/private-tab
+let PrivateTab = {
+  id: "privateTab at infocatcher",
+  onEnabled: function() {
+    this._resetNewTabButton();
+  },
+  onDisabled: function() {
+    this._resetNewTabButton();
+  },
+  _resetNewTabButton: function() {
+    TabmixSvc.forEachBrowserWindow(function(aWindow) {
+      aWindow.TMP_eventListener.updateMultiRow(true);
+    });
+  }
+}
+
+let SessionManager = {
   id: "{1280606b-2510-4fe0-97ef-9b5a22eafe30}",
-  saveTabmixPrefs: function(addon) {
+  init: function() {
+    this._saveTabmixPrefs();
+    try {
+      let tmp = {}
+      Cu.import("chrome://sessionmanager/content/modules/session_manager.jsm", tmp);
+      TabmixSvc.sessionManagerAddonInstalled = true;
+    }
+    catch (ex) {
+      TabmixSvc.sessionManagerAddonInstalled = false;
+    }
+  },
+  _saveTabmixPrefs: function() {
     this.manager_enabled = TabmixSvc.prefBranch.getBoolPref("sessions.manager");
     this.crashRecovery_enabled = TabmixSvc.prefBranch.getBoolPref("sessions.crashRecovery");
   },
-  onEnabled: function(addon) {
-    if (addon.id != this.id)
-      return;
+  onEnabled: function() {
     TabmixSvc.sessionManagerAddonInstalled = true;
-    this.saveTabmixPrefs();
+    this._saveTabmixPrefs();
     let win = TabmixSvc.topWin();
     if (win)
       win.TMP_SessionStore.setService(-1);
-    this.notify(true);
+    this._notify(true);
   },
-  onDisabled: function(addon) {
-    if (addon.id != this.id)
-      return;
+  onDisabled: function() {
     TabmixSvc.sessionManagerAddonInstalled = false;
     if (this.manager_enabled || this.crashRecovery_enabled) {
       let win = TabmixSvc.topWin();
@@ -38,15 +65,9 @@ let TabmixListener = {
       TabmixSvc.prefBranch.setBoolPref("sessions.manager", this.manager_enabled);
       TabmixSvc.prefBranch.setBoolPref("sessions.crashRecovery", this.crashRecovery_enabled);
     }
-    this.notify(false);
+    this._notify(false);
   },
-  onInstalled: function(addon) {
-    if (addon.id != this.id ||
-        !addon.isActive || addon.userDisabled || addon.appDisabled)
-      return;
-    this.onEnabled(addon);
-  },
-  notify: function(isActive) {
+  _notify: function(isActive) {
     // in preference/session.js we only care when the preference is boolean
     let pref = "sessionManagerAddon.isActive";
     TabmixSvc.prefBranch.setBoolPref(pref, isActive);
@@ -54,24 +75,38 @@ let TabmixListener = {
   }
 }
 
-let SessionManagerAddon = {
+let TabmixListener = {
+  onChange: function(aAddon, aAction) {
+    let id = aAddon.id
+    if (id == SessionManager.id)
+      SessionManager[aAction]();
+    else if (id == PrivateTab.id) {
+      PrivateTab[aAction]();
+    }
+  },
+  onEnabled: function(aAddon) {
+    this.onChange(aAddon, "onEnabled");
+  },
+  onDisabled: function(aAddon) {
+    this.onChange(aAddon, "onDisabled");
+  },
+  onInstalled: function(aAddon) {
+    if (!aAddon.isActive || aAddon.userDisabled || aAddon.appDisabled)
+      return;
+    this.onEnabled(aAddon);
+  }
+}
+
+let TabmixAddonManager = {
   initialized: false,
   init: function() {
     if (this.initialized)
       return;
     this.initialized = true;
-    TabmixListener.saveTabmixPrefs();
-    AddonManager.addAddonListener(TabmixListener);
 
-    try {
-      let tmp = {}
-      Cu.import("chrome://sessionmanager/content/modules/session_manager.jsm", tmp);
-      TabmixSvc.sessionManagerAddonInstalled = true;
-    }
-    catch (ex) {
-      TabmixSvc.sessionManagerAddonInstalled = false;
-    }
+    SessionManager.init();
+    AddonManager.addAddonListener(TabmixListener);
   }
 }
 
-SessionManagerAddon.init();
+TabmixAddonManager.init();
diff --git a/modules/extensions/TabGroupsManager.jsm b/modules/extensions/TabGroupsManager.jsm
index 88bdf46..a911238 100644
--- a/modules/extensions/TabGroupsManager.jsm
+++ b/modules/extensions/TabGroupsManager.jsm
@@ -54,7 +54,7 @@ let TMP_TabGroupsManager = {
     let sessionManager = aWindow.TabmixSessionManager;
     this.changeCode(sessionManager, "TabmixSessionManager.saveOneWindow")._replace(
       'if (caller == "windowbackup")',
-      '  this.saveAllGroupsData(null, rdfNodeThisWindow);' +
+      '  try{this.saveAllGroupsData(null, rdfNodeThisWindow);} catch(ex) {Tabmix.assert(ex);}' +
       '  $&'
     ).toCode();
 
@@ -88,7 +88,7 @@ let TMP_TabGroupsManager = {
       '}' +
       'if (false) {'
     )._replace(
-      'if (this.saveClosedtabs)',
+      'TMP_ClosedTabs.setButtonDisableState();',
       '  if (_restoreSelect && (overwrite || (!concatenate && !currentTabIsBalnk)))' +
       '    this.updateSelected(newIndex + _lastSelectedIndex, overwrite || caller=="firstwindowopen" || caller=="windowopenedbytabmix");' +
       '  $&'
@@ -113,7 +113,8 @@ let TMP_TabGroupsManager = {
     // for TabGroupsManager use - don't change function name from tabmixSessionsManager
     aWindow.TMP_TabGroupsManager = {}
     aWindow.TMP_TabGroupsManager.tabmixSessionsManager = this.tabmixSessionsManager.bind(aWindow);
-    aWindow.Tabmix.toCode(sessionManager, "saveAllGroupsData", this._saveAllGroupsData.toString());
+    this.changeCode(this, "TMP_TabGroupsManager._saveAllGroupsData", {forceUpdate: true})
+        .toCode(false, aWindow.TabmixSessionManager, "saveAllGroupsData");
   },
 
   // for TabGroupsManager use - don't change function name
diff --git a/modules/log.jsm b/modules/log.jsm
index 9981b5c..515e38f 100644
--- a/modules/log.jsm
+++ b/modules/log.jsm
@@ -1,13 +1,25 @@
 "use strict";
 
-var EXPORTED_SYMBOLS = ["log"];
+var EXPORTED_SYMBOLS = ["console"];
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
+Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://tabmixplus/Services.jsm");
 
-let log = {
+let gNextID = 1;
+
+let console = {
   getObject: function(aWindow, aMethod) {
+    let msg = "";
+    if (!aWindow)
+      msg += "aWindow is undefined";
+    if (typeof aMethod != "string")
+      msg += (msg ? "\n" : "") + "aMethod need to be a string";
+    if (msg) {
+      this.assert(msg);
+      return {toString: function() msg};
+    }
     var rootID, methodsList = aMethod.split(".");
     if (methodsList[0] == "window")
       methodsList.shift();
@@ -16,8 +28,7 @@ let log = {
       rootID = methodsList.shift().replace(/getElementById\(|\)|'|"/g , "");
     }
     try {
-      // this.window is only defined when we import this function into Tabmix
-      var obj = aWindow || this.window;
+      var obj = aWindow;
       if (rootID)
         obj = obj.document.getElementById(rootID);
       methodsList.forEach(function(aFn) {
@@ -27,7 +38,6 @@ let log = {
     return obj || {toString: function() "undefined"};
   },
 
-  _nextID: 0,
   _timers: {},
   show: function(aMethod, aDelay, aWindow) {
     try {
@@ -35,21 +45,34 @@ let log = {
         aDelay = 500;
 
       let logMethod = function _logMethod() {
-        let isObj = typeof aMethod == "object";
-        let result = isObj ? aMethod.obj[aMethod.name] :
+        let result = "", isObj = typeof aMethod == "object";
+        if (typeof aMethod != "function") {
+          result = isObj ? aMethod.obj[aMethod.name] :
                 this.getObject(aWindow, aMethod);
-        this.clog((isObj ? aMethod.fullName : aMethod) + " = " + result.toString());
+          result = " = " + result.toString()
+        }
+        this.clog((isObj ? aMethod.fullName : aMethod) + result);
       }.bind(this);
 
       if (aDelay >= 0) {
-        let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
-        let timerID = this._nextID++;
+        let timer = {};
+        timer.__proto__ = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+        let timerID = gNextID++;
         this._timers[timerID] = timer;
-        timer.initWithCallback(function() {
-          if (timerID in this._timers && this._timers[timerID] === timer)
+        timer.clear = function() {
+          if (timerID in this._timers)
             delete this._timers[timerID];
+          timer.cancel();
+        }.bind(this);
+        if (aWindow) {
+          aWindow.addEventListener("unload", function unload() {
+            timer.clear();
+          }, false);
+        }
+        timer.initWithCallback(function() {
+          timer.clear();
           logMethod();
-        }.bind(this), aDelay, Ci.nsITimer.TYPE_ONE_SHOT);
+        }, aDelay, Ci.nsITimer.TYPE_ONE_SHOT);
       }
       else
         logMethod();
@@ -57,49 +80,54 @@ let log = {
     } catch (ex) {this.assert(ex, "Error we can't show " + aMethod + " in Tabmix.show");}
   },
 
-  clog: function TMP_clog(aMessage) {
-    TabmixSvc.console.logStringMessage("TabMix :\n" + aMessage);
+  clog: function(aMessage) {
+    Services.console.logStringMessage("TabMix :\n" + aMessage);
   },
 
-  log: function TMP_log(aMessage, aShowCaller, offset) {
+  log: function TMP_console_log(aMessage, aShowCaller, offset) {
     offset = !offset ? 0 : 1;
     let names = this._getNames(aShowCaller ? 2 + offset : 1 + offset);
     let callerName = names[offset+0];
-    let callerCallerName = aShowCaller ? " (caller was " + names[offset+1] + ")" : "";
-    TabmixSvc.console.logStringMessage("TabMix " + callerName + callerCallerName + " :\n" + aMessage);
+    let callerCallerName = aShowCaller && names[offset+1] ? " (caller was " + names[offset+1] + ")" : "";
+    Services.console.logStringMessage("TabMix " + callerName + callerCallerName + " :\n" + aMessage);
   },
 
   // get functions names from Error().stack
+  // excluding any internal caller (name start with TMP_console_)
   _getNames: function(aCount, stack) {
-    if (!stack)
-      stack = Error().stack.split("\n").slice(1);
-    else
-      stack = stack.split("\n");
-    // cut the secound if it is from our utils
-    if (stack[0].indexOf("TMP_") == 0)
-      stack.splice(0, 1);
+    stack = this._getStackExcludingInternal(stack);
     if (!aCount)
       aCount = 1;
     else if (aCount < 0)
       aCount = stack.length;
     let names = [];
-    for (let i = 0; i < aCount; i++)
+    for (let i = 0, l = Math.min(aCount, stack.length); i < l; i++)
       names.push(this._name(stack[i]));
 
     return names;
   },
 
   // get the name of the function that is in the nth place in Error().stack
-  // don't include this function in the count
-  _getCallerNameByIndex: function TMP_getCallerNameByIndex(aPlace) {
-    let stack = Error().stack.split("\n");
-    let fn = stack[aPlace + 1];
-
+  // excluding any internal caller in the count
+  getCallerNameByIndex: function(aIndex) {
+    let fn = this._getStackExcludingInternal()[aIndex];
     if (fn)
       return this._name(fn);
     return null;
   },
 
+  _getStackExcludingInternal: function(stack) {
+    if (!stack)
+      stack = Error().stack.split("\n").slice(2);
+    else
+      stack = stack.split("\n");
+    // cut internal callers
+    let re = /TMP_console_.*/;
+    while (stack.length && stack[0].match(re))
+      stack.splice(0, 1);
+    return stack;
+  },
+
   // Bug 744842 - don't include actual args in error.stack.toString()
   // since Bug 744842 landed the stack string don't have (arg1, arg2....)
   // so we can get the name from the start of the string until @
@@ -110,7 +138,7 @@ let log = {
 
   _name: function(fn) {
     let name = fn.substr(0, fn.indexOf(this._char))
-    if (!name) {
+    if (fn && !name) {
       // get file name and line number
       let lastIndexOf = fn.lastIndexOf("/");
       name = lastIndexOf > -1 ? fn.substr(lastIndexOf+1) : "?";
@@ -125,7 +153,6 @@ let log = {
   },
 
   callerName: function() {
-///    return this._getCallerNameByIndex(2);
     try {
       var name = this._nameFromComponentsStack(Components.stack.caller.caller);
     } catch (ex) { }
@@ -133,20 +160,20 @@ let log = {
   },
 */
 
-  callerName: function() {
-    return this._getCallerNameByIndex(2);
+  callerName: function TMP_console_callerName() {
+    return this.getCallerNameByIndex(1);
   },
 
   // return true if the caller name of the calling function is in the
   // arguments list
-  isCallerInList: function() {
+  isCallerInList: function TMP_console_isCallerInList() {
     if (!arguments.length) {
       this.assert("no arguments in Tabmix.isCallerInList");
       return false;
     }
 
     try {
-      let callerName = this._getCallerNameByIndex(2);
+      let callerName = this.getCallerNameByIndex(1);
       if (!callerName)
         return false;
       if (typeof arguments[0] == "object")
@@ -170,7 +197,7 @@ options = {
   offset; for internal use only true / false default false
 }
 */
-  obj: function(aObj, aMessage, aDisallowLog, level) {
+  obj: function TMP_console_obj(aObj, aMessage, aDisallowLog, level) {
     var offset = typeof level == "string" ? "  " : "";
     aMessage = aMessage ? offset + aMessage + "\n" : "";
     var objS = aObj ? offset + aObj.toString() : offset + "aObj is " + typeof(aObj);
@@ -198,11 +225,38 @@ options = {
     if (aDisallowLog)
       objS = aMessage + "======================\n" + objS;
     else
-      this.log(aMessage + "=============== Object Properties ===============\n" + objS, true, 1);
+      this.log(aMessage + "=============== Object Properties ===============\n" + objS, true);
     return objS;
   },
 
-  assert: function TMP_utils_assert(aError, aMsg) {
+  // RegExp to remove path/to/profile/extensions from filename
+  get _pathRegExp() {
+    delete this._pathRegExp;
+    let folder = TabmixSvc.FileUtils.getDir("ProfD", ["extensions"]);
+    let path = folder.path.replace("\\", "/", "g") + "/";
+    return this._pathRegExp = new RegExp("jar:|file:///|" + path, "g");
+  },
+
+  _formatStack: function(stack) {
+    let lines = [], _char = this._char, re = this._pathRegExp;
+    stack.forEach(function(line) {
+      let atIndex = line.indexOf("@");
+      let columnIndex = line.lastIndexOf(":");
+      let fileName = line.slice(atIndex + 1, columnIndex).split(" -> ").pop();
+      if (fileName) {
+        fileName = decodeURI(fileName).replace(re, "");
+        let lineNumber = parseInt(line.slice(columnIndex + 1));
+        let atIndex = line.indexOf(_char);
+        let name = line.slice(0, atIndex).split("(").shift() || "null";
+        lines.push('  File "' + fileName + '", line ' +
+            lineNumber + ', in ' + name);
+      }
+    });
+
+    return lines.join("\n");
+  },
+
+  assert: function TMP_console_assert(aError, aMsg) {
     if (typeof aError.stack != "string") {
       this.trace((aMsg || "") + "\n" + aError, 2);
       return;
@@ -212,14 +266,12 @@ options = {
     let errAt = " at " + names[0];
     let location = aError.location ? "\n" + aError.location : "";
     let assertionText = "Tabmix Plus ERROR" + errAt + ":\n" + (aMsg ? aMsg + "\n" : "") + aError.message + location;
-    let stackText = "\nStack Trace: \n" + decodeURI(aError.stack);
-    TabmixSvc.console.logStringMessage(assertionText + stackText);
+    let stackText = "\nStack Trace: \n" + this._formatStack(aError.stack.split("\n"));
+    Services.console.logStringMessage(assertionText + stackText);
   },
 
-  trace: function(aMsg, slice) {
-    // cut off the first line of the stack trace, because that's just this function.
-    let stack = Error().stack.split("\n").slice(slice || 1);
-
-    TabmixSvc.console.logStringMessage("Tabmix Trace: " + (aMsg || "") + '\n' + stack.join("\n"));
+  trace: function TMP_console_trace(aMsg) {
+    let stack = this._formatStack(this._getStackExcludingInternal());
+    Services.console.logStringMessage("Tabmix Trace: " + (aMsg || "") + '\n' + stack);
   }
 }

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-mozext/tabmixplus.git



More information about the Pkg-mozext-commits mailing list