diff -Nuar omni_old/chrome/browser/content/browser/browser.js omni/chrome/browser/content/browser/browser.js
--- omni_old/chrome/browser/content/browser/browser.js	2010-01-01 00:00:00.000000000 +0800
+++ omni/chrome/browser/content/browser/browser.js	2012-08-30 15:23:11.405250339 +0800
@@ -7852,11 +7852,30 @@
   if (aState == "valid") {
     gLastValidURLStr = gURLBar.value;
     gURLBar.addEventListener("input", UpdatePageProxyState, false);
+
+    PageProxySetIcon(gBrowser.getIcon());
   } else if (aState == "invalid") {
     gURLBar.removeEventListener("input", UpdatePageProxyState, false);
+    PageProxyClearIcon();
   }
 }
 
+function PageProxySetIcon (aURL)
+{
+  if (!gProxyFavIcon)
+    return;
+
+  if (!aURL)
+    PageProxyClearIcon();
+  else if (gProxyFavIcon.getAttribute("src") != aURL)
+    gProxyFavIcon.setAttribute("src", aURL);
+}
+
+function PageProxyClearIcon ()
+{
+  gProxyFavIcon.removeAttribute("src");
+}
+
 function PageProxyClickHandler(aEvent)
 {
   if (aEvent.button == 1 && gPrefService.getBoolPref("middlemouse.paste"))
@@ -9222,6 +9241,12 @@
     return "_blank";
   },
 
+  onLinkIconAvailable: function (aIconURL) {
+    console.log('onLinkIconAvailable: ' + aIconURL);
+    if (gProxyFavIcon && gBrowser.userTypedValue === null)
+      PageProxySetIcon(aIconURL); // update the favicon in the URL bar
+  },
+
   onProgressChange: function (aWebProgress, aRequest,
                               aCurSelfProgress, aMaxSelfProgress,
                               aCurTotalProgress, aMaxTotalProgress) {
@@ -11821,6 +11846,7 @@
     switch (newMode) {
     case this.IDENTITY_MODE_DOMAIN_VERIFIED: {
       let iData = this.getIdentityData();
+      icon_label = this.getEffectiveHost();
 
       // Verifier is either the CA Org, for a normal cert, or a special string
       // for certs that are trusted because of a security exception.
diff -Nuar omni_old/chrome/browser/content/browser/browser.js.orig omni/chrome/browser/content/browser/browser.js.orig
--- omni_old/chrome/browser/content/browser/browser.js.orig	1970-01-01 08:00:00.000000000 +0800
+++ omni/chrome/browser/content/browser/browser.js.orig	2010-01-01 00:00:00.000000000 +0800
@@ -0,0 +1,12748 @@
+//@line 5 "/build/src/mozilla-release/browser/base/content/browser.js"
+
+let Ci = Components.interfaces;
+let Cu = Components.utils;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const nsIWebNavigation = Ci.nsIWebNavigation;
+
+var gCharsetMenu = null;
+var gLastBrowserCharset = null;
+var gPrevCharset = null;
+var gProxyFavIcon = null;
+var gLastValidURLStr = "";
+var gInPrintPreviewMode = false;
+var gDownloadMgr = null;
+var gContextMenu = null; // nsContextMenu instance
+var gDelayedStartupTimeoutId;
+var gStartupRan = false;
+
+//@line 25 "/build/src/mozilla-release/browser/base/content/browser.js"
+var gEditUIVisible = true;
+//@line 27 "/build/src/mozilla-release/browser/base/content/browser.js"
+
+[
+  ["gBrowser",            "content"],
+  ["gNavToolbox",         "navigator-toolbox"],
+  ["gURLBar",             "urlbar"],
+  ["gNavigatorBundle",    "bundle_browser"]
+].forEach(function (elementGlobal) {
+  var [name, id] = elementGlobal;
+  window.__defineGetter__(name, function () {
+    var element = document.getElementById(id);
+    if (!element)
+      return null;
+    delete window[name];
+    return window[name] = element;
+  });
+  window.__defineSetter__(name, function (val) {
+    delete window[name];
+    return window[name] = val;
+  });
+});
+
+// Smart getter for the findbar.  If you don't wish to force the creation of
+// the findbar, check gFindBarInitialized first.
+var gFindBarInitialized = false;
+XPCOMUtils.defineLazyGetter(window, "gFindBar", function() {
+  let XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+  let findbar = document.createElementNS(XULNS, "findbar");
+  findbar.id = "FindToolbar";
+
+  let browserBottomBox = document.getElementById("browser-bottombox");
+  browserBottomBox.insertBefore(findbar, browserBottomBox.firstChild);
+
+  // Force a style flush to ensure that our binding is attached.
+  findbar.clientTop;
+  findbar.browser = gBrowser;
+  window.gFindBarInitialized = true;
+  return findbar;
+});
+
+__defineGetter__("gPrefService", function() {
+  delete this.gPrefService;
+  return this.gPrefService = Services.prefs;
+});
+
+__defineGetter__("AddonManager", function() {
+  let tmp = {};
+  Cu.import("resource://gre/modules/AddonManager.jsm", tmp);
+  return this.AddonManager = tmp.AddonManager;
+});
+__defineSetter__("AddonManager", function (val) {
+  delete this.AddonManager;
+  return this.AddonManager = val;
+});
+
+__defineGetter__("PluralForm", function() {
+  Cu.import("resource://gre/modules/PluralForm.jsm");
+  return this.PluralForm;
+});
+__defineSetter__("PluralForm", function (val) {
+  delete this.PluralForm;
+  return this.PluralForm = val;
+});
+
+XPCOMUtils.defineLazyModuleGetter(this, "TelemetryStopwatch",
+                                  "resource://gre/modules/TelemetryStopwatch.jsm");
+
+//@line 94 "/build/src/mozilla-release/browser/base/content/browser.js"
+XPCOMUtils.defineLazyGetter(this, "Weave", function() {
+  let tmp = {};
+  Cu.import("resource://services-sync/main.js", tmp);
+  return tmp.Weave;
+});
+//@line 100 "/build/src/mozilla-release/browser/base/content/browser.js"
+
+XPCOMUtils.defineLazyGetter(this, "PopupNotifications", function () {
+  let tmp = {};
+  Cu.import("resource://gre/modules/PopupNotifications.jsm", tmp);
+  try {
+    return new tmp.PopupNotifications(gBrowser,
+                                      document.getElementById("notification-popup"),
+                                      document.getElementById("notification-popup-box"));
+  } catch (ex) {
+    Cu.reportError(ex);
+  }
+});
+
+XPCOMUtils.defineLazyGetter(this, "DeveloperToolbar", function() {
+  let tmp = {};
+  Cu.import("resource:///modules/devtools/DeveloperToolbar.jsm", tmp);
+  return new tmp.DeveloperToolbar(window, document.getElementById("developer-toolbar"));
+});
+
+XPCOMUtils.defineLazyGetter(this, "InspectorUI", function() {
+  let tmp = {};
+  Cu.import("resource:///modules/inspector.jsm", tmp);
+  return new tmp.InspectorUI(window);
+});
+
+XPCOMUtils.defineLazyGetter(this, "DebuggerUI", function() {
+  let tmp = {};
+  Cu.import("resource:///modules/devtools/DebuggerUI.jsm", tmp);
+  return new tmp.DebuggerUI(window);
+});
+
+XPCOMUtils.defineLazyGetter(this, "Tilt", function() {
+  let tmp = {};
+  Cu.import("resource:///modules/devtools/Tilt.jsm", tmp);
+  return new tmp.Tilt(window);
+});
+
+let gInitialPages = [
+  "about:blank",
+  "about:newtab",
+  "about:privatebrowsing",
+  "about:sessionrestore"
+];
+
+//@line 5 "/build/src/mozilla-release/browser/base/content/browser-addons.js"
+
+const gXPInstallObserver = {
+  _findChildShell: function (aDocShell, aSoughtShell)
+  {
+    if (aDocShell == aSoughtShell)
+      return aDocShell;
+
+    var node = aDocShell.QueryInterface(Components.interfaces.nsIDocShellTreeNode);
+    for (var i = 0; i < node.childCount; ++i) {
+      var docShell = node.getChildAt(i);
+      docShell = this._findChildShell(docShell, aSoughtShell);
+      if (docShell == aSoughtShell)
+        return docShell;
+    }
+    return null;
+  },
+
+  _getBrowser: function (aDocShell)
+  {
+    for (var i = 0; i < gBrowser.browsers.length; ++i) {
+      var browser = gBrowser.getBrowserAtIndex(i);
+      if (this._findChildShell(browser.docShell, aDocShell))
+        return browser;
+    }
+    return null;
+  },
+
+  observe: function (aSubject, aTopic, aData)
+  {
+    var brandBundle = document.getElementById("bundle_brand");
+    var installInfo = aSubject.QueryInterface(Components.interfaces.amIWebInstallInfo);
+    var win = installInfo.originatingWindow;
+    var shell = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+                   .getInterface(Components.interfaces.nsIWebNavigation)
+                   .QueryInterface(Components.interfaces.nsIDocShell);
+    var browser = this._getBrowser(shell);
+    if (!browser)
+      return;
+    const anchorID = "addons-notification-icon";
+    var messageString, action;
+    var brandShortName = brandBundle.getString("brandShortName");
+
+    var notificationID = aTopic;
+    // Make notifications persist a minimum of 30 seconds
+    var options = {
+      timeout: Date.now() + 30000
+    };
+
+    switch (aTopic) {
+    case "addon-install-disabled":
+      notificationID = "xpinstall-disabled"
+
+      if (gPrefService.prefIsLocked("xpinstall.enabled")) {
+        messageString = gNavigatorBundle.getString("xpinstallDisabledMessageLocked");
+        buttons = [];
+      }
+      else {
+        messageString = gNavigatorBundle.getString("xpinstallDisabledMessage");
+
+        action = {
+          label: gNavigatorBundle.getString("xpinstallDisabledButton"),
+          accessKey: gNavigatorBundle.getString("xpinstallDisabledButton.accesskey"),
+          callback: function editPrefs() {
+            gPrefService.setBoolPref("xpinstall.enabled", true);
+          }
+        };
+      }
+
+      PopupNotifications.show(browser, notificationID, messageString, anchorID,
+                              action, null, options);
+      break;
+    case "addon-install-blocked":
+      messageString = gNavigatorBundle.getFormattedString("xpinstallPromptWarning",
+                        [brandShortName, installInfo.originatingURI.host]);
+
+      action = {
+        label: gNavigatorBundle.getString("xpinstallPromptAllowButton"),
+        accessKey: gNavigatorBundle.getString("xpinstallPromptAllowButton.accesskey"),
+        callback: function() {
+          installInfo.install();
+        }
+      };
+
+      PopupNotifications.show(browser, notificationID, messageString, anchorID,
+                              action, null, options);
+      break;
+    case "addon-install-started":
+      function needsDownload(aInstall) {
+        return aInstall.state != AddonManager.STATE_DOWNLOADED;
+      }
+      // If all installs have already been downloaded then there is no need to
+      // show the download progress
+      if (!installInfo.installs.some(needsDownload))
+        return;
+      notificationID = "addon-progress";
+      messageString = gNavigatorBundle.getString("addonDownloading");
+      messageString = PluralForm.get(installInfo.installs.length, messageString);
+      options.installs = installInfo.installs;
+      options.contentWindow = browser.contentWindow;
+      options.sourceURI = browser.currentURI;
+      options.eventCallback = function(aEvent) {
+        if (aEvent != "removed")
+          return;
+        options.contentWindow = null;
+        options.sourceURI = null;
+      };
+      PopupNotifications.show(browser, notificationID, messageString, anchorID,
+                              null, null, options);
+      break;
+    case "addon-install-failed":
+      // TODO This isn't terribly ideal for the multiple failure case
+      installInfo.installs.forEach(function(aInstall) {
+        var host = (installInfo.originatingURI instanceof Ci.nsIStandardURL) &&
+                   installInfo.originatingURI.host;
+        if (!host)
+          host = (aInstall.sourceURI instanceof Ci.nsIStandardURL) &&
+                 aInstall.sourceURI.host;
+
+        var error = (host || aInstall.error == 0) ? "addonError" : "addonLocalError";
+        if (aInstall.error != 0)
+          error += aInstall.error;
+        else if (aInstall.addon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED)
+          error += "Blocklisted";
+        else
+          error += "Incompatible";
+
+        messageString = gNavigatorBundle.getString(error);
+        messageString = messageString.replace("#1", aInstall.name);
+        if (host)
+          messageString = messageString.replace("#2", host);
+        messageString = messageString.replace("#3", brandShortName);
+        messageString = messageString.replace("#4", Services.appinfo.version);
+
+        PopupNotifications.show(browser, notificationID, messageString, anchorID,
+                                action, null, options);
+      });
+      break;
+    case "addon-install-complete":
+      var needsRestart = installInfo.installs.some(function(i) {
+        return i.addon.pendingOperations != AddonManager.PENDING_NONE;
+      });
+
+      if (needsRestart) {
+        messageString = gNavigatorBundle.getString("addonsInstalledNeedsRestart");
+        action = {
+          label: gNavigatorBundle.getString("addonInstallRestartButton"),
+          accessKey: gNavigatorBundle.getString("addonInstallRestartButton.accesskey"),
+          callback: function() {
+            Application.restart();
+          }
+        };
+      }
+      else {
+        messageString = gNavigatorBundle.getString("addonsInstalled");
+        action = {
+          label: gNavigatorBundle.getString("addonInstallManage"),
+          accessKey: gNavigatorBundle.getString("addonInstallManage.accesskey"),
+          callback: function() {
+            // Calculate the add-on type that is most popular in the list of
+            // installs
+            var types = {};
+            var bestType = null;
+            installInfo.installs.forEach(function(aInstall) {
+              if (aInstall.type in types)
+                types[aInstall.type]++;
+              else
+                types[aInstall.type] = 1;
+              if (!bestType || types[aInstall.type] > types[bestType])
+                bestType = aInstall.type;
+            });
+
+            BrowserOpenAddonsMgr("addons://list/" + bestType);
+          }
+        };
+      }
+
+      messageString = PluralForm.get(installInfo.installs.length, messageString);
+      messageString = messageString.replace("#1", installInfo.installs[0].name);
+      messageString = messageString.replace("#2", installInfo.installs.length);
+      messageString = messageString.replace("#3", brandShortName);
+
+      // Remove notificaion on dismissal, since it's possible to cancel the
+      // install through the addons manager UI, making the "restart" prompt
+      // irrelevant.
+      options.removeOnDismissal = true;
+
+      PopupNotifications.show(browser, notificationID, messageString, anchorID,
+                              action, null, options);
+      break;
+    }
+  }
+};
+
+/*
+ * When addons are installed/uninstalled, check and see if the number of items
+ * on the add-on bar changed:
+ * - If an add-on was installed, incrementing the count, show the bar.
+ * - If an add-on was uninstalled, and no more items are left, hide the bar.
+ */
+let AddonsMgrListener = {
+  get addonBar() document.getElementById("addon-bar"),
+  get statusBar() document.getElementById("status-bar"),
+  getAddonBarItemCount: function() {
+    // Take into account the contents of the status bar shim for the count.
+    var itemCount = this.statusBar.childNodes.length;
+
+    var defaultOrNoninteractive = this.addonBar.getAttribute("defaultset")
+                                      .split(",")
+                                      .concat(["separator", "spacer", "spring"]);
+    this.addonBar.currentSet.split(",").forEach(function (item) {
+      if (defaultOrNoninteractive.indexOf(item) == -1)
+        itemCount++;
+    });
+
+    return itemCount;
+  },
+  onInstalling: function(aAddon) {
+    this.lastAddonBarCount = this.getAddonBarItemCount();
+  },
+  onInstalled: function(aAddon) {
+    if (this.getAddonBarItemCount() > this.lastAddonBarCount)
+      setToolbarVisibility(this.addonBar, true);
+  },
+  onUninstalling: function(aAddon) {
+    this.lastAddonBarCount = this.getAddonBarItemCount();
+  },
+  onUninstalled: function(aAddon) {
+    if (this.getAddonBarItemCount() == 0)
+      setToolbarVisibility(this.addonBar, false);
+  },
+  onEnabling: function(aAddon) this.onInstalling(),
+  onEnabled: function(aAddon) this.onInstalled(),
+  onDisabling: function(aAddon) this.onUninstalling(),
+  onDisabled: function(aAddon) this.onUninstalled(),
+};
+
+
+var LightWeightThemeWebInstaller = {
+  handleEvent: function (event) {
+    switch (event.type) {
+      case "InstallBrowserTheme":
+      case "PreviewBrowserTheme":
+      case "ResetBrowserThemePreview":
+        // ignore requests from background tabs
+        if (event.target.ownerDocument.defaultView.top != content)
+          return;
+    }
+    switch (event.type) {
+      case "InstallBrowserTheme":
+        this._installRequest(event);
+        break;
+      case "PreviewBrowserTheme":
+        this._preview(event);
+        break;
+      case "ResetBrowserThemePreview":
+        this._resetPreview(event);
+        break;
+      case "pagehide":
+      case "TabSelect":
+        this._resetPreview();
+        break;
+    }
+  },
+
+  get _manager () {
+    var temp = {};
+    Cu.import("resource://gre/modules/LightweightThemeManager.jsm", temp);
+    delete this._manager;
+    return this._manager = temp.LightweightThemeManager;
+  },
+
+  _installRequest: function (event) {
+    var node = event.target;
+    var data = this._getThemeFromNode(node);
+    if (!data)
+      return;
+
+    if (this._isAllowed(node)) {
+      this._install(data);
+      return;
+    }
+
+    var allowButtonText =
+      gNavigatorBundle.getString("lwthemeInstallRequest.allowButton");
+    var allowButtonAccesskey =
+      gNavigatorBundle.getString("lwthemeInstallRequest.allowButton.accesskey");
+    var message =
+      gNavigatorBundle.getFormattedString("lwthemeInstallRequest.message",
+                                          [node.ownerDocument.location.host]);
+    var buttons = [{
+      label: allowButtonText,
+      accessKey: allowButtonAccesskey,
+      callback: function () {
+        LightWeightThemeWebInstaller._install(data);
+      }
+    }];
+
+    this._removePreviousNotifications();
+
+    var notificationBox = gBrowser.getNotificationBox();
+    var notificationBar =
+      notificationBox.appendNotification(message, "lwtheme-install-request", "",
+                                         notificationBox.PRIORITY_INFO_MEDIUM,
+                                         buttons);
+    notificationBar.persistence = 1;
+  },
+
+  _install: function (newLWTheme) {
+    var previousLWTheme = this._manager.currentTheme;
+
+    var listener = {
+      onEnabling: function(aAddon, aRequiresRestart) {
+        if (!aRequiresRestart)
+          return;
+
+        let messageString = gNavigatorBundle.getFormattedString("lwthemeNeedsRestart.message",
+          [aAddon.name], 1);
+
+        let action = {
+          label: gNavigatorBundle.getString("lwthemeNeedsRestart.button"),
+          accessKey: gNavigatorBundle.getString("lwthemeNeedsRestart.accesskey"),
+          callback: function () {
+            Application.restart();
+          }
+        };
+
+        let options = {
+          timeout: Date.now() + 30000
+        };
+
+        PopupNotifications.show(gBrowser.selectedBrowser, "addon-theme-change",
+                                messageString, "addons-notification-icon",
+                                action, null, options);
+      },
+
+      onEnabled: function(aAddon) {
+        LightWeightThemeWebInstaller._postInstallNotification(newLWTheme, previousLWTheme);
+      }
+    };
+
+    AddonManager.addAddonListener(listener);
+    this._manager.currentTheme = newLWTheme;
+    AddonManager.removeAddonListener(listener);
+  },
+
+  _postInstallNotification: function (newTheme, previousTheme) {
+    function text(id) {
+      return gNavigatorBundle.getString("lwthemePostInstallNotification." + id);
+    }
+
+    var buttons = [{
+      label: text("undoButton"),
+      accessKey: text("undoButton.accesskey"),
+      callback: function () {
+        LightWeightThemeWebInstaller._manager.forgetUsedTheme(newTheme.id);
+        LightWeightThemeWebInstaller._manager.currentTheme = previousTheme;
+      }
+    }, {
+      label: text("manageButton"),
+      accessKey: text("manageButton.accesskey"),
+      callback: function () {
+        BrowserOpenAddonsMgr("addons://list/theme");
+      }
+    }];
+
+    this._removePreviousNotifications();
+
+    var notificationBox = gBrowser.getNotificationBox();
+    var notificationBar =
+      notificationBox.appendNotification(text("message"),
+                                         "lwtheme-install-notification", "",
+                                         notificationBox.PRIORITY_INFO_MEDIUM,
+                                         buttons);
+    notificationBar.persistence = 1;
+    notificationBar.timeout = Date.now() + 20000; // 20 seconds
+  },
+
+  _removePreviousNotifications: function () {
+    var box = gBrowser.getNotificationBox();
+
+    ["lwtheme-install-request",
+     "lwtheme-install-notification"].forEach(function (value) {
+        var notification = box.getNotificationWithValue(value);
+        if (notification)
+          box.removeNotification(notification);
+      });
+  },
+
+  _previewWindow: null,
+  _preview: function (event) {
+    if (!this._isAllowed(event.target))
+      return;
+
+    var data = this._getThemeFromNode(event.target);
+    if (!data)
+      return;
+
+    this._resetPreview();
+
+    this._previewWindow = event.target.ownerDocument.defaultView;
+    this._previewWindow.addEventListener("pagehide", this, true);
+    gBrowser.tabContainer.addEventListener("TabSelect", this, false);
+
+    this._manager.previewTheme(data);
+  },
+
+  _resetPreview: function (event) {
+    if (!this._previewWindow ||
+        event && !this._isAllowed(event.target))
+      return;
+
+    this._previewWindow.removeEventListener("pagehide", this, true);
+    this._previewWindow = null;
+    gBrowser.tabContainer.removeEventListener("TabSelect", this, false);
+
+    this._manager.resetPreview();
+  },
+
+  _isAllowed: function (node) {
+    var pm = Services.perms;
+
+    var uri = node.ownerDocument.documentURIObject;
+    return pm.testPermission(uri, "install") == pm.ALLOW_ACTION;
+  },
+
+  _getThemeFromNode: function (node) {
+    return this._manager.parseTheme(node.getAttribute("data-browsertheme"),
+                                    node.baseURI);
+  }
+}
+//@line 5 "/build/src/mozilla-release/browser/base/content/browser-feeds.js"
+
+/**
+ * The Feed Handler object manages discovery of RSS/ATOM feeds in web pages
+ * and shows UI when they are discovered.
+ */
+var FeedHandler = {
+  /**
+   * The click handler for the Feed icon in the toolbar. Opens the
+   * subscription page if user is not given a choice of feeds.
+   * (Otherwise the list of available feeds will be presented to the
+   * user in a popup menu.)
+   */
+  onFeedButtonClick: function(event) {
+    event.stopPropagation();
+
+    let feeds = gBrowser.selectedBrowser.feeds || [];
+    // If there are multiple feeds, the menu will open, so no need to do
+    // anything. If there are no feeds, nothing to do either.
+    if (feeds.length != 1)
+      return;
+
+    if (event.eventPhase == Event.AT_TARGET &&
+        (event.button == 0 || event.button == 1)) {
+      this.subscribeToFeed(feeds[0].href, event);
+    }
+  },
+
+ /** Called when the user clicks on the Subscribe to This Page... menu item.
+   * Builds a menu of unique feeds associated with the page, and if there
+   * is only one, shows the feed inline in the browser window.
+   * @param   menuPopup
+   *          The feed list menupopup to be populated.
+   * @returns true if the menu should be shown, false if there was only
+   *          one feed and the feed should be shown inline in the browser
+   *          window (do not show the menupopup).
+   */
+  buildFeedList: function(menuPopup) {
+    var feeds = gBrowser.selectedBrowser.feeds;
+    if (feeds == null) {
+      // XXX hack -- menu opening depends on setting of an "open"
+      // attribute, and the menu refuses to open if that attribute is
+      // set (because it thinks it's already open).  onpopupshowing gets
+      // called after the attribute is unset, and it doesn't get unset
+      // if we return false.  so we unset it here; otherwise, the menu
+      // refuses to work past this point.
+      menuPopup.parentNode.removeAttribute("open");
+      return false;
+    }
+
+    while (menuPopup.firstChild)
+      menuPopup.removeChild(menuPopup.firstChild);
+
+    if (feeds.length <= 1)
+      return false;
+
+    // Build the menu showing the available feed choices for viewing.
+    for (var i = 0; i < feeds.length; ++i) {
+      var feedInfo = feeds[i];
+      var menuItem = document.createElement("menuitem");
+      var baseTitle = feedInfo.title || feedInfo.href;
+      var labelStr = gNavigatorBundle.getFormattedString("feedShowFeedNew", [baseTitle]);
+      menuItem.setAttribute("class", "feed-menuitem");
+      menuItem.setAttribute("label", labelStr);
+      menuItem.setAttribute("feed", feedInfo.href);
+      menuItem.setAttribute("tooltiptext", feedInfo.href);
+      menuItem.setAttribute("crop", "center");
+      menuPopup.appendChild(menuItem);
+    }
+    return true;
+  },
+
+  /**
+   * Subscribe to a given feed.  Called when
+   *   1. Page has a single feed and user clicks feed icon in location bar
+   *   2. Page has a single feed and user selects Subscribe menu item
+   *   3. Page has multiple feeds and user selects from feed icon popup
+   *   4. Page has multiple feeds and user selects from Subscribe submenu
+   * @param   href
+   *          The feed to subscribe to. May be null, in which case the
+   *          event target's feed attribute is examined.
+   * @param   event
+   *          The event this method is handling. Used to decide where
+   *          to open the preview UI. (Optional, unless href is null)
+   */
+  subscribeToFeed: function(href, event) {
+    // Just load the feed in the content area to either subscribe or show the
+    // preview UI
+    if (!href)
+      href = event.target.getAttribute("feed");
+    urlSecurityCheck(href, gBrowser.contentPrincipal,
+                     Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
+    var feedURI = makeURI(href, document.characterSet);
+    // Use the feed scheme so X-Moz-Is-Feed will be set
+    // The value doesn't matter
+    if (/^https?/.test(feedURI.scheme))
+      href = "feed:" + href;
+    this.loadFeed(href, event);
+  },
+
+  loadFeed: function(href, event) {
+    var feeds = gBrowser.selectedBrowser.feeds;
+    try {
+      openUILink(href, event, { ignoreAlt: true });
+    }
+    finally {
+      // We might default to a livebookmarks modal dialog,
+      // so reset that if the user happens to click it again
+      gBrowser.selectedBrowser.feeds = feeds;
+    }
+  },
+
+  get _feedMenuitem() {
+    delete this._feedMenuitem;
+    return this._feedMenuitem = document.getElementById("singleFeedMenuitemState");
+  },
+
+  get _feedMenupopup() {
+    delete this._feedMenupopup;
+    return this._feedMenupopup = document.getElementById("multipleFeedsMenuState");
+  },
+
+  /**
+   * Update the browser UI to show whether or not feeds are available when
+   * a page is loaded or the user switches tabs to a page that has feeds.
+   */
+  updateFeeds: function() {
+    if (this._updateFeedTimeout)
+      clearTimeout(this._updateFeedTimeout);
+
+    var feeds = gBrowser.selectedBrowser.feeds;
+    var haveFeeds = feeds && feeds.length > 0;
+
+    var feedButton = document.getElementById("feed-button");
+    if (feedButton)
+      feedButton.disabled = !haveFeeds;
+
+    if (!haveFeeds) {
+      this._feedMenuitem.setAttribute("disabled", "true");
+      this._feedMenuitem.removeAttribute("hidden");
+      this._feedMenupopup.setAttribute("hidden", "true");
+      return;
+    }
+
+    if (feeds.length > 1) {
+      this._feedMenuitem.setAttribute("hidden", "true");
+      this._feedMenupopup.removeAttribute("hidden");
+    } else {
+      this._feedMenuitem.setAttribute("feed", feeds[0].href);
+      this._feedMenuitem.removeAttribute("disabled");
+      this._feedMenuitem.removeAttribute("hidden");
+      this._feedMenupopup.setAttribute("hidden", "true");
+    }
+  },
+
+  addFeed: function(link, targetDoc) {
+    // find which tab this is for, and set the attribute on the browser
+    var browserForLink = gBrowser.getBrowserForDocument(targetDoc);
+    if (!browserForLink) {
+      // ignore feeds loaded in subframes (see bug 305472)
+      return;
+    }
+
+    if (!browserForLink.feeds)
+      browserForLink.feeds = [];
+
+    browserForLink.feeds.push({ href: link.href, title: link.title });
+
+    // If this addition was for the current browser, update the UI. For
+    // background browsers, we'll update on tab switch.
+    if (browserForLink == gBrowser.selectedBrowser) {
+      // Batch updates to avoid updating the UI for multiple onLinkAdded events
+      // fired within 100ms of each other.
+      if (this._updateFeedTimeout)
+        clearTimeout(this._updateFeedTimeout);
+      this._updateFeedTimeout = setTimeout(this.updateFeeds.bind(this), 100);
+    }
+  }
+};
+//@line 5 "/build/src/mozilla-release/browser/base/content/browser-fullScreen.js"
+
+var FullScreen = {
+  _XULNS: "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
+  toggle: function (event) {
+    var enterFS = window.fullScreen;
+
+    // We get the fullscreen event _before_ the window transitions into or out of FS mode.
+    if (event && event.type == "fullscreen")
+      enterFS = !enterFS;
+
+    // Toggle the View:FullScreen command, which controls elements like the
+    // fullscreen menuitem, menubars, and the appmenu.
+    document.getElementById("View:FullScreen").setAttribute("checked", enterFS);
+
+//@line 24 "/build/src/mozilla-release/browser/base/content/browser-fullScreen.js"
+
+    // On OS X Lion we don't want to hide toolbars when entering fullscreen, unless
+    // we're entering DOM fullscreen, in which case we should hide the toolbars.
+    // If we're leaving fullscreen, then we'll go through the exit code below to
+    // make sure toolbars are made visible in the case of DOM fullscreen.
+    if (enterFS && this.useLionFullScreen) {
+      if (document.mozFullScreen)
+        this.showXULChrome("toolbar", false);
+      return;
+    }
+
+    // show/hide menubars, toolbars (except the full screen toolbar)
+    this.showXULChrome("toolbar", !enterFS);
+
+    if (enterFS) {
+      // Add a tiny toolbar to receive mouseover and dragenter events, and provide affordance.
+      // This will help simulate the "collapse" metaphor while also requiring less code and
+      // events than raw listening of mouse coords. We don't add the toolbar in DOM full-screen
+      // mode, only browser full-screen mode.
+      if (!document.mozFullScreen) {
+        let fullScrToggler = document.getElementById("fullscr-toggler");
+        if (!fullScrToggler) {
+          fullScrToggler = document.createElement("hbox");
+          fullScrToggler.id = "fullscr-toggler";
+          fullScrToggler.collapsed = true;
+          gNavToolbox.parentNode.insertBefore(fullScrToggler, gNavToolbox.nextSibling);
+        }
+        fullScrToggler.addEventListener("mouseover", this._expandCallback, false);
+        fullScrToggler.addEventListener("dragenter", this._expandCallback, false);
+      }
+      if (gPrefService.getBoolPref("browser.fullscreen.autohide"))
+        gBrowser.mPanelContainer.addEventListener("mousemove",
+                                                  this._collapseCallback, false);
+
+      document.addEventListener("keypress", this._keyToggleCallback, false);
+      document.addEventListener("popupshown", this._setPopupOpen, false);
+      document.addEventListener("popuphidden", this._setPopupOpen, false);
+      // We don't animate the toolbar collapse if in DOM full-screen mode,
+      // as the size of the content area would still be changing after the
+      // mozfullscreenchange event fired, which could confuse content script.
+      this._shouldAnimate = !document.mozFullScreen;
+      this.mouseoverToggle(false);
+
+      // Autohide prefs
+      gPrefService.addObserver("browser.fullscreen", this, false);
+    }
+    else {
+      // The user may quit fullscreen during an animation
+      this._cancelAnimation();
+      gNavToolbox.style.marginTop = "";
+      if (this._isChromeCollapsed)
+        this.mouseoverToggle(true);
+      // This is needed if they use the context menu to quit fullscreen
+      this._isPopupOpen = false;
+
+      this.cleanup();
+    }
+  },
+
+  exitDomFullScreen : function() {
+    document.mozCancelFullScreen();
+  },
+
+  handleEvent: function (event) {
+    switch (event.type) {
+      case "deactivate":
+        // We must call exitDomFullScreen asynchronously, since "deactivate" is
+        // dispatched in the middle of the focus manager's window lowering code,
+        // and the focus manager gets confused if we exit fullscreen mode in the
+        // middle of window lowering. See bug 729872.
+        setTimeout(this.exitDomFullScreen.bind(this), 0);
+        break;
+      case "transitionend":
+        if (event.propertyName == "opacity")
+          this.cancelWarning();
+        break;
+    }
+  },
+
+  enterDomFullscreen : function(event) {
+    if (!document.mozFullScreen)
+      return;
+
+    // However, if we receive a "MozEnteredDomFullScreen" event for a document
+    // which is not a subdocument of the currently selected tab, we know that
+    // we've switched tabs since the request to enter full-screen was made,
+    // so we should exit full-screen since the "full-screen document" isn't
+    // acutally visible.
+    if (event.target.defaultView.top != gBrowser.contentWindow) {
+      document.mozCancelFullScreen();
+      return;
+    }
+
+    let focusManager = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
+    if (focusManager.activeWindow != window) {
+      // The top-level window has lost focus since the request to enter
+      // full-screen was made. Cancel full-screen.
+      document.mozCancelFullScreen();
+      return;
+    }
+
+    // Ensure the sidebar is hidden.
+    if (!document.getElementById("sidebar-box").hidden)
+      toggleSidebar();
+
+    if (gFindBarInitialized)
+      gFindBar.close();
+
+    this.showWarning(event.target);
+
+    // Exit DOM full-screen mode upon open, close, or change tab.
+    gBrowser.tabContainer.addEventListener("TabOpen", this.exitDomFullScreen);
+    gBrowser.tabContainer.addEventListener("TabClose", this.exitDomFullScreen);
+    gBrowser.tabContainer.addEventListener("TabSelect", this.exitDomFullScreen);
+
+    // Exit DOM full-screen mode when the browser window loses focus (ALT+TAB, etc).
+    if (!this.useLionFullScreen &&
+        gPrefService.getBoolPref("full-screen-api.exit-on-deactivate")) {
+      window.addEventListener("deactivate", this);
+    }
+
+    // Cancel any "hide the toolbar" animation which is in progress, and make
+    // the toolbar hide immediately.
+    this._cancelAnimation();
+    this.mouseoverToggle(false);
+
+    // If there's a full-screen toggler, remove its listeners, so that mouseover
+    // the top of the screen will not cause the toolbar to re-appear.
+    let fullScrToggler = document.getElementById("fullscr-toggler");
+    if (fullScrToggler) {
+      fullScrToggler.removeEventListener("mouseover", this._expandCallback, false);
+      fullScrToggler.removeEventListener("dragenter", this._expandCallback, false);
+    }
+  },
+
+  cleanup: function () {
+    if (window.fullScreen) {
+      gBrowser.mPanelContainer.removeEventListener("mousemove",
+                                                   this._collapseCallback, false);
+      document.removeEventListener("keypress", this._keyToggleCallback, false);
+      document.removeEventListener("popupshown", this._setPopupOpen, false);
+      document.removeEventListener("popuphidden", this._setPopupOpen, false);
+      gPrefService.removeObserver("browser.fullscreen", this);
+
+      let fullScrToggler = document.getElementById("fullscr-toggler");
+      if (fullScrToggler) {
+        fullScrToggler.removeEventListener("mouseover", this._expandCallback, false);
+        fullScrToggler.removeEventListener("dragenter", this._expandCallback, false);
+      }
+      this.cancelWarning();
+      gBrowser.tabContainer.removeEventListener("TabOpen", this.exitDomFullScreen);
+      gBrowser.tabContainer.removeEventListener("TabClose", this.exitDomFullScreen);
+      gBrowser.tabContainer.removeEventListener("TabSelect", this.exitDomFullScreen);
+      if (!this.useLionFullScreen)
+        window.removeEventListener("deactivate", this);
+    }
+  },
+
+  observe: function(aSubject, aTopic, aData)
+  {
+    if (aData == "browser.fullscreen.autohide") {
+      if (gPrefService.getBoolPref("browser.fullscreen.autohide")) {
+        gBrowser.mPanelContainer.addEventListener("mousemove",
+                                                  this._collapseCallback, false);
+      }
+      else {
+        gBrowser.mPanelContainer.removeEventListener("mousemove",
+                                                     this._collapseCallback, false);
+      }
+    }
+  },
+
+  // Event callbacks
+  _expandCallback: function()
+  {
+    FullScreen.mouseoverToggle(true);
+  },
+  _collapseCallback: function()
+  {
+    FullScreen.mouseoverToggle(false);
+  },
+  _keyToggleCallback: function(aEvent)
+  {
+    // if we can use the keyboard (eg Ctrl+L or Ctrl+E) to open the toolbars, we
+    // should provide a way to collapse them too.
+    if (aEvent.keyCode == aEvent.DOM_VK_ESCAPE) {
+      FullScreen._shouldAnimate = false;
+      FullScreen.mouseoverToggle(false, true);
+    }
+    // F6 is another shortcut to the address bar, but its not covered in OpenLocation()
+    else if (aEvent.keyCode == aEvent.DOM_VK_F6)
+      FullScreen.mouseoverToggle(true);
+  },
+
+  // Checks whether we are allowed to collapse the chrome
+  _isPopupOpen: false,
+  _isChromeCollapsed: false,
+  _safeToCollapse: function(forceHide)
+  {
+    if (!gPrefService.getBoolPref("browser.fullscreen.autohide"))
+      return false;
+
+    // a popup menu is open in chrome: don't collapse chrome
+    if (!forceHide && this._isPopupOpen)
+      return false;
+
+    // a textbox in chrome is focused (location bar anyone?): don't collapse chrome
+    if (document.commandDispatcher.focusedElement &&
+        document.commandDispatcher.focusedElement.ownerDocument == document &&
+        document.commandDispatcher.focusedElement.localName == "input") {
+      if (forceHide)
+        // hidden textboxes that still have focus are bad bad bad
+        document.commandDispatcher.focusedElement.blur();
+      else
+        return false;
+    }
+    return true;
+  },
+
+  _setPopupOpen: function(aEvent)
+  {
+    // Popups should only veto chrome collapsing if they were opened when the chrome was not collapsed.
+    // Otherwise, they would not affect chrome and the user would expect the chrome to go away.
+    // e.g. we wouldn't want the autoscroll icon firing this event, so when the user
+    // toggles chrome when moving mouse to the top, it doesn't go away again.
+    if (aEvent.type == "popupshown" && !FullScreen._isChromeCollapsed &&
+        aEvent.target.localName != "tooltip" && aEvent.target.localName != "window")
+      FullScreen._isPopupOpen = true;
+    else if (aEvent.type == "popuphidden" && aEvent.target.localName != "tooltip" &&
+             aEvent.target.localName != "window")
+      FullScreen._isPopupOpen = false;
+  },
+
+  // Autohide helpers for the context menu item
+  getAutohide: function(aItem)
+  {
+    aItem.setAttribute("checked", gPrefService.getBoolPref("browser.fullscreen.autohide"));
+  },
+  setAutohide: function()
+  {
+    gPrefService.setBoolPref("browser.fullscreen.autohide", !gPrefService.getBoolPref("browser.fullscreen.autohide"));
+  },
+
+  // Animate the toolbars disappearing
+  _shouldAnimate: true,
+  _isAnimating: false,
+  _animationTimeout: 0,
+  _animationHandle: 0,
+  _animateUp: function() {
+    // check again, the user may have done something before the animation was due to start
+    if (!window.fullScreen || !this._safeToCollapse(false)) {
+      this._isAnimating = false;
+      this._shouldAnimate = true;
+      return;
+    }
+
+    this._animateStartTime = window.mozAnimationStartTime;
+    if (!this._animationHandle)
+      this._animationHandle = window.mozRequestAnimationFrame(this);
+  },
+
+  sample: function (timeStamp) {
+    const duration = 1500;
+    const timePassed = timeStamp - this._animateStartTime;
+    const pos = timePassed >= duration ? 1 :
+                1 - Math.pow(1 - timePassed / duration, 4);
+
+    if (pos >= 1) {
+      // We've animated enough
+      this._cancelAnimation();
+      gNavToolbox.style.marginTop = "";
+      this.mouseoverToggle(false);
+      return;
+    }
+
+    gNavToolbox.style.marginTop = (gNavToolbox.boxObject.height * pos * -1) + "px";
+    this._animationHandle = window.mozRequestAnimationFrame(this);
+  },
+
+  _cancelAnimation: function() {
+    window.mozCancelAnimationFrame(this._animationHandle);
+    this._animationHandle = 0;
+    clearTimeout(this._animationTimeout);
+    this._isAnimating = false;
+    this._shouldAnimate = false;
+  },
+
+  cancelWarning: function(event) {
+    if (!this.warningBox)
+      return;
+    this.fullscreenDoc = null;
+    this.warningBox.removeEventListener("transitionend", this);
+    if (this.warningFadeOutTimeout) {
+      clearTimeout(this.warningFadeOutTimeout);
+      this.warningFadeOutTimeout = null;
+    }
+
+    // Ensure focus switches away from the (now hidden) warning box. If the user
+    // clicked buttons in the fullscreen key authorization UI, it would have been
+    // focused, and any key events would be directed at the (now hidden) chrome
+    // document instead of the target document.
+    gBrowser.selectedBrowser.focus();
+
+    this.warningBox.setAttribute("hidden", true);
+    this.warningBox.removeAttribute("fade-warning-out");
+    this.warningBox.removeAttribute("obscure-browser");
+    this.warningBox = null;
+  },
+
+  setFullscreenAllowed: function(isApproved) {
+    // The "remember decision" checkbox is hidden when showing for documents that
+    // the permission manager can't handle (documents with URIs without a host).
+    // We simply require those to be approved every time instead.
+    let rememberCheckbox = document.getElementById("full-screen-remember-decision");
+    let uri = this.fullscreenDoc.nodePrincipal.URI;
+    if (!rememberCheckbox.hidden) {
+      if (rememberCheckbox.checked)
+        Services.perms.add(uri,
+                           "fullscreen",
+                           isApproved ? Services.perms.ALLOW_ACTION : Services.perms.DENY_ACTION,
+                           Services.perms.EXPIRE_NEVER);
+      else if (isApproved) {
+        // The user has only temporarily approved fullscren for this fullscreen
+        // session only. Add the permission (so Gecko knows to approve any further
+        // fullscreen requests for this host in this fullscreen session) but add
+        // a listener to revoke the permission when the chrome document exits
+        // fullscreen.
+        Services.perms.add(uri,
+                           "fullscreen",
+                           Services.perms.ALLOW_ACTION,
+                           Services.perms.EXPIRE_SESSION);
+        let host = uri.host;
+        function onFullscreenchange(event) {
+          if (event.target == document && document.mozFullScreenElement == null) {
+            // The chrome document has left fullscreen. Remove the temporary permission grant.
+            Services.perms.remove(host, "fullscreen");
+            document.removeEventListener("mozfullscreenchange", onFullscreenchange);
+          }
+        }
+        document.addEventListener("mozfullscreenchange", onFullscreenchange);
+      }
+    }
+    if (this.warningBox)
+      this.warningBox.setAttribute("fade-warning-out", "true");
+    // If the document has been granted fullscreen, notify Gecko so it can resume
+    // any pending pointer lock requests, otherwise exit fullscreen; the user denied
+    // the fullscreen request.
+    if (isApproved)
+      Services.obs.notifyObservers(this.fullscreenDoc, "fullscreen-approved", "");
+    else
+      document.mozCancelFullScreen();
+  },
+
+  warningBox: null,
+  warningFadeOutTimeout: null,
+  fullscreenDoc: null,
+
+  // Shows the fullscreen approval UI, or if the domain has already been approved
+  // for fullscreen, shows a warning that the site has entered fullscreen for a short
+  // duration.
+  showWarning: function(targetDoc) {
+    if (!document.mozFullScreen ||
+        !gPrefService.getBoolPref("full-screen-api.approval-required"))
+      return;
+
+    // Set the strings on the fullscreen approval UI.
+    this.fullscreenDoc = targetDoc;
+    let uri = this.fullscreenDoc.nodePrincipal.URI;
+    let host = null;
+    try {
+      host = uri.host;
+    } catch (e) { }
+    let hostLabel = document.getElementById("full-screen-domain-text");
+    let rememberCheckbox = document.getElementById("full-screen-remember-decision");
+    let isApproved = false;
+    if (host) {
+      // Document's principal's URI has a host. Display a warning including the hostname and
+      // show UI to enable the user to permanently grant this host permission to enter fullscreen.
+      let utils = {};
+      Cu.import("resource://gre/modules/DownloadUtils.jsm", utils);
+      let displayHost = utils.DownloadUtils.getURIHost(uri.spec)[0];
+      let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
+
+      hostLabel.textContent = bundle.formatStringFromName("fullscreen.entered", [displayHost], 1);
+      hostLabel.removeAttribute("hidden");
+
+      rememberCheckbox.label = bundle.formatStringFromName("fullscreen.rememberDecision", [displayHost], 1);
+      rememberCheckbox.checked = false;
+      rememberCheckbox.removeAttribute("hidden");
+
+      // Note we only allow documents whose principal's URI has a host to
+      // store permission grants.
+      isApproved = Services.perms.testPermission(uri, "fullscreen") == Services.perms.ALLOW_ACTION;
+    } else {
+      hostLabel.setAttribute("hidden", "true");
+      rememberCheckbox.setAttribute("hidden", "true");
+    }
+
+    // Note: the warning box can be non-null if the warning box from the previous request
+    // wasn't hidden before another request was made.
+    if (!this.warningBox) {
+      this.warningBox = document.getElementById("full-screen-warning-container");
+      // Add a listener to clean up state after the warning is hidden.
+      this.warningBox.addEventListener("transitionend", this);
+      this.warningBox.removeAttribute("hidden");
+    } else {
+      if (this.warningFadeOutTimeout) {
+        clearTimeout(this.warningFadeOutTimeout);
+        this.warningFadeOutTimeout = null;
+      }
+      this.warningBox.removeAttribute("fade-warning-out");
+    }
+
+    // If fullscreen mode has not yet been approved for the fullscreen
+    // document's domain, show the approval UI and don't auto fade out the
+    // fullscreen warning box. Otherwise, we're just notifying of entry into
+    // fullscreen mode. Note if the resource's host is null, we must be
+    // showing a local file or a local data URI, and we require explicit
+    // approval every time.
+    let authUI = document.getElementById("full-screen-approval-pane");
+    if (isApproved) {
+      authUI.setAttribute("hidden", "true");
+      this.warningBox.removeAttribute("obscure-browser");
+    } else {
+      // Partially obscure the <browser> element underneath the approval UI.
+      this.warningBox.setAttribute("obscure-browser", "true");
+      authUI.removeAttribute("hidden");
+    }
+
+    // If we're not showing the fullscreen approval UI, we're just notifying the user
+    // of the transition, so set a timeout to fade the warning out after a few moments.
+    if (isApproved)
+      this.warningFadeOutTimeout =
+        setTimeout(
+          function() {
+            if (this.warningBox)
+              this.warningBox.setAttribute("fade-warning-out", "true");
+          }.bind(this),
+          3000);
+  },
+
+  mouseoverToggle: function(aShow, forceHide)
+  {
+    // Don't do anything if:
+    // a) we're already in the state we want,
+    // b) we're animating and will become collapsed soon, or
+    // c) we can't collapse because it would be undesirable right now
+    if (aShow != this._isChromeCollapsed || (!aShow && this._isAnimating) ||
+        (!aShow && !this._safeToCollapse(forceHide)))
+      return;
+
+    // browser.fullscreen.animateUp
+    // 0 - never animate up
+    // 1 - animate only for first collapse after entering fullscreen (default for perf's sake)
+    // 2 - animate every time it collapses
+    if (gPrefService.getIntPref("browser.fullscreen.animateUp") == 0)
+      this._shouldAnimate = false;
+
+    if (!aShow && this._shouldAnimate) {
+      this._isAnimating = true;
+      this._shouldAnimate = false;
+      this._animationTimeout = setTimeout(this._animateUp.bind(this), 800);
+      return;
+    }
+
+    // The chrome is collapsed so don't spam needless mousemove events
+    if (aShow) {
+      gBrowser.mPanelContainer.addEventListener("mousemove",
+                                                this._collapseCallback, false);
+    }
+    else {
+      gBrowser.mPanelContainer.removeEventListener("mousemove",
+                                                   this._collapseCallback, false);
+    }
+
+    // Hiding/collapsing the toolbox interferes with the tab bar's scrollbox,
+    // so we just move it off-screen instead. See bug 430687.
+    gNavToolbox.style.marginTop =
+      aShow ? "" : -gNavToolbox.getBoundingClientRect().height + "px";
+
+    let toggler = document.getElementById("fullscr-toggler");
+    if (toggler) {
+      toggler.collapsed = aShow;
+    }
+    this._isChromeCollapsed = !aShow;
+    if (gPrefService.getIntPref("browser.fullscreen.animateUp") == 2)
+      this._shouldAnimate = true;
+  },
+
+  showXULChrome: function(aTag, aShow)
+  {
+    var els = document.getElementsByTagNameNS(this._XULNS, aTag);
+
+    for (var i = 0; i < els.length; ++i) {
+      // XXX don't interfere with previously collapsed toolbars
+      if (els[i].getAttribute("fullscreentoolbar") == "true") {
+        if (!aShow) {
+
+          var toolbarMode = els[i].getAttribute("mode");
+          if (toolbarMode != "text") {
+            els[i].setAttribute("saved-mode", toolbarMode);
+            els[i].setAttribute("saved-iconsize",
+                                els[i].getAttribute("iconsize"));
+            els[i].setAttribute("mode", "icons");
+            els[i].setAttribute("iconsize", "small");
+          }
+
+          // Give the main nav bar and the tab bar the fullscreen context menu,
+          // otherwise remove context menu to prevent breakage
+          els[i].setAttribute("saved-context",
+                              els[i].getAttribute("context"));
+          if (els[i].id == "nav-bar" || els[i].id == "TabsToolbar")
+            els[i].setAttribute("context", "autohide-context");
+          else
+            els[i].removeAttribute("context");
+
+          // Set the inFullscreen attribute to allow specific styling
+          // in fullscreen mode
+          els[i].setAttribute("inFullscreen", true);
+        }
+        else {
+          function restoreAttr(attrName) {
+            var savedAttr = "saved-" + attrName;
+            if (els[i].hasAttribute(savedAttr)) {
+              els[i].setAttribute(attrName, els[i].getAttribute(savedAttr));
+              els[i].removeAttribute(savedAttr);
+            }
+          }
+
+          restoreAttr("mode");
+          restoreAttr("iconsize");
+          restoreAttr("context");
+
+          els[i].removeAttribute("inFullscreen");
+        }
+      } else {
+        // use moz-collapsed so it doesn't persist hidden/collapsed,
+        // so that new windows don't have missing toolbars
+        if (aShow)
+          els[i].removeAttribute("moz-collapsed");
+        else
+          els[i].setAttribute("moz-collapsed", "true");
+      }
+    }
+
+    if (aShow) {
+      gNavToolbox.removeAttribute("inFullscreen");
+      document.documentElement.removeAttribute("inFullscreen");
+    } else {
+      gNavToolbox.setAttribute("inFullscreen", true);
+      document.documentElement.setAttribute("inFullscreen", true);
+    }
+
+    // In tabs-on-top mode, move window controls to the tab bar,
+    // and in tabs-on-bottom mode, move them back to the navigation toolbar.
+    // When there is a chance the tab bar may be collapsed, put window
+    // controls on nav bar.
+    var fullscreenctls = document.getElementById("window-controls");
+    var navbar = document.getElementById("nav-bar");
+    var ctlsOnTabbar = window.toolbar.visible &&
+                       (navbar.collapsed ||
+                          (TabsOnTop.enabled &&
+                           !gPrefService.getBoolPref("browser.tabs.autoHide")));
+    if (fullscreenctls.parentNode == navbar && ctlsOnTabbar) {
+      fullscreenctls.removeAttribute("flex");
+      document.getElementById("TabsToolbar").appendChild(fullscreenctls);
+    }
+    else if (fullscreenctls.parentNode.id == "TabsToolbar" && !ctlsOnTabbar) {
+      fullscreenctls.setAttribute("flex", "1");
+      navbar.appendChild(fullscreenctls);
+    }
+
+    var controls = document.getElementsByAttribute("fullscreencontrol", "true");
+    for (var i = 0; i < controls.length; ++i)
+      controls[i].hidden = aShow;
+  }
+};
+XPCOMUtils.defineLazyGetter(FullScreen, "useLionFullScreen", function() {
+  // We'll only use OS X Lion full screen if we're
+  // * on OS X
+  // * on Lion or higher (Darwin 11+)
+  // * have fullscreenbutton="true"
+//@line 610 "/build/src/mozilla-release/browser/base/content/browser-fullScreen.js"
+  return false;
+//@line 612 "/build/src/mozilla-release/browser/base/content/browser-fullScreen.js"
+});
+/*
+//@line 7 "/build/src/mozilla-release/browser/base/content/browser-fullZoom.js"
+ */
+
+// One of the possible values for the mousewheel.* preferences.
+// From nsEventStateManager.cpp.
+const MOUSE_SCROLL_ZOOM = 3;
+
+/**
+ * Controls the "full zoom" setting and its site-specific preferences.
+ */
+var FullZoom = {
+  // Identifies the setting in the content prefs database.
+  name: "browser.content.full-zoom",
+
+  // The global value (if any) for the setting.  Lazily loaded from the service
+  // when first requested, then updated by the pref change listener as it changes.
+  // If there is no global value, then this should be undefined.
+  get globalValue() {
+    var globalValue = Services.contentPrefs.getPref(null, this.name);
+    if (typeof globalValue != "undefined")
+      globalValue = this._ensureValid(globalValue);
+    delete this.globalValue;
+    return this.globalValue = globalValue;
+  },
+
+  // browser.zoom.siteSpecific preference cache
+  _siteSpecificPref: undefined,
+
+  // browser.zoom.updateBackgroundTabs preference cache
+  updateBackgroundTabs: undefined,
+
+  get siteSpecific() {
+    return this._siteSpecificPref;
+  },
+
+  //**************************************************************************//
+  // nsISupports
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMEventListener,
+                                         Ci.nsIObserver,
+                                         Ci.nsIContentPrefObserver,
+                                         Ci.nsISupportsWeakReference,
+                                         Ci.nsISupports]),
+
+  //**************************************************************************//
+  // Initialization & Destruction
+
+  init: function FullZoom_init() {
+    // Listen for scrollwheel events so we can save scrollwheel-based changes.
+    window.addEventListener("DOMMouseScroll", this, false);
+
+    // Register ourselves with the service so we know when our pref changes.
+    Services.contentPrefs.addObserver(this.name, this);
+
+    this._siteSpecificPref =
+      gPrefService.getBoolPref("browser.zoom.siteSpecific");
+    this.updateBackgroundTabs =
+      gPrefService.getBoolPref("browser.zoom.updateBackgroundTabs");
+    // Listen for changes to the browser.zoom branch so we can enable/disable
+    // updating background tabs and per-site saving and restoring of zoom levels.
+    gPrefService.addObserver("browser.zoom.", this, true);
+  },
+
+  destroy: function FullZoom_destroy() {
+    gPrefService.removeObserver("browser.zoom.", this);
+    Services.contentPrefs.removeObserver(this.name, this);
+    window.removeEventListener("DOMMouseScroll", this, false);
+  },
+
+
+  //**************************************************************************//
+  // Event Handlers
+
+  // nsIDOMEventListener
+
+  handleEvent: function FullZoom_handleEvent(event) {
+    switch (event.type) {
+      case "DOMMouseScroll":
+        this._handleMouseScrolled(event);
+        break;
+    }
+  },
+
+  _handleMouseScrolled: function FullZoom__handleMouseScrolled(event) {
+    // Construct the "mousewheel action" pref key corresponding to this event.
+    // Based on nsEventStateManager::GetBasePrefKeyForMouseWheel.
+    var pref = "mousewheel";
+    if (event.axis == event.HORIZONTAL_AXIS)
+      pref += ".horizscroll";
+
+    if (event.shiftKey)
+      pref += ".withshiftkey";
+    else if (event.ctrlKey)
+      pref += ".withcontrolkey";
+    else if (event.altKey)
+      pref += ".withaltkey";
+    else if (event.metaKey)
+      pref += ".withmetakey";
+    else
+      pref += ".withnokey";
+
+    pref += ".action";
+
+    // Don't do anything if this isn't a "zoom" scroll event.
+    var isZoomEvent = false;
+    try {
+      isZoomEvent = (gPrefService.getIntPref(pref) == MOUSE_SCROLL_ZOOM);
+    } catch (e) {}
+    if (!isZoomEvent)
+      return;
+
+    // XXX Lazily cache all the possible action prefs so we don't have to get
+    // them anew from the pref service for every scroll event?  We'd have to
+    // make sure to observe them so we can update the cache when they change.
+
+    // We have to call _applySettingToPref in a timeout because we handle
+    // the event before the event state manager has a chance to apply the zoom
+    // during nsEventStateManager::PostHandleEvent.
+    window.setTimeout(function (self) { self._applySettingToPref() }, 0, this);
+  },
+
+  // nsIObserver
+
+  observe: function (aSubject, aTopic, aData) {
+    switch (aTopic) {
+      case "nsPref:changed":
+        switch (aData) {
+          case "browser.zoom.siteSpecific":
+            this._siteSpecificPref =
+              gPrefService.getBoolPref("browser.zoom.siteSpecific");
+            break;
+          case "browser.zoom.updateBackgroundTabs":
+            this.updateBackgroundTabs =
+              gPrefService.getBoolPref("browser.zoom.updateBackgroundTabs");
+            break;
+        }
+        break;
+    }
+  },
+
+  // nsIContentPrefObserver
+
+  onContentPrefSet: function FullZoom_onContentPrefSet(aGroup, aName, aValue) {
+    if (aGroup == Services.contentPrefs.grouper.group(gBrowser.currentURI))
+      this._applyPrefToSetting(aValue);
+    else if (aGroup == null) {
+      this.globalValue = this._ensureValid(aValue);
+
+      // If the current page doesn't have a site-specific preference,
+      // then its zoom should be set to the new global preference now that
+      // the global preference has changed.
+      if (!Services.contentPrefs.hasPref(gBrowser.currentURI, this.name))
+        this._applyPrefToSetting();
+    }
+  },
+
+  onContentPrefRemoved: function FullZoom_onContentPrefRemoved(aGroup, aName) {
+    if (aGroup == Services.contentPrefs.grouper.group(gBrowser.currentURI))
+      this._applyPrefToSetting();
+    else if (aGroup == null) {
+      this.globalValue = undefined;
+
+      // If the current page doesn't have a site-specific preference,
+      // then its zoom should be set to the default preference now that
+      // the global preference has changed.
+      if (!Services.contentPrefs.hasPref(gBrowser.currentURI, this.name))
+        this._applyPrefToSetting();
+    }
+  },
+
+  // location change observer
+
+  /**
+   * Called when the location of a tab changes.
+   * When that happens, we need to update the current zoom level if appropriate.
+   *
+   * @param aURI
+   *        A URI object representing the new location.
+   * @param aIsTabSwitch
+   *        Whether this location change has happened because of a tab switch.
+   * @param aBrowser
+   *        (optional) browser object displaying the document
+   */
+  onLocationChange: function FullZoom_onLocationChange(aURI, aIsTabSwitch, aBrowser) {
+    if (!aURI || (aIsTabSwitch && !this.siteSpecific))
+      return;
+
+    // Avoid the cps roundtrip and apply the default/global pref.
+    if (aURI.spec == "about:blank") {
+      this._applyPrefToSetting(undefined, aBrowser);
+      return;
+    }
+
+    let browser = aBrowser || gBrowser.selectedBrowser;
+
+    // Media documents should always start at 1, and are not affected by prefs.
+    if (!aIsTabSwitch && browser.contentDocument.mozSyntheticDocument) {
+      ZoomManager.setZoomForBrowser(browser, 1);
+      return;
+    }
+
+    if (Services.contentPrefs.hasCachedPref(aURI, this.name)) {
+      let zoomValue = Services.contentPrefs.getPref(aURI, this.name);
+      this._applyPrefToSetting(zoomValue, browser);
+    } else {
+      var self = this;
+      Services.contentPrefs.getPref(aURI, this.name, function (aResult) {
+        // Check that we're still where we expect to be in case this took a while.
+        // Null check currentURI, since the window may have been destroyed before
+        // we were called.
+        if (browser.currentURI && aURI.equals(browser.currentURI)) {
+          self._applyPrefToSetting(aResult, browser);
+        }
+      });
+    }
+  },
+
+  // update state of zoom type menu item
+
+  updateMenu: function FullZoom_updateMenu() {
+    var menuItem = document.getElementById("toggle_zoom");
+
+    menuItem.setAttribute("checked", !ZoomManager.useFullZoom);
+  },
+
+  //**************************************************************************//
+  // Setting & Pref Manipulation
+
+  reduce: function FullZoom_reduce() {
+    ZoomManager.reduce();
+    this._applySettingToPref();
+  },
+
+  enlarge: function FullZoom_enlarge() {
+    ZoomManager.enlarge();
+    this._applySettingToPref();
+  },
+
+  reset: function FullZoom_reset() {
+    if (typeof this.globalValue != "undefined")
+      ZoomManager.zoom = this.globalValue;
+    else
+      ZoomManager.reset();
+
+    this._removePref();
+  },
+
+  /**
+   * Set the zoom level for the current tab.
+   *
+   * Per nsPresContext::setFullZoom, we can set the zoom to its current value
+   * without significant impact on performance, as the setting is only applied
+   * if it differs from the current setting.  In fact getting the zoom and then
+   * checking ourselves if it differs costs more.
+   * 
+   * And perhaps we should always set the zoom even if it was more expensive,
+   * since DocumentViewerImpl::SetTextZoom claims that child documents can have
+   * a different text zoom (although it would be unusual), and it implies that
+   * those child text zooms should get updated when the parent zoom gets set,
+   * and perhaps the same is true for full zoom
+   * (although DocumentViewerImpl::SetFullZoom doesn't mention it).
+   *
+   * So when we apply new zoom values to the browser, we simply set the zoom.
+   * We don't check first to see if the new value is the same as the current
+   * one.
+   **/
+  _applyPrefToSetting: function FullZoom__applyPrefToSetting(aValue, aBrowser) {
+    if ((!this.siteSpecific) || gInPrintPreviewMode)
+      return;
+
+    var browser = aBrowser || (gBrowser && gBrowser.selectedBrowser);
+    try {
+      if (browser.contentDocument.mozSyntheticDocument)
+        return;
+
+      if (typeof aValue != "undefined")
+        ZoomManager.setZoomForBrowser(browser, this._ensureValid(aValue));
+      else if (typeof this.globalValue != "undefined")
+        ZoomManager.setZoomForBrowser(browser, this.globalValue);
+      else
+        ZoomManager.setZoomForBrowser(browser, 1);
+    }
+    catch(ex) {}
+  },
+
+  _applySettingToPref: function FullZoom__applySettingToPref() {
+    if (!this.siteSpecific || gInPrintPreviewMode ||
+        content.document.mozSyntheticDocument)
+      return;
+
+    var zoomLevel = ZoomManager.zoom;
+    Services.contentPrefs.setPref(gBrowser.currentURI, this.name, zoomLevel);
+  },
+
+  _removePref: function FullZoom__removePref() {
+    if (!(content.document.mozSyntheticDocument))
+      Services.contentPrefs.removePref(gBrowser.currentURI, this.name);
+  },
+
+
+  //**************************************************************************//
+  // Utilities
+
+  _ensureValid: function FullZoom__ensureValid(aValue) {
+    if (isNaN(aValue))
+      return 1;
+
+    if (aValue < ZoomManager.MIN)
+      return ZoomManager.MIN;
+
+    if (aValue > ZoomManager.MAX)
+      return ZoomManager.MAX;
+
+    return aValue;
+  }
+};
+//@line 4 "/build/src/mozilla-release/browser/base/content/browser-places.js"
+
+
+var StarUI = {
+  _itemId: -1,
+  uri: null,
+  _batching: false,
+
+  _element: function(aID) {
+    return document.getElementById(aID);
+  },
+
+  // Edit-bookmark panel
+  get panel() {
+    delete this.panel;
+    var element = this._element("editBookmarkPanel");
+    // initially the panel is hidden
+    // to avoid impacting startup / new window performance
+    element.hidden = false;
+    element.addEventListener("popuphidden", this, false);
+    element.addEventListener("keypress", this, false);
+    return this.panel = element;
+  },
+
+  // Array of command elements to disable when the panel is opened.
+  get _blockedCommands() {
+    delete this._blockedCommands;
+    return this._blockedCommands =
+      ["cmd_close", "cmd_closeWindow"].map(function (id) this._element(id), this);
+  },
+
+  _blockCommands: function SU__blockCommands() {
+    this._blockedCommands.forEach(function (elt) {
+      // make sure not to permanently disable this item (see bug 409155)
+      if (elt.hasAttribute("wasDisabled"))
+        return;
+      if (elt.getAttribute("disabled") == "true") {
+        elt.setAttribute("wasDisabled", "true");
+      } else {
+        elt.setAttribute("wasDisabled", "false");
+        elt.setAttribute("disabled", "true");
+      }
+    });
+  },
+
+  _restoreCommandsState: function SU__restoreCommandsState() {
+    this._blockedCommands.forEach(function (elt) {
+      if (elt.getAttribute("wasDisabled") != "true")
+        elt.removeAttribute("disabled");
+      elt.removeAttribute("wasDisabled");
+    });
+  },
+
+  // nsIDOMEventListener
+  handleEvent: function SU_handleEvent(aEvent) {
+    switch (aEvent.type) {
+      case "popuphidden":
+        if (aEvent.originalTarget == this.panel) {
+          if (!this._element("editBookmarkPanelContent").hidden)
+            this.quitEditMode();
+
+          this._restoreCommandsState();
+          this._itemId = -1;
+          if (this._batching) {
+            PlacesUtils.transactionManager.endBatch();
+            this._batching = false;
+          }
+
+          switch (this._actionOnHide) {
+            case "cancel": {
+              PlacesUtils.transactionManager.undoTransaction();
+              break;
+            }
+            case "remove": {
+              // Remove all bookmarks for the bookmark's url, this also removes
+              // the tags for the url.
+              PlacesUtils.transactionManager.beginBatch();
+              let itemIds = PlacesUtils.getBookmarksForURI(this._uriForRemoval);
+              for (let i = 0; i < itemIds.length; i++) {
+                let txn = new PlacesRemoveItemTransaction(itemIds[i]);
+                PlacesUtils.transactionManager.doTransaction(txn);
+              }
+              PlacesUtils.transactionManager.endBatch();
+              break;
+            }
+          }
+          this._actionOnHide = "";
+        }
+        break;
+      case "keypress":
+        if (aEvent.defaultPrevented) {
+          // The event has already been consumed inside of the panel.
+          break;
+        }
+        switch (aEvent.keyCode) {
+          case KeyEvent.DOM_VK_ESCAPE:
+            if (!this._element("editBookmarkPanelContent").hidden)
+              this.cancelButtonOnCommand();
+            break;
+          case KeyEvent.DOM_VK_RETURN:
+            if (aEvent.target.className == "expander-up" ||
+                aEvent.target.className == "expander-down" ||
+                aEvent.target.id == "editBMPanel_newFolderButton") {
+              //XXX Why is this necessary? The defaultPrevented check should
+              //    be enough.
+              break;
+            }
+            this.panel.hidePopup();
+            break;
+        }
+        break;
+    }
+  },
+
+  _overlayLoaded: false,
+  _overlayLoading: false,
+  showEditBookmarkPopup:
+  function SU_showEditBookmarkPopup(aItemId, aAnchorElement, aPosition) {
+    // Performance: load the overlay the first time the panel is opened
+    // (see bug 392443).
+    if (this._overlayLoading)
+      return;
+
+    if (this._overlayLoaded) {
+      this._doShowEditBookmarkPanel(aItemId, aAnchorElement, aPosition);
+      return;
+    }
+
+    this._overlayLoading = true;
+    document.loadOverlay(
+      "chrome://browser/content/places/editBookmarkOverlay.xul",
+      (function (aSubject, aTopic, aData) {
+        //XXX We just caused localstore.rdf to be re-applied (bug 640158)
+        retrieveToolbarIconsizesFromTheme();
+
+        // Move the header (star, title, button) into the grid,
+        // so that it aligns nicely with the other items (bug 484022).
+        let header = this._element("editBookmarkPanelHeader");
+        let rows = this._element("editBookmarkPanelGrid").lastChild;
+        rows.insertBefore(header, rows.firstChild);
+        header.hidden = false;
+
+        this._overlayLoading = false;
+        this._overlayLoaded = true;
+        this._doShowEditBookmarkPanel(aItemId, aAnchorElement, aPosition);
+      }).bind(this)
+    );
+  },
+
+  _doShowEditBookmarkPanel:
+  function SU__doShowEditBookmarkPanel(aItemId, aAnchorElement, aPosition) {
+    if (this.panel.state != "closed")
+      return;
+
+    this._blockCommands(); // un-done in the popuphiding handler
+
+    // Set panel title:
+    // if we are batching, i.e. the bookmark has been added now,
+    // then show Page Bookmarked, else if the bookmark did already exist,
+    // we are about editing it, then use Edit This Bookmark.
+    this._element("editBookmarkPanelTitle").value =
+      this._batching ?
+        gNavigatorBundle.getString("editBookmarkPanel.pageBookmarkedTitle") :
+        gNavigatorBundle.getString("editBookmarkPanel.editBookmarkTitle");
+
+    // No description; show the Done, Cancel;
+    this._element("editBookmarkPanelDescription").textContent = "";
+    this._element("editBookmarkPanelBottomButtons").hidden = false;
+    this._element("editBookmarkPanelContent").hidden = false;
+
+    // The remove button is shown only if we're not already batching, i.e.
+    // if the cancel button/ESC does not remove the bookmark.
+    this._element("editBookmarkPanelRemoveButton").hidden = this._batching;
+
+    // The label of the remove button differs if the URI is bookmarked
+    // multiple times.
+    var bookmarks = PlacesUtils.getBookmarksForURI(gBrowser.currentURI);
+    var forms = gNavigatorBundle.getString("editBookmark.removeBookmarks.label");
+    var label = PluralForm.get(bookmarks.length, forms).replace("#1", bookmarks.length);
+    this._element("editBookmarkPanelRemoveButton").label = label;
+
+    // unset the unstarred state, if set
+    this._element("editBookmarkPanelStarIcon").removeAttribute("unstarred");
+
+    this._itemId = aItemId !== undefined ? aItemId : this._itemId;
+    this.beginBatch();
+
+    // Consume dismiss clicks, see bug 400924
+    this.panel.popupBoxObject
+        .setConsumeRollupEvent(Ci.nsIPopupBoxObject.ROLLUP_CONSUME);
+    this.panel.openPopup(aAnchorElement, aPosition);
+
+    gEditItemOverlay.initPanel(this._itemId,
+                               { hiddenRows: ["description", "location",
+                                              "loadInSidebar", "keyword"] });
+  },
+
+  panelShown:
+  function SU_panelShown(aEvent) {
+    if (aEvent.target == this.panel) {
+      if (!this._element("editBookmarkPanelContent").hidden) {
+        let fieldToFocus = "editBMPanel_" +
+          gPrefService.getCharPref("browser.bookmarks.editDialog.firstEditField");
+        var elt = this._element(fieldToFocus);
+        elt.focus();
+        elt.select();
+      }
+      else {
+        // Note this isn't actually used anymore, we should remove this
+        // once we decide not to bring back the page bookmarked notification
+        this.panel.focus();
+      }
+    }
+  },
+
+  quitEditMode: function SU_quitEditMode() {
+    this._element("editBookmarkPanelContent").hidden = true;
+    this._element("editBookmarkPanelBottomButtons").hidden = true;
+    gEditItemOverlay.uninitPanel(true);
+  },
+
+  editButtonCommand: function SU_editButtonCommand() {
+    this.showEditBookmarkPopup();
+  },
+
+  cancelButtonOnCommand: function SU_cancelButtonOnCommand() {
+    this._actionOnHide = "cancel";
+    this.panel.hidePopup();
+  },
+
+  removeBookmarkButtonCommand: function SU_removeBookmarkButtonCommand() {
+    this._uriForRemoval = PlacesUtils.bookmarks.getBookmarkURI(this._itemId);
+    this._actionOnHide = "remove";
+    this.panel.hidePopup();
+  },
+
+  beginBatch: function SU_beginBatch() {
+    if (!this._batching) {
+      PlacesUtils.transactionManager.beginBatch();
+      this._batching = true;
+    }
+  }
+}
+
+var PlacesCommandHook = {
+  /**
+   * Adds a bookmark to the page loaded in the given browser.
+   *
+   * @param aBrowser
+   *        a <browser> element.
+   * @param [optional] aParent
+   *        The folder in which to create a new bookmark if the page loaded in
+   *        aBrowser isn't bookmarked yet, defaults to the unfiled root.
+   * @param [optional] aShowEditUI
+   *        whether or not to show the edit-bookmark UI for the bookmark item
+   */  
+  bookmarkPage: function PCH_bookmarkPage(aBrowser, aParent, aShowEditUI) {
+    var uri = aBrowser.currentURI;
+    var itemId = PlacesUtils.getMostRecentBookmarkForURI(uri);
+    if (itemId == -1) {
+      // Copied over from addBookmarkForBrowser:
+      // Bug 52536: We obtain the URL and title from the nsIWebNavigation
+      // associated with a <browser/> rather than from a DOMWindow.
+      // This is because when a full page plugin is loaded, there is
+      // no DOMWindow (?) but information about the loaded document
+      // may still be obtained from the webNavigation.
+      var webNav = aBrowser.webNavigation;
+      var url = webNav.currentURI;
+      var title;
+      var description;
+      var charset;
+      try {
+        title = webNav.document.title || url.spec;
+        description = PlacesUIUtils.getDescriptionFromDocument(webNav.document);
+        charset = webNav.document.characterSet;
+      }
+      catch (e) { }
+
+      if (aShowEditUI) {
+        // If we bookmark the page here (i.e. page was not "starred" already)
+        // but open right into the "edit" state, start batching here, so
+        // "Cancel" in that state removes the bookmark.
+        StarUI.beginBatch();
+      }
+
+      var parent = aParent != undefined ?
+                   aParent : PlacesUtils.unfiledBookmarksFolderId;
+      var descAnno = { name: PlacesUIUtils.DESCRIPTION_ANNO, value: description };
+      var txn = new PlacesCreateBookmarkTransaction(uri, parent, 
+                                                    PlacesUtils.bookmarks.DEFAULT_INDEX,
+                                                    title, null, [descAnno]);
+      PlacesUtils.transactionManager.doTransaction(txn);
+      // Set the character-set
+      if (charset)
+        PlacesUtils.history.setCharsetForURI(uri, charset);
+      itemId = PlacesUtils.getMostRecentBookmarkForURI(uri);
+    }
+
+    // Revert the contents of the location bar
+    if (gURLBar)
+      gURLBar.handleRevert();
+
+    // dock the panel to the star icon when possible, otherwise dock
+    // it to the content area
+    if (aBrowser.contentWindow == window.content) {
+      var starIcon = aBrowser.ownerDocument.getElementById("star-button");
+      if (starIcon && isElementVisible(starIcon)) {
+        if (aShowEditUI)
+          StarUI.showEditBookmarkPopup(itemId, starIcon, "bottomcenter topright");
+        return;
+      }
+    }
+
+    StarUI.showEditBookmarkPopup(itemId, aBrowser, "overlap");
+  },
+
+  /**
+   * Adds a bookmark to the page loaded in the current tab. 
+   */
+  bookmarkCurrentPage: function PCH_bookmarkCurrentPage(aShowEditUI, aParent) {
+    this.bookmarkPage(gBrowser.selectedBrowser, aParent, aShowEditUI);
+  },
+
+  /**
+   * Adds a bookmark to the page targeted by a link.
+   * @param aParent
+   *        The folder in which to create a new bookmark if aURL isn't
+   *        bookmarked.
+   * @param aURL (string)
+   *        the address of the link target
+   * @param aTitle
+   *        The link text
+   */
+  bookmarkLink: function PCH_bookmarkLink(aParent, aURL, aTitle) {
+    var linkURI = makeURI(aURL);
+    var itemId = PlacesUtils.getMostRecentBookmarkForURI(linkURI);
+    if (itemId == -1) {
+      PlacesUIUtils.showBookmarkDialog({ action: "add"
+                                       , type: "bookmark"
+                                       , uri: linkURI
+                                       , title: aTitle
+                                       , hiddenRows: [ "description"
+                                                     , "location"
+                                                     , "loadInSidebar"
+                                                     , "keyword" ]
+                                       }, window);
+    }
+    else {
+      PlacesUIUtils.showBookmarkDialog({ action: "edit"
+                                       , type: "bookmark"
+                                       , itemId: itemId
+                                       }, window);
+    }
+  },
+
+  /**
+   * List of nsIURI objects characterizing the tabs currently open in the
+   * browser, modulo pinned tabs.  The URIs will be in the order in which their
+   * corresponding tabs appeared and duplicates are discarded.
+   */
+  get uniqueCurrentPages() {
+    let uniquePages = {};
+    let URIs = [];
+    gBrowser.visibleTabs.forEach(function (tab) {
+      let spec = tab.linkedBrowser.currentURI.spec;
+      if (!tab.pinned && !(spec in uniquePages)) {
+        uniquePages[spec] = null;
+        URIs.push(tab.linkedBrowser.currentURI);
+      }
+    });
+    return URIs;
+  },
+
+  /**
+   * Adds a folder with bookmarks to all of the currently open tabs in this 
+   * window.
+   */
+  bookmarkCurrentPages: function PCH_bookmarkCurrentPages() {
+    let pages = this.uniqueCurrentPages;
+    if (pages.length > 1) {
+    PlacesUIUtils.showBookmarkDialog({ action: "add"
+                                     , type: "folder"
+                                     , URIList: pages
+                                     , hiddenRows: [ "description" ]
+                                     }, window);
+    }
+  },
+
+  /**
+   * Updates disabled state for the "Bookmark All Tabs" command.
+   */
+  updateBookmarkAllTabsCommand:
+  function PCH_updateBookmarkAllTabsCommand() {
+    // There's nothing to do in non-browser windows.
+    if (window.location.href != getBrowserURL())
+      return;
+
+    // Disable "Bookmark All Tabs" if there are less than two
+    // "unique current pages".
+    goSetCommandEnabled("Browser:BookmarkAllTabs",
+                        this.uniqueCurrentPages.length >= 2);
+  },
+
+  /**
+   * Adds a Live Bookmark to a feed associated with the current page. 
+   * @param     url
+   *            The nsIURI of the page the feed was attached to
+   * @title     title
+   *            The title of the feed. Optional.
+   * @subtitle  subtitle
+   *            A short description of the feed. Optional.
+   */
+  addLiveBookmark: function PCH_addLiveBookmark(url, feedTitle, feedSubtitle) {
+    var feedURI = makeURI(url);
+    
+    var doc = gBrowser.contentDocument;
+    var title = (arguments.length > 1) ? feedTitle : doc.title;
+ 
+    var description;
+    if (arguments.length > 2)
+      description = feedSubtitle;
+    else
+      description = PlacesUIUtils.getDescriptionFromDocument(doc);
+
+    var toolbarIP = new InsertionPoint(PlacesUtils.toolbarFolderId, -1);
+    PlacesUIUtils.showBookmarkDialog({ action: "add"
+                                     , type: "livemark"
+                                     , feedURI: feedURI
+                                     , siteURI: gBrowser.currentURI
+                                     , title: title
+                                     , description: description
+                                     , defaultInsertionPoint: toolbarIP
+                                     , hiddenRows: [ "feedLocation"
+                                                   , "siteLocation"
+                                                   , "description" ]
+                                     }, window);
+  },
+
+  /**
+   * Opens the Places Organizer. 
+   * @param   aLeftPaneRoot
+   *          The query to select in the organizer window - options
+   *          are: History, AllBookmarks, BookmarksMenu, BookmarksToolbar,
+   *          UnfiledBookmarks and Tags.
+   */
+  showPlacesOrganizer: function PCH_showPlacesOrganizer(aLeftPaneRoot) {
+    var organizer = Services.wm.getMostRecentWindow("Places:Organizer");
+    if (!organizer) {
+      // No currently open places window, so open one with the specified mode.
+      openDialog("chrome://browser/content/places/places.xul", 
+                 "", "chrome,toolbar=yes,dialog=no,resizable", aLeftPaneRoot);
+    }
+    else {
+      organizer.PlacesOrganizer.selectLeftPaneQuery(aLeftPaneRoot);
+      organizer.focus();
+    }
+  }
+};
+
+// View for the history menu.
+function HistoryMenu(aPopupShowingEvent) {
+  // Workaround for Bug 610187.  The sidebar does not include all the Places
+  // views definitions, and we don't need them there.
+  // Defining the prototype inheritance in the prototype itself would cause
+  // browser.js to halt on "PlacesMenu is not defined" error.
+  this.__proto__.__proto__ = PlacesMenu.prototype;
+  XPCOMUtils.defineLazyServiceGetter(this, "_ss",
+                                     "@mozilla.org/browser/sessionstore;1",
+                                     "nsISessionStore");
+  PlacesMenu.call(this, aPopupShowingEvent,
+                  "place:sort=4&maxResults=15");
+}
+
+HistoryMenu.prototype = {
+  toggleRecentlyClosedTabs: function HM_toggleRecentlyClosedTabs() {
+    // enable/disable the Recently Closed Tabs sub menu
+    var undoMenu = this._rootElt.getElementsByClassName("recentlyClosedTabsMenu")[0];
+
+    // no restorable tabs, so disable menu
+    if (this._ss.getClosedTabCount(window) == 0)
+      undoMenu.setAttribute("disabled", true);
+    else
+      undoMenu.removeAttribute("disabled");
+  },
+
+  /**
+    * Re-open a closed tab and put it to the end of the tab strip.
+    * Used for a middle click.
+    * @param aEvent
+    *        The event when the user clicks the menu item
+    */
+  _undoCloseMiddleClick: function PHM__undoCloseMiddleClick(aEvent) {
+    if (aEvent.button != 1)
+      return;
+
+    undoCloseTab(aEvent.originalTarget.value);
+    gBrowser.moveTabToEnd();
+  },
+
+  /**
+   * Populate when the history menu is opened
+   */
+  populateUndoSubmenu: function PHM_populateUndoSubmenu() {
+    var undoMenu = this._rootElt.getElementsByClassName("recentlyClosedTabsMenu")[0];
+    var undoPopup = undoMenu.firstChild;
+
+    // remove existing menu items
+    while (undoPopup.hasChildNodes())
+      undoPopup.removeChild(undoPopup.firstChild);
+
+    // no restorable tabs, so make sure menu is disabled, and return
+    if (this._ss.getClosedTabCount(window) == 0) {
+      undoMenu.setAttribute("disabled", true);
+      return;
+    }
+
+    // enable menu
+    undoMenu.removeAttribute("disabled");
+
+    // populate menu
+    var undoItems = JSON.parse(this._ss.getClosedTabData(window));
+    for (var i = 0; i < undoItems.length; i++) {
+      var m = document.createElement("menuitem");
+      m.setAttribute("label", undoItems[i].title);
+      if (undoItems[i].image) {
+        let iconURL = undoItems[i].image;
+        // don't initiate a connection just to fetch a favicon (see bug 467828)
+        if (/^https?:/.test(iconURL))
+          iconURL = "moz-anno:favicon:" + iconURL;
+        m.setAttribute("image", iconURL);
+      }
+      m.setAttribute("class", "menuitem-iconic bookmark-item menuitem-with-favicon");
+      m.setAttribute("value", i);
+      m.setAttribute("oncommand", "undoCloseTab(" + i + ");");
+
+      // Set the targetURI attribute so it will be shown in tooltip and trigger
+      // onLinkHovered. SessionStore uses one-based indexes, so we need to
+      // normalize them.
+      let tabData = undoItems[i].state;
+      let activeIndex = (tabData.index || tabData.entries.length) - 1;
+      if (activeIndex >= 0 && tabData.entries[activeIndex])
+        m.setAttribute("targetURI", tabData.entries[activeIndex].url);
+
+      m.addEventListener("click", this._undoCloseMiddleClick, false);
+      if (i == 0)
+        m.setAttribute("key", "key_undoCloseTab");
+      undoPopup.appendChild(m);
+    }
+
+    // "Restore All Tabs"
+    var strings = gNavigatorBundle;
+    undoPopup.appendChild(document.createElement("menuseparator"));
+    m = undoPopup.appendChild(document.createElement("menuitem"));
+    m.id = "menu_restoreAllTabs";
+    m.setAttribute("label", strings.getString("menuRestoreAllTabs.label"));
+    m.addEventListener("command", function() {
+      for (var i = 0; i < undoItems.length; i++)
+        undoCloseTab();
+    }, false);
+  },
+
+  toggleRecentlyClosedWindows: function PHM_toggleRecentlyClosedWindows() {
+    // enable/disable the Recently Closed Windows sub menu
+    var undoMenu = this._rootElt.getElementsByClassName("recentlyClosedWindowsMenu")[0];
+
+    // no restorable windows, so disable menu
+    if (this._ss.getClosedWindowCount() == 0)
+      undoMenu.setAttribute("disabled", true);
+    else
+      undoMenu.removeAttribute("disabled");
+  },
+
+  /**
+   * Populate when the history menu is opened
+   */
+  populateUndoWindowSubmenu: function PHM_populateUndoWindowSubmenu() {
+    let undoMenu = this._rootElt.getElementsByClassName("recentlyClosedWindowsMenu")[0];
+    let undoPopup = undoMenu.firstChild;
+    let menuLabelString = gNavigatorBundle.getString("menuUndoCloseWindowLabel");
+    let menuLabelStringSingleTab =
+      gNavigatorBundle.getString("menuUndoCloseWindowSingleTabLabel");
+
+    // remove existing menu items
+    while (undoPopup.hasChildNodes())
+      undoPopup.removeChild(undoPopup.firstChild);
+
+    // no restorable windows, so make sure menu is disabled, and return
+    if (this._ss.getClosedWindowCount() == 0) {
+      undoMenu.setAttribute("disabled", true);
+      return;
+    }
+
+    // enable menu
+    undoMenu.removeAttribute("disabled");
+
+    // populate menu
+    let undoItems = JSON.parse(this._ss.getClosedWindowData());
+    for (let i = 0; i < undoItems.length; i++) {
+      let undoItem = undoItems[i];
+      let otherTabsCount = undoItem.tabs.length - 1;
+      let label = (otherTabsCount == 0) ? menuLabelStringSingleTab
+                                        : PluralForm.get(otherTabsCount, menuLabelString);
+      let menuLabel = label.replace("#1", undoItem.title)
+                           .replace("#2", otherTabsCount);
+      let m = document.createElement("menuitem");
+      m.setAttribute("label", menuLabel);
+      let selectedTab = undoItem.tabs[undoItem.selected - 1];
+      if (selectedTab.attributes.image) {
+        let iconURL = selectedTab.attributes.image;
+        // don't initiate a connection just to fetch a favicon (see bug 467828)
+        if (/^https?:/.test(iconURL))
+          iconURL = "moz-anno:favicon:" + iconURL;
+        m.setAttribute("image", iconURL);
+      }
+      m.setAttribute("class", "menuitem-iconic bookmark-item menuitem-with-favicon");
+      m.setAttribute("oncommand", "undoCloseWindow(" + i + ");");
+
+      // Set the targetURI attribute so it will be shown in tooltip.
+      // SessionStore uses one-based indexes, so we need to normalize them.
+      let activeIndex = (selectedTab.index || selectedTab.entries.length) - 1;
+      if (activeIndex >= 0 && selectedTab.entries[activeIndex])
+        m.setAttribute("targetURI", selectedTab.entries[activeIndex].url);
+
+      if (i == 0)
+        m.setAttribute("key", "key_undoCloseWindow");
+      undoPopup.appendChild(m);
+    }
+
+    // "Open All in Windows"
+    undoPopup.appendChild(document.createElement("menuseparator"));
+    let m = undoPopup.appendChild(document.createElement("menuitem"));
+    m.id = "menu_restoreAllWindows";
+    m.setAttribute("label", gNavigatorBundle.getString("menuRestoreAllWindows.label"));
+    m.setAttribute("oncommand",
+      "for (var i = 0; i < " + undoItems.length + "; i++) undoCloseWindow();");
+  },
+
+  toggleTabsFromOtherComputers: function PHM_toggleTabsFromOtherComputers() {
+    // This is a no-op if MOZ_SERVICES_SYNC isn't defined
+//@line 643 "/build/src/mozilla-release/browser/base/content/browser-places.js"
+    // Enable/disable the Tabs From Other Computers menu. Some of the menus handled
+    // by HistoryMenu do not have this menuitem.
+    let menuitem = this._rootElt.getElementsByClassName("syncTabsMenuItem")[0];
+    if (!menuitem)
+      return;
+
+    // If Sync isn't configured yet, then don't show the menuitem.
+    if (Weave.Status.checkSetup() == Weave.CLIENT_NOT_CONFIGURED ||
+        Weave.Svc.Prefs.get("firstSync", "") == "notReady") {
+      menuitem.setAttribute("hidden", true);
+      return;
+    }
+
+    // The tabs engine might never be inited (if services.sync.registerEngines
+    // is modified), so make sure we avoid undefined errors.
+    let enabled = Weave.Service.isLoggedIn && Weave.Engines.get("tabs") &&
+                  Weave.Engines.get("tabs").enabled;
+    menuitem.setAttribute("disabled", !enabled);
+    menuitem.setAttribute("hidden", false);
+//@line 663 "/build/src/mozilla-release/browser/base/content/browser-places.js"
+  },
+
+  toggleRestoreLastSession: function PHM_toggleRestoreLastSession() {
+    let restoreItem = this._rootElt.getElementsByClassName("restoreLastSession")[0];
+
+    if (this._ss.canRestoreLastSession)
+      restoreItem.removeAttribute("disabled");
+    else
+      restoreItem.setAttribute("disabled", true);
+  },
+
+  _onPopupShowing: function HM__onPopupShowing(aEvent) {
+    PlacesMenu.prototype._onPopupShowing.apply(this, arguments);
+
+    // Don't handle events for submenus.
+    if (aEvent.target != aEvent.currentTarget)
+      return;
+
+    this.toggleRecentlyClosedTabs();
+    this.toggleRecentlyClosedWindows();
+    this.toggleTabsFromOtherComputers();
+    this.toggleRestoreLastSession();
+  },
+
+  _onCommand: function HM__onCommand(aEvent) {
+    let placesNode = aEvent.target._placesNode;
+    if (placesNode) {
+      PlacesUIUtils.markPageAsTyped(placesNode.uri);
+      openUILink(placesNode.uri, aEvent, { ignoreAlt: true });
+    }
+  }
+};
+
+/**
+ * Functions for handling events in the Bookmarks Toolbar and menu.
+ */
+var BookmarksEventHandler = {
+  /**
+   * Handler for click event for an item in the bookmarks toolbar or menu.
+   * Menus and submenus from the folder buttons bubble up to this handler.
+   * Left-click is handled in the onCommand function.
+   * When items are middle-clicked (or clicked with modifier), open in tabs.
+   * If the click came through a menu, close the menu.
+   * @param aEvent
+   *        DOMEvent for the click
+   * @param aView
+   *        The places view which aEvent should be associated with.
+   */
+  onClick: function BEH_onClick(aEvent, aView) {
+    // Only handle middle-click or left-click with modifiers.
+//@line 716 "/build/src/mozilla-release/browser/base/content/browser-places.js"
+    var modifKey = aEvent.ctrlKey || aEvent.shiftKey;
+//@line 718 "/build/src/mozilla-release/browser/base/content/browser-places.js"
+    if (aEvent.button == 2 || (aEvent.button == 0 && !modifKey))
+      return;
+
+    var target = aEvent.originalTarget;
+    // If this event bubbled up from a menu or menuitem, close the menus.
+    // Do this before opening tabs, to avoid hiding the open tabs confirm-dialog.
+    if (target.localName == "menu" || target.localName == "menuitem") {
+      for (node = target.parentNode; node; node = node.parentNode) {
+        if (node.localName == "menupopup")
+          node.hidePopup();
+        else if (node.localName != "menu" &&
+                 node.localName != "splitmenu" &&
+                 node.localName != "hbox" &&
+                 node.localName != "vbox" )
+          break;
+      }
+    }
+
+    if (target._placesNode && PlacesUtils.nodeIsContainer(target._placesNode)) {
+      // Don't open the root folder in tabs when the empty area on the toolbar
+      // is middle-clicked or when a non-bookmark item except for Open in Tabs)
+      // in a bookmarks menupopup is middle-clicked.
+      if (target.localName == "menu" || target.localName == "toolbarbutton")
+        PlacesUIUtils.openContainerNodeInTabs(target._placesNode, aEvent, aView);
+    }
+    else if (aEvent.button == 1) {
+      // left-clicks with modifier are already served by onCommand
+      this.onCommand(aEvent, aView);
+    }
+  },
+
+  /**
+   * Handler for command event for an item in the bookmarks toolbar.
+   * Menus and submenus from the folder buttons bubble up to this handler.
+   * Opens the item.
+   * @param aEvent 
+   *        DOMEvent for the command
+   * @param aView
+   *        The places view which aEvent should be associated with.
+   */
+  onCommand: function BEH_onCommand(aEvent, aView) {
+    var target = aEvent.originalTarget;
+    if (target._placesNode)
+      PlacesUIUtils.openNodeWithEvent(target._placesNode, aEvent, aView);
+  },
+
+  fillInBHTooltip: function BEH_fillInBHTooltip(aDocument, aEvent) {
+    var node;
+    var cropped = false;
+    var targetURI;
+
+    if (aDocument.tooltipNode.localName == "treechildren") {
+      var tree = aDocument.tooltipNode.parentNode;
+      var row = {}, column = {};
+      var tbo = tree.treeBoxObject;
+      tbo.getCellAt(aEvent.clientX, aEvent.clientY, row, column, {});
+      if (row.value == -1)
+        return false;
+      node = tree.view.nodeForTreeIndex(row.value);
+      cropped = tbo.isCellCropped(row.value, column.value);
+    }
+    else {
+      // Check whether the tooltipNode is a Places node.
+      // In such a case use it, otherwise check for targetURI attribute.
+      var tooltipNode = aDocument.tooltipNode;
+      if (tooltipNode._placesNode)
+        node = tooltipNode._placesNode;
+      else {
+        // This is a static non-Places node.
+        targetURI = tooltipNode.getAttribute("targetURI");
+      }
+    }
+
+    if (!node && !targetURI)
+      return false;
+
+    // Show node.label as tooltip's title for non-Places nodes.
+    var title = node ? node.title : tooltipNode.label;
+
+    // Show URL only for Places URI-nodes or nodes with a targetURI attribute.
+    var url;
+    if (targetURI || PlacesUtils.nodeIsURI(node))
+      url = targetURI || node.uri;
+
+    // Show tooltip for containers only if their title is cropped.
+    if (!cropped && !url)
+      return false;
+
+    var tooltipTitle = aDocument.getElementById("bhtTitleText");
+    tooltipTitle.hidden = (!title || (title == url));
+    if (!tooltipTitle.hidden)
+      tooltipTitle.textContent = title;
+
+    var tooltipUrl = aDocument.getElementById("bhtUrlText");
+    tooltipUrl.hidden = !url;
+    if (!tooltipUrl.hidden)
+      tooltipUrl.value = url;
+
+    // Show tooltip.
+    return true;
+  }
+};
+
+
+// Handles special drag and drop functionality for Places menus that are not
+// part of a Places view (e.g. the bookmarks menu in the menubar).
+var PlacesMenuDNDHandler = {
+  _springLoadDelay: 350, // milliseconds
+  _loadTimer: null,
+  _closerTimer: null,
+
+  /**
+   * Called when the user enters the <menu> element during a drag.
+   * @param   event
+   *          The DragEnter event that spawned the opening. 
+   */
+  onDragEnter: function PMDH_onDragEnter(event) {
+    // Opening menus in a Places popup is handled by the view itself.
+    if (!this._isStaticContainer(event.target))
+      return;
+
+    this._loadTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+    this._loadTimer.initWithCallback(function() {
+      PlacesMenuDNDHandler._loadTimer = null;
+      event.target.lastChild.setAttribute("autoopened", "true");
+      event.target.lastChild.showPopup(event.target.lastChild);
+    }, this._springLoadDelay, Ci.nsITimer.TYPE_ONE_SHOT);
+    event.preventDefault();
+    event.stopPropagation();
+  },
+
+  /**
+   * Handles dragexit on the <menu> element.
+   * @returns true if the element is a container element (menu or 
+   *          menu-toolbarbutton), false otherwise.
+   */
+  onDragExit: function PMDH_onDragExit(event) {
+    // Closing menus in a Places popup is handled by the view itself.
+    if (!this._isStaticContainer(event.target))
+      return;
+
+    if (this._loadTimer) {
+      this._loadTimer.cancel();
+      this._loadTimer = null;
+    }
+    this._closeTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
+    this._closeTimer.initWithCallback(function() {
+      this._closeTimer = null;
+      let node = PlacesControllerDragHelper.currentDropTarget;
+      let inHierarchy = false;
+      while (node && !inHierarchy) {
+        inHierarchy = node == event.target;
+        node = node.parentNode;
+      }
+      if (!inHierarchy && event.target.lastChild &&
+          event.target.lastChild.hasAttribute("autoopened")) {
+        event.target.lastChild.removeAttribute("autoopened");
+        event.target.lastChild.hidePopup();
+      }
+    }, this._springLoadDelay, Ci.nsITimer.TYPE_ONE_SHOT);
+  },
+
+  /**
+   * Determines if a XUL element represents a static container.
+   * @returns true if the element is a container element (menu or 
+   *`         menu-toolbarbutton), false otherwise.
+   */
+  _isStaticContainer: function PMDH__isContainer(node) {
+    let isMenu = node.localName == "menu" ||
+                 (node.localName == "toolbarbutton" &&
+                  node.getAttribute("type") == "menu");
+    let isStatic = !("_placesNode" in node) && node.lastChild &&
+                   node.lastChild.hasAttribute("placespopup") &&
+                   !node.parentNode.hasAttribute("placespopup");
+    return isMenu && isStatic;
+  },
+
+  /**
+   * Called when the user drags over the <menu> element.
+   * @param   event
+   *          The DragOver event. 
+   */
+  onDragOver: function PMDH_onDragOver(event) {
+    let ip = new InsertionPoint(PlacesUtils.bookmarksMenuFolderId,
+                                PlacesUtils.bookmarks.DEFAULT_INDEX,
+                                Ci.nsITreeView.DROP_ON);
+    if (ip && PlacesControllerDragHelper.canDrop(ip, event.dataTransfer))
+      event.preventDefault();
+
+    event.stopPropagation();
+  },
+
+  /**
+   * Called when the user drops on the <menu> element.
+   * @param   event
+   *          The Drop event. 
+   */
+  onDrop: function PMDH_onDrop(event) {
+    // Put the item at the end of bookmark menu.
+    let ip = new InsertionPoint(PlacesUtils.bookmarksMenuFolderId,
+                                PlacesUtils.bookmarks.DEFAULT_INDEX,
+                                Ci.nsITreeView.DROP_ON);
+    PlacesControllerDragHelper.onDrop(ip, event.dataTransfer);
+    event.stopPropagation();
+  }
+};
+
+
+var PlacesStarButton = {
+  _hasBookmarksObserver: false,
+  uninit: function PSB_uninit()
+  {
+    if (this._hasBookmarksObserver) {
+      PlacesUtils.removeLazyBookmarkObserver(this);
+    }
+    if (this._pendingStmt) {
+      this._pendingStmt.cancel();
+      delete this._pendingStmt;
+    }
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([
+    Ci.nsINavBookmarkObserver
+  ]),
+
+  get _starredTooltip()
+  {
+    delete this._starredTooltip;
+    return this._starredTooltip =
+      gNavigatorBundle.getString("starButtonOn.tooltip");
+  },
+  get _unstarredTooltip()
+  {
+    delete this._unstarredTooltip;
+    return this._unstarredTooltip =
+      gNavigatorBundle.getString("starButtonOff.tooltip");
+  },
+
+  updateState: function PSB_updateState()
+  {
+    this._starIcon = document.getElementById("star-button");
+    if (!this._starIcon || (this._uri && gBrowser.currentURI.equals(this._uri))) {
+      return;
+    }
+
+    // Reset tracked values.
+    this._uri = gBrowser.currentURI;
+    this._itemIds = [];
+
+    if (this._pendingStmt) {
+      this._pendingStmt.cancel();
+      delete this._pendingStmt;
+    }
+
+    // We can load about:blank before the actual page, but there is no point in handling that page.
+    if (isBlankPageURL(this._uri.spec)) {
+      return;
+    }
+
+    this._pendingStmt = PlacesUtils.asyncGetBookmarkIds(this._uri, function (aItemIds, aURI) {
+      // Safety check that the bookmarked URI equals the tracked one.
+      if (!aURI.equals(this._uri)) {
+        Components.utils.reportError("PlacesStarButton did not receive current URI");
+        return;
+      }
+
+      // It's possible that onItemAdded gets called before the async statement
+      // calls back.  For such an edge case, retain all unique entries from both
+      // arrays.
+      this._itemIds = this._itemIds.filter(
+        function (id) aItemIds.indexOf(id) == -1
+      ).concat(aItemIds);
+      this._updateStateInternal();
+
+      // Start observing bookmarks if needed.
+      if (!this._hasBookmarksObserver) {
+        try {
+          PlacesUtils.addLazyBookmarkObserver(this);
+          this._hasBookmarksObserver = true;
+        } catch(ex) {
+          Components.utils.reportError("PlacesStarButton failed adding a bookmarks observer: " + ex);
+        }
+      }
+
+      delete this._pendingStmt;
+    }, this);
+  },
+
+  _updateStateInternal: function PSB__updateStateInternal()
+  {
+    if (!this._starIcon) {
+      return;
+    }
+
+    if (this._itemIds.length > 0) {
+      this._starIcon.setAttribute("starred", "true");
+      this._starIcon.setAttribute("tooltiptext", this._starredTooltip);
+    }
+    else {
+      this._starIcon.removeAttribute("starred");
+      this._starIcon.setAttribute("tooltiptext", this._unstarredTooltip);
+    }
+  },
+
+  onClick: function PSB_onClick(aEvent)
+  {
+    // Ignore clicks on the star while we update its state.
+    if (aEvent.button == 0 && !this._pendingStmt) {
+      PlacesCommandHook.bookmarkCurrentPage(this._itemIds.length > 0);
+    }
+    // Don't bubble to the textbox, to avoid unwanted selection of the address.
+    aEvent.stopPropagation();
+  },
+
+  // nsINavBookmarkObserver
+  onItemAdded:
+  function PSB_onItemAdded(aItemId, aFolder, aIndex, aItemType, aURI)
+  {
+    if (!this._starIcon) {
+      return;
+    }
+
+    if (aURI && aURI.equals(this._uri)) {
+      // If a new bookmark has been added to the tracked uri, register it.
+      if (this._itemIds.indexOf(aItemId) == -1) {
+        this._itemIds.push(aItemId);
+        this._updateStateInternal();
+      }
+    }
+  },
+
+  onItemRemoved:
+  function PSB_onItemRemoved(aItemId, aFolder, aIndex, aItemType)
+  {
+    if (!this._starIcon) {
+      return;
+    }
+
+    let index = this._itemIds.indexOf(aItemId);
+    // If one of the tracked bookmarks has been removed, unregister it.
+    if (index != -1) {
+      this._itemIds.splice(index, 1);
+      this._updateStateInternal();
+    }
+  },
+
+  onItemChanged:
+  function PSB_onItemChanged(aItemId, aProperty, aIsAnnotationProperty,
+                             aNewValue, aLastModified, aItemType)
+  {
+    if (!this._starIcon) {
+      return;
+    }
+
+    if (aProperty == "uri") {
+      let index = this._itemIds.indexOf(aItemId);
+      // If the changed bookmark was tracked, check if it is now pointing to
+      // a different uri and unregister it.
+      if (index != -1 && aNewValue != this._uri.spec) {
+        this._itemIds.splice(index, 1);
+        this._updateStateInternal();
+      }
+      // If another bookmark is now pointing to the tracked uri, register it.
+      else if (index == -1 && aNewValue == this._uri.spec) {
+        this._itemIds.push(aItemId);
+        this._updateStateInternal();
+      }
+    }
+  },
+
+  onBeginUpdateBatch: function () {},
+  onEndUpdateBatch: function () {},
+  onBeforeItemRemoved: function () {},
+  onItemVisited: function () {},
+  onItemMoved: function () {}
+};
+
+
+// This object handles the initialization and uninitialization of the bookmarks
+// toolbar.  updateState is called when the browser window is opened and
+// after closing the toolbar customization dialog.
+let PlacesToolbarHelper = {
+  _place: "place:folder=TOOLBAR",
+
+  get _viewElt() {
+    return document.getElementById("PlacesToolbar");
+  },
+
+  init: function PTH_init() {
+    let viewElt = this._viewElt;
+    if (!viewElt || viewElt._placesView)
+      return;
+
+    // If the bookmarks toolbar item is hidden because the parent toolbar is
+    // collapsed or hidden (i.e. in a popup), spare the initialization.  Also,
+    // there is no need to initialize the toolbar if customizing because
+    // init() will be called when the customization is done.
+    let toolbar = viewElt.parentNode.parentNode;
+    if (toolbar.collapsed ||
+        getComputedStyle(toolbar, "").display == "none" ||
+        this._isCustomizing)
+      return;
+
+    new PlacesToolbar(this._place);
+  },
+
+  customizeStart: function PTH_customizeStart() {
+    let viewElt = this._viewElt;
+    if (viewElt && viewElt._placesView)
+      viewElt._placesView.uninit();
+
+    this._isCustomizing = true;
+  },
+
+  customizeDone: function PTH_customizeDone() {
+    this._isCustomizing = false;
+    this.init();
+  }
+};
+
+
+// Handles the bookmarks menu button shown when the main menubar is hidden.
+let BookmarksMenuButton = {
+  get button() {
+    return document.getElementById("bookmarks-menu-button");
+  },
+
+  get buttonContainer() {
+    return document.getElementById("bookmarks-menu-button-container");
+  },
+
+  get personalToolbar() {
+    delete this.personalToolbar;
+    return this.personalToolbar = document.getElementById("PersonalToolbar");
+  },
+
+  get bookmarksToolbarItem() {
+    return document.getElementById("personal-bookmarks");
+  },
+
+  init: function BMB_init() {
+    this.updatePosition();
+
+    // Any other stuff that does not regard the button itself should be
+    // handled in the onPopupShowing handler, so it does not hit Ts.
+  },
+
+  _popupNeedsUpdate: {},
+  onPopupShowing: function BMB_onPopupShowing(event) {
+    // Don't handle events for submenus.
+    if (event.target != event.currentTarget)
+      return;
+
+    let popup = event.target;
+    let needsUpdate = this._popupNeedsUpdate[popup.id];
+
+    // Check if popup contents need to be updated.  Note that if needsUpdate is
+    // undefined we have never seen the popup, thus it should be updated.
+    if (needsUpdate === false)
+      return;
+    this._popupNeedsUpdate[popup.id] = false;
+
+    function getPlacesAnonymousElement(aAnonId)
+      document.getAnonymousElementByAttribute(popup.parentNode,
+                                              "placesanonid",
+                                              aAnonId);
+
+    let viewToolbarMenuitem = getPlacesAnonymousElement("view-toolbar");
+    if (viewToolbarMenuitem) {
+      // Update View bookmarks toolbar checkbox menuitem.
+      viewToolbarMenuitem.setAttribute("checked",
+                                       !this.personalToolbar.collapsed);
+    }
+
+    let toolbarMenuitem = getPlacesAnonymousElement("toolbar-autohide");
+    if (toolbarMenuitem) {
+      // If bookmarks items are visible, hide Bookmarks Toolbar menu and the
+      // separator after it.
+      toolbarMenuitem.collapsed = toolbarMenuitem.nextSibling.collapsed =
+        isElementVisible(this.bookmarksToolbarItem);
+    }
+  },
+
+  updatePosition: function BMB_updatePosition() {
+    // Popups will have to be updated when the user customizes the UI, or
+    // changes personal toolbar collapsed status.  Both of those location call
+    // updatePosition(), so this is the only point asking for popup updates.
+    for (let popupId in this._popupNeedsUpdate) {
+      this._popupNeedsUpdate[popupId] = true;
+    }
+
+    let button = this.button;
+    if (!button)
+      return;
+
+    // If the toolbar containing bookmarks is visible, we want to move the
+    // button to bookmarksToolbarItem.
+    let bookmarksToolbarItem = this.bookmarksToolbarItem;
+    let bookmarksOnVisibleToolbar = bookmarksToolbarItem &&
+                                    !bookmarksToolbarItem.parentNode.collapsed &&
+                                    bookmarksToolbarItem.parentNode.getAttribute("autohide") != "true";
+
+    // If the container has been moved by the user to the toolbar containing
+    // bookmarks, we want to preserve the desired position.
+    let container = this.buttonContainer;
+    let containerNearBookmarks = container && bookmarksToolbarItem &&
+                                 container.parentNode == bookmarksToolbarItem.parentNode;
+
+    if (bookmarksOnVisibleToolbar && !containerNearBookmarks) {
+      if (button.parentNode != bookmarksToolbarItem) {
+        this._uninitView();
+        bookmarksToolbarItem.appendChild(button);
+      }
+    }
+    else {
+      if (container && button.parentNode != container) {
+        this._uninitView();
+        container.appendChild(button);
+      }
+    }
+    this._updateStyle();
+  },
+
+  _updateStyle: function BMB__updateStyle() {
+    let button = this.button;
+    if (!button)
+      return;
+
+    let container = this.buttonContainer;
+    let containerOnPersonalToolbar = container &&
+                                     (container.parentNode == this.personalToolbar ||
+                                      container.parentNode.parentNode == this.personalToolbar);
+
+    if (button.parentNode == this.bookmarksToolbarItem ||
+        containerOnPersonalToolbar) {
+      button.classList.add("bookmark-item");
+      button.classList.remove("toolbarbutton-1");
+    }
+    else {
+      button.classList.remove("bookmark-item");
+      button.classList.add("toolbarbutton-1");
+    }
+  },
+
+  _uninitView: function BMB__uninitView() {
+    // When an element with a placesView attached is removed and re-inserted,
+    // XBL reapplies the binding causing any kind of issues and possible leaks,
+    // so kill current view and let popupshowing generate a new one.
+    let button = this.button;
+    if (button && button._placesView)
+      button._placesView.uninit();
+  },
+
+  customizeStart: function BMB_customizeStart() {
+    this._uninitView();
+    let button = this.button;
+    let container = this.buttonContainer;
+    if (button && container && button.parentNode != container) {
+      // Move button back to the container, so user can move or remove it.
+      container.appendChild(button);
+      this._updateStyle();
+    }
+  },
+
+  customizeChange: function BMB_customizeChange() {
+    this._updateStyle();
+  },
+
+  customizeDone: function BMB_customizeDone() {
+    this.updatePosition();
+  }
+};
+//@line 5 "/build/src/mozilla-release/browser/base/content/browser-plugins.js"
+
+function getPluginInfo(pluginElement)
+{
+  var tagMimetype;
+  var pluginsPage;
+  if (pluginElement instanceof HTMLAppletElement) {
+    tagMimetype = "application/x-java-vm";
+  } else {
+    if (pluginElement instanceof HTMLObjectElement) {
+      pluginsPage = pluginElement.getAttribute("codebase");
+    } else {
+      pluginsPage = pluginElement.getAttribute("pluginspage");
+    }
+
+    // only attempt if a pluginsPage is defined.
+    if (pluginsPage) {
+      var doc = pluginElement.ownerDocument;
+      var docShell = findChildShell(doc, gBrowser.docShell, null);
+      try {
+        pluginsPage = makeURI(pluginsPage, doc.characterSet, docShell.currentURI).spec;
+      } catch (ex) {
+        pluginsPage = "";
+      }
+    }
+
+    tagMimetype = pluginElement.QueryInterface(Components.interfaces.nsIObjectLoadingContent)
+                               .actualType;
+
+    if (tagMimetype == "") {
+      tagMimetype = pluginElement.type;
+    }
+  }
+
+  return {mimetype: tagMimetype, pluginsPage: pluginsPage};
+}
+
+var gPluginHandler = {
+
+//@line 50 "/build/src/mozilla-release/browser/base/content/browser-plugins.js"
+
+  // Map the plugin's name to a filtered version more suitable for user UI.
+  makeNicePluginName : function (aName, aFilename) {
+    if (aName == "Shockwave Flash")
+      return "Adobe Flash";
+
+    // Clean up the plugin name by stripping off any trailing version numbers
+    // or "plugin". EG, "Foo Bar Plugin 1.23_02" --> "Foo Bar"
+    let newName = aName.replace(/\bplug-?in\b/i, "").replace(/[\s\d\.\-\_\(\)]+$/, "");
+    return newName;
+  },
+
+  isTooSmall : function (plugin, overlay) {
+    // Is the <object>'s size too small to hold what we want to show?
+    let pluginRect = plugin.getBoundingClientRect();
+    // XXX bug 446693. The text-shadow on the submitted-report text at
+    //     the bottom causes scrollHeight to be larger than it should be.
+    let overflows = (overlay.scrollWidth > pluginRect.width) ||
+                    (overlay.scrollHeight - 5 > pluginRect.height);
+    return overflows;
+  },
+
+  addLinkClickCallback: function (linkNode, callbackName /*callbackArgs...*/) {
+    // XXX just doing (callback)(arg) was giving a same-origin error. bug?
+    let self = this;
+    let callbackArgs = Array.prototype.slice.call(arguments).slice(2);
+    linkNode.addEventListener("click",
+                              function(evt) {
+                                if (!evt.isTrusted)
+                                  return;
+                                evt.preventDefault();
+                                if (callbackArgs.length == 0)
+                                  callbackArgs = [ evt ];
+                                (self[callbackName]).apply(self, callbackArgs);
+                              },
+                              true);
+
+    linkNode.addEventListener("keydown",
+                              function(evt) {
+                                if (!evt.isTrusted)
+                                  return;
+                                if (evt.keyCode == evt.DOM_VK_RETURN) {
+                                  evt.preventDefault();
+                                  if (callbackArgs.length == 0)
+                                    callbackArgs = [ evt ];
+                                  evt.preventDefault();
+                                  (self[callbackName]).apply(self, callbackArgs);
+                                }
+                              },
+                              true);
+  },
+
+  handleEvent : function(event) {
+    let self = gPluginHandler;
+    let plugin = event.target;
+    let doc = plugin.ownerDocument;
+
+    // We're expecting the target to be a plugin.
+    if (!(plugin instanceof Ci.nsIObjectLoadingContent))
+      return;
+
+    // Force a style flush, so that we ensure our binding is attached.
+    plugin.clientTop;
+
+    switch (event.type) {
+      case "PluginCrashed":
+        self.pluginInstanceCrashed(plugin, event);
+        break;
+
+      case "PluginNotFound":
+        // For non-object plugin tags, register a click handler to install the
+        // plugin. Object tags can, and often do, deal with that themselves,
+        // so don't stomp on the page developers toes.
+        if (!(plugin instanceof HTMLObjectElement)) {
+          // We don't yet check to see if there's actually an installer available.
+          let installStatus = doc.getAnonymousElementByAttribute(plugin, "class", "installStatus");
+          installStatus.setAttribute("status", "ready");
+          let iconStatus = doc.getAnonymousElementByAttribute(plugin, "class", "icon");
+          iconStatus.setAttribute("status", "ready");
+
+          let installLink = doc.getAnonymousElementByAttribute(plugin, "class", "installPluginLink");
+          self.addLinkClickCallback(installLink, "installSinglePlugin", plugin);
+        }
+        /* FALLTHRU */
+
+      case "PluginBlocklisted":
+      case "PluginOutdated":
+//@line 140 "/build/src/mozilla-release/browser/base/content/browser-plugins.js"
+        self.pluginUnavailable(plugin, event.type);
+        break;
+
+      case "PluginClickToPlay":
+        self._handleClickToPlayEvent(plugin);
+        break;
+
+      case "PluginDisabled":
+        let manageLink = doc.getAnonymousElementByAttribute(plugin, "class", "managePluginsLink");
+        self.addLinkClickCallback(manageLink, "managePlugins");
+        break;
+    }
+
+    // Hide the in-content UI if it's too big. The crashed plugin handler already did this.
+    if (event.type != "PluginCrashed" && event.type != "PluginClickToPlay") {
+      let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
+      if (self.isTooSmall(plugin, overlay))
+          overlay.style.visibility = "hidden";
+    }
+  },
+
+  activatePlugins: function PH_activatePlugins(aContentWindow) {
+    let browser = gBrowser.getBrowserForDocument(aContentWindow.document);
+    browser._clickToPlayPluginsActivated = true;
+    let cwu = aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                            .getInterface(Ci.nsIDOMWindowUtils);
+    let plugins = cwu.plugins;
+    for (let plugin of plugins) {
+      let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
+      if (!objLoadingContent.activated)
+        objLoadingContent.playPlugin();
+    }
+    let notification = PopupNotifications.getNotification("click-to-play-plugins", browser);
+    if (notification)
+      notification.remove();
+  },
+
+  activateSinglePlugin: function PH_activateSinglePlugin(aContentWindow, aPlugin) {
+    let objLoadingContent = aPlugin.QueryInterface(Ci.nsIObjectLoadingContent);
+    if (!objLoadingContent.activated)
+      objLoadingContent.playPlugin();
+
+    let cwu = aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                            .getInterface(Ci.nsIDOMWindowUtils);
+    let haveUnplayedPlugins = cwu.plugins.some(function(plugin) {
+      let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
+      return (plugin != aPlugin && !objLoadingContent.activated);
+    });
+    let browser = gBrowser.getBrowserForDocument(aContentWindow.document);
+    let notification = PopupNotifications.getNotification("click-to-play-plugins", browser);
+    if (notification && !haveUnplayedPlugins) {
+      browser._clickToPlayDoorhangerShown = false;
+      notification.remove();
+    }
+  },
+
+  newPluginInstalled : function(event) {
+    // browser elements are anonymous so we can't just use target.
+    var browser = event.originalTarget;
+    // clear the plugin list, now that at least one plugin has been installed
+    browser.missingPlugins = null;
+
+    var notificationBox = gBrowser.getNotificationBox(browser);
+    var notification = notificationBox.getNotificationWithValue("missing-plugins");
+    if (notification)
+      notificationBox.removeNotification(notification);
+
+    // reload the browser to make the new plugin show.
+    browser.reload();
+  },
+
+  // Callback for user clicking on a missing (unsupported) plugin.
+  installSinglePlugin: function (plugin) {
+    var missingPluginsArray = {};
+
+    var pluginInfo = getPluginInfo(plugin);
+    missingPluginsArray[pluginInfo.mimetype] = pluginInfo;
+
+    openDialog("chrome://mozapps/content/plugins/pluginInstallerWizard.xul",
+               "PFSWindow", "chrome,centerscreen,resizable=yes",
+               {plugins: missingPluginsArray, browser: gBrowser.selectedBrowser});
+  },
+
+  // Callback for user clicking on a disabled plugin
+  managePlugins: function (aEvent) {
+    BrowserOpenAddonsMgr("addons://list/plugin");
+  },
+
+//@line 238 "/build/src/mozilla-release/browser/base/content/browser-plugins.js"
+
+  // Callback for user clicking a "reload page" link
+  reloadPage: function (browser) {
+    browser.reload();
+  },
+
+  // Callback for user clicking the help icon
+  openHelpPage: function () {
+    openHelpLink("plugin-crashed", false);
+  },
+
+  // Event listener for click-to-play plugins.
+  _handleClickToPlayEvent: function PH_handleClickToPlayEvent(aPlugin) {
+    let doc = aPlugin.ownerDocument;
+    let browser = gBrowser.getBrowserForDocument(doc.defaultView.top.document);
+    let pluginsPermission = Services.perms.testPermission(browser.currentURI, "plugins");
+    let overlay = doc.getAnonymousElementByAttribute(aPlugin, "class", "mainBox");
+
+    if (browser._clickToPlayPluginsActivated) {
+      let objLoadingContent = aPlugin.QueryInterface(Ci.nsIObjectLoadingContent);
+      objLoadingContent.playPlugin();
+      return;
+    } else if (pluginsPermission == Ci.nsIPermissionManager.DENY_ACTION) {
+      overlay.style.visibility = "hidden";
+      return;
+    }
+
+    let overlay = doc.getAnonymousElementByAttribute(aPlugin, "class", "mainBox");
+    // The overlay is null if the XBL binding is not attached (element is display:none).
+    if (overlay) {
+      overlay.addEventListener("click", function(aEvent) {
+        if (aEvent.button == 0 && aEvent.isTrusted)
+          gPluginHandler.activateSinglePlugin(aEvent.target.ownerDocument.defaultView.top, aPlugin);
+      }, true);
+    }
+
+    if (!browser._clickToPlayDoorhangerShown)
+      gPluginHandler._showClickToPlayNotification(browser);
+  },
+
+  reshowClickToPlayNotification: function PH_reshowClickToPlayNotification() {
+    if (!Services.prefs.getBoolPref("plugins.click_to_play"))
+      return;
+
+    let browser = gBrowser.selectedBrowser;
+
+    let pluginsPermission = Services.perms.testPermission(browser.currentURI, "plugins");
+    if (pluginsPermission == Ci.nsIPermissionManager.DENY_ACTION)
+      return;
+
+    let contentWindow = browser.contentWindow;
+    let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                           .getInterface(Ci.nsIDOMWindowUtils);
+    let pluginNeedsActivation = cwu.plugins.some(function(plugin) {
+      let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
+      return !objLoadingContent.activated;
+    });
+    if (pluginNeedsActivation)
+      gPluginHandler._showClickToPlayNotification(browser);
+  },
+
+  _showClickToPlayNotification: function PH_showClickToPlayNotification(aBrowser) {
+    aBrowser._clickToPlayDoorhangerShown = true;
+    let contentWindow = aBrowser.contentWindow;
+
+    let messageString = gNavigatorBundle.getString("activatePluginsMessage.message");
+    let mainAction = {
+      label: gNavigatorBundle.getString("activatePluginsMessage.label"),
+      accessKey: gNavigatorBundle.getString("activatePluginsMessage.accesskey"),
+      callback: function() { gPluginHandler.activatePlugins(contentWindow); }
+    };
+    let secondaryActions = [{
+      label: gNavigatorBundle.getString("activatePluginsMessage.always"),
+      accessKey: gNavigatorBundle.getString("activatePluginsMessage.always.accesskey"),
+      callback: function () {
+        Services.perms.add(aBrowser.currentURI, "plugins", Ci.nsIPermissionManager.ALLOW_ACTION);
+        gPluginHandler.activatePlugins(contentWindow);
+      }
+    },{
+      label: gNavigatorBundle.getString("activatePluginsMessage.never"),
+      accessKey: gNavigatorBundle.getString("activatePluginsMessage.never.accesskey"),
+      callback: function () {
+        Services.perms.add(aBrowser.currentURI, "plugins", Ci.nsIPermissionManager.DENY_ACTION);
+        let notification = PopupNotifications.getNotification("click-to-play-plugins", aBrowser);
+        if (notification)
+          notification.remove();
+        gPluginHandler._removeClickToPlayOverlays(contentWindow);
+      }
+    }];
+    let options = { dismissed: true };
+    PopupNotifications.show(aBrowser, "click-to-play-plugins",
+                            messageString, "plugins-notification-icon",
+                            mainAction, secondaryActions, options);
+  },
+
+  _removeClickToPlayOverlays: function PH_removeClickToPlayOverlays(aContentWindow) {
+    let doc = aContentWindow.document;
+    let cwu = aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                            .getInterface(Ci.nsIDOMWindowUtils);
+    for (let plugin of cwu.plugins) {
+      let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
+      overlay.style.visibility = "hidden";
+    }
+  },
+
+  // event listener for missing/blocklisted/outdated/carbonFailure plugins.
+  pluginUnavailable: function (plugin, eventType) {
+    let browser = gBrowser.getBrowserForDocument(plugin.ownerDocument
+                                                       .defaultView.top.document);
+    if (!browser.missingPlugins)
+      browser.missingPlugins = {};
+
+    var pluginInfo = getPluginInfo(plugin);
+    browser.missingPlugins[pluginInfo.mimetype] = pluginInfo;
+
+    var notificationBox = gBrowser.getNotificationBox(browser);
+
+    // Should only display one of these warnings per page.
+    // In order of priority, they are: outdated > missing > blocklisted
+    let outdatedNotification = notificationBox.getNotificationWithValue("outdated-plugins");
+    let blockedNotification  = notificationBox.getNotificationWithValue("blocked-plugins");
+    let missingNotification  = notificationBox.getNotificationWithValue("missing-plugins");
+
+
+    function showBlocklistInfo() {
+      var url = formatURL("extensions.blocklist.detailsURL", true);
+      gBrowser.loadOneTab(url, {inBackground: false});
+      return true;
+    }
+
+    function showOutdatedPluginsInfo() {
+      gPrefService.setBoolPref("plugins.update.notifyUser", false);
+      var url = formatURL("plugins.update.url", true);
+      gBrowser.loadOneTab(url, {inBackground: false});
+      return true;
+    }
+
+    function showPluginsMissing() {
+      // get the urls of missing plugins
+      var missingPluginsArray = gBrowser.selectedBrowser.missingPlugins;
+      if (missingPluginsArray) {
+        openDialog("chrome://mozapps/content/plugins/pluginInstallerWizard.xul",
+                   "PFSWindow", "chrome,centerscreen,resizable=yes",
+                   {plugins: missingPluginsArray, browser: gBrowser.selectedBrowser});
+      }
+    }
+
+//@line 401 "/build/src/mozilla-release/browser/base/content/browser-plugins.js"
+
+    let notifications = {
+      PluginBlocklisted : {
+                            barID   : "blocked-plugins",
+                            iconURL : "chrome://mozapps/skin/plugins/notifyPluginBlocked.png",
+                            message : gNavigatorBundle.getString("blockedpluginsMessage.title"),
+                            buttons : [{
+                                         label     : gNavigatorBundle.getString("blockedpluginsMessage.infoButton.label"),
+                                         accessKey : gNavigatorBundle.getString("blockedpluginsMessage.infoButton.accesskey"),
+                                         popup     : null,
+                                         callback  : showBlocklistInfo
+                                       },
+                                       {
+                                         label     : gNavigatorBundle.getString("blockedpluginsMessage.searchButton.label"),
+                                         accessKey : gNavigatorBundle.getString("blockedpluginsMessage.searchButton.accesskey"),
+                                         popup     : null,
+                                         callback  : showOutdatedPluginsInfo
+                                      }],
+                          },
+      PluginOutdated    : {
+                            barID   : "outdated-plugins",
+                            iconURL : "chrome://mozapps/skin/plugins/notifyPluginOutdated.png",
+                            message : gNavigatorBundle.getString("outdatedpluginsMessage.title"),
+                            buttons : [{
+                                         label     : gNavigatorBundle.getString("outdatedpluginsMessage.updateButton.label"),
+                                         accessKey : gNavigatorBundle.getString("outdatedpluginsMessage.updateButton.accesskey"),
+                                         popup     : null,
+                                         callback  : showOutdatedPluginsInfo
+                                      }],
+                          },
+      PluginNotFound    : {
+                            barID   : "missing-plugins",
+                            iconURL : "chrome://mozapps/skin/plugins/notifyPluginGeneric.png",
+                            message : gNavigatorBundle.getString("missingpluginsMessage.title"),
+                            buttons : [{
+                                         label     : gNavigatorBundle.getString("missingpluginsMessage.button.label"),
+                                         accessKey : gNavigatorBundle.getString("missingpluginsMessage.button.accesskey"),
+                                         popup     : null,
+                                         callback  : showPluginsMissing
+                                      }],
+                            },
+//@line 455 "/build/src/mozilla-release/browser/base/content/browser-plugins.js"
+    };
+
+    // If there is already an outdated plugin notification then do nothing
+    if (outdatedNotification)
+      return;
+
+//@line 478 "/build/src/mozilla-release/browser/base/content/browser-plugins.js"
+
+    if (eventType == "PluginBlocklisted") {
+      if (gPrefService.getBoolPref("plugins.hide_infobar_for_missing_plugin")) // XXX add a new pref?
+        return;
+
+      if (blockedNotification || missingNotification)
+        return;
+    }
+    else if (eventType == "PluginOutdated") {
+      if (gPrefService.getBoolPref("plugins.hide_infobar_for_outdated_plugin"))
+        return;
+
+      // Cancel any notification about blocklisting/missing plugins
+      if (blockedNotification)
+        blockedNotification.close();
+      if (missingNotification)
+        missingNotification.close();
+    }
+    else if (eventType == "PluginNotFound") {
+      if (gPrefService.getBoolPref("plugins.hide_infobar_for_missing_plugin"))
+        return;
+
+      if (missingNotification)
+        return;
+
+      // Cancel any notification about blocklisting plugins
+      if (blockedNotification)
+        blockedNotification.close();
+    }
+
+    let notify = notifications[eventType];
+    notificationBox.appendNotification(notify.message, notify.barID, notify.iconURL,
+                                       notificationBox.PRIORITY_WARNING_MEDIUM,
+                                       notify.buttons);
+  },
+
+  // Crashed-plugin observer. Notified once per plugin crash, before events
+  // are dispatched to individual plugin instances.
+  pluginCrashed : function(subject, topic, data) {
+    let propertyBag = subject;
+    if (!(propertyBag instanceof Ci.nsIPropertyBag2) ||
+        !(propertyBag instanceof Ci.nsIWritablePropertyBag2))
+     return;
+
+//@line 535 "/build/src/mozilla-release/browser/base/content/browser-plugins.js"
+  },
+
+  // Crashed-plugin event listener. Called for every instance of a
+  // plugin in content.
+  pluginInstanceCrashed: function (plugin, aEvent) {
+    // Ensure the plugin and event are of the right type.
+    if (!(aEvent instanceof Ci.nsIDOMDataContainerEvent))
+      return;
+
+    let submittedReport = aEvent.getData("submittedCrashReport");
+    let doPrompt        = true; // XXX followup for .getData("doPrompt");
+    let submitReports   = true; // XXX followup for .getData("submitReports");
+    let pluginName      = aEvent.getData("pluginName");
+    let pluginFilename  = aEvent.getData("pluginFilename");
+    let pluginDumpID    = aEvent.getData("pluginDumpID");
+    let browserDumpID   = aEvent.getData("browserDumpID");
+
+    // Remap the plugin name to a more user-presentable form.
+    pluginName = this.makeNicePluginName(pluginName, pluginFilename);
+
+    let messageString = gNavigatorBundle.getFormattedString("crashedpluginsMessage.title", [pluginName]);
+
+    //
+    // Configure the crashed-plugin placeholder.
+    //
+    let doc = plugin.ownerDocument;
+    let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
+    let statusDiv = doc.getAnonymousElementByAttribute(plugin, "class", "submitStatus");
+//@line 627 "/build/src/mozilla-release/browser/base/content/browser-plugins.js"
+
+    let crashText = doc.getAnonymousElementByAttribute(plugin, "class", "msg msgCrashed");
+    crashText.textContent = messageString;
+
+    let browser = gBrowser.getBrowserForDocument(doc.defaultView.top.document);
+
+    let link = doc.getAnonymousElementByAttribute(plugin, "class", "reloadLink");
+    this.addLinkClickCallback(link, "reloadPage", browser);
+
+    let notificationBox = gBrowser.getNotificationBox(browser);
+
+    // Is the <object>'s size too small to hold what we want to show?
+    if (this.isTooSmall(plugin, overlay)) {
+        // Hide the overlay's contents. Use visibility style, so that it
+        // doesn't collapse down to 0x0.
+        overlay.style.visibility = "hidden";
+        // If another plugin on the page was large enough to show our UI, we
+        // don't want to show a notification bar.
+        if (!doc.mozNoPluginCrashedNotification)
+          showNotificationBar(pluginDumpID, browserDumpID);
+    } else {
+        // If a previous plugin on the page was too small and resulted in
+        // adding a notification bar, then remove it because this plugin
+        // instance it big enough to serve as in-content notification.
+        hideNotificationBar();
+        doc.mozNoPluginCrashedNotification = true;
+    }
+
+    function hideNotificationBar() {
+      let notification = notificationBox.getNotificationWithValue("plugin-crashed");
+      if (notification)
+        notificationBox.removeNotification(notification, true);
+    }
+
+    function showNotificationBar(pluginDumpID, browserDumpID) {
+      // If there's already an existing notification bar, don't do anything.
+      let notification = notificationBox.getNotificationWithValue("plugin-crashed");
+      if (notification)
+        return;
+
+      // Configure the notification bar
+      let priority = notificationBox.PRIORITY_WARNING_MEDIUM;
+      let iconURL = "chrome://mozapps/skin/plugins/notifyPluginCrashed.png";
+      let reloadLabel = gNavigatorBundle.getString("crashedpluginsMessage.reloadButton.label");
+      let reloadKey   = gNavigatorBundle.getString("crashedpluginsMessage.reloadButton.accesskey");
+      let submitLabel = gNavigatorBundle.getString("crashedpluginsMessage.submitButton.label");
+      let submitKey   = gNavigatorBundle.getString("crashedpluginsMessage.submitButton.accesskey");
+
+      let buttons = [{
+        label: reloadLabel,
+        accessKey: reloadKey,
+        popup: null,
+        callback: function() { browser.reload(); },
+      }];
+//@line 691 "/build/src/mozilla-release/browser/base/content/browser-plugins.js"
+
+      let notification = notificationBox.appendNotification(messageString, "plugin-crashed",
+                                                            iconURL, priority, buttons);
+
+      // Add the "learn more" link.
+      let XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+      let link = notification.ownerDocument.createElementNS(XULNS, "label");
+      link.className = "text-link";
+      link.setAttribute("value", gNavigatorBundle.getString("crashedpluginsMessage.learnMore"));
+      let crashurl = formatURL("app.support.baseURL", true);
+      crashurl += "plugin-crashed-notificationbar";
+      link.href = crashurl;
+
+      let description = notification.ownerDocument.getAnonymousElementByAttribute(notification, "anonid", "messageText");
+      description.appendChild(link);
+
+      // Remove the notfication when the page is reloaded.
+      doc.defaultView.top.addEventListener("unload", function() {
+        notificationBox.removeNotification(notification);
+      }, false);
+    }
+
+  }
+};
+/*
+//@line 7 "/build/src/mozilla-release/browser/base/content/browser-tabPreviews.js"
+ */
+
+/**
+ * Tab previews utility, produces thumbnails
+ */
+var tabPreviews = {
+  aspectRatio: 0.5625, // 16:9
+  init: function tabPreviews_init() {
+    if (this._selectedTab)
+      return;
+    this._selectedTab = gBrowser.selectedTab;
+
+    this.width = Math.ceil(screen.availWidth / 5.75);
+    this.height = Math.round(this.width * this.aspectRatio);
+
+    window.addEventListener("unload", this, false);
+    gBrowser.tabContainer.addEventListener("TabSelect", this, false);
+    gBrowser.tabContainer.addEventListener("SSTabRestored", this, false);
+  },
+  uninit: function tabPreviews_uninit() {
+    window.removeEventListener("unload", this, false);
+    gBrowser.tabContainer.removeEventListener("TabSelect", this, false);
+    gBrowser.tabContainer.removeEventListener("SSTabRestored", this, false);
+    this._selectedTab = null;
+  },
+  get: function tabPreviews_get(aTab) {
+    this.init();
+
+    if (aTab.__thumbnail_lastURI &&
+        aTab.__thumbnail_lastURI != aTab.linkedBrowser.currentURI.spec) {
+      aTab.__thumbnail = null;
+      aTab.__thumbnail_lastURI = null;
+    }
+    return aTab.__thumbnail || this.capture(aTab, !aTab.hasAttribute("busy"));
+  },
+  capture: function tabPreviews_capture(aTab, aStore) {
+    this.init();
+
+    var thumbnail = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+    thumbnail.mozOpaque = true;
+    thumbnail.height = this.height;
+    thumbnail.width = this.width;
+
+    var ctx = thumbnail.getContext("2d");
+    var win = aTab.linkedBrowser.contentWindow;
+    var snippetWidth = win.innerWidth * .6;
+    var scale = this.width / snippetWidth;
+    ctx.scale(scale, scale);
+    ctx.drawWindow(win, win.scrollX, win.scrollY,
+                   snippetWidth, snippetWidth * this.aspectRatio, "rgb(255,255,255)");
+
+    if (aStore) {
+      aTab.__thumbnail = thumbnail;
+      aTab.__thumbnail_lastURI = aTab.linkedBrowser.currentURI.spec;
+    }
+    return thumbnail;
+  },
+  handleEvent: function tabPreviews_handleEvent(event) {
+    switch (event.type) {
+      case "TabSelect":
+        if (this._selectedTab &&
+            this._selectedTab.parentNode &&
+            !this._pendingUpdate) {
+          // Generate a thumbnail for the tab that was selected.
+          // The timeout keeps the UI snappy and prevents us from generating thumbnails
+          // for tabs that will be closed. During that timeout, don't generate other
+          // thumbnails in case multiple TabSelect events occur fast in succession.
+          this._pendingUpdate = true;
+          setTimeout(function (self, aTab) {
+            self._pendingUpdate = false;
+            if (aTab.parentNode && !aTab.hasAttribute("busy"))
+              self.capture(aTab, true);
+          }, 2000, this, this._selectedTab);
+        }
+        this._selectedTab = event.target;
+        break;
+      case "SSTabRestored":
+        this.capture(event.target, true);
+        break;
+      case "unload":
+        this.uninit();
+        break;
+    }
+  }
+};
+
+var tabPreviewPanelHelper = {
+  opening: function (host) {
+    host.panel.hidden = false;
+
+    var handler = this._generateHandler(host);
+    host.panel.addEventListener("popupshown", handler, false);
+    host.panel.addEventListener("popuphiding", handler, false);
+    host.panel.addEventListener("popuphidden", handler, false);
+
+    host._prevFocus = document.commandDispatcher.focusedElement;
+  },
+  _generateHandler: function (host) {
+    var self = this;
+    return function (event) {
+      if (event.target == host.panel) {
+        host.panel.removeEventListener(event.type, arguments.callee, false);
+        self["_" + event.type](host);
+      }
+    };
+  },
+  _popupshown: function (host) {
+    if ("setupGUI" in host)
+      host.setupGUI();
+  },
+  _popuphiding: function (host) {
+    if ("suspendGUI" in host)
+      host.suspendGUI();
+
+    if (host._prevFocus) {
+      Cc["@mozilla.org/focus-manager;1"]
+        .getService(Ci.nsIFocusManager)
+        .setFocus(host._prevFocus, Ci.nsIFocusManager.FLAG_NOSCROLL);
+      host._prevFocus = null;
+    } else
+      gBrowser.selectedBrowser.focus();
+
+    if (host.tabToSelect) {
+      gBrowser.selectedTab = host.tabToSelect;
+      host.tabToSelect = null;
+    }
+  },
+  _popuphidden: function (host) {
+    // Destroy the widget in order to prevent outdated content
+    // when re-opening the panel.
+    host.panel.hidden = true;
+  }
+};
+
+/**
+ * Ctrl-Tab panel
+ */
+var ctrlTab = {
+  get panel () {
+    delete this.panel;
+    return this.panel = document.getElementById("ctrlTab-panel");
+  },
+  get showAllButton () {
+    delete this.showAllButton;
+    return this.showAllButton = document.getElementById("ctrlTab-showAll");
+  },
+  get previews () {
+    delete this.previews;
+    return this.previews = this.panel.getElementsByClassName("ctrlTab-preview");
+  },
+  get recentlyUsedLimit () {
+    delete this.recentlyUsedLimit;
+    return this.recentlyUsedLimit = gPrefService.getIntPref("browser.ctrlTab.recentlyUsedLimit");
+  },
+  get keys () {
+    var keys = {};
+    ["close", "find", "selectAll"].forEach(function (key) {
+      keys[key] = document.getElementById("key_" + key)
+                          .getAttribute("key")
+                          .toLocaleLowerCase().charCodeAt(0);
+    });
+    delete this.keys;
+    return this.keys = keys;
+  },
+  _selectedIndex: 0,
+  get selected () this._selectedIndex < 0 ?
+                    document.activeElement :
+                    this.previews.item(this._selectedIndex),
+  get isOpen   () this.panel.state == "open" || this.panel.state == "showing" || this._timer,
+  get tabCount () this.tabList.length,
+  get tabPreviewCount () Math.min(this.previews.length - 1, this.tabCount),
+  get canvasWidth () Math.min(tabPreviews.width,
+                              Math.ceil(screen.availWidth * .85 / this.tabPreviewCount)),
+  get canvasHeight () Math.round(this.canvasWidth * tabPreviews.aspectRatio),
+
+  get tabList () {
+    if (this._tabList)
+      return this._tabList;
+
+    // Using gBrowser.tabs instead of gBrowser.visibleTabs, as the latter
+    // exlcudes closing tabs, breaking the following loop in case the the
+    // selected tab is closing.
+    let list = Array.filter(gBrowser.tabs, function (tab) !tab.hidden);
+
+    // Rotate the list until the selected tab is first
+    while (!list[0].selected)
+      list.push(list.shift());
+
+    list = list.filter(function (tab) !tab.closing);
+
+    if (this.recentlyUsedLimit != 0) {
+      let recentlyUsedTabs = this._recentlyUsedTabs;
+      if (this.recentlyUsedLimit > 0)
+        recentlyUsedTabs = this._recentlyUsedTabs.slice(0, this.recentlyUsedLimit);
+      for (let i = recentlyUsedTabs.length - 1; i >= 0; i--) {
+        list.splice(list.indexOf(recentlyUsedTabs[i]), 1);
+        list.unshift(recentlyUsedTabs[i]);
+      }
+    }
+
+    return this._tabList = list;
+  },
+
+  init: function ctrlTab_init() {
+    if (!this._recentlyUsedTabs) {
+      tabPreviews.init();
+
+      this._recentlyUsedTabs = [gBrowser.selectedTab];
+      this._init(true);
+    }
+  },
+
+  uninit: function ctrlTab_uninit() {
+    this._recentlyUsedTabs = null;
+    this._init(false);
+  },
+
+  prefName: "browser.ctrlTab.previews",
+  readPref: function ctrlTab_readPref() {
+    var enable =
+      gPrefService.getBoolPref(this.prefName) &&
+      (!gPrefService.prefHasUserValue("browser.ctrlTab.disallowForScreenReaders") ||
+       !gPrefService.getBoolPref("browser.ctrlTab.disallowForScreenReaders"));
+
+    if (enable)
+      this.init();
+    else
+      this.uninit();
+  },
+  observe: function (aSubject, aTopic, aPrefName) {
+    this.readPref();
+  },
+
+  updatePreviews: function ctrlTab_updatePreviews() {
+    for (let i = 0; i < this.previews.length; i++)
+      this.updatePreview(this.previews[i], this.tabList[i]);
+
+    var showAllLabel = gNavigatorBundle.getString("ctrlTab.showAll.label");
+    this.showAllButton.label =
+      PluralForm.get(this.tabCount, showAllLabel).replace("#1", this.tabCount);
+  },
+
+  updatePreview: function ctrlTab_updatePreview(aPreview, aTab) {
+    if (aPreview == this.showAllButton)
+      return;
+
+    aPreview._tab = aTab;
+
+    if (aPreview.firstChild)
+      aPreview.removeChild(aPreview.firstChild);
+    if (aTab) {
+      let canvasWidth = this.canvasWidth;
+      let canvasHeight = this.canvasHeight;
+      aPreview.appendChild(tabPreviews.get(aTab));
+      aPreview.setAttribute("label", aTab.label);
+      aPreview.setAttribute("tooltiptext", aTab.label);
+      aPreview.setAttribute("crop", aTab.crop);
+      aPreview.setAttribute("canvaswidth", canvasWidth);
+      aPreview.setAttribute("canvasstyle",
+                            "max-width:" + canvasWidth + "px;" +
+                            "min-width:" + canvasWidth + "px;" +
+                            "max-height:" + canvasHeight + "px;" +
+                            "min-height:" + canvasHeight + "px;");
+      if (aTab.image)
+        aPreview.setAttribute("image", aTab.image);
+      else
+        aPreview.removeAttribute("image");
+      aPreview.hidden = false;
+    } else {
+      aPreview.hidden = true;
+      aPreview.removeAttribute("label");
+      aPreview.removeAttribute("tooltiptext");
+      aPreview.removeAttribute("image");
+    }
+  },
+
+  advanceFocus: function ctrlTab_advanceFocus(aForward) {
+    if (this._selectedIndex == -1) {
+      // No virtual selectedIndex, focus must be in the panel already.
+      if (aForward)
+        document.commandDispatcher.advanceFocus();
+      else
+        document.commandDispatcher.rewindFocus();
+    } else {
+      // Focus isn't in the panel yet, so we maintain a virtual selectedIndex.
+      do {
+        this._selectedIndex += aForward ? 1 : -1;
+        if (this._selectedIndex < 0)
+          this._selectedIndex = this.previews.length - 1;
+        else if (this._selectedIndex >= this.previews.length)
+          this._selectedIndex = 0;
+      } while (this.selected.hidden);
+    }
+
+    if (this._timer) {
+      clearTimeout(this._timer);
+      this._timer = null;
+      this._openPanel();
+    }
+  },
+
+  _mouseOverFocus: function ctrlTab_mouseOverFocus(aPreview) {
+    if (this._trackMouseOver)
+      aPreview.focus();
+  },
+
+  pick: function ctrlTab_pick(aPreview) {
+    if (!this.tabCount)
+      return;
+
+    var select = (aPreview || this.selected);
+
+    if (select == this.showAllButton)
+      this.showAllTabs();
+    else
+      this.close(select._tab);
+  },
+
+  showAllTabs: function ctrlTab_showAllTabs(aPreview) {
+    this.close();
+    document.getElementById("Browser:ShowAllTabs").doCommand();
+  },
+
+  remove: function ctrlTab_remove(aPreview) {
+    if (aPreview._tab)
+      gBrowser.removeTab(aPreview._tab);
+  },
+
+  attachTab: function ctrlTab_attachTab(aTab, aPos) {
+    if (aPos == 0)
+      this._recentlyUsedTabs.unshift(aTab);
+    else if (aPos)
+      this._recentlyUsedTabs.splice(aPos, 0, aTab);
+    else
+      this._recentlyUsedTabs.push(aTab);
+  },
+  detachTab: function ctrlTab_detachTab(aTab) {
+    var i = this._recentlyUsedTabs.indexOf(aTab);
+    if (i >= 0)
+      this._recentlyUsedTabs.splice(i, 1);
+  },
+
+  open: function ctrlTab_open() {
+    if (this.isOpen)
+      return;
+
+    allTabs.close();
+
+    document.addEventListener("keyup", this, true);
+
+    this.updatePreviews();
+    this._selectedIndex = 1;
+
+    // Add a slight delay before showing the UI, so that a quick
+    // "ctrl-tab" keypress just flips back to the MRU tab.
+    this._timer = setTimeout(function (self) {
+      self._timer = null;
+      self._openPanel();
+    }, 200, this);
+  },
+
+  _openPanel: function ctrlTab_openPanel() {
+    tabPreviewPanelHelper.opening(this);
+
+    this.panel.width = Math.min(screen.availWidth * .99,
+                                this.canvasWidth * 1.25 * this.tabPreviewCount);
+    var estimateHeight = this.canvasHeight * 1.25 + 75;
+    this.panel.openPopupAtScreen(screen.availLeft + (screen.availWidth - this.panel.width) / 2,
+                                 screen.availTop + (screen.availHeight - estimateHeight) / 2,
+                                 false);
+  },
+
+  close: function ctrlTab_close(aTabToSelect) {
+    if (!this.isOpen)
+      return;
+
+    if (this._timer) {
+      clearTimeout(this._timer);
+      this._timer = null;
+      this.suspendGUI();
+      if (aTabToSelect)
+        gBrowser.selectedTab = aTabToSelect;
+      return;
+    }
+
+    this.tabToSelect = aTabToSelect;
+    this.panel.hidePopup();
+  },
+
+  setupGUI: function ctrlTab_setupGUI() {
+    this.selected.focus();
+    this._selectedIndex = -1;
+
+    // Track mouse movement after a brief delay so that the item that happens
+    // to be under the mouse pointer initially won't be selected unintentionally.
+    this._trackMouseOver = false;
+    setTimeout(function (self) {
+      if (self.isOpen)
+        self._trackMouseOver = true;
+    }, 0, this);
+  },
+
+  suspendGUI: function ctrlTab_suspendGUI() {
+    document.removeEventListener("keyup", this, true);
+
+    Array.forEach(this.previews, function (preview) {
+      this.updatePreview(preview, null);
+    }, this);
+
+    this._tabList = null;
+  },
+
+  onKeyPress: function ctrlTab_onKeyPress(event) {
+    var isOpen = this.isOpen;
+
+    if (isOpen) {
+      event.preventDefault();
+      event.stopPropagation();
+    }
+
+    switch (event.keyCode) {
+      case event.DOM_VK_TAB:
+        if (event.ctrlKey && !event.altKey && !event.metaKey) {
+          if (isOpen) {
+            this.advanceFocus(!event.shiftKey);
+          } else if (!event.shiftKey) {
+            event.preventDefault();
+            event.stopPropagation();
+            let tabs = gBrowser.visibleTabs;
+            if (tabs.length > 2) {
+              this.open();
+            } else if (tabs.length == 2) {
+              let index = gBrowser.selectedTab == tabs[0] ? 1 : 0;
+              gBrowser.selectedTab = tabs[index];
+            }
+          }
+        }
+        break;
+      default:
+        if (isOpen && event.ctrlKey) {
+          if (event.keyCode == event.DOM_VK_DELETE) {
+            this.remove(this.selected);
+            break;
+          }
+          switch (event.charCode) {
+            case this.keys.close:
+              this.remove(this.selected);
+              break;
+            case this.keys.find:
+            case this.keys.selectAll:
+              this.showAllTabs();
+              break;
+          }
+        }
+    }
+  },
+
+  removeClosingTabFromUI: function ctrlTab_removeClosingTabFromUI(aTab) {
+    if (this.tabCount == 2) {
+      this.close();
+      return;
+    }
+
+    this._tabList = null;
+    this.updatePreviews();
+
+    if (this.selected.hidden)
+      this.advanceFocus(false);
+    if (this.selected == this.showAllButton)
+      this.advanceFocus(false);
+
+    // If the current tab is removed, another tab can steal our focus.
+    if (aTab == gBrowser.selectedTab && this.panel.state == "open") {
+      setTimeout(function (selected) {
+        selected.focus();
+      }, 0, this.selected);
+    }
+  },
+
+  handleEvent: function ctrlTab_handleEvent(event) {
+    switch (event.type) {
+      case "TabAttrModified":
+        // tab attribute modified (e.g. label, crop, busy, image, selected)
+        for (let i = this.previews.length - 1; i >= 0; i--) {
+          if (this.previews[i]._tab && this.previews[i]._tab == event.target) {
+            this.updatePreview(this.previews[i], event.target);
+            break;
+          }
+        }
+        break;
+      case "TabSelect":
+        this.detachTab(event.target);
+        this.attachTab(event.target, 0);
+        break;
+      case "TabOpen":
+        this.attachTab(event.target, 1);
+        break;
+      case "TabClose":
+        this.detachTab(event.target);
+        if (this.isOpen)
+          this.removeClosingTabFromUI(event.target);
+        break;
+      case "keypress":
+        this.onKeyPress(event);
+        break;
+      case "keyup":
+        if (event.keyCode == event.DOM_VK_CONTROL)
+          this.pick();
+        break;
+    }
+  },
+
+  _init: function ctrlTab__init(enable) {
+    var toggleEventListener = enable ? "addEventListener" : "removeEventListener";
+
+    var tabContainer = gBrowser.tabContainer;
+    tabContainer[toggleEventListener]("TabOpen", this, false);
+    tabContainer[toggleEventListener]("TabAttrModified", this, false);
+    tabContainer[toggleEventListener]("TabSelect", this, false);
+    tabContainer[toggleEventListener]("TabClose", this, false);
+
+    document[toggleEventListener]("keypress", this, false);
+    gBrowser.mTabBox.handleCtrlTab = !enable;
+
+    // If we're not running, hide the "Show All Tabs" menu item,
+    // as Shift+Ctrl+Tab will be handled by the tab bar.
+    document.getElementById("menu_showAllTabs").hidden = !enable;
+
+    // Also disable the <key> to ensure Shift+Ctrl+Tab never triggers
+    // Show All Tabs.
+    var key_showAllTabs = document.getElementById("key_showAllTabs");
+    if (enable)
+      key_showAllTabs.removeAttribute("disabled");
+    else
+      key_showAllTabs.setAttribute("disabled", "true");
+  }
+};
+
+
+/**
+ * All Tabs panel
+ */
+var allTabs = {
+  get panel () {
+    delete this.panel;
+    return this.panel = document.getElementById("allTabs-panel");
+  },
+  get filterField () {
+    delete this.filterField;
+    return this.filterField = document.getElementById("allTabs-filter");
+  },
+  get container () {
+    delete this.container;
+    return this.container = document.getElementById("allTabs-container");
+  },
+  get tabCloseButton () {
+    delete this.tabCloseButton;
+    return this.tabCloseButton = document.getElementById("allTabs-tab-close-button");
+  },
+  get toolbarButton() document.getElementById("alltabs-button"),
+  get previews () this.container.getElementsByClassName("allTabs-preview"),
+  get isOpen () this.panel.state == "open" || this.panel.state == "showing",
+
+  init: function allTabs_init() {
+    if (this._initiated)
+      return;
+    this._initiated = true;
+
+    tabPreviews.init();
+
+    Array.forEach(gBrowser.tabs, function (tab) {
+      this._addPreview(tab);
+    }, this);
+
+    gBrowser.tabContainer.addEventListener("TabOpen", this, false);
+    gBrowser.tabContainer.addEventListener("TabAttrModified", this, false);
+    gBrowser.tabContainer.addEventListener("TabMove", this, false);
+    gBrowser.tabContainer.addEventListener("TabClose", this, false);
+  },
+
+  uninit: function allTabs_uninit() {
+    if (!this._initiated)
+      return;
+
+    gBrowser.tabContainer.removeEventListener("TabOpen", this, false);
+    gBrowser.tabContainer.removeEventListener("TabAttrModified", this, false);
+    gBrowser.tabContainer.removeEventListener("TabMove", this, false);
+    gBrowser.tabContainer.removeEventListener("TabClose", this, false);
+
+    while (this.container.hasChildNodes())
+      this.container.removeChild(this.container.firstChild);
+
+    this._initiated = false;
+  },
+
+  prefName: "browser.allTabs.previews",
+  readPref: function allTabs_readPref() {
+    var allTabsButton = this.toolbarButton;
+    if (!allTabsButton)
+      return;
+
+    if (gPrefService.getBoolPref(this.prefName)) {
+      allTabsButton.removeAttribute("type");
+      allTabsButton.setAttribute("command", "Browser:ShowAllTabs");
+    } else {
+      allTabsButton.setAttribute("type", "menu");
+      allTabsButton.removeAttribute("command");
+      allTabsButton.removeAttribute("oncommand");
+    }
+  },
+  observe: function (aSubject, aTopic, aPrefName) {
+    this.readPref();
+  },
+
+  pick: function allTabs_pick(aPreview) {
+    if (!aPreview)
+      aPreview = this._firstVisiblePreview;
+    if (aPreview)
+      this.tabToSelect = aPreview._tab;
+
+    this.close();
+  },
+
+  closeTab: function allTabs_closeTab(event) {
+    this.filterField.focus();
+    gBrowser.removeTab(event.currentTarget._targetPreview._tab);
+  },
+
+  filter: function allTabs_filter() {
+    if (this._currentFilter == this.filterField.value)
+      return;
+
+    this._currentFilter = this.filterField.value;
+
+    var filter = this._currentFilter.split(/\s+/g);
+    this._visible = 0;
+    Array.forEach(this.previews, function (preview) {
+      var tab = preview._tab;
+      var matches = 0;
+      if (filter.length && !tab.hidden) {
+        let tabstring = tab.linkedBrowser.currentURI.spec;
+        try {
+          tabstring = decodeURI(tabstring);
+        } catch (e) {}
+        tabstring = tab.label + " " + tab.label.toLocaleLowerCase() + " " + tabstring;
+        for (let i = 0; i < filter.length; i++)
+          matches += tabstring.indexOf(filter[i]) > -1;
+      }
+      if (matches < filter.length || tab.hidden) {
+        preview.hidden = true;
+      }
+      else {
+        this._visible++;
+        this._updatePreview(preview);
+        preview.hidden = false;
+      }
+    }, this);
+
+    this._reflow();
+  },
+
+  open: function allTabs_open() {
+    var allTabsButton = this.toolbarButton;
+    if (allTabsButton &&
+        allTabsButton.getAttribute("type") == "menu") {
+      // Without setTimeout, the menupopup won't stay open when invoking
+      // "View > Show All Tabs" and the menu bar auto-hides.
+      setTimeout(function () {
+        allTabsButton.open = true;
+      }, 0);
+      return;
+    }
+
+    this.init();
+
+    if (this.isOpen)
+      return;
+
+    this._maxPanelHeight = Math.max(gBrowser.clientHeight, screen.availHeight / 2);
+    this._maxPanelWidth = Math.max(gBrowser.clientWidth, screen.availWidth / 2);
+
+    this.filter();
+
+    tabPreviewPanelHelper.opening(this);
+
+    this.panel.popupBoxObject.setConsumeRollupEvent(Ci.nsIPopupBoxObject.ROLLUP_NO_CONSUME);
+    this.panel.openPopup(gBrowser, "overlap", 0, 0, false, true);
+  },
+
+  close: function allTabs_close() {
+    this.panel.hidePopup();
+  },
+
+  setupGUI: function allTabs_setupGUI() {
+    this.filterField.focus();
+    this.filterField.placeholder = this.filterField.tooltipText;
+
+    this.panel.addEventListener("keypress", this, false);
+    this.panel.addEventListener("keypress", this, true);
+    this._browserCommandSet.addEventListener("command", this, false);
+
+    // When the panel is open, a second click on the all tabs button should
+    // close the panel but not re-open it.
+    document.getElementById("Browser:ShowAllTabs").setAttribute("disabled", "true");
+  },
+
+  suspendGUI: function allTabs_suspendGUI() {
+    this.filterField.placeholder = "";
+    this.filterField.value = "";
+    this._currentFilter = null;
+
+    this._updateTabCloseButton();
+
+    this.panel.removeEventListener("keypress", this, false);
+    this.panel.removeEventListener("keypress", this, true);
+    this._browserCommandSet.removeEventListener("command", this, false);
+
+    setTimeout(function () {
+      document.getElementById("Browser:ShowAllTabs").removeAttribute("disabled");
+    }, 300);
+  },
+
+  handleEvent: function allTabs_handleEvent(event) {
+    if (/^Tab/.test(event.type)) {
+      var tab = event.target;
+      if (event.type != "TabOpen")
+        var preview = this._getPreview(tab);
+    }
+    switch (event.type) {
+      case "TabAttrModified":
+        // tab attribute modified (e.g. label, crop, busy, image)
+        if (!preview.hidden)
+          this._updatePreview(preview);
+        break;
+      case "TabOpen":
+        if (this.isOpen)
+          this.close();
+        this._addPreview(tab);
+        break;
+      case "TabMove":
+        let siblingPreview = tab.nextSibling &&
+                             this._getPreview(tab.nextSibling);
+        if (siblingPreview)
+          siblingPreview.parentNode.insertBefore(preview, siblingPreview);
+        else
+          this.container.lastChild.appendChild(preview);
+        if (this.isOpen && !preview.hidden) {
+          this._reflow();
+          preview.focus();
+        }
+        break;
+      case "TabClose":
+        this._removePreview(preview);
+        break;
+      case "keypress":
+        this._onKeyPress(event);
+        break;
+      case "command":
+        if (event.target.id != "Browser:ShowAllTabs") {
+          // Close the panel when there's a browser command executing in the background.
+          this.close();
+        }
+        break;
+    }
+  },
+
+  _visible: 0,
+  _currentFilter: null,
+  get _stack () {
+    delete this._stack;
+    return this._stack = document.getElementById("allTabs-stack");
+  },
+  get _browserCommandSet () {
+    delete this._browserCommandSet;
+    return this._browserCommandSet = document.getElementById("mainCommandSet");
+  },
+  get _previewLabelHeight () {
+    delete this._previewLabelHeight;
+    return this._previewLabelHeight = parseInt(getComputedStyle(this.previews[0], "").lineHeight);
+  },
+
+  get _visiblePreviews ()
+    Array.filter(this.previews, function (preview) !preview.hidden),
+
+  get _firstVisiblePreview () {
+    if (this._visible == 0)
+      return null;
+    var previews = this.previews;
+    for (let i = 0; i < previews.length; i++) {
+      if (!previews[i].hidden)
+        return previews[i];
+    }
+    return null;
+  },
+
+  _reflow: function allTabs_reflow() {
+    this._updateTabCloseButton();
+
+    const CONTAINER_MAX_WIDTH = this._maxPanelWidth * .95;
+    const CONTAINER_MAX_HEIGHT = this._maxPanelHeight - 35;
+    // the size of the whole preview relative to the thumbnail
+    const REL_PREVIEW_THUMBNAIL = 1.2;
+    const REL_PREVIEW_HEIGHT_WIDTH = tabPreviews.height / tabPreviews.width;
+    const PREVIEW_MAX_WIDTH = tabPreviews.width * REL_PREVIEW_THUMBNAIL;
+
+    var rows, previewHeight, previewWidth, outerHeight;
+    this._columns = Math.floor(CONTAINER_MAX_WIDTH / PREVIEW_MAX_WIDTH);
+    do {
+      rows = Math.ceil(this._visible / this._columns);
+      previewWidth = Math.min(PREVIEW_MAX_WIDTH,
+                              Math.round(CONTAINER_MAX_WIDTH / this._columns));
+      previewHeight = Math.round(previewWidth * REL_PREVIEW_HEIGHT_WIDTH);
+      outerHeight = previewHeight + this._previewLabelHeight;
+    } while (rows * outerHeight > CONTAINER_MAX_HEIGHT && ++this._columns);
+
+    var outerWidth = previewWidth;
+    {
+      let innerWidth = Math.ceil(previewWidth / REL_PREVIEW_THUMBNAIL);
+      let innerHeight = Math.ceil(previewHeight / REL_PREVIEW_THUMBNAIL);
+      var canvasStyle = "max-width:" + innerWidth + "px;" +
+                        "min-width:" + innerWidth + "px;" +
+                        "max-height:" + innerHeight + "px;" +
+                        "min-height:" + innerHeight + "px;";
+    }
+
+    var previews = Array.slice(this.previews);
+
+    while (this.container.hasChildNodes())
+      this.container.removeChild(this.container.firstChild);
+    for (let i = rows || 1; i > 0; i--)
+      this.container.appendChild(document.createElement("hbox"));
+
+    var row = this.container.firstChild;
+    var colCount = 0;
+    previews.forEach(function (preview) {
+      if (!preview.hidden &&
+          ++colCount > this._columns) {
+        row = row.nextSibling;
+        colCount = 1;
+      }
+      preview.setAttribute("minwidth", outerWidth);
+      preview.setAttribute("height", outerHeight);
+      preview.setAttribute("canvasstyle", canvasStyle);
+      preview.removeAttribute("closebuttonhover");
+      row.appendChild(preview);
+    }, this);
+
+    this._stack.width = this._maxPanelWidth;
+    this.container.width = Math.ceil(outerWidth * Math.min(this._columns, this._visible));
+    this.container.left = Math.round((this._maxPanelWidth - this.container.width) / 2);
+    this.container.maxWidth = this._maxPanelWidth - this.container.left;
+    this.container.maxHeight = rows * outerHeight;
+  },
+
+  _addPreview: function allTabs_addPreview(aTab) {
+    var preview = document.createElement("button");
+    preview.className = "allTabs-preview";
+    preview._tab = aTab;
+    this.container.lastChild.appendChild(preview);
+  },
+
+  _removePreview: function allTabs_removePreview(aPreview) {
+    var updateUI = (this.isOpen && !aPreview.hidden);
+    aPreview._tab = null;
+    aPreview.parentNode.removeChild(aPreview);
+    if (updateUI) {
+      this._visible--;
+      this._reflow();
+      this.filterField.focus();
+    }
+  },
+
+  _getPreview: function allTabs_getPreview(aTab) {
+    var previews = this.previews;
+    for (let i = 0; i < previews.length; i++)
+      if (previews[i]._tab == aTab)
+        return previews[i];
+    return null;
+  },
+
+  _updateTabCloseButton: function allTabs_updateTabCloseButton(event) {
+    if (event && event.target == this.tabCloseButton)
+      return;
+
+    if (this.tabCloseButton._targetPreview) {
+      if (event && event.target == this.tabCloseButton._targetPreview)
+        return;
+
+      this.tabCloseButton._targetPreview.removeAttribute("closebuttonhover");
+    }
+
+    if (event &&
+        event.target.parentNode.parentNode == this.container &&
+        (event.target._tab.previousSibling || event.target._tab.nextSibling)) {
+      let canvas = event.target.firstChild.getBoundingClientRect();
+      let container = this.container.getBoundingClientRect();
+      let tabCloseButton = this.tabCloseButton.getBoundingClientRect();
+      let alignLeft = getComputedStyle(this.panel, "").direction == "rtl";
+//@line 908 "/build/src/mozilla-release/browser/base/content/browser-tabPreviews.js"
+      this.tabCloseButton.left = canvas.left -
+                                 container.left +
+                                 parseInt(this.container.left) +
+                                 (alignLeft ? 0 :
+                                  canvas.width - tabCloseButton.width);
+      this.tabCloseButton.top = canvas.top - container.top;
+      this.tabCloseButton._targetPreview = event.target;
+      this.tabCloseButton.style.visibility = "visible";
+      event.target.setAttribute("closebuttonhover", "true");
+    } else {
+      this.tabCloseButton.style.visibility = "hidden";
+      this.tabCloseButton.left = this.tabCloseButton.top = 0;
+      this.tabCloseButton._targetPreview = null;
+    }
+  },
+
+  _updatePreview: function allTabs_updatePreview(aPreview) {
+    aPreview.setAttribute("label", aPreview._tab.label);
+    aPreview.setAttribute("tooltiptext", aPreview._tab.label);
+    aPreview.setAttribute("crop", aPreview._tab.crop);
+    if (aPreview._tab.image)
+      aPreview.setAttribute("image", aPreview._tab.image);
+    else
+      aPreview.removeAttribute("image");
+
+    var thumbnail = tabPreviews.get(aPreview._tab);
+    if (aPreview.firstChild) {
+      if (aPreview.firstChild == thumbnail)
+        return;
+      aPreview.removeChild(aPreview.firstChild);
+    }
+    aPreview.appendChild(thumbnail);
+  },
+
+  _onKeyPress: function allTabs_onKeyPress(event) {
+    if (event.eventPhase == event.CAPTURING_PHASE) {
+      this._onCapturingKeyPress(event);
+      return;
+    }
+
+    if (event.keyCode == event.DOM_VK_ESCAPE) {
+      this.close();
+      event.preventDefault();
+      event.stopPropagation();
+      return;
+    }
+
+    if (event.target == this.filterField) {
+      switch (event.keyCode) {
+        case event.DOM_VK_UP:
+          if (this._visible) {
+            let previews = this._visiblePreviews;
+            let columns = Math.min(previews.length, this._columns);
+            previews[Math.floor(previews.length / columns) * columns - 1].focus();
+            event.preventDefault();
+            event.stopPropagation();
+          }
+          break;
+        case event.DOM_VK_DOWN:
+          if (this._visible) {
+            this._firstVisiblePreview.focus();
+            event.preventDefault();
+            event.stopPropagation();
+          }
+          break;
+      }
+    }
+  },
+
+  _onCapturingKeyPress: function allTabs_onCapturingKeyPress(event) {
+    switch (event.keyCode) {
+      case event.DOM_VK_UP:
+      case event.DOM_VK_DOWN:
+        if (event.target != this.filterField)
+          this._advanceFocusVertically(event);
+        break;
+      case event.DOM_VK_RETURN:
+        if (event.target == this.filterField) {
+          this.filter();
+          this.pick();
+          event.preventDefault();
+          event.stopPropagation();
+        }
+        break;
+    }
+  },
+
+  _advanceFocusVertically: function allTabs_advanceFocusVertically(event) {
+    var preview = document.activeElement;
+    if (!preview || preview.parentNode.parentNode != this.container)
+      return;
+
+    event.stopPropagation();
+
+    var up = (event.keyCode == event.DOM_VK_UP);
+    var previews = this._visiblePreviews;
+
+    if (up && preview == previews[0]) {
+      this.filterField.focus();
+      return;
+    }
+
+    var i       = previews.indexOf(preview);
+    var columns = Math.min(previews.length, this._columns);
+    var column  = i % columns;
+    var row     = Math.floor(i / columns);
+
+    function newIndex()    row * columns + column;
+    function outOfBounds() newIndex() >= previews.length;
+
+    if (up) {
+      row--;
+      if (row < 0) {
+        let rows = Math.ceil(previews.length / columns);
+        row = rows - 1;
+        column--;
+        if (outOfBounds())
+          row--;
+      }
+    } else {
+      row++;
+      if (outOfBounds()) {
+        if (column == columns - 1) {
+          this.filterField.focus();
+          return;
+        }
+        row = 0;
+        column++;
+      }
+    }
+    previews[newIndex()].focus();
+  }
+};
+//@line 4 "/build/src/mozilla-release/browser/base/content/browser-tabview.js"
+
+let TabView = {
+  _deck: null,
+  _iframe: null,
+  _window: null,
+  _initialized: false,
+  _browserKeyHandlerInitialized: false,
+  _closedLastVisibleTabBeforeFrameInitialized: false,
+  _isFrameLoading: false,
+  _initFrameCallbacks: [],
+  _lastSessionGroupName: null,
+  PREF_BRANCH: "browser.panorama.",
+  PREF_FIRST_RUN: "browser.panorama.experienced_first_run",
+  PREF_STARTUP_PAGE: "browser.startup.page",
+  PREF_RESTORE_ENABLED_ONCE: "browser.panorama.session_restore_enabled_once",
+  GROUPS_IDENTIFIER: "tabview-groups",
+  VISIBILITY_IDENTIFIER: "tabview-visibility",
+
+  // ----------
+  get windowTitle() {
+    delete this.windowTitle;
+    let brandBundle = document.getElementById("bundle_brand");
+    let brandShortName = brandBundle.getString("brandShortName");
+    let title = gNavigatorBundle.getFormattedString("tabview.title", [brandShortName]);
+    return this.windowTitle = title;
+  },
+
+  // ----------
+  get firstUseExperienced() {
+    let pref = this.PREF_FIRST_RUN;
+    if (Services.prefs.prefHasUserValue(pref))
+      return Services.prefs.getBoolPref(pref);
+
+    return false;
+  },
+
+  // ----------
+  set firstUseExperienced(val) {
+    Services.prefs.setBoolPref(this.PREF_FIRST_RUN, val);
+  },
+
+  // ----------
+  get sessionRestoreEnabledOnce() {
+    let pref = this.PREF_RESTORE_ENABLED_ONCE;
+    if (Services.prefs.prefHasUserValue(pref))
+      return Services.prefs.getBoolPref(pref);
+
+    return false;
+  },
+
+  // ----------
+  set sessionRestoreEnabledOnce(val) {
+    Services.prefs.setBoolPref(this.PREF_RESTORE_ENABLED_ONCE, val);
+  },
+
+  // ----------
+  init: function TabView_init() {
+    // disable the ToggleTabView command for popup windows
+    goSetCommandEnabled("Browser:ToggleTabView", window.toolbar.visible);
+    if (!window.toolbar.visible)
+      return;
+
+    if (this._initialized)
+      return;
+
+    if (this.firstUseExperienced) {
+      // ___ visibility
+      let sessionstore =
+        Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
+
+      let data = sessionstore.getWindowValue(window, this.VISIBILITY_IDENTIFIER);
+      if (data && data == "true") {
+        this.show();
+      } else {
+        try {
+          data = sessionstore.getWindowValue(window, this.GROUPS_IDENTIFIER);
+          if (data) {
+            let parsedData = JSON.parse(data);
+            this.updateGroupNumberBroadcaster(parsedData.totalNumber || 1);
+          }
+        } catch (e) { }
+
+        let self = this;
+        // if a tab is changed from hidden to unhidden and the iframe is not
+        // initialized, load the iframe and setup the tab.
+        this._tabShowEventListener = function(event) {
+          if (!self._window)
+            self._initFrame(function() {
+              self._window.UI.onTabSelect(gBrowser.selectedTab);
+              if (self._closedLastVisibleTabBeforeFrameInitialized) {
+                self._closedLastVisibleTabBeforeFrameInitialized = false;
+                self._window.UI.showTabView(false);
+              }
+            });
+        };
+        this._tabCloseEventListener = function(event) {
+          if (!self._window && gBrowser.visibleTabs.length == 0)
+            self._closedLastVisibleTabBeforeFrameInitialized = true;
+        };
+        gBrowser.tabContainer.addEventListener(
+          "TabShow", this._tabShowEventListener, false);
+        gBrowser.tabContainer.addEventListener(
+          "TabClose", this._tabCloseEventListener, false);
+
+       if (this._tabBrowserHasHiddenTabs()) {
+         this._setBrowserKeyHandlers();
+       } else {
+         // for restoring last session and undoing recently closed window
+         this._SSWindowStateReadyListener = function (event) {
+           if (this._tabBrowserHasHiddenTabs())
+             this._setBrowserKeyHandlers();
+         }.bind(this);
+         window.addEventListener(
+           "SSWindowStateReady", this._SSWindowStateReadyListener, false);
+        }
+      }
+    }
+
+    Services.prefs.addObserver(this.PREF_BRANCH, this, false);
+
+    this._initialized = true;
+  },
+
+  // ----------
+  // Observes topic changes.
+  observe: function TabView_observe(subject, topic, data) {
+    if (data == this.PREF_FIRST_RUN && this.firstUseExperienced) {
+      this._addToolbarButton();
+      this.enableSessionRestore();
+    }
+  },
+
+  // ----------
+  // Uninitializes TabView.
+  uninit: function TabView_uninit() {
+    if (!this._initialized)
+      return;
+
+    Services.prefs.removeObserver(this.PREF_BRANCH, this);
+
+    if (this._tabShowEventListener)
+      gBrowser.tabContainer.removeEventListener(
+        "TabShow", this._tabShowEventListener, false);
+
+    if (this._tabCloseEventListener)
+      gBrowser.tabContainer.removeEventListener(
+        "TabClose", this._tabCloseEventListener, false);
+
+    if (this._SSWindowStateReadyListener)
+      window.removeEventListener(
+        "SSWindowStateReady", this._SSWindowStateReadyListener, false);
+
+    this._initialized = false;
+  },
+
+  // ----------
+  // Creates the frame and calls the callback once it's loaded. 
+  // If the frame already exists, calls the callback immediately. 
+  _initFrame: function TabView__initFrame(callback) {
+    let hasCallback = typeof callback == "function";
+
+    // prevent frame to be initialized for popup windows
+    if (!window.toolbar.visible)
+      return;
+
+    if (this._window) {
+      if (hasCallback)
+        callback();
+      return;
+    }
+
+    if (hasCallback)
+      this._initFrameCallbacks.push(callback);
+
+    if (this._isFrameLoading)
+      return;
+
+    this._isFrameLoading = true;
+
+    TelemetryStopwatch.start("PANORAMA_INITIALIZATION_TIME_MS");
+
+    // ___ find the deck
+    this._deck = document.getElementById("tab-view-deck");
+
+    // ___ create the frame
+    this._iframe = document.createElement("iframe");
+    this._iframe.id = "tab-view";
+    this._iframe.setAttribute("transparent", "true");
+    this._iframe.setAttribute("tooltip", "tab-view-tooltip");
+    this._iframe.flex = 1;
+
+    let self = this;
+
+    window.addEventListener("tabviewframeinitialized", function onInit() {
+      window.removeEventListener("tabviewframeinitialized", onInit, false);
+
+      TelemetryStopwatch.finish("PANORAMA_INITIALIZATION_TIME_MS");
+
+      self._isFrameLoading = false;
+      self._window = self._iframe.contentWindow;
+      self._setBrowserKeyHandlers();
+
+      if (self._tabShowEventListener) {
+        gBrowser.tabContainer.removeEventListener(
+          "TabShow", self._tabShowEventListener, false);
+        self._tabShowEventListener = null;
+      }
+      if (self._tabCloseEventListener) {
+        gBrowser.tabContainer.removeEventListener(
+          "TabClose", self._tabCloseEventListener, false);
+        self._tabCloseEventListener = null;
+      }
+      if (self._SSWindowStateReadyListener) {
+        window.removeEventListener(
+          "SSWindowStateReady", self._SSWindowStateReadyListener, false);
+        self._SSWindowStateReadyListener = null;
+      }
+
+      self._initFrameCallbacks.forEach(function (cb) cb());
+      self._initFrameCallbacks = [];
+    }, false);
+
+    this._iframe.setAttribute("src", "chrome://browser/content/tabview.html");
+    this._deck.appendChild(this._iframe);
+
+    // ___ create tooltip
+    let tooltip = document.createElement("tooltip");
+    tooltip.id = "tab-view-tooltip";
+    tooltip.setAttribute("onpopupshowing", "return TabView.fillInTooltip(document.tooltipNode);");
+    document.getElementById("mainPopupSet").appendChild(tooltip);
+  },
+
+  // ----------
+  getContentWindow: function TabView_getContentWindow() {
+    return this._window;
+  },
+
+  // ----------
+  isVisible: function TabView_isVisible() {
+    return (this._deck ? this._deck.selectedPanel == this._iframe : false);
+  },
+
+  // ----------
+  show: function TabView_show() {
+    if (this.isVisible())
+      return;
+
+    let self = this;
+    this._initFrame(function() {
+      self._window.UI.showTabView(true);
+    });
+  },
+
+  // ----------
+  hide: function TabView_hide() {
+    if (!this.isVisible())
+      return;
+
+    this._window.UI.exit();
+  },
+
+  // ----------
+  toggle: function TabView_toggle() {
+    if (this.isVisible())
+      this.hide();
+    else 
+      this.show();
+  },
+
+  // ----------
+  _tabBrowserHasHiddenTabs: function TabView_tabBrowserHasHiddenTabs() {
+    return (gBrowser.tabs.length - gBrowser.visibleTabs.length) > 0;
+  },
+
+  // ----------
+  updateContextMenu: function TabView_updateContextMenu(tab, popup) {
+    let separator = document.getElementById("context_tabViewNamedGroups");
+    let isEmpty = true;
+
+    while (popup.firstChild && popup.firstChild != separator)
+      popup.removeChild(popup.firstChild);
+
+    let self = this;
+    this._initFrame(function() {
+      let activeGroup = tab._tabViewTabItem.parent;
+      let groupItems = self._window.GroupItems.groupItems;
+
+      groupItems.forEach(function(groupItem) {
+        // if group has title, it's not hidden and there is no active group or
+        // the active group id doesn't match the group id, a group menu item
+        // would be added.
+        if (!groupItem.hidden && groupItem.getChildren().length &&
+            (!activeGroup || activeGroup.id != groupItem.id)) {
+          let menuItem = self._createGroupMenuItem(groupItem);
+          popup.insertBefore(menuItem, separator);
+          isEmpty = false;
+        }
+      });
+      separator.hidden = isEmpty;
+    });
+  },
+
+  // ----------
+  _createGroupMenuItem: function TabView__createGroupMenuItem(groupItem) {
+    let menuItem = document.createElement("menuitem");
+    let title = groupItem.getTitle();
+
+    if (!title.trim()) {
+      let topChildLabel = groupItem.getTopChild().tab.label;
+      let childNum = groupItem.getChildren().length;
+
+      if (childNum > 1) {
+        let num = childNum - 1;
+        title =
+          gNavigatorBundle.getString("tabview.moveToUnnamedGroup.label");
+        title = PluralForm.get(num, title).replace("#1", topChildLabel).replace("#2", num);
+      } else {
+        title = topChildLabel;
+      }
+    }
+
+    menuItem.setAttribute("label", title);
+    menuItem.setAttribute("tooltiptext", title);
+    menuItem.setAttribute("crop", "center");
+    menuItem.setAttribute("class", "tabview-menuitem");
+    menuItem.setAttribute(
+      "oncommand",
+      "TabView.moveTabTo(TabContextMenu.contextTab,'" + groupItem.id + "')");
+
+    return menuItem;
+  },
+
+  // ----------
+  moveTabTo: function TabView_moveTabTo(tab, groupItemId) {
+    if (this._window) {
+      this._window.GroupItems.moveTabToGroupItem(tab, groupItemId);
+    } else {
+      let self = this;
+      this._initFrame(function() {
+        self._window.GroupItems.moveTabToGroupItem(tab, groupItemId);
+      });
+    }
+  },
+
+  // ----------
+  // Adds new key commands to the browser, for invoking the Tab Candy UI
+  // and for switching between groups of tabs when outside of the Tab Candy UI.
+  _setBrowserKeyHandlers: function TabView__setBrowserKeyHandlers() {
+    if (this._browserKeyHandlerInitialized)
+      return;
+
+    this._browserKeyHandlerInitialized = true;
+
+    let self = this;
+    window.addEventListener("keypress", function(event) {
+      if (self.isVisible() || !self._tabBrowserHasHiddenTabs())
+        return;
+
+      let charCode = event.charCode;
+      // Control (+ Shift) + `
+      if (event.ctrlKey && !event.metaKey && !event.altKey &&
+          (charCode == 96 || charCode == 126)) {
+        event.stopPropagation();
+        event.preventDefault();
+
+        self._initFrame(function() {
+          let groupItems = self._window.GroupItems;
+          let tabItem = groupItems.getNextGroupItemTab(event.shiftKey);
+          if (!tabItem)
+            return;
+
+          if (gBrowser.selectedTab.pinned)
+            groupItems.updateActiveGroupItemAndTabBar(tabItem, {dontSetActiveTabInGroup: true});
+          else
+            gBrowser.selectedTab = tabItem.tab;
+        });
+      }
+    }, true);
+  },
+
+  // ----------
+  // Prepares the tab view for undo close tab.
+  prepareUndoCloseTab: function TabView_prepareUndoCloseTab(blankTabToRemove) {
+    if (this._window) {
+      this._window.UI.restoredClosedTab = true;
+
+      if (blankTabToRemove && blankTabToRemove._tabViewTabItem)
+        blankTabToRemove._tabViewTabItem.isRemovedAfterRestore = true;
+    }
+  },
+
+  // ----------
+  // Cleans up the tab view after undo close tab.
+  afterUndoCloseTab: function TabView_afterUndoCloseTab() {
+    if (this._window)
+      this._window.UI.restoredClosedTab = false;
+  },
+
+  // ----------
+  // On move to group pop showing.
+  moveToGroupPopupShowing: function TabView_moveToGroupPopupShowing(event) {
+    // Update the context menu only if Panorama was already initialized or if
+    // there are hidden tabs.
+    let numHiddenTabs = gBrowser.tabs.length - gBrowser.visibleTabs.length;
+    if (this._window || numHiddenTabs > 0)
+      this.updateContextMenu(TabContextMenu.contextTab, event.target);
+  },
+
+  // ----------
+  // Function: _addToolbarButton
+  // Adds the TabView button to the TabsToolbar.
+  _addToolbarButton: function TabView__addToolbarButton() {
+    let buttonId = "tabview-button";
+
+    if (document.getElementById(buttonId))
+      return;
+
+    let toolbar = document.getElementById("TabsToolbar");
+    let currentSet = toolbar.currentSet.split(",");
+
+    let alltabsPos = currentSet.indexOf("alltabs-button");
+    if (-1 == alltabsPos)
+      return;
+
+    currentSet[alltabsPos] += "," + buttonId;
+    currentSet = currentSet.join(",");
+    toolbar.currentSet = currentSet;
+    toolbar.setAttribute("currentset", currentSet);
+    document.persist(toolbar.id, "currentset");
+  },
+
+  // ----------
+  // Function: updateGroupNumberBroadcaster
+  // Updates the group number broadcaster.
+  updateGroupNumberBroadcaster: function TabView_updateGroupNumberBroadcaster(number) {
+    let groupsNumber = document.getElementById("tabviewGroupsNumber");
+    groupsNumber.setAttribute("groups", number);
+  },
+
+  // ----------
+  // Function: enableSessionRestore
+  // Enables automatic session restore when the browser is started. Does
+  // nothing if we already did that once in the past.
+  enableSessionRestore: function TabView_enableSessionRestore() {
+    if (!this._window || !this.firstUseExperienced)
+      return;
+
+    // do nothing if we already enabled session restore once
+    if (this.sessionRestoreEnabledOnce)
+      return;
+
+    this.sessionRestoreEnabledOnce = true;
+
+    // enable session restore if necessary
+    if (Services.prefs.getIntPref(this.PREF_STARTUP_PAGE) != 3) {
+      Services.prefs.setIntPref(this.PREF_STARTUP_PAGE, 3);
+
+      // show banner
+      this._window.UI.notifySessionRestoreEnabled();
+    }
+  },
+
+  // ----------
+  // Function: fillInTooltip
+  // Fills in the tooltip text.
+  fillInTooltip: function fillInTooltip(tipElement) {
+    let retVal = false;
+    let titleText = null;
+    let direction = tipElement.ownerDocument.dir;
+
+    while (!titleText && tipElement) {
+      if (tipElement.nodeType == Node.ELEMENT_NODE)
+        titleText = tipElement.getAttribute("title");
+      tipElement = tipElement.parentNode;
+    }
+    let tipNode = document.getElementById("tab-view-tooltip");
+    tipNode.style.direction = direction;
+
+    if (titleText) {
+      tipNode.setAttribute("label", titleText);
+      retVal = true;
+    }
+
+    return retVal;
+  }
+};
+//@line 6 "/build/src/mozilla-release/browser/base/content/browser-thumbnails.js"
+
+/**
+ * Keeps thumbnails of open web pages up-to-date.
+ */
+let gBrowserThumbnails = {
+  /**
+   * Pref that controls whether we can store SSL content on disk
+   */
+  PREF_DISK_CACHE_SSL: "browser.cache.disk_cache_ssl",
+
+  _captureDelayMS: 1000,
+
+  /**
+   * Used to keep track of disk_cache_ssl preference
+   */
+  _sslDiskCacheEnabled: null,
+
+  /**
+   * Map of capture() timeouts assigned to their browsers.
+   */
+  _timeouts: null,
+
+  /**
+   * Cache for the PageThumbs module.
+   */
+  _pageThumbs: null,
+
+  /**
+   * List of tab events we want to listen for.
+   */
+  _tabEvents: ["TabClose", "TabSelect"],
+
+  init: function Thumbnails_init() {
+    try {
+      if (Services.prefs.getBoolPref("browser.pagethumbnails.capturing_disabled"))
+        return;
+    } catch (e) {}
+
+    gBrowser.addTabsProgressListener(this);
+    Services.prefs.addObserver(this.PREF_DISK_CACHE_SSL, this, false);
+
+    this._sslDiskCacheEnabled =
+      Services.prefs.getBoolPref(this.PREF_DISK_CACHE_SSL);
+
+    this._tabEvents.forEach(function (aEvent) {
+      gBrowser.tabContainer.addEventListener(aEvent, this, false);
+    }, this);
+
+    this._timeouts = new WeakMap();
+
+    XPCOMUtils.defineLazyModuleGetter(this, "_pageThumbs",
+      "resource:///modules/PageThumbs.jsm", "PageThumbs");
+  },
+
+  uninit: function Thumbnails_uninit() {
+    gBrowser.removeTabsProgressListener(this);
+    Services.prefs.removeObserver(this.PREF_DISK_CACHE_SSL, this);
+
+    this._tabEvents.forEach(function (aEvent) {
+      gBrowser.tabContainer.removeEventListener(aEvent, this, false);
+    }, this);
+  },
+
+  handleEvent: function Thumbnails_handleEvent(aEvent) {
+    switch (aEvent.type) {
+      case "scroll":
+        let browser = aEvent.currentTarget;
+        if (this._timeouts.has(browser))
+          this._delayedCapture(browser);
+        break;
+      case "TabSelect":
+        this._delayedCapture(aEvent.target.linkedBrowser);
+        break;
+      case "TabClose": {
+        this._clearTimeout(aEvent.target.linkedBrowser);
+        break;
+      }
+    }
+  },
+
+  observe: function Thumbnails_observe() {
+    this._sslDiskCacheEnabled =
+      Services.prefs.getBoolPref(this.PREF_DISK_CACHE_SSL);
+  },
+
+  /**
+   * State change progress listener for all tabs.
+   */
+  onStateChange: function Thumbnails_onStateChange(aBrowser, aWebProgress,
+                                                   aRequest, aStateFlags, aStatus) {
+    if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+        aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK)
+      this._delayedCapture(aBrowser);
+  },
+
+  _capture: function Thumbnails_capture(aBrowser) {
+    if (this._shouldCapture(aBrowser))
+      this._pageThumbs.captureAndStore(aBrowser);
+  },
+
+  _delayedCapture: function Thumbnails_delayedCapture(aBrowser) {
+    if (this._timeouts.has(aBrowser))
+      clearTimeout(this._timeouts.get(aBrowser));
+    else
+      aBrowser.addEventListener("scroll", this, true);
+
+    let timeout = setTimeout(function () {
+      this._clearTimeout(aBrowser);
+      this._capture(aBrowser);
+    }.bind(this), this._captureDelayMS);
+
+    this._timeouts.set(aBrowser, timeout);
+  },
+
+  _shouldCapture: function Thumbnails_shouldCapture(aBrowser) {
+    // Capture only if it's the currently selected tab.
+    if (aBrowser != gBrowser.selectedBrowser)
+      return false;
+
+    // Don't capture in per-window private browsing mode.
+    if (gPrivateBrowsingUI.privateWindow)
+      return false;
+
+    let doc = aBrowser.contentDocument;
+
+    // FIXME Bug 720575 - Don't capture thumbnails for SVG or XML documents as
+    //       that currently regresses Talos SVG tests.
+    if (doc instanceof SVGDocument || doc instanceof XMLDocument)
+      return false;
+
+    // There's no point in taking screenshot of loading pages.
+    if (aBrowser.docShell.busyFlags != Ci.nsIDocShell.BUSY_FLAGS_NONE)
+      return false;
+
+    // Don't take screenshots of about: pages.
+    if (aBrowser.currentURI.schemeIs("about"))
+      return false;
+
+    let channel = aBrowser.docShell.currentDocumentChannel;
+
+    // No valid document channel. We shouldn't take a screenshot.
+    if (!channel)
+      return false;
+
+    // Don't take screenshots of internally redirecting about: pages.
+    // This includes error pages.
+    let uri = channel.originalURI;
+    if (uri.schemeIs("about"))
+      return false;
+
+    let httpChannel;
+    try {
+      httpChannel = channel.QueryInterface(Ci.nsIHttpChannel);
+    } catch (e) { /* Not an HTTP channel. */ }
+
+    if (httpChannel) {
+      // Continue only if we have a 2xx status code.
+      if (Math.floor(httpChannel.responseStatus / 100) != 2)
+        return false;
+
+      // Cache-Control: no-store.
+      if (httpChannel.isNoStoreResponse())
+        return false;
+
+      // Don't capture HTTPS pages unless the user explicitly enabled it.
+      if (uri.schemeIs("https") && !this._sslDiskCacheEnabled)
+        return false;
+    }
+
+    return true;
+  },
+
+  _clearTimeout: function Thumbnails_clearTimeout(aBrowser) {
+    if (this._timeouts.has(aBrowser)) {
+      aBrowser.removeEventListener("scroll", this, false);
+      clearTimeout(this._timeouts.get(aBrowser));
+      this._timeouts.delete(aBrowser);
+    }
+  }
+};
+//@line 153 "/build/src/mozilla-release/browser/base/content/browser.js"
+
+//@line 4 "/build/src/mozilla-release/browser/base/content/browser-syncui.js"
+
+// gSyncUI handles updating the tools menu
+let gSyncUI = {
+  _obs: ["weave:service:sync:start",
+         "weave:service:sync:delayed",
+         "weave:service:quota:remaining",
+         "weave:service:setup-complete",
+         "weave:service:login:start",
+         "weave:service:login:finish",
+         "weave:service:logout:finish",
+         "weave:service:start-over",
+         "weave:ui:login:error",
+         "weave:ui:sync:error",
+         "weave:ui:sync:finish",
+         "weave:ui:clear-error"],
+
+  _unloaded: false,
+
+  init: function SUI_init() {
+    // Proceed to set up the UI if Sync has already started up.
+    // Otherwise we'll do it when Sync is firing up.
+    if (Weave.Status.ready) {
+      this.initUI();
+      return;
+    }
+
+    Services.obs.addObserver(this, "weave:service:ready", true);
+
+    // Remove the observer if the window is closed before the observer
+    // was triggered.
+    window.addEventListener("unload", function onUnload() {
+      gSyncUI._unloaded = true;
+      window.removeEventListener("unload", onUnload, false);
+      Services.obs.removeObserver(gSyncUI, "weave:service:ready");
+
+      if (Weave.Status.ready) {
+        gSyncUI._obs.forEach(function(topic) {
+          Services.obs.removeObserver(gSyncUI, topic);
+        });
+      }
+    }, false);
+  },
+
+  initUI: function SUI_initUI() {
+    // If this is a browser window?
+    if (gBrowser) {
+      this._obs.push("weave:notification:added");
+    }
+
+    this._obs.forEach(function(topic) {
+      Services.obs.addObserver(this, topic, true);
+    }, this);
+
+    if (gBrowser && Weave.Notifications.notifications.length) {
+      this.initNotifications();
+    }
+    this.updateUI();
+  },
+
+  initNotifications: function SUI_initNotifications() {
+    const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+    let notificationbox = document.createElementNS(XULNS, "notificationbox");
+    notificationbox.id = "sync-notifications";
+    notificationbox.setAttribute("flex", "1");
+
+    let bottombox = document.getElementById("browser-bottombox");
+    bottombox.insertBefore(notificationbox, bottombox.firstChild);
+
+    // Force a style flush to ensure that our binding is attached.
+    notificationbox.clientTop;
+
+    // notificationbox will listen to observers from now on.
+    Services.obs.removeObserver(this, "weave:notification:added");
+  },
+
+  _wasDelayed: false,
+
+  _needsSetup: function SUI__needsSetup() {
+    let firstSync = "";
+    try {
+      firstSync = Services.prefs.getCharPref("services.sync.firstSync");
+    } catch (e) { }
+    return Weave.Status.checkSetup() == Weave.CLIENT_NOT_CONFIGURED ||
+           firstSync == "notReady";
+  },
+
+  updateUI: function SUI_updateUI() {
+    let needsSetup = this._needsSetup();
+    document.getElementById("sync-setup-state").hidden = !needsSetup;
+    document.getElementById("sync-syncnow-state").hidden = needsSetup;
+
+    if (!gBrowser)
+      return;
+
+    let button = document.getElementById("sync-button");
+    if (!button)
+      return;
+
+    button.removeAttribute("status");
+    this._updateLastSyncTime();
+    if (needsSetup)
+      button.removeAttribute("tooltiptext");
+  },
+
+
+  // Functions called by observers
+  onActivityStart: function SUI_onActivityStart() {
+    if (!gBrowser)
+      return;
+
+    let button = document.getElementById("sync-button");
+    if (!button)
+      return;
+
+    button.setAttribute("status", "active");
+  },
+
+  onSyncDelay: function SUI_onSyncDelay() {
+    // basically, we want to just inform users that stuff is going to take a while
+    let title = this._stringBundle.GetStringFromName("error.sync.no_node_found.title");
+    let description = this._stringBundle.GetStringFromName("error.sync.no_node_found");
+    let buttons = [new Weave.NotificationButton(
+      this._stringBundle.GetStringFromName("error.sync.serverStatusButton.label"),
+      this._stringBundle.GetStringFromName("error.sync.serverStatusButton.accesskey"),
+      function() { gSyncUI.openServerStatus(); return true; }
+    )];
+    let notification = new Weave.Notification(
+      title, description, null, Weave.Notifications.PRIORITY_INFO, buttons);
+    Weave.Notifications.replaceTitle(notification);
+    this._wasDelayed = true;
+  },
+
+  onLoginFinish: function SUI_onLoginFinish() {
+    // Clear out any login failure notifications
+    let title = this._stringBundle.GetStringFromName("error.login.title");
+    this.clearError(title);
+  },
+
+  onSetupComplete: function SUI_onSetupComplete() {
+    onLoginFinish();
+  },
+
+  onLoginError: function SUI_onLoginError() {
+    // if login fails, any other notifications are essentially moot
+    Weave.Notifications.removeAll();
+
+    // if we haven't set up the client, don't show errors
+    if (this._needsSetup()) {
+      this.updateUI();
+      return;
+    }
+
+    let title = this._stringBundle.GetStringFromName("error.login.title");
+
+    let description;
+    if (Weave.Status.sync == Weave.PROLONGED_SYNC_FAILURE) {
+      // Convert to days
+      let lastSync =
+        Services.prefs.getIntPref("services.sync.errorhandler.networkFailureReportTimeout") / 86400;
+      description =
+        this._stringBundle.formatStringFromName("error.sync.prolonged_failure", [lastSync], 1);
+    } else {
+      let reason = Weave.Utils.getErrorString(Weave.Status.login);
+      description =
+        this._stringBundle.formatStringFromName("error.sync.description", [reason], 1);
+    }
+
+    let buttons = [];
+    buttons.push(new Weave.NotificationButton(
+      this._stringBundle.GetStringFromName("error.login.prefs.label"),
+      this._stringBundle.GetStringFromName("error.login.prefs.accesskey"),
+      function() { gSyncUI.openPrefs(); return true; }
+    ));
+
+    let notification = new Weave.Notification(title, description, null,
+                                              Weave.Notifications.PRIORITY_WARNING, buttons);
+    Weave.Notifications.replaceTitle(notification);
+    this.updateUI();
+  },
+
+  onLogout: function SUI_onLogout() {
+    this.updateUI();
+  },
+
+  onStartOver: function SUI_onStartOver() {
+    this.clearError();
+  },
+
+  onQuotaNotice: function onQuotaNotice(subject, data) {
+    let title = this._stringBundle.GetStringFromName("warning.sync.quota.label");
+    let description = this._stringBundle.GetStringFromName("warning.sync.quota.description");
+    let buttons = [];
+    buttons.push(new Weave.NotificationButton(
+      this._stringBundle.GetStringFromName("error.sync.viewQuotaButton.label"),
+      this._stringBundle.GetStringFromName("error.sync.viewQuotaButton.accesskey"),
+      function() { gSyncUI.openQuotaDialog(); return true; }
+    ));
+
+    let notification = new Weave.Notification(
+      title, description, null, Weave.Notifications.PRIORITY_WARNING, buttons);
+    Weave.Notifications.replaceTitle(notification);
+  },
+
+  openServerStatus: function () {
+    let statusURL = Services.prefs.getCharPref("services.sync.statusURL");
+    window.openUILinkIn(statusURL, "tab");
+  },
+
+  // Commands
+  doSync: function SUI_doSync() {
+    setTimeout(function() Weave.ErrorHandler.syncAndReportErrors(), 0);
+  },
+
+  handleToolbarButton: function SUI_handleStatusbarButton() {
+    if (this._needsSetup())
+      this.openSetup();
+    else
+      this.doSync();
+  },
+
+  //XXXzpao should be part of syncCommon.js - which we might want to make a module...
+  //        To be fixed in a followup (bug 583366)
+
+  /**
+   * Invoke the Sync setup wizard.
+   *
+   * @param wizardType
+   *        Indicates type of wizard to launch:
+   *          null    -- regular set up wizard
+   *          "pair"  -- pair a device first
+   *          "reset" -- reset sync
+   */
+
+  openSetup: function SUI_openSetup(wizardType) {
+    let win = Services.wm.getMostRecentWindow("Weave:AccountSetup");
+    if (win)
+      win.focus();
+    else {
+      window.openDialog("chrome://browser/content/sync/setup.xul",
+                        "weaveSetup", "centerscreen,chrome,resizable=no",
+                        wizardType);
+    }
+  },
+
+  openAddDevice: function () {
+    if (!Weave.Utils.ensureMPUnlocked())
+      return;
+
+    let win = Services.wm.getMostRecentWindow("Sync:AddDevice");
+    if (win)
+      win.focus();
+    else
+      window.openDialog("chrome://browser/content/sync/addDevice.xul",
+                        "syncAddDevice", "centerscreen,chrome,resizable=no");
+  },
+
+  openQuotaDialog: function SUI_openQuotaDialog() {
+    let win = Services.wm.getMostRecentWindow("Sync:ViewQuota");
+    if (win)
+      win.focus();
+    else
+      Services.ww.activeWindow.openDialog(
+        "chrome://browser/content/sync/quota.xul", "",
+        "centerscreen,chrome,dialog,modal");
+  },
+
+  openPrefs: function SUI_openPrefs() {
+    openPreferences("paneSync");
+  },
+
+
+  // Helpers
+  _updateLastSyncTime: function SUI__updateLastSyncTime() {
+    if (!gBrowser)
+      return;
+
+    let syncButton = document.getElementById("sync-button");
+    if (!syncButton)
+      return;
+
+    let lastSync;
+    try {
+      lastSync = Services.prefs.getCharPref("services.sync.lastSync");
+    }
+    catch (e) { };
+    if (!lastSync || this._needsSetup()) {
+      syncButton.removeAttribute("tooltiptext");
+      return;
+    }
+
+    // Show the day-of-week and time (HH:MM) of last sync
+    let lastSyncDate = new Date(lastSync).toLocaleFormat("%a %H:%M");
+    let lastSyncLabel =
+      this._stringBundle.formatStringFromName("lastSync2.label", [lastSyncDate], 1);
+
+    syncButton.setAttribute("tooltiptext", lastSyncLabel);
+  },
+
+  clearError: function SUI_clearError(errorString) {
+    Weave.Notifications.removeAll(errorString);
+    this.updateUI();
+  },
+
+  onSyncFinish: function SUI_onSyncFinish() {
+    let title = this._stringBundle.GetStringFromName("error.sync.title");
+
+    // Clear out sync failures on a successful sync
+    this.clearError(title);
+
+    if (this._wasDelayed && Weave.Status.sync != Weave.NO_SYNC_NODE_FOUND) {
+      title = this._stringBundle.GetStringFromName("error.sync.no_node_found.title");
+      this.clearError(title);
+      this._wasDelayed = false;
+    }
+  },
+
+  onSyncError: function SUI_onSyncError() {
+    let title = this._stringBundle.GetStringFromName("error.sync.title");
+
+    if (Weave.Status.login != Weave.LOGIN_SUCCEEDED) {
+      this.onLoginError();
+      return;
+    }
+
+    let description;
+    if (Weave.Status.sync == Weave.PROLONGED_SYNC_FAILURE) {
+      // Convert to days
+      let lastSync =
+        Services.prefs.getIntPref("services.sync.errorhandler.networkFailureReportTimeout") / 86400;
+      description =
+        this._stringBundle.formatStringFromName("error.sync.prolonged_failure", [lastSync], 1);
+    } else {
+      let error = Weave.Utils.getErrorString(Weave.Status.sync);
+      description =
+        this._stringBundle.formatStringFromName("error.sync.description", [error], 1);
+    }
+    let priority = Weave.Notifications.PRIORITY_WARNING;
+    let buttons = [];
+
+    // Check if the client is outdated in some way
+    let outdated = Weave.Status.sync == Weave.VERSION_OUT_OF_DATE;
+    for (let [engine, reason] in Iterator(Weave.Status.engines))
+      outdated = outdated || reason == Weave.VERSION_OUT_OF_DATE;
+
+    if (outdated) {
+      description = this._stringBundle.GetStringFromName(
+        "error.sync.needUpdate.description");
+      buttons.push(new Weave.NotificationButton(
+        this._stringBundle.GetStringFromName("error.sync.needUpdate.label"),
+        this._stringBundle.GetStringFromName("error.sync.needUpdate.accesskey"),
+        function() { window.openUILinkIn("https://services.mozilla.com/update/", "tab"); return true; }
+      ));
+    }
+    else if (Weave.Status.sync == Weave.OVER_QUOTA) {
+      description = this._stringBundle.GetStringFromName(
+        "error.sync.quota.description");
+      buttons.push(new Weave.NotificationButton(
+        this._stringBundle.GetStringFromName(
+          "error.sync.viewQuotaButton.label"),
+        this._stringBundle.GetStringFromName(
+          "error.sync.viewQuotaButton.accesskey"),
+        function() { gSyncUI.openQuotaDialog(); return true; } )
+      );
+    }
+    else if (Weave.Status.enforceBackoff) {
+      priority = Weave.Notifications.PRIORITY_INFO;
+      buttons.push(new Weave.NotificationButton(
+        this._stringBundle.GetStringFromName("error.sync.serverStatusButton.label"),
+        this._stringBundle.GetStringFromName("error.sync.serverStatusButton.accesskey"),
+        function() { gSyncUI.openServerStatus(); return true; }
+      ));
+    }
+    else {
+      priority = Weave.Notifications.PRIORITY_INFO;
+      buttons.push(new Weave.NotificationButton(
+        this._stringBundle.GetStringFromName("error.sync.tryAgainButton.label"),
+        this._stringBundle.GetStringFromName("error.sync.tryAgainButton.accesskey"),
+        function() { gSyncUI.doSync(); return true; }
+      ));
+    }
+
+    let notification =
+      new Weave.Notification(title, description, null, priority, buttons);
+    Weave.Notifications.replaceTitle(notification);
+
+    if (this._wasDelayed && Weave.Status.sync != Weave.NO_SYNC_NODE_FOUND) {
+      title = this._stringBundle.GetStringFromName("error.sync.no_node_found.title");
+      Weave.Notifications.removeAll(title);
+      this._wasDelayed = false;
+    }
+
+    this.updateUI();
+  },
+
+  observe: function SUI_observe(subject, topic, data) {
+    if (this._unloaded) {
+      Cu.reportError("SyncUI observer called after unload: " + topic);
+      return;
+    }
+
+    switch (topic) {
+      case "weave:service:sync:start":
+        this.onActivityStart();
+        break;
+      case "weave:ui:sync:finish":
+        this.onSyncFinish();
+        break;
+      case "weave:ui:sync:error":
+        this.onSyncError();
+        break;
+      case "weave:service:sync:delayed":
+        this.onSyncDelay();
+        break;
+      case "weave:service:quota:remaining":
+        this.onQuotaNotice();
+        break;
+      case "weave:service:setup-complete":
+        this.onSetupComplete();
+        break;
+      case "weave:service:login:start":
+        this.onActivityStart();
+        break;
+      case "weave:service:login:finish":
+        this.onLoginFinish();
+        break;
+      case "weave:ui:login:error":
+        this.onLoginError();
+        break;
+      case "weave:service:logout:finish":
+        this.onLogout();
+        break;
+      case "weave:service:start-over":
+        this.onStartOver();
+        break;
+      case "weave:service:ready":
+        this.initUI();
+        break;
+      case "weave:notification:added":
+        this.initNotifications();
+        break;
+      case "weave:ui:clear-error":
+        this.clearError();
+        break;
+    }
+  },
+
+  QueryInterface: XPCOMUtils.generateQI([
+    Ci.nsIObserver,
+    Ci.nsISupportsWeakReference
+  ])
+};
+
+XPCOMUtils.defineLazyGetter(gSyncUI, "_stringBundle", function() {
+  //XXXzpao these strings should probably be moved from /services to /browser... (bug 583381)
+  //        but for now just make it work
+  return Cc["@mozilla.org/intl/stringbundle;1"].
+         getService(Ci.nsIStringBundleService).
+         createBundle("chrome://weave/locale/services/sync.properties");
+});
+
+//@line 157 "/build/src/mozilla-release/browser/base/content/browser.js"
+
+XPCOMUtils.defineLazyGetter(this, "Win7Features", function () {
+//@line 176 "/build/src/mozilla-release/browser/base/content/browser.js"
+  return null;
+});
+
+//@line 184 "/build/src/mozilla-release/browser/base/content/browser.js"
+
+XPCOMUtils.defineLazyGetter(this, "PageMenu", function() {
+  let tmp = {};
+  Cu.import("resource://gre/modules/PageMenu.jsm", tmp);
+  return new tmp.PageMenu();
+});
+
+/**
+* We can avoid adding multiple load event listeners and save some time by adding
+* one listener that calls all real handlers.
+*/
+function pageShowEventHandlers(event) {
+  // Filter out events that are not about the document load we are interested in
+  if (event.originalTarget == content.document) {
+    charsetLoadListener(event);
+    XULBrowserWindow.asyncUpdateUI();
+
+    // The PluginClickToPlay events are not fired when navigating using the
+    // BF cache. |event.persisted| is true when the page is loaded from the
+    // BF cache, so this code reshows the notification if necessary.
+    if (event.persisted)
+      gPluginHandler.reshowClickToPlayNotification();
+  }
+}
+
+function UpdateBackForwardCommands(aWebNavigation) {
+  var backBroadcaster = document.getElementById("Browser:Back");
+  var forwardBroadcaster = document.getElementById("Browser:Forward");
+
+  // Avoid setting attributes on broadcasters if the value hasn't changed!
+  // Remember, guys, setting attributes on elements is expensive!  They
+  // get inherited into anonymous content, broadcast to other widgets, etc.!
+  // Don't do it if the value hasn't changed! - dwh
+
+  var backDisabled = backBroadcaster.hasAttribute("disabled");
+  var forwardDisabled = forwardBroadcaster.hasAttribute("disabled");
+  if (backDisabled == aWebNavigation.canGoBack) {
+    if (backDisabled)
+      backBroadcaster.removeAttribute("disabled");
+    else
+      backBroadcaster.setAttribute("disabled", true);
+  }
+
+  if (forwardDisabled == aWebNavigation.canGoForward) {
+    if (forwardDisabled)
+      forwardBroadcaster.removeAttribute("disabled");
+    else
+      forwardBroadcaster.setAttribute("disabled", true);
+  }
+}
+
+/**
+ * Click-and-Hold implementation for the Back and Forward buttons
+ * XXXmano: should this live in toolbarbutton.xml?
+ */
+function SetClickAndHoldHandlers() {
+  var timer;
+
+  function openMenu(aButton) {
+    cancelHold(aButton);
+    aButton.firstChild.hidden = false;
+    aButton.open = true;
+  }
+
+  function mousedownHandler(aEvent) {
+    if (aEvent.button != 0 ||
+        aEvent.currentTarget.open ||
+        aEvent.currentTarget.disabled)
+      return;
+
+    // Prevent the menupopup from opening immediately
+    aEvent.currentTarget.firstChild.hidden = true;
+
+    aEvent.currentTarget.addEventListener("mouseout", mouseoutHandler, false);
+    aEvent.currentTarget.addEventListener("mouseup", mouseupHandler, false);
+    timer = setTimeout(openMenu, 500, aEvent.currentTarget);
+  }
+
+  function mouseoutHandler(aEvent) {
+    let buttonRect = aEvent.currentTarget.getBoundingClientRect();
+    if (aEvent.clientX >= buttonRect.left &&
+        aEvent.clientX <= buttonRect.right &&
+        aEvent.clientY >= buttonRect.bottom)
+      openMenu(aEvent.currentTarget);
+    else
+      cancelHold(aEvent.currentTarget);
+  }
+
+  function mouseupHandler(aEvent) {
+    cancelHold(aEvent.currentTarget);
+  }
+
+  function cancelHold(aButton) {
+    clearTimeout(timer);
+    aButton.removeEventListener("mouseout", mouseoutHandler, false);
+    aButton.removeEventListener("mouseup", mouseupHandler, false);
+  }
+
+  function clickHandler(aEvent) {
+    if (aEvent.button == 0 &&
+        aEvent.target == aEvent.currentTarget &&
+        !aEvent.currentTarget.open &&
+        !aEvent.currentTarget.disabled) {
+      let cmdEvent = document.createEvent("xulcommandevent");
+      cmdEvent.initCommandEvent("command", true, true, window, 0,
+                                aEvent.ctrlKey, aEvent.altKey, aEvent.shiftKey,
+                                aEvent.metaKey, null);
+      aEvent.currentTarget.dispatchEvent(cmdEvent);
+    }
+  }
+
+  function _addClickAndHoldListenersOnElement(aElm) {
+    aElm.addEventListener("mousedown", mousedownHandler, true);
+    aElm.addEventListener("click", clickHandler, true);
+  }
+
+  // Bug 414797: Clone unified-back-forward-button's context menu into both the
+  // back and the forward buttons.
+  var unifiedButton = document.getElementById("unified-back-forward-button");
+  if (unifiedButton && !unifiedButton._clickHandlersAttached) {
+    unifiedButton._clickHandlersAttached = true;
+
+    let popup = document.getElementById("backForwardMenu").cloneNode(true);
+    popup.removeAttribute("id");
+    // Prevent the context attribute on unified-back-forward-button from being
+    // inherited.
+    popup.setAttribute("context", "");
+
+    let backButton = document.getElementById("back-button");
+    backButton.setAttribute("type", "menu");
+    backButton.appendChild(popup);
+    _addClickAndHoldListenersOnElement(backButton);
+
+    let forwardButton = document.getElementById("forward-button");
+    popup = popup.cloneNode(true);
+    forwardButton.setAttribute("type", "menu");
+    forwardButton.appendChild(popup);
+    _addClickAndHoldListenersOnElement(forwardButton);
+  }
+}
+
+const gSessionHistoryObserver = {
+  observe: function(subject, topic, data)
+  {
+    if (topic != "browser:purge-session-history")
+      return;
+
+    var backCommand = document.getElementById("Browser:Back");
+    backCommand.setAttribute("disabled", "true");
+    var fwdCommand = document.getElementById("Browser:Forward");
+    fwdCommand.setAttribute("disabled", "true");
+
+    // Hide session restore button on about:home
+    window.messageManager.sendAsyncMessage("Browser:HideSessionRestoreButton");
+
+    if (gURLBar) {
+      // Clear undo history of the URL bar
+      gURLBar.editor.transactionManager.clear()
+    }
+  }
+};
+
+/**
+ * Given a starting docshell and a URI to look up, find the docshell the URI
+ * is loaded in.
+ * @param   aDocument
+ *          A document to find instead of using just a URI - this is more specific.
+ * @param   aDocShell
+ *          The doc shell to start at
+ * @param   aSoughtURI
+ *          The URI that we're looking for
+ * @returns The doc shell that the sought URI is loaded in. Can be in
+ *          subframes.
+ */
+function findChildShell(aDocument, aDocShell, aSoughtURI) {
+  aDocShell.QueryInterface(Components.interfaces.nsIWebNavigation);
+  aDocShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
+  var doc = aDocShell.getInterface(Components.interfaces.nsIDOMDocument);
+  if ((aDocument && doc == aDocument) ||
+      (aSoughtURI && aSoughtURI.spec == aDocShell.currentURI.spec))
+    return aDocShell;
+
+  var node = aDocShell.QueryInterface(Components.interfaces.nsIDocShellTreeNode);
+  for (var i = 0; i < node.childCount; ++i) {
+    var docShell = node.getChildAt(i);
+    docShell = findChildShell(aDocument, docShell, aSoughtURI);
+    if (docShell)
+      return docShell;
+  }
+  return null;
+}
+
+var gPopupBlockerObserver = {
+  _reportButton: null,
+
+  onReportButtonClick: function (aEvent)
+  {
+    if (aEvent.button != 0 || aEvent.target != this._reportButton)
+      return;
+
+    document.getElementById("blockedPopupOptions")
+            .openPopup(this._reportButton, "after_end", 0, 2, false, false, aEvent);
+  },
+
+  handleEvent: function (aEvent)
+  {
+    if (aEvent.originalTarget != gBrowser.selectedBrowser)
+      return;
+
+    if (!this._reportButton && gURLBar)
+      this._reportButton = document.getElementById("page-report-button");
+
+    if (!gBrowser.pageReport) {
+      // Hide the icon in the location bar (if the location bar exists)
+      if (gURLBar)
+        this._reportButton.hidden = true;
+      return;
+    }
+
+    if (gURLBar)
+      this._reportButton.hidden = false;
+
+    // Only show the notification again if we've not already shown it. Since
+    // notifications are per-browser, we don't need to worry about re-adding
+    // it.
+    if (!gBrowser.pageReport.reported) {
+      if (gPrefService.getBoolPref("privacy.popups.showBrowserMessage")) {
+        var brandBundle = document.getElementById("bundle_brand");
+        var brandShortName = brandBundle.getString("brandShortName");
+        var message;
+        var popupCount = gBrowser.pageReport.length;
+//@line 419 "/build/src/mozilla-release/browser/base/content/browser.js"
+        var popupButtonText = gNavigatorBundle.getString("popupWarningButtonUnix");
+        var popupButtonAccesskey = gNavigatorBundle.getString("popupWarningButtonUnix.accesskey");
+//@line 422 "/build/src/mozilla-release/browser/base/content/browser.js"
+        if (popupCount > 1)
+          message = gNavigatorBundle.getFormattedString("popupWarningMultiple", [brandShortName, popupCount]);
+        else
+          message = gNavigatorBundle.getFormattedString("popupWarning", [brandShortName]);
+
+        var notificationBox = gBrowser.getNotificationBox();
+        var notification = notificationBox.getNotificationWithValue("popup-blocked");
+        if (notification) {
+          notification.label = message;
+        }
+        else {
+          var buttons = [{
+            label: popupButtonText,
+            accessKey: popupButtonAccesskey,
+            popup: "blockedPopupOptions",
+            callback: null
+          }];
+
+          const priority = notificationBox.PRIORITY_WARNING_MEDIUM;
+          notificationBox.appendNotification(message, "popup-blocked",
+                                             "chrome://browser/skin/Info.png",
+                                             priority, buttons);
+        }
+      }
+
+      // Record the fact that we've reported this blocked popup, so we don't
+      // show it again.
+      gBrowser.pageReport.reported = true;
+    }
+  },
+
+  toggleAllowPopupsForSite: function (aEvent)
+  {
+    var pm = Services.perms;
+    var shouldBlock = aEvent.target.getAttribute("block") == "true";
+    var perm = shouldBlock ? pm.DENY_ACTION : pm.ALLOW_ACTION;
+    pm.add(gBrowser.currentURI, "popup", perm);
+
+    gBrowser.getNotificationBox().removeCurrentNotification();
+  },
+
+  fillPopupList: function (aEvent)
+  {
+    // XXXben - rather than using |currentURI| here, which breaks down on multi-framed sites
+    //          we should really walk the pageReport and create a list of "allow for <host>"
+    //          menuitems for the common subset of hosts present in the report, this will
+    //          make us frame-safe.
+    //
+    // XXXjst - Note that when this is fixed to work with multi-framed sites,
+    //          also back out the fix for bug 343772 where
+    //          nsGlobalWindow::CheckOpenAllow() was changed to also
+    //          check if the top window's location is whitelisted.
+    var uri = gBrowser.currentURI;
+    var blockedPopupAllowSite = document.getElementById("blockedPopupAllowSite");
+    try {
+      blockedPopupAllowSite.removeAttribute("hidden");
+
+      var pm = Services.perms;
+      if (pm.testPermission(uri, "popup") == pm.ALLOW_ACTION) {
+        // Offer an item to block popups for this site, if a whitelist entry exists
+        // already for it.
+        let blockString = gNavigatorBundle.getFormattedString("popupBlock", [uri.host]);
+        blockedPopupAllowSite.setAttribute("label", blockString);
+        blockedPopupAllowSite.setAttribute("block", "true");
+      }
+      else {
+        // Offer an item to allow popups for this site
+        let allowString = gNavigatorBundle.getFormattedString("popupAllow", [uri.host]);
+        blockedPopupAllowSite.setAttribute("label", allowString);
+        blockedPopupAllowSite.removeAttribute("block");
+      }
+    }
+    catch (e) {
+      blockedPopupAllowSite.setAttribute("hidden", "true");
+    }
+
+    if (gPrivateBrowsingUI.privateBrowsingEnabled)
+      blockedPopupAllowSite.setAttribute("disabled", "true");
+    else
+      blockedPopupAllowSite.removeAttribute("disabled");
+
+    var foundUsablePopupURI = false;
+    var pageReport = gBrowser.pageReport;
+    if (pageReport) {
+      for (var i = 0; i < pageReport.length; ++i) {
+        // popupWindowURI will be null if the file picker popup is blocked.
+        // xxxdz this should make the option say "Show file picker" and do it (Bug 590306)
+        if (!pageReport[i].popupWindowURI)
+          continue;
+        var popupURIspec = pageReport[i].popupWindowURI.spec;
+
+        // Sometimes the popup URI that we get back from the pageReport
+        // isn't useful (for instance, netscape.com's popup URI ends up
+        // being "http://www.netscape.com", which isn't really the URI of
+        // the popup they're trying to show).  This isn't going to be
+        // useful to the user, so we won't create a menu item for it.
+        if (popupURIspec == "" || popupURIspec == "about:blank" ||
+            popupURIspec == uri.spec)
+          continue;
+
+        // Because of the short-circuit above, we may end up in a situation
+        // in which we don't have any usable popup addresses to show in
+        // the menu, and therefore we shouldn't show the separator.  However,
+        // since we got past the short-circuit, we must've found at least
+        // one usable popup URI and thus we'll turn on the separator later.
+        foundUsablePopupURI = true;
+
+        var menuitem = document.createElement("menuitem");
+        var label = gNavigatorBundle.getFormattedString("popupShowPopupPrefix",
+                                                        [popupURIspec]);
+        menuitem.setAttribute("label", label);
+        menuitem.setAttribute("popupWindowURI", popupURIspec);
+        menuitem.setAttribute("popupWindowFeatures", pageReport[i].popupWindowFeatures);
+        menuitem.setAttribute("popupWindowName", pageReport[i].popupWindowName);
+        menuitem.setAttribute("oncommand", "gPopupBlockerObserver.showBlockedPopup(event);");
+        menuitem.requestingWindow = pageReport[i].requestingWindow;
+        menuitem.requestingDocument = pageReport[i].requestingDocument;
+        aEvent.target.appendChild(menuitem);
+      }
+    }
+
+    // Show or hide the separator, depending on whether we added any
+    // showable popup addresses to the menu.
+    var blockedPopupsSeparator =
+      document.getElementById("blockedPopupsSeparator");
+    if (foundUsablePopupURI)
+      blockedPopupsSeparator.removeAttribute("hidden");
+    else
+      blockedPopupsSeparator.setAttribute("hidden", true);
+
+    var blockedPopupDontShowMessage = document.getElementById("blockedPopupDontShowMessage");
+    var showMessage = gPrefService.getBoolPref("privacy.popups.showBrowserMessage");
+    blockedPopupDontShowMessage.setAttribute("checked", !showMessage);
+    if (aEvent.target.anchorNode.id == "page-report-button") {
+      aEvent.target.anchorNode.setAttribute("open", "true");
+      blockedPopupDontShowMessage.setAttribute("label", gNavigatorBundle.getString("popupWarningDontShowFromLocationbar"));
+    } else
+      blockedPopupDontShowMessage.setAttribute("label", gNavigatorBundle.getString("popupWarningDontShowFromMessage"));
+  },
+
+  onPopupHiding: function (aEvent) {
+    if (aEvent.target.anchorNode.id == "page-report-button")
+      aEvent.target.anchorNode.removeAttribute("open");
+
+    let item = aEvent.target.lastChild;
+    while (item && item.getAttribute("observes") != "blockedPopupsSeparator") {
+      let next = item.previousSibling;
+      item.parentNode.removeChild(item);
+      item = next;
+    }
+  },
+
+  showBlockedPopup: function (aEvent)
+  {
+    var target = aEvent.target;
+    var popupWindowURI = target.getAttribute("popupWindowURI");
+    var features = target.getAttribute("popupWindowFeatures");
+    var name = target.getAttribute("popupWindowName");
+
+    var dwi = target.requestingWindow;
+
+    // If we have a requesting window and the requesting document is
+    // still the current document, open the popup.
+    if (dwi && dwi.document == target.requestingDocument) {
+      dwi.open(popupWindowURI, name, features);
+    }
+  },
+
+  editPopupSettings: function ()
+  {
+    var host = "";
+    try {
+      host = gBrowser.currentURI.host;
+    }
+    catch (e) { }
+
+    var bundlePreferences = document.getElementById("bundle_preferences");
+    var params = { blockVisible   : false,
+                   sessionVisible : false,
+                   allowVisible   : true,
+                   prefilledHost  : host,
+                   permissionType : "popup",
+                   windowTitle    : bundlePreferences.getString("popuppermissionstitle"),
+                   introText      : bundlePreferences.getString("popuppermissionstext") };
+    var existingWindow = Services.wm.getMostRecentWindow("Browser:Permissions");
+    if (existingWindow) {
+      existingWindow.initWithParams(params);
+      existingWindow.focus();
+    }
+    else
+      window.openDialog("chrome://browser/content/preferences/permissions.xul",
+                        "_blank", "resizable,dialog=no,centerscreen", params);
+  },
+
+  dontShowMessage: function ()
+  {
+    var showMessage = gPrefService.getBoolPref("privacy.popups.showBrowserMessage");
+    gPrefService.setBoolPref("privacy.popups.showBrowserMessage", !showMessage);
+    gBrowser.getNotificationBox().removeCurrentNotification();
+  }
+};
+
+const gFormSubmitObserver = {
+  QueryInterface : XPCOMUtils.generateQI([Ci.nsIFormSubmitObserver]),
+
+  panel: null,
+
+  init: function()
+  {
+    this.panel = document.getElementById('invalid-form-popup');
+  },
+
+  panelIsOpen: function()
+  {
+    return this.panel && this.panel.state != "hiding" &&
+           this.panel.state != "closed";
+  },
+
+  notifyInvalidSubmit : function (aFormElement, aInvalidElements)
+  {
+    // We are going to handle invalid form submission attempt by focusing the
+    // first invalid element and show the corresponding validation message in a
+    // panel attached to the element.
+    if (!aInvalidElements.length) {
+      return;
+    }
+
+    // Don't show the popup if the current tab doesn't contain the invalid form.
+    if (gBrowser.contentDocument !=
+        aFormElement.ownerDocument.defaultView.top.document) {
+      return;
+    }
+
+    let element = aInvalidElements.queryElementAt(0, Ci.nsISupports);
+
+    if (!(element instanceof HTMLInputElement ||
+          element instanceof HTMLTextAreaElement ||
+          element instanceof HTMLSelectElement ||
+          element instanceof HTMLButtonElement)) {
+      return;
+    }
+
+    this.panel.firstChild.textContent = element.validationMessage;
+
+    element.focus();
+
+    // If the user interacts with the element and makes it valid or leaves it,
+    // we want to remove the popup.
+    // We could check for clicks but a click is already removing the popup.
+    function blurHandler() {
+      gFormSubmitObserver.panel.hidePopup();
+    };
+    function inputHandler(e) {
+      if (e.originalTarget.validity.valid) {
+        gFormSubmitObserver.panel.hidePopup();
+      } else {
+        // If the element is now invalid for a new reason, we should update the
+        // error message.
+        if (gFormSubmitObserver.panel.firstChild.textContent !=
+            e.originalTarget.validationMessage) {
+          gFormSubmitObserver.panel.firstChild.textContent =
+            e.originalTarget.validationMessage;
+        }
+      }
+    };
+    element.addEventListener("input", inputHandler, false);
+    element.addEventListener("blur", blurHandler, false);
+
+    // One event to bring them all and in the darkness bind them.
+    this.panel.addEventListener("popuphiding", function onPopupHiding(aEvent) {
+      aEvent.target.removeEventListener("popuphiding", onPopupHiding, false);
+      element.removeEventListener("input", inputHandler, false);
+      element.removeEventListener("blur", blurHandler, false);
+    }, false);
+
+    this.panel.hidden = false;
+
+    // We want to show the popup at the middle of checkbox and radio buttons
+    // and where the content begin for the other elements.
+    let offset = 0;
+    let position = "";
+
+    if (element.tagName == 'INPUT' &&
+        (element.type == 'radio' || element.type == 'checkbox')) {
+      position = "bottomcenter topleft";
+    } else {
+      let win = element.ownerDocument.defaultView;
+      let style = win.getComputedStyle(element, null);
+      let utils = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+                     .getInterface(Components.interfaces.nsIDOMWindowUtils);
+
+      if (style.direction == 'rtl') {
+        offset = parseInt(style.paddingRight) + parseInt(style.borderRightWidth);
+      } else {
+        offset = parseInt(style.paddingLeft) + parseInt(style.borderLeftWidth);
+      }
+
+      offset = Math.round(offset * utils.screenPixelsPerCSSPixel);
+
+      position = "after_start";
+    }
+
+    this.panel.openPopup(element, position, offset, 0);
+  }
+};
+
+// Simple gestures support
+//
+// As per bug #412486, web content must not be allowed to receive any
+// simple gesture events.  Multi-touch gesture APIs are in their
+// infancy and we do NOT want to be forced into supporting an API that
+// will probably have to change in the future.  (The current Mac OS X
+// API is undocumented and was reverse-engineered.)  Until support is
+// implemented in the event dispatcher to keep these events as
+// chrome-only, we must listen for the simple gesture events during
+// the capturing phase and call stopPropagation on every event.
+
+let gGestureSupport = {
+  /**
+   * Add or remove mouse gesture event listeners
+   *
+   * @param aAddListener
+   *        True to add/init listeners and false to remove/uninit
+   */
+  init: function GS_init(aAddListener) {
+    const gestureEvents = ["SwipeGesture",
+      "MagnifyGestureStart", "MagnifyGestureUpdate", "MagnifyGesture",
+      "RotateGestureStart", "RotateGestureUpdate", "RotateGesture",
+      "TapGesture", "PressTapGesture"];
+
+    let addRemove = aAddListener ? window.addEventListener :
+      window.removeEventListener;
+
+    gestureEvents.forEach(function (event) addRemove("Moz" + event, this, true),
+                          this);
+  },
+
+  /**
+   * Dispatch events based on the type of mouse gesture event. For now, make
+   * sure to stop propagation of every gesture event so that web content cannot
+   * receive gesture events.
+   *
+   * @param aEvent
+   *        The gesture event to handle
+   */
+  handleEvent: function GS_handleEvent(aEvent) {
+    aEvent.stopPropagation();
+
+    // Create a preference object with some defaults
+    let def = function(aThreshold, aLatched)
+      ({ threshold: aThreshold, latched: !!aLatched });
+
+    switch (aEvent.type) {
+      case "MozSwipeGesture":
+        aEvent.preventDefault();
+        this.onSwipe(aEvent);
+        break;
+      case "MozMagnifyGestureStart":
+        aEvent.preventDefault();
+//@line 784 "/build/src/mozilla-release/browser/base/content/browser.js"
+        this._setupGesture(aEvent, "pinch", def(150, 1), "out", "in");
+//@line 786 "/build/src/mozilla-release/browser/base/content/browser.js"
+        break;
+      case "MozRotateGestureStart":
+        aEvent.preventDefault();
+        this._setupGesture(aEvent, "twist", def(25, 0), "right", "left");
+        break;
+      case "MozMagnifyGestureUpdate":
+      case "MozRotateGestureUpdate":
+        aEvent.preventDefault();
+        this._doUpdate(aEvent);
+        break;
+      case "MozTapGesture":
+        aEvent.preventDefault();
+        this._doAction(aEvent, ["tap"]);
+        break;
+      /* case "MozPressTapGesture":
+        break; */
+    }
+  },
+
+  /**
+   * Called at the start of "pinch" and "twist" gestures to setup all of the
+   * information needed to process the gesture
+   *
+   * @param aEvent
+   *        The continual motion start event to handle
+   * @param aGesture
+   *        Name of the gesture to handle
+   * @param aPref
+   *        Preference object with the names of preferences and defaults
+   * @param aInc
+   *        Command to trigger for increasing motion (without gesture name)
+   * @param aDec
+   *        Command to trigger for decreasing motion (without gesture name)
+   */
+  _setupGesture: function GS__setupGesture(aEvent, aGesture, aPref, aInc, aDec) {
+    // Try to load user-set values from preferences
+    for (let [pref, def] in Iterator(aPref))
+      aPref[pref] = this._getPref(aGesture + "." + pref, def);
+
+    // Keep track of the total deltas and latching behavior
+    let offset = 0;
+    let latchDir = aEvent.delta > 0 ? 1 : -1;
+    let isLatched = false;
+
+    // Create the update function here to capture closure state
+    this._doUpdate = function GS__doUpdate(aEvent) {
+      // Update the offset with new event data
+      offset += aEvent.delta;
+
+      // Check if the cumulative deltas exceed the threshold
+      if (Math.abs(offset) > aPref["threshold"]) {
+        // Trigger the action if we don't care about latching; otherwise, make
+        // sure either we're not latched and going the same direction of the
+        // initial motion; or we're latched and going the opposite way
+        let sameDir = (latchDir ^ offset) >= 0;
+        if (!aPref["latched"] || (isLatched ^ sameDir)) {
+          this._doAction(aEvent, [aGesture, offset > 0 ? aInc : aDec]);
+
+          // We must be getting latched or leaving it, so just toggle
+          isLatched = !isLatched;
+        }
+
+        // Reset motion counter to prepare for more of the same gesture
+        offset = 0;
+      }
+    };
+
+    // The start event also contains deltas, so handle an update right away
+    this._doUpdate(aEvent);
+  },
+
+  /**
+   * Generator producing the powerset of the input array where the first result
+   * is the complete set and the last result (before StopIteration) is empty.
+   *
+   * @param aArray
+   *        Source array containing any number of elements
+   * @yield Array that is a subset of the input array from full set to empty
+   */
+  _power: function GS__power(aArray) {
+    // Create a bitmask based on the length of the array
+    let num = 1 << aArray.length;
+    while (--num >= 0) {
+      // Only select array elements where the current bit is set
+      yield aArray.reduce(function (aPrev, aCurr, aIndex) {
+        if (num & 1 << aIndex)
+          aPrev.push(aCurr);
+        return aPrev;
+      }, []);
+    }
+  },
+
+  /**
+   * Determine what action to do for the gesture based on which keys are
+   * pressed and which commands are set
+   *
+   * @param aEvent
+   *        The original gesture event to convert into a fake click event
+   * @param aGesture
+   *        Array of gesture name parts (to be joined by periods)
+   */
+  _doAction: function GS__doAction(aEvent, aGesture) {
+    // Create an array of pressed keys in a fixed order so that a command for
+    // "meta" is preferred over "ctrl" when both buttons are pressed (and a
+    // command for both don't exist)
+    let keyCombos = [];
+    ["shift", "alt", "ctrl", "meta"].forEach(function (key) {
+      if (aEvent[key + "Key"])
+        keyCombos.push(key);
+    });
+
+    // Try each combination of key presses in decreasing order for commands
+    for each (let subCombo in this._power(keyCombos)) {
+      // Convert a gesture and pressed keys into the corresponding command
+      // action where the preference has the gesture before "shift" before
+      // "alt" before "ctrl" before "meta" all separated by periods
+      let command;
+      try {
+        command = this._getPref(aGesture.concat(subCombo).join("."));
+      } catch (e) {}
+
+      if (!command)
+        continue;
+
+      let node = document.getElementById(command);
+      if (node) {
+        if (node.getAttribute("disabled") != "true") {
+          let cmdEvent = document.createEvent("xulcommandevent");
+          cmdEvent.initCommandEvent("command", true, true, window, 0,
+                                    aEvent.ctrlKey, aEvent.altKey, aEvent.shiftKey,
+                                    aEvent.metaKey, null);
+          node.dispatchEvent(cmdEvent);
+        }
+      } else {
+        goDoCommand(command);
+      }
+
+      break;
+    }
+  },
+
+  /**
+   * Convert continual motion events into an action if it exceeds a threshold
+   * in a given direction. This function will be set by _setupGesture to
+   * capture state that needs to be shared across multiple gesture updates.
+   *
+   * @param aEvent
+   *        The continual motion update event to handle
+   */
+  _doUpdate: function(aEvent) {},
+
+  /**
+   * Convert the swipe gesture into a browser action based on the direction
+   *
+   * @param aEvent
+   *        The swipe event to handle
+   */
+  onSwipe: function GS_onSwipe(aEvent) {
+    // Figure out which one (and only one) direction was triggered
+    for (let dir of ["UP", "RIGHT", "DOWN", "LEFT"]) {
+      if (aEvent.direction == aEvent["DIRECTION_" + dir]) {
+        this._doAction(aEvent, ["swipe", dir.toLowerCase()]);
+        break;
+      }
+    }
+  },
+
+  /**
+   * Get a gesture preference or use a default if it doesn't exist
+   *
+   * @param aPref
+   *        Name of the preference to load under the gesture branch
+   * @param aDef
+   *        Default value if the preference doesn't exist
+   */
+  _getPref: function GS__getPref(aPref, aDef) {
+    // Preferences branch under which all gestures preferences are stored
+    const branch = "browser.gesture.";
+
+    try {
+      // Determine what type of data to load based on default value's type
+      let type = typeof aDef;
+      let getFunc = "get" + (type == "boolean" ? "Bool" :
+                             type == "number" ? "Int" : "Char") + "Pref";
+      return gPrefService[getFunc](branch + aPref);
+    }
+    catch (e) {
+      return aDef;
+    }
+  },
+};
+
+function BrowserStartup() {
+  var uriToLoad = null;
+
+  // window.arguments[0]: URI to load (string), or an nsISupportsArray of
+  //                      nsISupportsStrings to load, or a xul:tab of
+  //                      a tabbrowser, which will be replaced by this
+  //                      window (for this case, all other arguments are
+  //                      ignored).
+  //                 [1]: character set (string)
+  //                 [2]: referrer (nsIURI)
+  //                 [3]: postData (nsIInputStream)
+  //                 [4]: allowThirdPartyFixup (bool)
+  if ("arguments" in window && window.arguments[0])
+    uriToLoad = window.arguments[0];
+
+  var isLoadingBlank = isBlankPageURL(uriToLoad);
+  var mustLoadSidebar = false;
+
+  prepareForStartup();
+
+  if (uriToLoad && uriToLoad != "about:blank") {
+    if (uriToLoad instanceof Ci.nsISupportsArray) {
+      let count = uriToLoad.Count();
+      let specs = [];
+      for (let i = 0; i < count; i++) {
+        let urisstring = uriToLoad.GetElementAt(i).QueryInterface(Ci.nsISupportsString);
+        specs.push(urisstring.data);
+      }
+
+      // This function throws for certain malformed URIs, so use exception handling
+      // so that we don't disrupt startup
+      try {
+        gBrowser.loadTabs(specs, false, true);
+      } catch (e) {}
+    }
+    else if (uriToLoad instanceof XULElement) {
+      // swap the given tab with the default about:blank tab and then close
+      // the original tab in the other window.
+
+      // Stop the about:blank load
+      gBrowser.stop();
+      // make sure it has a docshell
+      gBrowser.docShell;
+
+      gBrowser.swapBrowsersAndCloseOther(gBrowser.selectedTab, uriToLoad);
+    }
+    else if (window.arguments.length >= 3) {
+      loadURI(uriToLoad, window.arguments[2], window.arguments[3] || null,
+              window.arguments[4] || false);
+      content.focus();
+    }
+    // Note: loadOneOrMoreURIs *must not* be called if window.arguments.length >= 3.
+    // Such callers expect that window.arguments[0] is handled as a single URI.
+    else
+      loadOneOrMoreURIs(uriToLoad);
+  }
+
+  if (window.opener && !window.opener.closed) {
+    let openerSidebarBox = window.opener.document.getElementById("sidebar-box");
+    // If the opener had a sidebar, open the same sidebar in our window.
+    // The opener can be the hidden window too, if we're coming from the state
+    // where no windows are open, and the hidden window has no sidebar box.
+    if (openerSidebarBox && !openerSidebarBox.hidden) {
+      let sidebarCmd = openerSidebarBox.getAttribute("sidebarcommand");
+      let sidebarCmdElem = document.getElementById(sidebarCmd);
+
+      // dynamically generated sidebars will fail this check.
+      if (sidebarCmdElem) {
+        let sidebarBox = document.getElementById("sidebar-box");
+        let sidebarTitle = document.getElementById("sidebar-title");
+
+        sidebarTitle.setAttribute(
+          "value", window.opener.document.getElementById("sidebar-title").getAttribute("value"));
+        sidebarBox.setAttribute("width", openerSidebarBox.boxObject.width);
+
+        sidebarBox.setAttribute("sidebarcommand", sidebarCmd);
+        // Note: we're setting 'src' on sidebarBox, which is a <vbox>, not on
+        // the <browser id="sidebar">. This lets us delay the actual load until
+        // delayedStartup().
+        sidebarBox.setAttribute(
+          "src", window.opener.document.getElementById("sidebar").getAttribute("src"));
+        mustLoadSidebar = true;
+
+        sidebarBox.hidden = false;
+        document.getElementById("sidebar-splitter").hidden = false;
+        sidebarCmdElem.setAttribute("checked", "true");
+      }
+    }
+  }
+  else {
+    let box = document.getElementById("sidebar-box");
+    if (box.hasAttribute("sidebarcommand")) {
+      let commandID = box.getAttribute("sidebarcommand");
+      if (commandID) {
+        let command = document.getElementById(commandID);
+        if (command) {
+          mustLoadSidebar = true;
+          box.hidden = false;
+          document.getElementById("sidebar-splitter").hidden = false;
+          command.setAttribute("checked", "true");
+        }
+        else {
+          // Remove the |sidebarcommand| attribute, because the element it
+          // refers to no longer exists, so we should assume this sidebar
+          // panel has been uninstalled. (249883)
+          box.removeAttribute("sidebarcommand");
+        }
+      }
+    }
+  }
+
+  // Certain kinds of automigration rely on this notification to complete their
+  // tasks BEFORE the browser window is shown.
+  Services.obs.notifyObservers(null, "browser-window-before-show", "");
+
+  // Set a sane starting width/height for all resolutions on new profiles.
+  if (!document.documentElement.hasAttribute("width")) {
+    let defaultWidth = 994;
+    let defaultHeight;
+    if (screen.availHeight <= 600) {
+      document.documentElement.setAttribute("sizemode", "maximized");
+      defaultWidth = 610;
+      defaultHeight = 450;
+    }
+    else {
+      // Create a narrower window for large or wide-aspect displays, to suggest
+      // side-by-side page view.
+      if (screen.availWidth >= 1600)
+        defaultWidth = (screen.availWidth / 2) - 20;
+      defaultHeight = screen.availHeight - 10;
+//@line 1109 "/build/src/mozilla-release/browser/base/content/browser.js"
+      // On X, we're not currently able to account for the size of the window
+      // border.  Use 28px as a guess (titlebar + bottom window border)
+      defaultHeight -= 28;
+//@line 1113 "/build/src/mozilla-release/browser/base/content/browser.js"
+    }
+    document.documentElement.setAttribute("width", defaultWidth);
+    document.documentElement.setAttribute("height", defaultHeight);
+  }
+
+  if (!gShowPageResizers)
+    document.getElementById("status-bar").setAttribute("hideresizer", "true");
+
+  if (!window.toolbar.visible) {
+    // adjust browser UI for popups
+    if (gURLBar) {
+      gURLBar.setAttribute("readonly", "true");
+      gURLBar.setAttribute("enablehistory", "false");
+    }
+    goSetCommandEnabled("cmd_newNavigatorTab", false);
+  }
+
+//@line 1131 "/build/src/mozilla-release/browser/base/content/browser.js"
+  updateAppButtonDisplay();
+//@line 1133 "/build/src/mozilla-release/browser/base/content/browser.js"
+
+  CombinedStopReload.init();
+
+  allTabs.readPref();
+
+  TabsOnTop.init();
+
+  BookmarksMenuButton.init();
+
+  TabsInTitlebar.init();
+
+  gPrivateBrowsingUI.init();
+
+  DownloadsButton.initializePlaceholder();
+
+  retrieveToolbarIconsizesFromTheme();
+
+  gDelayedStartupTimeoutId = setTimeout(delayedStartup, 0, isLoadingBlank, mustLoadSidebar);
+  gStartupRan = true;
+}
+
+function HandleAppCommandEvent(evt) {
+  evt.stopPropagation();
+  switch (evt.command) {
+  case "Back":
+    BrowserBack();
+    break;
+  case "Forward":
+    BrowserForward();
+    break;
+  case "Reload":
+    BrowserReloadSkipCache();
+    break;
+  case "Stop":
+    BrowserStop();
+    break;
+  case "Search":
+    BrowserSearch.webSearch();
+    break;
+  case "Bookmarks":
+    toggleSidebar('viewBookmarksSidebar');
+    break;
+  case "Home":
+    BrowserHome();
+    break;
+  default:
+    break;
+  }
+}
+
+function prepareForStartup() {
+  gBrowser.addEventListener("DOMUpdatePageReport", gPopupBlockerObserver, false);
+
+  gBrowser.addEventListener("PluginNotFound",     gPluginHandler, true);
+  gBrowser.addEventListener("PluginCrashed",      gPluginHandler, true);
+  gBrowser.addEventListener("PluginBlocklisted",  gPluginHandler, true);
+  gBrowser.addEventListener("PluginOutdated",     gPluginHandler, true);
+  gBrowser.addEventListener("PluginDisabled",     gPluginHandler, true);
+  gBrowser.addEventListener("PluginClickToPlay",  gPluginHandler, true);
+  gBrowser.addEventListener("NewPluginInstalled", gPluginHandler.newPluginInstalled, true);
+//@line 1196 "/build/src/mozilla-release/browser/base/content/browser.js"
+
+  Services.obs.addObserver(gPluginHandler.pluginCrashed, "plugin-crashed", false);
+
+  window.addEventListener("AppCommand", HandleAppCommandEvent, true);
+
+  var webNavigation;
+  try {
+    webNavigation = getWebNavigation();
+    if (!webNavigation)
+      throw "no XBL binding for browser";
+  } catch (e) {
+    alert("Error launching browser window:" + e);
+    window.close(); // Give up.
+    return;
+  }
+
+  messageManager.loadFrameScript("chrome://browser/content/content.js", true);
+
+  // initialize observers and listeners
+  // and give C++ access to gBrowser
+  gBrowser.init();
+  XULBrowserWindow.init();
+  window.QueryInterface(Ci.nsIInterfaceRequestor)
+        .getInterface(nsIWebNavigation)
+        .QueryInterface(Ci.nsIDocShellTreeItem).treeOwner
+        .QueryInterface(Ci.nsIInterfaceRequestor)
+        .getInterface(Ci.nsIXULWindow)
+        .XULBrowserWindow = window.XULBrowserWindow;
+  window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow =
+    new nsBrowserAccess();
+
+  // set default character set if provided
+  if ("arguments" in window && window.arguments.length > 1 && window.arguments[1]) {
+    if (window.arguments[1].indexOf("charset=") != -1) {
+      var arrayArgComponents = window.arguments[1].split("=");
+      if (arrayArgComponents) {
+        //we should "inherit" the charset menu setting in a new window
+        getMarkupDocumentViewer().defaultCharacterSet = arrayArgComponents[1];
+      }
+    }
+  }
+
+  // Manually hook up session and global history for the first browser
+  // so that we don't have to load global history before bringing up a
+  // window.
+  // Wire up session and global history before any possible
+  // progress notifications for back/forward button updating
+  webNavigation.sessionHistory = Components.classes["@mozilla.org/browser/shistory;1"]
+                                           .createInstance(Components.interfaces.nsISHistory);
+  Services.obs.addObserver(gBrowser.browsers[0], "browser:purge-session-history", false);
+
+  // remove the disablehistory attribute so the browser cleans up, as
+  // though it had done this work itself
+  gBrowser.browsers[0].removeAttribute("disablehistory");
+
+  // enable global history
+  try {
+    gBrowser.docShell.QueryInterface(Components.interfaces.nsIDocShellHistory).useGlobalHistory = true;
+  } catch(ex) {
+    Components.utils.reportError("Places database may be locked: " + ex);
+  }
+
+//@line 1261 "/build/src/mozilla-release/browser/base/content/browser.js"
+  // hook up UI through progress listener
+  gBrowser.addProgressListener(window.XULBrowserWindow);
+  gBrowser.addTabsProgressListener(window.TabsProgressListener);
+//@line 1265 "/build/src/mozilla-release/browser/base/content/browser.js"
+
+  // setup our common DOMLinkAdded listener
+  gBrowser.addEventListener("DOMLinkAdded", DOMLinkHandler, false);
+
+  // setup our MozApplicationManifest listener
+  gBrowser.addEventListener("MozApplicationManifest",
+                            OfflineApps, false);
+
+  // setup simple gestures support
+  gGestureSupport.init(true);
+}
+
+function delayedStartup(isLoadingBlank, mustLoadSidebar) {
+  let tmp = {};
+  Cu.import("resource:///modules/TelemetryTimestamps.jsm", tmp);
+  let TelemetryTimestamps = tmp.TelemetryTimestamps;
+  TelemetryTimestamps.add("delayedStartupStarted");
+  gDelayedStartupTimeoutId = null;
+
+  Services.obs.addObserver(gSessionHistoryObserver, "browser:purge-session-history", false);
+  Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled", false);
+  Services.obs.addObserver(gXPInstallObserver, "addon-install-started", false);
+  Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked", false);
+  Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false);
+  Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false);
+  Services.obs.addObserver(gFormSubmitObserver, "invalidformsubmit", false);
+
+  BrowserOffline.init();
+  OfflineApps.init();
+  IndexedDBPromptHelper.init();
+  gFormSubmitObserver.init();
+  AddonManager.addAddonListener(AddonsMgrListener);
+
+  gBrowser.addEventListener("pageshow", function(evt) { setTimeout(pageShowEventHandlers, 0, evt); }, true);
+
+  // Ensure login manager is up and running.
+  Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
+
+  if (mustLoadSidebar) {
+    let sidebar = document.getElementById("sidebar");
+    let sidebarBox = document.getElementById("sidebar-box");
+    sidebar.setAttribute("src", sidebarBox.getAttribute("src"));
+  }
+
+  UpdateUrlbarSearchSplitterState();
+
+  if (isLoadingBlank && gURLBar && isElementVisible(gURLBar))
+    gURLBar.focus();
+  else
+    gBrowser.selectedBrowser.focus();
+
+  gNavToolbox.customizeDone = BrowserToolboxCustomizeDone;
+  gNavToolbox.customizeChange = BrowserToolboxCustomizeChange;
+
+  // Set up Sanitize Item
+  initializeSanitizer();
+
+  // Enable/Disable auto-hide tabbar
+  gBrowser.tabContainer.updateVisibility();
+
+  gPrefService.addObserver(gHomeButton.prefDomain, gHomeButton, false);
+
+  var homeButton = document.getElementById("home-button");
+  gHomeButton.updateTooltip(homeButton);
+  gHomeButton.updatePersonalToolbarStyle(homeButton);
+
+  // BiDi UI
+  gBidiUI = isBidiEnabled();
+  if (gBidiUI) {
+    document.getElementById("documentDirection-separator").hidden = false;
+    document.getElementById("documentDirection-swap").hidden = false;
+    document.getElementById("textfieldDirection-separator").hidden = false;
+    document.getElementById("textfieldDirection-swap").hidden = false;
+  }
+
+  // Setup click-and-hold gestures access to the session history
+  // menus if global click-and-hold isn't turned on
+  if (!getBoolPref("ui.click_hold_context_menus", false))
+    SetClickAndHoldHandlers();
+
+  // Initialize the full zoom setting.
+  // We do this before the session restore service gets initialized so we can
+  // apply full zoom settings to tabs restored by the session restore service.
+  try {
+    FullZoom.init();
+  }
+  catch(ex) {
+    Components.utils.reportError("Failed to init content pref service:\n" + ex);
+  }
+
+//@line 1358 "/build/src/mozilla-release/browser/base/content/browser.js"
+  let NP = {};
+  Cu.import("resource:///modules/NetworkPrioritizer.jsm", NP);
+  NP.trackBrowserWindow(window);
+//@line 1362 "/build/src/mozilla-release/browser/base/content/browser.js"
+
+  // initialize the session-restore service (in case it's not already running)
+  try {
+    Cc["@mozilla.org/browser/sessionstore;1"]
+      .getService(Ci.nsISessionStore)
+      .init(window);
+  } catch (ex) {
+    dump("nsSessionStore could not be initialized: " + ex + "\n");
+  }
+
+  PlacesToolbarHelper.init();
+
+  ctrlTab.readPref();
+  gPrefService.addObserver(ctrlTab.prefName, ctrlTab, false);
+  gPrefService.addObserver(allTabs.prefName, allTabs, false);
+
+  // Initialize the download manager some time after the app starts so that
+  // auto-resume downloads begin (such as after crashing or quitting with
+  // active downloads) and speeds up the first-load of the download manager UI.
+  // If the user manually opens the download manager before the timeout, the
+  // downloads will start right away, and getting the service again won't hurt.
+  setTimeout(function() {
+    gDownloadMgr = Cc["@mozilla.org/download-manager;1"].
+                   getService(Ci.nsIDownloadManager);
+
+//@line 1395 "/build/src/mozilla-release/browser/base/content/browser.js"
+  }, 10000);
+
+  // The object handling the downloads indicator is also initialized here in the
+  // delayed startup function, but the actual indicator element is not loaded
+  // unless there are downloads to be displayed.
+  DownloadsButton.initializeIndicator();
+
+//@line 1403 "/build/src/mozilla-release/browser/base/content/browser.js"
+  updateEditUIVisibility();
+  let placesContext = document.getElementById("placesContext");
+  placesContext.addEventListener("popupshowing", updateEditUIVisibility, false);
+  placesContext.addEventListener("popuphiding", updateEditUIVisibility, false);
+//@line 1408 "/build/src/mozilla-release/browser/base/content/browser.js"
+
+  gBrowser.mPanelContainer.addEventListener("InstallBrowserTheme", LightWeightThemeWebInstaller, false, true);
+  gBrowser.mPanelContainer.addEventListener("PreviewBrowserTheme", LightWeightThemeWebInstaller, false, true);
+  gBrowser.mPanelContainer.addEventListener("ResetBrowserThemePreview", LightWeightThemeWebInstaller, false, true);
+
+//@line 1416 "/build/src/mozilla-release/browser/base/content/browser.js"
+  if (Win7Features)
+    Win7Features.onOpenWindow();
+//@line 1419 "/build/src/mozilla-release/browser/base/content/browser.js"
+
+  // called when we go into full screen, even if it is
+  // initiated by a web page script
+  window.addEventListener("fullscreen", onFullScreen, true);
+
+  // Called when we enter DOM full-screen mode. Note we can already be in browser
+  // full-screen mode when we enter DOM full-screen mode.
+  window.addEventListener("MozEnteredDomFullscreen", onMozEnteredDomFullscreen, true);
+
+  if (window.fullScreen)
+    onFullScreen();
+  if (document.mozFullScreen)
+    onMozEnteredDomFullscreen();
+
+//@line 1434 "/build/src/mozilla-release/browser/base/content/browser.js"
+  // initialize the sync UI
+  gSyncUI.init();
+//@line 1437 "/build/src/mozilla-release/browser/base/content/browser.js"
+
+  gBrowserThumbnails.init();
+  TabView.init();
+
+  setUrlAndSearchBarWidthForConditionalForwardButton();
+  window.addEventListener("resize", function resizeHandler(event) {
+    if (event.target == window)
+      setUrlAndSearchBarWidthForConditionalForwardButton();
+  });
+
+  // Enable developer toolbar?
+  let devToolbarEnabled = gPrefService.getBoolPref("devtools.toolbar.enabled");
+  if (devToolbarEnabled) {
+    document.getElementById("menu_devToolbar").hidden = false;
+    document.getElementById("Tools:DevToolbar").removeAttribute("disabled");
+//@line 1453 "/build/src/mozilla-release/browser/base/content/browser.js"
+    document.getElementById("appmenu_devToolbar").hidden = false;
+//@line 1455 "/build/src/mozilla-release/browser/base/content/browser.js"
+  }
+
+  // Enable Inspector?
+  let enabled = gPrefService.getBoolPref("devtools.inspector.enabled");
+  if (enabled) {
+    document.getElementById("menu_pageinspect").hidden = false;
+    document.getElementById("Tools:Inspect").removeAttribute("disabled");
+//@line 1463 "/build/src/mozilla-release/browser/base/content/browser.js"
+    document.getElementById("appmenu_pageInspect").hidden = false;
+//@line 1465 "/build/src/mozilla-release/browser/base/content/browser.js"
+    document.getElementById("developer-toolbar-inspector").hidden = false;
+  }
+
+  // Enable Debugger?
+  let enabled = gPrefService.getBoolPref("devtools.debugger.enabled");
+  if (enabled) {
+    document.getElementById("menu_debugger").hidden = false;
+    document.getElementById("Tools:Debugger").removeAttribute("disabled");
+//@line 1474 "/build/src/mozilla-release/browser/base/content/browser.js"
+    document.getElementById("appmenu_debugger").hidden = false;
+//@line 1476 "/build/src/mozilla-release/browser/base/content/browser.js"
+    document.getElementById("developer-toolbar-debugger").hidden = false;
+  }
+
+  // Enable Remote Debugger?
+  let enabled = gPrefService.getBoolPref("devtools.debugger.remote-enabled");
+  if (enabled) {
+    document.getElementById("menu_remoteDebugger").hidden = false;
+    document.getElementById("Tools:RemoteDebugger").removeAttribute("disabled");
+//@line 1485 "/build/src/mozilla-release/browser/base/content/browser.js"
+    document.getElementById("appmenu_remoteDebugger").hidden = false;
+//@line 1487 "/build/src/mozilla-release/browser/base/content/browser.js"
+  }
+
+  // Enable Chrome Debugger?
+  let enabled = gPrefService.getBoolPref("devtools.chrome.enabled") &&
+                gPrefService.getBoolPref("devtools.debugger.chrome-enabled") &&
+                gPrefService.getBoolPref("devtools.debugger.remote-enabled");
+  if (enabled) {
+    document.getElementById("menu_chromeDebugger").hidden = false;
+    document.getElementById("Tools:ChromeDebugger").removeAttribute("disabled");
+//@line 1497 "/build/src/mozilla-release/browser/base/content/browser.js"
+    document.getElementById("appmenu_chromeDebugger").hidden = false;
+//@line 1499 "/build/src/mozilla-release/browser/base/content/browser.js"
+  }
+
+  // Enable Error Console?
+  // XXX Temporarily always-enabled, see bug 601201
+  let consoleEnabled = true || gPrefService.getBoolPref("devtools.errorconsole.enabled");
+  if (consoleEnabled) {
+    document.getElementById("javascriptConsole").hidden = false;
+    document.getElementById("key_errorConsole").removeAttribute("disabled");
+//@line 1508 "/build/src/mozilla-release/browser/base/content/browser.js"
+    document.getElementById("appmenu_errorConsole").hidden = false;
+//@line 1510 "/build/src/mozilla-release/browser/base/content/browser.js"
+  }
+
+  // Enable Scratchpad in the UI, if the preference allows this.
+  let scratchpadEnabled = gPrefService.getBoolPref(Scratchpad.prefEnabledName);
+  if (scratchpadEnabled) {
+    document.getElementById("menu_scratchpad").hidden = false;
+    document.getElementById("Tools:Scratchpad").removeAttribute("disabled");
+//@line 1518 "/build/src/mozilla-release/browser/base/content/browser.js"
+    document.getElementById("appmenu_scratchpad").hidden = false;
+//@line 1520 "/build/src/mozilla-release/browser/base/content/browser.js"
+  }
+
+  // Enable Style Editor?
+  let styleEditorEnabled = gPrefService.getBoolPref(StyleEditor.prefEnabledName);
+  if (styleEditorEnabled) {
+    document.getElementById("menu_styleeditor").hidden = false;
+    document.getElementById("Tools:StyleEditor").removeAttribute("disabled");
+//@line 1528 "/build/src/mozilla-release/browser/base/content/browser.js"
+    document.getElementById("appmenu_styleeditor").hidden = false;
+//@line 1530 "/build/src/mozilla-release/browser/base/content/browser.js"
+  }
+
+//@line 1533 "/build/src/mozilla-release/browser/base/content/browser.js"
+  // If the user (or the locale) hasn't enabled the top-level "Character
+  // Encoding" menu via the "browser.menu.showCharacterEncoding" preference,
+  // hide it.
+  if ("true" != gPrefService.getComplexValue("browser.menu.showCharacterEncoding",
+                                             Ci.nsIPrefLocalizedString).data)
+    document.getElementById("appmenu_charsetMenu").hidden = true;
+//@line 1540 "/build/src/mozilla-release/browser/base/content/browser.js"
+
+  // Enable Responsive UI?
+  let responsiveUIEnabled = gPrefService.getBoolPref("devtools.responsiveUI.enabled");
+  if (responsiveUIEnabled) {
+    document.getElementById("menu_responsiveUI").hidden = false;
+    document.getElementById("Tools:ResponsiveUI").removeAttribute("disabled");
+//@line 1547 "/build/src/mozilla-release/browser/base/content/browser.js"
+    document.getElementById("appmenu_responsiveUI").hidden = false;
+//@line 1549 "/build/src/mozilla-release/browser/base/content/browser.js"
+    document.getElementById("developer-toolbar-responsiveui").hidden = false;
+  }
+
+  let appMenuButton = document.getElementById("appmenu-button");
+  let appMenuPopup = document.getElementById("appmenu-popup");
+  if (appMenuButton && appMenuPopup) {
+    let appMenuOpening = null;
+    appMenuButton.addEventListener("mousedown", function(event) {
+      if (event.button == 0)
+        appMenuOpening = new Date();
+    }, false);
+    appMenuPopup.addEventListener("popupshown", function(event) {
+      if (event.target != appMenuPopup || !appMenuOpening)
+        return;
+      let duration = new Date() - appMenuOpening;
+      appMenuOpening = null;
+      Services.telemetry.getHistogramById("FX_APP_MENU_OPEN_MS").add(duration);
+    }, false);
+  }
+
+  window.addEventListener("mousemove", MousePosTracker, false);
+  window.addEventListener("dragover", MousePosTracker, false);
+
+  // End startup crash tracking after a delay to catch crashes while restoring
+  // tabs and to postpone saving the pref to disk.
+  try {
+    let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].
+                     getService(Ci.nsIAppStartup);
+    const startupCrashEndDelay = 30 * 1000;
+    setTimeout(appStartup.trackStartupCrashEnd, startupCrashEndDelay);
+  } catch (ex) {
+    Cu.reportError("Could not end startup crash tracking: " + ex);
+  }
+
+  Services.obs.notifyObservers(window, "browser-delayed-startup-finished", "");
+  TelemetryTimestamps.add("delayedStartupFinished");
+}
+
+function BrowserShutdown() {
+  // In certain scenarios it's possible for unload to be fired before onload,
+  // (e.g. if the window is being closed after browser.js loads but before the
+  // load completes). In that case, there's nothing to do here.
+  if (!gStartupRan)
+    return;
+
+  if (!__lookupGetter__("InspectorUI"))
+    InspectorUI.destroy();
+
+  // First clean up services initialized in BrowserStartup (or those whose
+  // uninit methods don't depend on the services having been initialized).
+  allTabs.uninit();
+
+  CombinedStopReload.uninit();
+
+  gGestureSupport.init(false);
+
+  FullScreen.cleanup();
+
+  Services.obs.removeObserver(gPluginHandler.pluginCrashed, "plugin-crashed");
+
+  try {
+    gBrowser.removeProgressListener(window.XULBrowserWindow);
+    gBrowser.removeTabsProgressListener(window.TabsProgressListener);
+  } catch (ex) {
+  }
+
+  PlacesStarButton.uninit();
+
+  gPrivateBrowsingUI.uninit();
+
+  TabsOnTop.uninit();
+
+  TabsInTitlebar.uninit();
+
+  var enumerator = Services.wm.getEnumerator(null);
+  enumerator.getNext();
+  if (!enumerator.hasMoreElements()) {
+    document.persist("sidebar-box", "sidebarcommand");
+    document.persist("sidebar-box", "width");
+    document.persist("sidebar-box", "src");
+    document.persist("sidebar-title", "value");
+  }
+
+  // Now either cancel delayedStartup, or clean up the services initialized from
+  // it.
+  if (gDelayedStartupTimeoutId) {
+    clearTimeout(gDelayedStartupTimeoutId);
+  } else {
+    if (Win7Features)
+      Win7Features.onCloseWindow();
+
+    gPrefService.removeObserver(ctrlTab.prefName, ctrlTab);
+    gPrefService.removeObserver(allTabs.prefName, allTabs);
+    ctrlTab.uninit();
+    TabView.uninit();
+    gBrowserThumbnails.uninit();
+
+    try {
+      FullZoom.destroy();
+    }
+    catch(ex) {
+      Components.utils.reportError(ex);
+    }
+
+    Services.obs.removeObserver(gSessionHistoryObserver, "browser:purge-session-history");
+    Services.obs.removeObserver(gXPInstallObserver, "addon-install-disabled");
+    Services.obs.removeObserver(gXPInstallObserver, "addon-install-started");
+    Services.obs.removeObserver(gXPInstallObserver, "addon-install-blocked");
+    Services.obs.removeObserver(gXPInstallObserver, "addon-install-failed");
+    Services.obs.removeObserver(gXPInstallObserver, "addon-install-complete");
+    Services.obs.removeObserver(gFormSubmitObserver, "invalidformsubmit");
+
+    try {
+      gPrefService.removeObserver(gHomeButton.prefDomain, gHomeButton);
+    } catch (ex) {
+      Components.utils.reportError(ex);
+    }
+
+    BrowserOffline.uninit();
+    OfflineApps.uninit();
+    IndexedDBPromptHelper.uninit();
+    AddonManager.removeAddonListener(AddonsMgrListener);
+  }
+
+  // Final window teardown, do this last.
+  window.XULBrowserWindow.destroy();
+  window.XULBrowserWindow = null;
+  window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+        .getInterface(Components.interfaces.nsIWebNavigation)
+        .QueryInterface(Components.interfaces.nsIDocShellTreeItem).treeOwner
+        .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
+        .getInterface(Components.interfaces.nsIXULWindow)
+        .XULBrowserWindow = null;
+  window.QueryInterface(Ci.nsIDOMChromeWindow).browserDOMWindow = null;
+}
+
+//@line 1773 "/build/src/mozilla-release/browser/base/content/browser.js"
+
+function initializeSanitizer()
+{
+  const kDidSanitizeDomain = "privacy.sanitize.didShutdownSanitize";
+  if (gPrefService.prefHasUserValue(kDidSanitizeDomain)) {
+    gPrefService.clearUserPref(kDidSanitizeDomain);
+    // We need to persist this preference change, since we want to
+    // check it at next app start even if the browser exits abruptly
+    gPrefService.savePrefFile(null);
+  }
+
+  /**
+   * Migrate Firefox 3.0 privacy.item prefs under one of these conditions:
+   *
+   * a) User has customized any privacy.item prefs
+   * b) privacy.sanitize.sanitizeOnShutdown is set
+   */
+  if (!gPrefService.getBoolPref("privacy.sanitize.migrateFx3Prefs")) {
+    let itemBranch = gPrefService.getBranch("privacy.item.");
+    let itemArray = itemBranch.getChildList("");
+
+    // See if any privacy.item prefs are set
+    let doMigrate = itemArray.some(function (name) itemBranch.prefHasUserValue(name));
+    // Or if sanitizeOnShutdown is set
+    if (!doMigrate)
+      doMigrate = gPrefService.getBoolPref("privacy.sanitize.sanitizeOnShutdown");
+
+    if (doMigrate) {
+      let cpdBranch = gPrefService.getBranch("privacy.cpd.");
+      let clearOnShutdownBranch = gPrefService.getBranch("privacy.clearOnShutdown.");
+      itemArray.forEach(function (name) {
+        try {
+          // don't migrate password or offlineApps clearing in the CRH dialog since
+          // there's no UI for those anymore. They default to false. bug 497656
+          if (name != "passwords" && name != "offlineApps")
+            cpdBranch.setBoolPref(name, itemBranch.getBoolPref(name));
+          clearOnShutdownBranch.setBoolPref(name, itemBranch.getBoolPref(name));
+        }
+        catch(e) {
+          Cu.reportError("Exception thrown during privacy pref migration: " + e);
+        }
+      });
+    }
+
+    gPrefService.setBoolPref("privacy.sanitize.migrateFx3Prefs", true);
+  }
+}
+
+function gotoHistoryIndex(aEvent) {
+  let index = aEvent.target.getAttribute("index");
+  if (!index)
+    return false;
+
+  let where = whereToOpenLink(aEvent);
+
+  if (where == "current") {
+    // Normal click. Go there in the current tab and update session history.
+
+    try {
+      gBrowser.gotoIndex(index);
+    }
+    catch(ex) {
+      return false;
+    }
+    return true;
+  }
+  // Modified click. Go there in a new tab/window.
+
+  duplicateTabIn(gBrowser.selectedTab, where, index - gBrowser.sessionHistory.index);
+  return true;
+}
+
+function BrowserForward(aEvent) {
+  let where = whereToOpenLink(aEvent, false, true);
+
+  if (where == "current") {
+    try {
+      gBrowser.goForward();
+    }
+    catch(ex) {
+    }
+  }
+  else {
+    duplicateTabIn(gBrowser.selectedTab, where, 1);
+  }
+}
+
+function BrowserBack(aEvent) {
+  let where = whereToOpenLink(aEvent, false, true);
+
+  if (where == "current") {
+    try {
+      gBrowser.goBack();
+    }
+    catch(ex) {
+    }
+  }
+  else {
+    duplicateTabIn(gBrowser.selectedTab, where, -1);
+  }
+}
+
+function BrowserHandleBackspace()
+{
+  switch (gPrefService.getIntPref("browser.backspace_action")) {
+  case 0:
+    BrowserBack();
+    break;
+  case 1:
+    goDoCommand("cmd_scrollPageUp");
+    break;
+  }
+}
+
+function BrowserHandleShiftBackspace()
+{
+  switch (gPrefService.getIntPref("browser.backspace_action")) {
+  case 0:
+    BrowserForward();
+    break;
+  case 1:
+    goDoCommand("cmd_scrollPageDown");
+    break;
+  }
+}
+
+function BrowserStop()
+{
+  try {
+    const stopFlags = nsIWebNavigation.STOP_ALL;
+    getWebNavigation().stop(stopFlags);
+  }
+  catch(ex) {
+  }
+}
+
+function BrowserReloadOrDuplicate(aEvent) {
+  var backgroundTabModifier = aEvent.button == 1 ||
+//@line 1914 "/build/src/mozilla-release/browser/base/content/browser.js"
+    aEvent.ctrlKey;
+//@line 1916 "/build/src/mozilla-release/browser/base/content/browser.js"
+  if (aEvent.shiftKey && !backgroundTabModifier) {
+    BrowserReloadSkipCache();
+    return;
+  }
+
+  let where = whereToOpenLink(aEvent, false, true);
+  if (where == "current")
+    BrowserReload();
+  else
+    duplicateTabIn(gBrowser.selectedTab, where);
+}
+
+function BrowserReload() {
+  const reloadFlags = nsIWebNavigation.LOAD_FLAGS_NONE;
+  BrowserReloadWithFlags(reloadFlags);
+}
+
+function BrowserReloadSkipCache() {
+  // Bypass proxy and cache.
+  const reloadFlags = nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY | nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE;
+  BrowserReloadWithFlags(reloadFlags);
+}
+
+var BrowserHome = BrowserGoHome;
+function BrowserGoHome(aEvent) {
+  if (aEvent && "button" in aEvent &&
+      aEvent.button == 2) // right-click: do nothing
+    return;
+
+  var homePage = gHomeButton.getHomePage();
+  var where = whereToOpenLink(aEvent, false, true);
+  var urls;
+
+  // Home page should open in a new tab when current tab is an app tab
+  if (where == "current" &&
+      gBrowser &&
+      gBrowser.selectedTab.pinned)
+    where = "tab";
+
+  // openUILinkIn in utilityOverlay.js doesn't handle loading multiple pages
+  switch (where) {
+  case "current":
+    loadOneOrMoreURIs(homePage);
+    break;
+  case "tabshifted":
+  case "tab":
+    urls = homePage.split("|");
+    var loadInBackground = getBoolPref("browser.tabs.loadBookmarksInBackground", false);
+    gBrowser.loadTabs(urls, loadInBackground);
+    break;
+  case "window":
+    OpenBrowserWindow();
+    break;
+  }
+}
+
+function loadOneOrMoreURIs(aURIString)
+{
+//@line 1982 "/build/src/mozilla-release/browser/base/content/browser.js"
+  // This function throws for certain malformed URIs, so use exception handling
+  // so that we don't disrupt startup
+  try {
+    gBrowser.loadTabs(aURIString.split("|"), false, true);
+  }
+  catch (e) {
+  }
+}
+
+function focusAndSelectUrlBar() {
+  if (gURLBar) {
+    if (window.fullScreen)
+      FullScreen.mouseoverToggle(true);
+    if (isElementVisible(gURLBar)) {
+      gURLBar.focus();
+      gURLBar.select();
+      return true;
+    }
+  }
+  return false;
+}
+
+function openLocation() {
+  if (focusAndSelectUrlBar())
+    return;
+
+//@line 2025 "/build/src/mozilla-release/browser/base/content/browser.js"
+  openDialog("chrome://browser/content/openLocation.xul", "_blank",
+             "chrome,modal,titlebar", window);
+}
+
+function openLocationCallback()
+{
+  // make sure the DOM is ready
+  setTimeout(function() { this.openLocation(); }, 0);
+}
+
+function BrowserOpenTab()
+{
+  openUILinkIn(BROWSER_NEW_TAB_URL, "tab");
+}
+
+/* Called from the openLocation dialog. This allows that dialog to instruct
+   its opener to open a new window and then step completely out of the way.
+   Anything less byzantine is causing horrible crashes, rather believably,
+   though oddly only on Linux. */
+function delayedOpenWindow(chrome, flags, href, postData)
+{
+  // The other way to use setTimeout,
+  // setTimeout(openDialog, 10, chrome, "_blank", flags, url),
+  // doesn't work here.  The extra "magic" extra argument setTimeout adds to
+  // the callback function would confuse prepareForStartup() by making
+  // window.arguments[1] be an integer instead of null.
+  setTimeout(function() { openDialog(chrome, "_blank", flags, href, null, null, postData); }, 10);
+}
+
+/* Required because the tab needs time to set up its content viewers and get the load of
+   the URI kicked off before becoming the active content area. */
+function delayedOpenTab(aUrl, aReferrer, aCharset, aPostData, aAllowThirdPartyFixup)
+{
+  gBrowser.loadOneTab(aUrl, {
+                      referrerURI: aReferrer,
+                      charset: aCharset,
+                      postData: aPostData,
+                      inBackground: false,
+                      allowThirdPartyFixup: aAllowThirdPartyFixup});
+}
+
+var gLastOpenDirectory = {
+  _lastDir: null,
+  get path() {
+    if (!this._lastDir || !this._lastDir.exists()) {
+      try {
+        this._lastDir = gPrefService.getComplexValue("browser.open.lastDir",
+                                                     Ci.nsILocalFile);
+        if (!this._lastDir.exists())
+          this._lastDir = null;
+      }
+      catch(e) {}
+    }
+    return this._lastDir;
+  },
+  set path(val) {
+    try {
+      if (!val || !val.isDirectory())
+        return;
+    } catch(e) {
+      return;
+    }
+    this._lastDir = val.clone();
+
+    // Don't save the last open directory pref inside the Private Browsing mode
+    if (!gPrivateBrowsingUI.privateBrowsingEnabled)
+      gPrefService.setComplexValue("browser.open.lastDir", Ci.nsILocalFile,
+                                   this._lastDir);
+  },
+  reset: function() {
+    this._lastDir = null;
+  }
+};
+
+function BrowserOpenFileWindow()
+{
+  // Get filepicker component.
+  try {
+    const nsIFilePicker = Components.interfaces.nsIFilePicker;
+    var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance(nsIFilePicker);
+    fp.init(window, gNavigatorBundle.getString("openFile"), nsIFilePicker.modeOpen);
+    fp.appendFilters(nsIFilePicker.filterAll | nsIFilePicker.filterText | nsIFilePicker.filterImages |
+                     nsIFilePicker.filterXML | nsIFilePicker.filterHTML);
+    fp.displayDirectory = gLastOpenDirectory.path;
+
+    if (fp.show() == nsIFilePicker.returnOK) {
+      try {
+        if (fp.file)
+          gLastOpenDirectory.path = fp.file.parent.QueryInterface(Ci.nsILocalFile);
+      } catch(e) {
+      }
+      openUILinkIn(fp.fileURL.spec, "current");
+    }
+  } catch (ex) {
+  }
+}
+
+function BrowserCloseTabOrWindow() {
+//@line 2130 "/build/src/mozilla-release/browser/base/content/browser.js"
+
+  // If the current tab is the last one, this will close the window.
+  gBrowser.removeCurrentTab({animate: true});
+}
+
+function BrowserTryToCloseWindow()
+{
+  if (WindowIsClosing())
+    window.close();     // WindowIsClosing does all the necessary checks
+}
+
+function loadURI(uri, referrer, postData, allowThirdPartyFixup) {
+  if (postData === undefined)
+    postData = null;
+
+  var flags = nsIWebNavigation.LOAD_FLAGS_NONE;
+  if (allowThirdPartyFixup)
+    flags |= nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP;
+
+  try {
+    gBrowser.loadURIWithFlags(uri, flags, referrer, null, postData);
+  } catch (e) {}
+}
+
+function getShortcutOrURI(aURL, aPostDataRef, aMayInheritPrincipal) {
+  // Initialize outparam to false
+  if (aMayInheritPrincipal)
+    aMayInheritPrincipal.value = false;
+
+  var shortcutURL = null;
+  var keyword = aURL;
+  var param = "";
+
+  var offset = aURL.indexOf(" ");
+  if (offset > 0) {
+    keyword = aURL.substr(0, offset);
+    param = aURL.substr(offset + 1);
+  }
+
+  if (!aPostDataRef)
+    aPostDataRef = {};
+
+  var engine = Services.search.getEngineByAlias(keyword);
+  if (engine) {
+    var submission = engine.getSubmission(param);
+    aPostDataRef.value = submission.postData;
+    return submission.uri.spec;
+  }
+
+  [shortcutURL, aPostDataRef.value] =
+    PlacesUtils.getURLAndPostDataForKeyword(keyword);
+
+  if (!shortcutURL)
+    return aURL;
+
+  var postData = "";
+  if (aPostDataRef.value)
+    postData = unescape(aPostDataRef.value);
+
+  if (/%s/i.test(shortcutURL) || /%s/i.test(postData)) {
+    var charset = "";
+    const re = /^(.*)\&mozcharset=([a-zA-Z][_\-a-zA-Z0-9]+)\s*$/;
+    var matches = shortcutURL.match(re);
+    if (matches)
+      [, shortcutURL, charset] = matches;
+    else {
+      // Try to get the saved character-set.
+      try {
+        // makeURI throws if URI is invalid.
+        // Will return an empty string if character-set is not found.
+        charset = PlacesUtils.history.getCharsetForURI(makeURI(shortcutURL));
+      } catch (e) {}
+    }
+
+    // encodeURIComponent produces UTF-8, and cannot be used for other charsets.
+    // escape() works in those cases, but it doesn't uri-encode +, @, and /.
+    // Therefore we need to manually replace these ASCII characters by their
+    // encodeURIComponent result, to match the behavior of nsEscape() with
+    // url_XPAlphas
+    var encodedParam = "";
+    if (charset && charset != "UTF-8")
+      encodedParam = escape(convertFromUnicode(charset, param)).
+                     replace(/[+@\/]+/g, encodeURIComponent);
+    else // Default charset is UTF-8
+      encodedParam = encodeURIComponent(param);
+
+    shortcutURL = shortcutURL.replace(/%s/g, encodedParam).replace(/%S/g, param);
+
+    if (/%s/i.test(postData)) // POST keyword
+      aPostDataRef.value = getPostDataStream(postData, param, encodedParam,
+                                             "application/x-www-form-urlencoded");
+  }
+  else if (param) {
+    // This keyword doesn't take a parameter, but one was provided. Just return
+    // the original URL.
+    aPostDataRef.value = null;
+
+    return aURL;
+  }
+
+  // This URL came from a bookmark, so it's safe to let it inherit the current
+  // document's principal.
+  if (aMayInheritPrincipal)
+    aMayInheritPrincipal.value = true;
+
+  return shortcutURL;
+}
+
+function getPostDataStream(aStringData, aKeyword, aEncKeyword, aType) {
+  var dataStream = Cc["@mozilla.org/io/string-input-stream;1"].
+                   createInstance(Ci.nsIStringInputStream);
+  aStringData = aStringData.replace(/%s/g, aEncKeyword).replace(/%S/g, aKeyword);
+  dataStream.data = aStringData;
+
+  var mimeStream = Cc["@mozilla.org/network/mime-input-stream;1"].
+                   createInstance(Ci.nsIMIMEInputStream);
+  mimeStream.addHeader("Content-Type", aType);
+  mimeStream.addContentLength = true;
+  mimeStream.setData(dataStream);
+  return mimeStream.QueryInterface(Ci.nsIInputStream);
+}
+
+function readFromClipboard()
+{
+  var url;
+
+  try {
+    // Get clipboard.
+    var clipboard = Components.classes["@mozilla.org/widget/clipboard;1"]
+                              .getService(Components.interfaces.nsIClipboard);
+
+    // Create transferable that will transfer the text.
+    var trans = Components.classes["@mozilla.org/widget/transferable;1"]
+                          .createInstance(Components.interfaces.nsITransferable);
+
+    trans.addDataFlavor("text/unicode");
+
+    // If available, use selection clipboard, otherwise global one
+    if (clipboard.supportsSelectionClipboard())
+      clipboard.getData(trans, clipboard.kSelectionClipboard);
+    else
+      clipboard.getData(trans, clipboard.kGlobalClipboard);
+
+    var data = {};
+    var dataLen = {};
+    trans.getTransferData("text/unicode", data, dataLen);
+
+    if (data) {
+      data = data.value.QueryInterface(Components.interfaces.nsISupportsString);
+      url = data.data.substring(0, dataLen.value / 2);
+    }
+  } catch (ex) {
+  }
+
+  return url;
+}
+
+function BrowserViewSourceOfDocument(aDocument)
+{
+  var pageCookie;
+  var webNav;
+
+  // Get the document charset
+  var docCharset = "charset=" + aDocument.characterSet;
+
+  // Get the nsIWebNavigation associated with the document
+  try {
+      var win;
+      var ifRequestor;
+
+      // Get the DOMWindow for the requested document.  If the DOMWindow
+      // cannot be found, then just use the content window...
+      //
+      // XXX:  This is a bit of a hack...
+      win = aDocument.defaultView;
+      if (win == window) {
+        win = content;
+      }
+      ifRequestor = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
+
+      webNav = ifRequestor.getInterface(nsIWebNavigation);
+  } catch(err) {
+      // If nsIWebNavigation cannot be found, just get the one for the whole
+      // window...
+      webNav = getWebNavigation();
+  }
+  //
+  // Get the 'PageDescriptor' for the current document. This allows the
+  // view-source to access the cached copy of the content rather than
+  // refetching it from the network...
+  //
+  try{
+    var PageLoader = webNav.QueryInterface(Components.interfaces.nsIWebPageDescriptor);
+
+    pageCookie = PageLoader.currentDescriptor;
+  } catch(err) {
+    // If no page descriptor is available, just use the view-source URL...
+  }
+
+  top.gViewSourceUtils.viewSource(webNav.currentURI.spec, pageCookie, aDocument);
+}
+
+// doc - document to use for source, or null for this window's document
+// initialTab - name of the initial tab to display, or null for the first tab
+// imageElement - image to load in the Media Tab of the Page Info window; can be null/omitted
+function BrowserPageInfo(doc, initialTab, imageElement) {
+  var args = {doc: doc, initialTab: initialTab, imageElement: imageElement};
+  var windows = Cc['@mozilla.org/appshell/window-mediator;1']
+                  .getService(Ci.nsIWindowMediator)
+                  .getEnumerator("Browser:page-info");
+
+  var documentURL = doc ? doc.location : window.content.document.location;
+
+  // Check for windows matching the url
+  while (windows.hasMoreElements()) {
+    var currentWindow = windows.getNext();
+    if (currentWindow.document.documentElement.getAttribute("relatedUrl") == documentURL) {
+      currentWindow.focus();
+      currentWindow.resetPageInfo(args);
+      return currentWindow;
+    }
+  }
+
+  // We didn't find a matching window, so open a new one.
+  return openDialog("chrome://browser/content/pageinfo/pageInfo.xul", "",
+                    "chrome,toolbar,dialog=no,resizable", args);
+}
+
+function URLBarSetURI(aURI) {
+  var value = gBrowser.userTypedValue;
+  var valid = false;
+
+  if (value == null) {
+    let uri = aURI || getWebNavigation().currentURI;
+
+    // Replace initial page URIs with an empty string
+    // only if there's no opener (bug 370555).
+    if (gInitialPages.indexOf(uri.spec) != -1)
+      value = content.opener ? uri.spec : "";
+    else
+      value = losslessDecodeURI(uri);
+
+    valid = !isBlankPageURL(uri.spec);
+  }
+
+  gURLBar.value = value;
+  gURLBar.valueIsTyped = !valid;
+  SetPageProxyState(valid ? "valid" : "invalid");
+}
+
+function losslessDecodeURI(aURI) {
+  var value = aURI.spec;
+  // Try to decode as UTF-8 if there's no encoding sequence that we would break.
+  if (!/%25(?:3B|2F|3F|3A|40|26|3D|2B|24|2C|23)/i.test(value))
+    try {
+      value = decodeURI(value)
+                // 1. decodeURI decodes %25 to %, which creates unintended
+                //    encoding sequences. Re-encode it, unless it's part of
+                //    a sequence that survived decodeURI, i.e. one for:
+                //    ';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '#'
+                //    (RFC 3987 section 3.2)
+                // 2. Re-encode whitespace so that it doesn't get eaten away
+                //    by the location bar (bug 410726).
+                .replace(/%(?!3B|2F|3F|3A|40|26|3D|2B|24|2C|23)|[\r\n\t]/ig,
+                         encodeURIComponent);
+    } catch (e) {}
+
+  // Encode invisible characters (line and paragraph separator,
+  // object replacement character) (bug 452979)
+  value = value.replace(/[\v\x0c\x1c\x1d\x1e\x1f\u2028\u2029\ufffc]/g,
+                        encodeURIComponent);
+
+  // Encode default ignorable characters (bug 546013)
+  // except ZWNJ (U+200C) and ZWJ (U+200D) (bug 582186).
+  // This includes all bidirectional formatting characters.
+  // (RFC 3987 sections 3.2 and 4.1 paragraph 6)
+  value = value.replace(/[\u00ad\u034f\u115f-\u1160\u17b4-\u17b5\u180b-\u180d\u200b\u200e-\u200f\u202a-\u202e\u2060-\u206f\u3164\ufe00-\ufe0f\ufeff\uffa0\ufff0-\ufff8]|\ud834[\udd73-\udd7a]|[\udb40-\udb43][\udc00-\udfff]/g,
+                        encodeURIComponent);
+  return value;
+}
+
+function UpdateUrlbarSearchSplitterState()
+{
+  var splitter = document.getElementById("urlbar-search-splitter");
+  var urlbar = document.getElementById("urlbar-container");
+  var searchbar = document.getElementById("search-container");
+  var stop = document.getElementById("stop-button");
+
+  var ibefore = null;
+  if (urlbar && searchbar) {
+    if (urlbar.nextSibling == searchbar ||
+        urlbar.getAttribute("combined") &&
+        stop && stop.nextSibling == searchbar)
+      ibefore = searchbar;
+    else if (searchbar.nextSibling == urlbar)
+      ibefore = urlbar;
+  }
+
+  if (ibefore) {
+    if (!splitter) {
+      splitter = document.createElement("splitter");
+      splitter.id = "urlbar-search-splitter";
+      splitter.setAttribute("resizebefore", "flex");
+      splitter.setAttribute("resizeafter", "flex");
+      splitter.setAttribute("skipintoolbarset", "true");
+      splitter.className = "chromeclass-toolbar-additional";
+    }
+    urlbar.parentNode.insertBefore(splitter, ibefore);
+  } else if (splitter)
+    splitter.parentNode.removeChild(splitter);
+}
+
+function setUrlAndSearchBarWidthForConditionalForwardButton() {
+  // Workaround for bug 694084: Showing/hiding the conditional forward button resizes
+  // the search bar when the url/search bar splitter hasn't been used.
+  var urlbarContainer = document.getElementById("urlbar-container");
+  var searchbarContainer = document.getElementById("search-container");
+  if (!urlbarContainer ||
+      !searchbarContainer ||
+      urlbarContainer.hasAttribute("width") ||
+      searchbarContainer.hasAttribute("width") ||
+      urlbarContainer.parentNode != searchbarContainer.parentNode)
+    return;
+  urlbarContainer.style.width = searchbarContainer.style.width = "";
+  var urlbarWidth = urlbarContainer.clientWidth;
+  var searchbarWidth = searchbarContainer.clientWidth;
+  urlbarContainer.style.width = urlbarWidth + "px";
+  searchbarContainer.style.width = searchbarWidth + "px";
+}
+
+function UpdatePageProxyState()
+{
+  if (gURLBar && gURLBar.value != gLastValidURLStr)
+    SetPageProxyState("invalid");
+}
+
+function SetPageProxyState(aState)
+{
+  if (!gURLBar)
+    return;
+
+  if (!gProxyFavIcon)
+    gProxyFavIcon = document.getElementById("page-proxy-favicon");
+
+  gURLBar.setAttribute("pageproxystate", aState);
+  gProxyFavIcon.setAttribute("pageproxystate", aState);
+
+  // the page proxy state is set to valid via OnLocationChange, which
+  // gets called when we switch tabs.
+  if (aState == "valid") {
+    gLastValidURLStr = gURLBar.value;
+    gURLBar.addEventListener("input", UpdatePageProxyState, false);
+  } else if (aState == "invalid") {
+    gURLBar.removeEventListener("input", UpdatePageProxyState, false);
+  }
+}
+
+function PageProxyClickHandler(aEvent)
+{
+  if (aEvent.button == 1 && gPrefService.getBoolPref("middlemouse.paste"))
+    middleMousePaste(aEvent);
+}
+
+/**
+ *  Handle load of some pages (about:*) so that we can make modifications
+ *  to the DOM for unprivileged pages.
+ */
+function BrowserOnAboutPageLoad(document) {
+  if (/^about:home$/i.test(document.documentURI)) {
+    // XXX bug 738646 - when Marketplace is launched, remove this statement and
+    // the hidden attribute set on the apps button in aboutHome.xhtml
+    if (getBoolPref("browser.aboutHome.apps", false))
+      document.getElementById("apps").removeAttribute("hidden");
+    let ss = Components.classes["@mozilla.org/browser/sessionstore;1"].
+             getService(Components.interfaces.nsISessionStore);
+    if (!ss.canRestoreLastSession)
+      document.getElementById("launcher").removeAttribute("session");
+  }
+}
+
+/**
+ * Handle command events bubbling up from error page content
+ */
+function BrowserOnClick(event) {
+    if (!event.isTrusted || // Don't trust synthetic events
+        event.button == 2 || event.target.localName != "button")
+      return;
+
+    var ot = event.originalTarget;
+    var ownerDoc = ot.ownerDocument;
+
+    // If the event came from an ssl error page, it is probably either the "Add
+    // Exception…" or "Get me out of here!" button
+    if (/^about:certerror/.test(ownerDoc.documentURI)) {
+      if (ot == ownerDoc.getElementById('exceptionDialogButton')) {
+        var params = { exceptionAdded : false, handlePrivateBrowsing : true };
+
+        try {
+          switch (gPrefService.getIntPref("browser.ssl_override_behavior")) {
+            case 2 : // Pre-fetch & pre-populate
+              params.prefetchCert = true;
+            case 1 : // Pre-populate
+              params.location = ownerDoc.location.href;
+          }
+        } catch (e) {
+          Components.utils.reportError("Couldn't get ssl_override pref: " + e);
+        }
+
+        window.openDialog('chrome://pippki/content/exceptionDialog.xul',
+                          '','chrome,centerscreen,modal', params);
+
+        // If the user added the exception cert, attempt to reload the page
+        if (params.exceptionAdded)
+          ownerDoc.location.reload();
+      }
+      else if (ot == ownerDoc.getElementById('getMeOutOfHereButton')) {
+        getMeOutOfHere();
+      }
+    }
+    else if (/^about:blocked/.test(ownerDoc.documentURI)) {
+      // The event came from a button on a malware/phishing block page
+      // First check whether it's malware or phishing, so that we can
+      // use the right strings/links
+      var isMalware = /e=malwareBlocked/.test(ownerDoc.documentURI);
+
+      if (ot == ownerDoc.getElementById('getMeOutButton')) {
+        getMeOutOfHere();
+      }
+      else if (ot == ownerDoc.getElementById('reportButton')) {
+        // This is the "Why is this site blocked" button.  For malware,
+        // we can fetch a site-specific report, for phishing, we redirect
+        // to the generic page describing phishing protection.
+
+        if (isMalware) {
+          // Get the stop badware "why is this blocked" report url,
+          // append the current url, and go there.
+          try {
+            let reportURL = formatURL("browser.safebrowsing.malware.reportURL", true);
+            reportURL += ownerDoc.location.href;
+            content.location = reportURL;
+          } catch (e) {
+            Components.utils.reportError("Couldn't get malware report URL: " + e);
+          }
+        }
+        else { // It's a phishing site, not malware
+          try {
+            content.location = formatURL("browser.safebrowsing.warning.infoURL", true);
+          } catch (e) {
+            Components.utils.reportError("Couldn't get phishing info URL: " + e);
+          }
+        }
+      }
+      else if (ot == ownerDoc.getElementById('ignoreWarningButton')) {
+        // Allow users to override and continue through to the site,
+        // but add a notify bar as a reminder, so that they don't lose
+        // track after, e.g., tab switching.
+        gBrowser.loadURIWithFlags(content.location.href,
+                                  nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER,
+                                  null, null, null);
+
+        Services.perms.add(makeURI(content.location.href), "safe-browsing",
+                           Ci.nsIPermissionManager.ALLOW_ACTION,
+                           Ci.nsIPermissionManager.EXPIRE_SESSION);
+
+        let buttons = [{
+          label: gNavigatorBundle.getString("safebrowsing.getMeOutOfHereButton.label"),
+          accessKey: gNavigatorBundle.getString("safebrowsing.getMeOutOfHereButton.accessKey"),
+          callback: function() { getMeOutOfHere(); }
+        }];
+
+        let title;
+        if (isMalware) {
+          title = gNavigatorBundle.getString("safebrowsing.reportedAttackSite");
+          buttons[1] = {
+            label: gNavigatorBundle.getString("safebrowsing.notAnAttackButton.label"),
+            accessKey: gNavigatorBundle.getString("safebrowsing.notAnAttackButton.accessKey"),
+            callback: function() {
+              openUILinkIn(safebrowsing.getReportURL('MalwareError'), 'tab');
+            }
+          };
+        } else {
+          title = gNavigatorBundle.getString("safebrowsing.reportedWebForgery");
+          buttons[1] = {
+            label: gNavigatorBundle.getString("safebrowsing.notAForgeryButton.label"),
+            accessKey: gNavigatorBundle.getString("safebrowsing.notAForgeryButton.accessKey"),
+            callback: function() {
+              openUILinkIn(safebrowsing.getReportURL('Error'), 'tab');
+            }
+          };
+        }
+
+        let notificationBox = gBrowser.getNotificationBox();
+        let value = "blocked-badware-page";
+
+        let previousNotification = notificationBox.getNotificationWithValue(value);
+        if (previousNotification)
+          notificationBox.removeNotification(previousNotification);
+
+        let notification = notificationBox.appendNotification(
+          title,
+          value,
+          "chrome://global/skin/icons/blacklist_favicon.png",
+          notificationBox.PRIORITY_CRITICAL_HIGH,
+          buttons
+        );
+        // Persist the notification until the user removes so it
+        // doesn't get removed on redirects.
+        notification.persistence = -1;
+      }
+    }
+    else if (/^about:home$/i.test(ownerDoc.documentURI)) {
+      if (ot == ownerDoc.getElementById("restorePreviousSession")) {
+        let ss = Cc["@mozilla.org/browser/sessionstore;1"].
+                 getService(Ci.nsISessionStore);
+        if (ss.canRestoreLastSession)
+          ss.restoreLastSession();
+        ownerDoc.getElementById("launcher").removeAttribute("session");
+      }
+      else if (ot == ownerDoc.getElementById("downloads")) {
+        BrowserDownloadsUI();
+      }
+      else if (ot == ownerDoc.getElementById("bookmarks")) {
+        PlacesCommandHook.showPlacesOrganizer("AllBookmarks");
+      }
+      else if (ot == ownerDoc.getElementById("history")) {
+        PlacesCommandHook.showPlacesOrganizer("History");
+      }
+      else if (ot == ownerDoc.getElementById("apps")) {
+        openUILinkIn("https://marketplace.mozilla.org/", "tab");
+      }
+      else if (ot == ownerDoc.getElementById("addons")) {
+        BrowserOpenAddonsMgr();
+      }
+      else if (ot == ownerDoc.getElementById("sync")) {
+        openPreferences("paneSync");
+      }
+      else if (ot == ownerDoc.getElementById("settings")) {
+        openPreferences();
+      }
+    }
+}
+
+/**
+ * Re-direct the browser to a known-safe page.  This function is
+ * used when, for example, the user browses to a known malware page
+ * and is presented with about:blocked.  The "Get me out of here!"
+ * button should take the user to the default start page so that even
+ * when their own homepage is infected, we can get them somewhere safe.
+ */
+function getMeOutOfHere() {
+  // Get the start page from the *default* pref branch, not the user's
+  var prefs = Cc["@mozilla.org/preferences-service;1"]
+             .getService(Ci.nsIPrefService).getDefaultBranch(null);
+  var url = BROWSER_NEW_TAB_URL;
+  try {
+    url = prefs.getComplexValue("browser.startup.homepage",
+                                Ci.nsIPrefLocalizedString).data;
+    // If url is a pipe-delimited set of pages, just take the first one.
+    if (url.indexOf("|") != -1)
+      url = url.split("|")[0];
+  } catch(e) {
+    Components.utils.reportError("Couldn't get homepage pref: " + e);
+  }
+  content.location = url;
+}
+
+function BrowserFullScreen()
+{
+  window.fullScreen = !window.fullScreen;
+}
+
+function onFullScreen(event) {
+  FullScreen.toggle(event);
+}
+
+function onMozEnteredDomFullscreen(event) {
+  FullScreen.enterDomFullscreen(event);
+}
+
+function getWebNavigation()
+{
+  try {
+    return gBrowser.webNavigation;
+  } catch (e) {
+    return null;
+  }
+}
+
+function BrowserReloadWithFlags(reloadFlags) {
+  /* First, we'll try to use the session history object to reload so
+   * that framesets are handled properly. If we're in a special
+   * window (such as view-source) that has no session history, fall
+   * back on using the web navigation's reload method.
+   */
+
+  var webNav = getWebNavigation();
+  try {
+    var sh = webNav.sessionHistory;
+    if (sh)
+      webNav = sh.QueryInterface(nsIWebNavigation);
+  } catch (e) {
+  }
+
+  try {
+    webNav.reload(reloadFlags);
+  } catch (e) {
+  }
+}
+
+var PrintPreviewListener = {
+  _printPreviewTab: null,
+  _tabBeforePrintPreview: null,
+
+  getPrintPreviewBrowser: function () {
+    if (!this._printPreviewTab) {
+      this._tabBeforePrintPreview = gBrowser.selectedTab;
+      this._printPreviewTab = gBrowser.loadOneTab("about:blank",
+                                                  { inBackground: false });
+      gBrowser.selectedTab = this._printPreviewTab;
+    }
+    return gBrowser.getBrowserForTab(this._printPreviewTab);
+  },
+  getSourceBrowser: function () {
+    return this._tabBeforePrintPreview ?
+      this._tabBeforePrintPreview.linkedBrowser : gBrowser.selectedBrowser;
+  },
+  getNavToolbox: function () {
+    return gNavToolbox;
+  },
+  onEnter: function () {
+    gInPrintPreviewMode = true;
+    this._toggleAffectedChrome();
+  },
+  onExit: function () {
+    gBrowser.selectedTab = this._tabBeforePrintPreview;
+    this._tabBeforePrintPreview = null;
+    gInPrintPreviewMode = false;
+    this._toggleAffectedChrome();
+    gBrowser.removeTab(this._printPreviewTab);
+    this._printPreviewTab = null;
+  },
+  _toggleAffectedChrome: function () {
+    gNavToolbox.collapsed = gInPrintPreviewMode;
+
+    if (gInPrintPreviewMode)
+      this._hideChrome();
+    else
+      this._showChrome();
+
+    if (this._chromeState.sidebarOpen)
+      toggleSidebar(this._sidebarCommand);
+
+//@line 2783 "/build/src/mozilla-release/browser/base/content/browser.js"
+    updateAppButtonDisplay();
+//@line 2785 "/build/src/mozilla-release/browser/base/content/browser.js"
+  },
+  _hideChrome: function () {
+    this._chromeState = {};
+
+    var sidebar = document.getElementById("sidebar-box");
+    this._chromeState.sidebarOpen = !sidebar.hidden;
+    this._sidebarCommand = sidebar.getAttribute("sidebarcommand");
+
+    var notificationBox = gBrowser.getNotificationBox();
+    this._chromeState.notificationsOpen = !notificationBox.notificationsHidden;
+    notificationBox.notificationsHidden = true;
+
+    document.getElementById("sidebar").setAttribute("src", "about:blank");
+    var addonBar = document.getElementById("addon-bar");
+    this._chromeState.addonBarOpen = !addonBar.collapsed;
+    addonBar.collapsed = true;
+    gBrowser.updateWindowResizers();
+
+    this._chromeState.findOpen = gFindBarInitialized && !gFindBar.hidden;
+    if (gFindBarInitialized)
+      gFindBar.close();
+
+    this._chromeState.syncNotificationsOpen = false;
+    var syncNotifications = document.getElementById("sync-notifications");
+    if (syncNotifications) {
+      this._chromeState.syncNotificationsOpen = !syncNotifications.notificationsHidden;
+      syncNotifications.notificationsHidden = true;
+    }
+  },
+  _showChrome: function () {
+    if (this._chromeState.notificationsOpen)
+      gBrowser.getNotificationBox().notificationsHidden = false;
+
+    if (this._chromeState.addonBarOpen) {
+      document.getElementById("addon-bar").collapsed = false;
+      gBrowser.updateWindowResizers();
+    }
+
+    if (this._chromeState.findOpen)
+      gFindBar.open();
+
+    if (this._chromeState.syncNotificationsOpen)
+      document.getElementById("sync-notifications").notificationsHidden = false;
+  }
+}
+
+function getMarkupDocumentViewer()
+{
+  return gBrowser.markupDocumentViewer;
+}
+
+/**
+ * Content area tooltip.
+ * XXX - this must move into XBL binding/equiv! Do not want to pollute
+ *       browser.js with functionality that can be encapsulated into
+ *       browser widget. TEMPORARY!
+ *
+ * NOTE: Any changes to this routine need to be mirrored in DefaultTooltipTextProvider::GetNodeText()
+ *       (located in mozilla/embedding/browser/webBrowser/nsDocShellTreeOwner.cpp)
+ *       which performs the same function, but for embedded clients that
+ *       don't use a XUL/JS layer. It is important that the logic of
+ *       these two routines be kept more or less in sync.
+ *       (pinkerton)
+ **/
+function FillInHTMLTooltip(tipElement)
+{
+  var retVal = false;
+  // Don't show the tooltip if the tooltip node is a document or disconnected.
+  if (!tipElement.ownerDocument ||
+      (tipElement.ownerDocument.compareDocumentPosition(tipElement) & document.DOCUMENT_POSITION_DISCONNECTED))
+    return retVal;
+
+  const XLinkNS = "http://www.w3.org/1999/xlink";
+  const XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
+
+  var titleText = null;
+  var XLinkTitleText = null;
+  var SVGTitleText = null;
+  var lookingForSVGTitle = true;
+  var direction = tipElement.ownerDocument.dir;
+
+  // If the element is invalid per HTML5 Forms specifications and has no title,
+  // show the constraint validation error message.
+  if ((tipElement instanceof HTMLInputElement ||
+       tipElement instanceof HTMLTextAreaElement ||
+       tipElement instanceof HTMLSelectElement ||
+       tipElement instanceof HTMLButtonElement) &&
+      !tipElement.hasAttribute('title') &&
+      (!tipElement.form || !tipElement.form.noValidate)) {
+    // If the element is barred from constraint validation or valid,
+    // the validation message will be the empty string.
+    titleText = tipElement.validationMessage;
+  }
+
+  while (!titleText && !XLinkTitleText && !SVGTitleText && tipElement) {
+    if (tipElement.nodeType == Node.ELEMENT_NODE &&
+        tipElement.namespaceURI != XULNS) {
+      titleText = tipElement.getAttribute("title");
+      if ((tipElement instanceof HTMLAnchorElement ||
+           tipElement instanceof HTMLAreaElement ||
+           tipElement instanceof HTMLLinkElement ||
+           tipElement instanceof SVGAElement) && tipElement.href) {
+        XLinkTitleText = tipElement.getAttributeNS(XLinkNS, "title");
+      }
+      if (lookingForSVGTitle &&
+          (!(tipElement instanceof SVGElement) ||
+           tipElement.parentNode.nodeType == Node.DOCUMENT_NODE)) {
+        lookingForSVGTitle = false;
+      }
+      if (lookingForSVGTitle) {
+        let length = tipElement.childNodes.length;
+        for (let i = 0; i < length; i++) {
+          let childNode = tipElement.childNodes[i];
+          if (childNode instanceof SVGTitleElement) {
+            SVGTitleText = childNode.textContent;
+            break;
+          }
+        }
+      }
+      var defView = tipElement.ownerDocument.defaultView;
+      // XXX Work around bug 350679:
+      // "Tooltips can be fired in documents with no view".
+      if (!defView)
+        return retVal;
+      direction = defView.getComputedStyle(tipElement, "")
+        .getPropertyValue("direction");
+    }
+    tipElement = tipElement.parentNode;
+  }
+
+  var tipNode = document.getElementById("aHTMLTooltip");
+  tipNode.style.direction = direction;
+
+  [titleText, XLinkTitleText, SVGTitleText].forEach(function (t) {
+    if (t && /\S/.test(t)) {
+      // Make CRLF and CR render one line break each.
+      t = t.replace(/\r\n?/g, '\n');
+
+      tipNode.setAttribute("label", t);
+      retVal = true;
+    }
+  });
+  return retVal;
+}
+
+var browserDragAndDrop = {
+  canDropLink: function (aEvent) Services.droppedLinkHandler.canDropLink(aEvent, true),
+
+  dragOver: function (aEvent)
+  {
+    if (this.canDropLink(aEvent)) {
+      aEvent.preventDefault();
+    }
+  },
+
+  drop: function (aEvent, aName, aDisallowInherit) {
+    return Services.droppedLinkHandler.dropLink(aEvent, aName, aDisallowInherit);
+  }
+};
+
+var homeButtonObserver = {
+  onDrop: function (aEvent)
+    {
+      // disallow setting home pages that inherit the principal
+      let url = browserDragAndDrop.drop(aEvent, {}, true);
+      setTimeout(openHomeDialog, 0, url);
+    },
+
+  onDragOver: function (aEvent)
+    {
+      browserDragAndDrop.dragOver(aEvent);
+      aEvent.dropEffect = "link";
+    },
+  onDragExit: function (aEvent)
+    {
+    }
+}
+
+function openHomeDialog(aURL)
+{
+  var promptTitle = gNavigatorBundle.getString("droponhometitle");
+  var promptMsg   = gNavigatorBundle.getString("droponhomemsg");
+  var pressedVal  = Services.prompt.confirmEx(window, promptTitle, promptMsg,
+                          Services.prompt.STD_YES_NO_BUTTONS,
+                          null, null, null, null, {value:0});
+
+  if (pressedVal == 0) {
+    try {
+      var str = Components.classes["@mozilla.org/supports-string;1"]
+                          .createInstance(Components.interfaces.nsISupportsString);
+      str.data = aURL;
+      gPrefService.setComplexValue("browser.startup.homepage",
+                                   Components.interfaces.nsISupportsString, str);
+    } catch (ex) {
+      dump("Failed to set the home page.\n"+ex+"\n");
+    }
+  }
+}
+
+var bookmarksButtonObserver = {
+  onDrop: function (aEvent)
+  {
+    let name = { };
+    let url = browserDragAndDrop.drop(aEvent, name);
+    try {
+      PlacesUIUtils.showBookmarkDialog({ action: "add"
+                                       , type: "bookmark"
+                                       , uri: makeURI(url)
+                                       , title: name
+                                       , hiddenRows: [ "description"
+                                                     , "location"
+                                                     , "loadInSidebar"
+                                                     , "keyword" ]
+                                       }, window);
+    } catch(ex) { }
+  },
+
+  onDragOver: function (aEvent)
+  {
+    browserDragAndDrop.dragOver(aEvent);
+    aEvent.dropEffect = "link";
+  },
+
+  onDragExit: function (aEvent)
+  {
+  }
+}
+
+var newTabButtonObserver = {
+  onDragOver: function (aEvent)
+  {
+    browserDragAndDrop.dragOver(aEvent);
+  },
+
+  onDragExit: function (aEvent)
+  {
+  },
+
+  onDrop: function (aEvent)
+  {
+    let url = browserDragAndDrop.drop(aEvent, { });
+    var postData = {};
+    url = getShortcutOrURI(url, postData);
+    if (url) {
+      // allow third-party services to fixup this URL
+      openNewTabWith(url, null, postData.value, aEvent, true);
+    }
+  }
+}
+
+var newWindowButtonObserver = {
+  onDragOver: function (aEvent)
+  {
+    browserDragAndDrop.dragOver(aEvent);
+  },
+  onDragExit: function (aEvent)
+  {
+  },
+  onDrop: function (aEvent)
+  {
+    let url = browserDragAndDrop.drop(aEvent, { });
+    var postData = {};
+    url = getShortcutOrURI(url, postData);
+    if (url) {
+      // allow third-party services to fixup this URL
+      openNewWindowWith(url, null, postData.value, true);
+    }
+  }
+}
+
+var DownloadsButtonDNDObserver = {
+  onDragOver: function (aEvent)
+  {
+    var types = aEvent.dataTransfer.types;
+    if (types.contains("text/x-moz-url") ||
+        types.contains("text/uri-list") ||
+        types.contains("text/plain"))
+      aEvent.preventDefault();
+  },
+
+  onDragExit: function (aEvent)
+  {
+  },
+
+  onDrop: function (aEvent)
+  {
+    let name = { };
+    let url = browserDragAndDrop.drop(aEvent, name);
+    if (url)
+      saveURL(url, name, null, true, true);
+  }
+}
+
+const DOMLinkHandler = {
+  handleEvent: function (event) {
+    switch (event.type) {
+      case "DOMLinkAdded":
+        this.onLinkAdded(event);
+        break;
+    }
+  },
+  onLinkAdded: function (event) {
+    var link = event.originalTarget;
+    var rel = link.rel && link.rel.toLowerCase();
+    if (!link || !link.ownerDocument || !rel || !link.href)
+      return;
+
+    var feedAdded = false;
+    var iconAdded = false;
+    var searchAdded = false;
+    var relStrings = rel.split(/\s+/);
+    var rels = {};
+    for (let i = 0; i < relStrings.length; i++)
+      rels[relStrings[i]] = true;
+
+    for (let relVal in rels) {
+      switch (relVal) {
+        case "feed":
+        case "alternate":
+          if (!feedAdded) {
+            if (!rels.feed && rels.alternate && rels.stylesheet)
+              break;
+
+            if (isValidFeed(link, link.ownerDocument.nodePrincipal, rels.feed)) {
+              FeedHandler.addFeed(link, link.ownerDocument);
+              feedAdded = true;
+            }
+          }
+          break;
+        case "icon":
+          if (!iconAdded) {
+            if (!gPrefService.getBoolPref("browser.chrome.site_icons"))
+              break;
+
+            var targetDoc = link.ownerDocument;
+            var uri = makeURI(link.href, targetDoc.characterSet);
+
+            if (gBrowser.isFailedIcon(uri))
+              break;
+
+            // Verify that the load of this icon is legal.
+            // Some error or special pages can load their favicon.
+            // To be on the safe side, only allow chrome:// favicons.
+            var isAllowedPage = [
+              /^about:neterror\?/,
+              /^about:blocked\?/,
+              /^about:certerror\?/,
+              /^about:home$/,
+            ].some(function (re) re.test(targetDoc.documentURI));
+
+            if (!isAllowedPage || !uri.schemeIs("chrome")) {
+              var ssm = Cc["@mozilla.org/scriptsecuritymanager;1"].
+                        getService(Ci.nsIScriptSecurityManager);
+              try {
+                ssm.checkLoadURIWithPrincipal(targetDoc.nodePrincipal, uri,
+                                              Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
+              } catch(e) {
+                break;
+              }
+            }
+
+            try {
+              var contentPolicy = Cc["@mozilla.org/layout/content-policy;1"].
+                                  getService(Ci.nsIContentPolicy);
+            } catch(e) {
+              break; // Refuse to load if we can't do a security check.
+            }
+
+            // Security says okay, now ask content policy
+            if (contentPolicy.shouldLoad(Ci.nsIContentPolicy.TYPE_IMAGE,
+                                         uri, targetDoc.documentURIObject,
+                                         link, link.type, null)
+                                         != Ci.nsIContentPolicy.ACCEPT)
+              break;
+
+            var browserIndex = gBrowser.getBrowserIndexForDocument(targetDoc);
+            // no browser? no favicon.
+            if (browserIndex == -1)
+              break;
+
+            let tab = gBrowser.tabs[browserIndex];
+            gBrowser.setIcon(tab, link.href);
+            iconAdded = true;
+          }
+          break;
+        case "search":
+          if (!searchAdded) {
+            var type = link.type && link.type.toLowerCase();
+            type = type.replace(/^\s+|\s*(?:;.*)?$/g, "");
+
+            if (type == "application/opensearchdescription+xml" && link.title &&
+                /^(?:https?|ftp):/i.test(link.href) &&
+                !gPrivateBrowsingUI.privateBrowsingEnabled) {
+              var engine = { title: link.title, href: link.href };
+              BrowserSearch.addEngine(engine, link.ownerDocument);
+              searchAdded = true;
+            }
+          }
+          break;
+      }
+    }
+  }
+}
+
+const BrowserSearch = {
+  addEngine: function(engine, targetDoc) {
+    if (!this.searchBar)
+      return;
+
+    var browser = gBrowser.getBrowserForDocument(targetDoc);
+    // ignore search engines from subframes (see bug 479408)
+    if (!browser)
+      return;
+
+    // Check to see whether we've already added an engine with this title
+    if (browser.engines) {
+      if (browser.engines.some(function (e) e.title == engine.title))
+        return;
+    }
+
+    // Append the URI and an appropriate title to the browser data.
+    // Use documentURIObject in the check for shouldLoadFavIcon so that we
+    // do the right thing with about:-style error pages.  Bug 453442
+    var iconURL = null;
+    if (gBrowser.shouldLoadFavIcon(targetDoc.documentURIObject))
+      iconURL = targetDoc.documentURIObject.prePath + "/favicon.ico";
+
+    var hidden = false;
+    // If this engine (identified by title) is already in the list, add it
+    // to the list of hidden engines rather than to the main list.
+    // XXX This will need to be changed when engines are identified by URL;
+    // see bug 335102.
+    if (Services.search.getEngineByName(engine.title))
+      hidden = true;
+
+    var engines = (hidden ? browser.hiddenEngines : browser.engines) || [];
+
+    engines.push({ uri: engine.href,
+                   title: engine.title,
+                   icon: iconURL });
+
+    if (hidden)
+      browser.hiddenEngines = engines;
+    else
+      browser.engines = engines;
+  },
+
+  /**
+   * Gives focus to the search bar, if it is present on the toolbar, or loads
+   * the default engine's search form otherwise. For Mac, opens a new window
+   * or focuses an existing window, if necessary.
+   */
+  webSearch: function BrowserSearch_webSearch() {
+//@line 3260 "/build/src/mozilla-release/browser/base/content/browser.js"
+    var searchBar = this.searchBar;
+    if (searchBar && window.fullScreen)
+      FullScreen.mouseoverToggle(true);
+
+    if (isElementVisible(searchBar)) {
+      searchBar.select();
+      searchBar.focus();
+    } else {
+      openUILinkIn(Services.search.defaultEngine.searchForm, "current");
+    }
+  },
+
+  /**
+   * Loads a search results page, given a set of search terms. Uses the current
+   * engine if the search bar is visible, or the default engine otherwise.
+   *
+   * @param searchText
+   *        The search terms to use for the search.
+   *
+   * @param useNewTab
+   *        Boolean indicating whether or not the search should load in a new
+   *        tab.
+   *
+   * @param responseType [optional]
+   *        The MIME type that we'd like to receive in response
+   *        to this submission.  If null or the the response type is not supported
+   *        for the search engine, will fallback to "text/html".
+   */
+  loadSearch: function BrowserSearch_search(searchText, useNewTab, responseType) {
+    var engine;
+
+    // If the search bar is visible, use the current engine, otherwise, fall
+    // back to the default engine.
+    if (isElementVisible(this.searchBar))
+      engine = Services.search.currentEngine;
+    else
+      engine = Services.search.defaultEngine;
+
+    var submission = engine.getSubmission(searchText, responseType);
+
+    // If a response type was specified and getSubmission returned null,
+    // fallback to the default response type.
+    if (!submission && responseType)
+      submission = engine.getSubmission(searchText);
+
+    // getSubmission can return null if the engine doesn't have a URL
+    // with a text/html response type.  This is unlikely (since
+    // SearchService._addEngineToStore() should fail for such an engine),
+    // but let's be on the safe side.
+    if (!submission)
+      return;
+
+    let inBackground = Services.prefs.getBoolPref("browser.search.context.loadInBackground");
+    openLinkIn(submission.uri.spec,
+               useNewTab ? "tab" : "current",
+               { postData: submission.postData,
+                 inBackground: inBackground,
+                 relatedToCurrent: true });
+  },
+
+  /**
+   * Returns the search bar element if it is present in the toolbar, null otherwise.
+   */
+  get searchBar() {
+    return document.getElementById("searchbar");
+  },
+
+  loadAddEngines: function BrowserSearch_loadAddEngines() {
+    var newWindowPref = gPrefService.getIntPref("browser.link.open_newwindow");
+    var where = newWindowPref == 3 ? "tab" : "window";
+    var searchEnginesURL = formatURL("browser.search.searchEnginesURL", true);
+    openUILinkIn(searchEnginesURL, where);
+  }
+}
+
+function FillHistoryMenu(aParent) {
+  // Lazily add the hover listeners on first showing and never remove them
+  if (!aParent.hasStatusListener) {
+    // Show history item's uri in the status bar when hovering, and clear on exit
+    aParent.addEventListener("DOMMenuItemActive", function(aEvent) {
+      // Only the current page should have the checked attribute, so skip it
+      if (!aEvent.target.hasAttribute("checked"))
+        XULBrowserWindow.setOverLink(aEvent.target.getAttribute("uri"));
+    }, false);
+    aParent.addEventListener("DOMMenuItemInactive", function() {
+      XULBrowserWindow.setOverLink("");
+    }, false);
+
+    aParent.hasStatusListener = true;
+  }
+
+  // Remove old entries if any
+  var children = aParent.childNodes;
+  for (var i = children.length - 1; i >= 0; --i) {
+    if (children[i].hasAttribute("index"))
+      aParent.removeChild(children[i]);
+  }
+
+  var webNav = getWebNavigation();
+  var sessionHistory = webNav.sessionHistory;
+
+  var count = sessionHistory.count;
+  if (count <= 1) // don't display the popup for a single item
+    return false;
+
+  const MAX_HISTORY_MENU_ITEMS = 15;
+  var index = sessionHistory.index;
+  var half_length = Math.floor(MAX_HISTORY_MENU_ITEMS / 2);
+  var start = Math.max(index - half_length, 0);
+  var end = Math.min(start == 0 ? MAX_HISTORY_MENU_ITEMS : index + half_length + 1, count);
+  if (end == count)
+    start = Math.max(count - MAX_HISTORY_MENU_ITEMS, 0);
+
+  var tooltipBack = gNavigatorBundle.getString("tabHistory.goBack");
+  var tooltipCurrent = gNavigatorBundle.getString("tabHistory.current");
+  var tooltipForward = gNavigatorBundle.getString("tabHistory.goForward");
+
+  for (var j = end - 1; j >= start; j--) {
+    let item = document.createElement("menuitem");
+    let entry = sessionHistory.getEntryAtIndex(j, false);
+    let uri = entry.URI.spec;
+
+    item.setAttribute("uri", uri);
+    item.setAttribute("label", entry.title || uri);
+    item.setAttribute("index", j);
+
+    if (j != index) {
+      PlacesUtils.favicons.getFaviconURLForPage(entry.URI, function (aURI) {
+        if (aURI) {
+          let iconURL = PlacesUtils.favicons.getFaviconLinkForIcon(aURI).spec;
+          item.style.listStyleImage = "url(" + iconURL + ")";
+        }
+      });
+    }
+
+    if (j < index) {
+      item.className = "unified-nav-back menuitem-iconic menuitem-with-favicon";
+      item.setAttribute("tooltiptext", tooltipBack);
+    } else if (j == index) {
+      item.setAttribute("type", "radio");
+      item.setAttribute("checked", "true");
+      item.className = "unified-nav-current";
+      item.setAttribute("tooltiptext", tooltipCurrent);
+    } else {
+      item.className = "unified-nav-forward menuitem-iconic menuitem-with-favicon";
+      item.setAttribute("tooltiptext", tooltipForward);
+    }
+
+    aParent.appendChild(item);
+  }
+  return true;
+}
+
+function addToUrlbarHistory(aUrlToAdd) {
+  if (aUrlToAdd &&
+      aUrlToAdd.indexOf(" ") == -1 &&
+      !/[\x00-\x1F]/.test(aUrlToAdd))
+    PlacesUIUtils.markPageAsTyped(aUrlToAdd);
+}
+
+function toJavaScriptConsole()
+{
+  toOpenWindowByType("global:console", "chrome://global/content/console.xul");
+}
+
+function BrowserDownloadsUI()
+{
+  Cc["@mozilla.org/download-manager-ui;1"].
+  getService(Ci.nsIDownloadManagerUI).show(window);
+}
+
+function toOpenWindowByType(inType, uri, features)
+{
+  var topWindow = Services.wm.getMostRecentWindow(inType);
+
+  if (topWindow)
+    topWindow.focus();
+  else if (features)
+    window.open(uri, "_blank", features);
+  else
+    window.open(uri, "_blank", "chrome,extrachrome,menubar,resizable,scrollbars,status,toolbar");
+}
+
+function OpenBrowserWindow()
+{
+  var telemetryObj = {};
+  TelemetryStopwatch.start("FX_NEW_WINDOW_MS", telemetryObj);
+
+  function newDocumentShown(doc, topic, data) {
+    if (topic == "document-shown" &&
+        doc != document &&
+        doc.defaultView == win) {
+      Services.obs.removeObserver(newDocumentShown, "document-shown");
+      TelemetryStopwatch.finish("FX_NEW_WINDOW_MS", telemetryObj);
+    }
+  };
+  Services.obs.addObserver(newDocumentShown, "document-shown", false);
+
+  var charsetArg = new String();
+  var handler = Components.classes["@mozilla.org/browser/clh;1"]
+                          .getService(Components.interfaces.nsIBrowserHandler);
+  var defaultArgs = handler.defaultArgs;
+  var wintype = document.documentElement.getAttribute('windowtype');
+
+  // if and only if the current window is a browser window and it has a document with a character
+  // set, then extract the current charset menu setting from the current document and use it to
+  // initialize the new browser window...
+  var win;
+  if (window && (wintype == "navigator:browser") && window.content && window.content.document)
+  {
+    var DocCharset = window.content.document.characterSet;
+    charsetArg = "charset="+DocCharset;
+
+    //we should "inherit" the charset menu setting in a new window
+    win = window.openDialog("chrome://browser/content/", "_blank", "chrome,all,dialog=no", defaultArgs, charsetArg);
+  }
+  else // forget about the charset information.
+  {
+    win = window.openDialog("chrome://browser/content/", "_blank", "chrome,all,dialog=no", defaultArgs);
+  }
+
+  return win;
+}
+
+var gCustomizeSheet = false;
+// Returns a reference to the window in which the toolbar
+// customization document is loaded.
+function BrowserCustomizeToolbar()
+{
+  // Disable the toolbar context menu items
+  var menubar = document.getElementById("main-menubar");
+  for (var i = 0; i < menubar.childNodes.length; ++i)
+    menubar.childNodes[i].setAttribute("disabled", true);
+
+  var cmd = document.getElementById("cmd_CustomizeToolbars");
+  cmd.setAttribute("disabled", "true");
+
+  var splitter = document.getElementById("urlbar-search-splitter");
+  if (splitter)
+    splitter.parentNode.removeChild(splitter);
+
+  CombinedStopReload.uninit();
+
+  PlacesToolbarHelper.customizeStart();
+  BookmarksMenuButton.customizeStart();
+  DownloadsButton.customizeStart();
+
+  TabsInTitlebar.allowedBy("customizing-toolbars", false);
+
+  var customizeURL = "chrome://global/content/customizeToolbar.xul";
+  gCustomizeSheet = getBoolPref("toolbar.customization.usesheet", false);
+
+  if (gCustomizeSheet) {
+    var sheetFrame = document.getElementById("customizeToolbarSheetIFrame");
+    var panel = document.getElementById("customizeToolbarSheetPopup");
+    sheetFrame.hidden = false;
+    sheetFrame.toolbox = gNavToolbox;
+    sheetFrame.panel = panel;
+
+    // The document might not have been loaded yet, if this is the first time.
+    // If it is already loaded, reload it so that the onload initialization code
+    // re-runs.
+    if (sheetFrame.getAttribute("src") == customizeURL)
+      sheetFrame.contentWindow.location.reload()
+    else
+      sheetFrame.setAttribute("src", customizeURL);
+
+    // Open the panel, but make it invisible until the iframe has loaded so
+    // that the user doesn't see a white flash.
+    panel.style.visibility = "hidden";
+    gNavToolbox.addEventListener("beforecustomization", function onBeforeCustomization() {
+      gNavToolbox.removeEventListener("beforecustomization", onBeforeCustomization, false);
+      panel.style.removeProperty("visibility");
+    }, false);
+    panel.openPopup(gNavToolbox, "after_start", 0, 0);
+    return sheetFrame.contentWindow;
+  } else {
+    return window.openDialog(customizeURL,
+                             "CustomizeToolbar",
+                             "chrome,titlebar,toolbar,location,resizable,dependent",
+                             gNavToolbox);
+  }
+}
+
+function BrowserToolboxCustomizeDone(aToolboxChanged) {
+  if (gCustomizeSheet) {
+    document.getElementById("customizeToolbarSheetIFrame").hidden = true;
+    document.getElementById("customizeToolbarSheetPopup").hidePopup();
+  }
+
+  // Update global UI elements that may have been added or removed
+  if (aToolboxChanged) {
+    gURLBar = document.getElementById("urlbar");
+
+    gProxyFavIcon = document.getElementById("page-proxy-favicon");
+    gHomeButton.updateTooltip();
+    gIdentityHandler._cacheElements();
+    window.XULBrowserWindow.init();
+
+//@line 3560 "/build/src/mozilla-release/browser/base/content/browser.js"
+    updateEditUIVisibility();
+//@line 3562 "/build/src/mozilla-release/browser/base/content/browser.js"
+
+    // Hacky: update the PopupNotifications' object's reference to the iconBox,
+    // if it already exists, since it may have changed if the URL bar was
+    // added/removed.
+    if (!__lookupGetter__("PopupNotifications"))
+      PopupNotifications.iconBox = document.getElementById("notification-popup-box");
+  }
+
+  PlacesToolbarHelper.customizeDone();
+  BookmarksMenuButton.customizeDone();
+  DownloadsButton.customizeDone();
+
+  // The url bar splitter state is dependent on whether stop/reload
+  // and the location bar are combined, so we need this ordering
+  CombinedStopReload.init();
+  UpdateUrlbarSearchSplitterState();
+  setUrlAndSearchBarWidthForConditionalForwardButton();
+
+  // Update the urlbar
+  if (gURLBar) {
+    URLBarSetURI();
+    XULBrowserWindow.asyncUpdateUI();
+    PlacesStarButton.updateState();
+  }
+
+  TabsInTitlebar.allowedBy("customizing-toolbars", true);
+
+  // Re-enable parts of the UI we disabled during the dialog
+  var menubar = document.getElementById("main-menubar");
+  for (var i = 0; i < menubar.childNodes.length; ++i)
+    menubar.childNodes[i].setAttribute("disabled", false);
+  var cmd = document.getElementById("cmd_CustomizeToolbars");
+  cmd.removeAttribute("disabled");
+
+  // make sure to re-enable click-and-hold
+  if (!getBoolPref("ui.click_hold_context_menus", false))
+    SetClickAndHoldHandlers();
+
+  window.content.focus();
+}
+
+function BrowserToolboxCustomizeChange(aType) {
+  switch (aType) {
+    case "iconsize":
+    case "mode":
+      retrieveToolbarIconsizesFromTheme();
+      break;
+    default:
+      gHomeButton.updatePersonalToolbarStyle();
+      BookmarksMenuButton.customizeChange();
+      allTabs.readPref();
+  }
+}
+
+/**
+ * Allows themes to override the "iconsize" attribute on toolbars.
+ */
+function retrieveToolbarIconsizesFromTheme() {
+  function retrieveToolbarIconsize(aToolbar) {
+    if (aToolbar.localName != "toolbar")
+      return;
+
+    // The theme indicates that it wants to override the "iconsize" attribute
+    // by specifying a special value for the "counter-reset" property on the
+    // toolbar. A custom property cannot be used because getComputedStyle can
+    // only return the values of standard CSS properties.
+    let counterReset = getComputedStyle(aToolbar).counterReset;
+    if (counterReset == "smallicons 0")
+      aToolbar.setAttribute("iconsize", "small");
+    else if (counterReset == "largeicons 0")
+      aToolbar.setAttribute("iconsize", "large");
+  }
+
+  Array.forEach(gNavToolbox.childNodes, retrieveToolbarIconsize);
+  gNavToolbox.externalToolbars.forEach(retrieveToolbarIconsize);
+}
+
+/**
+ * Update the global flag that tracks whether or not any edit UI (the Edit menu,
+ * edit-related items in the context menu, and edit-related toolbar buttons
+ * is visible, then update the edit commands' enabled state accordingly.  We use
+ * this flag to skip updating the edit commands on focus or selection changes
+ * when no UI is visible to improve performance (including pageload performance,
+ * since focus changes when you load a new page).
+ *
+ * If UI is visible, we use goUpdateGlobalEditMenuItems to set the commands'
+ * enabled state so the UI will reflect it appropriately.
+ *
+ * If the UI isn't visible, we enable all edit commands so keyboard shortcuts
+ * still work and just lazily disable them as needed when the user presses a
+ * shortcut.
+ *
+ * This doesn't work on Mac, since Mac menus flash when users press their
+ * keyboard shortcuts, so edit UI is essentially always visible on the Mac,
+ * and we need to always update the edit commands.  Thus on Mac this function
+ * is a no op.
+ */
+function updateEditUIVisibility()
+{
+//@line 3662 "/build/src/mozilla-release/browser/base/content/browser.js"
+  let editMenuPopupState = document.getElementById("menu_EditPopup").state;
+  let contextMenuPopupState = document.getElementById("contentAreaContextMenu").state;
+  let placesContextMenuPopupState = document.getElementById("placesContext").state;
+//@line 3666 "/build/src/mozilla-release/browser/base/content/browser.js"
+  let appMenuPopupState = document.getElementById("appmenu-popup").state;
+//@line 3668 "/build/src/mozilla-release/browser/base/content/browser.js"
+
+  // The UI is visible if the Edit menu is opening or open, if the context menu
+  // is open, or if the toolbar has been customized to include the Cut, Copy,
+  // or Paste toolbar buttons.
+  gEditUIVisible = editMenuPopupState == "showing" ||
+                   editMenuPopupState == "open" ||
+                   contextMenuPopupState == "showing" ||
+                   contextMenuPopupState == "open" ||
+                   placesContextMenuPopupState == "showing" ||
+                   placesContextMenuPopupState == "open" ||
+//@line 3679 "/build/src/mozilla-release/browser/base/content/browser.js"
+                   appMenuPopupState == "showing" ||
+                   appMenuPopupState == "open" ||
+//@line 3682 "/build/src/mozilla-release/browser/base/content/browser.js"
+                   document.getElementById("cut-button") ||
+                   document.getElementById("copy-button") ||
+                   document.getElementById("paste-button") ? true : false;
+
+  // If UI is visible, update the edit commands' enabled state to reflect
+  // whether or not they are actually enabled for the current focus/selection.
+  if (gEditUIVisible)
+    goUpdateGlobalEditMenuItems();
+
+  // Otherwise, enable all commands, so that keyboard shortcuts still work,
+  // then lazily determine their actual enabled state when the user presses
+  // a keyboard shortcut.
+  else {
+    goSetCommandEnabled("cmd_undo", true);
+    goSetCommandEnabled("cmd_redo", true);
+    goSetCommandEnabled("cmd_cut", true);
+    goSetCommandEnabled("cmd_copy", true);
+    goSetCommandEnabled("cmd_paste", true);
+    goSetCommandEnabled("cmd_selectAll", true);
+    goSetCommandEnabled("cmd_delete", true);
+    goSetCommandEnabled("cmd_switchTextDirection", true);
+  }
+//@line 3705 "/build/src/mozilla-release/browser/base/content/browser.js"
+}
+
+/**
+ * Returns true if |aMimeType| is text-based, false otherwise.
+ *
+ * @param aMimeType
+ *        The MIME type to check.
+ *
+ * If adding types to this function, please also check the similar
+ * function in findbar.xml
+ */
+function mimeTypeIsTextBased(aMimeType)
+{
+  return /^text\/|\+xml$/.test(aMimeType) ||
+         aMimeType == "application/x-javascript" ||
+         aMimeType == "application/javascript" ||
+         aMimeType == "application/xml" ||
+         aMimeType == "mozilla.application/cached-xul";
+}
+
+var XULBrowserWindow = {
+  // Stored Status, Link and Loading values
+  status: "",
+  defaultStatus: "",
+  jsStatus: "",
+  jsDefaultStatus: "",
+  overLink: "",
+  startTime: 0,
+  statusText: "",
+  isBusy: false,
+  inContentWhitelist: ["about:addons", "about:permissions",
+                       "about:sync-progress", "about:preferences"],
+
+  QueryInterface: function (aIID) {
+    if (aIID.equals(Ci.nsIWebProgressListener) ||
+        aIID.equals(Ci.nsIWebProgressListener2) ||
+        aIID.equals(Ci.nsISupportsWeakReference) ||
+        aIID.equals(Ci.nsIXULBrowserWindow) ||
+        aIID.equals(Ci.nsISupports))
+      return this;
+    throw Cr.NS_NOINTERFACE;
+  },
+
+  get stopCommand () {
+    delete this.stopCommand;
+    return this.stopCommand = document.getElementById("Browser:Stop");
+  },
+  get reloadCommand () {
+    delete this.reloadCommand;
+    return this.reloadCommand = document.getElementById("Browser:Reload");
+  },
+  get statusTextField () {
+    delete this.statusTextField;
+    return this.statusTextField = document.getElementById("statusbar-display");
+  },
+  get isImage () {
+    delete this.isImage;
+    return this.isImage = document.getElementById("isImage");
+  },
+  get _uriFixup () {
+    delete this._uriFixup;
+    return this._uriFixup = Cc["@mozilla.org/docshell/urifixup;1"]
+                              .getService(Ci.nsIURIFixup);
+  },
+
+  init: function () {
+    this.throbberElement = document.getElementById("navigator-throbber");
+
+//@line 3776 "/build/src/mozilla-release/browser/base/content/browser.js"
+    // Initialize the security button's state and tooltip text.  Remember to reset
+    // _hostChanged, otherwise onSecurityChange will short circuit.
+    var securityUI = gBrowser.securityUI;
+    this._hostChanged = true;
+    this.onSecurityChange(null, null, securityUI.state);
+//@line 3782 "/build/src/mozilla-release/browser/base/content/browser.js"
+  },
+
+  destroy: function () {
+    // XXXjag to avoid leaks :-/, see bug 60729
+    delete this.throbberElement;
+    delete this.stopCommand;
+    delete this.reloadCommand;
+    delete this.statusTextField;
+    delete this.statusText;
+  },
+
+  setJSStatus: function (status) {
+    this.jsStatus = status;
+    this.updateStatusField();
+  },
+
+  setJSDefaultStatus: function (status) {
+    this.jsDefaultStatus = status;
+    this.updateStatusField();
+  },
+
+  setDefaultStatus: function (status) {
+    this.defaultStatus = status;
+    this.updateStatusField();
+  },
+
+  setOverLink: function (url, anchorElt) {
+    // Encode bidirectional formatting characters.
+    // (RFC 3987 sections 3.2 and 4.1 paragraph 6)
+    url = url.replace(/[\u200e\u200f\u202a\u202b\u202c\u202d\u202e]/g,
+                      encodeURIComponent);
+
+    if (gURLBar && gURLBar._mayTrimURLs /* corresponds to browser.urlbar.trimURLs */)
+      url = trimURL(url);
+
+    this.overLink = url;
+    LinkTargetDisplay.update();
+  },
+
+  updateStatusField: function () {
+    var text, type, types = ["overLink"];
+    if (this._busyUI)
+      types.push("status");
+    types.push("jsStatus", "jsDefaultStatus", "defaultStatus");
+    for (let i = 0; !text && i < types.length; i++) {
+      type = types[i];
+      text = this[type];
+    }
+
+    // check the current value so we don't trigger an attribute change
+    // and cause needless (slow!) UI updates
+    if (this.statusText != text) {
+      let field = this.statusTextField;
+      field.setAttribute("previoustype", field.getAttribute("type"));
+      field.setAttribute("type", type);
+      field.label = text;
+      field.setAttribute("crop", type == "overLink" ? "center" : "end");
+      this.statusText = text;
+    }
+  },
+
+  // Called before links are navigated to to allow us to retarget them if needed.
+  onBeforeLinkTraversal: function(originalTarget, linkURI, linkNode, isAppTab) {
+    // Don't modify non-default targets or targets that aren't in top-level app
+    // tab docshells (isAppTab will be false for app tab subframes).
+    if (originalTarget != "" || !isAppTab)
+      return originalTarget;
+
+    // External links from within app tabs should always open in new tabs
+    // instead of replacing the app tab's page (Bug 575561)
+    let linkHost;
+    let docHost;
+    try {
+      linkHost = linkURI.host;
+      docHost = linkNode.ownerDocument.documentURIObject.host;
+    } catch(e) {
+      // nsIURI.host can throw for non-nsStandardURL nsIURIs.
+      // If we fail to get either host, just return originalTarget.
+      return originalTarget;
+    }
+
+    if (docHost == linkHost)
+      return originalTarget;
+
+    // Special case: ignore "www" prefix if it is part of host string
+    let [longHost, shortHost] =
+      linkHost.length > docHost.length ? [linkHost, docHost] : [docHost, linkHost];
+    if (longHost == "www." + shortHost)
+      return originalTarget;
+
+    return "_blank";
+  },
+
+  onProgressChange: function (aWebProgress, aRequest,
+                              aCurSelfProgress, aMaxSelfProgress,
+                              aCurTotalProgress, aMaxTotalProgress) {
+    // Do nothing.
+  },
+
+  onProgressChange64: function (aWebProgress, aRequest,
+                                aCurSelfProgress, aMaxSelfProgress,
+                                aCurTotalProgress, aMaxTotalProgress) {
+    return this.onProgressChange(aWebProgress, aRequest,
+      aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress,
+      aMaxTotalProgress);
+  },
+
+  onStateChange: function (aWebProgress, aRequest, aStateFlags, aStatus) {
+    const nsIWebProgressListener = Ci.nsIWebProgressListener;
+    const nsIChannel = Ci.nsIChannel;
+
+    if (aStateFlags & nsIWebProgressListener.STATE_START &&
+        aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK) {
+
+      if (aRequest && aWebProgress.DOMWindow == content)
+        this.startDocumentLoad(aRequest);
+
+      this.isBusy = true;
+
+      if (!(aStateFlags & nsIWebProgressListener.STATE_RESTORING)) {
+        this._busyUI = true;
+
+        // Turn the throbber on.
+        if (this.throbberElement)
+          this.throbberElement.setAttribute("busy", "true");
+
+        // XXX: This needs to be based on window activity...
+        this.stopCommand.removeAttribute("disabled");
+        CombinedStopReload.switchToStop();
+      }
+    }
+    else if (aStateFlags & nsIWebProgressListener.STATE_STOP) {
+      if (aStateFlags & nsIWebProgressListener.STATE_IS_NETWORK &&
+          aWebProgress.DOMWindow == content &&
+          aRequest)
+        this.endDocumentLoad(aRequest, aStatus);
+
+      // This (thanks to the filter) is a network stop or the last
+      // request stop outside of loading the document, stop throbbers
+      // and progress bars and such
+      if (aRequest) {
+        let msg = "";
+        let location;
+        // Get the URI either from a channel or a pseudo-object
+        if (aRequest instanceof nsIChannel || "URI" in aRequest) {
+          location = aRequest.URI;
+
+          // For keyword URIs clear the user typed value since they will be changed into real URIs
+          if (location.scheme == "keyword" && aWebProgress.DOMWindow == content)
+            gBrowser.userTypedValue = null;
+
+          if (location.spec != "about:blank") {
+            switch (aStatus) {
+              case Components.results.NS_ERROR_NET_TIMEOUT:
+                msg = gNavigatorBundle.getString("nv_timeout");
+                break;
+            }
+          }
+        }
+
+        this.status = "";
+        this.setDefaultStatus(msg);
+
+        // Disable menu entries for images, enable otherwise
+        if (content.document && mimeTypeIsTextBased(content.document.contentType))
+          this.isImage.removeAttribute('disabled');
+        else
+          this.isImage.setAttribute('disabled', 'true');
+      }
+
+      this.isBusy = false;
+
+      if (this._busyUI) {
+        this._busyUI = false;
+
+        // Turn the throbber off.
+        if (this.throbberElement)
+          this.throbberElement.removeAttribute("busy");
+
+        this.stopCommand.setAttribute("disabled", "true");
+        CombinedStopReload.switchToReload(aRequest instanceof Ci.nsIRequest);
+      }
+    }
+  },
+
+  onLocationChange: function (aWebProgress, aRequest, aLocationURI, aFlags) {
+    var location = aLocationURI ? aLocationURI.spec : "";
+    this._hostChanged = true;
+
+    // Hide the form invalid popup.
+    if (gFormSubmitObserver.panelIsOpen()) {
+      gFormSubmitObserver.panel.hidePopup();
+    }
+
+    if (document.tooltipNode) {
+      // Optimise for the common case
+      if (aWebProgress.DOMWindow == content) {
+        document.getElementById("aHTMLTooltip").hidePopup();
+        document.tooltipNode = null;
+      }
+      else {
+        for (let tooltipWindow =
+               document.tooltipNode.ownerDocument.defaultView;
+             tooltipWindow != tooltipWindow.parent;
+             tooltipWindow = tooltipWindow.parent) {
+          if (tooltipWindow == aWebProgress.DOMWindow) {
+            document.getElementById("aHTMLTooltip").hidePopup();
+            document.tooltipNode = null;
+            break;
+          }
+        }
+      }
+    }
+
+    // This code here does not compare uris exactly when determining
+    // whether or not the message should be hidden since the message
+    // may be prematurely hidden when an install is invoked by a click
+    // on a link that looks like this:
+    //
+    // <a href="#" onclick="return install();">Install Foo</a>
+    //
+    // - which fires a onLocationChange message to uri + '#'...
+    var selectedBrowser = gBrowser.selectedBrowser;
+    if (selectedBrowser.lastURI) {
+      let oldSpec = selectedBrowser.lastURI.spec;
+      let oldIndexOfHash = oldSpec.indexOf("#");
+      if (oldIndexOfHash != -1)
+        oldSpec = oldSpec.substr(0, oldIndexOfHash);
+      let newSpec = location;
+      let newIndexOfHash = newSpec.indexOf("#");
+      if (newIndexOfHash != -1)
+        newSpec = newSpec.substr(0, newSpec.indexOf("#"));
+      if (newSpec != oldSpec) {
+        // Remove all the notifications, except for those which want to
+        // persist across the first location change.
+        let nBox = gBrowser.getNotificationBox(selectedBrowser);
+        nBox.removeTransientNotifications();
+
+        // Only need to call locationChange if the PopupNotifications object
+        // for this window has already been initialized (i.e. its getter no
+        // longer exists)
+        if (!__lookupGetter__("PopupNotifications"))
+          PopupNotifications.locationChange();
+      }
+    }
+
+    // Disable menu entries for images, enable otherwise
+    if (content.document && mimeTypeIsTextBased(content.document.contentType))
+      this.isImage.removeAttribute('disabled');
+    else
+      this.isImage.setAttribute('disabled', 'true');
+
+    this.hideOverLinkImmediately = true;
+    this.setOverLink("", null);
+    this.hideOverLinkImmediately = false;
+
+    // We should probably not do this if the value has changed since the user
+    // searched
+    // Update urlbar only if a new page was loaded on the primary content area
+    // Do not update urlbar if there was a subframe navigation
+
+    var browser = gBrowser.selectedBrowser;
+    if (aWebProgress.DOMWindow == content) {
+      if ((location == "about:blank" && !content.opener) ||
+          location == "") {  // Second condition is for new tabs, otherwise
+                             // reload function is enabled until tab is refreshed.
+        this.reloadCommand.setAttribute("disabled", "true");
+      } else {
+        this.reloadCommand.removeAttribute("disabled");
+      }
+
+      if (gURLBar) {
+        // Strip off "wyciwyg://" and passwords for the location bar
+        let uri = aLocationURI;
+        try {
+          uri = this._uriFixup.createExposableURI(uri);
+        } catch (e) {}
+        URLBarSetURI(uri);
+
+        // Update starring UI
+        PlacesStarButton.updateState();
+      }
+
+      // Show or hide browser chrome based on the whitelist
+      if (this.hideChromeForLocation(location)) {
+        document.documentElement.setAttribute("disablechrome", "true");
+      } else {
+        let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
+        if (ss.getTabValue(gBrowser.selectedTab, "appOrigin"))
+          document.documentElement.setAttribute("disablechrome", "true");
+        else
+          document.documentElement.removeAttribute("disablechrome");
+      }
+
+      // Disable find commands in documents that ask for them to be disabled.
+      let disableFind = false;
+      if (aLocationURI &&
+          (aLocationURI.schemeIs("about") || aLocationURI.schemeIs("chrome"))) {
+        let docElt = content.document.documentElement;
+        disableFind = docElt && docElt.getAttribute("disablefastfind") == "true";
+      }
+      let findCommands = [document.getElementById("cmd_find"),
+                          document.getElementById("cmd_findAgain"),
+                          document.getElementById("cmd_findPrevious")];
+      findCommands.forEach(function (elt) {
+        if (disableFind)
+          elt.setAttribute("disabled", "true");
+        else
+          elt.removeAttribute("disabled");
+      });
+
+      if (gFindBarInitialized) {
+        if (gFindBar.findMode != gFindBar.FIND_NORMAL) {
+          // Close the Find toolbar if we're in old-style TAF mode
+          gFindBar.close();
+        }
+
+        // fix bug 253793 - turn off highlight when page changes
+        gFindBar.getElement("highlight").checked = false;
+      }
+    }
+    UpdateBackForwardCommands(gBrowser.webNavigation);
+
+    // See bug 358202, when tabs are switched during a drag operation,
+    // timers don't fire on windows (bug 203573)
+    if (aRequest)
+      setTimeout(function () { XULBrowserWindow.asyncUpdateUI(); }, 0);
+    else
+      this.asyncUpdateUI();
+  },
+
+  asyncUpdateUI: function () {
+    FeedHandler.updateFeeds();
+  },
+
+  hideChromeForLocation: function(aLocation) {
+    aLocation = aLocation.toLowerCase();
+    return this.inContentWhitelist.some(function(aSpec) {
+      return aSpec == aLocation;
+    });
+  },
+
+  onStatusChange: function (aWebProgress, aRequest, aStatus, aMessage) {
+    this.status = aMessage;
+    this.updateStatusField();
+  },
+
+  // Properties used to cache security state used to update the UI
+  _state: null,
+  _hostChanged: false, // onLocationChange will flip this bit
+
+  onSecurityChange: function (aWebProgress, aRequest, aState) {
+    // Don't need to do anything if the data we use to update the UI hasn't
+    // changed
+    if (this._state == aState &&
+        !this._hostChanged) {
+//@line 4149 "/build/src/mozilla-release/browser/base/content/browser.js"
+      return;
+    }
+    this._state = aState;
+
+//@line 4160 "/build/src/mozilla-release/browser/base/content/browser.js"
+
+    this._hostChanged = false;
+
+    // aState is defined as a bitmask that may be extended in the future.
+    // We filter out any unknown bits before testing for known values.
+    const wpl = Components.interfaces.nsIWebProgressListener;
+    const wpl_security_bits = wpl.STATE_IS_SECURE |
+                              wpl.STATE_IS_BROKEN |
+                              wpl.STATE_IS_INSECURE |
+                              wpl.STATE_SECURE_HIGH |
+                              wpl.STATE_SECURE_MED |
+                              wpl.STATE_SECURE_LOW;
+    var level;
+
+    switch (this._state & wpl_security_bits) {
+      case wpl.STATE_IS_SECURE | wpl.STATE_SECURE_HIGH:
+        level = "high";
+        break;
+      case wpl.STATE_IS_SECURE | wpl.STATE_SECURE_MED:
+      case wpl.STATE_IS_SECURE | wpl.STATE_SECURE_LOW:
+        level = "low";
+        break;
+      case wpl.STATE_IS_BROKEN:
+        level = "broken";
+        break;
+    }
+
+    if (level) {
+      // We don't style the Location Bar based on the the 'level' attribute
+      // anymore, but still set it for third-party themes.
+      if (gURLBar)
+        gURLBar.setAttribute("level", level);
+    } else {
+      if (gURLBar)
+        gURLBar.removeAttribute("level");
+    }
+
+    // Don't pass in the actual location object, since it can cause us to
+    // hold on to the window object too long.  Just pass in the fields we
+    // care about. (bug 424829)
+    var location = gBrowser.contentWindow.location;
+    var locationObj = {};
+    try {
+      // about:blank can be used by webpages so pretend it is http
+      locationObj.protocol = location == "about:blank" ? "http:" : location.protocol;
+      locationObj.host = location.host;
+      locationObj.hostname = location.hostname;
+      locationObj.port = location.port;
+    } catch (ex) {
+      // Can sometimes throw if the URL being visited has no host/hostname,
+      // e.g. about:blank. The _state for these pages means we won't need these
+      // properties anyways, though.
+    }
+    gIdentityHandler.checkIdentity(this._state, locationObj);
+  },
+
+  // simulate all change notifications after switching tabs
+  onUpdateCurrentBrowser: function XWB_onUpdateCurrentBrowser(aStateFlags, aStatus, aMessage, aTotalProgress) {
+    if (FullZoom.updateBackgroundTabs)
+      FullZoom.onLocationChange(gBrowser.currentURI, true);
+    var nsIWebProgressListener = Components.interfaces.nsIWebProgressListener;
+    var loadingDone = aStateFlags & nsIWebProgressListener.STATE_STOP;
+    // use a pseudo-object instead of a (potentially nonexistent) channel for getting
+    // a correct error message - and make sure that the UI is always either in
+    // loading (STATE_START) or done (STATE_STOP) mode
+    this.onStateChange(
+      gBrowser.webProgress,
+      { URI: gBrowser.currentURI },
+      loadingDone ? nsIWebProgressListener.STATE_STOP : nsIWebProgressListener.STATE_START,
+      aStatus
+    );
+    // status message and progress value are undefined if we're done with loading
+    if (loadingDone)
+      return;
+    this.onStatusChange(gBrowser.webProgress, null, 0, aMessage);
+  },
+
+  startDocumentLoad: function XWB_startDocumentLoad(aRequest) {
+    // clear out feed data
+    gBrowser.selectedBrowser.feeds = null;
+
+    // clear out search-engine data
+    gBrowser.selectedBrowser.engines = null;
+
+    var uri = aRequest.QueryInterface(Ci.nsIChannel).URI;
+    try {
+      Services.obs.notifyObservers(content, "StartDocumentLoad", uri.spec);
+    } catch (e) {
+    }
+  },
+
+  endDocumentLoad: function XWB_endDocumentLoad(aRequest, aStatus) {
+    var urlStr = aRequest.QueryInterface(Ci.nsIChannel).originalURI.spec;
+
+    var notification = Components.isSuccessCode(aStatus) ? "EndDocumentLoad" : "FailDocumentLoad";
+    try {
+      Services.obs.notifyObservers(content, notification, urlStr);
+    } catch (e) {
+    }
+  }
+};
+
+var LinkTargetDisplay = {
+  get DELAY_SHOW() {
+     delete this.DELAY_SHOW;
+     return this.DELAY_SHOW = Services.prefs.getIntPref("browser.overlink-delay");
+  },
+
+  DELAY_HIDE: 250,
+  _timer: 0,
+
+  get _isVisible () XULBrowserWindow.statusTextField.label != "",
+
+  update: function () {
+    clearTimeout(this._timer);
+    window.removeEventListener("mousemove", this, true);
+
+    if (!XULBrowserWindow.overLink) {
+      if (XULBrowserWindow.hideOverLinkImmediately)
+        this._hide();
+      else
+        this._timer = setTimeout(this._hide.bind(this), this.DELAY_HIDE);
+      return;
+    }
+
+    if (this._isVisible) {
+      XULBrowserWindow.updateStatusField();
+    } else {
+      // Let the display appear when the mouse doesn't move within the delay
+      this._showDelayed();
+      window.addEventListener("mousemove", this, true);
+    }
+  },
+
+  handleEvent: function (event) {
+    switch (event.type) {
+      case "mousemove":
+        // Restart the delay since the mouse was moved
+        clearTimeout(this._timer);
+        this._showDelayed();
+        break;
+    }
+  },
+
+  _showDelayed: function () {
+    this._timer = setTimeout(function (self) {
+      XULBrowserWindow.updateStatusField();
+      window.removeEventListener("mousemove", self, true);
+    }, this.DELAY_SHOW, this);
+  },
+
+  _hide: function () {
+    clearTimeout(this._timer);
+
+    XULBrowserWindow.updateStatusField();
+  }
+};
+
+var CombinedStopReload = {
+  init: function () {
+    if (this._initialized)
+      return;
+
+    var urlbar = document.getElementById("urlbar-container");
+    var reload = document.getElementById("reload-button");
+    var stop = document.getElementById("stop-button");
+
+    if (urlbar) {
+      if (urlbar.parentNode.getAttribute("mode") != "icons" ||
+          !reload || urlbar.nextSibling != reload ||
+          !stop || reload.nextSibling != stop)
+        urlbar.removeAttribute("combined");
+      else {
+        urlbar.setAttribute("combined", "true");
+        reload = document.getElementById("urlbar-reload-button");
+        stop = document.getElementById("urlbar-stop-button");
+      }
+    }
+    if (!stop || !reload || reload.nextSibling != stop)
+      return;
+
+    this._initialized = true;
+    if (XULBrowserWindow.stopCommand.getAttribute("disabled") != "true")
+      reload.setAttribute("displaystop", "true");
+    stop.addEventListener("click", this, false);
+    this.reload = reload;
+    this.stop = stop;
+  },
+
+  uninit: function () {
+    if (!this._initialized)
+      return;
+
+    this._cancelTransition();
+    this._initialized = false;
+    this.stop.removeEventListener("click", this, false);
+    this.reload = null;
+    this.stop = null;
+  },
+
+  handleEvent: function (event) {
+    // the only event we listen to is "click" on the stop button
+    if (event.button == 0 &&
+        !this.stop.disabled)
+      this._stopClicked = true;
+  },
+
+  switchToStop: function () {
+    if (!this._initialized)
+      return;
+
+    this._cancelTransition();
+    this.reload.setAttribute("displaystop", "true");
+  },
+
+  switchToReload: function (aDelay) {
+    if (!this._initialized)
+      return;
+
+    this.reload.removeAttribute("displaystop");
+
+    if (!aDelay || this._stopClicked) {
+      this._stopClicked = false;
+      this._cancelTransition();
+      this.reload.disabled = XULBrowserWindow.reloadCommand
+                                             .getAttribute("disabled") == "true";
+      return;
+    }
+
+    if (this._timer)
+      return;
+
+    // Temporarily disable the reload button to prevent the user from
+    // accidentally reloading the page when intending to click the stop button
+    this.reload.disabled = true;
+    this._timer = setTimeout(function (self) {
+      self._timer = 0;
+      self.reload.disabled = XULBrowserWindow.reloadCommand
+                                             .getAttribute("disabled") == "true";
+    }, 650, this);
+  },
+
+  _cancelTransition: function () {
+    if (this._timer) {
+      clearTimeout(this._timer);
+      this._timer = 0;
+    }
+  }
+};
+
+var TabsProgressListener = {
+  onStateChange: function (aBrowser, aWebProgress, aRequest, aStateFlags, aStatus) {
+//@line 4420 "/build/src/mozilla-release/browser/base/content/browser.js"
+
+    // Attach a listener to watch for "click" events bubbling up from error
+    // pages and other similar page. This lets us fix bugs like 401575 which
+    // require error page UI to do privileged things, without letting error
+    // pages have any privilege themselves.
+    // We can't look for this during onLocationChange since at that point the
+    // document URI is not yet the about:-uri of the error page.
+
+    if (aStateFlags & Ci.nsIWebProgressListener.STATE_STOP &&
+        Components.isSuccessCode(aStatus) &&
+        /^about:/.test(aWebProgress.DOMWindow.document.documentURI)) {
+      aBrowser.addEventListener("click", BrowserOnClick, false);
+      aBrowser.addEventListener("pagehide", function onPageHide(event) {
+        if (event.target.defaultView.frameElement)
+          return;
+        aBrowser.removeEventListener("click", BrowserOnClick, false);
+        aBrowser.removeEventListener("pagehide", onPageHide, true);
+      }, true);
+
+      // We also want to make changes to page UI for unprivileged about pages.
+      BrowserOnAboutPageLoad(aWebProgress.DOMWindow.document);
+    }
+  },
+
+  onLocationChange: function (aBrowser, aWebProgress, aRequest, aLocationURI,
+                              aFlags) {
+    // Filter out any sub-frame loads
+    if (aBrowser.contentWindow == aWebProgress.DOMWindow) {
+      // Filter out any onLocationChanges triggered by anchor navigation
+      // or history.push/pop/replaceState.
+      if (aRequest) {
+        // Initialize the click-to-play state.
+        aBrowser._clickToPlayDoorhangerShown = false;
+        aBrowser._clickToPlayPluginsActivated = false;
+      }
+      FullZoom.onLocationChange(aLocationURI, false, aBrowser);
+    }
+  },
+
+  onRefreshAttempted: function (aBrowser, aWebProgress, aURI, aDelay, aSameURI) {
+    if (gPrefService.getBoolPref("accessibility.blockautorefresh")) {
+      let brandBundle = document.getElementById("bundle_brand");
+      let brandShortName = brandBundle.getString("brandShortName");
+      let refreshButtonText =
+        gNavigatorBundle.getString("refreshBlocked.goButton");
+      let refreshButtonAccesskey =
+        gNavigatorBundle.getString("refreshBlocked.goButton.accesskey");
+      let message =
+        gNavigatorBundle.getFormattedString(aSameURI ? "refreshBlocked.refreshLabel"
+                                                     : "refreshBlocked.redirectLabel",
+                                            [brandShortName]);
+      let docShell = aWebProgress.DOMWindow
+                                 .QueryInterface(Ci.nsIInterfaceRequestor)
+                                 .getInterface(Ci.nsIWebNavigation)
+                                 .QueryInterface(Ci.nsIDocShell);
+      let notificationBox = gBrowser.getNotificationBox(aBrowser);
+      let notification = notificationBox.getNotificationWithValue("refresh-blocked");
+      if (notification) {
+        notification.label = message;
+        notification.refreshURI = aURI;
+        notification.delay = aDelay;
+        notification.docShell = docShell;
+      } else {
+        let buttons = [{
+          label: refreshButtonText,
+          accessKey: refreshButtonAccesskey,
+          callback: function (aNotification, aButton) {
+            var refreshURI = aNotification.docShell
+                                          .QueryInterface(Ci.nsIRefreshURI);
+            refreshURI.forceRefreshURI(aNotification.refreshURI,
+                                       aNotification.delay, true);
+          }
+        }];
+        notification =
+          notificationBox.appendNotification(message, "refresh-blocked",
+                                             "chrome://browser/skin/Info.png",
+                                             notificationBox.PRIORITY_INFO_MEDIUM,
+                                             buttons);
+        notification.refreshURI = aURI;
+        notification.delay = aDelay;
+        notification.docShell = docShell;
+      }
+      return false;
+    }
+    return true;
+  }
+}
+
+function nsBrowserAccess() { }
+
+nsBrowserAccess.prototype = {
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserDOMWindow, Ci.nsISupports]),
+
+  openURI: function (aURI, aOpener, aWhere, aContext) {
+    var newWindow = null;
+    var isExternal = (aContext == Ci.nsIBrowserDOMWindow.OPEN_EXTERNAL);
+
+    if (isExternal && aURI && aURI.schemeIs("chrome")) {
+      dump("use -chrome command-line option to load external chrome urls\n");
+      return null;
+    }
+
+    if (aWhere == Ci.nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW) {
+      if (isExternal &&
+          gPrefService.prefHasUserValue("browser.link.open_newwindow.override.external"))
+        aWhere = gPrefService.getIntPref("browser.link.open_newwindow.override.external");
+      else
+        aWhere = gPrefService.getIntPref("browser.link.open_newwindow");
+    }
+    switch (aWhere) {
+      case Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW :
+        // FIXME: Bug 408379. So how come this doesn't send the
+        // referrer like the other loads do?
+        var url = aURI ? aURI.spec : BROWSER_NEW_TAB_URL;
+        // Pass all params to openDialog to ensure that "url" isn't passed through
+        // loadOneOrMoreURIs, which splits based on "|"
+        newWindow = openDialog(getBrowserURL(), "_blank", "all,dialog=no", url, null, null, null);
+        break;
+      case Ci.nsIBrowserDOMWindow.OPEN_NEWTAB :
+        let win, needToFocusWin;
+
+        // try the current window.  if we're in a popup, fall back on the most recent browser window
+        if (window.toolbar.visible)
+          win = window;
+        else {
+          win = Cc["@mozilla.org/browser/browserglue;1"]
+                  .getService(Ci.nsIBrowserGlue)
+                  .getMostRecentBrowserWindow();
+          needToFocusWin = true;
+        }
+
+        if (!win) {
+          // we couldn't find a suitable window, a new one needs to be opened.
+          return null;
+        }
+
+        if (isExternal && (!aURI || aURI.spec == "about:blank")) {
+          win.BrowserOpenTab(); // this also focuses the location bar
+          win.focus();
+          newWindow = win.content;
+          break;
+        }
+
+        let loadInBackground = gPrefService.getBoolPref("browser.tabs.loadDivertedInBackground");
+        let referrer = aOpener ? makeURI(aOpener.location.href) : null;
+
+        let tab = win.gBrowser.loadOneTab(aURI ? aURI.spec : BROWSER_NEW_TAB_URL, {
+                                          referrerURI: referrer,
+                                          fromExternal: isExternal,
+                                          inBackground: loadInBackground});
+        let browser = win.gBrowser.getBrowserForTab(tab);
+
+        newWindow = browser.contentWindow;
+        if (needToFocusWin || (!loadInBackground && isExternal))
+          newWindow.focus();
+        break;
+      default : // OPEN_CURRENTWINDOW or an illegal value
+        newWindow = content;
+        if (aURI) {
+          let referrer = aOpener ? makeURI(aOpener.location.href) : null;
+          let loadflags = isExternal ?
+                            Ci.nsIWebNavigation.LOAD_FLAGS_FROM_EXTERNAL :
+                            Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
+          gBrowser.loadURIWithFlags(aURI.spec, loadflags, referrer, null, null);
+        }
+        if (!gPrefService.getBoolPref("browser.tabs.loadDivertedInBackground"))
+          content.focus();
+    }
+    return newWindow;
+  },
+
+  isTabContentWindow: function (aWindow) {
+    return gBrowser.browsers.some(function (browser) browser.contentWindow == aWindow);
+  }
+}
+
+function onViewToolbarsPopupShowing(aEvent, aInsertPoint) {
+  var popup = aEvent.target;
+  if (popup != aEvent.currentTarget)
+    return;
+
+  // Empty the menu
+  for (var i = popup.childNodes.length-1; i >= 0; --i) {
+    var deadItem = popup.childNodes[i];
+    if (deadItem.hasAttribute("toolbarId"))
+      popup.removeChild(deadItem);
+  }
+
+  var firstMenuItem = aInsertPoint || popup.firstChild;
+
+  let toolbarNodes = Array.slice(gNavToolbox.childNodes);
+  toolbarNodes.push(document.getElementById("addon-bar"));
+
+  toolbarNodes.forEach(function(toolbar) {
+    var toolbarName = toolbar.getAttribute("toolbarname");
+    if (toolbarName) {
+      let menuItem = document.createElement("menuitem");
+      let hidingAttribute = toolbar.getAttribute("type") == "menubar" ?
+                            "autohide" : "collapsed";
+      menuItem.setAttribute("id", "toggle_" + toolbar.id);
+      menuItem.setAttribute("toolbarId", toolbar.id);
+      menuItem.setAttribute("type", "checkbox");
+      menuItem.setAttribute("label", toolbarName);
+      menuItem.setAttribute("checked", toolbar.getAttribute(hidingAttribute) != "true");
+      if (popup.id != "appmenu_customizeMenu")
+        menuItem.setAttribute("accesskey", toolbar.getAttribute("accesskey"));
+      if (popup.id != "toolbar-context-menu")
+        menuItem.setAttribute("key", toolbar.getAttribute("key"));
+
+      popup.insertBefore(menuItem, firstMenuItem);
+
+      menuItem.addEventListener("command", onViewToolbarCommand, false);
+    }
+  }, this);
+}
+
+function onViewToolbarCommand(aEvent) {
+  var toolbarId = aEvent.originalTarget.getAttribute("toolbarId");
+  var toolbar = document.getElementById(toolbarId);
+  var isVisible = aEvent.originalTarget.getAttribute("checked") == "true";
+  setToolbarVisibility(toolbar, isVisible);
+}
+
+function setToolbarVisibility(toolbar, isVisible) {
+  var hidingAttribute = toolbar.getAttribute("type") == "menubar" ?
+                        "autohide" : "collapsed";
+
+  toolbar.setAttribute(hidingAttribute, !isVisible);
+  document.persist(toolbar.id, hidingAttribute);
+
+  PlacesToolbarHelper.init();
+  BookmarksMenuButton.updatePosition();
+  gBrowser.updateWindowResizers();
+
+//@line 4655 "/build/src/mozilla-release/browser/base/content/browser.js"
+  updateAppButtonDisplay();
+//@line 4657 "/build/src/mozilla-release/browser/base/content/browser.js"
+}
+
+var TabsOnTop = {
+  init: function TabsOnTop_init() {
+    Services.prefs.addObserver(this._prefName, this, false);
+
+    // Only show the toggle UI if the user disabled tabs on top.
+    if (Services.prefs.getBoolPref(this._prefName)) {
+      for (let item of document.querySelectorAll("menuitem[command=cmd_ToggleTabsOnTop]"))
+        item.parentNode.removeChild(item);
+    }
+  },
+
+  uninit: function TabsOnTop_uninit() {
+    Services.prefs.removeObserver(this._prefName, this);
+  },
+
+  toggle: function () {
+    this.enabled = !Services.prefs.getBoolPref(this._prefName);
+  },
+
+  syncUI: function () {
+    let userEnabled = Services.prefs.getBoolPref(this._prefName);
+    let enabled = userEnabled && gBrowser.tabContainer.visible;
+
+    document.getElementById("cmd_ToggleTabsOnTop")
+            .setAttribute("checked", userEnabled);
+
+    document.documentElement.setAttribute("tabsontop", enabled);
+    document.getElementById("navigator-toolbox").setAttribute("tabsontop", enabled);
+    document.getElementById("TabsToolbar").setAttribute("tabsontop", enabled);
+    document.getElementById("nav-bar").setAttribute("tabsontop", enabled);
+    gBrowser.tabContainer.setAttribute("tabsontop", enabled);
+    TabsInTitlebar.allowedBy("tabs-on-top", enabled);
+  },
+
+  get enabled () {
+    return gNavToolbox.getAttribute("tabsontop") == "true";
+  },
+
+  set enabled (val) {
+    Services.prefs.setBoolPref(this._prefName, !!val);
+    return val;
+  },
+
+  observe: function (subject, topic, data) {
+    if (topic == "nsPref:changed")
+      this.syncUI();
+  },
+
+  _prefName: "browser.tabs.onTop"
+}
+
+var TabsInTitlebar = {
+  init: function () {
+//@line 4728 "/build/src/mozilla-release/browser/base/content/browser.js"
+  },
+
+  allowedBy: function (condition, allow) {
+//@line 4744 "/build/src/mozilla-release/browser/base/content/browser.js"
+  },
+
+  get enabled() {
+    return document.documentElement.getAttribute("tabsintitlebar") == "true";
+  },
+
+//@line 4818 "/build/src/mozilla-release/browser/base/content/browser.js"
+
+  uninit: function () {
+//@line 4824 "/build/src/mozilla-release/browser/base/content/browser.js"
+  }
+};
+
+//@line 4828 "/build/src/mozilla-release/browser/base/content/browser.js"
+function updateAppButtonDisplay() {
+  var displayAppButton =
+    !gInPrintPreviewMode &&
+    window.menubar.visible &&
+    document.getElementById("toolbar-menubar").getAttribute("autohide") == "true";
+
+//@line 4844 "/build/src/mozilla-release/browser/base/content/browser.js"
+  document.getElementById("appmenu-toolbar-button").hidden =
+    !displayAppButton;
+//@line 4847 "/build/src/mozilla-release/browser/base/content/browser.js"
+}
+//@line 4849 "/build/src/mozilla-release/browser/base/content/browser.js"
+
+//@line 4858 "/build/src/mozilla-release/browser/base/content/browser.js"
+
+function displaySecurityInfo()
+{
+  BrowserPageInfo(null, "securityTab");
+}
+
+/**
+ * Opens or closes the sidebar identified by commandID.
+ *
+ * @param commandID a string identifying the sidebar to toggle; see the
+ *                  note below. (Optional if a sidebar is already open.)
+ * @param forceOpen boolean indicating whether the sidebar should be
+ *                  opened regardless of its current state (optional).
+ * @note
+ * We expect to find a xul:broadcaster element with the specified ID.
+ * The following attributes on that element may be used and/or modified:
+ *  - id           (required) the string to match commandID. The convention
+ *                 is to use this naming scheme: 'view<sidebar-name>Sidebar'.
+ *  - sidebarurl   (required) specifies the URL to load in this sidebar.
+ *  - sidebartitle or label (in that order) specify the title to
+ *                 display on the sidebar.
+ *  - checked      indicates whether the sidebar is currently displayed.
+ *                 Note that toggleSidebar updates this attribute when
+ *                 it changes the sidebar's visibility.
+ *  - group        this attribute must be set to "sidebar".
+ */
+function toggleSidebar(commandID, forceOpen) {
+
+  var sidebarBox = document.getElementById("sidebar-box");
+  if (!commandID)
+    commandID = sidebarBox.getAttribute("sidebarcommand");
+
+  var sidebarBroadcaster = document.getElementById(commandID);
+  var sidebar = document.getElementById("sidebar"); // xul:browser
+  var sidebarTitle = document.getElementById("sidebar-title");
+  var sidebarSplitter = document.getElementById("sidebar-splitter");
+
+  if (sidebarBroadcaster.getAttribute("checked") == "true") {
+    if (!forceOpen) {
+      // Replace the document currently displayed in the sidebar with about:blank
+      // so that we can free memory by unloading the page. We need to explicitly
+      // create a new content viewer because the old one doesn't get destroyed
+      // until about:blank has loaded (which does not happen as long as the
+      // element is hidden).
+      sidebar.setAttribute("src", "about:blank");
+      sidebar.docShell.createAboutBlankContentViewer(null);
+
+      sidebarBroadcaster.removeAttribute("checked");
+      sidebarBox.setAttribute("sidebarcommand", "");
+      sidebarTitle.value = "";
+      sidebarBox.hidden = true;
+      sidebarSplitter.hidden = true;
+      content.focus();
+    } else {
+      fireSidebarFocusedEvent();
+    }
+    return;
+  }
+
+  // now we need to show the specified sidebar
+
+  // ..but first update the 'checked' state of all sidebar broadcasters
+  var broadcasters = document.getElementsByAttribute("group", "sidebar");
+  for (var i = 0; i < broadcasters.length; ++i) {
+    // skip elements that observe sidebar broadcasters and random
+    // other elements
+    if (broadcasters[i].localName != "broadcaster")
+      continue;
+
+    if (broadcasters[i] != sidebarBroadcaster)
+      broadcasters[i].removeAttribute("checked");
+    else
+      sidebarBroadcaster.setAttribute("checked", "true");
+  }
+
+  sidebarBox.hidden = false;
+  sidebarSplitter.hidden = false;
+
+  var url = sidebarBroadcaster.getAttribute("sidebarurl");
+  var title = sidebarBroadcaster.getAttribute("sidebartitle");
+  if (!title)
+    title = sidebarBroadcaster.getAttribute("label");
+  sidebar.setAttribute("src", url); // kick off async load
+  sidebarBox.setAttribute("sidebarcommand", sidebarBroadcaster.id);
+  sidebarTitle.value = title;
+
+  // We set this attribute here in addition to setting it on the <browser>
+  // element itself, because the code in BrowserShutdown persists this
+  // attribute, not the "src" of the <browser id="sidebar">. The reason it
+  // does that is that we want to delay sidebar load a bit when a browser
+  // window opens. See delayedStartup().
+  sidebarBox.setAttribute("src", url);
+
+  if (sidebar.contentDocument.location.href != url)
+    sidebar.addEventListener("load", sidebarOnLoad, true);
+  else // older code handled this case, so we do it too
+    fireSidebarFocusedEvent();
+}
+
+function sidebarOnLoad(event) {
+  var sidebar = document.getElementById("sidebar");
+  sidebar.removeEventListener("load", sidebarOnLoad, true);
+  // We're handling the 'load' event before it bubbles up to the usual
+  // (non-capturing) event handlers. Let it bubble up before firing the
+  // SidebarFocused event.
+  setTimeout(fireSidebarFocusedEvent, 0);
+}
+
+/**
+ * Fire a "SidebarFocused" event on the sidebar's |window| to give the sidebar
+ * a chance to adjust focus as needed. An additional event is needed, because
+ * we don't want to focus the sidebar when it's opened on startup or in a new
+ * window, only when the user opens the sidebar.
+ */
+function fireSidebarFocusedEvent() {
+  var sidebar = document.getElementById("sidebar");
+  var event = document.createEvent("Events");
+  event.initEvent("SidebarFocused", true, false);
+  sidebar.contentWindow.dispatchEvent(event);
+}
+
+var gHomeButton = {
+  prefDomain: "browser.startup.homepage",
+  observe: function (aSubject, aTopic, aPrefName)
+  {
+    if (aTopic != "nsPref:changed" || aPrefName != this.prefDomain)
+      return;
+
+    this.updateTooltip();
+  },
+
+  updateTooltip: function (homeButton)
+  {
+    if (!homeButton)
+      homeButton = document.getElementById("home-button");
+    if (homeButton) {
+      var homePage = this.getHomePage();
+      homePage = homePage.replace(/\|/g,', ');
+      if (homePage.toLowerCase() == "about:home")
+        homeButton.setAttribute("tooltiptext", homeButton.getAttribute("aboutHomeOverrideTooltip"));
+      else
+        homeButton.setAttribute("tooltiptext", homePage);
+    }
+  },
+
+  getHomePage: function ()
+  {
+    var url;
+    try {
+      url = gPrefService.getComplexValue(this.prefDomain,
+                                Components.interfaces.nsIPrefLocalizedString).data;
+    } catch (e) {
+    }
+
+    // use this if we can't find the pref
+    if (!url) {
+      var SBS = Cc["@mozilla.org/intl/stringbundle;1"].getService(Ci.nsIStringBundleService);
+      var configBundle = SBS.createBundle("chrome://branding/locale/browserconfig.properties");
+      url = configBundle.GetStringFromName(this.prefDomain);
+    }
+
+    return url;
+  },
+
+  updatePersonalToolbarStyle: function (homeButton)
+  {
+    if (!homeButton)
+      homeButton = document.getElementById("home-button");
+    if (homeButton)
+      homeButton.className = homeButton.parentNode.id == "PersonalToolbar"
+                               || homeButton.parentNode.parentNode.id == "PersonalToolbar" ?
+                             homeButton.className.replace("toolbarbutton-1", "bookmark-item") :
+                             homeButton.className.replace("bookmark-item", "toolbarbutton-1");
+  }
+};
+
+/**
+ * Gets the selected text in the active browser. Leading and trailing
+ * whitespace is removed, and consecutive whitespace is replaced by a single
+ * space. A maximum of 150 characters will be returned, regardless of the value
+ * of aCharLen.
+ *
+ * @param aCharLen
+ *        The maximum number of characters to return.
+ */
+function getBrowserSelection(aCharLen) {
+  // selections of more than 150 characters aren't useful
+  const kMaxSelectionLen = 150;
+  const charLen = Math.min(aCharLen || kMaxSelectionLen, kMaxSelectionLen);
+
+  var focusedWindow = document.commandDispatcher.focusedWindow;
+  var selection = focusedWindow.getSelection().toString();
+
+  if (selection) {
+    if (selection.length > charLen) {
+      // only use the first charLen important chars. see bug 221361
+      var pattern = new RegExp("^(?:\\s*.){0," + charLen + "}");
+      pattern.test(selection);
+      selection = RegExp.lastMatch;
+    }
+
+    selection = selection.replace(/^\s+/, "")
+                         .replace(/\s+$/, "")
+                         .replace(/\s+/g, " ");
+
+    if (selection.length > charLen)
+      selection = selection.substr(0, charLen);
+  }
+  return selection;
+}
+
+var gWebPanelURI;
+function openWebPanel(aTitle, aURI)
+{
+    // Ensure that the web panels sidebar is open.
+    toggleSidebar('viewWebPanelsSidebar', true);
+
+    // Set the title of the panel.
+    document.getElementById("sidebar-title").value = aTitle;
+
+    // Tell the Web Panels sidebar to load the bookmark.
+    var sidebar = document.getElementById("sidebar");
+    if (sidebar.docShell && sidebar.contentDocument && sidebar.contentDocument.getElementById('web-panels-browser')) {
+        sidebar.contentWindow.loadWebPanel(aURI);
+        if (gWebPanelURI) {
+            gWebPanelURI = "";
+            sidebar.removeEventListener("load", asyncOpenWebPanel, true);
+        }
+    }
+    else {
+        // The panel is still being constructed.  Attach an onload handler.
+        if (!gWebPanelURI)
+            sidebar.addEventListener("load", asyncOpenWebPanel, true);
+        gWebPanelURI = aURI;
+    }
+}
+
+function asyncOpenWebPanel(event)
+{
+    var sidebar = document.getElementById("sidebar");
+    if (gWebPanelURI && sidebar.contentDocument && sidebar.contentDocument.getElementById('web-panels-browser'))
+        sidebar.contentWindow.loadWebPanel(gWebPanelURI);
+    gWebPanelURI = "";
+    sidebar.removeEventListener("load", asyncOpenWebPanel, true);
+}
+
+/*
+ * - [ Dependencies ] ---------------------------------------------------------
+ *  utilityOverlay.js:
+ *    - gatherTextUnder
+ */
+
+/**
+ * Extracts linkNode and href for the current click target.
+ *
+ * @param event
+ *        The click event.
+ * @return [href, linkNode].
+ *
+ * @note linkNode will be null if the click wasn't on an anchor
+ *       element (or XLink).
+ */
+function hrefAndLinkNodeForClickEvent(event)
+{
+  function isHTMLLink(aNode)
+  {
+    // Be consistent with what nsContextMenu.js does.
+    return ((aNode instanceof HTMLAnchorElement && aNode.href) ||
+            (aNode instanceof HTMLAreaElement && aNode.href) ||
+            aNode instanceof HTMLLinkElement);
+  }
+
+  let node = event.target;
+  while (node && !isHTMLLink(node)) {
+    node = node.parentNode;
+  }
+
+  if (node)
+    return [node.href, node];
+
+  // If there is no linkNode, try simple XLink.
+  let href, baseURI;
+  node = event.target;
+  while (node && !href) {
+    if (node.nodeType == Node.ELEMENT_NODE) {
+      href = node.getAttributeNS("http://www.w3.org/1999/xlink", "href");
+      if (href)
+        baseURI = node.baseURI;
+    }
+    node = node.parentNode;
+  }
+
+  // In case of XLink, we don't return the node we got href from since
+  // callers expect <a>-like elements.
+  return [href ? makeURLAbsolute(baseURI, href) : null, null];
+}
+
+/**
+ * Called whenever the user clicks in the content area.
+ *
+ * @param event
+ *        The click event.
+ * @param isPanelClick
+ *        Whether the event comes from a web panel.
+ * @note default event is prevented if the click is handled.
+ */
+function contentAreaClick(event, isPanelClick)
+{
+  if (!event.isTrusted || event.defaultPrevented || event.button == 2)
+    return;
+
+  let [href, linkNode] = hrefAndLinkNodeForClickEvent(event);
+  if (!href) {
+    // Not a link, handle middle mouse navigation.
+    if (event.button == 1 &&
+        gPrefService.getBoolPref("middlemouse.contentLoadURL") &&
+        !gPrefService.getBoolPref("general.autoScroll")) {
+      middleMousePaste(event);
+      event.preventDefault();
+    }
+    return;
+  }
+
+  // This code only applies if we have a linkNode (i.e. clicks on real anchor
+  // elements, as opposed to XLink).
+  if (linkNode && event.button == 0 &&
+      !event.ctrlKey && !event.shiftKey && !event.altKey && !event.metaKey) {
+    // A Web panel's links should target the main content area.  Do this
+    // if no modifier keys are down and if there's no target or the target
+    // equals _main (the IE convention) or _content (the Mozilla convention).
+    let target = linkNode.target;
+    let mainTarget = !target || target == "_content" || target  == "_main";
+    if (isPanelClick && mainTarget) {
+      // javascript and data links should be executed in the current browser.
+      if (linkNode.getAttribute("onclick") ||
+          href.substr(0, 11) === "javascript:" ||
+          href.substr(0, 5) === "data:")
+        return;
+
+      try {
+        urlSecurityCheck(href, linkNode.ownerDocument.nodePrincipal);
+      }
+      catch(ex) {
+        // Prevent loading unsecure destinations.
+        event.preventDefault();
+        return;
+      }
+
+      let postData = {};
+      let url = getShortcutOrURI(href, postData);
+      if (!url)
+        return;
+      loadURI(url, null, postData.value, false);
+      event.preventDefault();
+      return;
+    }
+
+    if (linkNode.getAttribute("rel") == "sidebar") {
+      // This is the Opera convention for a special link that, when clicked,
+      // allows to add a sidebar panel.  The link's title attribute contains
+      // the title that should be used for the sidebar panel.
+      PlacesUIUtils.showBookmarkDialog({ action: "add"
+                                       , type: "bookmark"
+                                       , uri: makeURI(href)
+                                       , title: linkNode.getAttribute("title")
+                                       , loadBookmarkInSidebar: true
+                                       , hiddenRows: [ "description"
+                                                     , "location"
+                                                     , "keyword" ]
+                                       }, window);
+      event.preventDefault();
+      return;
+    }
+  }
+
+  handleLinkClick(event, href, linkNode);
+
+  // Mark the page as a user followed link.  This is done so that history can
+  // distinguish automatic embed visits from user activated ones.  For example
+  // pages loaded in frames are embed visits and lost with the session, while
+  // visits across frames should be preserved.
+  try {
+    PlacesUIUtils.markPageAsFollowedLink(href);
+  } catch (ex) { /* Skip invalid URIs. */ }
+}
+
+/**
+ * Handles clicks on links.
+ *
+ * @return true if the click event was handled, false otherwise.
+ */
+function handleLinkClick(event, href, linkNode) {
+  if (event.button == 2) // right click
+    return false;
+
+  var where = whereToOpenLink(event);
+  if (where == "current")
+    return false;
+
+  var doc = event.target.ownerDocument;
+
+  if (where == "save") {
+    saveURL(href, linkNode ? gatherTextUnder(linkNode) : "", null, true,
+            true, doc.documentURIObject);
+    event.preventDefault();
+    return true;
+  }
+
+  urlSecurityCheck(href, doc.nodePrincipal);
+  openLinkIn(href, where, { referrerURI: doc.documentURIObject,
+                            charset: doc.characterSet });
+  event.preventDefault();
+  return true;
+}
+
+function middleMousePaste(event) {
+  let clipboard = readFromClipboard();
+  if (!clipboard)
+    return;
+
+  // Strip embedded newlines and surrounding whitespace, to match the URL
+  // bar's behavior (stripsurroundingwhitespace)
+  clipboard = clipboard.replace(/\s*\n\s*/g, "");
+
+  let mayInheritPrincipal = { value: false };
+  let url = getShortcutOrURI(clipboard, mayInheritPrincipal);
+  try {
+    makeURI(url);
+  } catch (ex) {
+    // Not a valid URI.
+    return;
+  }
+
+  try {
+    addToUrlbarHistory(url);
+  } catch (ex) {
+    // Things may go wrong when adding url to session history,
+    // but don't let that interfere with the loading of the url.
+    Cu.reportError(ex);
+  }
+
+  // FIXME: Bug 631500, use openUILink directly
+  let where = whereToOpenLink(event, true);
+  openUILinkIn(url, where,
+               { disallowInheritPrincipal: !mayInheritPrincipal.value });
+
+  event.stopPropagation();
+}
+
+function handleDroppedLink(event, url, name)
+{
+  let postData = { };
+  let uri = getShortcutOrURI(url, postData);
+  if (uri)
+    loadURI(uri, null, postData.value, false);
+
+  // Keep the event from being handled by the dragDrop listeners
+  // built-in to gecko if they happen to be above us.
+  event.preventDefault();
+};
+
+function MultiplexHandler(event)
+{ try {
+    var node = event.target;
+    var name = node.getAttribute('name');
+
+    if (name == 'detectorGroup') {
+        BrowserCharsetReload();
+        SelectDetector(event, false);
+    } else if (name == 'charsetGroup') {
+        var charset = node.getAttribute('id');
+        charset = charset.substring('charset.'.length, charset.length)
+        BrowserSetForcedCharacterSet(charset);
+    } else if (name == 'charsetCustomize') {
+        //do nothing - please remove this else statement, once the charset prefs moves to the pref window
+    } else {
+        BrowserSetForcedCharacterSet(node.getAttribute('id'));
+    }
+    } catch(ex) { alert(ex); }
+}
+
+function SelectDetector(event, doReload)
+{
+    var uri =  event.target.getAttribute("id");
+    var prefvalue = uri.substring('chardet.'.length, uri.length);
+    if ("off" == prefvalue) { // "off" is special value to turn off the detectors
+        prefvalue = "";
+    }
+
+    try {
+        var str =  Cc["@mozilla.org/supports-string;1"].
+                   createInstance(Ci.nsISupportsString);
+
+        str.data = prefvalue;
+        gPrefService.setComplexValue("intl.charset.detector", Ci.nsISupportsString, str);
+        if (doReload)
+          window.content.location.reload();
+    }
+    catch (ex) {
+        dump("Failed to set the intl.charset.detector preference.\n");
+    }
+}
+
+function BrowserSetForcedCharacterSet(aCharset)
+{
+  gBrowser.docShell.charset = aCharset;
+  // Save the forced character-set
+  PlacesUtils.history.setCharsetForURI(getWebNavigation().currentURI, aCharset);
+  BrowserCharsetReload();
+}
+
+function BrowserCharsetReload()
+{
+  BrowserReloadWithFlags(nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
+}
+
+function charsetMenuGetElement(parent, id) {
+  return parent.getElementsByAttribute("id", id)[0];
+}
+
+function UpdateCurrentCharset(target) {
+    // extract the charset from DOM
+    var wnd = document.commandDispatcher.focusedWindow;
+    if ((window == wnd) || (wnd == null)) wnd = window.content;
+
+    // Uncheck previous item
+    if (gPrevCharset) {
+        var pref_item = charsetMenuGetElement(target, "charset." + gPrevCharset);
+        if (pref_item)
+          pref_item.setAttribute('checked', 'false');
+    }
+
+    var menuitem = charsetMenuGetElement(target, "charset." + wnd.document.characterSet);
+    if (menuitem) {
+        menuitem.setAttribute('checked', 'true');
+    }
+}
+
+function UpdateCharsetDetector(target) {
+  var prefvalue;
+
+  try {
+    prefvalue = gPrefService.getComplexValue("intl.charset.detector", Ci.nsIPrefLocalizedString).data;
+  }
+  catch (ex) {}
+
+  if (!prefvalue)
+    prefvalue = "off";
+
+  var menuitem = charsetMenuGetElement(target, "chardet." + prefvalue);
+  if (menuitem)
+    menuitem.setAttribute("checked", "true");
+}
+
+function UpdateMenus(event) {
+  UpdateCurrentCharset(event.target);
+  UpdateCharsetDetector(event.target);
+}
+
+function CreateMenu(node) {
+  Services.obs.notifyObservers(null, "charsetmenu-selected", node);
+}
+
+function charsetLoadListener(event) {
+  var charset = window.content.document.characterSet;
+
+  if (charset.length > 0 && (charset != gLastBrowserCharset)) {
+    if (!gCharsetMenu)
+      gCharsetMenu = Cc['@mozilla.org/rdf/datasource;1?name=charset-menu'].getService(Ci.nsICurrentCharsetListener);
+    gCharsetMenu.SetCurrentCharset(charset);
+    gPrevCharset = gLastBrowserCharset;
+    gLastBrowserCharset = charset;
+  }
+}
+
+
+var gPageStyleMenu = {
+
+  _getAllStyleSheets: function (frameset) {
+    var styleSheetsArray = Array.slice(frameset.document.styleSheets);
+    for (let i = 0; i < frameset.frames.length; i++) {
+      let frameSheets = this._getAllStyleSheets(frameset.frames[i]);
+      styleSheetsArray = styleSheetsArray.concat(frameSheets);
+    }
+    return styleSheetsArray;
+  },
+
+  fillPopup: function (menuPopup) {
+    var noStyle = menuPopup.firstChild;
+    var persistentOnly = noStyle.nextSibling;
+    var sep = persistentOnly.nextSibling;
+    while (sep.nextSibling)
+      menuPopup.removeChild(sep.nextSibling);
+
+    var styleSheets = this._getAllStyleSheets(window.content);
+    var currentStyleSheets = {};
+    var styleDisabled = getMarkupDocumentViewer().authorStyleDisabled;
+    var haveAltSheets = false;
+    var altStyleSelected = false;
+
+    for (let i = 0; i < styleSheets.length; ++i) {
+      let currentStyleSheet = styleSheets[i];
+
+      if (!currentStyleSheet.title)
+        continue;
+
+      // Skip any stylesheets whose media attribute doesn't match.
+      if (currentStyleSheet.media.length > 0) {
+        let mediaQueryList = currentStyleSheet.media.mediaText;
+        if (!window.content.matchMedia(mediaQueryList).matches)
+          continue;
+      }
+
+      if (!currentStyleSheet.disabled)
+        altStyleSelected = true;
+
+      haveAltSheets = true;
+
+      let lastWithSameTitle = null;
+      if (currentStyleSheet.title in currentStyleSheets)
+        lastWithSameTitle = currentStyleSheets[currentStyleSheet.title];
+
+      if (!lastWithSameTitle) {
+        let menuItem = document.createElement("menuitem");
+        menuItem.setAttribute("type", "radio");
+        menuItem.setAttribute("label", currentStyleSheet.title);
+        menuItem.setAttribute("data", currentStyleSheet.title);
+        menuItem.setAttribute("checked", !currentStyleSheet.disabled && !styleDisabled);
+        menuItem.setAttribute("oncommand", "gPageStyleMenu.switchStyleSheet(this.getAttribute('data'));");
+        menuPopup.appendChild(menuItem);
+        currentStyleSheets[currentStyleSheet.title] = menuItem;
+      } else if (currentStyleSheet.disabled) {
+        lastWithSameTitle.removeAttribute("checked");
+      }
+    }
+
+    noStyle.setAttribute("checked", styleDisabled);
+    persistentOnly.setAttribute("checked", !altStyleSelected && !styleDisabled);
+    persistentOnly.hidden = (window.content.document.preferredStyleSheetSet) ? haveAltSheets : false;
+    sep.hidden = (noStyle.hidden && persistentOnly.hidden) || !haveAltSheets;
+  },
+
+  _stylesheetInFrame: function (frame, title) {
+    return Array.some(frame.document.styleSheets,
+                      function (stylesheet) stylesheet.title == title);
+  },
+
+  _stylesheetSwitchFrame: function (frame, title) {
+    var docStyleSheets = frame.document.styleSheets;
+
+    for (let i = 0; i < docStyleSheets.length; ++i) {
+      let docStyleSheet = docStyleSheets[i];
+
+      if (docStyleSheet.title)
+        docStyleSheet.disabled = (docStyleSheet.title != title);
+      else if (docStyleSheet.disabled)
+        docStyleSheet.disabled = false;
+    }
+  },
+
+  _stylesheetSwitchAll: function (frameset, title) {
+    if (!title || this._stylesheetInFrame(frameset, title))
+      this._stylesheetSwitchFrame(frameset, title);
+
+    for (let i = 0; i < frameset.frames.length; i++)
+      this._stylesheetSwitchAll(frameset.frames[i], title);
+  },
+
+  switchStyleSheet: function (title, contentWindow) {
+    getMarkupDocumentViewer().authorStyleDisabled = false;
+    this._stylesheetSwitchAll(contentWindow || content, title);
+  },
+
+  disableStyle: function () {
+    getMarkupDocumentViewer().authorStyleDisabled = true;
+  },
+};
+
+/* Legacy global page-style functions */
+var getAllStyleSheets   = gPageStyleMenu._getAllStyleSheets.bind(gPageStyleMenu);
+var stylesheetFillPopup = gPageStyleMenu.fillPopup.bind(gPageStyleMenu);
+function stylesheetSwitchAll(contentWindow, title) {
+  gPageStyleMenu.switchStyleSheet(title, contentWindow);
+}
+function setStyleDisabled(disabled) {
+  if (disabled)
+    gPageStyleMenu.disableStyle();
+}
+
+
+var BrowserOffline = {
+  _inited: false,
+
+  /////////////////////////////////////////////////////////////////////////////
+  // BrowserOffline Public Methods
+  init: function ()
+  {
+    if (!this._uiElement)
+      this._uiElement = document.getElementById("workOfflineMenuitemState");
+
+    Services.obs.addObserver(this, "network:offline-status-changed", false);
+
+    this._updateOfflineUI(Services.io.offline);
+
+    this._inited = true;
+  },
+
+  uninit: function ()
+  {
+    if (this._inited) {
+      Services.obs.removeObserver(this, "network:offline-status-changed");
+    }
+  },
+
+  toggleOfflineStatus: function ()
+  {
+    var ioService = Services.io;
+
+    // Stop automatic management of the offline status
+    try {
+      ioService.manageOfflineStatus = false;
+    } catch (ex) {
+    }
+
+    if (!ioService.offline && !this._canGoOffline()) {
+      this._updateOfflineUI(false);
+      return;
+    }
+
+    ioService.offline = !ioService.offline;
+  },
+
+  /////////////////////////////////////////////////////////////////////////////
+  // nsIObserver
+  observe: function (aSubject, aTopic, aState)
+  {
+    if (aTopic != "network:offline-status-changed")
+      return;
+
+    this._updateOfflineUI(aState == "offline");
+  },
+
+  /////////////////////////////////////////////////////////////////////////////
+  // BrowserOffline Implementation Methods
+  _canGoOffline: function ()
+  {
+    try {
+      var cancelGoOffline = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
+      Services.obs.notifyObservers(cancelGoOffline, "offline-requested", null);
+
+      // Something aborted the quit process.
+      if (cancelGoOffline.data)
+        return false;
+    }
+    catch (ex) {
+    }
+
+    return true;
+  },
+
+  _uiElement: null,
+  _updateOfflineUI: function (aOffline)
+  {
+    var offlineLocked = gPrefService.prefIsLocked("network.online");
+    if (offlineLocked)
+      this._uiElement.setAttribute("disabled", "true");
+
+    this._uiElement.setAttribute("checked", aOffline);
+  }
+};
+
+var OfflineApps = {
+  /////////////////////////////////////////////////////////////////////////////
+  // OfflineApps Public Methods
+  init: function ()
+  {
+    Services.obs.addObserver(this, "dom-storage-warn-quota-exceeded", false);
+    Services.obs.addObserver(this, "offline-cache-update-completed", false);
+  },
+
+  uninit: function ()
+  {
+    Services.obs.removeObserver(this, "dom-storage-warn-quota-exceeded");
+    Services.obs.removeObserver(this, "offline-cache-update-completed");
+  },
+
+  handleEvent: function(event) {
+    if (event.type == "MozApplicationManifest") {
+      this.offlineAppRequested(event.originalTarget.defaultView);
+    }
+  },
+
+  /////////////////////////////////////////////////////////////////////////////
+  // OfflineApps Implementation Methods
+
+  // XXX: _getBrowserWindowForContentWindow and _getBrowserForContentWindow
+  // were taken from browser/components/feeds/src/WebContentConverter.
+  _getBrowserWindowForContentWindow: function(aContentWindow) {
+    return aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                         .getInterface(Ci.nsIWebNavigation)
+                         .QueryInterface(Ci.nsIDocShellTreeItem)
+                         .rootTreeItem
+                         .QueryInterface(Ci.nsIInterfaceRequestor)
+                         .getInterface(Ci.nsIDOMWindow)
+                         .wrappedJSObject;
+  },
+
+  _getBrowserForContentWindow: function(aBrowserWindow, aContentWindow) {
+    // This depends on pseudo APIs of browser.js and tabbrowser.xml
+    aContentWindow = aContentWindow.top;
+    var browsers = aBrowserWindow.gBrowser.browsers;
+    for (var i = 0; i < browsers.length; ++i) {
+      if (browsers[i].contentWindow == aContentWindow)
+        return browsers[i];
+    }
+    return null;
+  },
+
+  _getManifestURI: function(aWindow) {
+    if (!aWindow.document.documentElement)
+      return null;
+
+    var attr = aWindow.document.documentElement.getAttribute("manifest");
+    if (!attr)
+      return null;
+
+    try {
+      var contentURI = makeURI(aWindow.location.href, null, null);
+      return makeURI(attr, aWindow.document.characterSet, contentURI);
+    } catch (e) {
+      return null;
+    }
+  },
+
+  // A cache update isn't tied to a specific window.  Try to find
+  // the best browser in which to warn the user about space usage
+  _getBrowserForCacheUpdate: function(aCacheUpdate) {
+    // Prefer the current browser
+    var uri = this._getManifestURI(content);
+    if (uri && uri.equals(aCacheUpdate.manifestURI)) {
+      return gBrowser.selectedBrowser;
+    }
+
+    var browsers = gBrowser.browsers;
+    for (var i = 0; i < browsers.length; ++i) {
+      uri = this._getManifestURI(browsers[i].contentWindow);
+      if (uri && uri.equals(aCacheUpdate.manifestURI)) {
+        return browsers[i];
+      }
+    }
+
+    return null;
+  },
+
+  _warnUsage: function(aBrowser, aURI) {
+    if (!aBrowser)
+      return;
+
+    var notificationBox = gBrowser.getNotificationBox(aBrowser);
+    var notification = notificationBox.getNotificationWithValue("offline-app-usage");
+    if (!notification) {
+      var buttons = [{
+          label: gNavigatorBundle.getString("offlineApps.manageUsage"),
+          accessKey: gNavigatorBundle.getString("offlineApps.manageUsageAccessKey"),
+          callback: OfflineApps.manage
+        }];
+
+      var warnQuota = gPrefService.getIntPref("offline-apps.quota.warn");
+      const priority = notificationBox.PRIORITY_WARNING_MEDIUM;
+      var message = gNavigatorBundle.getFormattedString("offlineApps.usage",
+                                                        [ aURI.host,
+                                                          warnQuota / 1024 ]);
+
+      notificationBox.appendNotification(message, "offline-app-usage",
+                                         "chrome://browser/skin/Info.png",
+                                         priority, buttons);
+    }
+
+    // Now that we've warned once, prevent the warning from showing up
+    // again.
+    Services.perms.add(aURI, "offline-app",
+                       Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN);
+  },
+
+  // XXX: duplicated in preferences/advanced.js
+  _getOfflineAppUsage: function (host, groups)
+  {
+    var cacheService = Cc["@mozilla.org/network/application-cache-service;1"].
+                       getService(Ci.nsIApplicationCacheService);
+    if (!groups)
+      groups = cacheService.getGroups();
+
+    var usage = 0;
+    for (var i = 0; i < groups.length; i++) {
+      var uri = Services.io.newURI(groups[i], null, null);
+      if (uri.asciiHost == host) {
+        var cache = cacheService.getActiveCache(groups[i]);
+        usage += cache.usage;
+      }
+    }
+
+    var storageManager = Cc["@mozilla.org/dom/storagemanager;1"].
+                         getService(Ci.nsIDOMStorageManager);
+    usage += storageManager.getUsage(host);
+
+    return usage;
+  },
+
+  _checkUsage: function(aURI) {
+    // if the user has already allowed excessive usage, don't bother checking
+    if (Services.perms.testExactPermission(aURI, "offline-app") !=
+        Ci.nsIOfflineCacheUpdateService.ALLOW_NO_WARN) {
+      var usage = this._getOfflineAppUsage(aURI.asciiHost);
+      var warnQuota = gPrefService.getIntPref("offline-apps.quota.warn");
+      if (usage >= warnQuota * 1024) {
+        return true;
+      }
+    }
+
+    return false;
+  },
+
+  offlineAppRequested: function(aContentWindow) {
+    if (!gPrefService.getBoolPref("browser.offline-apps.notify")) {
+      return;
+    }
+
+    var browserWindow = this._getBrowserWindowForContentWindow(aContentWindow);
+    var browser = this._getBrowserForContentWindow(browserWindow,
+                                                   aContentWindow);
+
+    var currentURI = aContentWindow.document.documentURIObject;
+
+    // don't bother showing UI if the user has already made a decision
+    if (Services.perms.testExactPermission(currentURI, "offline-app") != Services.perms.UNKNOWN_ACTION)
+      return;
+
+    try {
+      if (gPrefService.getBoolPref("offline-apps.allow_by_default")) {
+        // all pages can use offline capabilities, no need to ask the user
+        return;
+      }
+    } catch(e) {
+      // this pref isn't set by default, ignore failures
+    }
+
+    var host = currentURI.asciiHost;
+    var notificationBox = gBrowser.getNotificationBox(browser);
+    var notificationID = "offline-app-requested-" + host;
+    var notification = notificationBox.getNotificationWithValue(notificationID);
+
+    if (notification) {
+      notification.documents.push(aContentWindow.document);
+    } else {
+      var buttons = [{
+        label: gNavigatorBundle.getString("offlineApps.allow"),
+        accessKey: gNavigatorBundle.getString("offlineApps.allowAccessKey"),
+        callback: function() {
+          for (var i = 0; i < notification.documents.length; i++) {
+            OfflineApps.allowSite(notification.documents[i]);
+          }
+        }
+      },{
+        label: gNavigatorBundle.getString("offlineApps.never"),
+        accessKey: gNavigatorBundle.getString("offlineApps.neverAccessKey"),
+        callback: function() {
+          for (var i = 0; i < notification.documents.length; i++) {
+            OfflineApps.disallowSite(notification.documents[i]);
+          }
+        }
+      },{
+        label: gNavigatorBundle.getString("offlineApps.notNow"),
+        accessKey: gNavigatorBundle.getString("offlineApps.notNowAccessKey"),
+        callback: function() { /* noop */ }
+      }];
+
+      const priority = notificationBox.PRIORITY_INFO_LOW;
+      var message = gNavigatorBundle.getFormattedString("offlineApps.available",
+                                                        [ host ]);
+      notification =
+        notificationBox.appendNotification(message, notificationID,
+                                           "chrome://browser/skin/Info.png",
+                                           priority, buttons);
+      notification.documents = [ aContentWindow.document ];
+    }
+  },
+
+  allowSite: function(aDocument) {
+    Services.perms.add(aDocument.documentURIObject, "offline-app", Services.perms.ALLOW_ACTION);
+
+    // When a site is enabled while loading, manifest resources will
+    // start fetching immediately.  This one time we need to do it
+    // ourselves.
+    this._startFetching(aDocument);
+  },
+
+  disallowSite: function(aDocument) {
+    Services.perms.add(aDocument.documentURIObject, "offline-app", Services.perms.DENY_ACTION);
+  },
+
+  manage: function() {
+    openAdvancedPreferences("networkTab");
+  },
+
+  _startFetching: function(aDocument) {
+    if (!aDocument.documentElement)
+      return;
+
+    var manifest = aDocument.documentElement.getAttribute("manifest");
+    if (!manifest)
+      return;
+
+    var manifestURI = makeURI(manifest, aDocument.characterSet,
+                              aDocument.documentURIObject);
+
+    var updateService = Cc["@mozilla.org/offlinecacheupdate-service;1"].
+                        getService(Ci.nsIOfflineCacheUpdateService);
+    updateService.scheduleUpdate(manifestURI, aDocument.documentURIObject, window);
+  },
+
+  /////////////////////////////////////////////////////////////////////////////
+  // nsIObserver
+  observe: function (aSubject, aTopic, aState)
+  {
+    if (aTopic == "dom-storage-warn-quota-exceeded") {
+      if (aSubject) {
+        var uri = makeURI(aSubject.location.href);
+
+        if (OfflineApps._checkUsage(uri)) {
+          var browserWindow =
+            this._getBrowserWindowForContentWindow(aSubject);
+          var browser = this._getBrowserForContentWindow(browserWindow,
+                                                         aSubject);
+          OfflineApps._warnUsage(browser, uri);
+        }
+      }
+    } else if (aTopic == "offline-cache-update-completed") {
+      var cacheUpdate = aSubject.QueryInterface(Ci.nsIOfflineCacheUpdate);
+
+      var uri = cacheUpdate.manifestURI;
+      if (OfflineApps._checkUsage(uri)) {
+        var browser = this._getBrowserForCacheUpdate(cacheUpdate);
+        if (browser) {
+          OfflineApps._warnUsage(browser, cacheUpdate.manifestURI);
+        }
+      }
+    }
+  }
+};
+
+var IndexedDBPromptHelper = {
+  _permissionsPrompt: "indexedDB-permissions-prompt",
+  _permissionsResponse: "indexedDB-permissions-response",
+
+  _quotaPrompt: "indexedDB-quota-prompt",
+  _quotaResponse: "indexedDB-quota-response",
+  _quotaCancel: "indexedDB-quota-cancel",
+
+  _notificationIcon: "indexedDB-notification-icon",
+
+  init:
+  function IndexedDBPromptHelper_init() {
+    Services.obs.addObserver(this, this._permissionsPrompt, false);
+    Services.obs.addObserver(this, this._quotaPrompt, false);
+    Services.obs.addObserver(this, this._quotaCancel, false);
+  },
+
+  uninit:
+  function IndexedDBPromptHelper_uninit() {
+    Services.obs.removeObserver(this, this._permissionsPrompt, false);
+    Services.obs.removeObserver(this, this._quotaPrompt, false);
+    Services.obs.removeObserver(this, this._quotaCancel, false);
+  },
+
+  observe:
+  function IndexedDBPromptHelper_observe(subject, topic, data) {
+    if (topic != this._permissionsPrompt &&
+        topic != this._quotaPrompt &&
+        topic != this._quotaCancel) {
+      throw new Error("Unexpected topic!");
+    }
+
+    var requestor = subject.QueryInterface(Ci.nsIInterfaceRequestor);
+
+    var contentWindow = requestor.getInterface(Ci.nsIDOMWindow);
+    var contentDocument = contentWindow.document;
+    var browserWindow =
+      OfflineApps._getBrowserWindowForContentWindow(contentWindow);
+
+    if (browserWindow != window) {
+      // Must belong to some other window.
+      return;
+    }
+
+    var browser =
+      OfflineApps._getBrowserForContentWindow(browserWindow, contentWindow);
+
+    var host = contentDocument.documentURIObject.asciiHost;
+
+    var message;
+    var responseTopic;
+    if (topic == this._permissionsPrompt) {
+      message = gNavigatorBundle.getFormattedString("offlineApps.available",
+                                                    [ host ]);
+      responseTopic = this._permissionsResponse;
+    }
+    else if (topic == this._quotaPrompt) {
+      message = gNavigatorBundle.getFormattedString("indexedDB.usage",
+                                                    [ host, data ]);
+      responseTopic = this._quotaResponse;
+    }
+    else if (topic == this._quotaCancel) {
+      responseTopic = this._quotaResponse;
+    }
+
+    const hiddenTimeoutDuration = 30000; // 30 seconds
+    const firstTimeoutDuration = 360000; // 5 minutes
+
+    var timeoutId;
+
+    var observer = requestor.getInterface(Ci.nsIObserver);
+
+    var mainAction = {
+      label: gNavigatorBundle.getString("offlineApps.allow"),
+      accessKey: gNavigatorBundle.getString("offlineApps.allowAccessKey"),
+      callback: function() {
+        clearTimeout(timeoutId);
+        observer.observe(null, responseTopic,
+                         Ci.nsIPermissionManager.ALLOW_ACTION);
+      }
+    };
+
+    var secondaryActions = [
+      {
+        label: gNavigatorBundle.getString("offlineApps.never"),
+        accessKey: gNavigatorBundle.getString("offlineApps.neverAccessKey"),
+        callback: function() {
+          clearTimeout(timeoutId);
+          observer.observe(null, responseTopic,
+                           Ci.nsIPermissionManager.DENY_ACTION);
+        }
+      }
+    ];
+
+    // This will be set to the result of PopupNotifications.show() below, or to
+    // the result of PopupNotifications.getNotification() if this is a
+    // quotaCancel notification.
+    var notification;
+
+    function timeoutNotification() {
+      // Remove the notification.
+      if (notification) {
+        notification.remove();
+      }
+
+      // Clear all of our timeout stuff. We may be called directly, not just
+      // when the timeout actually elapses.
+      clearTimeout(timeoutId);
+
+      // And tell the page that the popup timed out.
+      observer.observe(null, responseTopic,
+                       Ci.nsIPermissionManager.UNKNOWN_ACTION);
+    }
+
+    var options = {
+      eventCallback: function(state) {
+        // Don't do anything if the timeout has not been set yet.
+        if (!timeoutId) {
+          return;
+        }
+
+        // If the popup is being dismissed start the short timeout.
+        if (state == "dismissed") {
+          clearTimeout(timeoutId);
+          timeoutId = setTimeout(timeoutNotification, hiddenTimeoutDuration);
+          return;
+        }
+
+        // If the popup is being re-shown then clear the timeout allowing
+        // unlimited waiting.
+        if (state == "shown") {
+          clearTimeout(timeoutId);
+        }
+      }
+    };
+
+    if (topic == this._quotaCancel) {
+      notification = PopupNotifications.getNotification(this._quotaPrompt,
+                                                        browser);
+      timeoutNotification();
+      return;
+    }
+
+    notification = PopupNotifications.show(browser, topic, message,
+                                           this._notificationIcon, mainAction,
+                                           secondaryActions, options);
+
+    // Set the timeoutId after the popup has been created, and use the long
+    // timeout value. If the user doesn't notice the popup after this amount of
+    // time then it is most likely not visible and we want to alert the page.
+    timeoutId = setTimeout(timeoutNotification, firstTimeoutDuration);
+  }
+};
+
+function WindowIsClosing()
+{
+  if (TabView.isVisible()) {
+    TabView.hide();
+    return false;
+  }
+
+  var reallyClose = closeWindow(false, warnAboutClosingWindow);
+  if (!reallyClose)
+    return false;
+
+  var numBrowsers = gBrowser.browsers.length;
+  for (let i = 0; reallyClose && i < numBrowsers; ++i) {
+    let ds = gBrowser.browsers[i].docShell;
+
+    if (ds.contentViewer && !ds.contentViewer.permitUnload())
+      reallyClose = false;
+  }
+
+  return reallyClose;
+}
+
+/**
+ * Checks if this is the last full *browser* window around. If it is, this will
+ * be communicated like quitting. Otherwise, we warn about closing multiple tabs.
+ * @returns true if closing can proceed, false if it got cancelled.
+ */
+function warnAboutClosingWindow() {
+  // Popups aren't considered full browser windows.
+  if (!toolbar.visible)
+    return gBrowser.warnAboutClosingTabs(true);
+
+  // Figure out if there's at least one other browser window around.
+  let e = Services.wm.getEnumerator("navigator:browser");
+  while (e.hasMoreElements()) {
+    let win = e.getNext();
+    if (win != window && win.toolbar.visible)
+      return gBrowser.warnAboutClosingTabs(true);
+  }
+
+  let os = Services.obs;
+
+  let closingCanceled = Cc["@mozilla.org/supports-PRBool;1"].
+                        createInstance(Ci.nsISupportsPRBool);
+  os.notifyObservers(closingCanceled,
+                     "browser-lastwindow-close-requested", null);
+  if (closingCanceled.data)
+    return false;
+
+  os.notifyObservers(null, "browser-lastwindow-close-granted", null);
+
+//@line 6119 "/build/src/mozilla-release/browser/base/content/browser.js"
+  return true;
+//@line 6121 "/build/src/mozilla-release/browser/base/content/browser.js"
+}
+
+var MailIntegration = {
+  sendLinkForWindow: function (aWindow) {
+    this.sendMessage(aWindow.location.href,
+                     aWindow.document.title);
+  },
+
+  sendMessage: function (aBody, aSubject) {
+    // generate a mailto url based on the url and the url's title
+    var mailtoUrl = "mailto:";
+    if (aBody) {
+      mailtoUrl += "?body=" + encodeURIComponent(aBody);
+      mailtoUrl += "&subject=" + encodeURIComponent(aSubject);
+    }
+
+    var uri = makeURI(mailtoUrl);
+
+    // now pass this uri to the operating system
+    this._launchExternalUrl(uri);
+  },
+
+  // a generic method which can be used to pass arbitrary urls to the operating
+  // system.
+  // aURL --> a nsIURI which represents the url to launch
+  _launchExternalUrl: function (aURL) {
+    var extProtocolSvc =
+       Cc["@mozilla.org/uriloader/external-protocol-service;1"]
+         .getService(Ci.nsIExternalProtocolService);
+    if (extProtocolSvc)
+      extProtocolSvc.loadUrl(aURL);
+  }
+};
+
+function BrowserOpenAddonsMgr(aView) {
+  if (aView) {
+    let emWindow;
+    let browserWindow;
+
+    function receivePong(aSubject, aTopic, aData) {
+      let browserWin = aSubject.QueryInterface(Ci.nsIInterfaceRequestor)
+                               .getInterface(Ci.nsIWebNavigation)
+                               .QueryInterface(Ci.nsIDocShellTreeItem)
+                               .rootTreeItem
+                               .QueryInterface(Ci.nsIInterfaceRequestor)
+                               .getInterface(Ci.nsIDOMWindow);
+      if (!emWindow || browserWin == window /* favor the current window */) {
+        emWindow = aSubject;
+        browserWindow = browserWin;
+      }
+    }
+    Services.obs.addObserver(receivePong, "EM-pong", false);
+    Services.obs.notifyObservers(null, "EM-ping", "");
+    Services.obs.removeObserver(receivePong, "EM-pong");
+
+    if (emWindow) {
+      emWindow.loadView(aView);
+      browserWindow.gBrowser.selectedTab =
+        browserWindow.gBrowser._getTabForContentWindow(emWindow);
+      emWindow.focus();
+      return;
+    }
+  }
+
+  var newLoad = !switchToTabHavingURI("about:addons", true);
+
+  if (aView) {
+    // This must be a new load, else the ping/pong would have
+    // found the window above.
+    Services.obs.addObserver(function observer(aSubject, aTopic, aData) {
+      Services.obs.removeObserver(observer, aTopic);
+      aSubject.loadView(aView);
+    }, "EM-loaded", false);
+  }
+}
+
+function AddKeywordForSearchField() {
+  var node = document.popupNode;
+
+  var charset = node.ownerDocument.characterSet;
+
+  var docURI = makeURI(node.ownerDocument.URL,
+                       charset);
+
+  var formURI = makeURI(node.form.getAttribute("action"),
+                        charset,
+                        docURI);
+
+  var spec = formURI.spec;
+
+  var isURLEncoded =
+               (node.form.method.toUpperCase() == "POST"
+                && (node.form.enctype == "application/x-www-form-urlencoded" ||
+                    node.form.enctype == ""));
+
+  var title = gNavigatorBundle.getFormattedString("addKeywordTitleAutoFill",
+                                                  [node.ownerDocument.title]);
+  var description = PlacesUIUtils.getDescriptionFromDocument(node.ownerDocument);
+
+  var el, type;
+  var formData = [];
+
+  function escapeNameValuePair(aName, aValue, aIsFormUrlEncoded) {
+    if (aIsFormUrlEncoded)
+      return escape(aName + "=" + aValue);
+    else
+      return escape(aName) + "=" + escape(aValue);
+  }
+
+  for (var i=0; i < node.form.elements.length; i++) {
+    el = node.form.elements[i];
+
+    if (!el.type) // happens with fieldsets
+      continue;
+
+    if (el == node) {
+      formData.push((isURLEncoded) ? escapeNameValuePair(el.name, "%s", true) :
+                                     // Don't escape "%s", just append
+                                     escapeNameValuePair(el.name, "", false) + "%s");
+      continue;
+    }
+
+    type = el.type.toLowerCase();
+
+    if (((el instanceof HTMLInputElement && el.mozIsTextField(true)) ||
+        type == "hidden" || type == "textarea") ||
+        ((type == "checkbox" || type == "radio") && el.checked)) {
+      formData.push(escapeNameValuePair(el.name, el.value, isURLEncoded));
+    } else if (el instanceof HTMLSelectElement && el.selectedIndex >= 0) {
+      for (var j=0; j < el.options.length; j++) {
+        if (el.options[j].selected)
+          formData.push(escapeNameValuePair(el.name, el.options[j].value,
+                                            isURLEncoded));
+      }
+    }
+  }
+
+  var postData;
+
+  if (isURLEncoded)
+    postData = formData.join("&");
+  else
+    spec += "?" + formData.join("&");
+
+  PlacesUIUtils.showBookmarkDialog({ action: "add"
+                                   , type: "bookmark"
+                                   , uri: makeURI(spec)
+                                   , title: title
+                                   , description: description
+                                   , keyword: ""
+                                   , postData: postData
+                                   , charSet: charset
+                                   , hiddenRows: [ "location"
+                                                 , "description"
+                                                 , "tags"
+                                                 , "loadInSidebar" ]
+                                   }, window);
+}
+
+function SwitchDocumentDirection(aWindow) {
+  aWindow.document.dir = (aWindow.document.dir == "ltr" ? "rtl" : "ltr");
+  for (var run = 0; run < aWindow.frames.length; run++)
+    SwitchDocumentDirection(aWindow.frames[run]);
+}
+
+function convertFromUnicode(charset, str)
+{
+  try {
+    var unicodeConverter = Components
+       .classes["@mozilla.org/intl/scriptableunicodeconverter"]
+       .createInstance(Components.interfaces.nsIScriptableUnicodeConverter);
+    unicodeConverter.charset = charset;
+    str = unicodeConverter.ConvertFromUnicode(str);
+    return str + unicodeConverter.Finish();
+  } catch(ex) {
+    return null;
+  }
+}
+
+/**
+ * Re-open a closed tab.
+ * @param aIndex
+ *        The index of the tab (via nsSessionStore.getClosedTabData)
+ * @returns a reference to the reopened tab.
+ */
+function undoCloseTab(aIndex) {
+  // wallpaper patch to prevent an unnecessary blank tab (bug 343895)
+  var blankTabToRemove = null;
+  if (gBrowser.tabs.length == 1 &&
+      !gPrefService.getBoolPref("browser.tabs.autoHide") &&
+      isTabEmpty(gBrowser.selectedTab))
+    blankTabToRemove = gBrowser.selectedTab;
+
+  var tab = null;
+  var ss = Cc["@mozilla.org/browser/sessionstore;1"].
+           getService(Ci.nsISessionStore);
+  if (ss.getClosedTabCount(window) > (aIndex || 0)) {
+    TabView.prepareUndoCloseTab(blankTabToRemove);
+    tab = ss.undoCloseTab(window, aIndex || 0);
+    TabView.afterUndoCloseTab();
+
+    if (blankTabToRemove)
+      gBrowser.removeTab(blankTabToRemove);
+  }
+
+  return tab;
+}
+
+/**
+ * Re-open a closed window.
+ * @param aIndex
+ *        The index of the window (via nsSessionStore.getClosedWindowData)
+ * @returns a reference to the reopened window.
+ */
+function undoCloseWindow(aIndex) {
+  let ss = Cc["@mozilla.org/browser/sessionstore;1"].
+           getService(Ci.nsISessionStore);
+  let window = null;
+  if (ss.getClosedWindowCount() > (aIndex || 0))
+    window = ss.undoCloseWindow(aIndex || 0);
+
+  return window;
+}
+
+/*
+ * Determines if a tab is "empty", usually used in the context of determining
+ * if it's ok to close the tab.
+ */
+function isTabEmpty(aTab) {
+  if (aTab.hasAttribute("busy"))
+    return false;
+
+  let browser = aTab.linkedBrowser;
+  if (!isBlankPageURL(browser.currentURI.spec))
+    return false;
+
+  if (browser.contentWindow.opener)
+    return false;
+
+  if (browser.sessionHistory && browser.sessionHistory.count >= 2)
+    return false;
+
+  return true;
+}
+
+//@line 6367 "/build/src/mozilla-release/browser/base/content/browser.js"
+function BrowserOpenSyncTabs() {
+  switchToTabHavingURI("about:sync-tabs", true);
+}
+//@line 6371 "/build/src/mozilla-release/browser/base/content/browser.js"
+
+/**
+ * Format a URL
+ * eg:
+ * echo formatURL("https://addons.mozilla.org/%LOCALE%/%APP%/%VERSION%/");
+ * > https://addons.mozilla.org/en-US/firefox/3.0a1/
+ *
+ * Currently supported built-ins are LOCALE, APP, and any value from nsIXULAppInfo, uppercased.
+ */
+function formatURL(aFormat, aIsPref) {
+  var formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].getService(Ci.nsIURLFormatter);
+  return aIsPref ? formatter.formatURLPref(aFormat) : formatter.formatURL(aFormat);
+}
+
+/**
+ * Utility object to handle manipulations of the identity indicators in the UI
+ */
+var gIdentityHandler = {
+  // Mode strings used to control CSS display
+  IDENTITY_MODE_IDENTIFIED       : "verifiedIdentity", // High-quality identity information
+  IDENTITY_MODE_DOMAIN_VERIFIED  : "verifiedDomain",   // Minimal SSL CA-signed domain verification
+  IDENTITY_MODE_UNKNOWN          : "unknownIdentity",  // No trusted identity information
+  IDENTITY_MODE_MIXED_CONTENT    : "unknownIdentity mixedContent",  // SSL with unauthenticated content
+  IDENTITY_MODE_CHROMEUI         : "chromeUI",         // Part of the product's UI
+
+  // Cache the most recent SSLStatus and Location seen in checkIdentity
+  _lastStatus : null,
+  _lastLocation : null,
+  _mode : "unknownIdentity",
+
+  // smart getters
+  get _encryptionLabel () {
+    delete this._encryptionLabel;
+    this._encryptionLabel = {};
+    this._encryptionLabel[this.IDENTITY_MODE_DOMAIN_VERIFIED] =
+      gNavigatorBundle.getString("identity.encrypted");
+    this._encryptionLabel[this.IDENTITY_MODE_IDENTIFIED] =
+      gNavigatorBundle.getString("identity.encrypted");
+    this._encryptionLabel[this.IDENTITY_MODE_UNKNOWN] =
+      gNavigatorBundle.getString("identity.unencrypted");
+    this._encryptionLabel[this.IDENTITY_MODE_MIXED_CONTENT] =
+      gNavigatorBundle.getString("identity.mixed_content");
+    return this._encryptionLabel;
+  },
+  get _identityPopup () {
+    delete this._identityPopup;
+    return this._identityPopup = document.getElementById("identity-popup");
+  },
+  get _identityBox () {
+    delete this._identityBox;
+    return this._identityBox = document.getElementById("identity-box");
+  },
+  get _identityPopupContentBox () {
+    delete this._identityPopupContentBox;
+    return this._identityPopupContentBox =
+      document.getElementById("identity-popup-content-box");
+  },
+  get _identityPopupContentHost () {
+    delete this._identityPopupContentHost;
+    return this._identityPopupContentHost =
+      document.getElementById("identity-popup-content-host");
+  },
+  get _identityPopupContentOwner () {
+    delete this._identityPopupContentOwner;
+    return this._identityPopupContentOwner =
+      document.getElementById("identity-popup-content-owner");
+  },
+  get _identityPopupContentSupp () {
+    delete this._identityPopupContentSupp;
+    return this._identityPopupContentSupp =
+      document.getElementById("identity-popup-content-supplemental");
+  },
+  get _identityPopupContentVerif () {
+    delete this._identityPopupContentVerif;
+    return this._identityPopupContentVerif =
+      document.getElementById("identity-popup-content-verifier");
+  },
+  get _identityPopupEncLabel () {
+    delete this._identityPopupEncLabel;
+    return this._identityPopupEncLabel =
+      document.getElementById("identity-popup-encryption-label");
+  },
+  get _identityIconLabel () {
+    delete this._identityIconLabel;
+    return this._identityIconLabel = document.getElementById("identity-icon-label");
+  },
+  get _overrideService () {
+    delete this._overrideService;
+    return this._overrideService = Cc["@mozilla.org/security/certoverride;1"]
+                                     .getService(Ci.nsICertOverrideService);
+  },
+  get _identityIconCountryLabel () {
+    delete this._identityIconCountryLabel;
+    return this._identityIconCountryLabel = document.getElementById("identity-icon-country-label");
+  },
+  get _identityIcon () {
+    delete this._identityIcon;
+    return this._identityIcon = document.getElementById("page-proxy-favicon");
+  },
+
+  /**
+   * Rebuild cache of the elements that may or may not exist depending
+   * on whether there's a location bar.
+   */
+  _cacheElements : function() {
+    delete this._identityBox;
+    delete this._identityIconLabel;
+    delete this._identityIconCountryLabel;
+    delete this._identityIcon;
+    this._identityBox = document.getElementById("identity-box");
+    this._identityIconLabel = document.getElementById("identity-icon-label");
+    this._identityIconCountryLabel = document.getElementById("identity-icon-country-label");
+    this._identityIcon = document.getElementById("page-proxy-favicon");
+  },
+
+  /**
+   * Handler for mouseclicks on the "More Information" button in the
+   * "identity-popup" panel.
+   */
+  handleMoreInfoClick : function(event) {
+    displaySecurityInfo();
+    event.stopPropagation();
+  },
+
+  /**
+   * Helper to parse out the important parts of _lastStatus (of the SSL cert in
+   * particular) for use in constructing identity UI strings
+  */
+  getIdentityData : function() {
+    var result = {};
+    var status = this._lastStatus.QueryInterface(Components.interfaces.nsISSLStatus);
+    var cert = status.serverCert;
+
+    // Human readable name of Subject
+    result.subjectOrg = cert.organization;
+
+    // SubjectName fields, broken up for individual access
+    if (cert.subjectName) {
+      result.subjectNameFields = {};
+      cert.subjectName.split(",").forEach(function(v) {
+        var field = v.split("=");
+        this[field[0]] = field[1];
+      }, result.subjectNameFields);
+
+      // Call out city, state, and country specifically
+      result.city = result.subjectNameFields.L;
+      result.state = result.subjectNameFields.ST;
+      result.country = result.subjectNameFields.C;
+    }
+
+    // Human readable name of Certificate Authority
+    result.caOrg =  cert.issuerOrganization || cert.issuerCommonName;
+    result.cert = cert;
+
+    return result;
+  },
+
+  /**
+   * Determine the identity of the page being displayed by examining its SSL cert
+   * (if available) and, if necessary, update the UI to reflect this.  Intended to
+   * be called by onSecurityChange
+   *
+   * @param PRUint32 state
+   * @param JS Object location that mirrors an nsLocation (i.e. has .host and
+   *                           .hostname and .port)
+   */
+  checkIdentity : function(state, location) {
+    var currentStatus = gBrowser.securityUI
+                                .QueryInterface(Components.interfaces.nsISSLStatusProvider)
+                                .SSLStatus;
+    this._lastStatus = currentStatus;
+    this._lastLocation = location;
+
+    let nsIWebProgressListener = Ci.nsIWebProgressListener;
+    if (location.protocol == "chrome:" || location.protocol == "about:")
+      this.setMode(this.IDENTITY_MODE_CHROMEUI);
+    else if (state & nsIWebProgressListener.STATE_IDENTITY_EV_TOPLEVEL)
+      this.setMode(this.IDENTITY_MODE_IDENTIFIED);
+    else if (state & nsIWebProgressListener.STATE_SECURE_HIGH)
+      this.setMode(this.IDENTITY_MODE_DOMAIN_VERIFIED);
+    else if (state & nsIWebProgressListener.STATE_IS_BROKEN)
+      this.setMode(this.IDENTITY_MODE_MIXED_CONTENT);
+    else
+      this.setMode(this.IDENTITY_MODE_UNKNOWN);
+  },
+
+  /**
+   * Return the eTLD+1 version of the current hostname
+   */
+  getEffectiveHost : function() {
+    // Cache the eTLDService if this is our first time through
+    if (!this._eTLDService)
+      this._eTLDService = Cc["@mozilla.org/network/effective-tld-service;1"]
+                         .getService(Ci.nsIEffectiveTLDService);
+    if (!this._IDNService)
+      this._IDNService = Cc["@mozilla.org/network/idn-service;1"]
+                         .getService(Ci.nsIIDNService);
+    try {
+      let baseDomain =
+        this._eTLDService.getBaseDomainFromHost(this._lastLocation.hostname);
+      return this._IDNService.convertToDisplayIDN(baseDomain, {});
+    } catch (e) {
+      // If something goes wrong (e.g. hostname is an IP address) just fail back
+      // to the full domain.
+      return this._lastLocation.hostname;
+    }
+  },
+
+  /**
+   * Update the UI to reflect the specified mode, which should be one of the
+   * IDENTITY_MODE_* constants.
+   */
+  setMode : function(newMode) {
+    if (!this._identityBox) {
+      // No identity box means the identity box is not visible, in which
+      // case there's nothing to do.
+      return;
+    }
+
+    this._identityBox.className = newMode;
+    this.setIdentityMessages(newMode);
+
+    // Update the popup too, if it's open
+    if (this._identityPopup.state == "open")
+      this.setPopupMessages(newMode);
+
+    this._mode = newMode;
+  },
+
+  /**
+   * Set up the messages for the primary identity UI based on the specified mode,
+   * and the details of the SSL cert, where applicable
+   *
+   * @param newMode The newly set identity mode.  Should be one of the IDENTITY_MODE_* constants.
+   */
+  setIdentityMessages : function(newMode) {
+    let icon_label = "";
+    let tooltip = "";
+    let icon_country_label = "";
+    let icon_labels_dir = "ltr";
+
+    switch (newMode) {
+    case this.IDENTITY_MODE_DOMAIN_VERIFIED: {
+      let iData = this.getIdentityData();
+
+      // Verifier is either the CA Org, for a normal cert, or a special string
+      // for certs that are trusted because of a security exception.
+      tooltip = gNavigatorBundle.getFormattedString("identity.identified.verifier",
+                                                    [iData.caOrg]);
+
+      // Check whether this site is a security exception. XPConnect does the right
+      // thing here in terms of converting _lastLocation.port from string to int, but
+      // the overrideService doesn't like undefined ports, so make sure we have
+      // something in the default case (bug 432241).
+      // .hostname can return an empty string in some exceptional cases -
+      // hasMatchingOverride does not handle that, so avoid calling it.
+      // Updating the tooltip value in those cases isn't critical.
+      // FIXME: Fixing bug 646690 would probably makes this check unnecessary
+      if (this._lastLocation.hostname &&
+          this._overrideService.hasMatchingOverride(this._lastLocation.hostname,
+                                                    (this._lastLocation.port || 443),
+                                                    iData.cert, {}, {}))
+        tooltip = gNavigatorBundle.getString("identity.identified.verified_by_you");
+      break; }
+    case this.IDENTITY_MODE_IDENTIFIED: {
+      // If it's identified, then we can populate the dialog with credentials
+      let iData = this.getIdentityData();
+      tooltip = gNavigatorBundle.getFormattedString("identity.identified.verifier",
+                                                    [iData.caOrg]);
+      icon_label = iData.subjectOrg;
+      if (iData.country)
+        icon_country_label = "(" + iData.country + ")";
+
+      // If the organization name starts with an RTL character, then
+      // swap the positions of the organization and country code labels.
+      // The Unicode ranges reflect the definition of the UCS2_CHAR_IS_BIDI
+      // macro in intl/unicharutil/util/nsBidiUtils.h. When bug 218823 gets
+      // fixed, this test should be replaced by one adhering to the
+      // Unicode Bidirectional Algorithm proper (at the paragraph level).
+      icon_labels_dir = /^[\u0590-\u08ff\ufb1d-\ufdff\ufe70-\ufefc]/.test(icon_label) ?
+                        "rtl" : "ltr";
+      break; }
+    case this.IDENTITY_MODE_CHROMEUI:
+      break;
+    default:
+      tooltip = gNavigatorBundle.getString("identity.unknown.tooltip");
+    }
+
+    // Push the appropriate strings out to the UI
+    this._identityBox.tooltipText = tooltip;
+    this._identityIconLabel.value = icon_label;
+    this._identityIconCountryLabel.value = icon_country_label;
+    // Set cropping and direction
+    this._identityIconLabel.crop = icon_country_label ? "end" : "center";
+    this._identityIconLabel.parentNode.style.direction = icon_labels_dir;
+    // Hide completely if the organization label is empty
+    this._identityIconLabel.parentNode.collapsed = icon_label ? false : true;
+  },
+
+  /**
+   * Set up the title and content messages for the identity message popup,
+   * based on the specified mode, and the details of the SSL cert, where
+   * applicable
+   *
+   * @param newMode The newly set identity mode.  Should be one of the IDENTITY_MODE_* constants.
+   */
+  setPopupMessages : function(newMode) {
+
+    this._identityPopup.className = newMode;
+    this._identityPopupContentBox.className = newMode;
+
+    // Set the static strings up front
+    this._identityPopupEncLabel.textContent = this._encryptionLabel[newMode];
+
+    // Initialize the optional strings to empty values
+    let supplemental = "";
+    let verifier = "";
+    let host = "";
+    let owner = "";
+
+    switch (newMode) {
+    case this.IDENTITY_MODE_DOMAIN_VERIFIED:
+      host = this.getEffectiveHost();
+      owner = gNavigatorBundle.getString("identity.ownerUnknown2");
+      verifier = this._identityBox.tooltipText;
+      break;
+    case this.IDENTITY_MODE_IDENTIFIED: {
+      // If it's identified, then we can populate the dialog with credentials
+      let iData = this.getIdentityData();
+      host = this.getEffectiveHost();
+      owner = iData.subjectOrg;
+      verifier = this._identityBox.tooltipText;
+
+      // Build an appropriate supplemental block out of whatever location data we have
+      if (iData.city)
+        supplemental += iData.city + "\n";
+      if (iData.state && iData.country)
+        supplemental += gNavigatorBundle.getFormattedString("identity.identified.state_and_country",
+                                                            [iData.state, iData.country]);
+      else if (iData.state) // State only
+        supplemental += iData.state;
+      else if (iData.country) // Country only
+        supplemental += iData.country;
+      break; }
+    }
+
+    // Push the appropriate strings out to the UI
+    this._identityPopupContentHost.textContent = host;
+    this._identityPopupContentOwner.textContent = owner;
+    this._identityPopupContentSupp.textContent = supplemental;
+    this._identityPopupContentVerif.textContent = verifier;
+  },
+
+  hideIdentityPopup : function() {
+    this._identityPopup.hidePopup();
+  },
+
+  _popupOpenTime : null,
+
+  /**
+   * Click handler for the identity-box element in primary chrome.
+   */
+  handleIdentityButtonEvent : function(event) {
+    this._popupOpenTime = new Date();
+    event.stopPropagation();
+
+    if ((event.type == "click" && event.button != 0) ||
+        (event.type == "keypress" && event.charCode != KeyEvent.DOM_VK_SPACE &&
+         event.keyCode != KeyEvent.DOM_VK_RETURN))
+      return; // Left click, space or enter only
+
+    // Don't allow left click, space or enter if the location
+    // is chrome UI or the location has been modified.
+    if (this._mode == this.IDENTITY_MODE_CHROMEUI ||
+        gURLBar.getAttribute("pageproxystate") != "valid")
+      return;
+
+    // Make sure that the display:none style we set in xul is removed now that
+    // the popup is actually needed
+    this._identityPopup.hidden = false;
+
+    // Tell the popup to consume dismiss clicks, to avoid bug 395314
+    this._identityPopup.popupBoxObject
+        .setConsumeRollupEvent(Ci.nsIPopupBoxObject.ROLLUP_CONSUME);
+
+    // Update the popup strings
+    this.setPopupMessages(this._identityBox.className);
+
+    // Add the "open" attribute to the identity box for styling
+    this._identityBox.setAttribute("open", "true");
+    var self = this;
+    this._identityPopup.addEventListener("popuphidden", function onPopupHidden(e) {
+      e.currentTarget.removeEventListener("popuphidden", onPopupHidden, false);
+      self._identityBox.removeAttribute("open");
+    }, false);
+
+    // Now open the popup, anchored off the primary chrome element
+    this._identityPopup.openPopup(this._identityIcon, "bottomcenter topleft");
+  },
+
+  onPopupShown : function(event) {
+    let openingDuration = new Date() - this._popupOpenTime;
+    this._popupOpenTime = null;
+    try {
+      Services.telemetry.getHistogramById("FX_IDENTITY_POPUP_OPEN_MS").add(openingDuration);
+    } catch (ex) {
+      Components.utils.reportError("Unable to report telemetry for FX_IDENTITY_POPUP_OPEN_MS.");
+    }
+    document.getElementById('identity-popup-more-info-button').focus();
+  },
+
+  onDragStart: function (event) {
+    if (gURLBar.getAttribute("pageproxystate") != "valid")
+      return;
+
+    var value = content.location.href;
+    var urlString = value + "\n" + content.document.title;
+    var htmlString = "<a href=\"" + value + "\">" + value + "</a>";
+
+    var dt = event.dataTransfer;
+    dt.setData("text/x-moz-url", urlString);
+    dt.setData("text/uri-list", value);
+    dt.setData("text/plain", value);
+    dt.setData("text/html", htmlString);
+    dt.setDragImage(gProxyFavIcon, 16, 16);
+  }
+};
+
+let DownloadMonitorPanel = {
+  //////////////////////////////////////////////////////////////////////////////
+  //// DownloadMonitorPanel Member Variables
+
+  _panel: null,
+  _activeStr: null,
+  _pausedStr: null,
+  _lastTime: Infinity,
+  _listening: false,
+
+  get DownloadUtils() {
+    delete this.DownloadUtils;
+    Cu.import("resource://gre/modules/DownloadUtils.jsm", this);
+    return this.DownloadUtils;
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// DownloadMonitorPanel Public Methods
+
+  /**
+   * Initialize the status panel and member variables
+   */
+  init: function DMP_init() {
+    // Initialize "private" member variables
+    this._panel = document.getElementById("download-monitor");
+
+    // Cache the status strings
+    this._activeStr = gNavigatorBundle.getString("activeDownloads1");
+    this._pausedStr = gNavigatorBundle.getString("pausedDownloads1");
+
+    gDownloadMgr.addListener(this);
+    this._listening = true;
+
+    this.updateStatus();
+  },
+
+  uninit: function DMP_uninit() {
+    if (this._listening)
+      gDownloadMgr.removeListener(this);
+  },
+
+  inited: function DMP_inited() {
+    return this._panel != null;
+  },
+
+  /**
+   * Update status based on the number of active and paused downloads
+   */
+  updateStatus: function DMP_updateStatus() {
+    if (!this.inited())
+      return;
+
+    let numActive = gDownloadMgr.activeDownloadCount;
+
+    // Hide the panel and reset the "last time" if there's no downloads
+    if (numActive == 0) {
+      this._panel.hidden = true;
+      this._lastTime = Infinity;
+
+      return;
+    }
+
+    // Find the download with the longest remaining time
+    let numPaused = 0;
+    let maxTime = -Infinity;
+    let dls = gDownloadMgr.activeDownloads;
+    while (dls.hasMoreElements()) {
+      let dl = dls.getNext();
+      if (dl.state == gDownloadMgr.DOWNLOAD_DOWNLOADING) {
+        // Figure out if this download takes longer
+        if (dl.speed > 0 && dl.size > 0)
+          maxTime = Math.max(maxTime, (dl.size - dl.amountTransferred) / dl.speed);
+        else
+          maxTime = -1;
+      }
+      else if (dl.state == gDownloadMgr.DOWNLOAD_PAUSED)
+        numPaused++;
+    }
+
+    // Get the remaining time string and last sec for time estimation
+    let timeLeft;
+    [timeLeft, this._lastTime] =
+      this.DownloadUtils.getTimeLeft(maxTime, this._lastTime);
+
+    // Figure out how many downloads are currently downloading
+    let numDls = numActive - numPaused;
+    let status = this._activeStr;
+
+    // If all downloads are paused, show the paused message instead
+    if (numDls == 0) {
+      numDls = numPaused;
+      status = this._pausedStr;
+    }
+
+    // Get the correct plural form and insert the number of downloads and time
+    // left message if necessary
+    status = PluralForm.get(numDls, status);
+    status = status.replace("#1", numDls);
+    status = status.replace("#2", timeLeft);
+
+    // Update the panel and show it
+    this._panel.label = status;
+    this._panel.hidden = false;
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// nsIDownloadProgressListener
+
+  /**
+   * Update status for download progress changes
+   */
+  onProgressChange: function() {
+    this.updateStatus();
+  },
+
+  /**
+   * Update status for download state changes
+   */
+  onDownloadStateChange: function() {
+    this.updateStatus();
+  },
+
+  onStateChange: function(aWebProgress, aRequest, aStateFlags, aStatus, aDownload) {
+  },
+
+  onSecurityChange: function(aWebProgress, aRequest, aState, aDownload) {
+  },
+
+  //////////////////////////////////////////////////////////////////////////////
+  //// nsISupports
+
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsIDownloadProgressListener]),
+};
+
+function getNotificationBox(aWindow) {
+  var foundBrowser = gBrowser.getBrowserForDocument(aWindow.document);
+  if (foundBrowser)
+    return gBrowser.getNotificationBox(foundBrowser)
+  return null;
+};
+
+function getTabModalPromptBox(aWindow) {
+  var foundBrowser = gBrowser.getBrowserForDocument(aWindow.document);
+  if (foundBrowser)
+    return gBrowser.getTabModalPromptBox(foundBrowser);
+  return null;
+};
+
+/* DEPRECATED */
+function getBrowser() gBrowser;
+function getNavToolbox() gNavToolbox;
+
+let gPrivateBrowsingUI = {
+  _privateBrowsingService: null,
+  _searchBarValue: null,
+  _findBarValue: null,
+  _inited: false,
+
+  init: function PBUI_init() {
+    Services.obs.addObserver(this, "private-browsing", false);
+    Services.obs.addObserver(this, "private-browsing-transition-complete", false);
+
+    this._privateBrowsingService = Cc["@mozilla.org/privatebrowsing;1"].
+                                   getService(Ci.nsIPrivateBrowsingService);
+
+    if (this.privateBrowsingEnabled)
+      this.onEnterPrivateBrowsing(true);
+
+    this._inited = true;
+  },
+
+  uninit: function PBUI_unint() {
+    if (!this._inited)
+      return;
+
+    Services.obs.removeObserver(this, "private-browsing");
+    Services.obs.removeObserver(this, "private-browsing-transition-complete");
+  },
+
+  get _disableUIOnToggle() {
+    if (this._privateBrowsingService.autoStarted)
+      return false;
+
+    try {
+      return !gPrefService.getBoolPref("browser.privatebrowsing.keep_current_session");
+    }
+    catch (e) {
+      return true;
+    }
+  },
+
+  observe: function PBUI_observe(aSubject, aTopic, aData) {
+    if (aTopic == "private-browsing") {
+      if (aData == "enter")
+        this.onEnterPrivateBrowsing();
+      else if (aData == "exit")
+        this.onExitPrivateBrowsing();
+    }
+    else if (aTopic == "private-browsing-transition-complete") {
+      if (this._disableUIOnToggle) {
+        document.getElementById("Tools:PrivateBrowsing")
+                .removeAttribute("disabled");
+      }
+    }
+  },
+
+  _shouldEnter: function PBUI__shouldEnter() {
+    try {
+      // Never prompt if the session is not going to be closed, or if user has
+      // already requested not to be prompted.
+      if (gPrefService.getBoolPref("browser.privatebrowsing.dont_prompt_on_enter") ||
+          gPrefService.getBoolPref("browser.privatebrowsing.keep_current_session"))
+        return true;
+    }
+    catch (ex) { }
+
+    var bundleService = Cc["@mozilla.org/intl/stringbundle;1"].
+                        getService(Ci.nsIStringBundleService);
+    var pbBundle = bundleService.createBundle("chrome://browser/locale/browser.properties");
+    var brandBundle = bundleService.createBundle("chrome://branding/locale/brand.properties");
+
+    var appName = brandBundle.GetStringFromName("brandShortName");
+//@line 7026 "/build/src/mozilla-release/browser/base/content/browser.js"
+    var dialogTitle = pbBundle.GetStringFromName("privateBrowsingDialogTitle");
+    var header = pbBundle.GetStringFromName("privateBrowsingMessageHeader") + "\n\n";
+//@line 7029 "/build/src/mozilla-release/browser/base/content/browser.js"
+    var message = pbBundle.formatStringFromName("privateBrowsingMessage", [appName], 1);
+
+    var ps = Services.prompt;
+
+    var flags = ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_0 +
+                ps.BUTTON_TITLE_IS_STRING * ps.BUTTON_POS_1 +
+                ps.BUTTON_POS_0_DEFAULT;
+
+    var neverAsk = {value:false};
+    var button0Title = pbBundle.GetStringFromName("privateBrowsingYesTitle");
+    var button1Title = pbBundle.GetStringFromName("privateBrowsingNoTitle");
+    var neverAskText = pbBundle.GetStringFromName("privateBrowsingNeverAsk");
+
+    var result;
+    var choice = ps.confirmEx(null, dialogTitle, header + message,
+                              flags, button0Title, button1Title, null,
+                              neverAskText, neverAsk);
+
+    switch (choice) {
+    case 0: // Start Private Browsing
+      result = true;
+      if (neverAsk.value)
+        gPrefService.setBoolPref("browser.privatebrowsing.dont_prompt_on_enter", true);
+      break;
+    case 1: // Keep
+      result = false;
+      break;
+    }
+
+    return result;
+  },
+
+  onEnterPrivateBrowsing: function PBUI_onEnterPrivateBrowsing(aOnWindowOpen) {
+    if (BrowserSearch.searchBar)
+      this._searchBarValue = BrowserSearch.searchBar.textbox.value;
+
+    if (gFindBarInitialized)
+      this._findBarValue = gFindBar.getElement("findbar-textbox").value;
+
+    this._setPBMenuTitle("stop");
+
+    // Disable the Clear Recent History... menu item when in PB mode
+    // temporary fix until bug 463607 is fixed
+    document.getElementById("Tools:Sanitize").setAttribute("disabled", "true");
+
+    let docElement = document.documentElement;
+    if (this._privateBrowsingService.autoStarted) {
+      // Disable the menu item in auto-start mode
+      document.getElementById("privateBrowsingItem")
+              .setAttribute("disabled", "true");
+//@line 7080 "/build/src/mozilla-release/browser/base/content/browser.js"
+      document.getElementById("appmenu_privateBrowsing")
+              .setAttribute("disabled", "true");
+//@line 7083 "/build/src/mozilla-release/browser/base/content/browser.js"
+      document.getElementById("Tools:PrivateBrowsing")
+              .setAttribute("disabled", "true");
+      if (window.location.href == getBrowserURL())
+        docElement.setAttribute("privatebrowsingmode", "permanent");
+    }
+    else if (window.location.href == getBrowserURL()) {
+      // Adjust the window's title
+      docElement.setAttribute("title",
+        docElement.getAttribute("title_privatebrowsing"));
+      docElement.setAttribute("titlemodifier",
+        docElement.getAttribute("titlemodifier_privatebrowsing"));
+      docElement.setAttribute("privatebrowsingmode", "temporary");
+      gBrowser.updateTitlebar();
+    }
+
+    if (!aOnWindowOpen && this._disableUIOnToggle)
+      document.getElementById("Tools:PrivateBrowsing")
+              .setAttribute("disabled", "true");
+  },
+
+  onExitPrivateBrowsing: function PBUI_onExitPrivateBrowsing() {
+    if (BrowserSearch.searchBar) {
+      let searchBox = BrowserSearch.searchBar.textbox;
+      searchBox.reset();
+      if (this._searchBarValue) {
+        searchBox.value = this._searchBarValue;
+        this._searchBarValue = null;
+      }
+    }
+
+    if (gURLBar) {
+      gURLBar.editor.transactionManager.clear();
+    }
+
+    // Re-enable the Clear Recent History... menu item on exit of PB mode
+    // temporary fix until bug 463607 is fixed
+    document.getElementById("Tools:Sanitize").removeAttribute("disabled");
+
+    if (gFindBarInitialized) {
+      let findbox = gFindBar.getElement("findbar-textbox");
+      findbox.reset();
+      if (this._findBarValue) {
+        findbox.value = this._findBarValue;
+        this._findBarValue = null;
+      }
+    }
+
+    this._setPBMenuTitle("start");
+
+    if (window.location.href == getBrowserURL()) {
+      // Adjust the window's title
+      let docElement = document.documentElement;
+      docElement.setAttribute("title",
+        docElement.getAttribute("title_normal"));
+      docElement.setAttribute("titlemodifier",
+        docElement.getAttribute("titlemodifier_normal"));
+      docElement.removeAttribute("privatebrowsingmode");
+    }
+
+    // Enable the menu item in after exiting the auto-start mode
+    document.getElementById("privateBrowsingItem")
+            .removeAttribute("disabled");
+//@line 7146 "/build/src/mozilla-release/browser/base/content/browser.js"
+    document.getElementById("appmenu_privateBrowsing")
+            .removeAttribute("disabled");
+//@line 7149 "/build/src/mozilla-release/browser/base/content/browser.js"
+    document.getElementById("Tools:PrivateBrowsing")
+            .removeAttribute("disabled");
+
+    gLastOpenDirectory.reset();
+
+    if (this._disableUIOnToggle)
+      document.getElementById("Tools:PrivateBrowsing")
+              .setAttribute("disabled", "true");
+  },
+
+  _setPBMenuTitle: function PBUI__setPBMenuTitle(aMode) {
+    let pbMenuItem = document.getElementById("privateBrowsingItem");
+    pbMenuItem.setAttribute("label", pbMenuItem.getAttribute(aMode + "label"));
+    pbMenuItem.setAttribute("accesskey", pbMenuItem.getAttribute(aMode + "accesskey"));
+//@line 7164 "/build/src/mozilla-release/browser/base/content/browser.js"
+    let appmenupbMenuItem = document.getElementById("appmenu_privateBrowsing");
+    appmenupbMenuItem.setAttribute("label", appmenupbMenuItem.getAttribute(aMode + "label"));
+    appmenupbMenuItem.setAttribute("accesskey", appmenupbMenuItem.getAttribute(aMode + "accesskey"));
+//@line 7168 "/build/src/mozilla-release/browser/base/content/browser.js"
+  },
+
+  toggleMode: function PBUI_toggleMode() {
+    // prompt the users on entering the private mode, if needed
+    if (!this.privateBrowsingEnabled)
+      if (!this._shouldEnter())
+        return;
+
+    this._privateBrowsingService.privateBrowsingEnabled =
+      !this.privateBrowsingEnabled;
+  },
+
+  get privateBrowsingEnabled() {
+    return this._privateBrowsingService.privateBrowsingEnabled;
+  },
+
+  /**
+   * These accessors are used to support per-window Private Browsing mode.
+   * For now the getter returns nsIPrivateBrowsingService.privateBrowsingEnabled,
+   * and the setter should only be used in tests.
+   */
+  get privateWindow() {
+    return window.QueryInterface(Ci.nsIInterfaceRequestor)
+                 .getInterface(Ci.nsIWebNavigation)
+                 .QueryInterface(Ci.nsIDocShellTreeItem)
+                 .treeOwner
+                 .QueryInterface(Ci.nsIInterfaceRequestor)
+                 .getInterface(Ci.nsIXULWindow)
+                 .docShell.QueryInterface(Ci.nsILoadContext)
+                 .usePrivateBrowsing;
+  },
+
+  set privateWindow(val) {
+    return window.QueryInterface(Ci.nsIInterfaceRequestor)
+                 .getInterface(Ci.nsIWebNavigation)
+                 .QueryInterface(Ci.nsIDocShellTreeItem)
+                 .treeOwner
+                 .QueryInterface(Ci.nsIInterfaceRequestor)
+                 .getInterface(Ci.nsIXULWindow)
+                 .docShell.QueryInterface(Ci.nsILoadContext)
+                 .usePrivateBrowsing = val;
+  }
+};
+
+
+/**
+ * Switch to a tab that has a given URI, and focusses its browser window.
+ * If a matching tab is in this window, it will be switched to. Otherwise, other
+ * windows will be searched.
+ *
+ * @param aURI
+ *        URI to search for
+ * @param aOpenNew
+ *        True to open a new tab and switch to it, if no existing tab is found.
+ *        If no suitable window is found, a new one will be opened.
+ * @return True if an existing tab was found, false otherwise
+ */
+function switchToTabHavingURI(aURI, aOpenNew) {
+  // This will switch to the tab in aWindow having aURI, if present.
+  function switchIfURIInWindow(aWindow) {
+    let browsers = aWindow.gBrowser.browsers;
+    for (let i = 0; i < browsers.length; i++) {
+      let browser = browsers[i];
+      if (browser.currentURI.equals(aURI)) {
+        // Focus the matching window & tab
+        aWindow.focus();
+        aWindow.gBrowser.tabContainer.selectedIndex = i;
+        return true;
+      }
+    }
+    return false;
+  }
+
+  // This can be passed either nsIURI or a string.
+  if (!(aURI instanceof Ci.nsIURI))
+    aURI = Services.io.newURI(aURI, null, null);
+
+  let isBrowserWindow = !!window.gBrowser;
+
+  // Prioritise this window.
+  if (isBrowserWindow && switchIfURIInWindow(window))
+    return true;
+
+  let winEnum = Services.wm.getEnumerator("navigator:browser");
+  while (winEnum.hasMoreElements()) {
+    let browserWin = winEnum.getNext();
+    // Skip closed (but not yet destroyed) windows,
+    // and the current window (which was checked earlier).
+    if (browserWin.closed || browserWin == window)
+      continue;
+    if (switchIfURIInWindow(browserWin))
+      return true;
+  }
+
+  // No opened tab has that url.
+  if (aOpenNew) {
+    if (isBrowserWindow && isTabEmpty(gBrowser.selectedTab))
+      gBrowser.selectedBrowser.loadURI(aURI.spec);
+    else
+      openUILinkIn(aURI.spec, "tab");
+  }
+
+  return false;
+}
+
+function restoreLastSession() {
+  let ss = Cc["@mozilla.org/browser/sessionstore;1"].
+           getService(Ci.nsISessionStore);
+  ss.restoreLastSession();
+}
+
+var TabContextMenu = {
+  contextTab: null,
+  updateContextMenu: function updateContextMenu(aPopupMenu) {
+    this.contextTab = aPopupMenu.triggerNode.localName == "tab" ?
+                      aPopupMenu.triggerNode : gBrowser.selectedTab;
+    let disabled = gBrowser.tabs.length == 1;
+
+    // Enable the "Close Tab" menuitem when the window doesn't close with the last tab.
+    document.getElementById("context_closeTab").disabled =
+      disabled && gBrowser.tabContainer._closeWindowWithLastTab;
+
+    var menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-multiple");
+    for (var i = 0; i < menuItems.length; i++)
+      menuItems[i].disabled = disabled;
+
+    disabled = gBrowser.visibleTabs.length == 1;
+    menuItems = aPopupMenu.getElementsByAttribute("tbattr", "tabbrowser-multiple-visible");
+    for (var i = 0; i < menuItems.length; i++)
+      menuItems[i].disabled = disabled;
+
+    // Session store
+    document.getElementById("context_undoCloseTab").disabled =
+      Cc["@mozilla.org/browser/sessionstore;1"].
+      getService(Ci.nsISessionStore).
+      getClosedTabCount(window) == 0;
+
+    // Only one of pin/unpin should be visible
+    document.getElementById("context_pinTab").hidden = this.contextTab.pinned;
+    document.getElementById("context_unpinTab").hidden = !this.contextTab.pinned;
+
+    // Disable "Close other Tabs" if there is only one unpinned tab and
+    // hide it when the user rightclicked on a pinned tab.
+    let unpinnedTabs = gBrowser.visibleTabs.length - gBrowser._numPinnedTabs;
+    document.getElementById("context_closeOtherTabs").disabled = unpinnedTabs <= 1;
+    document.getElementById("context_closeOtherTabs").hidden = this.contextTab.pinned;
+
+    // Hide "Bookmark All Tabs" for a pinned tab.  Update its state if visible.
+    let bookmarkAllTabs = document.getElementById("context_bookmarkAllTabs");
+    bookmarkAllTabs.hidden = this.contextTab.pinned;
+    if (!bookmarkAllTabs.hidden)
+      PlacesCommandHook.updateBookmarkAllTabsCommand();
+
+    // Hide "Move to Group" if it's a pinned tab.
+    document.getElementById("context_tabViewMenu").hidden =
+      (this.contextTab.pinned || !TabView.firstUseExperienced);
+  }
+};
+
+XPCOMUtils.defineLazyGetter(this, "HUDConsoleUI", function () {
+  let tempScope = {};
+  Cu.import("resource:///modules/HUDService.jsm", tempScope);
+  try {
+    return tempScope.HUDService.consoleUI;
+  }
+  catch (ex) {
+    Components.utils.reportError(ex);
+  }
+});
+
+// Prompt user to restart the browser in safe mode
+function safeModeRestart()
+{
+  // prompt the user to confirm
+  let promptTitle = gNavigatorBundle.getString("safeModeRestartPromptTitle");
+  let promptMessage =
+    gNavigatorBundle.getString("safeModeRestartPromptMessage");
+  let restartText = gNavigatorBundle.getString("safeModeRestartButton");
+  let buttonFlags = (Services.prompt.BUTTON_POS_0 *
+                     Services.prompt.BUTTON_TITLE_IS_STRING) +
+                    (Services.prompt.BUTTON_POS_1 *
+                     Services.prompt.BUTTON_TITLE_CANCEL) +
+                    Services.prompt.BUTTON_POS_0_DEFAULT;
+
+  let rv = Services.prompt.confirmEx(window, promptTitle, promptMessage,
+                                     buttonFlags, restartText, null, null,
+                                     null, {});
+  if (rv == 0) {
+    let appStartup = Cc["@mozilla.org/toolkit/app-startup;1"].
+                     getService(Ci.nsIAppStartup);
+    appStartup.restartInSafeMode(Ci.nsIAppStartup.eAttemptQuit);
+  }
+}
+
+/* duplicateTabIn duplicates tab in a place specified by the parameter |where|.
+ *
+ * |where| can be:
+ *  "tab"         new tab
+ *  "tabshifted"  same as "tab" but in background if default is to select new
+ *                tabs, and vice versa
+ *  "window"      new window
+ *
+ * delta is the offset to the history entry that you want to load.
+ */
+function duplicateTabIn(aTab, where, delta) {
+  let newTab = Cc['@mozilla.org/browser/sessionstore;1']
+                 .getService(Ci.nsISessionStore)
+                 .duplicateTab(window, aTab, delta);
+
+  switch (where) {
+    case "window":
+      gBrowser.hideTab(newTab);
+      gBrowser.replaceTabWithWindow(newTab);
+      break;
+    case "tabshifted":
+      // A background tab has been opened, nothing else to do here.
+      break;
+    case "tab":
+      gBrowser.selectedTab = newTab;
+      break;
+  }
+}
+
+function toggleAddonBar() {
+  let addonBar = document.getElementById("addon-bar");
+  setToolbarVisibility(addonBar, addonBar.collapsed);
+}
+
+var Scratchpad = {
+  prefEnabledName: "devtools.scratchpad.enabled",
+
+  openScratchpad: function SP_openScratchpad() {
+    return this.ScratchpadManager.openScratchpad();
+  }
+};
+
+XPCOMUtils.defineLazyGetter(Scratchpad, "ScratchpadManager", function() {
+  let tmp = {};
+  Cu.import("resource:///modules/devtools/scratchpad-manager.jsm", tmp);
+  return tmp.ScratchpadManager;
+});
+
+var ResponsiveUI = {
+  toggle: function RUI_toggle() {
+    this.ResponsiveUIManager.toggle(window, gBrowser.selectedTab);
+  }
+};
+
+XPCOMUtils.defineLazyGetter(ResponsiveUI, "ResponsiveUIManager", function() {
+  let tmp = {};
+  Cu.import("resource:///modules/devtools/responsivedesign.jsm", tmp);
+  return tmp.ResponsiveUIManager;
+});
+
+var StyleEditor = {
+  prefEnabledName: "devtools.styleeditor.enabled",
+  /**
+   * Opens the style editor. If the UI is already open, it will be focused.
+   *
+   * @param {CSSStyleSheet} [aSelectedStyleSheet] default Stylesheet.
+   * @param {Number} [aLine] Line to which the caret should be moved (one-indexed).
+   * @param {Number} [aCol] Column to which the caret should be moved (one-indexed).
+   */
+  openChrome: function SE_openChrome(aSelectedStyleSheet, aLine, aCol)
+  {
+    const CHROME_URL = "chrome://browser/content/styleeditor.xul";
+    const CHROME_WINDOW_TYPE = "Tools:StyleEditor";
+    const CHROME_WINDOW_FLAGS = "chrome,centerscreen,resizable,dialog=no";
+
+    // focus currently open Style Editor window for this document, if any
+    let contentWindow = gBrowser.selectedBrowser.contentWindow;
+    let contentWindowID = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).
+      getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID;
+    let enumerator = Services.wm.getEnumerator(CHROME_WINDOW_TYPE);
+    while (enumerator.hasMoreElements()) {
+      var win = enumerator.getNext();
+      if (win.styleEditorChrome.contentWindowID == contentWindowID) {
+        if (aSelectedStyleSheet) {
+          win.styleEditorChrome.selectStyleSheet(aSelectedStyleSheet, aLine, aCol);
+        }
+        win.focus();
+        return win;
+      }
+    }
+
+    let args = {
+      contentWindow: contentWindow,
+      selectedStyleSheet: aSelectedStyleSheet,
+      line: aLine,
+      col: aCol
+    };
+    args.wrappedJSObject = args;
+    let chromeWindow = Services.ww.openWindow(null, CHROME_URL, "_blank",
+                                              CHROME_WINDOW_FLAGS, args);
+    chromeWindow.focus();
+    return chromeWindow;
+  }
+};
+
+
+XPCOMUtils.defineLazyGetter(window, "gShowPageResizers", function () {
+//@line 7475 "/build/src/mozilla-release/browser/base/content/browser.js"
+  return false;
+//@line 7477 "/build/src/mozilla-release/browser/base/content/browser.js"
+});
+
+var MousePosTracker = {
+  _listeners: [],
+  _x: 0,
+  _y: 0,
+  get _windowUtils() {
+    delete this._windowUtils;
+    return this._windowUtils = window.getInterface(Ci.nsIDOMWindowUtils);
+  },
+
+  addListener: function (listener) {
+    if (this._listeners.indexOf(listener) >= 0)
+      return;
+
+    listener._hover = false;
+    this._listeners.push(listener);
+
+    this._callListener(listener);
+  },
+
+  removeListener: function (listener) {
+    var index = this._listeners.indexOf(listener);
+    if (index < 0)
+      return;
+
+    this._listeners.splice(index, 1);
+  },
+
+  handleEvent: function (event) {
+    var screenPixelsPerCSSPixel = this._windowUtils.screenPixelsPerCSSPixel;
+    this._x = event.screenX / screenPixelsPerCSSPixel - window.mozInnerScreenX;
+    this._y = event.screenY / screenPixelsPerCSSPixel - window.mozInnerScreenY;
+
+    this._listeners.forEach(function (listener) {
+      try {
+        this._callListener(listener);
+      } catch (e) {
+        Cu.reportError(e);
+      }
+    }, this);
+  },
+
+  _callListener: function (listener) {
+    let rect = listener.getMouseTargetRect();
+    let hover = this._x >= rect.left &&
+                this._x <= rect.right &&
+                this._y >= rect.top &&
+                this._y <= rect.bottom;
+
+    if (hover == listener._hover)
+      return;
+
+    listener._hover = hover;
+
+    if (hover) {
+      if (listener.onMouseEnter)
+        listener.onMouseEnter();
+    } else {
+      if (listener.onMouseLeave)
+        listener.onMouseLeave();
+    }
+  }
+};
+
+function focusNextFrame(event) {
+  let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
+  let dir = event.shiftKey ? fm.MOVEFOCUS_BACKWARDDOC : fm.MOVEFOCUS_FORWARDDOC;
+  let element = fm.moveFocus(window, null, dir, fm.FLAG_BYKEY);
+  if (element.ownerDocument == document)
+    focusAndSelectUrlBar();
+}
diff -Nuar omni_old/chrome/browser/content/browser/browser.xul omni/chrome/browser/content/browser/browser.xul
--- omni_old/chrome/browser/content/browser/browser.xul	2010-01-01 00:00:00.000000000 +0800
+++ omni/chrome/browser/content/browser/browser.xul	2012-08-30 14:42:50.580546596 +0800
@@ -1745,8 +1745,9 @@
             <hbox id="identity-box-inner" align="center">
               <hbox id="page-proxy-stack"
                     onclick="PageProxyClickHandler(event);">
-                <image id="page-proxy-favicon"
-                       pageproxystate="invalid"/>
+                <image id="page-proxy-favicon" validate="never"
+                       pageproxystate="invalid"
+                       onerror="this.removeAttribute('src');"/>
               </hbox>
               <hbox id="identity-icon-labels">
                 <label id="identity-icon-label" class="plain" flex="1"/>
diff -Nuar omni_old/chrome/browser/content/browser/browser.xul.orig omni/chrome/browser/content/browser/browser.xul.orig
--- omni_old/chrome/browser/content/browser/browser.xul.orig	1970-01-01 08:00:00.000000000 +0800
+++ omni/chrome/browser/content/browser/browser.xul.orig	2010-01-01 00:00:00.000000000 +0800
@@ -0,0 +1,2956 @@
+<?xml version="1.0"?>
+
+<?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/content/places/places.css" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/skin/devtools/common.css" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/skin/devtools/webconsole.css" type="text/css"?>
+<?xml-stylesheet href="chrome://browser/skin/" type="text/css"?>
+
+<?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?>
+<?xul-overlay href="chrome://browser/content/baseMenuOverlay.xul"?>
+<?xul-overlay href="chrome://browser/content/places/placesOverlay.xul"?>
+
+<!DOCTYPE window [
+<!ENTITY % brandDTD SYSTEM "chrome://branding/locale/brand.dtd" >
+%brandDTD;
+<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd" >
+%browserDTD;
+<!ENTITY % baseMenuDTD SYSTEM "chrome://browser/locale/baseMenuOverlay.dtd" >
+%baseMenuDTD;
+<!ENTITY % charsetDTD SYSTEM "chrome://global/locale/charsetOverlay.dtd" >
+%charsetDTD;
+<!ENTITY % textcontextDTD SYSTEM "chrome://global/locale/textcontext.dtd" >
+%textcontextDTD;
+<!ENTITY % customizeToolbarDTD SYSTEM "chrome://global/locale/customizeToolbar.dtd">
+  %customizeToolbarDTD;
+<!ENTITY % placesDTD SYSTEM "chrome://browser/locale/places/places.dtd">
+%placesDTD;
+<!ENTITY % safebrowsingDTD SYSTEM "chrome://browser/locale/safebrowsing/phishing-afterload-warning-message.dtd">
+%safebrowsingDTD;
+<!ENTITY % aboutHomeDTD SYSTEM "chrome://browser/locale/aboutHome.dtd">
+%aboutHomeDTD;
+<!ENTITY % debuggerDTD SYSTEM "chrome://browser/locale/devtools/debugger.dtd">
+%debuggerDTD;
+]>
+
+
+<window id="main-window"
+        xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+        xmlns:svg="http://www.w3.org/2000/svg"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+        onload="BrowserStartup()" onunload="BrowserShutdown()" onclose="return WindowIsClosing();"
+        title="&mainWindow.title;"
+        title_normal="&mainWindow.title;"
+        title_privatebrowsing="&mainWindow.titlemodifier; &mainWindow.titlePrivateBrowsingSuffix;"
+        titlemodifier="&mainWindow.titlemodifier;"
+        titlemodifier_normal="&mainWindow.titlemodifier;"
+        titlemodifier_privatebrowsing="&mainWindow.titlemodifier; &mainWindow.titlePrivateBrowsingSuffix;"
+        titlemenuseparator="&mainWindow.titlemodifiermenuseparator;"
+        lightweightthemes="true"
+        lightweightthemesfooter="browser-bottombox"
+        windowtype="navigator:browser"
+        macanimationtype="document"
+        screenX="4" screenY="4"
+        browsingmode="normal"
+        fullscreenbutton="true"
+        persist="screenX screenY width height sizemode">
+
+
+<script type="application/javascript" src="chrome://global/content/printUtils.js"/>
+<script type="application/javascript" src="chrome://global/content/viewZoomOverlay.js"/>
+<script type="application/javascript" src="chrome://browser/content/places/browserPlacesViews.js"/>
+<script type="application/javascript" src="chrome://browser/content/browser.js"/>
+<script type="application/javascript" src="chrome://browser/content/downloads/downloads.js"/>
+<script type="application/javascript" src="chrome://browser/content/downloads/indicator.js"/>
+<script type="application/javascript" src="chrome://global/content/inlineSpellCheckUI.js"/>
+<script type="application/javascript" src="chrome://global/content/viewSourceUtils.js"/>
+<script type="application/javascript" src="chrome://browser/content/nsContextMenu.js"/>
+
+<script type="application/javascript" src="chrome://browser/content/safebrowsing/sb-loader.js"/>
+<script type="application/javascript" src="chrome://global/content/contentAreaUtils.js"/>
+
+<script type="application/javascript" src="chrome://browser/content/places/editBookmarkOverlay.js"/>
+
+
+
+  <stringbundleset id="stringbundleset">
+    <stringbundle id="bundle_brand" src="chrome://branding/locale/brand.properties"/>
+    <stringbundle id="bundle_shell" src="chrome://browser/locale/shellservice.properties"/>
+    <stringbundle id="bundle_preferences" src="chrome://browser/locale/preferences/preferences.properties"/>
+  </stringbundleset>
+
+  <commandset id="mainCommandSet">
+    <command id="cmd_newNavigator"                 oncommand="OpenBrowserWindow()"/>
+    <command id="cmd_handleBackspace" oncommand="BrowserHandleBackspace();" />
+    <command id="cmd_handleShiftBackspace" oncommand="BrowserHandleShiftBackspace();" />
+
+    <command id="cmd_newNavigatorTab" oncommand="BrowserOpenTab();"/>
+    <command id="Browser:OpenFile"  oncommand="BrowserOpenFileWindow();"/>
+    <command id="Browser:SavePage" oncommand="saveDocument(window.content.document);"/>
+
+    <command id="Browser:SendLink"
+             oncommand="MailIntegration.sendLinkForWindow(window.content);"/>
+
+    <command id="cmd_pageSetup" oncommand="PrintUtils.showPageSetup();"/>
+    <command id="cmd_print" oncommand="PrintUtils.print();"/>
+    <command id="cmd_printPreview" oncommand="PrintUtils.printPreview(PrintPreviewListener);"/>
+    <command id="cmd_close" oncommand="BrowserCloseTabOrWindow()"/>
+    <command id="cmd_closeWindow" oncommand="BrowserTryToCloseWindow()"/>
+    <command id="cmd_ToggleTabsOnTop" oncommand="TabsOnTop.toggle()"/>
+    <command id="cmd_CustomizeToolbars" oncommand="BrowserCustomizeToolbar()"/>
+    <command id="cmd_quitApplication" oncommand="goQuitApplication()"/>
+
+
+    <commandset id="editMenuCommands"/>
+
+    <command id="View:PageSource" oncommand="BrowserViewSourceOfDocument(content.document);" observes="isImage"/>
+    <command id="View:PageInfo" oncommand="BrowserPageInfo();"/>
+    <command id="View:FullScreen" oncommand="BrowserFullScreen();"/>
+    <command id="cmd_find"
+             oncommand="gFindBar.onFindCommand();"
+             observes="isImage"/>
+    <command id="cmd_findAgain"
+             oncommand="gFindBar.onFindAgainCommand(false);"
+             observes="isImage"/>
+    <command id="cmd_findPrevious"
+             oncommand="gFindBar.onFindAgainCommand(true);"
+             observes="isImage"/>
+    <!-- work-around bug 392512 -->
+    <command id="Browser:AddBookmarkAs"
+             oncommand="PlacesCommandHook.bookmarkCurrentPage(true, PlacesUtils.bookmarksMenuFolderId);"/>
+    <!-- The command disabled state must be manually updated through
+         PlacesCommandHook.updateBookmarkAllTabsCommand() -->
+    <command id="Browser:BookmarkAllTabs"
+             oncommand="PlacesCommandHook.bookmarkCurrentPages();"/>
+    <command id="Browser:Home"    oncommand="BrowserHome();"/>
+    <command id="Browser:Back"    oncommand="BrowserBack();" disabled="true"/>
+    <command id="Browser:BackOrBackDuplicate" oncommand="BrowserBack(event);" disabled="true">
+      <observes element="Browser:Back" attribute="disabled"/>
+    </command>
+    <command id="Browser:Forward" oncommand="BrowserForward();" disabled="true"/>
+    <command id="Browser:ForwardOrForwardDuplicate" oncommand="BrowserForward(event);" disabled="true">
+      <observes element="Browser:Forward" attribute="disabled"/>
+    </command>
+    <command id="Browser:Stop"    oncommand="BrowserStop();" disabled="true"/>
+    <command id="Browser:Reload"  oncommand="if (event.shiftKey) BrowserReloadSkipCache(); else BrowserReload()" disabled="true"/>
+    <command id="Browser:ReloadOrDuplicate" oncommand="BrowserReloadOrDuplicate(event)" disabled="true">
+      <observes element="Browser:Reload" attribute="disabled"/>
+    </command>
+    <command id="Browser:ReloadSkipCache" oncommand="BrowserReloadSkipCache()" disabled="true">
+      <observes element="Browser:Reload" attribute="disabled"/>
+    </command>
+    <command id="Browser:NextTab" oncommand="gBrowser.tabContainer.advanceSelectedTab(1, true);"/>
+    <command id="Browser:PrevTab" oncommand="gBrowser.tabContainer.advanceSelectedTab(-1, true);"/>
+    <command id="Browser:ShowAllTabs" oncommand="allTabs.open();"/>
+    <command id="Browser:ToggleTabView" oncommand="TabView.toggle();"/>
+    <command id="Browser:FocusNextFrame" oncommand="focusNextFrame(event);"/>
+    <command id="cmd_fullZoomReduce"  oncommand="FullZoom.reduce()"/>
+    <command id="cmd_fullZoomEnlarge" oncommand="FullZoom.enlarge()"/>
+    <command id="cmd_fullZoomReset"   oncommand="FullZoom.reset()"/>
+    <command id="cmd_fullZoomToggle"  oncommand="ZoomManager.toggleZoom();"/>
+    <command id="Browser:OpenLocation" oncommand="openLocation();"/>
+
+    <command id="Tools:Search" oncommand="BrowserSearch.webSearch();"/>
+    <command id="Tools:Downloads" oncommand="BrowserDownloadsUI();"/>
+    <command id="Tools:DevToolbar" oncommand="DeveloperToolbar.toggle();" disabled="true"/>
+    <command id="Tools:WebConsole" oncommand="HUDConsoleUI.toggleHUD();"/>
+    <command id="Tools:Inspect" oncommand="InspectorUI.toggleInspectorUI();" disabled="true"/>
+    <command id="Tools:Debugger" oncommand="DebuggerUI.toggleDebugger();" disabled="true"/>
+    <command id="Tools:RemoteDebugger" oncommand="DebuggerUI.toggleRemoteDebugger();" disabled="true"/>
+    <command id="Tools:ChromeDebugger" oncommand="DebuggerUI.toggleChromeDebugger();" disabled="true"/>
+    <command id="Tools:Scratchpad" oncommand="Scratchpad.openScratchpad();" disabled="true"/>
+    <command id="Tools:StyleEditor" oncommand="StyleEditor.openChrome();" disabled="true"/>
+    <command id="Tools:ResponsiveUI" oncommand="ResponsiveUI.toggle();" disabled="true"/>
+    <command id="Tools:Addons" oncommand="BrowserOpenAddonsMgr();"/>
+    <command id="Tools:Sanitize"
+     oncommand="Cc['@mozilla.org/browser/browserglue;1'].getService(Ci.nsIBrowserGlue).sanitize(window);"/>
+    <command id="Tools:PrivateBrowsing" oncommand="gPrivateBrowsingUI.toggleMode();"/>
+    <command id="History:UndoCloseTab" oncommand="undoCloseTab();"/>
+    <command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/>
+    <command id="Browser:ToggleAddonBar" oncommand="toggleAddonBar();"/>
+  </commandset>
+
+  <commandset id="placesCommands">
+    <command id="Browser:ShowAllBookmarks"
+             oncommand="PlacesCommandHook.showPlacesOrganizer('AllBookmarks');"/>
+    <command id="Browser:ShowAllHistory"
+             oncommand="PlacesCommandHook.showPlacesOrganizer('History');"/>
+  </commandset>
+
+  <commandset id="inspectorCommands">
+    <command id="Inspector:Inspect"
+             oncommand="InspectorUI.toggleInspection();"/>
+    <command id="Inspector:Sidebar"
+             oncommand="InspectorUI.toggleSidebar();"/>
+    <command id="Inspector:Tilt"
+             oncommand="Tilt.initialize();"/>
+    <command id="Inspector:HTMLPanel"
+             oncommand="InspectorUI.toggleHTMLPanel();"/>
+    <command id="Inspector:CopyInner"
+             oncommand="InspectorUI.copyInnerHTML();"/>
+    <command id="Inspector:CopyOuter"
+             oncommand="InspectorUI.copyOuterHTML();"/>
+    <command id="Inspector:DeleteNode"
+             oncommand="InspectorUI.deleteNode();"/>
+    <command id="Inspector:ToggleVeil"
+             oncommand="InspectorUI.toggleVeil();"/>
+    <command id="Inspector:ToggleInfobar"
+             oncommand="InspectorUI.toggleInfobar();"/>
+  </commandset>
+
+  <broadcasterset id="mainBroadcasterSet">
+    <broadcaster id="viewBookmarksSidebar" autoCheck="false" label="&bookmarksButton.label;"
+                 type="checkbox" group="sidebar" sidebarurl="chrome://browser/content/bookmarks/bookmarksPanel.xul"
+                 oncommand="toggleSidebar('viewBookmarksSidebar');"/>
+
+    <!-- for both places and non-places, the sidebar lives at
+         chrome://browser/content/history/history-panel.xul so there are no
+         problems when switching between versions -->
+    <broadcaster id="viewHistorySidebar" autoCheck="false" sidebartitle="&historyButton.label;"
+                 type="checkbox" group="sidebar"
+                 sidebarurl="chrome://browser/content/history/history-panel.xul"
+                 oncommand="toggleSidebar('viewHistorySidebar');"/>
+
+    <broadcaster id="viewWebPanelsSidebar" autoCheck="false"
+                 type="checkbox" group="sidebar" sidebarurl="chrome://browser/content/web-panels.xul"
+                 oncommand="toggleSidebar('viewWebPanelsSidebar');"/>
+
+    <!-- popup blocking menu items -->
+    <broadcaster id="blockedPopupAllowSite"
+                 accesskey="&allowPopups.accesskey;"
+                 oncommand="gPopupBlockerObserver.toggleAllowPopupsForSite(event);"/>
+    <broadcaster id="blockedPopupEditSettings"
+                 label="&editPopupSettingsUnix.label;"
+                 accesskey="&editPopupSettings.accesskey;"
+                 oncommand="gPopupBlockerObserver.editPopupSettings();"/>
+    <broadcaster id="blockedPopupDontShowMessage"
+                 accesskey="&dontShowMessage.accesskey;"
+                 type="checkbox"
+                 oncommand="gPopupBlockerObserver.dontShowMessage();"/>
+    <broadcaster id="blockedPopupsSeparator"/>
+    <broadcaster id="isImage"/>
+    <broadcaster id="isFrameImage"/>
+    <broadcaster id="singleFeedMenuitemState" disabled="true"/>
+    <broadcaster id="multipleFeedsMenuState" hidden="true"/>
+    <broadcaster id="tabviewGroupsNumber" groups="1"/>
+    <broadcaster id="sync-setup-state"/>
+    <broadcaster id="sync-syncnow-state"/>
+    <broadcaster id="workOfflineMenuitemState"/>
+  </broadcasterset>
+
+  <keyset id="mainKeyset">
+    <key id="key_newNavigator"
+         key="&newNavigatorCmd.key;"
+         command="cmd_newNavigator"
+         modifiers="accel"/>
+    <key id="key_newNavigatorTab" key="&tabCmd.commandkey;" modifiers="accel" command="cmd_newNavigatorTab"/>
+    <key id="focusURLBar" key="&openCmd.commandkey;" command="Browser:OpenLocation"
+         modifiers="accel"/>
+    <key id="focusURLBar2" key="&urlbar.accesskey;" command="Browser:OpenLocation"
+         modifiers="alt"/>
+
+    <key id="key_search" key="&searchFocus.commandkey;" command="Tools:Search" modifiers="accel"/>
+    <key id="key_search2" key="&searchFocusUnix.commandkey;" command="Tools:Search" modifiers="accel"/>
+    <key id="key_openDownloads" key="&downloadsUnix.commandkey;" command="Tools:Downloads" modifiers="accel,shift"/>
+    <key id="key_openAddons" key="&addons.commandkey;" command="Tools:Addons" modifiers="accel,shift"/>
+    <key id="key_errorConsole" key="&errorConsoleCmd.commandkey;" oncommand="toJavaScriptConsole();" modifiers="accel,shift" disabled="true"/>
+    <key id="key_devToolbar" key="&devToolbar.commandkey;" command="Tools:DevToolbar"
+         modifiers="accel,shift"
+    />
+    <key id="key_webConsole" key="&webConsoleCmd.commandkey;" oncommand="HUDConsoleUI.toggleHUD();"
+        modifiers="accel,shift"
+    />
+    <key id="key_debugger" key="&debuggerMenu.commandkey;" command="Tools:Debugger"
+        modifiers="accel,shift"
+    />
+    <key id="key_inspect" key="&inspectMenu.commandkey;" command="Inspector:Inspect"
+        modifiers="accel,shift"
+    />
+    <key id="key_responsiveUI" key="&responsiveDesignTool.commandkey;" command="Tools:ResponsiveUI"
+        modifiers="accel,shift"
+    />
+    <key id="key_scratchpad" keycode="&scratchpad.keycode;" modifiers="shift"
+         keytext="&scratchpad.keytext;" command="Tools:Scratchpad"/>
+    <key id="key_styleeditor" keycode="&styleeditor.keycode;" modifiers="shift"
+         keytext="&styleeditor.keytext;" command="Tools:StyleEditor"/>
+    <key id="openFileKb" key="&openFileCmd.commandkey;" command="Browser:OpenFile"  modifiers="accel"/>
+    <key id="key_savePage" key="&savePageCmd.commandkey;" command="Browser:SavePage" modifiers="accel"/>
+    <key id="printKb" key="&printCmd.commandkey;" command="cmd_print"  modifiers="accel"/>
+    <key id="key_close" key="&closeCmd.key;" command="cmd_close" modifiers="accel"/>
+    <key id="key_closeWindow" key="&closeCmd.key;" command="cmd_closeWindow" modifiers="accel,shift"/>
+    <key id="key_undo"
+         key="&undoCmd.key;"
+         modifiers="accel"/>
+    <key id="key_redo" key="&undoCmd.key;" modifiers="accel,shift"/>
+    <key id="key_cut"
+         key="&cutCmd.key;"
+         modifiers="accel"/>
+    <key id="key_copy"
+         key="&copyCmd.key;"
+         modifiers="accel"/>
+    <key id="key_paste"
+         key="&pasteCmd.key;"
+         modifiers="accel"/>
+    <key id="key_delete" keycode="VK_DELETE" command="cmd_delete"/>
+    <key id="key_selectAll" key="&selectAllCmd.key;" modifiers="accel"/>
+
+    <key keycode="VK_BACK" command="cmd_handleBackspace"/>
+    <key keycode="VK_BACK" command="cmd_handleShiftBackspace" modifiers="shift"/>
+    <key id="goBackKb"  keycode="VK_LEFT" command="Browser:Back" modifiers="alt"/>
+    <key id="goForwardKb"  keycode="VK_RIGHT" command="Browser:Forward" modifiers="alt"/>
+    <key id="goBackKb2" key="&goBackCmd.commandKey;" command="Browser:Back" modifiers="accel"/>
+    <key id="goForwardKb2" key="&goForwardCmd.commandKey;" command="Browser:Forward" modifiers="accel"/>
+    <key id="goHome" keycode="VK_HOME" command="Browser:Home" modifiers="alt"/>
+    <key keycode="VK_F5" command="Browser:Reload"/>
+    <key id="showAllHistoryKb" key="&showAllHistoryCmd.commandkey;" command="Browser:ShowAllHistory" modifiers="accel,shift"/>
+    <key keycode="VK_F5" command="Browser:ReloadSkipCache" modifiers="accel"/>
+    <key keycode="VK_F6" command="Browser:FocusNextFrame"/>
+    <key keycode="VK_F6" command="Browser:FocusNextFrame" modifiers="shift"/>
+    <key id="key_fullScreen" keycode="VK_F11" command="View:FullScreen"/>
+    <key key="&reloadCmd.commandkey;" command="Browser:Reload" modifiers="accel" id="key_reload"/>
+    <key key="&reloadCmd.commandkey;" command="Browser:ReloadSkipCache" modifiers="accel,shift"/>
+    <key id="key_viewSource" key="&pageSourceCmd.commandkey;" command="View:PageSource" modifiers="accel"/>
+    <key id="key_viewInfo"   key="&pageInfoCmd.commandkey;"   command="View:PageInfo"   modifiers="accel"/>
+    <key id="key_find" key="&findOnCmd.commandkey;" command="cmd_find" modifiers="accel"/>
+    <key id="key_findAgain" key="&findAgainCmd.commandkey;" command="cmd_findAgain" modifiers="accel"/>
+    <key id="key_findPrevious" key="&findAgainCmd.commandkey;" command="cmd_findPrevious" modifiers="accel,shift"/>
+    <key keycode="&findAgainCmd.commandkey2;" command="cmd_findAgain"/>
+    <key keycode="&findAgainCmd.commandkey2;"  command="cmd_findPrevious" modifiers="shift"/>
+
+    <key id="addBookmarkAsKb" key="&bookmarkThisPageCmd.commandkey;" command="Browser:AddBookmarkAs" modifiers="accel"/>
+    <key id="manBookmarkKb" key="&bookmarksGtkCmd.commandkey;" command="Browser:ShowAllBookmarks" modifiers="accel,shift"/>
+    <key id="viewBookmarksSidebarKb" key="&bookmarksCmd.commandkey;" command="viewBookmarksSidebar" modifiers="accel"/>
+
+    <key id="key_stop" keycode="VK_ESCAPE" oncommand="BrowserStop();"/>
+
+
+    <key id="key_gotoHistory"
+         key="&historySidebarCmd.commandKey;"
+         modifiers="accel"
+         command="viewHistorySidebar"/>
+
+    <key id="key_fullZoomReduce"  key="&fullZoomReduceCmd.commandkey;"   command="cmd_fullZoomReduce"  modifiers="accel"/>
+    <key                          key="&fullZoomReduceCmd.commandkey2;"  command="cmd_fullZoomReduce"  modifiers="accel"/>
+    <key id="key_fullZoomEnlarge" key="&fullZoomEnlargeCmd.commandkey;"  command="cmd_fullZoomEnlarge" modifiers="accel"/>
+    <key                          key="&fullZoomEnlargeCmd.commandkey2;" command="cmd_fullZoomEnlarge" modifiers="accel"/>
+    <key                          key="&fullZoomEnlargeCmd.commandkey3;" command="cmd_fullZoomEnlarge" modifiers="accel"/>
+    <key id="key_fullZoomReset"   key="&fullZoomResetCmd.commandkey;"    command="cmd_fullZoomReset"   modifiers="accel"/>
+    <key                          key="&fullZoomResetCmd.commandkey2;"   command="cmd_fullZoomReset"   modifiers="accel"/>
+
+    <key id="key_showAllTabs" command="Browser:ShowAllTabs" keycode="VK_TAB" modifiers="control,shift"/>
+
+    <key id="key_switchTextDirection" key="&bidiSwitchTextDirectionItem.commandkey;" command="cmd_switchTextDirection" modifiers="accel,shift" />
+
+    <key id="key_tabview" key="&tabView.commandkey;" command="Browser:ToggleTabView" modifiers="accel,shift"/>
+
+    <key id="key_privatebrowsing" command="Tools:PrivateBrowsing" key="&privateBrowsingCmd.commandkey;" modifiers="accel,shift"/>
+    <key id="key_sanitize" command="Tools:Sanitize" keycode="VK_DELETE" modifiers="accel,shift"/>
+    <key id="key_quitApplication" key="&quitApplicationCmdMac.key;" command="cmd_quitApplication" modifiers="accel"/>
+
+    <key id="key_undoCloseTab" command="History:UndoCloseTab" key="&tabCmd.commandkey;" modifiers="accel,shift"/>
+    <key id="key_undoCloseWindow" command="History:UndoCloseWindow" key="&newNavigatorCmd.key;" modifiers="accel,shift"/>
+
+
+   <key id="key_selectTab1" oncommand="gBrowser.selectTabAtIndex(0, event);" key="1" modifiers="alt"/>
+   <key id="key_selectTab2" oncommand="gBrowser.selectTabAtIndex(1, event);" key="2" modifiers="alt"/>
+   <key id="key_selectTab3" oncommand="gBrowser.selectTabAtIndex(2, event);" key="3" modifiers="alt"/>
+   <key id="key_selectTab4" oncommand="gBrowser.selectTabAtIndex(3, event);" key="4" modifiers="alt"/>
+   <key id="key_selectTab5" oncommand="gBrowser.selectTabAtIndex(4, event);" key="5" modifiers="alt"/>
+   <key id="key_selectTab6" oncommand="gBrowser.selectTabAtIndex(5, event);" key="6" modifiers="alt"/>
+   <key id="key_selectTab7" oncommand="gBrowser.selectTabAtIndex(6, event);" key="7" modifiers="alt"/>
+   <key id="key_selectTab8" oncommand="gBrowser.selectTabAtIndex(7, event);" key="8" modifiers="alt"/>
+   <key id="key_selectLastTab" oncommand="gBrowser.selectTabAtIndex(-1, event);" key="9" modifiers="alt"/>
+
+    <key id="key_toggleAddonBar" command="Browser:ToggleAddonBar" key="&toggleAddonBarCmd.key;" modifiers="accel"/>
+
+  </keyset>
+
+  <keyset id="baseMenuKeyset" />
+
+  <popupset id="mainPopupSet">
+    <menupopup id="tabContextMenu"
+               onpopupshowing="if (event.target == this) TabContextMenu.updateContextMenu(this);"
+               onpopuphidden="if (event.target == this) TabContextMenu.contextTab = null;">
+      <menuitem id="context_reloadTab" label="&reloadTab.label;" accesskey="&reloadTab.accesskey;"
+                oncommand="gBrowser.reloadTab(TabContextMenu.contextTab);"/>
+      <menuseparator/>
+      <menuitem id="context_pinTab" label="&pinAppTab.label;"
+                accesskey="&pinAppTab.accesskey;"
+                oncommand="gBrowser.pinTab(TabContextMenu.contextTab);"/>
+      <menuitem id="context_unpinTab" label="&unpinAppTab.label;" hidden="true"
+                accesskey="&unpinAppTab.accesskey;"
+                oncommand="gBrowser.unpinTab(TabContextMenu.contextTab);"/>
+      <menu id="context_tabViewMenu" label="&moveToGroup.label;"
+            accesskey="&moveToGroup.accesskey;">
+        <menupopup id="context_tabViewMenuPopup"
+                   onpopupshowing="if (event.target == this) TabView.moveToGroupPopupShowing(event);">
+          <menuseparator id="context_tabViewNamedGroups" hidden="true"/>
+          <menuitem id="context_tabViewNewGroup" label="&moveToNewGroup.label;"
+                    oncommand="TabView.moveTabTo(TabContextMenu.contextTab, null);"/>
+        </menupopup>
+      </menu>
+      <menuitem id="context_openTabInWindow" label="&moveToNewWindow.label;"
+                accesskey="&moveToNewWindow.accesskey;"
+                tbattr="tabbrowser-multiple"
+                oncommand="gBrowser.replaceTabWithWindow(TabContextMenu.contextTab);"/>
+      <menuseparator/>
+      <menuitem id="context_reloadAllTabs" label="&reloadAllTabs.label;" accesskey="&reloadAllTabs.accesskey;"
+                tbattr="tabbrowser-multiple-visible"
+                oncommand="gBrowser.reloadAllTabs();"/>
+      <menuitem id="context_bookmarkAllTabs"
+                label="&bookmarkAllTabs.label;"
+                accesskey="&bookmarkAllTabs.accesskey;"
+                command="Browser:BookmarkAllTabs"/>
+      <menuitem id="context_closeOtherTabs" label="&closeOtherTabs.label;" accesskey="&closeOtherTabs.accesskey;"
+                oncommand="gBrowser.removeAllTabsBut(TabContextMenu.contextTab);"/>
+      <menuseparator/>
+      <menuitem id="context_undoCloseTab"
+                label="&undoCloseTab.label;"
+                accesskey="&undoCloseTab.accesskey;"
+                observes="History:UndoCloseTab"/>
+      <menuitem id="context_closeTab" label="&closeTab.label;" accesskey="&closeTab.accesskey;"
+                oncommand="gBrowser.removeTab(TabContextMenu.contextTab, { animate: true });"/>
+    </menupopup>
+
+    <!-- bug 415444/582485: event.stopPropagation is here for the cloned version
+         of this menupopup -->
+    <menupopup id="backForwardMenu"
+               onpopupshowing="return FillHistoryMenu(event.target);"
+               oncommand="gotoHistoryIndex(event); event.stopPropagation();"
+               onclick="checkForMiddleClick(this, event);"/>
+    <tooltip id="aHTMLTooltip" onpopupshowing="return FillInHTMLTooltip(document.tooltipNode);"/>
+
+    <!-- for search and content formfill/pw manager -->
+    <panel type="autocomplete" id="PopupAutoComplete" noautofocus="true" hidden="true"/>
+
+    <!-- for url bar autocomplete -->
+    <panel type="autocomplete-richlistbox" id="PopupAutoCompleteRichResult" noautofocus="true" hidden="true"/>
+
+    <!-- for invalid form error message -->
+    <panel id="invalid-form-popup" type="arrow" orient="vertical" noautofocus="true" hidden="true" level="parent">
+      <description/>
+    </panel>
+
+    <panel id="editBookmarkPanel"
+           type="arrow"
+           footertype="promobox"
+           orient="vertical"
+           ignorekeys="true"
+           hidden="true"
+           onpopupshown="StarUI.panelShown(event);"
+           aria-labelledby="editBookmarkPanelTitle">
+      <row id="editBookmarkPanelHeader" align="center" hidden="true">
+        <vbox align="center">
+          <image id="editBookmarkPanelStarIcon"/>
+        </vbox>
+        <vbox>
+          <label id="editBookmarkPanelTitle"/>
+          <description id="editBookmarkPanelDescription"/>
+          <hbox>
+            <button id="editBookmarkPanelRemoveButton"
+                    class="editBookmarkPanelHeaderButton"
+                    oncommand="StarUI.removeBookmarkButtonCommand();"
+                    accesskey="&editBookmark.removeBookmark.accessKey;"/>
+          </hbox>
+        </vbox>
+      </row>
+      <vbox id="editBookmarkPanelContent" flex="1" hidden="true"/>
+      <hbox id="editBookmarkPanelBottomButtons" pack="end">
+        <button id="editBookmarkPanelDeleteButton"
+                class="editBookmarkPanelBottomButton"
+                label="&editBookmark.cancel.label;"
+                oncommand="StarUI.cancelButtonOnCommand();"/>
+        <button id="editBookmarkPanelDoneButton"
+                class="editBookmarkPanelBottomButton"
+                label="&editBookmark.done.label;"
+                default="true"
+                oncommand="StarUI.panel.hidePopup();"/>
+      </hbox>
+    </panel>
+
+    <menupopup id="inspector-node-popup">
+      <menuitem id="inspectorHTMLCopyInner"
+                label="&inspectorHTMLCopyInner.label;"
+                accesskey="&inspectorHTMLCopyInner.accesskey;"
+                command="Inspector:CopyInner"/>
+      <menuitem id="inspectorHTMLCopyOuter"
+                label="&inspectorHTMLCopyOuter.label;"
+                accesskey="&inspectorHTMLCopyOuter.accesskey;"
+                command="Inspector:CopyOuter"/>
+      <menuseparator/>
+      <menuitem id="inspectorHTMLDelete"
+                label="&inspectorHTMLDelete.label;"
+                accesskey="&inspectorHTMLDelete.accesskey;"
+                command="Inspector:DeleteNode"/>
+    </menupopup>
+
+    <menupopup id="toolbar-context-menu"
+               onpopupshowing="onViewToolbarsPopupShowing(event);">
+      <menuseparator/>
+      <menuitem command="cmd_ToggleTabsOnTop"
+                type="checkbox"
+                label="&viewTabsOnTop.label;"
+                accesskey="&viewTabsOnTop.accesskey;"/>
+      <menuitem command="cmd_CustomizeToolbars"
+                label="&viewCustomizeToolbar.label;"
+                accesskey="&viewCustomizeToolbar.accesskey;"/>
+    </menupopup>
+
+    <menupopup id="blockedPopupOptions"
+               onpopupshowing="gPopupBlockerObserver.fillPopupList(event);"
+               onpopuphiding="gPopupBlockerObserver.onPopupHiding(event);">
+      <menuitem observes="blockedPopupAllowSite"/>
+      <menuitem observes="blockedPopupEditSettings"/>
+      <menuitem observes="blockedPopupDontShowMessage"/>
+      <menuseparator observes="blockedPopupsSeparator"/>
+    </menupopup>
+
+    <menupopup id="autohide-context"
+           onpopupshowing="FullScreen.getAutohide(this.firstChild);">
+      <menuitem type="checkbox" label="&fullScreenAutohide.label;"
+                accesskey="&fullScreenAutohide.accesskey;"
+                oncommand="FullScreen.setAutohide();"/>
+      <menuseparator/>
+      <menuitem label="&fullScreenExit.label;"
+                accesskey="&fullScreenExit.accesskey;"
+                oncommand="BrowserFullScreen();"/>
+    </menupopup>
+
+    <menupopup id="contentAreaContextMenu" pagemenu="start"
+               onpopupshowing="if (event.target != this)
+                                 return true;
+                               gContextMenu = new nsContextMenu(this, gBrowser, event.shiftKey);
+                               if (gContextMenu.shouldDisplay)
+                                 updateEditUIVisibility();
+                               return gContextMenu.shouldDisplay;"
+               onpopuphiding="if (event.target != this)
+                                return;
+                              gContextMenu.hiding();
+                              gContextMenu = null;
+                              updateEditUIVisibility();">
+
+      <menuseparator id="page-menu-separator"/>
+      <menuitem id="spell-no-suggestions"
+                disabled="true"
+                label="&spellNoSuggestions.label;"/>
+      <menuitem id="spell-add-to-dictionary"
+                label="&spellAddToDictionary.label;"
+                accesskey="&spellAddToDictionary.accesskey;"
+                oncommand="InlineSpellCheckerUI.addToDictionary();"/>
+      <menuitem id="spell-undo-add-to-dictionary"
+                label="&spellUndoAddToDictionary.label;"
+                accesskey="&spellUndoAddToDictionary.accesskey;"
+                oncommand="InlineSpellCheckerUI.undoAddToDictionary();" />
+      <menuseparator id="spell-suggestions-separator"/>
+      <menuitem id="context-openlinkincurrent"
+                label="&openLinkCmdInCurrent.label;"
+                accesskey="&openLinkCmdInCurrent.accesskey;"
+                oncommand="gContextMenu.openLinkInCurrent();"/>
+      <menuitem id="context-openlinkintab"
+                label="&openLinkCmdInTab.label;"
+                accesskey="&openLinkCmdInTab.accesskey;"
+                oncommand="gContextMenu.openLinkInTab();"/>
+      <menuitem id="context-openlink"
+                label="&openLinkCmd.label;"
+                accesskey="&openLinkCmd.accesskey;"
+                oncommand="gContextMenu.openLink();"/>
+      <menuseparator id="context-sep-open"/>
+      <menuitem id="context-bookmarklink"
+                label="&bookmarkThisLinkCmd.label;"
+                accesskey="&bookmarkThisLinkCmd.accesskey;"
+                oncommand="gContextMenu.bookmarkLink();"/>
+      <menuitem id="context-savelink"
+                label="&saveLinkCmd.label;"
+                accesskey="&saveLinkCmd.accesskey;"
+                oncommand="gContextMenu.saveLink();"/>
+      <menuitem id="context-sendlink"
+                label="&sendLinkCmd.label;"
+                accesskey="&sendLinkCmd.accesskey;"
+                oncommand="gContextMenu.sendLink();"/>
+      <menuitem id="context-copyemail"
+                label="&copyEmailCmd.label;"
+                accesskey="&copyEmailCmd.accesskey;"
+                oncommand="gContextMenu.copyEmail();"/>
+      <menuitem id="context-copylink"
+                label="&copyLinkCmd.label;"
+                accesskey="&copyLinkCmd.accesskey;"
+                oncommand="goDoCommand('cmd_copyLink');"/>
+      <menuseparator id="context-sep-copylink"/>
+      <menuitem id="context-media-play"
+                label="&mediaPlay.label;"
+                accesskey="&mediaPlay.accesskey;"
+                oncommand="gContextMenu.mediaCommand('play');"/>
+      <menuitem id="context-media-pause"
+                label="&mediaPause.label;"
+                accesskey="&mediaPause.accesskey;"
+                oncommand="gContextMenu.mediaCommand('pause');"/>
+      <menuitem id="context-media-mute"
+                label="&mediaMute.label;"
+                accesskey="&mediaMute.accesskey;"
+                oncommand="gContextMenu.mediaCommand('mute');"/>
+      <menuitem id="context-media-unmute"
+                label="&mediaUnmute.label;"
+                accesskey="&mediaUnmute.accesskey;"
+                oncommand="gContextMenu.mediaCommand('unmute');"/>
+      <menuitem id="context-media-showcontrols"
+                label="&mediaShowControls.label;"
+                accesskey="&mediaShowControls.accesskey;"
+                oncommand="gContextMenu.mediaCommand('showcontrols');"/>
+      <menuitem id="context-media-hidecontrols"
+                label="&mediaHideControls.label;"
+                accesskey="&mediaHideControls.accesskey;"
+                oncommand="gContextMenu.mediaCommand('hidecontrols');"/>
+      <menuitem id="context-video-showstats"
+                accesskey="&videoShowStats.accesskey;"
+                label="&videoShowStats.label;"
+                oncommand="gContextMenu.mediaCommand('showstats');"/>
+      <menuitem id="context-video-hidestats"
+                accesskey="&videoHideStats.accesskey;"
+                label="&videoHideStats.label;"
+                oncommand="gContextMenu.mediaCommand('hidestats');"/>
+      <menuitem id="context-video-fullscreen"
+                accesskey="&videoFullScreen.accesskey;"
+                label="&videoFullScreen.label;"
+                oncommand="gContextMenu.fullScreenVideo();"/>
+      <menuitem id="context-leave-dom-fullscreen"
+                accesskey="&leaveDOMFullScreen.accesskey;"
+                label="&leaveDOMFullScreen.label;"
+                oncommand="gContextMenu.leaveDOMFullScreen();"/>
+      <menuseparator id="context-media-sep-commands"/>
+      <menuitem id="context-reloadimage"
+                label="&reloadImageCmd.label;"
+                accesskey="&reloadImageCmd.accesskey;"
+                oncommand="gContextMenu.reloadImage();"/>
+      <menuitem id="context-viewimage"
+                label="&viewImageCmd.label;"
+                accesskey="&viewImageCmd.accesskey;"
+                oncommand="gContextMenu.viewMedia(event);"
+                onclick="checkForMiddleClick(this, event);"/>
+      <menuitem id="context-viewvideo"
+                label="&viewVideoCmd.label;"
+                accesskey="&viewVideoCmd.accesskey;"
+                oncommand="gContextMenu.viewMedia(event);"
+                onclick="checkForMiddleClick(this, event);"/>
+      <menuitem id="context-copyimage-contents"
+                label="&copyImageContentsCmd.label;"
+                accesskey="&copyImageContentsCmd.accesskey;"
+                oncommand="goDoCommand('cmd_copyImage');"/>
+      <menuitem id="context-copyimage"
+                label="&copyImageCmd.label;"
+                accesskey="&copyImageCmd.accesskey;"
+                oncommand="gContextMenu.copyMediaLocation();"/>
+      <menuitem id="context-copyvideourl"
+                label="&copyVideoURLCmd.label;"
+                accesskey="&copyVideoURLCmd.accesskey;"
+                oncommand="gContextMenu.copyMediaLocation();"/>
+      <menuitem id="context-copyaudiourl"
+                label="&copyAudioURLCmd.label;"
+                accesskey="&copyAudioURLCmd.accesskey;"
+                oncommand="gContextMenu.copyMediaLocation();"/>
+      <menuseparator id="context-sep-copyimage"/>
+      <menuitem id="context-saveimage"
+                label="&saveImageCmd.label;"
+                accesskey="&saveImageCmd.accesskey;"
+                oncommand="gContextMenu.saveMedia();"/>
+      <menuitem id="context-sendimage"
+                label="&sendImageCmd.label;"
+                accesskey="&sendImageCmd.accesskey;"
+                oncommand="gContextMenu.sendMedia();"/>
+      <menuitem id="context-setDesktopBackground"
+                label="&setDesktopBackgroundCmd.label;"
+                accesskey="&setDesktopBackgroundCmd.accesskey;"
+                oncommand="gContextMenu.setDesktopBackground();"/>
+      <menuitem id="context-viewimageinfo"
+                label="&viewImageInfoCmd.label;"
+                accesskey="&viewImageInfoCmd.accesskey;"
+                oncommand="gContextMenu.viewImageInfo();"/>
+      <menuitem id="context-savevideo"
+                label="&saveVideoCmd.label;"
+                accesskey="&saveVideoCmd.accesskey;"
+                oncommand="gContextMenu.saveMedia();"/>
+      <menuitem id="context-saveaudio"
+                label="&saveAudioCmd.label;"
+                accesskey="&saveAudioCmd.accesskey;"
+                oncommand="gContextMenu.saveMedia();"/>
+      <menuitem id="context-video-saveimage"
+                accesskey="&videoSaveImage.accesskey;"
+                label="&videoSaveImage.label;"
+                oncommand="gContextMenu.saveVideoFrameAsImage();"/>
+      <menuitem id="context-sendvideo"
+                label="&sendVideoCmd.label;"
+                accesskey="&sendVideoCmd.accesskey;"
+                oncommand="gContextMenu.sendMedia();"/>
+      <menuitem id="context-sendaudio"
+                label="&sendAudioCmd.label;"
+                accesskey="&sendAudioCmd.accesskey;"
+                oncommand="gContextMenu.sendMedia();"/>
+      <menuitem id="context-back"
+                label="&backCmd.label;"
+                accesskey="&backCmd.accesskey;"
+                command="Browser:BackOrBackDuplicate"
+                onclick="checkForMiddleClick(this, event);"/>
+      <menuitem id="context-forward"
+                label="&forwardCmd.label;"
+                accesskey="&forwardCmd.accesskey;"
+                command="Browser:ForwardOrForwardDuplicate"
+                onclick="checkForMiddleClick(this, event);"/>
+      <menuitem id="context-reload"
+                label="&reloadCmd.label;"
+                accesskey="&reloadCmd.accesskey;"
+                command="Browser:ReloadOrDuplicate"
+                onclick="checkForMiddleClick(this, event);"/>
+      <menuitem id="context-stop"
+                label="&stopCmd.label;"
+                accesskey="&stopCmd.accesskey;"
+                command="Browser:Stop"/>
+      <menuseparator id="context-sep-stop"/>
+      <menuitem id="context-bookmarkpage"
+                label="&bookmarkPageCmd2.label;"
+                accesskey="&bookmarkPageCmd2.accesskey;"
+                oncommand="gContextMenu.bookmarkThisPage();"/>
+      <menuitem id="context-savepage"
+                label="&savePageCmd.label;"
+                accesskey="&savePageCmd.accesskey2;"
+                oncommand="gContextMenu.savePageAs();"/>
+      <menuitem id="context-sendpage"
+                label="&sendPageCmd.label;"
+                accesskey="&sendPageCmd.accesskey;"
+                oncommand="gContextMenu.sendPage();"/>
+      <menuseparator id="context-sep-viewbgimage"/>
+      <menuitem id="context-viewbgimage"
+                label="&viewBGImageCmd.label;"
+                accesskey="&viewBGImageCmd.accesskey;"
+                oncommand="gContextMenu.viewBGImage(event);"
+                onclick="checkForMiddleClick(this, event);"/>
+      <menuitem id="context-undo"
+                label="&undoCmd.label;"
+                accesskey="&undoCmd.accesskey;"
+                command="cmd_undo"/>
+      <menuseparator id="context-sep-undo"/>
+      <menuitem id="context-cut"
+                label="&cutCmd.label;"
+                accesskey="&cutCmd.accesskey;"
+                command="cmd_cut"/>
+      <menuitem id="context-copy"
+                label="&copyCmd.label;"
+                accesskey="&copyCmd.accesskey;"
+                command="cmd_copy"/>
+      <menuitem id="context-paste"
+                label="&pasteCmd.label;"
+                accesskey="&pasteCmd.accesskey;"
+                command="cmd_paste"/>
+      <menuitem id="context-delete"
+                label="&deleteCmd.label;"
+                accesskey="&deleteCmd.accesskey;"
+                command="cmd_delete"/>
+      <menuseparator id="context-sep-paste"/>
+      <menuitem id="context-selectall"
+                label="&selectAllCmd.label;"
+                accesskey="&selectAllCmd.accesskey;"
+                command="cmd_selectAll"/>
+      <menuseparator id="context-sep-selectall"/>
+      <menuitem id="context-keywordfield"
+                label="&keywordfield.label;"
+                accesskey="&keywordfield.accesskey;"
+                oncommand="AddKeywordForSearchField();"/>
+      <menuitem id="context-searchselect"
+                oncommand="BrowserSearch.loadSearch(getBrowserSelection(), true,
+                                                    'application/x-moz-contextsearch');"/>
+      <menuseparator id="frame-sep"/>
+      <menu id="frame" label="&thisFrameMenu.label;" accesskey="&thisFrameMenu.accesskey;">
+        <menupopup>
+          <menuitem id="context-showonlythisframe"
+                    label="&showOnlyThisFrameCmd.label;"
+                    accesskey="&showOnlyThisFrameCmd.accesskey;"
+                    oncommand="gContextMenu.showOnlyThisFrame();"/>
+          <menuitem id="context-openframeintab"
+                    label="&openFrameCmdInTab.label;"
+                    accesskey="&openFrameCmdInTab.accesskey;"
+                    oncommand="gContextMenu.openFrameInTab();"/>
+          <menuitem id="context-openframe"
+                    label="&openFrameCmd.label;"
+                    accesskey="&openFrameCmd.accesskey;"
+                    oncommand="gContextMenu.openFrame();"/>
+          <menuseparator/>
+          <menuitem id="context-reloadframe"
+                    label="&reloadFrameCmd.label;"
+                    accesskey="&reloadFrameCmd.accesskey;"
+                    oncommand="gContextMenu.reloadFrame();"/>
+          <menuseparator/>
+          <menuitem id="context-bookmarkframe"
+                    label="&bookmarkThisFrameCmd.label;"
+                    accesskey="&bookmarkThisFrameCmd.accesskey;"
+                    oncommand="gContextMenu.addBookmarkForFrame();"/>
+          <menuitem id="context-saveframe"
+                    label="&saveFrameCmd.label;"
+                    accesskey="&saveFrameCmd.accesskey;"
+                    oncommand="gContextMenu.saveFrame();"/>
+          <menuseparator/>
+          <menuitem id="context-printframe"
+                    label="&printFrameCmd.label;"
+                    accesskey="&printFrameCmd.accesskey;"
+                    oncommand="gContextMenu.printFrame();"/>
+          <menuseparator/>
+          <menuitem id="context-viewframesource"
+                    label="&viewFrameSourceCmd.label;"
+                    accesskey="&viewFrameSourceCmd.accesskey;"
+                    oncommand="gContextMenu.viewFrameSource();"
+                    observes="isFrameImage"/>
+          <menuitem id="context-viewframeinfo"
+                    label="&viewFrameInfoCmd.label;"
+                    accesskey="&viewFrameInfoCmd.accesskey;"
+                    oncommand="gContextMenu.viewFrameInfo();"/>
+        </menupopup>
+      </menu>
+      <menuitem id="context-viewpartialsource-selection"
+                label="&viewPartialSourceForSelectionCmd.label;"
+                accesskey="&viewPartialSourceCmd.accesskey;"
+                oncommand="gContextMenu.viewPartialSource('selection');"
+                observes="isImage"/>
+      <menuitem id="context-viewpartialsource-mathml"
+                label="&viewPartialSourceForMathMLCmd.label;"
+                accesskey="&viewPartialSourceCmd.accesskey;"
+                oncommand="gContextMenu.viewPartialSource('mathml');"
+                observes="isImage"/>
+      <menuseparator id="context-sep-viewsource"/>
+      <menuitem id="context-viewsource"
+                label="&viewPageSourceCmd.label;"
+                accesskey="&viewPageSourceCmd.accesskey;"
+                oncommand="BrowserViewSourceOfDocument(gContextMenu.browser.contentDocument);"
+                observes="isImage"/>
+      <menuitem id="context-viewinfo"
+                label="&viewPageInfoCmd.label;"
+                accesskey="&viewPageInfoCmd.accesskey;"
+                oncommand="gContextMenu.viewInfo();"/>
+      <menuseparator id="spell-separator"/>
+      <menuitem id="spell-check-enabled"
+                label="&spellCheckEnable.label;"
+                type="checkbox"
+                accesskey="&spellCheckEnable.accesskey;"
+                oncommand="InlineSpellCheckerUI.toggleEnabled();"/>
+      <menuitem id="spell-add-dictionaries-main"
+                label="&spellAddDictionaries.label;"
+                accesskey="&spellAddDictionaries.accesskey;"
+                oncommand="gContextMenu.addDictionaries();"/>
+      <menu id="spell-dictionaries"
+            label="&spellDictionaries.label;"
+            accesskey="&spellDictionaries.accesskey;">
+          <menupopup id="spell-dictionaries-menu">
+              <menuseparator id="spell-language-separator"/>
+              <menuitem id="spell-add-dictionaries"
+                        label="&spellAddDictionaries.label;"
+                        accesskey="&spellAddDictionaries.accesskey;"
+                        oncommand="gContextMenu.addDictionaries();"/>
+          </menupopup>
+      </menu>
+      <menuseparator hidden="true" id="context-sep-bidi"/>
+      <menuitem hidden="true" id="context-bidi-text-direction-toggle"
+                label="&bidiSwitchTextDirectionItem.label;"
+                accesskey="&bidiSwitchTextDirectionItem.accesskey;"
+                command="cmd_switchTextDirection"/>
+      <menuitem hidden="true" id="context-bidi-page-direction-toggle"
+                label="&bidiSwitchPageDirectionItem.label;"
+                accesskey="&bidiSwitchPageDirectionItem.accesskey;"
+                oncommand="gContextMenu.switchPageDirection();"/>
+      <menuseparator id="inspect-separator" hidden="true"/>
+      <menuitem id="context-inspect"
+                hidden="true"
+                label="&inspectContextMenu.label;"
+                accesskey="&inspectContextMenu.accesskey;"
+                oncommand="gContextMenu.inspectNode();"/>
+    </menupopup>
+
+    <menupopup id="placesContext"/>
+
+    <panel id="notification-popup"
+           type="arrow"
+           footertype="promobox"
+           position="after_start"
+           hidden="true"
+           orient="vertical"
+           role="alert"/>
+
+    <!-- Popup for site identity information -->
+    <panel id="identity-popup"
+           type="arrow"
+           hidden="true"
+           noautofocus="true"
+           onpopupshown="gIdentityHandler.onPopupShown(event);"
+           level="top">
+      <hbox id="identity-popup-container" align="top">
+        <image id="identity-popup-icon"/>
+        <vbox id="identity-popup-content-box">
+          <label id="identity-popup-connectedToLabel"
+                 class="identity-popup-label"
+                 value="&identity.connectedTo;"/>
+          <label id="identity-popup-connectedToLabel2"
+                 class="identity-popup-label"
+                 value="&identity.unverifiedsite2;"/>
+          <description id="identity-popup-content-host"
+                       class="identity-popup-description"/>
+          <label id="identity-popup-runByLabel"
+                 class="identity-popup-label"
+                 value="&identity.runBy;"/>
+          <description id="identity-popup-content-owner"
+                       class="identity-popup-description"/>
+          <description id="identity-popup-content-supplemental"
+                       class="identity-popup-description"/>
+          <description id="identity-popup-content-verifier"
+                       class="identity-popup-description"/>
+          <hbox id="identity-popup-encryption" flex="1">
+            <vbox>
+              <image id="identity-popup-encryption-icon"/>
+            </vbox>
+            <description id="identity-popup-encryption-label" flex="1"
+                         class="identity-popup-description"/>
+          </hbox>
+          <!-- Footer button to open security page info -->
+          <hbox id="identity-popup-button-container" pack="end">
+            <button id="identity-popup-more-info-button"
+                    label="&identity.moreInfoLinkText;"
+                    onblur="gIdentityHandler.hideIdentityPopup();"
+                    oncommand="gIdentityHandler.handleMoreInfoClick(event);"/>
+          </hbox>
+        </vbox>
+      </hbox>
+    </panel>
+
+    <tooltip id="urlTooltip">
+      <label crop="center" flex="1" class="tooltip-label"/>
+    </tooltip>
+
+    <panel id="ctrlTab-panel" class="KUI-panel" hidden="true" norestorefocus="true" level="top">
+      <hbox>
+        <button class="ctrlTab-preview" flex="1"/>
+        <button class="ctrlTab-preview" flex="1"/>
+        <button class="ctrlTab-preview" flex="1"/>
+        <button class="ctrlTab-preview" flex="1"/>
+        <button class="ctrlTab-preview" flex="1"/>
+        <button class="ctrlTab-preview" flex="1"/>
+      </hbox>
+      <hbox pack="center">
+        <button id="ctrlTab-showAll" class="ctrlTab-preview" noicon="true"/>
+      </hbox>
+    </panel>
+
+    <panel id="allTabs-panel" hidden="true" norestorefocus="true" ignorekeys="true"
+           onmouseover="allTabs._updateTabCloseButton(event);">
+      <hbox id="allTabs-meta" align="center">
+        <spacer flex="1"/>
+        <textbox id="allTabs-filter"
+                 tooltiptext="&allTabs.filter.emptyText;"
+                 type="search"
+                 oncommand="allTabs.filter();"/>
+        <spacer flex="1"/>
+        <toolbarbutton class="KUI-panel-closebutton"
+                       oncommand="allTabs.close()"
+                       tooltiptext="&closeCmd.label;"/>
+      </hbox>
+      <stack id="allTabs-stack">
+        <vbox id="allTabs-container"><hbox/></vbox>
+        <toolbarbutton id="allTabs-tab-close-button"
+                       class="tabs-closebutton"
+                       oncommand="allTabs.closeTab(event);"
+                       tooltiptext="&closeCmd.label;"
+                       style="visibility:hidden"/>
+      </stack>
+    </panel>
+
+    <!-- Bookmarks and history tooltip -->
+    <tooltip id="bhTooltip"/>
+
+    <panel id="customizeToolbarSheetPopup"
+           noautohide="true">
+      <iframe id="customizeToolbarSheetIFrame"
+              style="&dialog.dimensions;"
+              hidden="true"/>
+    </panel>
+
+    <tooltip id="tabbrowser-tab-tooltip" onpopupshowing="gBrowser.createTooltip(event);"/>
+
+    <tooltip id="back-button-tooltip">
+      <label class="tooltip-label" value="&backButton.tooltip;"/>
+      <label class="tooltip-label" value="&backForwardButtonMenu.tooltip;"/>
+    </tooltip>
+
+    <tooltip id="forward-button-tooltip">
+      <label class="tooltip-label" value="&forwardButton.tooltip;"/>
+      <label class="tooltip-label" value="&backForwardButtonMenu.tooltip;"/>
+    </tooltip>
+  </popupset>
+
+
+<deck flex="1" id="tab-view-deck">
+<vbox flex="1" id="browser-panel">
+
+  <toolbox id="navigator-toolbox"
+           defaultmode="icons" mode="icons"
+           iconsize="large">
+    <!-- Menu -->
+    <toolbar type="menubar" id="toolbar-menubar" class="chromeclass-menubar" customizable="true"
+             defaultset="menubar-items"
+             mode="icons" iconsize="small" defaulticonsize="small"
+             lockiconsize="true"
+             toolbarname="&menubarCmd.label;"
+             accesskey="&menubarCmd.accesskey;"
+             context="toolbar-context-menu">
+      <toolbaritem id="menubar-items" align="center">
+
+       <menubar id="main-menubar"
+                onpopupshowing="if (event.target.parentNode.parentNode == this &amp;&amp;
+                                    !('@mozilla.org/widget/nativemenuservice;1' in Cc))
+                                  this.setAttribute('openedwithkey',
+                                                    event.target.parentNode.openedWithKey);"
+                style="border:0px;padding:0px;margin:0px;-moz-appearance:none">
+            <menu id="file-menu" label="&fileMenu.label;"
+                  accesskey="&fileMenu.accesskey;">
+              <menupopup id="menu_FilePopup">
+                <menuitem id="menu_newNavigatorTab"
+                          label="&tabCmd.label;"
+                          command="cmd_newNavigatorTab"
+                          key="key_newNavigatorTab"
+                          accesskey="&tabCmd.accesskey;"/>
+                <menuitem id="menu_newNavigator"
+                          label="&newNavigatorCmd.label;"
+                          accesskey="&newNavigatorCmd.accesskey;"
+                          key="key_newNavigator"
+                          command="cmd_newNavigator"/>
+                <menuitem id="menu_openLocation"
+                          class="show-only-for-keyboard"
+                          label="&openLocationCmd.label;"
+                          command="Browser:OpenLocation"
+                          key="focusURLBar"
+                          accesskey="&openLocationCmd.accesskey;"/>
+                <menuitem id="menu_openFile"
+                          label="&openFileCmd.label;"
+                          command="Browser:OpenFile"
+                          key="openFileKb"
+                          accesskey="&openFileCmd.accesskey;"/>
+                <menuitem id="menu_close"
+                          class="show-only-for-keyboard"
+                          label="&closeCmd.label;"
+                          key="key_close"
+                          accesskey="&closeCmd.accesskey;"
+                          command="cmd_close"/>
+                <menuitem id="menu_closeWindow"
+                          class="show-only-for-keyboard"
+                          hidden="true"
+                          command="cmd_closeWindow"
+                          key="key_closeWindow"
+                          label="&closeWindow.label;"
+                          accesskey="&closeWindow.accesskey;"/>
+                <menuseparator/>
+                <menuitem id="menu_savePage"
+                          label="&savePageCmd.label;"
+                          accesskey="&savePageCmd.accesskey;"
+                          key="key_savePage"
+                          command="Browser:SavePage"/>
+                <menuitem id="menu_sendLink"
+                          label="&sendPageCmd.label;"
+                          accesskey="&sendPageCmd.accesskey;"
+                          command="Browser:SendLink"/>
+                <menuseparator/>
+                <menuitem id="menu_printSetup"
+                          label="&printSetupCmd.label;"
+                          accesskey="&printSetupCmd.accesskey;"
+                          command="cmd_pageSetup"/>
+                <menuitem id="menu_printPreview"
+                          label="&printPreviewCmd.label;"
+                          accesskey="&printPreviewCmd.accesskey;"
+                          command="cmd_printPreview"/>
+                <menuitem id="menu_print"
+                          label="&printCmd.label;"
+                          accesskey="&printCmd.accesskey;"
+                          key="printKb"
+                          command="cmd_print"/>
+                <menuseparator/>
+                <menuitem id="goOfflineMenuitem"
+                          label="&goOfflineCmd.label;"
+                          accesskey="&goOfflineCmd.accesskey;"
+                          type="checkbox"
+                          observes="workOfflineMenuitemState"
+                          oncommand="BrowserOffline.toggleOfflineStatus();"/>
+                <menuitem id="menu_FileQuitItem"
+                          label="&quitApplicationCmd.label;"
+                          accesskey="&quitApplicationCmd.accesskey;"
+                          key="key_quitApplication"
+                          command="cmd_quitApplication"/>
+              </menupopup>
+            </menu>
+
+            <menu id="edit-menu" label="&editMenu.label;"
+                  accesskey="&editMenu.accesskey;">
+              <menupopup id="menu_EditPopup"
+                         onpopupshowing="updateEditUIVisibility()"
+                         onpopuphidden="updateEditUIVisibility()">
+                <menuitem id="menu_undo"
+                          label="&undoCmd.label;"
+                          key="key_undo"
+                          accesskey="&undoCmd.accesskey;"
+                          command="cmd_undo"/>
+                <menuitem id="menu_redo"
+                          label="&redoCmd.label;"
+                          key="key_redo"
+                          accesskey="&redoCmd.accesskey;"
+                          command="cmd_redo"/>
+                <menuseparator/>
+                <menuitem id="menu_cut"
+                          label="&cutCmd.label;"
+                          key="key_cut"
+                          accesskey="&cutCmd.accesskey;"
+                          command="cmd_cut"/>
+                <menuitem id="menu_copy"
+                          label="&copyCmd.label;"
+                          key="key_copy"
+                          accesskey="&copyCmd.accesskey;"
+                          command="cmd_copy"/>
+                <menuitem id="menu_paste"
+                          label="&pasteCmd.label;"
+                          key="key_paste"
+                          accesskey="&pasteCmd.accesskey;"
+                          command="cmd_paste"/>
+                <menuitem id="menu_delete"
+                          label="&deleteCmd.label;"
+                          key="key_delete"
+                          accesskey="&deleteCmd.accesskey;"
+                          command="cmd_delete"/>
+                <menuseparator/>
+                <menuitem id="menu_selectAll"
+                          label="&selectAllCmd.label;"
+                          key="key_selectAll"
+                          accesskey="&selectAllCmd.accesskey;"
+                          command="cmd_selectAll"/>
+                <menuseparator/>
+                <menuitem id="menu_find"
+                          label="&findOnCmd.label;"
+                          accesskey="&findOnCmd.accesskey;"
+                          key="key_find"
+                          command="cmd_find"/>
+                <menuitem id="menu_findAgain"
+                          class="show-only-for-keyboard"
+                          label="&findAgainCmd.label;"
+                          accesskey="&findAgainCmd.accesskey;"
+                          key="key_findAgain"
+                          command="cmd_findAgain"/>
+                <menuseparator hidden="true" id="textfieldDirection-separator"/>
+                <menuitem id="textfieldDirection-swap"
+                          command="cmd_switchTextDirection"
+                          key="key_switchTextDirection"
+                          label="&bidiSwitchTextDirectionItem.label;"
+                          accesskey="&bidiSwitchTextDirectionItem.accesskey;"
+                          hidden="true"/>
+                <menuseparator/>
+                <menuitem id="menu_preferences"
+                          label="&preferencesCmdUnix.label;"
+                          accesskey="&preferencesCmdUnix.accesskey;"
+                          oncommand="openPreferences();"/>
+              </menupopup>
+            </menu>
+
+            <menu id="view-menu" label="&viewMenu.label;"
+                  accesskey="&viewMenu.accesskey;">
+              <menupopup id="menu_viewPopup">
+                <menu id="viewToolbarsMenu"
+                      label="&viewToolbarsMenu.label;"
+                      accesskey="&viewToolbarsMenu.accesskey;">
+                  <menupopup onpopupshowing="onViewToolbarsPopupShowing(event);">
+                    <menuseparator/>
+                    <menuitem id="menu_tabsOnTop"
+                              command="cmd_ToggleTabsOnTop"
+                              type="checkbox"
+                              label="&viewTabsOnTop.label;"
+                              accesskey="&viewTabsOnTop.accesskey;"/>
+                    <menuitem id="menu_customizeToolbars"
+                              label="&viewCustomizeToolbar.label;"
+                              accesskey="&viewCustomizeToolbar.accesskey;"
+                              command="cmd_CustomizeToolbars"/>
+                  </menupopup>
+                </menu>
+                <menu id="viewSidebarMenuMenu"
+                      label="&viewSidebarMenu.label;"
+                      accesskey="&viewSidebarMenu.accesskey;">
+                  <menupopup id="viewSidebarMenu">
+                    <menuitem id="menu_bookmarksSidebar"
+                              key="viewBookmarksSidebarKb"
+                              observes="viewBookmarksSidebar"
+                              accesskey="&bookmarksButton.accesskey;"/>
+                    <menuitem id="menu_historySidebar"
+                              key="key_gotoHistory"
+                              observes="viewHistorySidebar"
+                              label="&historyButton.label;"
+                              accesskey="&historySidebarCmd.accesskey;"/>
+                  </menupopup>
+                </menu>
+                <menuseparator/>
+                <menuitem id="menu_stop"
+                          class="show-only-for-keyboard"
+                          label="&stopCmd.label;"
+                          accesskey="&stopCmd.accesskey;"
+                          command="Browser:Stop"
+                          key="key_stop"/>
+                <menuitem id="menu_reload"
+                          class="show-only-for-keyboard"
+                          label="&reloadCmd.label;"
+                          accesskey="&reloadCmd.accesskey;"
+                          key="key_reload"
+                          command="Browser:ReloadOrDuplicate"
+                          onclick="checkForMiddleClick(this, event);"/>
+                <menuseparator class="show-only-for-keyboard"/>
+                <menu id="viewFullZoomMenu" label="&fullZoom.label;"
+                      accesskey="&fullZoom.accesskey;"
+                      onpopupshowing="FullZoom.updateMenu();">
+                  <menupopup>
+                    <menuitem id="menu_zoomEnlarge"
+                              key="key_fullZoomEnlarge"
+                              label="&fullZoomEnlargeCmd.label;"
+                              accesskey="&fullZoomEnlargeCmd.accesskey;"
+                              command="cmd_fullZoomEnlarge"/>
+                    <menuitem id="menu_zoomReduce"
+                              key="key_fullZoomReduce"
+                              label="&fullZoomReduceCmd.label;"
+                              accesskey="&fullZoomReduceCmd.accesskey;"
+                              command="cmd_fullZoomReduce"/>
+                    <menuseparator/>
+                    <menuitem id="menu_zoomReset"
+                              key="key_fullZoomReset"
+                              label="&fullZoomResetCmd.label;"
+                              accesskey="&fullZoomResetCmd.accesskey;"
+                              command="cmd_fullZoomReset"/>
+                    <menuseparator/>
+                    <menuitem id="toggle_zoom"
+                              label="&fullZoomToggleCmd.label;"
+                              accesskey="&fullZoomToggleCmd.accesskey;"
+                              type="checkbox"
+                              command="cmd_fullZoomToggle"
+                              checked="false"/>
+                  </menupopup>
+                </menu>
+                <menu id="pageStyleMenu" label="&pageStyleMenu.label;"
+                      accesskey="&pageStyleMenu.accesskey;" observes="isImage">
+                  <menupopup onpopupshowing="gPageStyleMenu.fillPopup(this);">
+                    <menuitem id="menu_pageStyleNoStyle"
+                              label="&pageStyleNoStyle.label;"
+                              accesskey="&pageStyleNoStyle.accesskey;"
+                              oncommand="gPageStyleMenu.disableStyle();"
+                              type="radio"/>
+                    <menuitem id="menu_pageStylePersistentOnly"
+                              label="&pageStylePersistentOnly.label;"
+                              accesskey="&pageStylePersistentOnly.accesskey;"
+                              oncommand="gPageStyleMenu.switchStyleSheet('');"
+                              type="radio"
+                              checked="true"/>
+                    <menuseparator/>
+                  </menupopup>
+                </menu>
+
+
+<menu id="charsetMenu"
+    label="&charsetMenu.label;"
+    accesskey="&charsetMenu.accesskey;"
+    datasources="rdf:charset-menu"
+    ref="NC:BrowserCharsetMenuRoot"
+    oncommand="MultiplexHandler(event)"
+    onpopupshowing="CreateMenu('browser'); CreateMenu('more-menu');"
+    onpopupshown="UpdateMenus(event);"
+    observes="isImage">
+  <template>
+    <rule rdf:type="http://home.netscape.com/NC-rdf#BookmarkSeparator">
+      <menupopup>
+      <menuseparator uri="..." />
+      </menupopup>
+    </rule>
+    <rule>
+      <menupopup>
+      <menuitem type="radio" name="charsetGroup" checked="rdf:http://home.netscape.com/NC-rdf#Checked" uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+      </menupopup>
+    </rule>
+  </template>
+
+  <menupopup>
+  <menu label="&charsetMenuAutodet.label;"
+        accesskey="&charsetMenuAutodet.accesskey;"
+        datasources="rdf:charset-menu" ref="NC:BrowserAutodetMenuRoot">
+    <template>
+      <rule rdf:type="http://home.netscape.com/NC-rdf#CharsetDetector">
+        <menupopup>
+        <menuitem type="radio" name="detectorGroup" checked="rdf:http://home.netscape.com/NC-rdf#Checked" uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+        </menupopup>
+      </rule>
+    </template>
+    <menupopup>
+    </menupopup>
+  </menu>
+  <menu label="&charsetMenuMore.label;"
+        accesskey="&charsetMenuMore.accesskey;"
+        datasources="rdf:charset-menu" ref="NC:BrowserMoreCharsetMenuRoot">
+    <template>
+      <rule>
+        <menupopup>
+        <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+        </menupopup>
+      </rule>
+    </template>
+    <menupopup>
+      <menu label="&charsetMenuMore1.label;"
+            accesskey="&charsetMenuMore1.accesskey;"
+            datasources="rdf:charset-menu" ref="NC:BrowserMore1CharsetMenuRoot">
+        <template>
+          <rule>
+            <menupopup>
+            <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+            </menupopup>
+          </rule>
+        </template>
+        <menupopup>
+        </menupopup>
+      </menu>
+      <menu label="&charsetMenuMore2.label;"
+            accesskey="&charsetMenuMore2.accesskey;"
+            datasources="rdf:charset-menu" ref="NC:BrowserMore2CharsetMenuRoot">
+        <template>
+          <rule>
+            <menupopup>
+            <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+            </menupopup>
+          </rule>
+        </template>
+        <menupopup>
+        </menupopup>
+      </menu>
+      <menu label="&charsetMenuMore3.label;"
+            accesskey="&charsetMenuMore3.accesskey;"
+            datasources="rdf:charset-menu" ref="NC:BrowserMore3CharsetMenuRoot">
+        <template>
+          <rule>
+            <menupopup>
+            <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+            </menupopup>
+          </rule>
+        </template>
+        <menupopup>
+        </menupopup>
+      </menu>
+      <menu label="&charsetMenuMore4.label;"
+            accesskey="&charsetMenuMore4.accesskey;"
+            datasources="rdf:charset-menu" ref="NC:BrowserMore4CharsetMenuRoot">
+        <template>
+          <rule>
+            <menupopup>
+            <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+            </menupopup>
+          </rule>
+        </template>
+        <menupopup>
+        </menupopup>
+      </menu>
+      <menu label="&charsetMenuMore5.label;"
+            accesskey="&charsetMenuMore5.accesskey;"
+            datasources="rdf:charset-menu" ref="NC:BrowserMore5CharsetMenuRoot">
+        <template>
+          <rule>
+            <menupopup>
+            <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+            </menupopup>
+          </rule>
+        </template>
+        <menupopup>
+        </menupopup>
+      </menu>
+      <menu label="&charsetMenuUnicode.label;"
+            accesskey="&charsetMenuUnicode.accesskey;"
+            datasources="rdf:charset-menu" ref="NC:BrowserUnicodeCharsetMenuRoot">
+        <template>
+          <rule>
+            <menupopup>
+            <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+            </menupopup>
+          </rule>
+        </template>
+        <menupopup>
+        </menupopup>
+      </menu>
+      <menuseparator />
+    </menupopup>
+  </menu>
+  <menuitem name="charsetCustomize"
+            accesskey="&charsetCustomize.accesskey;"
+            label="&charsetCustomize.label;"
+            oncommand="window.openDialog('chrome://global/content/customizeCharset.xul', 'PrefWindow', 'chrome,modal=yes,resizable=yes', 'browser');"/>
+  </menupopup>
+</menu>
+                <menuseparator/>
+                <menuitem id="fullScreenItem"
+                          accesskey="&fullScreenCmd.accesskey;"
+                          label="&fullScreenCmd.label;"
+                          key="key_fullScreen"
+                          type="checkbox"
+                          observes="View:FullScreen"/>
+                <menuitem id="menu_showAllTabs"
+                          hidden="true"
+                          accesskey="&showAllTabsCmd.accesskey;"
+                          label="&showAllTabsCmd.label;"
+                          command="Browser:ShowAllTabs"
+                          key="key_showAllTabs"/>
+                <menuseparator hidden="true" id="documentDirection-separator"/>
+                <menuitem id="documentDirection-swap"
+                          hidden="true"
+                          label="&bidiSwitchPageDirectionItem.label;"
+                          accesskey="&bidiSwitchPageDirectionItem.accesskey;"
+                          oncommand="SwitchDocumentDirection(window.content)"/>
+              </menupopup>
+            </menu>
+
+            <menu id="history-menu"
+                  label="&historyMenu.label;"
+                  accesskey="&historyMenu.accesskey;">
+              <menupopup id="goPopup"
+                         placespopup="true"
+                         oncommand="this.parentNode._placesView._onCommand(event);"
+                         onclick="checkForMiddleClick(this, event);"
+                         onpopupshowing="if (!this.parentNode._placesView)
+                                           new HistoryMenu(event);"
+                         tooltip="bhTooltip"
+                         popupsinherittooltip="true">
+                <menuitem id="historyMenuBack"
+                          class="show-only-for-keyboard"
+                          label="&backCmd.label;"
+                          key="goBackKb"
+                          command="Browser:BackOrBackDuplicate"
+                          onclick="checkForMiddleClick(this, event);"/>
+                <menuitem id="historyMenuForward"
+                          class="show-only-for-keyboard"
+                          label="&forwardCmd.label;"
+                          key="goForwardKb"
+                          command="Browser:ForwardOrForwardDuplicate"
+                          onclick="checkForMiddleClick(this, event);"/>
+                <menuitem id="historyMenuHome"
+                          class="show-only-for-keyboard"
+                          label="&historyHomeCmd.label;"
+                          oncommand="BrowserGoHome(event);"
+                          onclick="checkForMiddleClick(this, event);"
+                          key="goHome"/>
+                <menuseparator id="historyMenuHomeSeparator"
+                               class="show-only-for-keyboard"/>
+                <menuitem id="menu_showAllHistory"
+                          label="&showAllHistoryCmd2.label;"
+                          key="showAllHistoryKb"
+                          command="Browser:ShowAllHistory"/>
+                <menuseparator id="showAllHistorySeparator"/>
+                <menuitem id="sync-tabs-menuitem"
+                          class="syncTabsMenuItem"
+                          label="&syncTabsMenu.label;"
+                          oncommand="BrowserOpenSyncTabs();"
+                          disabled="true"/>
+                <menuitem id="historyRestoreLastSession"
+                          class="restoreLastSession"
+                          label="&historyRestoreLastSession.label;"
+                          oncommand="restoreLastSession();"
+                          disabled="true"/>
+                <menu id="historyUndoMenu"
+                      class="recentlyClosedTabsMenu"
+                      label="&historyUndoMenu.label;"
+                      disabled="true">
+                  <menupopup id="historyUndoPopup"
+                             placespopup="true"
+                             onpopupshowing="document.getElementById('history-menu')._placesView.populateUndoSubmenu();"/>
+                </menu>
+                <menu id="historyUndoWindowMenu"
+                      class="recentlyClosedWindowsMenu"
+                      label="&historyUndoWindowMenu.label;"
+                      disabled="true">
+                  <menupopup id="historyUndoWindowPopup"
+                             placespopup="true"
+                             onpopupshowing="document.getElementById('history-menu')._placesView.populateUndoWindowSubmenu();"/>
+                </menu>
+                <menuseparator id="startHistorySeparator"
+                               class="hide-if-empty-places-result"/>
+              </menupopup>
+            </menu>
+
+  <menu id="bookmarksMenu"
+        label="&bookmarksMenu.label;"
+        accesskey="&bookmarksMenu.accesskey;"
+        ondragenter="PlacesMenuDNDHandler.onDragEnter(event);"
+        ondragover="PlacesMenuDNDHandler.onDragOver(event);"
+        ondrop="PlacesMenuDNDHandler.onDrop(event);">
+    <menupopup id="bookmarksMenuPopup"
+               placespopup="true"
+               context="placesContext"
+               openInTabs="children"
+               oncommand="BookmarksEventHandler.onCommand(event, this.parentNode._placesView);"
+               onclick="BookmarksEventHandler.onClick(event, this.parentNode._placesView);"
+               onpopupshowing="PlacesCommandHook.updateBookmarkAllTabsCommand();
+                               if (!this.parentNode._placesView)
+                                 new PlacesMenu(event, 'place:folder=BOOKMARKS_MENU');"
+               tooltip="bhTooltip" popupsinherittooltip="true">
+      <menuitem id="bookmarksShowAll"
+                label="&showAllBookmarks2.label;"
+                command="Browser:ShowAllBookmarks"
+                key="manBookmarkKb"/>
+      <menuseparator id="organizeBookmarksSeparator"/>
+      <menuitem id="menu_bookmarkThisPage"
+                label="&bookmarkThisPageCmd.label;"
+                command="Browser:AddBookmarkAs"
+                key="addBookmarkAsKb"/>
+      <menuitem id="subscribeToPageMenuitem"
+                class="menuitem-iconic"
+                label="&subscribeToPageMenuitem.label;"
+                oncommand="return FeedHandler.subscribeToFeed(null, event);"
+                onclick="checkForMiddleClick(this, event);"
+                observes="singleFeedMenuitemState"/>
+      <menu id="subscribeToPageMenupopup"
+            class="menu-iconic"
+            label="&subscribeToPageMenupopup.label;"
+            observes="multipleFeedsMenuState">
+        <menupopup id="subscribeToPageSubmenuMenupopup"
+                   onpopupshowing="return FeedHandler.buildFeedList(event.target);"
+                   oncommand="return FeedHandler.subscribeToFeed(null, event);"
+                   onclick="checkForMiddleClick(this, event);"/>
+      </menu>
+      <menuitem id="menu_bookmarkAllTabs"
+                label="&addCurPagesCmd.label;"
+                class="show-only-for-keyboard"
+                command="Browser:BookmarkAllTabs"
+                key="bookmarkAllTabsKb"/>
+      <menuseparator id="bookmarksToolbarSeparator"/>
+      <menu id="bookmarksToolbarFolderMenu"
+            class="menu-iconic bookmark-item"
+            label="&personalbarCmd.label;"
+            container="true">
+        <menupopup id="bookmarksToolbarFolderPopup"
+                   placespopup="true"
+                   context="placesContext"
+                   onpopupshowing="if (!this.parentNode._placesView)
+                                     new PlacesMenu(event, 'place:folder=TOOLBAR');"/>
+      </menu>
+      <menuseparator id="bookmarksMenuItemsSeparator"/>
+      <!-- Bookmarks menu items -->
+      <menuseparator builder="end"
+                     class="hide-if-empty-places-result"/>
+      <menuitem id="menu_unsortedBookmarks"
+                label="&unsortedBookmarksCmd.label;"
+                oncommand="PlacesCommandHook.showPlacesOrganizer('UnfiledBookmarks');"/>
+    </menupopup>
+  </menu>
+
+            <menu id="tools-menu"
+                  label="&toolsMenu.label;"
+                  accesskey="&toolsMenu.accesskey;">
+              <menupopup id="menu_ToolsPopup"
+                         onpopupshowing="gSyncUI.updateUI();"
+                         >
+              <menuitem id="menu_search"
+                        class="show-only-for-keyboard"
+                        label="&search.label;"
+                        accesskey="&search.accesskey;"
+                        key="key_search"
+                        command="Tools:Search"/>
+              <menuseparator id="browserToolsSeparator"
+                             class="show-only-for-keyboard"/>
+              <menuitem id="menu_openDownloads"
+                        label="&downloads.label;"
+                        accesskey="&downloads.accesskey;"
+                        key="key_openDownloads"
+                        command="Tools:Downloads"/>
+              <menuitem id="menu_openAddons"
+                        label="&addons.label;"
+                        accesskey="&addons.accesskey;"
+                        key="key_openAddons"
+                        command="Tools:Addons"/>
+              <!-- only one of sync-setup or sync-menu will be showing at once -->
+              <menuitem id="sync-setup"
+                        label="&syncSetup.label;"
+                        accesskey="&syncSetup.accesskey;"
+                        observes="sync-setup-state"
+                        oncommand="gSyncUI.openSetup()"/>
+              <menuitem id="sync-syncnowitem"
+                        label="&syncSyncNowItem.label;"
+                        accesskey="&syncSyncNowItem.accesskey;"
+                        observes="sync-syncnow-state"
+                        oncommand="gSyncUI.doSync(event);"/>
+              <menuseparator id="devToolsSeparator"/>
+              <menu id="webDeveloperMenu"
+                    label="&webDeveloperMenu.label;"
+                    accesskey="&webDeveloperMenu.accesskey;">
+                <menupopup id="menuWebDeveloperPopup">
+                  <menuitem id="menu_devToolbar"
+                            type="checkbox"
+                            autocheck="false"
+                            hidden="true"
+                            label="&devToolbarMenu.label;"
+                            accesskey="&devToolbarMenu.accesskey;"
+                            key="key_devToolbar"
+                            command="Tools:DevToolbar"/>
+                  <menuitem id="webConsole"
+                            type="checkbox"
+                            label="&webConsoleCmd.label;"
+                            accesskey="&webConsoleCmd.accesskey;"
+                            key="key_webConsole"
+                            command="Tools:WebConsole"/>
+                  <menuitem id="menu_pageinspect"
+                            type="checkbox"
+                            hidden="true"
+                            label="&inspectMenu.label;"
+                            accesskey="&inspectMenu.accesskey;"
+                            key="key_inspect"
+                            command="Tools:Inspect"/>
+                  <menuitem id="menu_responsiveUI"
+                            type="checkbox"
+                            hidden="true"
+                            label="&responsiveDesignTool.label;"
+                            accesskey="&responsiveDesignTool.accesskey;"
+                            key="key_responsiveUI"
+                            command="Tools:ResponsiveUI"/>
+                  <menuitem id="menu_debugger"
+                            hidden="true"
+                            type="checkbox"
+                            label="&debuggerMenu.label2;"
+                            key="key_debugger"
+                            command="Tools:Debugger"/>
+                  <menuitem id="menu_remoteDebugger"
+                            hidden="true"
+                            label="&remoteDebuggerMenu.label;"
+                            command="Tools:RemoteDebugger"/>
+                  <menuitem id="menu_chromeDebugger"
+                            hidden="true"
+                            label="&chromeDebuggerMenu.label;"
+                            command="Tools:ChromeDebugger"/>
+                  <menuitem id="menu_scratchpad"
+                            hidden="true"
+                            label="&scratchpad.label;"
+                            accesskey="&scratchpad.accesskey;"
+                            key="key_scratchpad"
+                            command="Tools:Scratchpad"/>
+                  <menuitem id="menu_styleeditor"
+                            hidden="true"
+                            label="&styleeditor.label;"
+                            accesskey="&styleeditor.accesskey;"
+                            key="key_styleeditor"
+                            command="Tools:StyleEditor"/>
+                  <menuitem id="menu_pageSource"
+                            accesskey="&pageSourceCmd.accesskey;"
+                            label="&pageSourceCmd.label;"
+                            key="key_viewSource"
+                            command="View:PageSource"/>
+                  <menuitem id="javascriptConsole"
+                            hidden="true"
+                            label="&errorConsoleCmd.label;"
+                            accesskey="&errorConsoleCmd.accesskey;"
+                            key="key_errorConsole"
+                            oncommand="toJavaScriptConsole();"/>
+                  <menuseparator id="devToolsEndSeparator"/>
+                  <menuitem id="getMoreDevtools"
+                            label="&getMoreDevtoolsCmd.label;"
+                            accesskey="&getMoreDevtoolsCmd.accesskey;"
+                            oncommand="openUILinkIn('https://addons.mozilla.org/firefox/collections/mozilla/webdeveloper/', 'tab');"/>
+                </menupopup>
+              </menu>
+              <menuitem id="menu_pageInfo"
+                        accesskey="&pageInfoCmd.accesskey;"
+                        label="&pageInfoCmd.label;"
+                        key="key_viewInfo"
+                        command="View:PageInfo"/>
+              <menuseparator id="sanitizeSeparator"/>
+              <menuitem id="privateBrowsingItem"
+                        label="&privateBrowsingCmd.start.label;"
+                        accesskey="&privateBrowsingCmd.start.accesskey;"
+                        startlabel="&privateBrowsingCmd.start.label;"
+                        startaccesskey="&privateBrowsingCmd.start.accesskey;"
+                        stoplabel="&privateBrowsingCmd.stop.label;"
+                        stopaccesskey="&privateBrowsingCmd.stop.accesskey;"
+                        key="key_privatebrowsing"
+                        command="Tools:PrivateBrowsing"/>
+              <menuitem id="sanitizeItem"
+                        accesskey="&clearRecentHistory.accesskey;"
+                        label="&clearRecentHistory.label;"
+                        key="key_sanitize"
+                        command="Tools:Sanitize"/>
+              </menupopup>
+            </menu>
+
+          <menu id="helpMenu" />
+        </menubar>
+      </toolbaritem>
+
+    </toolbar>
+
+    <toolbar id="nav-bar" class="toolbar-primary chromeclass-toolbar"
+             toolbarname="&navbarCmd.label;" accesskey="&navbarCmd.accesskey;"
+             fullscreentoolbar="true" mode="icons" customizable="true"
+             iconsize="large"
+             defaultset="unified-back-forward-button,urlbar-container,reload-button,stop-button,search-container,home-button,bookmarks-menu-button-container,window-controls"
+             context="toolbar-context-menu">
+
+      <toolbaritem id="unified-back-forward-button" class="chromeclass-toolbar-additional"
+                   context="backForwardMenu" removable="true"
+                   forwarddisabled="true"
+                   title="&backForwardItem.title;">
+        <toolbarbutton id="back-button" class="toolbarbutton-1"
+                       label="&backCmd.label;"
+                       command="Browser:BackOrBackDuplicate"
+                       onclick="checkForMiddleClick(this, event);"
+                       tooltip="back-button-tooltip"/>
+        <toolbarbutton id="forward-button" class="toolbarbutton-1"
+                       label="&forwardCmd.label;"
+                       command="Browser:ForwardOrForwardDuplicate"
+                       onclick="checkForMiddleClick(this, event);"
+                       tooltip="forward-button-tooltip"/>
+        <dummyobservertarget hidden="true"
+                             onbroadcast="if (this.getAttribute('disabled') == 'true')
+                                            this.parentNode.setAttribute('forwarddisabled', 'true');
+                                          else
+                                            this.parentNode.removeAttribute('forwarddisabled');">
+          <observes element="Browser:ForwardOrForwardDuplicate" attribute="disabled"/>
+        </dummyobservertarget>
+      </toolbaritem>
+
+      <toolbaritem id="urlbar-container" align="center" flex="400" persist="width" combined="true"
+                   title="&locationItem.title;" class="chromeclass-location" removable="true">
+        <textbox id="urlbar" flex="1"
+                 placeholder="&urlbar.placeholder;"
+                 type="autocomplete"
+                 autocompletesearch="urlinline history"
+                 autocompletesearchparam="enable-actions"
+                 autocompletepopup="PopupAutoCompleteRichResult"
+                 completeselectedindex="true"
+                 tabscrolling="true"
+                 showcommentcolumn="true"
+                 showimagecolumn="true"
+                 enablehistory="true"
+                 maxrows="6"
+                 newlines="stripsurroundingwhitespace"
+                 oninput="gBrowser.userTypedValue = this.value;"
+                 ontextentered="this.handleCommand(param);"
+                 ontextreverted="return this.handleRevert();"
+                 pageproxystate="invalid"
+                 onfocus="document.getElementById('identity-box').style.MozUserFocus= 'normal'"
+                 onblur="setTimeout(function() document.getElementById('identity-box').style.MozUserFocus = '', 0);">
+          <box id="notification-popup-box" hidden="true" align="center">
+            <image id="default-notification-icon" class="notification-anchor-icon" role="button"/>
+            <image id="geo-notification-icon" class="notification-anchor-icon" role="button"/>
+            <image id="addons-notification-icon" class="notification-anchor-icon" role="button"/>
+            <image id="indexedDB-notification-icon" class="notification-anchor-icon" role="button"/>
+            <image id="password-notification-icon" class="notification-anchor-icon" role="button"/>
+            <image id="webapps-notification-icon" class="notification-anchor-icon" role="button"/>
+            <image id="plugins-notification-icon" class="notification-anchor-icon" role="button"/>
+          </box>
+          <!-- Use onclick instead of normal popup= syntax since the popup
+               code fires onmousedown, and hence eats our favicon drag events.
+               We only add the identity-box button to the tab order when the location bar
+               has focus, otherwise pressing F6 focuses it instead of the location bar -->
+          <box id="identity-box" role="button"
+               onclick="gIdentityHandler.handleIdentityButtonEvent(event);"
+               onkeypress="gIdentityHandler.handleIdentityButtonEvent(event);"
+               ondragstart="gIdentityHandler.onDragStart(event);">
+            <hbox id="identity-box-inner" align="center">
+              <hbox id="page-proxy-stack"
+                    onclick="PageProxyClickHandler(event);">
+                <image id="page-proxy-favicon"
+                       pageproxystate="invalid"/>
+              </hbox>
+              <hbox id="identity-icon-labels">
+                <label id="identity-icon-label" class="plain" flex="1"/>
+                <label id="identity-icon-country-label" class="plain"/>
+              </hbox>
+            </hbox>
+          </box>
+          <box id="urlbar-display-box" align="center">
+            <label id="urlbar-display" value="&urlbar.switchToTab.label;"/>
+          </box>
+          <hbox id="urlbar-icons">
+            <image id="page-report-button"
+                   class="urlbar-icon"
+                   hidden="true"
+                   tooltiptext="&pageReportIcon.tooltip;"
+                   onclick="gPopupBlockerObserver.onReportButtonClick(event);"/>
+
+            <image id="star-button"
+                   class="urlbar-icon"
+                   onclick="PlacesStarButton.onClick(event);"/>
+            <image id="go-button"
+                   class="urlbar-icon"
+                   tooltiptext="&goEndCap.tooltip;"
+                   onclick="gURLBar.handleCommand(event);"/>
+          </hbox>
+          <toolbarbutton id="urlbar-go-button"
+                         class="chromeclass-toolbar-additional"
+                         onclick="gURLBar.handleCommand(event);"
+                         tooltiptext="&goEndCap.tooltip;"/>
+          <toolbarbutton id="urlbar-reload-button"
+                         class="chromeclass-toolbar-additional"
+                         command="Browser:ReloadOrDuplicate"
+                         onclick="checkForMiddleClick(this, event);"
+                         tooltiptext="&reloadButton.tooltip;"/>
+          <toolbarbutton id="urlbar-stop-button"
+                         class="chromeclass-toolbar-additional"
+                         command="Browser:Stop"
+                         tooltiptext="&stopButton.tooltip;"/>
+        </textbox>
+      </toolbaritem>
+
+      <toolbarbutton id="reload-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     label="&reloadCmd.label;" removable="true"
+                     command="Browser:ReloadOrDuplicate"
+                     onclick="checkForMiddleClick(this, event);"
+                     tooltiptext="&reloadButton.tooltip;"/>
+
+      <toolbarbutton id="stop-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     label="&stopCmd.label;" removable="true"
+                     command="Browser:Stop"
+                     tooltiptext="&stopButton.tooltip;"/>
+
+      <toolbaritem id="search-container" title="&searchItem.title;"
+                   align="center" class="chromeclass-toolbar-additional"
+                   flex="100" persist="width" removable="true">
+        <searchbar id="searchbar" flex="1"/>
+      </toolbaritem>
+
+      <toolbarbutton id="home-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     persist="class" removable="true"
+                     label="&homeButton.label;"
+                     ondragover="homeButtonObserver.onDragOver(event)"
+                     ondragenter="homeButtonObserver.onDragOver(event)"
+                     ondrop="homeButtonObserver.onDrop(event)"
+                     ondragexit="homeButtonObserver.onDragExit(event)"
+                     onclick="BrowserGoHome(event);"
+                     aboutHomeOverrideTooltip="&abouthome.pageTitle;"/>
+
+      <toolbaritem id="bookmarks-menu-button-container"
+                   class="chromeclass-toolbar-additional"
+                   removable="true"
+                   title="&bookmarksMenuButton.label;">
+        <toolbarbutton id="bookmarks-menu-button"
+                       type="menu"
+                       class="toolbarbutton-1"
+                       label="&bookmarksMenuButton.label;"
+                       tooltiptext="&bookmarksMenuButton.tooltip;"
+                       ondragenter="PlacesMenuDNDHandler.onDragEnter(event);"
+                       ondragover="PlacesMenuDNDHandler.onDragOver(event);"
+                       ondragexit="PlacesMenuDNDHandler.onDragExit(event);"
+                       ondrop="PlacesMenuDNDHandler.onDrop(event);">
+          <menupopup id="BMB_bookmarksPopup"
+                     placespopup="true"
+                     context="placesContext"
+                     openInTabs="children"
+                     oncommand="BookmarksEventHandler.onCommand(event, this.parentNode._placesView);"
+                     onclick="BookmarksEventHandler.onClick(event, this.parentNode._placesView);"
+                     onpopupshowing="BookmarksMenuButton.onPopupShowing(event);
+                                     if (!this.parentNode._placesView)
+                                       new PlacesMenu(event, 'place:folder=BOOKMARKS_MENU');"
+                     tooltip="bhTooltip" popupsinherittooltip="true">
+            <menuitem id="BMB_viewBookmarksToolbar"
+                      placesanonid="view-toolbar"
+                      toolbarId="PersonalToolbar"
+                      type="checkbox"
+                      oncommand="onViewToolbarCommand(event)"
+                      label="&viewBookmarksToolbar.label;"/>
+            <menuseparator/>
+            <menuitem id="BMB_bookmarksShowAll"
+                      label="&showAllBookmarks2.label;"
+                      command="Browser:ShowAllBookmarks"
+                      key="manBookmarkKb"/>
+            <menuseparator/>
+            <menuitem id="BMB_bookmarkThisPage"
+                      class="menuitem-iconic"
+                      label="&bookmarkThisPageCmd.label;"
+                      command="Browser:AddBookmarkAs"
+                      key="addBookmarkAsKb"/>
+            <menuitem id="BMB_subscribeToPageMenuitem"
+                      class="menuitem-iconic"
+                      label="&subscribeToPageMenuitem.label;"
+                      oncommand="return FeedHandler.subscribeToFeed(null, event);"
+                      onclick="checkForMiddleClick(this, event);"
+                      observes="singleFeedMenuitemState"/>
+            <menu id="BMB_subscribeToPageMenupopup"
+                  class="menu-iconic"
+                  label="&subscribeToPageMenupopup.label;"
+                  observes="multipleFeedsMenuState">
+              <menupopup id="BMB_subscribeToPageSubmenuMenupopup"
+                         onpopupshowing="return FeedHandler.buildFeedList(event.target);"
+                         oncommand="return FeedHandler.subscribeToFeed(null, event);"
+                         onclick="checkForMiddleClick(this, event);"/>
+            </menu>
+            <menuseparator/>
+            <menu id="BMB_bookmarksToolbar"
+                  placesanonid="toolbar-autohide"
+                  class="menu-iconic bookmark-item"
+                  label="&personalbarCmd.label;"
+                  container="true">
+              <menupopup id="BMB_bookmarksToolbarPopup"
+                         placespopup="true"
+                         context="placesContext"
+                         onpopupshowing="if (!this.parentNode._placesView)
+                                           new PlacesMenu(event, 'place:folder=TOOLBAR');"/>
+            </menu>
+            <menuseparator/>
+            <!-- Bookmarks menu items -->
+            <menuseparator builder="end"
+                           class="hide-if-empty-places-result"/>
+            <menuitem id="BMB_unsortedBookmarks"
+                      label="&bookmarksMenuButton.unsorted.label;"
+                      oncommand="PlacesCommandHook.showPlacesOrganizer('UnfiledBookmarks');"
+                      class="menuitem-iconic"/>
+          </menupopup>
+        </toolbarbutton>
+      </toolbaritem>
+
+      <hbox id="window-controls" hidden="true" fullscreencontrol="true" pack="end">
+        <toolbarbutton id="minimize-button"
+                       tooltiptext="&fullScreenMinimize.tooltip;"
+                       oncommand="window.minimize();"/>
+
+        <toolbarbutton id="restore-button"
+                       tooltiptext="&fullScreenRestore.tooltip;"
+                       oncommand="BrowserFullScreen();"/>
+
+        <toolbarbutton id="close-button"
+                       tooltiptext="&fullScreenClose.tooltip;"
+                       oncommand="BrowserTryToCloseWindow();"/>
+      </hbox>
+    </toolbar>
+
+    <toolbarset id="customToolbars" context="toolbar-context-menu"/>
+
+    <toolbar id="PersonalToolbar"
+             mode="icons" iconsize="small" defaulticonsize="small"
+             lockiconsize="true"
+             class="chromeclass-directories"
+             context="toolbar-context-menu"
+             defaultset="personal-bookmarks"
+             toolbarname="&personalbarCmd.label;" accesskey="&personalbarCmd.accesskey;"
+             collapsed="true"
+             customizable="true">
+      <toolbaritem flex="1" id="personal-bookmarks" title="&bookmarksItem.title;"
+                   removable="true">
+        <hbox flex="1"
+              id="PlacesToolbar"
+              context="placesContext"
+              onclick="BookmarksEventHandler.onClick(event, this._placesView);"
+              oncommand="BookmarksEventHandler.onCommand(event, this._placesView);"
+              tooltip="bhTooltip"
+              popupsinherittooltip="true">
+          <toolbarbutton class="bookmark-item bookmarks-toolbar-customize"
+                         mousethrough="never"
+                         label="&bookmarksToolbarItem.label;"/>
+          <hbox flex="1">
+            <hbox align="center">
+              <image id="PlacesToolbarDropIndicator"
+                     mousethrough="always"
+                     collapsed="true"/>
+            </hbox>
+            <scrollbox orient="horizontal"
+                       id="PlacesToolbarItems"
+                       flex="1"/>
+            <toolbarbutton type="menu"
+                           id="PlacesChevron"
+                           class="chevron"
+                           mousethrough="never"
+                           collapsed="true"
+                           tooltiptext="&bookmarksToolbarChevron.tooltip;"
+                           onpopupshowing="document.getElementById('PlacesToolbar')
+                                                   ._placesView._onChevronPopupShowing(event);">
+              <menupopup id="PlacesChevronPopup"
+                         placespopup="true"
+                         tooltip="bhTooltip" popupsinherittooltip="true"
+                         context="placesContext"/>
+            </toolbarbutton>
+          </hbox>
+        </hbox>
+      </toolbaritem>
+    </toolbar>
+
+
+
+    <toolbar id="TabsToolbar"
+             class="toolbar-primary"
+             fullscreentoolbar="true"
+             customizable="true"
+             mode="icons" lockmode="true"
+             iconsize="small" defaulticonsize="small" lockiconsize="true"
+             aria-label="&tabsToolbar.label;"
+             context="toolbar-context-menu"
+             defaultset="appmenu-toolbar-button,tabbrowser-tabs,new-tab-button,alltabs-button,tabs-closebutton"
+             collapsed="true">
+
+      <toolbarbutton id="appmenu-toolbar-button"
+                     class="chromeclass-toolbar-additional"
+                     type="menu"
+                     label="&brandShortName;"
+                     tooltiptext="&appMenuButton.tooltip;">
+
+<menupopup id="appmenu-popup"
+           onpopupshowing="if (event.target == this) {
+                             updateEditUIVisibility();
+                             gSyncUI.updateUI();
+                             return;
+                           }
+                           if (event.target.parentNode.parentNode.parentNode.parentNode == this)
+                             this._currentPopup = event.target;">
+  <hbox>
+    <vbox id="appmenuPrimaryPane">
+      <splitmenu id="appmenu_newTab"
+                 label="&tabCmd.label;"
+                 command="cmd_newNavigatorTab">
+          <menupopup>
+            <menuitem id="appmenu_newTab_popup"
+                      label="&tabCmd.label;"
+                      command="cmd_newNavigatorTab"
+                      key="key_newNavigatorTab"/>
+            <menuitem id="appmenu_newNavigator"
+                      label="&newNavigatorCmd.label;"
+                      command="cmd_newNavigator"
+                      key="key_newNavigator"/>
+            <menuseparator/>
+            <menuitem id="appmenu_openFile"
+                      label="&openFileCmd.label;"
+                      command="Browser:OpenFile"
+                      key="openFileKb"/>
+          </menupopup>
+      </splitmenu>
+      <menuitem id="appmenu_privateBrowsing"
+                class="menuitem-iconic menuitem-iconic-tooltip"
+                label="&privateBrowsingCmd.start.label;"
+                startlabel="&privateBrowsingCmd.start.label;"
+                stoplabel="&privateBrowsingCmd.stop.label;"
+                command="Tools:PrivateBrowsing"
+                key="key_privatebrowsing"/>
+      <menuitem label="&goOfflineCmd.label;"
+                id="appmenu_offlineModeRecovery"
+                type="checkbox"
+                observes="workOfflineMenuitemState"
+                oncommand="BrowserOffline.toggleOfflineStatus();"/>
+      <menuseparator class="appmenu-menuseparator"/>
+      <hbox>
+        <menuitem id="appmenu-edit-label"
+                  label="&appMenuEdit.label;"
+                  disabled="true"/>
+        <toolbarbutton id="appmenu-cut"
+                       class="appmenu-edit-button"
+                       command="cmd_cut"
+                       onclick="if (!this.disabled) hidePopup();"
+                       tooltiptext="&cutButton.tooltip;"/>
+        <toolbarbutton id="appmenu-copy"
+                       class="appmenu-edit-button"
+                       command="cmd_copy"
+                       onclick="if (!this.disabled) hidePopup();"
+                       tooltiptext="&copyButton.tooltip;"/>
+        <toolbarbutton id="appmenu-paste"
+                       class="appmenu-edit-button"
+                       command="cmd_paste"
+                       onclick="if (!this.disabled) hidePopup();"
+                       tooltiptext="&pasteButton.tooltip;"/>
+        <spacer flex="1"/>
+        <menu id="appmenu-editmenu">
+          <menupopup id="appmenu-editmenu-menupopup">
+            <menuitem id="appmenu-editmenu-cut"
+                      class="menuitem-iconic"
+                      label="&cutCmd.label;"
+                      key="key_cut"
+                      command="cmd_cut"/>
+            <menuitem id="appmenu-editmenu-copy"
+                      class="menuitem-iconic"
+                      label="&copyCmd.label;"
+                      key="key_copy"
+                      command="cmd_copy"/>
+            <menuitem id="appmenu-editmenu-paste"
+                      class="menuitem-iconic"
+                      label="&pasteCmd.label;"
+                      key="key_paste"
+                      command="cmd_paste"/>
+            <menuseparator/>
+            <menuitem id="appmenu-editmenu-undo"
+                      label="&undoCmd.label;"
+                      key="key_undo"
+                      command="cmd_undo"/>
+            <menuitem id="appmenu-editmenu-redo"
+                      label="&redoCmd.label;"
+                      key="key_redo"
+                      command="cmd_redo"/>
+            <menuseparator/>
+            <menuitem id="appmenu-editmenu-selectAll"
+                      label="&selectAllCmd.label;"
+                      key="key_selectAll"
+                      command="cmd_selectAll"/>
+            <menuseparator/>
+            <menuitem id="appmenu-editmenu-delete"
+                      label="&deleteCmd.label;"
+                      key="key_delete"
+                      command="cmd_delete"/>
+          </menupopup>
+        </menu>
+      </hbox>
+      <menuitem id="appmenu_find"
+                class="menuitem-tooltip"
+                label="&appMenuFind.label;"
+                command="cmd_find"
+                key="key_find"/>
+      <menuseparator class="appmenu-menuseparator"/>
+      <menuitem id="appmenu_savePage"
+                class="menuitem-tooltip"
+                label="&savePageCmd.label;"
+                command="Browser:SavePage"
+                key="key_savePage"/>
+      <menuitem id="appmenu_sendLink"
+                label="&sendPageCmd.label;"
+                command="Browser:SendLink"/>
+      <splitmenu id="appmenu_print"
+                 iconic="true"
+                 label="&printCmd.label;"
+                 command="cmd_print">
+          <menupopup>
+            <menuitem id="appmenu_print_popup"
+                      class="menuitem-iconic"
+                      label="&printCmd.label;"
+                      command="cmd_print"
+                      key="printKb"/>
+            <menuitem id="appmenu_printPreview"
+                      label="&printPreviewCmd.label;"
+                      command="cmd_printPreview"/>
+            <menuitem id="appmenu_printSetup"
+                      label="&printSetupCmd.label;"
+                      command="cmd_pageSetup"/>
+          </menupopup>
+      </splitmenu>
+      <menuseparator class="appmenu-menuseparator"/>
+      <menu id="appmenu_webDeveloper"
+            label="&appMenuWebDeveloper.label;">
+        <menupopup id="appmenu_webDeveloper_popup">
+          <menuitem id="appmenu_devToolbar"
+                    type="checkbox"
+                    autocheck="false"
+                    hidden="true"
+                    label="&devToolbarMenu.label;"
+                    command="Tools:DevToolbar"
+                    key="key_devToolbar"/>
+          <menuitem id="appmenu_webConsole"
+                    label="&webConsoleCmd.label;"
+                    type="checkbox"
+                    command="Tools:WebConsole"
+                    key="key_webConsole"/>
+          <menuitem id="appmenu_pageInspect"
+                    hidden="true"
+                    label="&inspectMenu.label;"
+                    type="checkbox"
+                    command="Tools:Inspect"
+                    key="key_inspect"/>
+          <menuitem id="appmenu_responsiveUI"
+                    hidden="true"
+                    label="&responsiveDesignTool.label;"
+                    type="checkbox"
+                    command="Tools:ResponsiveUI"
+                    key="key_responsiveUI"/>
+          <menuitem id="appmenu_debugger"
+                    hidden="true"
+                    type="checkbox"
+                    label="&debuggerMenu.label2;"
+                    key="key_debugger"
+                    command="Tools:Debugger"/>
+          <menuitem id="appmenu_remoteDebugger"
+                    hidden="true"
+                    label="&remoteDebuggerMenu.label;"
+                    command="Tools:RemoteDebugger"/>
+          <menuitem id="appmenu_chromeDebugger"
+                    hidden="true"
+                    label="&chromeDebuggerMenu.label;"
+                    command="Tools:ChromeDebugger"/>
+          <menuitem id="appmenu_scratchpad"
+                    hidden="true"
+                    label="&scratchpad.label;"
+                    key="key_scratchpad"
+                    command="Tools:Scratchpad"/>
+          <menuitem id="appmenu_styleeditor"
+                    hidden="true"
+                    label="&styleeditor.label;"
+                    key="key_styleeditor"
+                    command="Tools:StyleEditor"/>
+          <menuitem id="appmenu_pageSource"
+                    label="&viewPageSourceCmd.label;"
+                    command="View:PageSource"
+                    key="key_viewSource"/>
+          <menuitem id="appmenu_errorConsole"
+                    hidden="true"
+                    label="&errorConsoleCmd.label;"
+                    key="key_errorConsole"
+                    oncommand="toJavaScriptConsole();"/>
+          <menuseparator id="appmenu_devToolsEndSeparator"/>
+          <menuitem id="appmenu_getMoreDevtools"
+                    label="&getMoreDevtoolsCmd.label;"
+                    oncommand="openUILinkIn('https://addons.mozilla.org/firefox/collections/mozilla/webdeveloper/', 'tab');"/>
+          <menuseparator/>
+
+
+<menu id="appmenu_developer_charsetMenu"
+    label="&charsetMenu.label;"
+    datasources="rdf:charset-menu"
+    ref="NC:BrowserCharsetMenuRoot"
+    oncommand="MultiplexHandler(event)"
+    onpopupshowing="CreateMenu('browser'); CreateMenu('more-menu');"
+    onpopupshown="UpdateMenus(event);"
+    observes="isImage">
+  <template>
+    <rule rdf:type="http://home.netscape.com/NC-rdf#BookmarkSeparator">
+      <menupopup>
+      <menuseparator uri="..." />
+      </menupopup>
+    </rule>
+    <rule>
+      <menupopup>
+      <menuitem type="radio" name="charsetGroup" checked="rdf:http://home.netscape.com/NC-rdf#Checked" uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+      </menupopup>
+    </rule>
+  </template>
+
+  <menupopup>
+  <menu label="&charsetMenuAutodet.label;"
+        datasources="rdf:charset-menu" ref="NC:BrowserAutodetMenuRoot">
+    <template>
+      <rule rdf:type="http://home.netscape.com/NC-rdf#CharsetDetector">
+        <menupopup>
+        <menuitem type="radio" name="detectorGroup" checked="rdf:http://home.netscape.com/NC-rdf#Checked" uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+        </menupopup>
+      </rule>
+    </template>
+    <menupopup>
+    </menupopup>
+  </menu>
+  <menu label="&charsetMenuMore.label;"
+        datasources="rdf:charset-menu" ref="NC:BrowserMoreCharsetMenuRoot">
+    <template>
+      <rule>
+        <menupopup>
+        <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+        </menupopup>
+      </rule>
+    </template>
+    <menupopup>
+      <menu label="&charsetMenuMore1.label;"
+            datasources="rdf:charset-menu" ref="NC:BrowserMore1CharsetMenuRoot">
+        <template>
+          <rule>
+            <menupopup>
+            <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+            </menupopup>
+          </rule>
+        </template>
+        <menupopup>
+        </menupopup>
+      </menu>
+      <menu label="&charsetMenuMore2.label;"
+            datasources="rdf:charset-menu" ref="NC:BrowserMore2CharsetMenuRoot">
+        <template>
+          <rule>
+            <menupopup>
+            <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+            </menupopup>
+          </rule>
+        </template>
+        <menupopup>
+        </menupopup>
+      </menu>
+      <menu label="&charsetMenuMore3.label;"
+            datasources="rdf:charset-menu" ref="NC:BrowserMore3CharsetMenuRoot">
+        <template>
+          <rule>
+            <menupopup>
+            <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+            </menupopup>
+          </rule>
+        </template>
+        <menupopup>
+        </menupopup>
+      </menu>
+      <menu label="&charsetMenuMore4.label;"
+            datasources="rdf:charset-menu" ref="NC:BrowserMore4CharsetMenuRoot">
+        <template>
+          <rule>
+            <menupopup>
+            <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+            </menupopup>
+          </rule>
+        </template>
+        <menupopup>
+        </menupopup>
+      </menu>
+      <menu label="&charsetMenuMore5.label;"
+            datasources="rdf:charset-menu" ref="NC:BrowserMore5CharsetMenuRoot">
+        <template>
+          <rule>
+            <menupopup>
+            <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+            </menupopup>
+          </rule>
+        </template>
+        <menupopup>
+        </menupopup>
+      </menu>
+      <menu label="&charsetMenuUnicode.label;"
+            datasources="rdf:charset-menu" ref="NC:BrowserUnicodeCharsetMenuRoot">
+        <template>
+          <rule>
+            <menupopup>
+            <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+            </menupopup>
+          </rule>
+        </template>
+        <menupopup>
+        </menupopup>
+      </menu>
+      <menuseparator />
+    </menupopup>
+  </menu>
+  <menuitem name="charsetCustomize"
+            label="&charsetCustomize.label;"
+            oncommand="window.openDialog('chrome://global/content/customizeCharset.xul', 'PrefWindow', 'chrome,modal=yes,resizable=yes', 'browser');"/>
+  </menupopup>
+</menu>
+          <menuitem label="&goOfflineCmd.label;"
+                    type="checkbox"
+                    observes="workOfflineMenuitemState"
+                    oncommand="BrowserOffline.toggleOfflineStatus();"/>
+        </menupopup>
+      </menu>
+      <menuseparator class="appmenu-menuseparator"/>
+
+
+<menu id="appmenu_charsetMenu"
+    label="&charsetMenu.label;"
+    datasources="rdf:charset-menu"
+    ref="NC:BrowserCharsetMenuRoot"
+    oncommand="MultiplexHandler(event)"
+    onpopupshowing="CreateMenu('browser'); CreateMenu('more-menu');"
+    onpopupshown="UpdateMenus(event);"
+    observes="isImage">
+  <template>
+    <rule rdf:type="http://home.netscape.com/NC-rdf#BookmarkSeparator">
+      <menupopup>
+      <menuseparator uri="..." />
+      </menupopup>
+    </rule>
+    <rule>
+      <menupopup>
+      <menuitem type="radio" name="charsetGroup" checked="rdf:http://home.netscape.com/NC-rdf#Checked" uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+      </menupopup>
+    </rule>
+  </template>
+
+  <menupopup>
+  <menu label="&charsetMenuAutodet.label;"
+        datasources="rdf:charset-menu" ref="NC:BrowserAutodetMenuRoot">
+    <template>
+      <rule rdf:type="http://home.netscape.com/NC-rdf#CharsetDetector">
+        <menupopup>
+        <menuitem type="radio" name="detectorGroup" checked="rdf:http://home.netscape.com/NC-rdf#Checked" uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+        </menupopup>
+      </rule>
+    </template>
+    <menupopup>
+    </menupopup>
+  </menu>
+  <menu label="&charsetMenuMore.label;"
+        datasources="rdf:charset-menu" ref="NC:BrowserMoreCharsetMenuRoot">
+    <template>
+      <rule>
+        <menupopup>
+        <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+        </menupopup>
+      </rule>
+    </template>
+    <menupopup>
+      <menu label="&charsetMenuMore1.label;"
+            datasources="rdf:charset-menu" ref="NC:BrowserMore1CharsetMenuRoot">
+        <template>
+          <rule>
+            <menupopup>
+            <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+            </menupopup>
+          </rule>
+        </template>
+        <menupopup>
+        </menupopup>
+      </menu>
+      <menu label="&charsetMenuMore2.label;"
+            datasources="rdf:charset-menu" ref="NC:BrowserMore2CharsetMenuRoot">
+        <template>
+          <rule>
+            <menupopup>
+            <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+            </menupopup>
+          </rule>
+        </template>
+        <menupopup>
+        </menupopup>
+      </menu>
+      <menu label="&charsetMenuMore3.label;"
+            datasources="rdf:charset-menu" ref="NC:BrowserMore3CharsetMenuRoot">
+        <template>
+          <rule>
+            <menupopup>
+            <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+            </menupopup>
+          </rule>
+        </template>
+        <menupopup>
+        </menupopup>
+      </menu>
+      <menu label="&charsetMenuMore4.label;"
+            datasources="rdf:charset-menu" ref="NC:BrowserMore4CharsetMenuRoot">
+        <template>
+          <rule>
+            <menupopup>
+            <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+            </menupopup>
+          </rule>
+        </template>
+        <menupopup>
+        </menupopup>
+      </menu>
+      <menu label="&charsetMenuMore5.label;"
+            datasources="rdf:charset-menu" ref="NC:BrowserMore5CharsetMenuRoot">
+        <template>
+          <rule>
+            <menupopup>
+            <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+            </menupopup>
+          </rule>
+        </template>
+        <menupopup>
+        </menupopup>
+      </menu>
+      <menu label="&charsetMenuUnicode.label;"
+            datasources="rdf:charset-menu" ref="NC:BrowserUnicodeCharsetMenuRoot">
+        <template>
+          <rule>
+            <menupopup>
+            <menuitem uri="..." label="rdf:http://home.netscape.com/NC-rdf#Name"/>
+            </menupopup>
+          </rule>
+        </template>
+        <menupopup>
+        </menupopup>
+      </menu>
+      <menuseparator />
+    </menupopup>
+  </menu>
+  <menuitem name="charsetCustomize"
+            label="&charsetCustomize.label;"
+            oncommand="window.openDialog('chrome://global/content/customizeCharset.xul', 'PrefWindow', 'chrome,modal=yes,resizable=yes', 'browser');"/>
+  </menupopup>
+</menu>
+      <menuitem id="appmenu_fullScreen"
+                class="menuitem-tooltip"
+                label="&fullScreenCmd.label;"
+                type="checkbox"
+                observes="View:FullScreen"
+                key="key_fullScreen"/>
+      <!-- only one of sync-setup or sync-syncnow will be showing at once -->
+      <menuitem id="sync-setup-appmenu"
+                label="&syncSetup.label;"
+                observes="sync-setup-state"
+                oncommand="gSyncUI.openSetup()"/>
+      <menuitem id="sync-syncnowitem-appmenu"
+                label="&syncSyncNowItem.label;"
+                observes="sync-syncnow-state"
+                oncommand="gSyncUI.doSync(event);"/>
+      <menuitem id="appmenu-quit"
+                class="menuitem-iconic"
+                label="&quitApplicationCmd.label;"
+                command="cmd_quitApplication"/>
+    </vbox>
+    <vbox id="appmenuSecondaryPane">
+      <splitmenu id="appmenu_bookmarks"
+                 iconic="true"
+                 label="&bookmarksMenu.label;"
+                 command="Browser:ShowAllBookmarks">
+          <menupopup id="appmenu_bookmarksPopup"
+                     placespopup="true"
+                     context="placesContext"
+                     openInTabs="children"
+                     oncommand="BookmarksEventHandler.onCommand(event, this.parentNode._placesView);"
+                     onclick="BookmarksEventHandler.onClick(event, this.parentNode._placesView);"
+                     onpopupshowing="BookmarksMenuButton.onPopupShowing(event);
+                                     if (!this.parentNode._placesView)
+                                       new PlacesMenu(event, 'place:folder=BOOKMARKS_MENU');"
+                     tooltip="bhTooltip"
+                     popupsinherittooltip="true">
+            <menuitem id="appmenu_showAllBookmarks"
+                      label="&showAllBookmarks2.label;"
+                      command="Browser:ShowAllBookmarks"
+                      context=""
+                      key="manBookmarkKb"/>
+            <menuseparator/>
+            <menuitem id="appmenu_bookmarkThisPage"
+                      class="menuitem-iconic"
+                      label="&bookmarkThisPageCmd.label;"
+                      command="Browser:AddBookmarkAs"
+                      key="addBookmarkAsKb"/>
+            <menuitem id="appmenu_subscribeToPage"
+                      class="menuitem-iconic"
+                      label="&subscribeToPageMenuitem.label;"
+                      oncommand="return FeedHandler.subscribeToFeed(null, event);"
+                      onclick="checkForMiddleClick(this, event);"
+                      observes="singleFeedMenuitemState"/>
+            <menu id="appmenu_subscribeToPageMenu"
+                  class="menu-iconic"
+                  label="&subscribeToPageMenupopup.label;"
+                  observes="multipleFeedsMenuState">
+              <menupopup id="appmenu_subscribeToPageMenupopup"
+                         onpopupshowing="return FeedHandler.buildFeedList(event.target);"
+                         oncommand="return FeedHandler.subscribeToFeed(null, event);"
+                         onclick="checkForMiddleClick(this, event);"/>
+            </menu>
+            <menuseparator/>
+            <menu id="appmenu_bookmarksToolbar"
+                  placesanonid="toolbar-autohide"
+                  class="menu-iconic bookmark-item"
+                  label="&personalbarCmd.label;"
+                  container="true">
+              <menupopup id="appmenu_bookmarksToolbarPopup"
+                         placespopup="true"
+                         context="placesContext"
+                         onpopupshowing="if (!this.parentNode._placesView)
+                                           new PlacesMenu(event, 'place:folder=TOOLBAR');"/>
+            </menu>
+            <menuseparator/>
+            <!-- Bookmarks menu items -->
+            <menuseparator builder="end"
+                           class="hide-if-empty-places-result"/>
+            <menuitem id="appmenu_unsortedBookmarks"
+                      label="&appMenuUnsorted.label;"
+                      oncommand="PlacesCommandHook.showPlacesOrganizer('UnfiledBookmarks');"
+                      class="menuitem-iconic"/>
+          </menupopup>
+      </splitmenu>
+      <splitmenu id="appmenu_history"
+                 iconic="true"
+                 label="&historyMenu.label;"
+                 command="Browser:ShowAllHistory">
+          <menupopup id="appmenu_historyMenupopup"
+                     placespopup="true"
+                     oncommand="this.parentNode._placesView._onCommand(event);"
+                     onclick="checkForMiddleClick(this, event);"
+                     onpopupshowing="if (!this.parentNode._placesView)
+                                       new HistoryMenu(event);"
+                     tooltip="bhTooltip"
+                     popupsinherittooltip="true">
+            <menuitem id="appmenu_showAllHistory"
+                      label="&showAllHistoryCmd2.label;"
+                      command="Browser:ShowAllHistory"
+                      key="showAllHistoryKb"/>
+            <menuseparator/>
+            <menuitem id="appmenu_sanitizeHistory"
+                      label="&clearRecentHistory.label;"
+                      key="key_sanitize"
+                      command="Tools:Sanitize"/>
+            <menuseparator class="hide-if-empty-places-result"/>
+            <menuitem id="appmenu_sync-tabs"
+                      class="syncTabsMenuItem"
+                      label="&syncTabsMenu.label;"
+                      oncommand="BrowserOpenSyncTabs();"
+                      disabled="true"/>
+            <menuitem id="appmenu_restoreLastSession"
+                      class="restoreLastSession"
+                      label="&historyRestoreLastSession.label;"
+                      oncommand="restoreLastSession();"
+                      disabled="true"/>
+            <menu id="appmenu_recentlyClosedTabsMenu"
+                  class="recentlyClosedTabsMenu"
+                  label="&historyUndoMenu.label;"
+                  disabled="true">
+              <menupopup id="appmenu_recentlyClosedTabsMenupopup"
+                         onpopupshowing="document.getElementById('appmenu_history')._placesView.populateUndoSubmenu();"/>
+            </menu>
+            <menu id="appmenu_recentlyClosedWindowsMenu"
+                  class="recentlyClosedWindowsMenu"
+                  label="&historyUndoWindowMenu.label;"
+                  disabled="true">
+              <menupopup id="appmenu_recentlyClosedWindowsMenupopup"
+                         onpopupshowing="document.getElementById('appmenu_history')._placesView.populateUndoWindowSubmenu();"/>
+            </menu>
+            <menuseparator/>
+          </menupopup>
+      </splitmenu>
+      <menuitem id="appmenu_downloads"
+                class="menuitem-tooltip"
+                label="&downloads.label;"
+                command="Tools:Downloads"
+                key="key_openDownloads"/>
+      <spacer id="appmenuSecondaryPane-spacer"/>
+      <menuitem id="appmenu_addons"
+                class="menuitem-iconic menuitem-iconic-tooltip"
+                label="&addons.label;"
+                command="Tools:Addons"
+                key="key_openAddons"/>
+      <splitmenu id="appmenu_customize"
+                 label="&preferencesCmdUnix.label;"
+                 oncommand="openPreferences();">
+          <menupopup id="appmenu_customizeMenu"
+                     onpopupshowing="onViewToolbarsPopupShowing(event, document.getElementById('appmenu_toggleToolbarsSeparator'));">
+            <menuitem id="appmenu_preferences"
+                      label="&preferencesCmdUnix.label;"
+                      oncommand="openPreferences();"/>
+            <menuseparator/>
+            <menuseparator id="appmenu_toggleToolbarsSeparator"/>
+            <menuitem id="appmenu_toggleTabsOnTop"
+                      label="&viewTabsOnTop.label;"
+                      type="checkbox"
+                      command="cmd_ToggleTabsOnTop"/>
+            <menuitem id="appmenu_toolbarLayout"
+                      label="&appMenuToolbarLayout.label;"
+                      command="cmd_CustomizeToolbars"/>
+          </menupopup>
+      </splitmenu>
+      <splitmenu id="appmenu_help"
+                 label="&helpMenu.label;"
+                 oncommand="openHelpLink('firefox-help')">
+          <menupopup id="appmenu_helpMenupopup">
+            <menuitem id="appmenu_openHelp"
+                      label="&helpMenu.label;"
+                      oncommand="openHelpLink('firefox-help')"
+                      onclick="checkForMiddleClick(this, event);"/>
+            <menuitem id="appmenu_gettingStarted"
+                      label="&appMenuGettingStarted.label;"
+                      oncommand="gBrowser.loadOneTab('http://www.mozilla.com/firefox/central/', {inBackground: false});"
+                      onclick="checkForMiddleClick(this, event);"/>
+            <menuitem id="appmenu_troubleshootingInfo"
+                      label="&helpTroubleshootingInfo.label;"
+                      oncommand="openTroubleshootingPage()"
+                      onclick="checkForMiddleClick(this,event);"/>
+            <menuitem id="appmenu_feedbackPage"
+                      label="&helpFeedbackPage.label;"
+                      oncommand="openFeedbackPage()"
+                      onclick="checkForMiddleClick(this, event);"/>
+            <menuseparator/>
+            <menuitem id="appmenu_safeMode"
+                      label="&appMenuSafeMode.label;"
+                      oncommand="safeModeRestart();"/>
+            <menuseparator/>
+            <menuitem id="appmenu_about"
+                      label="&aboutProduct.label;"
+                      oncommand="openAboutDialog();"/>
+          </menupopup>
+      </splitmenu>
+    </vbox>
+  </hbox>
+</menupopup>
+      </toolbarbutton>
+
+      <tabs id="tabbrowser-tabs"
+            class="tabbrowser-tabs"
+            tabbrowser="content"
+            flex="1"
+            setfocus="false"
+            tooltip="tabbrowser-tab-tooltip">
+        <tab class="tabbrowser-tab" selected="true" fadein="true"/>
+      </tabs>
+
+      <toolbarbutton id="new-tab-button"
+                     class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     label="&tabCmd.label;"
+                     command="cmd_newNavigatorTab"
+                     onclick="checkForMiddleClick(this, event);"
+                     tooltiptext="&newTabButton.tooltip;"
+                     ondrop="newTabButtonObserver.onDrop(event)"
+                     ondragover="newTabButtonObserver.onDragOver(event)"
+                     ondragenter="newTabButtonObserver.onDragOver(event)"
+                     ondragexit="newTabButtonObserver.onDragExit(event)"
+                     removable="true"/>
+
+      <toolbarbutton id="alltabs-button"
+                     class="toolbarbutton-1 chromeclass-toolbar-additional tabs-alltabs-button"
+                     type="menu"
+                     label="&listAllTabs.label;"
+                     tooltiptext="&listAllTabs.label;"
+                     removable="true">
+        <menupopup id="alltabs-popup"
+                   position="after_end">
+          <menuitem id="menu_tabview"
+                    class="menuitem-iconic"
+                    key="key_tabview"
+                    label="&viewTabGroups.label;"
+                    command="Browser:ToggleTabView"
+                    observes="tabviewGroupsNumber"/>
+          <menuseparator id="alltabs-popup-separator"/>
+        </menupopup>
+      </toolbarbutton>
+
+      <toolbarbutton id="tabs-closebutton"
+                     class="close-button tabs-closebutton"
+                     command="cmd_close"
+                     label="&closeTab.label;"
+                     tooltiptext="&closeTab.label;"/>
+
+    </toolbar>
+
+    <toolbarpalette id="BrowserToolbarPalette">
+
+
+      <toolbarbutton id="print-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     label="&printButton.label;" command="cmd_print"
+                     tooltiptext="&printButton.tooltip;"/>
+
+      <!-- This is a placeholder for the Downloads Indicator.  It is visible
+           only during the customization of the toolbar or in the palette, and
+           is replaced when customization is done. -->
+      <toolbarbutton id="downloads-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     observes="Tools:Downloads"
+                     ondrop="DownloadsButtonDNDObserver.onDrop(event)"
+                     ondragover="DownloadsButtonDNDObserver.onDragOver(event)"
+                     ondragenter="DownloadsButtonDNDObserver.onDragOver(event)"
+                     ondragexit="DownloadsButtonDNDObserver.onDragExit(event)"
+                     label="&downloads.label;"
+                     tooltiptext="&downloads.tooltip;"/>
+
+      <toolbarbutton id="history-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     observes="viewHistorySidebar" label="&historyButton.label;"
+                     tooltiptext="&historyButton.tooltip;"/>
+
+      <toolbarbutton id="bookmarks-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     observes="viewBookmarksSidebar"
+                     tooltiptext="&bookmarksButton.tooltip;"
+                     ondrop="bookmarksButtonObserver.onDrop(event)"
+                     ondragover="bookmarksButtonObserver.onDragOver(event)"
+                     ondragenter="bookmarksButtonObserver.onDragOver(event)"
+                     ondragexit="bookmarksButtonObserver.onDragExit(event)"/>
+
+      <toolbarbutton id="new-window-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     label="&newNavigatorCmd.label;"
+                     command="key_newNavigator"
+                     tooltiptext="&newWindowButton.tooltip;"
+                     ondrop="newWindowButtonObserver.onDrop(event)"
+                     ondragover="newWindowButtonObserver.onDragOver(event)"
+                     ondragenter="newWindowButtonObserver.onDragOver(event)"
+                     ondragexit="newWindowButtonObserver.onDragExit(event)"/>
+
+      <toolbarbutton id="fullscreen-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     observes="View:FullScreen"
+                     type="checkbox"
+                     label="&fullScreenCmd.label;"
+                     tooltiptext="&fullScreenButton.tooltip;"/>
+
+      <toolbaritem id="zoom-controls" class="chromeclass-toolbar-additional"
+                   title="&zoomControls.label;">
+        <toolbarbutton id="zoom-out-button" class="toolbarbutton-1"
+                       label="&fullZoomReduceCmd.label;"
+                       command="cmd_fullZoomReduce"
+                       tooltiptext="&zoomOutButton.tooltip;"/>
+        <toolbarbutton id="zoom-in-button" class="toolbarbutton-1"
+                       label="&fullZoomEnlargeCmd.label;"
+                       command="cmd_fullZoomEnlarge"
+                       tooltiptext="&zoomInButton.tooltip;"/>
+      </toolbaritem>
+
+      <toolbarbutton id="feed-button"
+                     type="menu"
+                     class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     disabled="true"
+                     label="&feedButton.label;"
+                     tooltiptext="&feedButton.tooltip;"
+                     onclick="return FeedHandler.onFeedButtonClick(event);">
+        <menupopup position="after_end"
+                   id="feed-menu"
+                   onpopupshowing="return FeedHandler.buildFeedList(this);"
+                   oncommand="return FeedHandler.subscribeToFeed(null, event);"
+                   onclick="checkForMiddleClick(this, event);"/>
+      </toolbarbutton>
+
+      <toolbarbutton id="cut-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     label="&cutCmd.label;"
+                     command="cmd_cut"
+                     tooltiptext="&cutButton.tooltip;"/>
+
+      <toolbarbutton id="copy-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     label="&copyCmd.label;"
+                     command="cmd_copy"
+                     tooltiptext="&copyButton.tooltip;"/>
+
+      <toolbarbutton id="paste-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     label="&pasteCmd.label;"
+                     command="cmd_paste"
+                     tooltiptext="&pasteButton.tooltip;"/>
+
+      <toolbarbutton id="sync-button"
+                     class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     label="&syncToolbarButton.label;"
+                     oncommand="gSyncUI.handleToolbarButton()"/>
+
+      <toolbaritem id="navigator-throbber" title="&throbberItem.title;" align="center" pack="center"
+                   mousethrough="always">
+        <image/>
+      </toolbaritem>
+
+      <toolbarbutton id="tabview-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
+                     label="&tabGroupsButton.label;"
+                     command="Browser:ToggleTabView"
+                     tooltiptext="&tabGroupsButton.tooltip;"
+                     observes="tabviewGroupsNumber"/>
+    </toolbarpalette>
+  </toolbox>
+
+  <hbox flex="1" id="browser">
+    <vbox id="browser-border-start" hidden="true" layer="true"/>
+    <vbox id="sidebar-box" hidden="true" class="chromeclass-extrachrome">
+      <sidebarheader id="sidebar-header" align="center">
+        <label id="sidebar-title" persist="value" flex="1" crop="end" control="sidebar"/>
+        <image id="sidebar-throbber"/>
+        <toolbarbutton class="tabs-closebutton" tooltiptext="&sidebarCloseButton.tooltip;" oncommand="toggleSidebar();"/>
+      </sidebarheader>
+      <browser id="sidebar" flex="1" autoscroll="false" disablehistory="true"
+                style="min-width: 14em; width: 18em; max-width: 36em;"/>
+    </vbox>
+
+    <splitter id="sidebar-splitter" class="chromeclass-extrachrome" hidden="true"/>
+    <vbox id="appcontent" flex="1">
+      <tabbrowser id="content" disablehistory="true"
+                  flex="1" contenttooltip="aHTMLTooltip"
+                  tabcontainer="tabbrowser-tabs"
+                  contentcontextmenu="contentAreaContextMenu"
+                  autocompletepopup="PopupAutoComplete"
+                  onclick="contentAreaClick(event, false);"/>
+      <statuspanel id="statusbar-display" inactive="true"/>
+    </vbox>
+    <splitter id="devtools-side-splitter" hidden="true"/>
+    <vbox id="devtools-sidebar-box" hidden="true"
+          style="min-width: 18em; width: 22em; max-width: 42em;" persist="width">
+      <toolbar id="devtools-sidebar-toolbar"
+               class="devtools-toolbar"
+               nowindowdrag="true"/>
+      <deck id="devtools-sidebar-deck" flex="1"/>
+    </vbox>
+    <vbox id="browser-border-end" hidden="true" layer="true"/>
+  </hbox>
+
+  <hbox id="full-screen-warning-container" hidden="true" fadeout="true">
+    <hbox style="width: 100%;" pack="center"> <!-- Inner hbox needed due to bug 579776. -->
+      <vbox id="full-screen-warning-message" align="center">
+        <description id="full-screen-domain-text"/>
+        <description class="full-screen-description" value="&fullscreenExitHint.value;"/>
+        <vbox id="full-screen-approval-pane" align="center">
+          <description class="full-screen-description" value="&fullscreenApproval.value;"/>
+          <hbox>
+            <button label="&fullscreenAllowButton.label;"
+                    oncommand="FullScreen.setFullscreenAllowed(true);"
+                    class="full-screen-approval-button"/>
+            <button label="&fullscreenExitButton.label;"
+                    oncommand="FullScreen.setFullscreenAllowed(false);"
+                    class="full-screen-approval-button"/>
+          </hbox>
+          <checkbox id="full-screen-remember-decision"/>
+        </vbox>
+      </vbox>
+    </hbox>
+  </hbox>
+
+  <vbox id="browser-bottombox" layer="true">
+    <toolbar id="inspector-toolbar"
+             class="devtools-toolbar"
+             nowindowdrag="true"
+             hidden="true">
+      <toolbarbutton id="inspector-option-toolbarbutton"
+                     type="menu"
+                     tabindex="0"
+                     tooltiptext="&inspectOptionButton.tooltiptext;">
+        <menupopup id="inspector-option-popup"
+                   position="before_start">
+          <menuitem id="inspectorToggleVeil"
+                    type="checkbox"
+                    label="&inspectorToggleVeil.label;"
+                    accesskey="&inspectorToggleVeil.accesskey;"
+                    command="Inspector:ToggleVeil"/>
+          <menuitem id="inspectorToggleInfobar"
+                    type="checkbox"
+                    label="&inspectorToggleInfobar.label;"
+                    accesskey="&inspectorToggleInfobar.accesskey;"
+                    command="Inspector:ToggleInfobar"/>
+        </menupopup>
+      </toolbarbutton>
+      <toolbarbutton id="inspector-inspect-toolbutton"
+                     class="devtools-toolbarbutton"
+                     command="Inspector:Inspect"/>
+      <toolbarbutton id="inspector-treepanel-toolbutton"
+                     class="devtools-toolbarbutton"
+                     tabindex="0"
+                     aria-label="&markupButton.arialabel;"
+                     accesskey="&markupButton.accesskey;"
+                     command="Inspector:HTMLPanel"/>
+      <arrowscrollbox id="inspector-breadcrumbs"
+                      flex="1" orient="horizontal"
+                      clicktoscroll="true"/>
+      <hbox id="inspector-tools">
+        <toolbarbutton id="inspector-3D-button"
+                       class="devtools-toolbarbutton"
+                       hidden="true"
+                       label="&inspect3DViewButton.label;"
+                       accesskey="&inspect3DViewButton.accesskey;"
+                       tabindex="0"
+                       command="Inspector:Tilt"/>
+        <toolbarbutton id="inspector-style-button"
+                       class="devtools-toolbarbutton"
+                       label="&inspectStyleButton.label;"
+                       accesskey="&inspectStyleButton.accesskey;"
+                       tabindex="0"
+                       command="Inspector:Sidebar"/>
+        <!-- registered tools go here -->
+      </hbox>
+      <toolbarbutton id="highlighter-closebutton"
+                     class="devtools-closebutton"
+                     oncommand="InspectorUI.closeInspectorUI(false);"
+                     tooltiptext="&inspectCloseButton.tooltiptext;"/>
+    </toolbar>
+
+    <toolbar id="developer-toolbar"
+             class="devtools-toolbar"
+             hidden="true">
+          <stack class="gclitoolbar-stack-node" flex="1">
+            <hbox class="gclitoolbar-prompt">
+              <label class="gclitoolbar-prompt-label">&#187;</label>
+            </hbox>
+            <hbox class="gclitoolbar-complete-node"/>
+            <textbox class="gclitoolbar-input-node" rows="1"/>
+          </stack>
+          <toolbarbutton id="developer-toolbar-webconsole"
+                         label="&webConsoleButton.label;"
+                         class="devtools-toolbarbutton"
+                         command="Tools:WebConsole"/>
+          <toolbarbutton id="developer-toolbar-inspector"
+                         label="&inspectorButton.label;"
+                         class="devtools-toolbarbutton"
+                         hidden="true"
+                         command="Tools:Inspect"/>
+          <toolbarbutton id="developer-toolbar-responsiveui"
+                         label="&responsiveDesignTool.label;"
+                         class="devtools-toolbarbutton"
+                         hidden="true"
+                         command="Tools:ResponsiveUI"/>
+          <toolbarbutton id="developer-toolbar-debugger"
+                         label="&scriptsButton.label;"
+                         class="devtools-toolbarbutton"
+                         hidden="true"
+                         command="Tools:Debugger"/>
+          <toolbarbutton id="developer-toolbar-closebutton"
+                         class="devtools-closebutton"
+                         oncommand="DeveloperToolbar.hide();"
+                         tooltiptext="&devToolbarCloseButton.tooltiptext;"/>
+   </toolbar>
+
+    <toolbar id="addon-bar"
+             toolbarname="&addonBarCmd.label;" accesskey="&addonBarCmd.accesskey;"
+             collapsed="true"
+             class="toolbar-primary chromeclass-toolbar"
+             context="toolbar-context-menu" toolboxid="navigator-toolbox"
+             mode="icons" iconsize="small" defaulticonsize="small"
+             lockiconsize="true"
+             defaultset="addonbar-closebutton,spring,status-bar"
+             customizable="true"
+             key="key_toggleAddonBar">
+      <toolbarbutton id="addonbar-closebutton"
+                     tooltiptext="&addonBarCloseButton.tooltip;"
+                     oncommand="setToolbarVisibility(this.parentNode, false);"/>
+      <statusbar id="status-bar" ordinal="1000"/>
+    </toolbar>
+  </vbox>
+
+
+</vbox>
+</deck>
+
+</window>
diff -Nuar omni_old/chrome/browser/content/browser/tabview.js omni/chrome/browser/content/browser/tabview.js
--- omni_old/chrome/browser/content/browser/tabview.js	2010-01-01 00:00:00.000000000 +0800
+++ omni/chrome/browser/content/browser/tabview.js	2012-08-30 14:42:50.583879910 +0800
@@ -1186,7 +1186,7 @@
         drag.info.stop();
 
         if (!this.isAGroupItem && !this.parent) {
-          new GroupItem([drag.info.$el], {focusTitle: true});
+          new GroupItem([drag.info.$el]);
           gTabView.firstUseExperienced = true;
         }
 
@@ -3804,40 +3804,8 @@
     let self = this;
     let lastMouseDownTarget;
 
-    container.mousedown(function(e) {
-      let target = e.target;
-      // only set the last mouse down target if it is a left click, not on the
-      // close button, not on the expand button, not on the title bar and its
-      // elements
-      if (Utils.isLeftClick(e) &&
-          self.$closeButton[0] != target &&
-          self.$titlebar[0] != target &&
-          self.$expander[0] != target &&
-          !self.$titlebar.contains(target) &&
-          !self.$appTabTray.contains(target)) {
-        lastMouseDownTarget = target;
-      } else {
-        lastMouseDownTarget = null;
-      }
-    });
-    container.mouseup(function(e) {
-      let same = (e.target == lastMouseDownTarget);
-      lastMouseDownTarget = null;
-
-      if (same && !self.isDragging) {
-        if (gBrowser.selectedTab.pinned &&
-            UI.getActiveTab() != self.getActiveTab() &&
-            self.getChildren().length > 0) {
-          UI.setActive(self, { dontSetActiveTabInGroup: true });
-          UI.goToTab(gBrowser.selectedTab);
-        } else {
-          let tabItem = self.getTopChild();
-          if (tabItem)
-            tabItem.zoomIn();
-          else
-            self.newTab();
-        }
-      }
+    container.dblclick(function(e) {
+        self.newTab();
     });
 
     let dropIndex = false;
@@ -9309,7 +9277,7 @@
       let box = item.getBounds();
       if (box.width > minMinSize && box.height > minMinSize &&
          (box.width > minSize || box.height > minSize)) {
-        let opts = {bounds: item.getBounds(), focusTitle: true};
+        let opts = {bounds: item.getBounds()};
         let groupItem = new GroupItem([], opts);
         self.setActive(groupItem);
         phantom.remove();
diff -Nuar omni_old/chrome/browser/content/browser/tabview.js.orig omni/chrome/browser/content/browser/tabview.js.orig
--- omni_old/chrome/browser/content/browser/tabview.js.orig	1970-01-01 08:00:00.000000000 +0800
+++ omni/chrome/browser/content/browser/tabview.js.orig	2010-01-01 00:00:00.000000000 +0800
@@ -0,0 +1,9608 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+"use strict";
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cu = Components.utils;
+const Cr = Components.results;
+
+Cu.import("resource:///modules/tabview/utils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyGetter(this, "tabviewBundle", function() {
+  return Services.strings.
+    createBundle("chrome://browser/locale/tabview.properties");
+});
+XPCOMUtils.defineLazyGetter(this, "tabbrowserBundle", function() {
+  return Services.strings.
+    createBundle("chrome://browser/locale/tabbrowser.properties");
+});
+
+function tabviewString(name) tabviewBundle.GetStringFromName('tabview.' + name);
+function tabbrowserString(name) tabbrowserBundle.GetStringFromName(name);
+
+XPCOMUtils.defineLazyGetter(this, "gPrefBranch", function() {
+  return Services.prefs.getBranch("browser.panorama.");
+});
+
+XPCOMUtils.defineLazyServiceGetter(this, "gPrivateBrowsing",
+  "@mozilla.org/privatebrowsing;1", "nsIPrivateBrowsingService");
+
+XPCOMUtils.defineLazyModuleGetter(this, "gPageThumbnails",
+  "resource:///modules/PageThumbs.jsm", "PageThumbs");
+
+var gWindow = window.parent;
+var gBrowser = gWindow.gBrowser;
+var gTabView = gWindow.TabView;
+var gTabViewDeck = gWindow.document.getElementById("tab-view-deck");
+var gBrowserPanel = gWindow.document.getElementById("browser-panel");
+var gTabViewFrame = gWindow.document.getElementById("tab-view");
+
+let AllTabs = {
+  _events: {
+    attrModified: "TabAttrModified",
+    close:        "TabClose",
+    move:         "TabMove",
+    open:         "TabOpen",
+    select:       "TabSelect",
+    pinned:       "TabPinned",
+    unpinned:     "TabUnpinned"
+  },
+
+  get tabs() {
+    return Array.filter(gBrowser.tabs, function (tab) Utils.isValidXULTab(tab));
+  },
+
+  register: function AllTabs_register(eventName, callback) {
+    gBrowser.tabContainer.addEventListener(this._events[eventName], callback, false);
+  },
+
+  unregister: function AllTabs_unregister(eventName, callback) {
+    gBrowser.tabContainer.removeEventListener(this._events[eventName], callback, false);
+  }
+};
+
+//@line 70 "/build/src/mozilla-release/browser/components/tabview/tabview.js"
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// **********
+// Title: iq.js
+// Various helper functions, in the vein of jQuery.
+
+// ----------
+// Function: iQ
+// Returns an iQClass object which represents an individual element or a group
+// of elements. It works pretty much like jQuery(), with a few exceptions,
+// most notably that you can't use strings with complex html,
+// just simple tags like '<div>'.
+function iQ(selector, context) {
+  // The iQ object is actually just the init constructor 'enhanced'
+  return new iQClass(selector, context);
+};
+
+// A simple way to check for HTML strings or ID strings
+// (both of which we optimize for)
+let quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/;
+
+// Match a standalone tag
+let rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/;
+
+// ##########
+// Class: iQClass
+// The actual class of iQ result objects, representing an individual element
+// or a group of elements.
+//
+// ----------
+// Function: iQClass
+// You don't call this directly; this is what's called by iQ().
+function iQClass(selector, context) {
+
+  // Handle $(""), $(null), or $(undefined)
+  if (!selector) {
+    return this;
+  }
+
+  // Handle $(DOMElement)
+  if (selector.nodeType) {
+    this.context = selector;
+    this[0] = selector;
+    this.length = 1;
+    return this;
+  }
+
+  // The body element only exists once, optimize finding it
+  if (selector === "body" && !context) {
+    this.context = document;
+    this[0] = document.body;
+    this.selector = "body";
+    this.length = 1;
+    return this;
+  }
+
+  // Handle HTML strings
+  if (typeof selector === "string") {
+    // Are we dealing with HTML string or an ID?
+
+    let match = quickExpr.exec(selector);
+
+    // Verify a match, and that no context was specified for #id
+    if (match && (match[1] || !context)) {
+
+      // HANDLE $(html) -> $(array)
+      if (match[1]) {
+        let doc = (context ? context.ownerDocument || context : document);
+
+        // If a single string is passed in and it's a single tag
+        // just do a createElement and skip the rest
+        let ret = rsingleTag.exec(selector);
+
+        if (ret) {
+          if (Utils.isPlainObject(context)) {
+            Utils.assert(false, 'does not support HTML creation with context');
+          } else {
+            selector = [doc.createElement(ret[1])];
+          }
+
+        } else {
+          Utils.assert(false, 'does not support complex HTML creation');
+        }
+
+        return Utils.merge(this, selector);
+
+      // HANDLE $("#id")
+      } else {
+        let elem = document.getElementById(match[2]);
+
+        if (elem) {
+          this.length = 1;
+          this[0] = elem;
+        }
+
+        this.context = document;
+        this.selector = selector;
+        return this;
+      }
+
+    // HANDLE $("TAG")
+    } else if (!context && /^\w+$/.test(selector)) {
+      this.selector = selector;
+      this.context = document;
+      selector = document.getElementsByTagName(selector);
+      return Utils.merge(this, selector);
+
+    // HANDLE $(expr, $(...))
+    } else if (!context || context.iq) {
+      return (context || iQ(document)).find(selector);
+
+    // HANDLE $(expr, context)
+    // (which is just equivalent to: $(context).find(expr)
+    } else {
+      return iQ(context).find(selector);
+    }
+
+  // HANDLE $(function)
+  // Shortcut for document ready
+  } else if (typeof selector == "function") {
+    Utils.log('iQ does not support ready functions');
+    return null;
+  }
+
+  if ("selector" in selector) {
+    this.selector = selector.selector;
+    this.context = selector.context;
+  }
+
+  let ret = this || [];
+  if (selector != null) {
+    // The window, strings (and functions) also have 'length'
+    if (selector.length == null || typeof selector == "string" || selector.setInterval) {
+      Array.push(ret, selector);
+    } else {
+      Utils.merge(ret, selector);
+    }
+  }
+  return ret;
+};
+  
+iQClass.prototype = {
+
+  // ----------
+  // Function: toString
+  // Prints [iQ...] for debug use
+  toString: function iQClass_toString() {
+    if (this.length > 1) {
+      if (this.selector)
+        return "[iQ (" + this.selector + ")]";
+      else
+        return "[iQ multi-object]";
+    }
+
+    if (this.length == 1)
+      return "[iQ (" + this[0].toString() + ")]";
+
+    return "[iQ non-object]";
+  },
+
+  // Start with an empty selector
+  selector: "",
+
+  // The default length of a iQ object is 0
+  length: 0,
+
+  // ----------
+  // Function: each
+  // Execute a callback for every element in the matched set.
+  each: function iQClass_each(callback) {
+    if (typeof callback != "function") {
+      Utils.assert(false, "each's argument must be a function");
+      return null;
+    }
+    for (let i = 0; this[i] != null && callback(this[i]) !== false; i++) {}
+    return this;
+  },
+
+  // ----------
+  // Function: addClass
+  // Adds the given class(es) to the receiver.
+  addClass: function iQClass_addClass(value) {
+    Utils.assertThrow(typeof value == "string" && value,
+                      'requires a valid string argument');
+
+    let length = this.length;
+    for (let i = 0; i < length; i++) {
+      let elem = this[i];
+      if (elem.nodeType === 1) {
+        value.split(/\s+/).forEach(function(className) {
+          elem.classList.add(className);
+        });
+      }
+    }
+
+    return this;
+  },
+
+  // ----------
+  // Function: removeClass
+  // Removes the given class(es) from the receiver.
+  removeClass: function iQClass_removeClass(value) {
+    if (typeof value != "string" || !value) {
+      Utils.assert(false, 'does not support function argument');
+      return null;
+    }
+
+    let length = this.length;
+    for (let i = 0; i < length; i++) {
+      let elem = this[i];
+      if (elem.nodeType === 1 && elem.className) {
+        value.split(/\s+/).forEach(function(className) {
+          elem.classList.remove(className);
+        });
+      }
+    }
+
+    return this;
+  },
+
+  // ----------
+  // Function: hasClass
+  // Returns true is the receiver has the given css class.
+  hasClass: function iQClass_hasClass(singleClassName) {
+    let length = this.length;
+    for (let i = 0; i < length; i++) {
+      if (this[i].classList.contains(singleClassName)) {
+        return true;
+      }
+    }
+    return false;
+  },
+
+  // ----------
+  // Function: find
+  // Searches the receiver and its children, returning a new iQ object with
+  // elements that match the given selector.
+  find: function iQClass_find(selector) {
+    let ret = [];
+    let length = 0;
+
+    let l = this.length;
+    for (let i = 0; i < l; i++) {
+      length = ret.length;
+      try {
+        Utils.merge(ret, this[i].querySelectorAll(selector));
+      } catch(e) {
+        Utils.log('iQ.find error (bad selector)', e);
+      }
+
+      if (i > 0) {
+        // Make sure that the results are unique
+        for (let n = length; n < ret.length; n++) {
+          for (let r = 0; r < length; r++) {
+            if (ret[r] === ret[n]) {
+              ret.splice(n--, 1);
+              break;
+            }
+          }
+        }
+      }
+    }
+
+    return iQ(ret);
+  },
+
+  // ----------
+  // Function: contains
+  // Check to see if a given DOM node descends from the receiver.
+  contains: function iQClass_contains(selector) {
+    Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
+
+    // fast path when querySelector() can be used
+    if ('string' == typeof selector)
+      return null != this[0].querySelector(selector);
+
+    let object = iQ(selector);
+    Utils.assert(object.length <= 1, 'does not yet support multi-objects');
+
+    let elem = object[0];
+    if (!elem || !elem.parentNode)
+      return false;
+
+    do {
+      elem = elem.parentNode;
+    } while (elem && this[0] != elem);
+
+    return this[0] == elem;
+  },
+
+  // ----------
+  // Function: remove
+  // Removes the receiver from the DOM.
+  remove: function iQClass_remove(options) {
+    if (!options || !options.preserveEventHandlers)
+      this.unbindAll();
+    for (let i = 0; this[i] != null; i++) {
+      let elem = this[i];
+      if (elem.parentNode) {
+        elem.parentNode.removeChild(elem);
+      }
+    }
+    return this;
+  },
+
+  // ----------
+  // Function: empty
+  // Removes all of the reciever's children and HTML content from the DOM.
+  empty: function iQClass_empty() {
+    for (let i = 0; this[i] != null; i++) {
+      let elem = this[i];
+      while (elem.firstChild) {
+        iQ(elem.firstChild).unbindAll();
+        elem.removeChild(elem.firstChild);
+      }
+    }
+    return this;
+  },
+
+  // ----------
+  // Function: width
+  // Returns the width of the receiver, including padding and border.
+  width: function iQClass_width() {
+    return Math.floor(this[0].offsetWidth);
+  },
+
+  // ----------
+  // Function: height
+  // Returns the height of the receiver, including padding and border.
+  height: function iQClass_height() {
+    return Math.floor(this[0].offsetHeight);
+  },
+
+  // ----------
+  // Function: position
+  // Returns an object with the receiver's position in left and top
+  // properties.
+  position: function iQClass_position() {
+    let bounds = this.bounds();
+    return new Point(bounds.left, bounds.top);
+  },
+
+  // ----------
+  // Function: bounds
+  // Returns a <Rect> with the receiver's bounds.
+  bounds: function iQClass_bounds() {
+    Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
+    let rect = this[0].getBoundingClientRect();
+    return new Rect(Math.floor(rect.left), Math.floor(rect.top),
+                    Math.floor(rect.width), Math.floor(rect.height));
+  },
+
+  // ----------
+  // Function: data
+  // Pass in both key and value to attach some data to the receiver;
+  // pass in just key to retrieve it.
+  data: function iQClass_data(key, value) {
+    let data = null;
+    if (value === undefined) {
+      Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
+      data = this[0].iQData;
+      if (data)
+        return data[key];
+      else
+        return null;
+    }
+
+    for (let i = 0; this[i] != null; i++) {
+      let elem = this[i];
+      data = elem.iQData;
+
+      if (!data)
+        data = elem.iQData = {};
+
+      data[key] = value;
+    }
+
+    return this;
+  },
+
+  // ----------
+  // Function: html
+  // Given a value, sets the receiver's innerHTML to it; otherwise returns
+  // what's already there.
+  html: function iQClass_html(value) {
+    Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
+    if (value === undefined)
+      return this[0].innerHTML;
+
+    this[0].innerHTML = value;
+    return this;
+  },
+
+  // ----------
+  // Function: text
+  // Given a value, sets the receiver's textContent to it; otherwise returns
+  // what's already there.
+  text: function iQClass_text(value) {
+    Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
+    if (value === undefined) {
+      return this[0].textContent;
+    }
+
+    return this.empty().append((this[0] && this[0].ownerDocument || document).createTextNode(value));
+  },
+
+  // ----------
+  // Function: val
+  // Given a value, sets the receiver's value to it; otherwise returns what's already there.
+  val: function iQClass_val(value) {
+    Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
+    if (value === undefined) {
+      return this[0].value;
+    }
+
+    this[0].value = value;
+    return this;
+  },
+
+  // ----------
+  // Function: appendTo
+  // Appends the receiver to the result of iQ(selector).
+  appendTo: function iQClass_appendTo(selector) {
+    Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
+    iQ(selector).append(this);
+    return this;
+  },
+
+  // ----------
+  // Function: append
+  // Appends the result of iQ(selector) to the receiver.
+  append: function iQClass_append(selector) {
+    let object = iQ(selector);
+    Utils.assert(object.length == 1 && this.length == 1, 
+        'does not yet support multi-objects (or null objects)');
+    this[0].appendChild(object[0]);
+    return this;
+  },
+
+  // ----------
+  // Function: attr
+  // Sets or gets an attribute on the element(s).
+  attr: function iQClass_attr(key, value) {
+    Utils.assert(typeof key === 'string', 'string key');
+    if (value === undefined) {
+      Utils.assert(this.length == 1, 'retrieval does not support multi-objects (or null objects)');
+      return this[0].getAttribute(key);
+    }
+
+    for (let i = 0; this[i] != null; i++)
+      this[i].setAttribute(key, value);
+
+    return this;
+  },
+
+  // ----------
+  // Function: css
+  // Sets or gets CSS properties on the receiver. When setting certain numerical properties,
+  // will automatically add "px". A property can be removed by setting it to null.
+  //
+  // Possible call patterns:
+  //   a: object, b: undefined - sets with properties from a
+  //   a: string, b: undefined - gets property specified by a
+  //   a: string, b: string/number - sets property specified by a to b
+  css: function iQClass_css(a, b) {
+    let properties = null;
+
+    if (typeof a === 'string') {
+      let key = a;
+      if (b === undefined) {
+        Utils.assert(this.length == 1, 'retrieval does not support multi-objects (or null objects)');
+
+        return window.getComputedStyle(this[0], null).getPropertyValue(key);
+      }
+      properties = {};
+      properties[key] = b;
+    } else if (a instanceof Rect) {
+      properties = {
+        left: a.left,
+        top: a.top,
+        width: a.width,
+        height: a.height
+      };
+    } else {
+      properties = a;
+    }
+
+    let pixels = {
+      'left': true,
+      'top': true,
+      'right': true,
+      'bottom': true,
+      'width': true,
+      'height': true
+    };
+
+    for (let i = 0; this[i] != null; i++) {
+      let elem = this[i];
+      for (let key in properties) {
+        let value = properties[key];
+
+        if (pixels[key] && typeof value != 'string')
+          value += 'px';
+
+        if (value == null) {
+          elem.style.removeProperty(key);
+        } else if (key.indexOf('-') != -1)
+          elem.style.setProperty(key, value, '');
+        else
+          elem.style[key] = value;
+      }
+    }
+
+    return this;
+  },
+
+  // ----------
+  // Function: animate
+  // Uses CSS transitions to animate the element.
+  //
+  // Parameters:
+  //   css - an object map of the CSS properties to change
+  //   options - an object with various properites (see below)
+  //
+  // Possible "options" properties:
+  //   duration - how long to animate, in milliseconds
+  //   easing - easing function to use. Possibilities include
+  //     "tabviewBounce", "easeInQuad". Default is "ease".
+  //   complete - function to call once the animation is done, takes nothing
+  //     in, but "this" is set to the element that was animated.
+  animate: function iQClass_animate(css, options) {
+    Utils.assert(this.length == 1, 'does not yet support multi-objects (or null objects)');
+
+    if (!options)
+      options = {};
+
+    let easings = {
+      tabviewBounce: "cubic-bezier(0.0, 0.63, .6, 1.29)", 
+      easeInQuad: 'ease-in', // TODO: make it a real easeInQuad, or decide we don't care
+      fast: 'cubic-bezier(0.7,0,1,1)'
+    };
+
+    let duration = (options.duration || 400);
+    let easing = (easings[options.easing] || 'ease');
+
+    if (css instanceof Rect) {
+      css = {
+        left: css.left,
+        top: css.top,
+        width: css.width,
+        height: css.height
+      };
+    }
+
+
+    // The latest versions of Firefox do not animate from a non-explicitly
+    // set css properties. So for each element to be animated, go through
+    // and explicitly define 'em.
+    let rupper = /([A-Z])/g;
+    this.each(function(elem) {
+      let cStyle = window.getComputedStyle(elem, null);
+      for (let prop in css) {
+        prop = prop.replace(rupper, "-$1").toLowerCase();
+        iQ(elem).css(prop, cStyle.getPropertyValue(prop));
+      }
+    });
+
+    this.css({
+      '-moz-transition-property': Object.keys(css).join(", "),
+      '-moz-transition-duration': (duration / 1000) + 's',
+      '-moz-transition-timing-function': easing
+    });
+
+    this.css(css);
+
+    let self = this;
+    setTimeout(function() {
+      self.css({
+        '-moz-transition-property': 'none',
+        '-moz-transition-duration': '',
+        '-moz-transition-timing-function': ''
+      });
+
+      if (typeof options.complete == "function")
+        options.complete.apply(self);
+    }, duration);
+
+    return this;
+  },
+
+  // ----------
+  // Function: fadeOut
+  // Animates the receiver to full transparency. Calls callback on completion.
+  fadeOut: function iQClass_fadeOut(callback) {
+    Utils.assert(typeof callback == "function" || callback === undefined, 
+        'does not yet support duration');
+
+    this.animate({
+      opacity: 0
+    }, {
+      duration: 400,
+      complete: function() {
+        iQ(this).css({display: 'none'});
+        if (typeof callback == "function")
+          callback.apply(this);
+      }
+    });
+
+    return this;
+  },
+
+  // ----------
+  // Function: fadeIn
+  // Animates the receiver to full opacity.
+  fadeIn: function iQClass_fadeIn() {
+    this.css({display: ''});
+    this.animate({
+      opacity: 1
+    }, {
+      duration: 400
+    });
+
+    return this;
+  },
+
+  // ----------
+  // Function: hide
+  // Hides the receiver.
+  hide: function iQClass_hide() {
+    this.css({display: 'none', opacity: 0});
+    return this;
+  },
+
+  // ----------
+  // Function: show
+  // Shows the receiver.
+  show: function iQClass_show() {
+    this.css({display: '', opacity: 1});
+    return this;
+  },
+
+  // ----------
+  // Function: bind
+  // Binds the given function to the given event type. Also wraps the function
+  // in a try/catch block that does a Utils.log on any errors.
+  bind: function iQClass_bind(type, func) {
+    let handler = function(event) func.apply(this, [event]);
+
+    for (let i = 0; this[i] != null; i++) {
+      let elem = this[i];
+      if (!elem.iQEventData)
+        elem.iQEventData = {};
+
+      if (!elem.iQEventData[type])
+        elem.iQEventData[type] = [];
+
+      elem.iQEventData[type].push({
+        original: func,
+        modified: handler
+      });
+
+      elem.addEventListener(type, handler, false);
+    }
+
+    return this;
+  },
+
+  // ----------
+  // Function: one
+  // Binds the given function to the given event type, but only for one call;
+  // automatically unbinds after the event fires once.
+  one: function iQClass_one(type, func) {
+    Utils.assert(typeof func == "function", 'does not support eventData argument');
+
+    let handler = function(e) {
+      iQ(this).unbind(type, handler);
+      return func.apply(this, [e]);
+    };
+
+    return this.bind(type, handler);
+  },
+
+  // ----------
+  // Function: unbind
+  // Unbinds the given function from the given event type.
+  unbind: function iQClass_unbind(type, func) {
+    Utils.assert(typeof func == "function", 'Must provide a function');
+
+    for (let i = 0; this[i] != null; i++) {
+      let elem = this[i];
+      let handler = func;
+      if (elem.iQEventData && elem.iQEventData[type]) {
+        let count = elem.iQEventData[type].length;
+        for (let a = 0; a < count; a++) {
+          let pair = elem.iQEventData[type][a];
+          if (pair.original == func) {
+            handler = pair.modified;
+            elem.iQEventData[type].splice(a, 1);
+            if (!elem.iQEventData[type].length) {
+              delete elem.iQEventData[type];
+              if (!Object.keys(elem.iQEventData).length)
+                delete elem.iQEventData;
+            }
+            break;
+          }
+        }
+      }
+
+      elem.removeEventListener(type, handler, false);
+    }
+
+    return this;
+  },
+
+  // ----------
+  // Function: unbindAll
+  // Unbinds all event handlers.
+  unbindAll: function iQClass_unbindAll() {
+    for (let i = 0; this[i] != null; i++) {
+      let elem = this[i];
+
+      for (let j = 0; j < elem.childElementCount; j++)
+        iQ(elem.children[j]).unbindAll();
+
+      if (!elem.iQEventData)
+        continue;
+
+      Object.keys(elem.iQEventData).forEach(function (type) {
+        while (elem.iQEventData && elem.iQEventData[type])
+          this.unbind(type, elem.iQEventData[type][0].original);
+      }, this);
+    }
+
+    return this;
+  }
+};
+
+// ----------
+// Create various event aliases
+let events = [
+  'keyup',
+  'keydown',
+  'keypress',
+  'mouseup',
+  'mousedown',
+  'mouseover',
+  'mouseout',
+  'mousemove',
+  'click',
+  'dblclick',
+  'resize',
+  'change',
+  'blur',
+  'focus'
+];
+
+events.forEach(function(event) {
+  iQClass.prototype[event] = function(func) {
+    return this.bind(event, func);
+  };
+});
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// **********
+// Title: storage.js
+
+// ##########
+// Class: Storage
+// Singleton for permanent storage of TabView data.
+let Storage = {
+  GROUP_DATA_IDENTIFIER: "tabview-group",
+  GROUPS_DATA_IDENTIFIER: "tabview-groups",
+  TAB_DATA_IDENTIFIER: "tabview-tab",
+  UI_DATA_IDENTIFIER: "tabview-ui",
+
+  // ----------
+  // Function: toString
+  // Prints [Storage] for debug use
+  toString: function Storage_toString() {
+    return "[Storage]";
+  },
+
+  // ----------
+  // Function: init
+  // Sets up the object.
+  init: function Storage_init() {
+    this._sessionStore =
+      Cc["@mozilla.org/browser/sessionstore;1"].
+        getService(Ci.nsISessionStore);
+  },
+
+  // ----------
+  // Function: uninit
+  uninit: function Storage_uninit () {
+    this._sessionStore = null;
+  },
+
+  // ----------
+  // Function: wipe
+  // Cleans out all the stored data, leaving empty objects.
+  wipe: function Storage_wipe() {
+    try {
+      var self = this;
+
+      // ___ Tabs
+      AllTabs.tabs.forEach(function(tab) {
+        self.saveTab(tab, null);
+      });
+
+      // ___ Other
+      this.saveGroupItemsData(gWindow, {});
+      this.saveUIData(gWindow, {});
+
+      this._sessionStore.setWindowValue(gWindow, this.GROUP_DATA_IDENTIFIER,
+        JSON.stringify({}));
+    } catch (e) {
+      Utils.log("Error in wipe: "+e);
+    }
+  },
+
+  // ----------
+  // Function: saveTab
+  // Saves the data for a single tab.
+  saveTab: function Storage_saveTab(tab, data) {
+    Utils.assert(tab, "tab");
+
+    this._sessionStore.setTabValue(tab, this.TAB_DATA_IDENTIFIER,
+      JSON.stringify(data));
+  },
+
+  // ----------
+  // Function: getTabData
+  // Load tab data from session store and return it.
+  getTabData: function Storage_getTabData(tab) {
+    Utils.assert(tab, "tab");
+
+    let existingData = null;
+
+    try {
+      let tabData = this._sessionStore.getTabValue(tab, this.TAB_DATA_IDENTIFIER);
+      if (tabData != "")
+        existingData = JSON.parse(tabData);
+    } catch (e) {
+      // getTabValue will fail if the property doesn't exist.
+      Utils.log(e);
+    }
+
+    return existingData;
+  },
+
+  // ----------
+  // Function: getTabState
+  // Returns the current state of the given tab.
+  getTabState: function Storage_getTabState(tab) {
+    Utils.assert(tab, "tab");
+    let tabState;
+
+    try {
+      tabState = JSON.parse(this._sessionStore.getTabState(tab));
+    } catch (e) {}
+
+    return tabState;
+  },
+
+  // ----------
+  // Function: saveGroupItem
+  // Saves the data for a single groupItem, associated with a specific window.
+  saveGroupItem: function Storage_saveGroupItem(win, data) {
+    var id = data.id;
+    var existingData = this.readGroupItemData(win);
+    existingData[id] = data;
+    this._sessionStore.setWindowValue(win, this.GROUP_DATA_IDENTIFIER,
+      JSON.stringify(existingData));
+  },
+
+  // ----------
+  // Function: deleteGroupItem
+  // Deletes the data for a single groupItem from the given window.
+  deleteGroupItem: function Storage_deleteGroupItem(win, id) {
+    var existingData = this.readGroupItemData(win);
+    delete existingData[id];
+    this._sessionStore.setWindowValue(win, this.GROUP_DATA_IDENTIFIER,
+      JSON.stringify(existingData));
+  },
+
+  // ----------
+  // Function: readGroupItemData
+  // Returns the data for all groupItems associated with the given window.
+  readGroupItemData: function Storage_readGroupItemData(win) {
+    var existingData = {};
+    let data;
+    try {
+      data = this._sessionStore.getWindowValue(win, this.GROUP_DATA_IDENTIFIER);
+      if (data)
+        existingData = JSON.parse(data);
+    } catch (e) {
+      // getWindowValue will fail if the property doesn't exist
+      Utils.log("Error in readGroupItemData: "+e, data);
+    }
+    return existingData;
+  },
+
+  // ----------
+  // Function: readWindowBusyState
+  // Returns the current busyState for the given window.
+  readWindowBusyState: function Storage_readWindowBusyState(win) {
+    let state;
+
+    try {
+      let data = this._sessionStore.getWindowState(win);
+      if (data)
+        state = JSON.parse(data);
+    } catch (e) {
+      Utils.log("Error while parsing window state");
+    }
+
+    return (state && state.windows[0].busy);
+  },
+
+  // ----------
+  // Function: saveGroupItemsData
+  // Saves the global data for the <GroupItems> singleton for the given window.
+  saveGroupItemsData: function Storage_saveGroupItemsData(win, data) {
+    this.saveData(win, this.GROUPS_DATA_IDENTIFIER, data);
+  },
+
+  // ----------
+  // Function: readGroupItemsData
+  // Reads the global data for the <GroupItems> singleton for the given window.
+  readGroupItemsData: function Storage_readGroupItemsData(win) {
+    return this.readData(win, this.GROUPS_DATA_IDENTIFIER);
+  },
+
+  // ----------
+  // Function: saveUIData
+  // Saves the global data for the <UIManager> singleton for the given window.
+  saveUIData: function Storage_saveUIData(win, data) {
+    this.saveData(win, this.UI_DATA_IDENTIFIER, data);
+  },
+
+  // ----------
+  // Function: readUIData
+  // Reads the global data for the <UIManager> singleton for the given window.
+  readUIData: function Storage_readUIData(win) {
+    return this.readData(win, this.UI_DATA_IDENTIFIER);
+  },
+
+  // ----------
+  // Function: saveVisibilityData
+  // Saves visibility for the given window.
+  saveVisibilityData: function Storage_saveVisibilityData(win, data) {
+    this._sessionStore.setWindowValue(
+      win, win.TabView.VISIBILITY_IDENTIFIER, data);
+  },
+
+  // ----------
+  // Function: saveData
+  // Generic routine for saving data to a window.
+  saveData: function Storage_saveData(win, id, data) {
+    try {
+      this._sessionStore.setWindowValue(win, id, JSON.stringify(data));
+    } catch (e) {
+      Utils.log("Error in saveData: "+e);
+    }
+  },
+
+  // ----------
+  // Function: readData
+  // Generic routine for reading data from a window.
+  readData: function Storage_readData(win, id) {
+    var existingData = {};
+    try {
+      var data = this._sessionStore.getWindowValue(win, id);
+      if (data)
+        existingData = JSON.parse(data);
+    } catch (e) {
+      Utils.log("Error in readData: "+e);
+    }
+
+    return existingData;
+  }
+};
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// **********
+// Title: items.js
+
+// ##########
+// Class: Item
+// Superclass for all visible objects (<TabItem>s and <GroupItem>s).
+//
+// If you subclass, in addition to the things Item provides, you need to also provide these methods:
+//   setBounds - function(rect, immediately, options)
+//   setZ - function(value)
+//   close - function()
+//   save - function()
+//
+// Subclasses of Item must also provide the <Subscribable> interface.
+//
+// ... and this property:
+//   defaultSize - a Point
+//
+// Make sure to call _init() from your subclass's constructor.
+function Item() {
+  // Variable: isAnItem
+  // Always true for Items
+  this.isAnItem = true;
+
+  // Variable: bounds
+  // The position and size of this Item, represented as a <Rect>.
+  // This should never be modified without using setBounds()
+  this.bounds = null;
+
+  // Variable: zIndex
+  // The z-index for this item.
+  this.zIndex = 0;
+
+  // Variable: container
+  // The outermost DOM element that describes this item on screen.
+  this.container = null;
+
+  // Variable: parent
+  // The groupItem that this item is a child of
+  this.parent = null;
+
+  // Variable: userSize
+  // A <Point> that describes the last size specifically chosen by the user.
+  // Used by unsquish.
+  this.userSize = null;
+
+  // Variable: dragOptions
+  // Used by <draggable>
+  //
+  // Possible properties:
+  //   cancelClass - A space-delimited list of classes that should cancel a drag
+  //   start - A function to be called when a drag starts
+  //   drag - A function to be called each time the mouse moves during drag
+  //   stop - A function to be called when the drag is done
+  this.dragOptions = null;
+
+  // Variable: dropOptions
+  // Used by <draggable> if the item is set to droppable.
+  //
+  // Possible properties:
+  //   accept - A function to determine if a particular item should be accepted for dropping
+  //   over - A function to be called when an item is over this item
+  //   out - A function to be called when an item leaves this item
+  //   drop - A function to be called when an item is dropped in this item
+  this.dropOptions = null;
+
+  // Variable: resizeOptions
+  // Used by <resizable>
+  //
+  // Possible properties:
+  //   minWidth - Minimum width allowable during resize
+  //   minHeight - Minimum height allowable during resize
+  //   aspectRatio - true if we should respect aspect ratio; default false
+  //   start - A function to be called when resizing starts
+  //   resize - A function to be called each time the mouse moves during resize
+  //   stop - A function to be called when the resize is done
+  this.resizeOptions = null;
+
+  // Variable: isDragging
+  // Boolean for whether the item is currently being dragged or not.
+  this.isDragging = false;
+};
+
+Item.prototype = {
+  // ----------
+  // Function: _init
+  // Initializes the object. To be called from the subclass's intialization function.
+  //
+  // Parameters:
+  //   container - the outermost DOM element that describes this item onscreen.
+  _init: function Item__init(container) {
+    Utils.assert(typeof this.addSubscriber == 'function' && 
+        typeof this.removeSubscriber == 'function' && 
+        typeof this._sendToSubscribers == 'function',
+        'Subclass must implement the Subscribable interface');
+    Utils.assert(Utils.isDOMElement(container), 'container must be a DOM element');
+    Utils.assert(typeof this.setBounds == 'function', 'Subclass must provide setBounds');
+    Utils.assert(typeof this.setZ == 'function', 'Subclass must provide setZ');
+    Utils.assert(typeof this.close == 'function', 'Subclass must provide close');
+    Utils.assert(typeof this.save == 'function', 'Subclass must provide save');
+    Utils.assert(Utils.isPoint(this.defaultSize), 'Subclass must provide defaultSize');
+    Utils.assert(Utils.isRect(this.bounds), 'Subclass must provide bounds');
+
+    this.container = container;
+    this.$container = iQ(container);
+
+    iQ(this.container).data('item', this);
+
+    // ___ drag
+    this.dragOptions = {
+      cancelClass: 'close stackExpander',
+      start: function(e, ui) {
+        UI.setActive(this);
+        if (this.isAGroupItem)
+          this._unfreezeItemSize();
+        // if we start dragging a tab within a group, start with dropSpace on.
+        else if (this.parent != null)
+          this.parent._dropSpaceActive = true;
+        drag.info = new Drag(this, e);
+      },
+      drag: function(e) {
+        drag.info.drag(e);
+      },
+      stop: function() {
+        drag.info.stop();
+
+        if (!this.isAGroupItem && !this.parent) {
+          new GroupItem([drag.info.$el], {focusTitle: true});
+          gTabView.firstUseExperienced = true;
+        }
+
+        drag.info = null;
+      },
+      // The minimum the mouse must move after mouseDown in order to move an 
+      // item
+      minDragDistance: 3
+    };
+
+    // ___ drop
+    this.dropOptions = {
+      over: function() {},
+      out: function() {
+        let groupItem = drag.info.item.parent;
+        if (groupItem)
+          groupItem.remove(drag.info.$el, {dontClose: true});
+        iQ(this.container).removeClass("acceptsDrop");
+      },
+      drop: function(event) {
+        iQ(this.container).removeClass("acceptsDrop");
+      },
+      // Function: dropAcceptFunction
+      // Given a DOM element, returns true if it should accept tabs being dropped on it.
+      // Private to this file.
+      accept: function dropAcceptFunction(item) {
+        return (item && item.isATabItem && (!item.parent || !item.parent.expanded));
+      }
+    };
+
+    // ___ resize
+    var self = this;
+    this.resizeOptions = {
+      aspectRatio: self.keepProportional,
+      minWidth: 90,
+      minHeight: 90,
+      start: function(e,ui) {
+        UI.setActive(this);
+        resize.info = new Drag(this, e);
+      },
+      resize: function(e,ui) {
+        resize.info.snap(UI.rtl ? 'topright' : 'topleft', false, self.keepProportional);
+      },
+      stop: function() {
+        self.setUserSize();
+        self.pushAway();
+        resize.info.stop();
+        resize.info = null;
+      }
+    };
+  },
+
+  // ----------
+  // Function: getBounds
+  // Returns a copy of the Item's bounds as a <Rect>.
+  getBounds: function Item_getBounds() {
+    Utils.assert(Utils.isRect(this.bounds), 'this.bounds should be a rect');
+    return new Rect(this.bounds);
+  },
+
+  // ----------
+  // Function: overlapsWithOtherItems
+  // Returns true if this Item overlaps with any other Item on the screen.
+  overlapsWithOtherItems: function Item_overlapsWithOtherItems() {
+    var self = this;
+    var items = Items.getTopLevelItems();
+    var bounds = this.getBounds();
+    return items.some(function(item) {
+      if (item == self) // can't overlap with yourself.
+        return false;
+      var myBounds = item.getBounds();
+      return myBounds.intersects(bounds);
+    } );
+  },
+
+  // ----------
+  // Function: setPosition
+  // Moves the Item to the specified location.
+  //
+  // Parameters:
+  //   left - the new left coordinate relative to the window
+  //   top - the new top coordinate relative to the window
+  //   immediately - if false or omitted, animates to the new position;
+  //   otherwise goes there immediately
+  setPosition: function Item_setPosition(left, top, immediately) {
+    Utils.assert(Utils.isRect(this.bounds), 'this.bounds');
+    this.setBounds(new Rect(left, top, this.bounds.width, this.bounds.height), immediately);
+  },
+
+  // ----------
+  // Function: setSize
+  // Resizes the Item to the specified size.
+  //
+  // Parameters:
+  //   width - the new width in pixels
+  //   height - the new height in pixels
+  //   immediately - if false or omitted, animates to the new size;
+  //   otherwise resizes immediately
+  setSize: function Item_setSize(width, height, immediately) {
+    Utils.assert(Utils.isRect(this.bounds), 'this.bounds');
+    this.setBounds(new Rect(this.bounds.left, this.bounds.top, width, height), immediately);
+  },
+
+  // ----------
+  // Function: setUserSize
+  // Remembers the current size as one the user has chosen.
+  setUserSize: function Item_setUserSize() {
+    Utils.assert(Utils.isRect(this.bounds), 'this.bounds');
+    this.userSize = new Point(this.bounds.width, this.bounds.height);
+    this.save();
+  },
+
+  // ----------
+  // Function: getZ
+  // Returns the zIndex of the Item.
+  getZ: function Item_getZ() {
+    return this.zIndex;
+  },
+
+  // ----------
+  // Function: setRotation
+  // Rotates the object to the given number of degrees.
+  setRotation: function Item_setRotation(degrees) {
+    var value = degrees ? "rotate(%deg)".replace(/%/, degrees) : null;
+    iQ(this.container).css({"-moz-transform": value});
+  },
+
+  // ----------
+  // Function: setParent
+  // Sets the receiver's parent to the given <Item>.
+  setParent: function Item_setParent(parent) {
+    this.parent = parent;
+    this.removeTrenches();
+    this.save();
+  },
+
+  // ----------
+  // Function: pushAway
+  // Pushes all other items away so none overlap this Item.
+  //
+  // Parameters:
+  //  immediately - boolean for doing the pushAway without animation
+  pushAway: function Item_pushAway(immediately) {
+    var items = Items.getTopLevelItems();
+
+    // we need at least two top-level items to push something away
+    if (items.length < 2)
+      return;
+
+    var buffer = Math.floor(Items.defaultGutter / 2);
+
+    // setup each Item's pushAwayData attribute:
+    items.forEach(function pushAway_setupPushAwayData(item) {
+      var data = {};
+      data.bounds = item.getBounds();
+      data.startBounds = new Rect(data.bounds);
+      // Infinity = (as yet) unaffected
+      data.generation = Infinity;
+      item.pushAwayData = data;
+    });
+
+    // The first item is a 0-generation pushed item. It all starts here.
+    var itemsToPush = [this];
+    this.pushAwayData.generation = 0;
+
+    var pushOne = function Item_pushAway_pushOne(baseItem) {
+      // the baseItem is an n-generation pushed item. (n could be 0)
+      var baseData = baseItem.pushAwayData;
+      var bb = new Rect(baseData.bounds);
+
+      // make the bounds larger, adding a +buffer margin to each side.
+      bb.inset(-buffer, -buffer);
+      // bbc = center of the base's bounds
+      var bbc = bb.center();
+
+      items.forEach(function Item_pushAway_pushOne_pushEach(item) {
+        if (item == baseItem)
+          return;
+
+        var data = item.pushAwayData;
+        // if the item under consideration has already been pushed, or has a lower
+        // "generation" (and thus an implictly greater placement priority) then don't move it.
+        if (data.generation <= baseData.generation)
+          return;
+
+        // box = this item's current bounds, with a +buffer margin.
+        var bounds = data.bounds;
+        var box = new Rect(bounds);
+        box.inset(-buffer, -buffer);
+
+        // if the item under consideration overlaps with the base item...
+        if (box.intersects(bb)) {
+
+          // Let's push it a little.
+
+          // First, decide in which direction and how far to push. This is the offset.
+          var offset = new Point();
+          // center = the current item's center.
+          var center = box.center();
+
+          // Consider the relationship between the current item (box) + the base item.
+          // If it's more vertically stacked than "side by side"...
+          if (Math.abs(center.x - bbc.x) < Math.abs(center.y - bbc.y)) {
+            // push vertically.
+            if (center.y > bbc.y)
+              offset.y = bb.bottom - box.top;
+            else
+              offset.y = bb.top - box.bottom;
+          } else { // if they're more "side by side" than stacked vertically...
+            // push horizontally.
+            if (center.x > bbc.x)
+              offset.x = bb.right - box.left;
+            else
+              offset.x = bb.left - box.right;
+          }
+
+          // Actually push the Item.
+          bounds.offset(offset);
+
+          // This item now becomes an (n+1)-generation pushed item.
+          data.generation = baseData.generation + 1;
+          // keep track of who pushed this item.
+          data.pusher = baseItem;
+          // add this item to the queue, so that it, in turn, can push some other things.
+          itemsToPush.push(item);
+        }
+      });
+    };
+
+    // push each of the itemsToPush, one at a time.
+    // itemsToPush starts with just [this], but pushOne can add more items to the stack.
+    // Maximally, this could run through all Items on the screen.
+    while (itemsToPush.length)
+      pushOne(itemsToPush.shift());
+
+    // ___ Squish!
+    var pageBounds = Items.getSafeWindowBounds();
+    items.forEach(function Item_pushAway_squish(item) {
+      var data = item.pushAwayData;
+      if (data.generation == 0)
+        return;
+
+      let apply = function Item_pushAway_squish_apply(item, posStep, posStep2, sizeStep) {
+        var data = item.pushAwayData;
+        if (data.generation == 0)
+          return;
+
+        var bounds = data.bounds;
+        bounds.width -= sizeStep.x;
+        bounds.height -= sizeStep.y;
+        bounds.left += posStep.x;
+        bounds.top += posStep.y;
+
+        let validSize;
+        if (item.isAGroupItem) {
+          validSize = GroupItems.calcValidSize(
+            new Point(bounds.width, bounds.height));
+          bounds.width = validSize.x;
+          bounds.height = validSize.y;
+        } else {
+          if (sizeStep.y > sizeStep.x) {
+            validSize = TabItems.calcValidSize(new Point(-1, bounds.height));
+            bounds.left += (bounds.width - validSize.x) / 2;
+            bounds.width = validSize.x;
+          } else {
+            validSize = TabItems.calcValidSize(new Point(bounds.width, -1));
+            bounds.top += (bounds.height - validSize.y) / 2;
+            bounds.height = validSize.y;        
+          }
+        }
+
+        var pusher = data.pusher;
+        if (pusher) {
+          var newPosStep = new Point(posStep.x + posStep2.x, posStep.y + posStep2.y);
+          apply(pusher, newPosStep, posStep2, sizeStep);
+        }
+      }
+
+      var bounds = data.bounds;
+      var posStep = new Point();
+      var posStep2 = new Point();
+      var sizeStep = new Point();
+
+      if (bounds.left < pageBounds.left) {
+        posStep.x = pageBounds.left - bounds.left;
+        sizeStep.x = posStep.x / data.generation;
+        posStep2.x = -sizeStep.x;
+      } else if (bounds.right > pageBounds.right) { // this may be less of a problem post-601534
+        posStep.x = pageBounds.right - bounds.right;
+        sizeStep.x = -posStep.x / data.generation;
+        posStep.x += sizeStep.x;
+        posStep2.x = sizeStep.x;
+      }
+
+      if (bounds.top < pageBounds.top) {
+        posStep.y = pageBounds.top - bounds.top;
+        sizeStep.y = posStep.y / data.generation;
+        posStep2.y = -sizeStep.y;
+      } else if (bounds.bottom > pageBounds.bottom) { // this may be less of a problem post-601534
+        posStep.y = pageBounds.bottom - bounds.bottom;
+        sizeStep.y = -posStep.y / data.generation;
+        posStep.y += sizeStep.y;
+        posStep2.y = sizeStep.y;
+      }
+
+      if (posStep.x || posStep.y || sizeStep.x || sizeStep.y)
+        apply(item, posStep, posStep2, sizeStep);        
+    });
+
+    // ___ Unsquish
+    var pairs = [];
+    items.forEach(function Item_pushAway_setupUnsquish(item) {
+      var data = item.pushAwayData;
+      pairs.push({
+        item: item,
+        bounds: data.bounds
+      });
+    });
+
+    Items.unsquish(pairs);
+
+    // ___ Apply changes
+    items.forEach(function Item_pushAway_setBounds(item) {
+      var data = item.pushAwayData;
+      var bounds = data.bounds;
+      if (!bounds.equals(data.startBounds)) {
+        item.setBounds(bounds, immediately);
+      }
+    });
+  },
+
+  // ----------
+  // Function: setTrenches
+  // Sets up/moves the trenches for snapping to this item.
+  setTrenches: function Item_setTrenches(rect) {
+    if (this.parent !== null)
+      return;
+
+    if (!this.borderTrenches)
+      this.borderTrenches = Trenches.registerWithItem(this,"border");
+
+    var bT = this.borderTrenches;
+    Trenches.getById(bT.left).setWithRect(rect);
+    Trenches.getById(bT.right).setWithRect(rect);
+    Trenches.getById(bT.top).setWithRect(rect);
+    Trenches.getById(bT.bottom).setWithRect(rect);
+
+    if (!this.guideTrenches)
+      this.guideTrenches = Trenches.registerWithItem(this,"guide");
+
+    var gT = this.guideTrenches;
+    Trenches.getById(gT.left).setWithRect(rect);
+    Trenches.getById(gT.right).setWithRect(rect);
+    Trenches.getById(gT.top).setWithRect(rect);
+    Trenches.getById(gT.bottom).setWithRect(rect);
+
+  },
+
+  // ----------
+  // Function: removeTrenches
+  // Removes the trenches for snapping to this item.
+  removeTrenches: function Item_removeTrenches() {
+    for (var edge in this.borderTrenches) {
+      Trenches.unregister(this.borderTrenches[edge]); // unregister can take an array
+    }
+    this.borderTrenches = null;
+    for (var edge in this.guideTrenches) {
+      Trenches.unregister(this.guideTrenches[edge]); // unregister can take an array
+    }
+    this.guideTrenches = null;
+  },
+
+  // ----------
+  // Function: snap
+  // The snap function used during groupItem creation via drag-out
+  //
+  // Parameters:
+  //  immediately - bool for having the drag do the final positioning without animation
+  snap: function Item_snap(immediately) {
+    // make the snapping work with a wider range!
+    var defaultRadius = Trenches.defaultRadius;
+    Trenches.defaultRadius = 2 * defaultRadius; // bump up from 10 to 20!
+
+    var FauxDragInfo = new Drag(this, {});
+    FauxDragInfo.snap('none', false);
+    FauxDragInfo.stop(immediately);
+
+    Trenches.defaultRadius = defaultRadius;
+  },
+
+  // ----------
+  // Function: draggable
+  // Enables dragging on this item. Note: not to be called multiple times on the same item!
+  draggable: function Item_draggable() {
+    try {
+      Utils.assert(this.dragOptions, 'dragOptions');
+
+      var cancelClasses = [];
+      if (typeof this.dragOptions.cancelClass == 'string')
+        cancelClasses = this.dragOptions.cancelClass.split(' ');
+
+      var self = this;
+      var $container = iQ(this.container);
+      var startMouse;
+      var startPos;
+      var startSent;
+      var startEvent;
+      var droppables;
+      var dropTarget;
+
+      // determine the best drop target based on the current mouse coordinates
+      let determineBestDropTarget = function (e, box) {
+        // drop events
+        var best = {
+          dropTarget: null,
+          score: 0
+        };
+
+        droppables.forEach(function(droppable) {
+          var intersection = box.intersection(droppable.bounds);
+          if (intersection && intersection.area() > best.score) {
+            var possibleDropTarget = droppable.item;
+            var accept = true;
+            if (possibleDropTarget != dropTarget) {
+              var dropOptions = possibleDropTarget.dropOptions;
+              if (dropOptions && typeof dropOptions.accept == "function")
+                accept = dropOptions.accept.apply(possibleDropTarget, [self]);
+            }
+
+            if (accept) {
+              best.dropTarget = possibleDropTarget;
+              best.score = intersection.area();
+            }
+          }
+        });
+
+        return best.dropTarget;
+      }
+
+      // ___ mousemove
+      var handleMouseMove = function(e) {
+        // global drag tracking
+        drag.lastMoveTime = Date.now();
+
+        // positioning
+        var mouse = new Point(e.pageX, e.pageY);
+        if (!startSent) {
+          if(Math.abs(mouse.x - startMouse.x) > self.dragOptions.minDragDistance ||
+             Math.abs(mouse.y - startMouse.y) > self.dragOptions.minDragDistance) {
+            if (typeof self.dragOptions.start == "function")
+              self.dragOptions.start.apply(self,
+                  [startEvent, {position: {left: startPos.x, top: startPos.y}}]);
+            startSent = true;
+          }
+        }
+        if (startSent) {
+          // drag events
+          var box = self.getBounds();
+          box.left = startPos.x + (mouse.x - startMouse.x);
+          box.top = startPos.y + (mouse.y - startMouse.y);
+          self.setBounds(box, true);
+
+          if (typeof self.dragOptions.drag == "function")
+            self.dragOptions.drag.apply(self, [e]);
+
+          let bestDropTarget = determineBestDropTarget(e, box);
+
+          if (bestDropTarget != dropTarget) {
+            var dropOptions;
+            if (dropTarget) {
+              dropOptions = dropTarget.dropOptions;
+              if (dropOptions && typeof dropOptions.out == "function")
+                dropOptions.out.apply(dropTarget, [e]);
+            }
+
+            dropTarget = bestDropTarget;
+
+            if (dropTarget) {
+              dropOptions = dropTarget.dropOptions;
+              if (dropOptions && typeof dropOptions.over == "function")
+                dropOptions.over.apply(dropTarget, [e]);
+            }
+          }
+          if (dropTarget) {
+            dropOptions = dropTarget.dropOptions;
+            if (dropOptions && typeof dropOptions.move == "function")
+              dropOptions.move.apply(dropTarget, [e]);
+          }
+        }
+
+        e.preventDefault();
+      };
+
+      // ___ mouseup
+      var handleMouseUp = function(e) {
+        iQ(gWindow)
+          .unbind('mousemove', handleMouseMove)
+          .unbind('mouseup', handleMouseUp);
+
+        if (startSent && dropTarget) {
+          var dropOptions = dropTarget.dropOptions;
+          if (dropOptions && typeof dropOptions.drop == "function")
+            dropOptions.drop.apply(dropTarget, [e]);
+        }
+
+        if (startSent && typeof self.dragOptions.stop == "function")
+          self.dragOptions.stop.apply(self, [e]);
+
+        e.preventDefault();
+      };
+
+      // ___ mousedown
+      $container.mousedown(function(e) {
+        if (!Utils.isLeftClick(e))
+          return;
+
+        var cancel = false;
+        var $target = iQ(e.target);
+        cancelClasses.forEach(function(className) {
+          if ($target.hasClass(className))
+            cancel = true;
+        });
+
+        if (cancel) {
+          e.preventDefault();
+          return;
+        }
+
+        startMouse = new Point(e.pageX, e.pageY);
+        let bounds = self.getBounds();
+        startPos = bounds.position();
+        startEvent = e;
+        startSent = false;
+
+        droppables = [];
+        iQ('.iq-droppable').each(function(elem) {
+          if (elem != self.container) {
+            var item = Items.item(elem);
+            droppables.push({
+              item: item,
+              bounds: item.getBounds()
+            });
+          }
+        });
+
+        dropTarget = determineBestDropTarget(e, bounds);
+
+        iQ(gWindow)
+          .mousemove(handleMouseMove)
+          .mouseup(handleMouseUp);
+
+        e.preventDefault();
+      });
+    } catch(e) {
+      Utils.log(e);
+    }
+  },
+
+  // ----------
+  // Function: droppable
+  // Enables or disables dropping on this item.
+  droppable: function Item_droppable(value) {
+    try {
+      var $container = iQ(this.container);
+      if (value) {
+        Utils.assert(this.dropOptions, 'dropOptions');
+        $container.addClass('iq-droppable');
+      } else
+        $container.removeClass('iq-droppable');
+    } catch(e) {
+      Utils.log(e);
+    }
+  },
+
+  // ----------
+  // Function: resizable
+  // Enables or disables resizing of this item.
+  resizable: function Item_resizable(value) {
+    try {
+      var $container = iQ(this.container);
+      iQ('.iq-resizable-handle', $container).remove();
+
+      if (!value) {
+        $container.removeClass('iq-resizable');
+      } else {
+        Utils.assert(this.resizeOptions, 'resizeOptions');
+
+        $container.addClass('iq-resizable');
+
+        var self = this;
+        var startMouse;
+        var startSize;
+        var startAspect;
+
+        // ___ mousemove
+        var handleMouseMove = function(e) {
+          // global resize tracking
+          resize.lastMoveTime = Date.now();
+
+          var mouse = new Point(e.pageX, e.pageY);
+          var box = self.getBounds();
+          if (UI.rtl) {
+            var minWidth = (self.resizeOptions.minWidth || 0);
+            var oldWidth = box.width;
+            if (minWidth != oldWidth || mouse.x < startMouse.x) {
+              box.width = Math.max(minWidth, startSize.x - (mouse.x - startMouse.x));
+              box.left -= box.width - oldWidth;
+            }
+          } else {
+            box.width = Math.max(self.resizeOptions.minWidth || 0, startSize.x + (mouse.x - startMouse.x));
+          }
+          box.height = Math.max(self.resizeOptions.minHeight || 0, startSize.y + (mouse.y - startMouse.y));
+
+          if (self.resizeOptions.aspectRatio) {
+            if (startAspect < 1)
+              box.height = box.width * startAspect;
+            else
+              box.width = box.height / startAspect;
+          }
+
+          self.setBounds(box, true);
+
+          if (typeof self.resizeOptions.resize == "function")
+            self.resizeOptions.resize.apply(self, [e]);
+
+          e.preventDefault();
+          e.stopPropagation();
+        };
+
+        // ___ mouseup
+        var handleMouseUp = function(e) {
+          iQ(gWindow)
+            .unbind('mousemove', handleMouseMove)
+            .unbind('mouseup', handleMouseUp);
+
+          if (typeof self.resizeOptions.stop == "function")
+            self.resizeOptions.stop.apply(self, [e]);
+
+          e.preventDefault();
+          e.stopPropagation();
+        };
+
+        // ___ handle + mousedown
+        iQ('<div>')
+          .addClass('iq-resizable-handle iq-resizable-se')
+          .appendTo($container)
+          .mousedown(function(e) {
+            if (!Utils.isLeftClick(e))
+              return;
+
+            startMouse = new Point(e.pageX, e.pageY);
+            startSize = self.getBounds().size();
+            startAspect = startSize.y / startSize.x;
+
+            if (typeof self.resizeOptions.start == "function")
+              self.resizeOptions.start.apply(self, [e]);
+
+            iQ(gWindow)
+              .mousemove(handleMouseMove)
+              .mouseup(handleMouseUp);
+
+            e.preventDefault();
+            e.stopPropagation();
+          });
+        }
+    } catch(e) {
+      Utils.log(e);
+    }
+  }
+};
+
+// ##########
+// Class: Items
+// Keeps track of all Items.
+let Items = {
+  // ----------
+  // Function: toString
+  // Prints [Items] for debug use
+  toString: function Items_toString() {
+    return "[Items]";
+  },
+
+  // ----------
+  // Variable: defaultGutter
+  // How far apart Items should be from each other and from bounds
+  defaultGutter: 15,
+
+  // ----------
+  // Function: item
+  // Given a DOM element representing an Item, returns the Item.
+  item: function Items_item(el) {
+    return iQ(el).data('item');
+  },
+
+  // ----------
+  // Function: getTopLevelItems
+  // Returns an array of all Items not grouped into groupItems.
+  getTopLevelItems: function Items_getTopLevelItems() {
+    var items = [];
+
+    iQ('.tab, .groupItem').each(function(elem) {
+      var $this = iQ(elem);
+      var item = $this.data('item');
+      if (item && !item.parent && !$this.hasClass('phantom'))
+        items.push(item);
+    });
+
+    return items;
+  },
+
+  // ----------
+  // Function: getPageBounds
+  // Returns a <Rect> defining the area of the page <Item>s should stay within.
+  getPageBounds: function Items_getPageBounds() {
+    var width = Math.max(100, window.innerWidth);
+    var height = Math.max(100, window.innerHeight);
+    return new Rect(0, 0, width, height);
+  },
+
+  // ----------
+  // Function: getSafeWindowBounds
+  // Returns the bounds within which it is safe to place all non-stationary <Item>s.
+  getSafeWindowBounds: function Items_getSafeWindowBounds() {
+    // the safe bounds that would keep it "in the window"
+    var gutter = Items.defaultGutter;
+    // Here, I've set the top gutter separately, as the top of the window has its own
+    // extra chrome which makes a large top gutter unnecessary.
+    // TODO: set top gutter separately, elsewhere.
+    var topGutter = 5;
+    return new Rect(gutter, topGutter,
+        window.innerWidth - 2 * gutter, window.innerHeight - gutter - topGutter);
+
+  },
+
+  // ----------
+  // Function: arrange
+  // Arranges the given items in a grid within the given bounds,
+  // maximizing item size but maintaining standard tab aspect ratio for each
+  //
+  // Parameters:
+  //   items - an array of <Item>s. Can be null, in which case we won't
+  //     actually move anything.
+  //   bounds - a <Rect> defining the space to arrange within
+  //   options - an object with various properites (see below)
+  //
+  // Possible "options" properties:
+  //   animate - whether to animate; default: true.
+  //   z - the z index to set all the items; default: don't change z.
+  //   return - if set to 'widthAndColumns', it'll return an object with the
+  //     width of children and the columns.
+  //   count - overrides the item count for layout purposes;
+  //     default: the actual item count
+  //   columns - (int) a preset number of columns to use
+  //   dropPos - a <Point> which should have a one-tab space left open, used
+  //             when a tab is dragged over.
+  //
+  // Returns:
+  //   By default, an object with three properties: `rects`, the list of <Rect>s,
+  //   `dropIndex`, the index which a dragged tab should have if dropped
+  //   (null if no `dropPos` was specified), and the number of columns (`columns`).
+  //   If the `return` option is set to 'widthAndColumns', an object with the
+  //   width value of the child items (`childWidth`) and the number of columns
+  //   (`columns`) is returned.
+  arrange: function Items_arrange(items, bounds, options) {
+    if (!options)
+      options = {};
+    var animate = "animate" in options ? options.animate : true;
+    var immediately = !animate;
+
+    var rects = [];
+
+    var count = options.count || (items ? items.length : 0);
+    if (options.addTab)
+      count++;
+    if (!count) {
+      let dropIndex = (Utils.isPoint(options.dropPos)) ? 0 : null;
+      return {rects: rects, dropIndex: dropIndex};
+    }
+
+    var columns = options.columns || 1;
+    // We'll assume for the time being that all the items have the same styling
+    // and that the margin is the same width around.
+    var itemMargin = items && items.length ?
+                       parseInt(iQ(items[0].container).css('margin-left')) : 0;
+    var padding = itemMargin * 2;
+    var rows;
+    var tabWidth;
+    var tabHeight;
+    var totalHeight;
+
+    function figure() {
+      rows = Math.ceil(count / columns);
+      let validSize = TabItems.calcValidSize(
+        new Point((bounds.width - (padding * columns)) / columns, -1),
+        options);
+      tabWidth = validSize.x;
+      tabHeight = validSize.y;
+
+      totalHeight = (tabHeight * rows) + (padding * rows);    
+    }
+
+    figure();
+
+    while (rows > 1 && totalHeight > bounds.height) {
+      columns++;
+      figure();
+    }
+
+    if (rows == 1) {
+      let validSize = TabItems.calcValidSize(new Point(tabWidth,
+        bounds.height - 2 * itemMargin), options);
+      tabWidth = validSize.x;
+      tabHeight = validSize.y;
+    }
+    
+    if (options.return == 'widthAndColumns')
+      return {childWidth: tabWidth, columns: columns};
+
+    let initialOffset = 0;
+    if (UI.rtl) {
+      initialOffset = bounds.width - tabWidth - padding;
+    }
+    var box = new Rect(bounds.left + initialOffset, bounds.top, tabWidth, tabHeight);
+
+    var column = 0;
+
+    var dropIndex = false;
+    var dropRect = false;
+    if (Utils.isPoint(options.dropPos))
+      dropRect = new Rect(options.dropPos.x, options.dropPos.y, 1, 1);
+    for (let a = 0; a < count; a++) {
+      // If we had a dropPos, see if this is where we should place it
+      if (dropRect) {
+        let activeBox = new Rect(box);
+        activeBox.inset(-itemMargin - 1, -itemMargin - 1);
+        // if the designated position (dropRect) is within the active box,
+        // this is where, if we drop the tab being dragged, it should land!
+        if (activeBox.contains(dropRect))
+          dropIndex = a;
+      }
+      
+      // record the box.
+      rects.push(new Rect(box));
+
+      box.left += (UI.rtl ? -1 : 1) * (box.width + padding);
+      column++;
+      if (column == columns) {
+        box.left = bounds.left + initialOffset;
+        box.top += box.height + padding;
+        column = 0;
+      }
+    }
+
+    return {rects: rects, dropIndex: dropIndex, columns: columns};
+  },
+
+  // ----------
+  // Function: unsquish
+  // Checks to see which items can now be unsquished.
+  //
+  // Parameters:
+  //   pairs - an array of objects, each with two properties: item and bounds. The bounds are
+  //     modified as appropriate, but the items are not changed. If pairs is null, the
+  //     operation is performed directly on all of the top level items.
+  //   ignore - an <Item> to not include in calculations (because it's about to be closed, for instance)
+  unsquish: function Items_unsquish(pairs, ignore) {
+    var pairsProvided = (pairs ? true : false);
+    if (!pairsProvided) {
+      var items = Items.getTopLevelItems();
+      pairs = [];
+      items.forEach(function(item) {
+        pairs.push({
+          item: item,
+          bounds: item.getBounds()
+        });
+      });
+    }
+
+    var pageBounds = Items.getSafeWindowBounds();
+    pairs.forEach(function(pair) {
+      var item = pair.item;
+      if (item == ignore)
+        return;
+
+      var bounds = pair.bounds;
+      var newBounds = new Rect(bounds);
+
+      var newSize;
+      if (Utils.isPoint(item.userSize))
+        newSize = new Point(item.userSize);
+      else if (item.isAGroupItem)
+        newSize = GroupItems.calcValidSize(
+          new Point(GroupItems.minGroupWidth, -1));
+      else
+        newSize = TabItems.calcValidSize(
+          new Point(TabItems.tabWidth, -1));
+
+      if (item.isAGroupItem) {
+          newBounds.width = Math.max(newBounds.width, newSize.x);
+          newBounds.height = Math.max(newBounds.height, newSize.y);
+      } else {
+        if (bounds.width < newSize.x) {
+          newBounds.width = newSize.x;
+          newBounds.height = newSize.y;
+        }
+      }
+
+      newBounds.left -= (newBounds.width - bounds.width) / 2;
+      newBounds.top -= (newBounds.height - bounds.height) / 2;
+
+      var offset = new Point();
+      if (newBounds.left < pageBounds.left)
+        offset.x = pageBounds.left - newBounds.left;
+      else if (newBounds.right > pageBounds.right)
+        offset.x = pageBounds.right - newBounds.right;
+
+      if (newBounds.top < pageBounds.top)
+        offset.y = pageBounds.top - newBounds.top;
+      else if (newBounds.bottom > pageBounds.bottom)
+        offset.y = pageBounds.bottom - newBounds.bottom;
+
+      newBounds.offset(offset);
+
+      if (!bounds.equals(newBounds)) {
+        var blocked = false;
+        pairs.forEach(function(pair2) {
+          if (pair2 == pair || pair2.item == ignore)
+            return;
+
+          var bounds2 = pair2.bounds;
+          if (bounds2.intersects(newBounds))
+            blocked = true;
+          return;
+        });
+
+        if (!blocked) {
+          pair.bounds.copy(newBounds);
+        }
+      }
+      return;
+    });
+
+    if (!pairsProvided) {
+      pairs.forEach(function(pair) {
+        pair.item.setBounds(pair.bounds);
+      });
+    }
+  }
+};
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// **********
+// Title: groupItems.js
+
+// ##########
+// Class: GroupItem
+// A single groupItem in the TabView window. Descended from <Item>.
+// Note that it implements the <Subscribable> interface.
+//
+// ----------
+// Constructor: GroupItem
+//
+// Parameters:
+//   listOfEls - an array of DOM elements for tabs to be added to this groupItem
+//   options - various options for this groupItem (see below). In addition, gets passed
+//     to <add> along with the elements provided.
+//
+// Possible options:
+//   id - specifies the groupItem's id; otherwise automatically generated
+//   userSize - see <Item.userSize>; default is null
+//   bounds - a <Rect>; otherwise based on the locations of the provided elements
+//   container - a DOM element to use as the container for this groupItem; otherwise will create
+//   title - the title for the groupItem; otherwise blank
+//   focusTitle - focus the title's input field after creation
+//   dontPush - true if this groupItem shouldn't push away or snap on creation; default is false
+//   immediately - true if we want all placement immediately, not with animation
+function GroupItem(listOfEls, options) {
+  if (!options)
+    options = {};
+
+  this._inited = false;
+  this._uninited = false;
+  this._children = []; // an array of Items
+  this.defaultSize = new Point(TabItems.tabWidth * 1.5, TabItems.tabHeight * 1.5);
+  this.isAGroupItem = true;
+  this.id = options.id || GroupItems.getNextID();
+  this._isStacked = false;
+  this.expanded = null;
+  this.hidden = false;
+  this.fadeAwayUndoButtonDelay = 15000;
+  this.fadeAwayUndoButtonDuration = 300;
+
+  this.keepProportional = false;
+  this._frozenItemSizeData = {};
+
+  this._onChildClose = this._onChildClose.bind(this);
+
+  // Variable: _activeTab
+  // The <TabItem> for the groupItem's active tab.
+  this._activeTab = null;
+
+  if (Utils.isPoint(options.userSize))
+    this.userSize = new Point(options.userSize);
+
+  var self = this;
+
+  var rectToBe;
+  if (options.bounds) {
+    Utils.assert(Utils.isRect(options.bounds), "options.bounds must be a Rect");
+    rectToBe = new Rect(options.bounds);
+  }
+
+  if (!rectToBe) {
+    rectToBe = GroupItems.getBoundingBox(listOfEls);
+    rectToBe.inset(-42, -42);
+  }
+
+  var $container = options.container;
+  let immediately = options.immediately || $container ? true : false;
+  if (!$container) {
+    $container = iQ('<div>')
+      .addClass('groupItem')
+      .css({position: 'absolute'})
+      .css(rectToBe);
+  }
+
+  this.bounds = $container.bounds();
+
+  this.isDragging = false;
+  $container
+    .css({zIndex: -100})
+    .attr("data-id", this.id)
+    .appendTo("body");
+
+  // ___ Resizer
+  this.$resizer = iQ("<div>")
+    .addClass('resizer')
+    .appendTo($container)
+    .hide();
+
+  // ___ Titlebar
+  var html =
+    "<div class='title-container'>" +
+      "<input class='name' placeholder='" + this.defaultName + "'/>" +
+      "<div class='title-shield' />" +
+    "</div>";
+
+  this.$titlebar = iQ('<div>')
+    .addClass('titlebar')
+    .html(html)
+    .appendTo($container);
+
+  this.$closeButton = iQ('<div>')
+    .addClass('close')
+    .click(function() {
+      self.closeAll();
+    })
+    .attr("title", tabviewString("groupItem.closeGroup"))
+    .appendTo($container);
+
+  // ___ Title
+  this.$titleContainer = iQ('.title-container', this.$titlebar);
+  this.$title = iQ('.name', this.$titlebar);
+  this.$titleShield = iQ('.title-shield', this.$titlebar);
+  this.setTitle(options.title);
+
+  var handleKeyPress = function (e) {
+    if (e.keyCode == KeyEvent.DOM_VK_ESCAPE ||
+        e.keyCode == KeyEvent.DOM_VK_RETURN ||
+        e.keyCode == KeyEvent.DOM_VK_ENTER) {
+      (self.$title)[0].blur();
+      self.$title
+        .addClass("transparentBorder")
+        .one("mouseout", function() {
+          self.$title.removeClass("transparentBorder");
+        });
+      e.stopPropagation();
+      e.preventDefault();
+    }
+  };
+
+  var handleKeyUp = function(e) {
+    // NOTE: When user commits or cancels IME composition, the last key
+    //       event fires only a keyup event.  Then, we shouldn't take any
+    //       reactions but we should update our status.
+    self.save();
+  };
+
+  this.$title
+    .blur(function() {
+      self._titleFocused = false;
+      self.$title[0].setSelectionRange(0, 0);
+      self.$titleShield.show();
+      if (self.getTitle())
+        gTabView.firstUseExperienced = true;
+      self.save();
+    })
+    .focus(function() {
+      self._unfreezeItemSize();
+      if (!self._titleFocused) {
+        (self.$title)[0].select();
+        self._titleFocused = true;
+      }
+    })
+    .mousedown(function(e) {
+      e.stopPropagation();
+    })
+    .keypress(handleKeyPress)
+    .keyup(handleKeyUp)
+    .attr("title", tabviewString("groupItem.defaultName"));
+
+  this.$titleShield
+    .mousedown(function(e) {
+      self.lastMouseDownTarget = (Utils.isLeftClick(e) ? e.target : null);
+    })
+    .mouseup(function(e) {
+      var same = (e.target == self.lastMouseDownTarget);
+      self.lastMouseDownTarget = null;
+      if (!same)
+        return;
+
+      if (!self.isDragging)
+        self.focusTitle();
+    })
+    .attr("title", tabviewString("groupItem.defaultName"));
+
+  if (options.focusTitle)
+    this.focusTitle();
+
+  // ___ Stack Expander
+  this.$expander = iQ("<div/>")
+    .addClass("stackExpander")
+    .appendTo($container)
+    .hide();
+
+  // ___ app tabs: create app tab tray and populate it
+  let appTabTrayContainer = iQ("<div/>")
+    .addClass("appTabTrayContainer")
+    .appendTo($container);
+  this.$appTabTray = iQ("<div/>")
+    .addClass("appTabTray")
+    .appendTo(appTabTrayContainer);
+
+  let pinnedTabCount = gBrowser._numPinnedTabs;
+  AllTabs.tabs.forEach(function (xulTab, index) {
+    // only adjust tray when it's the last app tab.
+    if (xulTab.pinned)
+      this.addAppTab(xulTab, {dontAdjustTray: index + 1 < pinnedTabCount});
+  }, this);
+
+  // ___ Undo Close
+  this.$undoContainer = null;
+  this._undoButtonTimeoutId = null;
+
+  // ___ Superclass initialization
+  this._init($container[0]);
+
+  // ___ Children
+  // We explicitly set dontArrange=true to prevent the groupItem from
+  // re-arranging its children after a tabItem has been added. This saves us a
+  // group.arrange() call per child and therefore some tab.setBounds() calls.
+  options.dontArrange = true;
+  listOfEls.forEach(function (el) {
+    self.add(el, options);
+  });
+
+  // ___ Finish Up
+  this._addHandlers($container);
+
+  this.setResizable(true, immediately);
+
+  GroupItems.register(this);
+
+  // ___ Position
+  this.setBounds(rectToBe, immediately);
+  if (options.dontPush) {
+    this.setZ(drag.zIndex);
+    drag.zIndex++; 
+  } else {
+    // Calling snap will also trigger pushAway
+    this.snap(immediately);
+  }
+
+  if (!options.immediately && listOfEls.length > 0)
+    $container.hide().fadeIn();
+
+  this._inited = true;
+  this.save();
+
+  GroupItems.updateGroupCloseButtons();
+};
+
+// ----------
+GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
+  // ----------
+  // Function: toString
+  // Prints [GroupItem id=id] for debug use
+  toString: function GroupItem_toString() {
+    return "[GroupItem id=" + this.id + "]";
+  },
+
+  // ----------
+  // Variable: defaultName
+  // The prompt text for the title field.
+  defaultName: tabviewString('groupItem.defaultName'),
+
+  // -----------
+  // Function: setActiveTab
+  // Sets the active <TabItem> for this groupItem; can be null, but only
+  // if there are no children.
+  setActiveTab: function GroupItem_setActiveTab(tab) {
+    Utils.assertThrow((!tab && this._children.length == 0) || tab.isATabItem,
+        "tab must be null (if no children) or a TabItem");
+
+    this._activeTab = tab;
+
+    if (this.isStacked())
+      this.arrange({immediately: true});
+  },
+
+  // -----------
+  // Function: getActiveTab
+  // Gets the active <TabItem> for this groupItem; can be null, but only
+  // if there are no children.
+  getActiveTab: function GroupItem_getActiveTab() {
+    return this._activeTab;
+  },
+
+  // ----------
+  // Function: getStorageData
+  // Returns all of the info worth storing about this groupItem.
+  getStorageData: function GroupItem_getStorageData() {
+    var data = {
+      bounds: this.getBounds(),
+      userSize: null,
+      title: this.getTitle(),
+      id: this.id
+    };
+
+    if (Utils.isPoint(this.userSize))
+      data.userSize = new Point(this.userSize);
+
+    return data;
+  },
+
+  // ----------
+  // Function: isEmpty
+  // Returns true if the tab groupItem is empty and unnamed.
+  isEmpty: function GroupItem_isEmpty() {
+    return !this._children.length && !this.getTitle();
+  },
+
+  // ----------
+  // Function: isStacked
+  // Returns true if this item is in a stacked groupItem.
+  isStacked: function GroupItem_isStacked() {
+    return this._isStacked;
+  },
+
+  // ----------
+  // Function: isTopOfStack
+  // Returns true if the item is showing on top of this group's stack,
+  // determined by whether the tab is this group's topChild, or
+  // if it doesn't have one, its first child.
+  isTopOfStack: function GroupItem_isTopOfStack(item) {
+    return this.isStacked() && item == this.getTopChild();
+  },
+
+  // ----------
+  // Function: save
+  // Saves this groupItem to persistent storage.
+  save: function GroupItem_save() {
+    if (!this._inited || this._uninited) // too soon/late to save
+      return;
+
+    var data = this.getStorageData();
+    if (GroupItems.groupItemStorageSanity(data))
+      Storage.saveGroupItem(gWindow, data);
+  },
+
+  // ----------
+  // Function: deleteData
+  // Deletes the groupItem in the persistent storage.
+  deleteData: function GroupItem_deleteData() {
+    this._uninited = true;
+    Storage.deleteGroupItem(gWindow, this.id);
+  },
+
+  // ----------
+  // Function: getTitle
+  // Returns the title of this groupItem as a string.
+  getTitle: function GroupItem_getTitle() {
+    return this.$title ? this.$title.val() : '';
+  },
+
+  // ----------
+  // Function: setTitle
+  // Sets the title of this groupItem with the given string
+  setTitle: function GroupItem_setTitle(value) {
+    this.$title.val(value);
+    this.save();
+  },
+
+  // ----------
+  // Function: focusTitle
+  // Hide the title's shield and focus the underlying input field.
+  focusTitle: function GroupItem_focusTitle() {
+    this.$titleShield.hide();
+    this.$title[0].focus();
+  },
+
+  // ----------
+  // Function: adjustAppTabTray
+  // Used to adjust the appTabTray size, to split the appTabIcons across
+  // multiple columns when needed - if the groupItem size is too small.
+  //
+  // Parameters:
+  //   arrangeGroup - rearrange the groupItem if the number of appTab columns
+  //   changes. If true, then this.arrange() is called, otherwise not.
+  adjustAppTabTray: function GroupItem_adjustAppTabTray(arrangeGroup) {
+    let icons = iQ(".appTabIcon", this.$appTabTray);
+    let container = iQ(this.$appTabTray[0].parentNode);
+    if (!icons.length) {
+      // There are no icons, so hide the appTabTray if needed.
+      if (parseInt(container.css("width")) != 0) {
+        this.$appTabTray.css("-moz-column-count", "auto");
+        this.$appTabTray.css("height", 0);
+        container.css("width", 0);
+        container.css("height", 0);
+
+        if (container.hasClass("appTabTrayContainerTruncated"))
+          container.removeClass("appTabTrayContainerTruncated");
+
+        if (arrangeGroup)
+          this.arrange();
+      }
+      return;
+    }
+
+    let iconBounds = iQ(icons[0]).bounds();
+    let boxBounds = this.getBounds();
+    let contentHeight = boxBounds.height -
+                        parseInt(container.css("top")) -
+                        this.$resizer.height();
+    let rows = Math.floor(contentHeight / iconBounds.height);
+    let columns = Math.ceil(icons.length / rows);
+    let columnsGap = parseInt(this.$appTabTray.css("-moz-column-gap"));
+    let iconWidth = iconBounds.width + columnsGap;
+    let maxColumns = Math.floor((boxBounds.width * 0.20) / iconWidth);
+
+    Utils.assert(rows > 0 && columns > 0 && maxColumns > 0,
+      "make sure the calculated rows, columns and maxColumns are correct");
+
+    if (columns > maxColumns)
+      container.addClass("appTabTrayContainerTruncated");
+    else if (container.hasClass("appTabTrayContainerTruncated"))
+      container.removeClass("appTabTrayContainerTruncated");
+
+    // Need to drop the -moz- prefix when Gecko makes it obsolete.
+    // See bug 629452.
+    if (parseInt(this.$appTabTray.css("-moz-column-count")) != columns)
+      this.$appTabTray.css("-moz-column-count", columns);
+
+    if (parseInt(this.$appTabTray.css("height")) != contentHeight) {
+      this.$appTabTray.css("height", contentHeight + "px");
+      container.css("height", contentHeight + "px");
+    }
+
+    let fullTrayWidth = iconWidth * columns - columnsGap;
+    if (parseInt(this.$appTabTray.css("width")) != fullTrayWidth)
+      this.$appTabTray.css("width", fullTrayWidth + "px");
+
+    let trayWidth = iconWidth * Math.min(columns, maxColumns) - columnsGap;
+    if (parseInt(container.css("width")) != trayWidth) {
+      container.css("width", trayWidth + "px");
+
+      // Rearrange the groupItem if the width changed.
+      if (arrangeGroup)
+        this.arrange();
+    }
+  },
+
+  // ----------
+  // Function: getContentBounds
+  // Returns a <Rect> for the groupItem's content area (which doesn't include the title, etc).
+  //
+  // Parameters:
+  //   options - an object with additional parameters, see below
+  //
+  // Possible options:
+  //   stacked - true to get content bounds for stacked mode
+  getContentBounds: function GroupItem_getContentBounds(options) {
+    let box = this.getBounds();
+    let titleHeight = this.$titlebar.height();
+    box.top += titleHeight;
+    box.height -= titleHeight;
+
+    let appTabTrayContainer = iQ(this.$appTabTray[0].parentNode);
+    let appTabTrayWidth = appTabTrayContainer.width();
+    if (appTabTrayWidth)
+      appTabTrayWidth += parseInt(appTabTrayContainer.css(UI.rtl ? "left" : "right"));
+
+    box.width -= appTabTrayWidth;
+    if (UI.rtl) {
+      box.left += appTabTrayWidth;
+    }
+
+    // Make the computed bounds' "padding" and expand button margin actually be
+    // themeable --OR-- compute this from actual bounds. Bug 586546
+    box.inset(6, 6);
+
+    // make some room for the expand button in stacked mode
+    if (options && options.stacked)
+      box.height -= this.$expander.height() + 9; // the button height plus padding
+
+    return box;
+  },
+
+  // ----------
+  // Function: setBounds
+  // Sets the bounds with the given <Rect>, animating unless "immediately" is false.
+  //
+  // Parameters:
+  //   inRect - a <Rect> giving the new bounds
+  //   immediately - true if it should not animate; default false
+  //   options - an object with additional parameters, see below
+  //
+  // Possible options:
+  //   force - true to always update the DOM even if the bounds haven't changed; default false
+  setBounds: function GroupItem_setBounds(inRect, immediately, options) {
+      Utils.assert(Utils.isRect(inRect), 'GroupItem.setBounds: rect is not a real rectangle!');
+
+    // Validate and conform passed in size
+    let validSize = GroupItems.calcValidSize(
+      new Point(inRect.width, inRect.height));
+    let rect = new Rect(inRect.left, inRect.top, validSize.x, validSize.y);
+
+    if (!options)
+      options = {};
+
+    var titleHeight = this.$titlebar.height();
+
+    // ___ Determine what has changed
+    var css = {};
+    var titlebarCSS = {};
+    var contentCSS = {};
+
+    if (rect.left != this.bounds.left || options.force)
+      css.left = rect.left;
+
+    if (rect.top != this.bounds.top || options.force)
+      css.top = rect.top;
+
+    if (rect.width != this.bounds.width || options.force) {
+      css.width = rect.width;
+      titlebarCSS.width = rect.width;
+      contentCSS.width = rect.width;
+    }
+
+    if (rect.height != this.bounds.height || options.force) {
+      css.height = rect.height;
+      contentCSS.height = rect.height - titleHeight;
+    }
+
+    if (Utils.isEmptyObject(css))
+      return;
+
+    var offset = new Point(rect.left - this.bounds.left, rect.top - this.bounds.top);
+    this.bounds = new Rect(rect);
+
+    // Make sure the AppTab icons fit the new groupItem size.
+    if (css.width || css.height)
+      this.adjustAppTabTray();
+
+    // ___ Deal with children
+    if (css.width || css.height) {
+      this.arrange({animate: !immediately}); //(immediately ? 'sometimes' : true)});
+    } else if (css.left || css.top) {
+      this._children.forEach(function(child) {
+        if (!child.getHidden()) {
+          var box = child.getBounds();
+          child.setPosition(box.left + offset.x, box.top + offset.y, immediately);
+        }
+      });
+    }
+
+    // ___ Update our representation
+    if (immediately) {
+      iQ(this.container).css(css);
+      this.$titlebar.css(titlebarCSS);
+    } else {
+      TabItems.pausePainting();
+      iQ(this.container).animate(css, {
+        duration: 350,
+        easing: "tabviewBounce",
+        complete: function() {
+          TabItems.resumePainting();
+        }
+      });
+
+      this.$titlebar.animate(titlebarCSS, {
+        duration: 350
+      });
+    }
+
+    UI.clearShouldResizeItems();
+    this.setTrenches(rect);
+    this.save();
+  },
+
+  // ----------
+  // Function: setZ
+  // Set the Z order for the groupItem's container, as well as its children.
+  setZ: function GroupItem_setZ(value) {
+    this.zIndex = value;
+
+    iQ(this.container).css({zIndex: value});
+
+    var count = this._children.length;
+    if (count) {
+      var topZIndex = value + count + 1;
+      var zIndex = topZIndex;
+      var self = this;
+      this._children.forEach(function(child) {
+        if (child == self.getTopChild())
+          child.setZ(topZIndex + 1);
+        else {
+          child.setZ(zIndex);
+          zIndex--;
+        }
+      });
+    }
+  },
+
+  // ----------
+  // Function: close
+  // Closes the groupItem, removing (but not closing) all of its children.
+  //
+  // Parameters:
+  //   options - An object with optional settings for this call.
+  //
+  // Options:
+  //   immediately - (bool) if true, no animation will be used
+  close: function GroupItem_close(options) {
+    this.removeAll({dontClose: true});
+    GroupItems.unregister(this);
+
+    // remove unfreeze event handlers, if item size is frozen
+    this._unfreezeItemSize({dontArrange: true});
+
+    let self = this;
+    let destroyGroup = function () {
+      iQ(self.container).remove();
+      if (self.$undoContainer) {
+        self.$undoContainer.remove();
+        self.$undoContainer = null;
+      }
+      self.removeTrenches();
+      Items.unsquish();
+      self._sendToSubscribers("close");
+      GroupItems.updateGroupCloseButtons();
+    }
+
+    if (this.hidden || (options && options.immediately)) {
+      destroyGroup();
+    } else {
+      iQ(this.container).animate({
+        opacity: 0,
+        "-moz-transform": "scale(.3)",
+      }, {
+        duration: 170,
+        complete: destroyGroup
+      });
+    }
+
+    this.deleteData();
+  },
+
+  // ----------
+  // Function: closeAll
+  // Closes the groupItem and all of its children.
+  closeAll: function GroupItem_closeAll() {
+    if (this._children.length > 0) {
+      this._unfreezeItemSize();
+      this._children.forEach(function(child) {
+        iQ(child.container).hide();
+      });
+
+      iQ(this.container).animate({
+         opacity: 0,
+         "-moz-transform": "scale(.3)",
+      }, {
+        duration: 170,
+        complete: function() {
+          iQ(this).hide();
+        }
+      });
+
+      this.droppable(false);
+      this.removeTrenches();
+      this._createUndoButton();
+    } else
+      this.close();
+
+    this._makeLastActiveGroupItemActive();
+  },
+  
+  // ----------
+  // Function: _makeClosestTabActive
+  // Make the closest tab external to this group active.
+  // Used when closing the group.
+  _makeClosestTabActive: function GroupItem__makeClosestTabActive() {
+    let closeCenter = this.getBounds().center();
+    // Find closest tab to make active
+    let closestTabItem = UI.getClosestTab(closeCenter);
+    if (closestTabItem)
+      UI.setActive(closestTabItem);
+  },
+
+  // ----------
+  // Function: _makeLastActiveGroupItemActive
+  // Makes the last active group item active.
+  _makeLastActiveGroupItemActive: function GroupItem__makeLastActiveGroupItemActive() {
+    let groupItem = GroupItems.getLastActiveGroupItem();
+    if (groupItem)
+      UI.setActive(groupItem);
+    else
+      this._makeClosestTabActive();
+  },
+
+  // ----------
+  // Function: closeIfEmpty
+  // Closes the group if it's empty, is closable, and autoclose is enabled
+  // (see pauseAutoclose()). Returns true if the close occurred and false
+  // otherwise.
+  closeIfEmpty: function GroupItem_closeIfEmpty() {
+    if (this.isEmpty() && !UI._closedLastVisibleTab &&
+        !GroupItems.getUnclosableGroupItemId() && !GroupItems._autoclosePaused) {
+      this.close();
+      return true;
+    }
+    return false;
+  },
+
+  // ----------
+  // Function: _unhide
+  // Shows the hidden group.
+  //
+  // Parameters:
+  //   options - various options (see below)
+  //
+  // Possible options:
+  //   immediately - true when no animations should be used
+  _unhide: function GroupItem__unhide(options) {
+    this._cancelFadeAwayUndoButtonTimer();
+    this.hidden = false;
+    this.$undoContainer.remove();
+    this.$undoContainer = null;
+    this.droppable(true);
+    this.setTrenches(this.bounds);
+
+    let self = this;
+
+    let finalize = function () {
+      self._children.forEach(function(child) {
+        iQ(child.container).show();
+      });
+
+      UI.setActive(self);
+      self._sendToSubscribers("groupShown");
+    };
+
+    let $container = iQ(this.container).show();
+
+    if (!options || !options.immediately) {
+      $container.animate({
+        "-moz-transform": "scale(1)",
+        "opacity": 1
+      }, {
+        duration: 170,
+        complete: finalize
+      });
+    } else {
+      $container.css({"-moz-transform": "none", opacity: 1});
+      finalize();
+    }
+
+    GroupItems.updateGroupCloseButtons();
+  },
+
+  // ----------
+  // Function: closeHidden
+  // Removes the group item, its children and its container.
+  closeHidden: function GroupItem_closeHidden() {
+    let self = this;
+
+    this._cancelFadeAwayUndoButtonTimer();
+
+    // When the last non-empty groupItem is closed and there are no
+    // pinned tabs then create a new group with a blank tab.
+    let remainingGroups = GroupItems.groupItems.filter(function (groupItem) {
+      return (groupItem != self && groupItem.getChildren().length);
+    });
+
+    let tab = null;
+
+    if (!gBrowser._numPinnedTabs && !remainingGroups.length) {
+      let emptyGroups = GroupItems.groupItems.filter(function (groupItem) {
+        return (groupItem != self && !groupItem.getChildren().length);
+      });
+      let group = (emptyGroups.length ? emptyGroups[0] : GroupItems.newGroup());
+      tab = group.newTab(null, {dontZoomIn: true});
+    }
+
+    let closed = this.destroy();
+
+    if (!tab)
+      return;
+
+    if (closed) {
+      // Let's make the new tab the selected tab.
+      UI.goToTab(tab);
+    } else {
+      // Remove the new tab and group, if this group is no longer closed.
+      tab._tabViewTabItem.parent.destroy({immediately: true});
+    }
+  },
+
+  // ----------
+  // Function: destroy
+  // Close all tabs linked to children (tabItems), removes all children and 
+  // close the groupItem.
+  //
+  // Parameters:
+  //   options - An object with optional settings for this call.
+  //
+  // Options:
+  //   immediately - (bool) if true, no animation will be used
+  //
+  // Returns true if the groupItem has been closed, or false otherwise. A group
+  // could not have been closed due to a tab with an onUnload handler (that
+  // waits for user interaction).
+  destroy: function GroupItem_destroy(options) {
+    let self = this;
+
+    // when "TabClose" event is fired, the browser tab is about to close and our 
+    // item "close" event is fired.  And then, the browser tab gets closed. 
+    // In other words, the group "close" event is fired before all browser
+    // tabs in the group are closed.  The below code would fire the group "close"
+    // event only after all browser tabs in that group are closed.
+    this._children.concat().forEach(function(child) {
+      child.removeSubscriber("close", self._onChildClose);
+
+      if (child.close(true)) {
+        self.remove(child, { dontArrange: true });
+      } else {
+        // child.removeSubscriber() must be called before child.close(), 
+        // therefore we call child.addSubscriber() if the tab is not removed.
+        child.addSubscriber("close", self._onChildClose);
+      }
+    });
+
+    if (this._children.length) {
+      if (this.hidden)
+        this.$undoContainer.fadeOut(function() { self._unhide() });
+
+      return false;
+    } else {
+      this.close(options);
+      return true;
+    }
+  },
+
+  // ----------
+  // Function: _fadeAwayUndoButton
+  // Fades away the undo button
+  _fadeAwayUndoButton: function GroupItem__fadeAwayUndoButton() {
+    let self = this;
+
+    if (this.$undoContainer) {
+      // if there is more than one group and other groups are not empty,
+      // fade away the undo button.
+      let shouldFadeAway = false;
+
+      if (GroupItems.groupItems.length > 1) {
+        shouldFadeAway = 
+          GroupItems.groupItems.some(function(groupItem) {
+            return (groupItem != self && groupItem.getChildren().length > 0);
+          });
+      }
+
+      if (shouldFadeAway) {
+        self.$undoContainer.animate({
+          color: "transparent",
+          opacity: 0
+        }, {
+          duration: this._fadeAwayUndoButtonDuration,
+          complete: function() { self.closeHidden(); }
+        });
+      }
+    }
+  },
+
+  // ----------
+  // Function: _createUndoButton
+  // Makes the affordance for undo a close group action
+  _createUndoButton: function GroupItem__createUndoButton() {
+    let self = this;
+    this.$undoContainer = iQ("<div/>")
+      .addClass("undo")
+      .attr("type", "button")
+      .attr("data-group-id", this.id)
+      .appendTo("body");
+    iQ("<span/>")
+      .text(tabviewString("groupItem.undoCloseGroup"))
+      .appendTo(this.$undoContainer);
+    let undoClose = iQ("<span/>")
+      .addClass("close")
+      .attr("title", tabviewString("groupItem.discardClosedGroup"))
+      .appendTo(this.$undoContainer);
+
+    this.$undoContainer.css({
+      left: this.bounds.left + this.bounds.width/2 - iQ(self.$undoContainer).width()/2,
+      top:  this.bounds.top + this.bounds.height/2 - iQ(self.$undoContainer).height()/2,
+      "-moz-transform": "scale(.1)",
+      opacity: 0
+    });
+    this.hidden = true;
+
+    // hide group item and show undo container.
+    setTimeout(function() {
+      self.$undoContainer.animate({
+        "-moz-transform": "scale(1)",
+        "opacity": 1
+      }, {
+        easing: "tabviewBounce",
+        duration: 170,
+        complete: function() {
+          self._sendToSubscribers("groupHidden");
+        }
+      });
+    }, 50);
+
+    // add click handlers
+    this.$undoContainer.click(function(e) {
+      // don't do anything if the close button is clicked.
+      if (e.target == undoClose[0])
+        return;
+
+      self.$undoContainer.fadeOut(function() { self._unhide(); });
+    });
+
+    undoClose.click(function() {
+      self.$undoContainer.fadeOut(function() { self.closeHidden(); });
+    });
+
+    this.setupFadeAwayUndoButtonTimer();
+    // Cancel the fadeaway if you move the mouse over the undo
+    // button, and restart the countdown once you move out of it.
+    this.$undoContainer.mouseover(function() { 
+      self._cancelFadeAwayUndoButtonTimer();
+    });
+    this.$undoContainer.mouseout(function() {
+      self.setupFadeAwayUndoButtonTimer();
+    });
+
+    GroupItems.updateGroupCloseButtons();
+  },
+
+  // ----------
+  // Sets up fade away undo button timeout. 
+  setupFadeAwayUndoButtonTimer: function GroupItem_setupFadeAwayUndoButtonTimer() {
+    let self = this;
+
+    if (!this._undoButtonTimeoutId) {
+      this._undoButtonTimeoutId = setTimeout(function() { 
+        self._fadeAwayUndoButton(); 
+      }, this.fadeAwayUndoButtonDelay);
+    }
+  },
+  
+  // ----------
+  // Cancels the fade away undo button timeout. 
+  _cancelFadeAwayUndoButtonTimer: function GroupItem__cancelFadeAwayUndoButtonTimer() {
+    clearTimeout(this._undoButtonTimeoutId);
+    this._undoButtonTimeoutId = null;
+  }, 
+
+  // ----------
+  // Function: add
+  // Adds an item to the groupItem.
+  // Parameters:
+  //
+  //   a - The item to add. Can be an <Item>, a DOM element or an iQ object.
+  //       The latter two must refer to the container of an <Item>.
+  //   options - An object with optional settings for this call.
+  //
+  // Options:
+  //
+  //   index - (int) if set, add this tab at this index
+  //   immediately - (bool) if true, no animation will be used
+  //   dontArrange - (bool) if true, will not trigger an arrange on the group
+  add: function GroupItem_add(a, options) {
+    try {
+      var item;
+      var $el;
+      if (a.isAnItem) {
+        item = a;
+        $el = iQ(a.container);
+      } else {
+        $el = iQ(a);
+        item = Items.item($el);
+      }
+
+      // safeguard to remove the item from its previous group
+      if (item.parent && item.parent !== this)
+        item.parent.remove(item);
+
+      item.removeTrenches();
+
+      if (!options)
+        options = {};
+
+      var self = this;
+
+      var wasAlreadyInThisGroupItem = false;
+      var oldIndex = this._children.indexOf(item);
+      if (oldIndex != -1) {
+        this._children.splice(oldIndex, 1);
+        wasAlreadyInThisGroupItem = true;
+      }
+
+      // Insert the tab into the right position.
+      var index = ("index" in options) ? options.index : this._children.length;
+      this._children.splice(index, 0, item);
+
+      item.setZ(this.getZ() + 1);
+
+      if (!wasAlreadyInThisGroupItem) {
+        item.droppable(false);
+        item.groupItemData = {};
+
+        item.addSubscriber("close", this._onChildClose);
+        item.setParent(this);
+        $el.attr("data-group-id", this.id);
+
+        if (typeof item.setResizable == 'function')
+          item.setResizable(false, options.immediately);
+
+        if (item == UI.getActiveTab() || !this._activeTab)
+          this.setActiveTab(item);
+
+        // if it matches the selected tab or no active tab and the browser
+        // tab is hidden, the active group item would be set.
+        if (item.tab == gBrowser.selectedTab ||
+            (!GroupItems.getActiveGroupItem() && !item.tab.hidden))
+          UI.setActive(this);
+      }
+
+      if (!options.dontArrange)
+        this.arrange({animate: !options.immediately});
+
+      this._unfreezeItemSize({dontArrange: true});
+      this._sendToSubscribers("childAdded", { item: item });
+
+      UI.setReorderTabsOnHide(this);
+    } catch(e) {
+      Utils.log('GroupItem.add error', e);
+    }
+  },
+
+  // ----------
+  // Function: _onChildClose
+  // Handles "close" events from the group's children.
+  //
+  // Parameters:
+  //   tabItem - The tabItem that is closed.
+  _onChildClose: function GroupItem__onChildClose(tabItem) {
+    let count = this._children.length;
+    let dontArrange = tabItem.closedManually &&
+                      (this.expanded || !this.shouldStack(count));
+    let dontClose = !tabItem.closedManually && gBrowser._numPinnedTabs > 0;
+    this.remove(tabItem, {dontArrange: dontArrange, dontClose: dontClose});
+
+    if (dontArrange)
+      this._freezeItemSize(count);
+
+    if (this._children.length > 0 && this._activeTab && tabItem.closedManually)
+      UI.setActive(this);
+  },
+
+  // ----------
+  // Function: remove
+  // Removes an item from the groupItem.
+  // Parameters:
+  //
+  //   a - The item to remove. Can be an <Item>, a DOM element or an iQ object.
+  //       The latter two must refer to the container of an <Item>.
+  //   options - An optional object with settings for this call. See below.
+  //
+  // Possible options: 
+  //   dontArrange - don't rearrange the remaining items
+  //   dontClose - don't close the group even if it normally would
+  //   immediately - don't animate
+  remove: function GroupItem_remove(a, options) {
+    try {
+      let $el;
+      let item;
+
+      if (a.isAnItem) {
+        item = a;
+        $el = iQ(item.container);
+      } else {
+        $el = iQ(a);
+        item = Items.item($el);
+      }
+
+      if (!options)
+        options = {};
+
+      let index = this._children.indexOf(item);
+      if (index != -1)
+        this._children.splice(index, 1);
+
+      if (item == this._activeTab || !this._activeTab) {
+        if (this._children.length > 0)
+          this._activeTab = this._children[0];
+        else
+          this._activeTab = null;
+      }
+
+      $el[0].removeAttribute("data-group-id");
+      item.setParent(null);
+      item.removeClass("stacked");
+      item.isStacked = false;
+      item.setHidden(false);
+      item.removeClass("stack-trayed");
+      item.setRotation(0);
+
+      // Force tabItem resize if it's dragged out of a stacked groupItem.
+      // The tabItems's title will be visible and that's why we need to
+      // recalculate its height.
+      if (item.isDragging && this.isStacked())
+        item.setBounds(item.getBounds(), true, {force: true});
+
+      item.droppable(true);
+      item.removeSubscriber("close", this._onChildClose);
+
+      if (typeof item.setResizable == 'function')
+        item.setResizable(true, options.immediately);
+
+      // if a blank tab is selected while restoring a tab the blank tab gets
+      // removed. we need to keep the group alive for the restored tab.
+      if (item.isRemovedAfterRestore)
+        options.dontClose = true;
+
+      let closed = options.dontClose ? false : this.closeIfEmpty();
+      if (closed ||
+          (this._children.length == 0 && !gBrowser._numPinnedTabs &&
+           !item.isDragging)) {
+        this._makeLastActiveGroupItemActive();
+      } else if (!options.dontArrange) {
+        this.arrange({animate: !options.immediately});
+        this._unfreezeItemSize({dontArrange: true});
+      }
+
+      this._sendToSubscribers("childRemoved", { item: item });
+    } catch(e) {
+      Utils.log(e);
+    }
+  },
+
+  // ----------
+  // Function: removeAll
+  // Removes all of the groupItem's children.
+  // The optional "options" param is passed to each remove call. 
+  removeAll: function GroupItem_removeAll(options) {
+    let self = this;
+    let newOptions = {dontArrange: true};
+    if (options)
+      Utils.extend(newOptions, options);
+      
+    let toRemove = this._children.concat();
+    toRemove.forEach(function(child) {
+      self.remove(child, newOptions);
+    });
+  },
+
+  // ----------
+  // Adds the given xul:tab as an app tab in this group's apptab tray
+  //
+  // Parameters:
+  //   xulTab - the xul:tab.
+  //   options - change how the app tab is added.
+  //
+  // Options:
+  //   position - the position of the app tab should be added to.
+  //   dontAdjustTray - (boolean) if true, do not adjust the tray.
+  addAppTab: function GroupItem_addAppTab(xulTab, options) {
+    GroupItems.getAppTabFavIconUrl(xulTab, function(iconUrl) {
+      // The tab might have been removed or unpinned while waiting.
+      if (!Utils.isValidXULTab(xulTab) || !xulTab.pinned)
+        return;
+
+      let self = this;
+      let $appTab = iQ("<img>")
+        .addClass("appTabIcon")
+        .attr("src", iconUrl)
+        .data("xulTab", xulTab)
+        .mousedown(function GroupItem_addAppTab_onAppTabMousedown(event) {
+          // stop mousedown propagation to disable group dragging on app tabs
+          event.stopPropagation();
+        })
+        .click(function GroupItem_addAppTab_onAppTabClick(event) {
+          if (!Utils.isLeftClick(event))
+            return;
+
+          UI.setActive(self, { dontSetActiveTabInGroup: true });
+          UI.goToTab(iQ(this).data("xulTab"));
+        });
+
+      if (options && "position" in options) {
+        let children = this.$appTabTray[0].childNodes;
+
+        if (options.position >= children.length)
+          $appTab.appendTo(this.$appTabTray);
+        else
+          this.$appTabTray[0].insertBefore($appTab[0], children[options.position]);
+      } else {
+        $appTab.appendTo(this.$appTabTray);
+      }
+      if (!options || !options.dontAdjustTray)
+        this.adjustAppTabTray(true);
+
+      this._sendToSubscribers("appTabIconAdded", { item: $appTab });
+    }.bind(this));
+  },
+
+  // ----------
+  // Removes the given xul:tab as an app tab in this group's apptab tray
+  removeAppTab: function GroupItem_removeAppTab(xulTab) {
+    // remove the icon
+    iQ(".appTabIcon", this.$appTabTray).each(function(icon) {
+      let $icon = iQ(icon);
+      if ($icon.data("xulTab") != xulTab)
+        return true;
+        
+      $icon.remove();
+      return false;
+    });
+    
+    // adjust the tray
+    this.adjustAppTabTray(true);
+  },
+
+  // ----------
+  // Arranges the given xul:tab as an app tab in the group's apptab tray
+  arrangeAppTab: function GroupItem_arrangeAppTab(xulTab) {
+    let self = this;
+
+    let elements = iQ(".appTabIcon", this.$appTabTray);
+    let length = elements.length;
+
+    elements.each(function(icon) {
+      let $icon = iQ(icon);
+      if ($icon.data("xulTab") != xulTab)
+        return true;
+
+      let targetIndex = xulTab._tPos;
+
+      $icon.remove({ preserveEventHandlers: true });
+      if (targetIndex < (length - 1))
+        self.$appTabTray[0].insertBefore(
+          icon,
+          iQ(".appTabIcon:nth-child(" + (targetIndex + 1) + ")", self.$appTabTray)[0]);
+      else
+        $icon.appendTo(self.$appTabTray);
+      return false;
+    });
+  },
+
+  // ----------
+  // Function: hideExpandControl
+  // Hide the control which expands a stacked groupItem into a quick-look view.
+  hideExpandControl: function GroupItem_hideExpandControl() {
+    this.$expander.hide();
+  },
+
+  // ----------
+  // Function: showExpandControl
+  // Show the control which expands a stacked groupItem into a quick-look view.
+  showExpandControl: function GroupItem_showExpandControl() {
+    let parentBB = this.getBounds();
+    let childBB = this.getChild(0).getBounds();
+    this.$expander
+        .show()
+        .css({
+          left: parentBB.width/2 - this.$expander.width()/2
+        });
+  },
+
+  // ----------
+  // Function: shouldStack
+  // Returns true if the groupItem, given "count", should stack (instead of 
+  // grid).
+  shouldStack: function GroupItem_shouldStack(count) {
+    let bb = this.getContentBounds();
+    let options = {
+      return: 'widthAndColumns',
+      count: count || this._children.length,
+      hideTitle: false
+    };
+    let arrObj = Items.arrange(this._children, bb, options);
+
+    let shouldStack = arrObj.childWidth < TabItems.minTabWidth * 1.35;
+    this._columns = shouldStack ? null : arrObj.columns;
+
+    return shouldStack;
+  },
+
+  // ----------
+  // Function: _freezeItemSize
+  // Freezes current item size (when removing a child).
+  //
+  // Parameters:
+  //   itemCount - the number of children before the last one was removed
+  _freezeItemSize: function GroupItem__freezeItemSize(itemCount) {
+    let data = this._frozenItemSizeData;
+
+    if (!data.lastItemCount) {
+      let self = this;
+      data.lastItemCount = itemCount;
+
+      // unfreeze item size when tabview is hidden
+      data.onTabViewHidden = function () self._unfreezeItemSize();
+      window.addEventListener('tabviewhidden', data.onTabViewHidden, false);
+
+      // we don't need to observe mouse movement when expanded because the
+      // tray is closed when we leave it and collapse causes unfreezing
+      if (!self.expanded) {
+        // unfreeze item size when cursor is moved out of group bounds
+        data.onMouseMove = function (e) {
+          let cursor = new Point(e.pageX, e.pageY);
+          if (!self.bounds.contains(cursor))
+            self._unfreezeItemSize();
+        }
+        iQ(window).mousemove(data.onMouseMove);
+      }
+    }
+
+    this.arrange({animate: true, count: data.lastItemCount});
+  },
+
+  // ----------
+  // Function: _unfreezeItemSize
+  // Unfreezes and updates item size.
+  //
+  // Parameters:
+  //   options - various options (see below)
+  //
+  // Possible options:
+  //   dontArrange - do not arrange items when unfreezing
+  _unfreezeItemSize: function GroupItem__unfreezeItemSize(options) {
+    let data = this._frozenItemSizeData;
+    if (!data.lastItemCount)
+      return;
+
+    if (!options || !options.dontArrange)
+      this.arrange({animate: true});
+
+    // unbind event listeners
+    window.removeEventListener('tabviewhidden', data.onTabViewHidden, false);
+    if (data.onMouseMove)
+      iQ(window).unbind('mousemove', data.onMouseMove);
+
+    // reset freeze status
+    this._frozenItemSizeData = {};
+  },
+
+  // ----------
+  // Function: arrange
+  // Lays out all of the children.
+  //
+  // Parameters:
+  //   options - passed to <Items.arrange> or <_stackArrange>, except those below
+  //
+  // Options:
+  //   addTab - (boolean) if true, we add one to the child count
+  //   oldDropIndex - if set, we will only set any bounds if the dropIndex has
+  //                  changed
+  //   dropPos - (<Point>) a position where a tab is currently positioned, above
+  //             this group.
+  //   animate - (boolean) if true, movement of children will be animated.
+  //
+  // Returns:
+  //   dropIndex - an index value for where an item would be dropped, if 
+  //               options.dropPos is given.
+  arrange: function GroupItem_arrange(options) {
+    if (!options)
+      options = {};
+
+    let childrenToArrange = [];
+    this._children.forEach(function(child) {
+      if (child.isDragging)
+        options.addTab = true;
+      else
+        childrenToArrange.push(child);
+    });
+
+    if (GroupItems._arrangePaused) {
+      GroupItems.pushArrange(this, options);
+      return false;
+    }
+
+    let shouldStack = this.shouldStack(childrenToArrange.length + (options.addTab ? 1 : 0));
+    let shouldStackArrange = (shouldStack && !this.expanded);
+    let box;
+
+    // if we should stack and we're not expanded
+    if (shouldStackArrange) {
+      this.showExpandControl();
+      box = this.getContentBounds({stacked: true});
+      this._stackArrange(childrenToArrange, box, options);
+      return false;
+    } else {
+      this.hideExpandControl();
+      box = this.getContentBounds();
+      // a dropIndex is returned
+      return this._gridArrange(childrenToArrange, box, options);
+    }
+  },
+
+  // ----------
+  // Function: _stackArrange
+  // Arranges the children in a stack.
+  //
+  // Parameters:
+  //   childrenToArrange - array of <TabItem> children
+  //   bb - <Rect> to arrange within
+  //   options - see below
+  //
+  // Possible "options" properties:
+  //   animate - whether to animate; default: true.
+  _stackArrange: function GroupItem__stackArrange(childrenToArrange, bb, options) {
+    if (!options)
+      options = {};
+    var animate = "animate" in options ? options.animate : true;
+
+    var count = childrenToArrange.length;
+    if (!count)
+      return;
+
+    let itemAspect = TabItems.tabHeight / TabItems.tabWidth;
+    let zIndex = this.getZ() + count + 1;
+    let maxRotation = 35; // degress
+    let scale = 0.7;
+    let newTabsPad = 10;
+    let bbAspect = bb.height / bb.width;
+    let numInPile = 6;
+    let angleDelta = 3.5; // degrees
+
+    // compute size of the entire stack, modulo rotation.
+    let size;
+    if (bbAspect > itemAspect) { // Tall, thin groupItem
+      size = TabItems.calcValidSize(new Point(bb.width * scale, -1),
+        {hideTitle:true});
+     } else { // Short, wide groupItem
+      size = TabItems.calcValidSize(new Point(-1, bb.height * scale),
+        {hideTitle:true});
+     }
+
+    // x is the left margin that the stack will have, within the content area (bb)
+    // y is the vertical margin
+    var x = (bb.width - size.x) / 2;
+    var y = Math.min(size.x, (bb.height - size.y) / 2);
+    var box = new Rect(bb.left + x, bb.top + y, size.x, size.y);
+
+    var self = this;
+    var children = [];
+
+    // ensure topChild is the first item in childrenToArrange
+    let topChild = this.getTopChild();
+    let topChildPos = childrenToArrange.indexOf(topChild);
+    if (topChildPos > 0) {
+      childrenToArrange.splice(topChildPos, 1);
+      childrenToArrange.unshift(topChild);
+    }
+
+    childrenToArrange.forEach(function GroupItem__stackArrange_order(child) {
+      // Children are still considered stacked even if they're hidden later.
+      child.addClass("stacked");
+      child.isStacked = true;
+      if (numInPile-- > 0) {
+        children.push(child);
+      } else {
+        child.setHidden(true);
+      }
+    });
+
+    self._isStacked = true;
+
+    let angleAccum = 0;
+    children.forEach(function GroupItem__stackArrange_apply(child, index) {
+      child.setZ(zIndex);
+      zIndex--;
+
+      // Force a recalculation of height because we've changed how the title
+      // is shown.
+      child.setBounds(box, !animate || child.getHidden(), {force:true});
+      child.setRotation((UI.rtl ? -1 : 1) * angleAccum);
+      child.setHidden(false);
+      angleAccum += angleDelta;
+    });
+  },
+  
+  // ----------
+  // Function: _gridArrange
+  // Arranges the children into a grid.
+  //
+  // Parameters:
+  //   childrenToArrange - array of <TabItem> children
+  //   box - <Rect> to arrange within
+  //   options - see below
+  //
+  // Possible "options" properties:
+  //   animate - whether to animate; default: true.
+  //   z - (int) a z-index to assign the children
+  //   columns - the number of columns to use in the layout, if known in advance
+  //
+  // Returns:
+  //   dropIndex - (int) the index at which a dragged item (if there is one) should be added
+  //               if it is dropped. Otherwise (boolean) false.
+  _gridArrange: function GroupItem__gridArrange(childrenToArrange, box, options) {
+    let arrangeOptions;
+    if (this.expanded) {
+      // if we're expanded, we actually want to use the expanded tray's bounds.
+      box = new Rect(this.expanded.bounds);
+      box.inset(8, 8);
+      arrangeOptions = Utils.extend({}, options, {z: 99999});
+    } else {
+      this._isStacked = false;
+      arrangeOptions = Utils.extend({}, options, {
+        columns: this._columns
+      });
+
+      childrenToArrange.forEach(function(child) {
+        child.removeClass("stacked");
+        child.isStacked = false;
+        child.setHidden(false);
+      });
+    }
+  
+    if (!childrenToArrange.length)
+      return false;
+
+    // Items.arrange will determine where/how the child items should be
+    // placed, but will *not* actually move them for us. This is our job.
+    let result = Items.arrange(childrenToArrange, box, arrangeOptions);
+    let {dropIndex, rects, columns} = result;
+    if ("oldDropIndex" in options && options.oldDropIndex === dropIndex)
+      return dropIndex;
+
+    this._columns = columns;
+    let index = 0;
+    let self = this;
+    childrenToArrange.forEach(function GroupItem_arrange_children_each(child, i) {
+      // If dropIndex spacing is active and this is a child after index,
+      // bump it up one so we actually use the correct rect
+      // (and skip one for the dropPos)
+      if (self._dropSpaceActive && index === dropIndex)
+        index++;
+      child.setBounds(rects[index], !options.animate);
+      child.setRotation(0);
+      if (arrangeOptions.z)
+        child.setZ(arrangeOptions.z);
+      index++;
+    });
+
+    return dropIndex;
+  },
+
+  expand: function GroupItem_expand() {
+    var self = this;
+    // ___ we're stacked, and command is held down so expand
+    UI.setActive(this.getTopChild());
+    
+    var startBounds = this.getChild(0).getBounds();
+    var $tray = iQ("<div>").css({
+      top: startBounds.top,
+      left: startBounds.left,
+      width: startBounds.width,
+      height: startBounds.height,
+      position: "absolute",
+      zIndex: 99998
+    }).appendTo("body");
+    $tray[0].id = "expandedTray";
+
+    var w = 180;
+    var h = w * (TabItems.tabHeight / TabItems.tabWidth) * 1.1;
+    var padding = 20;
+    var col = Math.ceil(Math.sqrt(this._children.length));
+    var row = Math.ceil(this._children.length/col);
+
+    var overlayWidth = Math.min(window.innerWidth - (padding * 2), w*col + padding*(col+1));
+    var overlayHeight = Math.min(window.innerHeight - (padding * 2), h*row + padding*(row+1));
+
+    var pos = {left: startBounds.left, top: startBounds.top};
+    pos.left -= overlayWidth / 3;
+    pos.top  -= overlayHeight / 3;
+
+    if (pos.top < 0)
+      pos.top = 20;
+    if (pos.left < 0)
+      pos.left = 20;
+    if (pos.top + overlayHeight > window.innerHeight)
+      pos.top = window.innerHeight - overlayHeight - 20;
+    if (pos.left + overlayWidth > window.innerWidth)
+      pos.left = window.innerWidth - overlayWidth - 20;
+
+    $tray
+      .animate({
+        width:  overlayWidth,
+        height: overlayHeight,
+        top: pos.top,
+        left: pos.left
+      }, {
+        duration: 200,
+        easing: "tabviewBounce",
+        complete: function GroupItem_expand_animate_complete() {
+          self._sendToSubscribers("expanded");
+        }
+      })
+      .addClass("overlay");
+
+    this._children.forEach(function(child) {
+      child.addClass("stack-trayed");
+      child.setHidden(false);
+    });
+
+    var $shield = iQ('<div>')
+      .addClass('shield')
+      .css({
+        zIndex: 99997
+      })
+      .appendTo('body')
+      .click(function() { // just in case
+        self.collapse();
+      });
+
+    // There is a race-condition here. If there is
+    // a mouse-move while the shield is coming up
+    // it will collapse, which we don't want. Thus,
+    // we wait a little bit before adding this event
+    // handler.
+    setTimeout(function() {
+      $shield.mouseover(function() {
+        self.collapse();
+      });
+    }, 200);
+
+    this.expanded = {
+      $tray: $tray,
+      $shield: $shield,
+      bounds: new Rect(pos.left, pos.top, overlayWidth, overlayHeight)
+    };
+
+    this.arrange();
+  },
+
+  // ----------
+  // Function: collapse
+  // Collapses the groupItem from the expanded "tray" mode.
+  collapse: function GroupItem_collapse() {
+    if (this.expanded) {
+      var z = this.getZ();
+      var box = this.getBounds();
+      let self = this;
+      this.expanded.$tray
+        .css({
+          zIndex: z + 1
+        })
+        .animate({
+          width:  box.width,
+          height: box.height,
+          top: box.top,
+          left: box.left,
+          opacity: 0
+        }, {
+          duration: 350,
+          easing: "tabviewBounce",
+          complete: function GroupItem_collapse_animate_complete() {
+            iQ(this).remove();
+            self._sendToSubscribers("collapsed");
+          }
+        });
+
+      this.expanded.$shield.remove();
+      this.expanded = null;
+
+      this._children.forEach(function(child) {
+        child.removeClass("stack-trayed");
+      });
+
+      this.arrange({z: z + 2});
+      this._unfreezeItemSize({dontArrange: true});
+    }
+  },
+
+  // ----------
+  // Function: _addHandlers
+  // Helper routine for the constructor; adds various event handlers to the container.
+  _addHandlers: function GroupItem__addHandlers(container) {
+    let self = this;
+    let lastMouseDownTarget;
+
+    container.mousedown(function(e) {
+      let target = e.target;
+      // only set the last mouse down target if it is a left click, not on the
+      // close button, not on the expand button, not on the title bar and its
+      // elements
+      if (Utils.isLeftClick(e) &&
+          self.$closeButton[0] != target &&
+          self.$titlebar[0] != target &&
+          self.$expander[0] != target &&
+          !self.$titlebar.contains(target) &&
+          !self.$appTabTray.contains(target)) {
+        lastMouseDownTarget = target;
+      } else {
+        lastMouseDownTarget = null;
+      }
+    });
+    container.mouseup(function(e) {
+      let same = (e.target == lastMouseDownTarget);
+      lastMouseDownTarget = null;
+
+      if (same && !self.isDragging) {
+        if (gBrowser.selectedTab.pinned &&
+            UI.getActiveTab() != self.getActiveTab() &&
+            self.getChildren().length > 0) {
+          UI.setActive(self, { dontSetActiveTabInGroup: true });
+          UI.goToTab(gBrowser.selectedTab);
+        } else {
+          let tabItem = self.getTopChild();
+          if (tabItem)
+            tabItem.zoomIn();
+          else
+            self.newTab();
+        }
+      }
+    });
+
+    let dropIndex = false;
+    let dropSpaceTimer = null;
+
+    // When the _dropSpaceActive flag is turned on on a group, and a tab is
+    // dragged on top, a space will open up.
+    this._dropSpaceActive = false;
+
+    this.dropOptions.over = function GroupItem_dropOptions_over(event) {
+      iQ(this.container).addClass("acceptsDrop");
+    };
+    this.dropOptions.move = function GroupItem_dropOptions_move(event) {
+      let oldDropIndex = dropIndex;
+      let dropPos = drag.info.item.getBounds().center();
+      let options = {dropPos: dropPos,
+                     addTab: self._dropSpaceActive && drag.info.item.parent != self,
+                     oldDropIndex: oldDropIndex};
+      let newDropIndex = self.arrange(options);
+      // If this is a new drop index, start a timer!
+      if (newDropIndex !== oldDropIndex) {
+        dropIndex = newDropIndex;
+        if (this._dropSpaceActive)
+          return;
+          
+        if (dropSpaceTimer) {
+          clearTimeout(dropSpaceTimer);
+          dropSpaceTimer = null;
+        }
+
+        dropSpaceTimer = setTimeout(function GroupItem_arrange_evaluateDropSpace() {
+          // Note that dropIndex's scope is GroupItem__addHandlers, but
+          // newDropIndex's scope is GroupItem_dropOptions_move. Thus,
+          // dropIndex may change with other movement events before we come
+          // back and check this. If it's still the same dropIndex, activate
+          // drop space display!
+          if (dropIndex === newDropIndex) {
+            self._dropSpaceActive = true;
+            dropIndex = self.arrange({dropPos: dropPos,
+                                      addTab: drag.info.item.parent != self,
+                                      animate: true});
+          }
+          dropSpaceTimer = null;
+        }, 250);
+      }
+    };
+    this.dropOptions.drop = function GroupItem_dropOptions_drop(event) {
+      iQ(this.container).removeClass("acceptsDrop");
+      let options = {};
+      if (this._dropSpaceActive)
+        this._dropSpaceActive = false;
+
+      if (dropSpaceTimer) {
+        clearTimeout(dropSpaceTimer);
+        dropSpaceTimer = null;
+        // If we drop this item before the timed rearrange was executed,
+        // we won't have an accurate dropIndex value. Get that now.
+        let dropPos = drag.info.item.getBounds().center();
+        dropIndex = self.arrange({dropPos: dropPos,
+                                  addTab: drag.info.item.parent != self,
+                                  animate: true});
+      }
+
+      if (dropIndex !== false)
+        options = {index: dropIndex};
+      this.add(drag.info.$el, options);
+      UI.setActive(this);
+      dropIndex = false;
+    };
+    this.dropOptions.out = function GroupItem_dropOptions_out(event) {
+      dropIndex = false;
+      if (this._dropSpaceActive)
+        this._dropSpaceActive = false;
+
+      if (dropSpaceTimer) {
+        clearTimeout(dropSpaceTimer);
+        dropSpaceTimer = null;
+      }
+      self.arrange();
+      var groupItem = drag.info.item.parent;
+      if (groupItem)
+        groupItem.remove(drag.info.$el, {dontClose: true});
+      iQ(this.container).removeClass("acceptsDrop");
+    }
+
+    this.draggable();
+    this.droppable(true);
+
+    this.$expander.click(function() {
+      self.expand();
+    });
+  },
+
+  // ----------
+  // Function: setResizable
+  // Sets whether the groupItem is resizable and updates the UI accordingly.
+  setResizable: function GroupItem_setResizable(value, immediately) {
+    var self = this;
+
+    this.resizeOptions.minWidth = GroupItems.minGroupWidth;
+    this.resizeOptions.minHeight = GroupItems.minGroupHeight;
+
+    let start = this.resizeOptions.start;
+    this.resizeOptions.start = function (event) {
+      start.call(self, event);
+      self._unfreezeItemSize();
+    }
+
+    if (value) {
+      immediately ? this.$resizer.show() : this.$resizer.fadeIn();
+      this.resizable(true);
+    } else {
+      immediately ? this.$resizer.hide() : this.$resizer.fadeOut();
+      this.resizable(false);
+    }
+  },
+
+  // ----------
+  // Function: newTab
+  // Creates a new tab within this groupItem.
+  // Parameters:
+  //  url - the new tab should open this url as well
+  //  options - the options object
+  //    dontZoomIn - set to true to not zoom into the newly created tab
+  //    closedLastTab - boolean indicates the last tab has just been closed
+  newTab: function GroupItem_newTab(url, options) {
+    if (options && options.closedLastTab)
+      UI.closedLastTabInTabView = true;
+
+    UI.setActive(this, { dontSetActiveTabInGroup: true });
+
+    let dontZoomIn = !!(options && options.dontZoomIn);
+    return gBrowser.loadOneTab(url || gWindow.BROWSER_NEW_TAB_URL, { inBackground: dontZoomIn });
+  },
+
+  // ----------
+  // Function: reorderTabItemsBasedOnTabOrder
+  // Reorders the tabs in a groupItem based on the arrangment of the tabs
+  // shown in the tab bar. It does it by sorting the children
+  // of the groupItem by the positions of their respective tabs in the
+  // tab bar.
+  reorderTabItemsBasedOnTabOrder: function GroupItem_reorderTabItemsBasedOnTabOrder() {
+    this._children.sort(function(a,b) a.tab._tPos - b.tab._tPos);
+
+    this.arrange({animate: false});
+    // this.arrange calls this.save for us
+  },
+
+  // Function: reorderTabsBasedOnTabItemOrder
+  // Reorders the tabs in the tab bar based on the arrangment of the tabs
+  // shown in the groupItem.
+  reorderTabsBasedOnTabItemOrder: function GroupItem_reorderTabsBasedOnTabItemOrder() {
+    let indices;
+    let tabs = this._children.map(function (tabItem) tabItem.tab);
+
+    tabs.forEach(function (tab, index) {
+      if (!indices)
+        indices = tabs.map(function (tab) tab._tPos);
+
+      let start = index ? indices[index - 1] + 1 : 0;
+      let end = index + 1 < indices.length ? indices[index + 1] - 1 : Infinity;
+      let targetRange = new Range(start, end);
+
+      if (!targetRange.contains(tab._tPos)) {
+        gBrowser.moveTabTo(tab, start);
+        indices = null;
+      }
+    });
+  },
+
+  // ----------
+  // Function: getTopChild
+  // Gets the <Item> that should be displayed on top when in stack mode.
+  getTopChild: function GroupItem_getTopChild() {
+    if (!this.getChildren().length) {
+      return null;
+    }
+
+    return this.getActiveTab() || this.getChild(0);
+  },
+
+  // ----------
+  // Function: getChild
+  // Returns the nth child tab or null if index is out of range.
+  //
+  // Parameters:
+  //  index - the index of the child tab to return, use negative
+  //          numbers to index from the end (-1 is the last child)
+  getChild: function GroupItem_getChild(index) {
+    if (index < 0)
+      index = this._children.length + index;
+    if (index >= this._children.length || index < 0)
+      return null;
+    return this._children[index];
+  },
+
+  // ----------
+  // Function: getChildren
+  // Returns all children.
+  getChildren: function GroupItem_getChildren() {
+    return this._children;
+  }
+});
+
+// ##########
+// Class: GroupItems
+// Singleton for managing all <GroupItem>s.
+let GroupItems = {
+  groupItems: [],
+  nextID: 1,
+  _inited: false,
+  _activeGroupItem: null,
+  _cleanupFunctions: [],
+  _arrangePaused: false,
+  _arrangesPending: [],
+  _removingHiddenGroups: false,
+  _delayedModUpdates: [],
+  _autoclosePaused: false,
+  minGroupHeight: 110,
+  minGroupWidth: 125,
+  _lastActiveList: null,
+
+  // ----------
+  // Function: toString
+  // Prints [GroupItems] for debug use
+  toString: function GroupItems_toString() {
+    return "[GroupItems count=" + this.groupItems.length + "]";
+  },
+
+  // ----------
+  // Function: init
+  init: function GroupItems_init() {
+    let self = this;
+
+    // setup attr modified handler, and prepare for its uninit
+    function handleAttrModified(event) {
+      self._handleAttrModified(event.target);
+    }
+
+    // make sure any closed tabs are removed from the delay update list
+    function handleClose(event) {
+      let idx = self._delayedModUpdates.indexOf(event.target);
+      if (idx != -1)
+        self._delayedModUpdates.splice(idx, 1);
+    }
+
+    this._lastActiveList = new MRUList();
+
+    AllTabs.register("attrModified", handleAttrModified);
+    AllTabs.register("close", handleClose);
+    this._cleanupFunctions.push(function() {
+      AllTabs.unregister("attrModified", handleAttrModified);
+      AllTabs.unregister("close", handleClose);
+    });
+  },
+
+  // ----------
+  // Function: uninit
+  uninit: function GroupItems_uninit() {
+    // call our cleanup functions
+    this._cleanupFunctions.forEach(function(func) {
+      func();
+    });
+
+    this._cleanupFunctions = [];
+
+    // additional clean up
+    this.groupItems = null;
+  },
+
+  // ----------
+  // Function: newGroup
+  // Creates a new empty group.
+  newGroup: function GroupItems_newGroup() {
+    let bounds = new Rect(20, 20, 250, 200);
+    return new GroupItem([], {bounds: bounds, immediately: true});
+  },
+
+  // ----------
+  // Function: pauseArrange
+  // Bypass arrange() calls and collect for resolution in
+  // resumeArrange()
+  pauseArrange: function GroupItems_pauseArrange() {
+    Utils.assert(this._arrangePaused == false, 
+      "pauseArrange has been called while already paused");
+    Utils.assert(this._arrangesPending.length == 0, 
+      "There are bypassed arrange() calls that haven't been resolved");
+    this._arrangePaused = true;
+  },
+
+  // ----------
+  // Function: pushArrange
+  // Push an arrange() call and its arguments onto an array
+  // to be resolved in resumeArrange()
+  pushArrange: function GroupItems_pushArrange(groupItem, options) {
+    Utils.assert(this._arrangePaused, 
+      "Ensure pushArrange() called while arrange()s aren't paused"); 
+    let i;
+    for (i = 0; i < this._arrangesPending.length; i++)
+      if (this._arrangesPending[i].groupItem === groupItem)
+        break;
+    let arrangeInfo = {
+      groupItem: groupItem,
+      options: options
+    };
+    if (i < this._arrangesPending.length)
+      this._arrangesPending[i] = arrangeInfo;
+    else
+      this._arrangesPending.push(arrangeInfo);
+  },
+
+  // ----------
+  // Function: resumeArrange
+  // Resolve bypassed and collected arrange() calls
+  resumeArrange: function GroupItems_resumeArrange() {
+    this._arrangePaused = false;
+    for (let i = 0; i < this._arrangesPending.length; i++) {
+      let g = this._arrangesPending[i];
+      g.groupItem.arrange(g.options);
+    }
+    this._arrangesPending = [];
+  },
+
+  // ----------
+  // Function: _handleAttrModified
+  // watch for icon changes on app tabs
+  _handleAttrModified: function GroupItems__handleAttrModified(xulTab) {
+    if (!UI.isTabViewVisible()) {
+      if (this._delayedModUpdates.indexOf(xulTab) == -1) {
+        this._delayedModUpdates.push(xulTab);
+      }
+    } else
+      this._updateAppTabIcons(xulTab); 
+  },
+
+  // ----------
+  // Function: flushTabUpdates
+  // Update apptab icons based on xulTabs which have been updated
+  // while the TabView hasn't been visible 
+  flushAppTabUpdates: function GroupItems_flushAppTabUpdates() {
+    let self = this;
+    this._delayedModUpdates.forEach(function(xulTab) {
+      self._updateAppTabIcons(xulTab);
+    });
+    this._delayedModUpdates = [];
+  },
+
+  // ----------
+  // Function: _updateAppTabIcons
+  // Update images of any apptab icons that point to passed in xultab 
+  _updateAppTabIcons: function GroupItems__updateAppTabIcons(xulTab) {
+    if (!xulTab.pinned)
+      return;
+
+    this.getAppTabFavIconUrl(xulTab, function(iconUrl) {
+      iQ(".appTabIcon").each(function GroupItems__updateAppTabIcons_forEach(icon) {
+         let $icon = iQ(icon);
+         if ($icon.data("xulTab") == xulTab && iconUrl != $icon.attr("src"))
+           $icon.attr("src", iconUrl);
+      });
+    });
+  },
+
+  // ----------
+  // Function: getAppTabFavIconUrl
+  // Gets the fav icon url for app tab.
+  getAppTabFavIconUrl: function GroupItems_getAppTabFavIconUrl(xulTab, callback) {
+    FavIcons.getFavIconUrlForTab(xulTab, function GroupItems_getAppTabFavIconUrl_getFavIconUrlForTab(iconUrl) {
+      callback(iconUrl || FavIcons.defaultFavicon);
+    });
+  },
+
+  // ----------
+  // Function: addAppTab
+  // Adds the given xul:tab to the app tab tray in all groups
+  addAppTab: function GroupItems_addAppTab(xulTab) {
+    this.groupItems.forEach(function(groupItem) {
+      groupItem.addAppTab(xulTab);
+    });
+    this.updateGroupCloseButtons();
+  },
+
+  // ----------
+  // Function: removeAppTab
+  // Removes the given xul:tab from the app tab tray in all groups
+  removeAppTab: function GroupItems_removeAppTab(xulTab) {
+    this.groupItems.forEach(function(groupItem) {
+      groupItem.removeAppTab(xulTab);
+    });
+    this.updateGroupCloseButtons();
+  },
+
+  // ----------
+  // Function: arrangeAppTab
+  // Arranges the given xul:tab as an app tab from app tab tray in all groups
+  arrangeAppTab: function GroupItems_arrangeAppTab(xulTab) {
+    this.groupItems.forEach(function(groupItem) {
+      groupItem.arrangeAppTab(xulTab);
+    });
+  },
+
+  // ----------
+  // Function: getNextID
+  // Returns the next unused groupItem ID.
+  getNextID: function GroupItems_getNextID() {
+    var result = this.nextID;
+    this.nextID++;
+    this._save();
+    return result;
+  },
+
+  // ----------
+  // Function: getStorageData
+  // Returns an object for saving GroupItems state to persistent storage.
+  getStorageData: function GroupItems_getStorageData() {
+    var data = {nextID: this.nextID, groupItems: []};
+    this.groupItems.forEach(function(groupItem) {
+      data.groupItems.push(groupItem.getStorageData());
+    });
+
+    return data;
+  },
+
+  // ----------
+  // Function: saveAll
+  // Saves GroupItems state, as well as the state of all of the groupItems.
+  saveAll: function GroupItems_saveAll() {
+    this._save();
+    this.groupItems.forEach(function(groupItem) {
+      groupItem.save();
+    });
+  },
+
+  // ----------
+  // Function: _save
+  // Saves GroupItems state.
+  _save: function GroupItems__save() {
+    if (!this._inited) // too soon to save now
+      return;
+
+    let activeGroupId = this._activeGroupItem ? this._activeGroupItem.id : null;
+    Storage.saveGroupItemsData(
+      gWindow,
+      { nextID: this.nextID, activeGroupId: activeGroupId,
+        totalNumber: this.groupItems.length });
+  },
+
+  // ----------
+  // Function: getBoundingBox
+  // Given an array of DOM elements, returns a <Rect> with (roughly) the union of their locations.
+  getBoundingBox: function GroupItems_getBoundingBox(els) {
+    var bounds = [iQ(el).bounds() for each (el in els)];
+    var left   = Math.min.apply({},[ b.left   for each (b in bounds) ]);
+    var top    = Math.min.apply({},[ b.top    for each (b in bounds) ]);
+    var right  = Math.max.apply({},[ b.right  for each (b in bounds) ]);
+    var bottom = Math.max.apply({},[ b.bottom for each (b in bounds) ]);
+
+    return new Rect(left, top, right-left, bottom-top);
+  },
+
+  // ----------
+  // Function: reconstitute
+  // Restores to stored state, creating groupItems as needed.
+  reconstitute: function GroupItems_reconstitute(groupItemsData, groupItemData) {
+    try {
+      let activeGroupId;
+
+      if (groupItemsData) {
+        if (groupItemsData.nextID)
+          this.nextID = Math.max(this.nextID, groupItemsData.nextID);
+        if (groupItemsData.activeGroupId)
+          activeGroupId = groupItemsData.activeGroupId;
+      }
+
+      if (groupItemData) {
+        var toClose = this.groupItems.concat();
+        for (var id in groupItemData) {
+          let data = groupItemData[id];
+          if (this.groupItemStorageSanity(data)) {
+            let groupItem = this.groupItem(data.id); 
+            if (groupItem && !groupItem.hidden) {
+              groupItem.userSize = data.userSize;
+              groupItem.setTitle(data.title);
+              groupItem.setBounds(data.bounds, true);
+              
+              let index = toClose.indexOf(groupItem);
+              if (index != -1)
+                toClose.splice(index, 1);
+            } else {
+              var options = {
+                dontPush: true,
+                immediately: true
+              };
+  
+              new GroupItem([], Utils.extend({}, data, options));
+            }
+          }
+        }
+
+        toClose.forEach(function(groupItem) {
+          // all tabs still existing in closed groups will be moved to new
+          // groups. prepare them to be reconnected later.
+          groupItem.getChildren().forEach(function (tabItem) {
+            if (tabItem.parent.hidden)
+              iQ(tabItem.container).show();
+
+            tabItem._reconnected = false;
+
+            // sanity check the tab's groupID
+            let tabData = Storage.getTabData(tabItem.tab);
+
+            if (tabData) {
+              let parentGroup = GroupItems.groupItem(tabData.groupID);
+
+              // the tab's group id could be invalid or point to a non-existing
+              // group. correct it by assigning the active group id or the first
+              // group of the just restored session.
+              if (!parentGroup || -1 < toClose.indexOf(parentGroup)) {
+                tabData.groupID = activeGroupId || Object.keys(groupItemData)[0];
+                Storage.saveTab(tabItem.tab, tabData);
+              }
+            }
+          });
+
+          // this closes the group but not its children
+          groupItem.close({immediately: true});
+        });
+      }
+
+      // set active group item
+      if (activeGroupId) {
+        let activeGroupItem = this.groupItem(activeGroupId);
+        if (activeGroupItem)
+          UI.setActive(activeGroupItem);
+      }
+
+      this._inited = true;
+      this._save(); // for nextID
+    } catch(e) {
+      Utils.log("error in recons: "+e);
+    }
+  },
+
+  // ----------
+  // Function: load
+  // Loads the storage data for groups. 
+  // Returns true if there was global group data.
+  load: function GroupItems_load() {
+    let groupItemsData = Storage.readGroupItemsData(gWindow);
+    let groupItemData = Storage.readGroupItemData(gWindow);
+    this.reconstitute(groupItemsData, groupItemData);
+    
+    return (groupItemsData && !Utils.isEmptyObject(groupItemsData));
+  },
+
+  // ----------
+  // Function: groupItemStorageSanity
+  // Given persistent storage data for a groupItem, returns true if it appears to not be damaged.
+  groupItemStorageSanity: function GroupItems_groupItemStorageSanity(groupItemData) {
+    let sane = true;
+    if (!groupItemData.bounds || !Utils.isRect(groupItemData.bounds)) {
+      Utils.log('GroupItems.groupItemStorageSanity: bad bounds', groupItemData.bounds);
+      sane = false;
+    } else if ((groupItemData.userSize && 
+               !Utils.isPoint(groupItemData.userSize)) ||
+               !groupItemData.id) {
+      sane = false;
+    }
+
+    return sane;
+  },
+
+  // ----------
+  // Function: register
+  // Adds the given <GroupItem> to the list of groupItems we're tracking.
+  register: function GroupItems_register(groupItem) {
+    Utils.assert(groupItem, 'groupItem');
+    Utils.assert(this.groupItems.indexOf(groupItem) == -1, 'only register once per groupItem');
+    this.groupItems.push(groupItem);
+    UI.updateTabButton();
+  },
+
+  // ----------
+  // Function: unregister
+  // Removes the given <GroupItem> from the list of groupItems we're tracking.
+  unregister: function GroupItems_unregister(groupItem) {
+    var index = this.groupItems.indexOf(groupItem);
+    if (index != -1)
+      this.groupItems.splice(index, 1);
+
+    if (groupItem == this._activeGroupItem)
+      this._activeGroupItem = null;
+
+    this._arrangesPending = this._arrangesPending.filter(function (pending) {
+      return groupItem != pending.groupItem;
+    });
+
+    this._lastActiveList.remove(groupItem);
+    UI.updateTabButton();
+  },
+
+  // ----------
+  // Function: groupItem
+  // Given some sort of identifier, returns the appropriate groupItem.
+  // Currently only supports groupItem ids.
+  groupItem: function GroupItems_groupItem(a) {
+    var result = null;
+    this.groupItems.forEach(function(candidate) {
+      if (candidate.id == a)
+        result = candidate;
+    });
+
+    return result;
+  },
+
+  // ----------
+  // Function: removeAll
+  // Removes all tabs from all groupItems (which automatically closes all unnamed groupItems).
+  removeAll: function GroupItems_removeAll() {
+    var toRemove = this.groupItems.concat();
+    toRemove.forEach(function(groupItem) {
+      groupItem.removeAll();
+    });
+  },
+
+  // ----------
+  // Function: newTab
+  // Given a <TabItem>, files it in the appropriate groupItem.
+  newTab: function GroupItems_newTab(tabItem, options) {
+    let activeGroupItem = this.getActiveGroupItem();
+
+    // 1. Active group
+    // 2. First visible non-app tab (that's not the tab in question)
+    // 3. First group
+    // 4. At this point there should be no groups or tabs (except for app tabs and the
+    // tab in question): make a new group
+
+    if (activeGroupItem && !activeGroupItem.hidden) {
+      activeGroupItem.add(tabItem, options);
+      return;
+    }
+
+    let targetGroupItem;
+    // find first non-app visible tab belongs a group, and add the new tabItem
+    // to that group
+    gBrowser.visibleTabs.some(function(tab) {
+      if (!tab.pinned && tab != tabItem.tab) {
+        if (tab._tabViewTabItem && tab._tabViewTabItem.parent &&
+            !tab._tabViewTabItem.parent.hidden) {
+          targetGroupItem = tab._tabViewTabItem.parent;
+        }
+        return true;
+      }
+      return false;
+    });
+
+    let visibleGroupItems;
+    if (targetGroupItem) {
+      // add the new tabItem to the first group item
+      targetGroupItem.add(tabItem);
+      UI.setActive(targetGroupItem);
+      return;
+    } else {
+      // find the first visible group item
+      visibleGroupItems = this.groupItems.filter(function(groupItem) {
+        return (!groupItem.hidden);
+      });
+      if (visibleGroupItems.length > 0) {
+        visibleGroupItems[0].add(tabItem);
+        UI.setActive(visibleGroupItems[0]);
+        return;
+      }
+    }
+
+    // create new group for the new tabItem
+    tabItem.setPosition(60, 60, true);
+    let newGroupItemBounds = tabItem.getBounds();
+
+    newGroupItemBounds.inset(-40,-40);
+    let newGroupItem = new GroupItem([tabItem], { bounds: newGroupItemBounds });
+    newGroupItem.snap();
+    UI.setActive(newGroupItem);
+  },
+
+  // ----------
+  // Function: getActiveGroupItem
+  // Returns the active groupItem. Active means its tabs are
+  // shown in the tab bar when not in the TabView interface.
+  getActiveGroupItem: function GroupItems_getActiveGroupItem() {
+    return this._activeGroupItem;
+  },
+
+  // ----------
+  // Function: setActiveGroupItem
+  // Sets the active groupItem, thereby showing only the relevant tabs and
+  // setting the groupItem which will receive new tabs.
+  //
+  // Paramaters:
+  //  groupItem - the active <GroupItem>
+  setActiveGroupItem: function GroupItems_setActiveGroupItem(groupItem) {
+    Utils.assert(groupItem, "groupItem must be given");
+
+    if (this._activeGroupItem)
+      iQ(this._activeGroupItem.container).removeClass('activeGroupItem');
+
+    iQ(groupItem.container).addClass('activeGroupItem');
+
+    this._lastActiveList.update(groupItem);
+    this._activeGroupItem = groupItem;
+    this._save();
+  },
+
+  // ----------
+  // Function: getLastActiveGroupItem
+  // Gets last active group item.
+  // Returns the <groupItem>. If nothing is found, return null.
+  getLastActiveGroupItem: function GroupItem_getLastActiveGroupItem() {
+    return this._lastActiveList.peek(function(groupItem) {
+      return (groupItem && !groupItem.hidden && groupItem.getChildren().length > 0)
+    });
+  },
+
+  // ----------
+  // Function: _updateTabBar
+  // Hides and shows tabs in the tab bar based on the active groupItem
+  _updateTabBar: function GroupItems__updateTabBar() {
+    if (!window.UI)
+      return; // called too soon
+
+    Utils.assert(this._activeGroupItem, "There must be something to show in the tab bar!");
+
+    let tabItems = this._activeGroupItem._children;
+    gBrowser.showOnlyTheseTabs(tabItems.map(function(item) item.tab));
+  },
+
+  // ----------
+  // Function: updateActiveGroupItemAndTabBar
+  // Sets active TabItem and GroupItem, and updates tab bar appropriately.
+  // Parameters:
+  // tabItem - the tab item
+  // options - is passed to UI.setActive() directly
+  updateActiveGroupItemAndTabBar: 
+    function GroupItems_updateActiveGroupItemAndTabBar(tabItem, options) {
+    Utils.assertThrow(tabItem && tabItem.isATabItem, "tabItem must be a TabItem");
+
+    UI.setActive(tabItem, options);
+    this._updateTabBar();
+  },
+
+  // ----------
+  // Function: getNextGroupItemTab
+  // Paramaters:
+  //  reverse - the boolean indicates the direction to look for the next groupItem.
+  // Returns the <tabItem>. If nothing is found, return null.
+  getNextGroupItemTab: function GroupItems_getNextGroupItemTab(reverse) {
+    var groupItems = Utils.copy(GroupItems.groupItems);
+    var activeGroupItem = GroupItems.getActiveGroupItem();
+    var tabItem = null;
+
+    if (reverse)
+      groupItems = groupItems.reverse();
+
+    if (!activeGroupItem) {
+      if (groupItems.length > 0) {
+        groupItems.some(function(groupItem) {
+          if (!groupItem.hidden) {
+            // restore the last active tab in the group
+            let activeTab = groupItem.getActiveTab();
+            if (activeTab) {
+              tabItem = activeTab;
+              return true;
+            }
+            // if no tab is active, use the first one
+            var child = groupItem.getChild(0);
+            if (child) {
+              tabItem = child;
+              return true;
+            }
+          }
+          return false;
+        });
+      }
+    } else {
+      var currentIndex;
+      groupItems.some(function(groupItem, index) {
+        if (!groupItem.hidden && groupItem == activeGroupItem) {
+          currentIndex = index;
+          return true;
+        }
+        return false;
+      });
+      var firstGroupItems = groupItems.slice(currentIndex + 1);
+      firstGroupItems.some(function(groupItem) {
+        if (!groupItem.hidden) {
+          // restore the last active tab in the group
+          let activeTab = groupItem.getActiveTab();
+          if (activeTab) {
+            tabItem = activeTab;
+            return true;
+          }
+          // if no tab is active, use the first one
+          var child = groupItem.getChild(0);
+          if (child) {
+            tabItem = child;
+            return true;
+          }
+        }
+        return false;
+      });
+      if (!tabItem) {
+        var secondGroupItems = groupItems.slice(0, currentIndex);
+        secondGroupItems.some(function(groupItem) {
+          if (!groupItem.hidden) {
+            // restore the last active tab in the group
+            let activeTab = groupItem.getActiveTab();
+            if (activeTab) {
+              tabItem = activeTab;
+              return true;
+            }
+            // if no tab is active, use the first one
+            var child = groupItem.getChild(0);
+            if (child) {
+              tabItem = child;
+              return true;
+            }
+          }
+          return false;
+        });
+      }
+    }
+    return tabItem;
+  },
+
+  // ----------
+  // Function: moveTabToGroupItem
+  // Used for the right click menu in the tab strip; moves the given tab
+  // into the given group. Does nothing if the tab is an app tab.
+  // Paramaters:
+  //  tab - the <xul:tab>.
+  //  groupItemId - the <groupItem>'s id.  If nothing, create a new <groupItem>.
+  moveTabToGroupItem : function GroupItems_moveTabToGroupItem(tab, groupItemId) {
+    if (tab.pinned)
+      return;
+
+    Utils.assertThrow(tab._tabViewTabItem, "tab must be linked to a TabItem");
+
+    // given tab is already contained in target group
+    if (tab._tabViewTabItem.parent && tab._tabViewTabItem.parent.id == groupItemId)
+      return;
+
+    let shouldUpdateTabBar = false;
+    let shouldShowTabView = false;
+    let groupItem;
+
+    // switch to the appropriate tab first.
+    if (gBrowser.selectedTab == tab) {
+      if (gBrowser.visibleTabs.length > 1) {
+        gBrowser._blurTab(tab);
+        shouldUpdateTabBar = true;
+      } else {
+        shouldShowTabView = true;
+      }
+    } else {
+      shouldUpdateTabBar = true
+    }
+
+    // remove tab item from a groupItem
+    if (tab._tabViewTabItem.parent)
+      tab._tabViewTabItem.parent.remove(tab._tabViewTabItem);
+
+    // add tab item to a groupItem
+    if (groupItemId) {
+      groupItem = GroupItems.groupItem(groupItemId);
+      groupItem.add(tab._tabViewTabItem);
+      groupItem.reorderTabsBasedOnTabItemOrder()
+    } else {
+      let pageBounds = Items.getPageBounds();
+      pageBounds.inset(20, 20);
+
+      let box = new Rect(pageBounds);
+      box.width = 250;
+      box.height = 200;
+
+      new GroupItem([ tab._tabViewTabItem ], { bounds: box, immediately: true });
+    }
+
+    if (shouldUpdateTabBar)
+      this._updateTabBar();
+    else if (shouldShowTabView)
+      UI.showTabView();
+  },
+
+  // ----------
+  // Function: removeHiddenGroups
+  // Removes all hidden groups' data and its browser tabs.
+  removeHiddenGroups: function GroupItems_removeHiddenGroups() {
+    if (this._removingHiddenGroups)
+      return;
+    this._removingHiddenGroups = true;
+
+    let groupItems = this.groupItems.concat();
+    groupItems.forEach(function(groupItem) {
+      if (groupItem.hidden)
+        groupItem.closeHidden();
+     });
+
+    this._removingHiddenGroups = false;
+  },
+
+  // ----------
+  // Function: getUnclosableGroupItemId
+  // If there's only one (non-hidden) group, and there are app tabs present, 
+  // returns that group.
+  // Return the <GroupItem>'s Id
+  getUnclosableGroupItemId: function GroupItems_getUnclosableGroupItemId() {
+    let unclosableGroupItemId = null;
+
+    if (gBrowser._numPinnedTabs > 0) {
+      let hiddenGroupItems = 
+        this.groupItems.concat().filter(function(groupItem) {
+          return !groupItem.hidden;
+        });
+      if (hiddenGroupItems.length == 1)
+        unclosableGroupItemId = hiddenGroupItems[0].id;
+    }
+
+    return unclosableGroupItemId;
+  },
+
+  // ----------
+  // Function: updateGroupCloseButtons
+  // Updates group close buttons.
+  updateGroupCloseButtons: function GroupItems_updateGroupCloseButtons() {
+    let unclosableGroupItemId = this.getUnclosableGroupItemId();
+
+    if (unclosableGroupItemId) {
+      let groupItem = this.groupItem(unclosableGroupItemId);
+
+      if (groupItem) {
+        groupItem.$closeButton.hide();
+      }
+    } else {
+      this.groupItems.forEach(function(groupItem) {
+        groupItem.$closeButton.show();
+      });
+    }
+  },
+  
+  // ----------
+  // Function: calcValidSize
+  // Basic measure rules. Assures that item is a minimum size.
+  calcValidSize: function GroupItems_calcValidSize(size, options) {
+    Utils.assert(Utils.isPoint(size), 'input is a Point');
+    Utils.assert((size.x>0 || size.y>0) && (size.x!=0 && size.y!=0), 
+      "dimensions are valid:"+size.x+","+size.y);
+    return new Point(
+      Math.max(size.x, GroupItems.minGroupWidth),
+      Math.max(size.y, GroupItems.minGroupHeight));
+  },
+
+  // ----------
+  // Function: pauseAutoclose()
+  // Temporarily disable the behavior that closes groups when they become
+  // empty. This is used when entering private browsing, to avoid trashing the
+  // user's groups while private browsing is shuffling things around.
+  pauseAutoclose: function GroupItems_pauseAutoclose() {
+    this._autoclosePaused = true;
+  },
+
+  // ----------
+  // Function: unpauseAutoclose()
+  // Re-enables the auto-close behavior.
+  resumeAutoclose: function GroupItems_resumeAutoclose() {
+    this._autoclosePaused = false;
+  }
+};
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// **********
+// Title: tabitems.js
+
+// ##########
+// Class: TabItem
+// An <Item> that represents a tab. Also implements the <Subscribable> interface.
+//
+// Parameters:
+//   tab - a xul:tab
+function TabItem(tab, options) {
+  Utils.assert(tab, "tab");
+
+  this.tab = tab;
+  // register this as the tab's tabItem
+  this.tab._tabViewTabItem = this;
+
+  if (!options)
+    options = {};
+
+  // ___ set up div
+  document.body.appendChild(TabItems.fragment().cloneNode(true));
+  
+  // The document fragment contains just one Node
+  // As per DOM3 appendChild: it will then be the last child
+  let div = document.body.lastChild;
+  let $div = iQ(div);
+
+  this._showsCachedData = false;
+  this.canvasSizeForced = false;
+  this.$thumb = iQ('.thumb', $div);
+  this.$fav   = iQ('.favicon', $div);
+  this.$tabTitle = iQ('.tab-title', $div);
+  this.$canvas = iQ('.thumb canvas', $div);
+  this.$cachedThumb = iQ('img.cached-thumb', $div);
+  this.$favImage = iQ('.favicon>img', $div);
+  this.$close = iQ('.close', $div);
+
+  this.tabCanvas = new TabCanvas(this.tab, this.$canvas[0]);
+
+  this.defaultSize = new Point(TabItems.tabWidth, TabItems.tabHeight);
+  this._hidden = false;
+  this.isATabItem = true;
+  this.keepProportional = true;
+  this._hasBeenDrawn = false;
+  this._reconnected = false;
+  this.isDragging = false;
+  this.isStacked = false;
+
+  // Read off the total vertical and horizontal padding on the tab container
+  // and cache this value, as it must be the same for every TabItem.
+  if (Utils.isEmptyObject(TabItems.tabItemPadding)) {
+    TabItems.tabItemPadding.x = parseInt($div.css('padding-left'))
+        + parseInt($div.css('padding-right'));
+
+    TabItems.tabItemPadding.y = parseInt($div.css('padding-top'))
+        + parseInt($div.css('padding-bottom'));
+  }
+  
+  this.bounds = new Rect(0,0,1,1);
+
+  this._lastTabUpdateTime = Date.now();
+
+  // ___ superclass setup
+  this._init(div);
+
+  // ___ drag/drop
+  // override dropOptions with custom tabitem methods
+  this.dropOptions.drop = function(e) {
+    let groupItem = drag.info.item.parent;
+    groupItem.add(drag.info.$el);
+  };
+
+  this.draggable();
+
+  let self = this;
+
+  // ___ more div setup
+  $div.mousedown(function(e) {
+    if (!Utils.isRightClick(e))
+      self.lastMouseDownTarget = e.target;
+  });
+
+  $div.mouseup(function(e) {
+    var same = (e.target == self.lastMouseDownTarget);
+    self.lastMouseDownTarget = null;
+    if (!same)
+      return;
+
+    // press close button or middle mouse click
+    if (iQ(e.target).hasClass("close") || Utils.isMiddleClick(e)) {
+      self.closedManually = true;
+      self.close();
+    } else {
+      if (!Items.item(this).isDragging)
+        self.zoomIn();
+    }
+  });
+
+  this.droppable(true);
+
+  this.$close.attr("title", tabbrowserString("tabs.closeTab"));
+
+  TabItems.register(this);
+
+  // ___ reconnect to data from Storage
+  if (!TabItems.reconnectingPaused())
+    this._reconnect(options);
+};
+
+TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
+  // ----------
+  // Function: toString
+  // Prints [TabItem (tab)] for debug use
+  toString: function TabItem_toString() {
+    return "[TabItem (" + this.tab + ")]";
+  },
+
+  // ----------
+  // Function: forceCanvasSize
+  // Repaints the thumbnail with the given resolution, and forces it
+  // to stay that resolution until unforceCanvasSize is called.
+  forceCanvasSize: function TabItem_forceCanvasSize(w, h) {
+    this.canvasSizeForced = true;
+    this.$canvas[0].width = w;
+    this.$canvas[0].height = h;
+    this.tabCanvas.paint();
+  },
+
+  // ----------
+  // Function: unforceCanvasSize
+  // Stops holding the thumbnail resolution; allows it to shift to the
+  // size of thumbnail on screen. Note that this call does not nest, unlike
+  // <TabItems.resumePainting>; if you call forceCanvasSize multiple
+  // times, you just need a single unforce to clear them all.
+  unforceCanvasSize: function TabItem_unforceCanvasSize() {
+    this.canvasSizeForced = false;
+  },
+
+  // ----------
+  // Function: isShowingCachedData
+  // Returns a boolean indicates whether the cached data is being displayed or
+  // not. 
+  isShowingCachedData: function TabItem_isShowingCachedData() {
+    return this._showsCachedData;
+  },
+
+  // ----------
+  // Function: showCachedData
+  // Shows the cached data i.e. image and title.  Note: this method should only
+  // be called at browser startup with the cached data avaliable.
+  showCachedData: function TabItem_showCachedData() {
+    let {title, url} = this.getTabState();
+    let thumbnailURL = gPageThumbnails.getThumbnailURL(url);
+
+    this.$cachedThumb.attr("src", thumbnailURL).show();
+    this.$canvas.css({opacity: 0});
+
+    let tooltip = (title && title != url ? title + "\n" + url : url);
+    this.$tabTitle.text(title).attr("title", tooltip);
+    this._showsCachedData = true;
+  },
+
+  // ----------
+  // Function: hideCachedData
+  // Hides the cached data i.e. image and title and show the canvas.
+  hideCachedData: function TabItem_hideCachedData() {
+    this.$cachedThumb.attr("src", "").hide();
+    this.$canvas.css({opacity: 1.0});
+    this._showsCachedData = false;
+  },
+
+  // ----------
+  // Function: getStorageData
+  // Get data to be used for persistent storage of this object.
+  getStorageData: function TabItem_getStorageData() {
+    let data = {
+      groupID: (this.parent ? this.parent.id : 0)
+    };
+    if (this.parent && this.parent.getActiveTab() == this)
+      data.active = true;
+
+    return data;
+  },
+
+  // ----------
+  // Function: save
+  // Store persistent for this object.
+  save: function TabItem_save() {
+    try {
+      if (!this.tab || !Utils.isValidXULTab(this.tab) || !this._reconnected) // too soon/late to save
+        return;
+
+      let data = this.getStorageData();
+      if (TabItems.storageSanity(data))
+        Storage.saveTab(this.tab, data);
+    } catch(e) {
+      Utils.log("Error in saving tab value: "+e);
+    }
+  },
+
+  // ----------
+  // Function: _getCurrentTabStateEntry
+  // Returns the current tab state's active history entry.
+  _getCurrentTabStateEntry: function TabItem__getCurrentTabStateEntry() {
+    let tabState = Storage.getTabState(this.tab);
+
+    if (tabState) {
+      let index = (tabState.index || tabState.entries.length) - 1;
+      if (index in tabState.entries)
+        return tabState.entries[index];
+    }
+
+    return null;
+  },
+
+  // ----------
+  // Function: getTabState
+  // Returns the current tab state, i.e. the title and URL of the active
+  // history entry.
+  getTabState: function TabItem_getTabState() {
+    let entry = this._getCurrentTabStateEntry();
+    let title = "";
+    let url = "";
+
+    if (entry) {
+      if (entry.title)
+        title = entry.title;
+
+      url = entry.url;
+    } else {
+      url = this.tab.linkedBrowser.currentURI.spec;
+    }
+
+    return {title: title, url: url};
+  },
+
+  // ----------
+  // Function: _reconnect
+  // Load the reciever's persistent data from storage. If there is none, 
+  // treats it as a new tab. 
+  //
+  // Parameters:
+  //   options - an object with additional parameters, see below
+  //
+  // Possible options:
+  //   groupItemId - if the tab doesn't have any data associated with it and
+  //                 groupItemId is available, add the tab to that group.
+  _reconnect: function TabItem__reconnect(options) {
+    Utils.assertThrow(!this._reconnected, "shouldn't already be reconnected");
+    Utils.assertThrow(this.tab, "should have a xul:tab");
+
+    let tabData = Storage.getTabData(this.tab);
+    let groupItem;
+
+    if (tabData && TabItems.storageSanity(tabData)) {
+      // Show the cached data while we're waiting for the tabItem to be updated.
+      // If the tab isn't restored yet this acts as a placeholder until it is.
+      this.showCachedData();
+
+      if (this.parent)
+        this.parent.remove(this, {immediately: true});
+
+      if (tabData.groupID)
+        groupItem = GroupItems.groupItem(tabData.groupID);
+      else
+        groupItem = new GroupItem([], {immediately: true, bounds: tabData.bounds});
+
+      if (groupItem) {
+        groupItem.add(this, {immediately: true});
+
+        // restore the active tab for each group between browser sessions
+        if (tabData.active)
+          groupItem.setActiveTab(this);
+
+        // if it matches the selected tab or no active tab and the browser
+        // tab is hidden, the active group item would be set.
+        if (this.tab == gBrowser.selectedTab ||
+            (!GroupItems.getActiveGroupItem() && !this.tab.hidden))
+          UI.setActive(this.parent);
+      }
+    } else {
+      if (options && options.groupItemId)
+        groupItem = GroupItems.groupItem(options.groupItemId);
+
+      if (groupItem) {
+        groupItem.add(this, {immediately: true});
+      } else {
+        // create tab group by double click is handled in UI_init().
+        GroupItems.newTab(this, {immediately: true});
+      }
+    }
+
+    this._reconnected = true;
+    this.save();
+    this._sendToSubscribers("reconnected");
+  },
+
+  // ----------
+  // Function: setHidden
+  // Hide/unhide this item
+  setHidden: function TabItem_setHidden(val) {
+    if (val)
+      this.addClass("tabHidden");
+    else
+      this.removeClass("tabHidden");
+    this._hidden = val;
+  },
+
+  // ----------
+  // Function: getHidden
+  // Return hide state of item
+  getHidden: function TabItem_getHidden() {
+    return this._hidden;
+  },
+
+  // ----------
+  // Function: setBounds
+  // Moves this item to the specified location and size.
+  //
+  // Parameters:
+  //   rect - a <Rect> giving the new bounds
+  //   immediately - true if it should not animate; default false
+  //   options - an object with additional parameters, see below
+  //
+  // Possible options:
+  //   force - true to always update the DOM even if the bounds haven't changed; default false
+  setBounds: function TabItem_setBounds(inRect, immediately, options) {
+    Utils.assert(Utils.isRect(inRect), 'TabItem.setBounds: rect is not a real rectangle!');
+
+    if (!options)
+      options = {};
+
+    // force the input size to be valid
+    let validSize = TabItems.calcValidSize(
+      new Point(inRect.width, inRect.height), 
+      {hideTitle: (this.isStacked || options.hideTitle === true)});
+    let rect = new Rect(inRect.left, inRect.top, 
+      validSize.x, validSize.y);
+
+    var css = {};
+
+    if (rect.left != this.bounds.left || options.force)
+      css.left = rect.left;
+
+    if (rect.top != this.bounds.top || options.force)
+      css.top = rect.top;
+
+    if (rect.width != this.bounds.width || options.force) {
+      css.width = rect.width - TabItems.tabItemPadding.x;
+      css.fontSize = TabItems.getFontSizeFromWidth(rect.width);
+      css.fontSize += 'px';
+    }
+
+    if (rect.height != this.bounds.height || options.force) {
+      css.height = rect.height - TabItems.tabItemPadding.y;
+      if (!this.isStacked)
+        css.height -= TabItems.fontSizeRange.max;
+    }
+
+    if (Utils.isEmptyObject(css))
+      return;
+
+    this.bounds.copy(rect);
+
+    // If this is a brand new tab don't animate it in from
+    // a random location (i.e., from [0,0]). Instead, just
+    // have it appear where it should be.
+    if (immediately || (!this._hasBeenDrawn)) {
+      this.$container.css(css);
+    } else {
+      TabItems.pausePainting();
+      this.$container.animate(css, {
+          duration: 200,
+        easing: "tabviewBounce",
+        complete: function() {
+          TabItems.resumePainting();
+        }
+      });
+    }
+
+    if (css.fontSize && !(this.parent && this.parent.isStacked())) {
+      if (css.fontSize < TabItems.fontSizeRange.min)
+        immediately ? this.$tabTitle.hide() : this.$tabTitle.fadeOut();
+      else
+        immediately ? this.$tabTitle.show() : this.$tabTitle.fadeIn();
+    }
+
+    if (css.width) {
+      TabItems.update(this.tab);
+
+      let widthRange, proportion;
+
+      if (this.parent && this.parent.isStacked()) {
+        if (UI.rtl) {
+          this.$fav.css({top:0, right:0});
+        } else {
+          this.$fav.css({top:0, left:0});
+        }
+        widthRange = new Range(70, 90);
+        proportion = widthRange.proportion(css.width); // between 0 and 1
+      } else {
+        if (UI.rtl) {
+          this.$fav.css({top:4, right:2});
+        } else {
+          this.$fav.css({top:4, left:4});
+        }
+        widthRange = new Range(40, 45);
+        proportion = widthRange.proportion(css.width); // between 0 and 1
+      }
+
+      if (proportion <= .1)
+        this.$close.hide();
+      else
+        this.$close.show().css({opacity:proportion});
+
+      var pad = 1 + 5 * proportion;
+      var alphaRange = new Range(0.1,0.2);
+      this.$fav.css({
+       "-moz-padding-start": pad + "px",
+       "-moz-padding-end": pad + 2 + "px",
+       "padding-top": pad + "px",
+       "padding-bottom": pad + "px",
+       "border-color": "rgba(0,0,0,"+ alphaRange.scale(proportion) +")",
+      });
+    }
+
+    this._hasBeenDrawn = true;
+
+    UI.clearShouldResizeItems();
+
+    rect = this.getBounds(); // ensure that it's a <Rect>
+
+    Utils.assert(Utils.isRect(this.bounds), 'TabItem.setBounds: this.bounds is not a real rectangle!');
+
+    if (!this.parent && Utils.isValidXULTab(this.tab))
+      this.setTrenches(rect);
+
+    this.save();
+  },
+
+  // ----------
+  // Function: setZ
+  // Sets the z-index for this item.
+  setZ: function TabItem_setZ(value) {
+    this.zIndex = value;
+    this.$container.css({zIndex: value});
+  },
+
+  // ----------
+  // Function: close
+  // Closes this item (actually closes the tab associated with it, which automatically
+  // closes the item.
+  // Parameters:
+  //   groupClose - true if this method is called by group close action.
+  // Returns true if this tab is removed.
+  close: function TabItem_close(groupClose) {
+    // When the last tab is closed, put a new tab into closing tab's group. If
+    // closing tab doesn't belong to a group and no empty group, create a new 
+    // one for the new tab.
+    if (!groupClose && gBrowser.tabs.length == 1) {
+      let group = this.tab._tabViewTabItem.parent;
+      group.newTab(null, { closedLastTab: true });
+    }
+
+    // when "TabClose" event is fired, the browser tab is about to close and our 
+    // item "close" is fired before the browser tab actually get closed. 
+    // Therefore, we need "tabRemoved" event below.
+    gBrowser.removeTab(this.tab);
+    let tabClosed = !this.tab;
+
+    if (tabClosed)
+      this._sendToSubscribers("tabRemoved");
+
+    // No need to explicitly delete the tab data, becasue sessionstore data
+    // associated with the tab will automatically go away
+    return tabClosed;
+  },
+
+  // ----------
+  // Function: addClass
+  // Adds the specified CSS class to this item's container DOM element.
+  addClass: function TabItem_addClass(className) {
+    this.$container.addClass(className);
+  },
+
+  // ----------
+  // Function: removeClass
+  // Removes the specified CSS class from this item's container DOM element.
+  removeClass: function TabItem_removeClass(className) {
+    this.$container.removeClass(className);
+  },
+
+  // ----------
+  // Function: makeActive
+  // Updates this item to visually indicate that it's active.
+  makeActive: function TabItem_makeActive() {
+    this.$container.addClass("focus");
+
+    if (this.parent)
+      this.parent.setActiveTab(this);
+  },
+
+  // ----------
+  // Function: makeDeactive
+  // Updates this item to visually indicate that it's not active.
+  makeDeactive: function TabItem_makeDeactive() {
+    this.$container.removeClass("focus");
+  },
+
+  // ----------
+  // Function: zoomIn
+  // Allows you to select the tab and zoom in on it, thereby bringing you
+  // to the tab in Firefox to interact with.
+  // Parameters:
+  //   isNewBlankTab - boolean indicates whether it is a newly opened blank tab.
+  zoomIn: function TabItem_zoomIn(isNewBlankTab) {
+    // don't allow zoom in if its group is hidden
+    if (this.parent && this.parent.hidden)
+      return;
+
+    let self = this;
+    let $tabEl = this.$container;
+    let $canvas = this.$canvas;
+
+    Search.hide();
+
+    UI.setActive(this);
+    TabItems._update(this.tab, {force: true});
+
+    // Zoom in!
+    let tab = this.tab;
+
+    function onZoomDone() {
+      $canvas.css({ '-moz-transform': null });
+      $tabEl.removeClass("front");
+
+      UI.goToTab(tab);
+
+      // tab might not be selected because hideTabView() is invoked after 
+      // UI.goToTab() so we need to setup everything for the gBrowser.selectedTab
+      if (tab != gBrowser.selectedTab) {
+        UI.onTabSelect(gBrowser.selectedTab);
+      } else { 
+        if (isNewBlankTab)
+          gWindow.gURLBar.focus();
+      }
+      if (self.parent && self.parent.expanded)
+        self.parent.collapse();
+
+      self._sendToSubscribers("zoomedIn");
+    }
+
+    let animateZoom = gPrefBranch.getBoolPref("animate_zoom");
+    if (animateZoom) {
+      let transform = this.getZoomTransform();
+      TabItems.pausePainting();
+
+      if (this.parent && this.parent.expanded)
+        $tabEl.removeClass("stack-trayed");
+      $tabEl.addClass("front");
+      $canvas
+        .css({ '-moz-transform-origin': transform.transformOrigin })
+        .animate({ '-moz-transform': transform.transform }, {
+          duration: 230,
+          easing: 'fast',
+          complete: function() {
+            onZoomDone();
+
+            setTimeout(function() {
+              TabItems.resumePainting();
+            }, 0);
+          }
+        });
+    } else {
+      setTimeout(onZoomDone, 0);
+    }
+  },
+
+  // ----------
+  // Function: zoomOut
+  // Handles the zoom down animation after returning to TabView.
+  // It is expected that this routine will be called from the chrome thread
+  //
+  // Parameters:
+  //   complete - a function to call after the zoom down animation
+  zoomOut: function TabItem_zoomOut(complete) {
+    let $tab = this.$container, $canvas = this.$canvas;
+    var self = this;
+    
+    let onZoomDone = function onZoomDone() {
+      $tab.removeClass("front");
+      $canvas.css("-moz-transform", null);
+
+      if (typeof complete == "function")
+        complete();
+    };
+
+    UI.setActive(this);
+    TabItems._update(this.tab, {force: true});
+
+    $tab.addClass("front");
+
+    let animateZoom = gPrefBranch.getBoolPref("animate_zoom");
+    if (animateZoom) {
+      // The scaleCheat of 2 here is a clever way to speed up the zoom-out
+      // code. See getZoomTransform() below.
+      let transform = this.getZoomTransform(2);
+      TabItems.pausePainting();
+
+      $canvas.css({
+        '-moz-transform': transform.transform,
+        '-moz-transform-origin': transform.transformOrigin
+      });
+
+      $canvas.animate({ "-moz-transform": "scale(1.0)" }, {
+        duration: 300,
+        easing: 'cubic-bezier', // note that this is legal easing, even without parameters
+        complete: function() {
+          TabItems.resumePainting();
+          onZoomDone();
+        }
+      });
+    } else {
+      onZoomDone();
+    }
+  },
+
+  // ----------
+  // Function: getZoomTransform
+  // Returns the transform function which represents the maximum bounds of the
+  // tab thumbnail in the zoom animation.
+  getZoomTransform: function TabItem_getZoomTransform(scaleCheat) {
+    // Taking the bounds of the container (as opposed to the canvas) makes us
+    // immune to any transformations applied to the canvas.
+    let { left, top, width, height, right, bottom } = this.$container.bounds();
+
+    let { innerWidth: windowWidth, innerHeight: windowHeight } = window;
+
+    // The scaleCheat is a clever way to speed up the zoom-in code.
+    // Because image scaling is slowest on big images, we cheat and stop
+    // the image at scaled-down size and placed accordingly. Because the
+    // animation is fast, you can't see the difference but it feels a lot
+    // zippier. The only trick is choosing the right animation function so
+    // that you don't see a change in percieved animation speed from frame #1
+    // (the tab) to frame #2 (the half-size image) to frame #3 (the first frame
+    // of real animation). Choosing an animation that starts fast is key.
+
+    if (!scaleCheat)
+      scaleCheat = 1.7;
+
+    let zoomWidth = width + (window.innerWidth - width) / scaleCheat;
+    let zoomScaleFactor = zoomWidth / width;
+
+    let zoomHeight = height * zoomScaleFactor;
+    let zoomTop = top * (1 - 1/scaleCheat);
+    let zoomLeft = left * (1 - 1/scaleCheat);
+
+    let xOrigin = (left - zoomLeft) / ((left - zoomLeft) + (zoomLeft + zoomWidth - right)) * 100;
+    let yOrigin = (top - zoomTop) / ((top - zoomTop) + (zoomTop + zoomHeight - bottom)) * 100;
+
+    return {
+      transformOrigin: xOrigin + "% " + yOrigin + "%",
+      transform: "scale(" + zoomScaleFactor + ")"
+    };
+  },
+
+  // ----------
+  // Function: updateCanvas
+  // Updates the tabitem's canvas.
+  updateCanvas: function TabItem_updateCanvas() {
+    // ___ thumbnail
+    let $canvas = this.$canvas;
+    if (!this.canvasSizeForced) {
+      let w = $canvas.width();
+      let h = $canvas.height();
+      if (w != $canvas[0].width || h != $canvas[0].height) {
+        $canvas[0].width = w;
+        $canvas[0].height = h;
+      }
+    }
+
+    TabItems._lastUpdateTime = Date.now();
+    this._lastTabUpdateTime = TabItems._lastUpdateTime;
+
+    if (this.tabCanvas)
+      this.tabCanvas.paint();
+
+    // ___ cache
+    if (this.isShowingCachedData())
+      this.hideCachedData();
+  }
+});
+
+// ##########
+// Class: TabItems
+// Singleton for managing <TabItem>s
+let TabItems = {
+  minTabWidth: 40,
+  tabWidth: 160,
+  tabHeight: 120,
+  tabAspect: 0, // set in init
+  invTabAspect: 0, // set in init  
+  fontSize: 9,
+  fontSizeRange: new Range(8,15),
+  _fragment: null,
+  items: [],
+  paintingPaused: 0,
+  _tabsWaitingForUpdate: null,
+  _heartbeat: null, // see explanation at startHeartbeat() below
+  _heartbeatTiming: 200, // milliseconds between calls
+  _maxTimeForUpdating: 200, // milliseconds that consecutive updates can take
+  _lastUpdateTime: Date.now(),
+  _eventListeners: [],
+  _pauseUpdateForTest: false,
+  tempCanvas: null,
+  _reconnectingPaused: false,
+  tabItemPadding: {},
+  _mozAfterPaintHandler: null,
+
+  // ----------
+  // Function: toString
+  // Prints [TabItems count=count] for debug use
+  toString: function TabItems_toString() {
+    return "[TabItems count=" + this.items.length + "]";
+  },
+
+  // ----------
+  // Function: init
+  // Set up the necessary tracking to maintain the <TabItems>s.
+  init: function TabItems_init() {
+    Utils.assert(window.AllTabs, "AllTabs must be initialized first");
+    let self = this;
+    
+    // Set up tab priority queue
+    this._tabsWaitingForUpdate = new TabPriorityQueue();
+    this.minTabHeight = this.minTabWidth * this.tabHeight / this.tabWidth;
+    this.tabAspect = this.tabHeight / this.tabWidth;
+    this.invTabAspect = 1 / this.tabAspect;
+
+    let $canvas = iQ("<canvas>")
+      .attr('moz-opaque', '');
+    $canvas.appendTo(iQ("body"));
+    $canvas.hide();
+    this.tempCanvas = $canvas[0];
+    // 150 pixels is an empirical size, below which FF's drawWindow()
+    // algorithm breaks down
+    this.tempCanvas.width = 150;
+    this.tempCanvas.height = 112;
+
+    let mm = gWindow.messageManager;
+    this._mozAfterPaintHandler = this.onMozAfterPaint.bind(this);
+    mm.addMessageListener("Panorama:MozAfterPaint", this._mozAfterPaintHandler);
+
+    // When a tab is opened, create the TabItem
+    this._eventListeners.open = function (event) {
+      let tab = event.target;
+
+      if (!tab.pinned)
+        self.link(tab);
+    }
+    // When a tab's content is loaded, show the canvas and hide the cached data
+    // if necessary.
+    this._eventListeners.attrModified = function (event) {
+      let tab = event.target;
+
+      if (!tab.pinned)
+        self.update(tab);
+    }
+    // When a tab is closed, unlink.
+    this._eventListeners.close = function (event) {
+      let tab = event.target;
+
+      // XXX bug #635975 - don't unlink the tab if the dom window is closing.
+      if (!tab.pinned && !UI.isDOMWindowClosing)
+        self.unlink(tab);
+    }
+    for (let name in this._eventListeners) {
+      AllTabs.register(name, this._eventListeners[name]);
+    }
+
+    let activeGroupItem = GroupItems.getActiveGroupItem();
+    let activeGroupItemId = activeGroupItem ? activeGroupItem.id : null;
+    // For each tab, create the link.
+    AllTabs.tabs.forEach(function (tab) {
+      if (tab.pinned)
+        return;
+
+      let options = {immediately: true};
+      // if tab is visible in the tabstrip and doesn't have any data stored in 
+      // the session store (see TabItem__reconnect), it implies that it is a 
+      // new tab which is created before Panorama is initialized. Therefore, 
+      // passing the active group id to the link() method for setting it up.
+      if (!tab.hidden && activeGroupItemId)
+         options.groupItemId = activeGroupItemId;
+      self.link(tab, options);
+      self.update(tab);
+    });
+  },
+
+  // ----------
+  // Function: uninit
+  uninit: function TabItems_uninit() {
+    let mm = gWindow.messageManager;
+    mm.removeMessageListener("Panorama:MozAfterPaint", this._mozAfterPaintHandler);
+
+    for (let name in this._eventListeners) {
+      AllTabs.unregister(name, this._eventListeners[name]);
+    }
+    this.items.forEach(function(tabItem) {
+      for (let x in tabItem) {
+        if (typeof tabItem[x] == "object")
+          tabItem[x] = null;
+      }
+    });
+
+    this.items = null;
+    this._eventListeners = null;
+    this._lastUpdateTime = null;
+    this._tabsWaitingForUpdate.clear();
+  },
+
+  // ----------
+  // Function: fragment
+  // Return a DocumentFragment which has a single <div> child. This child node
+  // will act as a template for all TabItem containers.
+  // The first call of this function caches the DocumentFragment in _fragment.
+  fragment: function TabItems_fragment() {
+    if (this._fragment)
+      return this._fragment;
+
+    let div = document.createElement("div");
+    div.classList.add("tab");
+    div.innerHTML = "<div class='thumb'>" +
+            "<img class='cached-thumb' style='display:none'/><canvas moz-opaque/></div>" +
+            "<div class='favicon'><img/></div>" +
+            "<span class='tab-title'>&nbsp;</span>" +
+            "<div class='close'></div>";
+    this._fragment = document.createDocumentFragment();
+    this._fragment.appendChild(div);
+
+    return this._fragment;
+  },
+
+  // Function: _isComplete
+  // Checks whether the xul:tab has fully loaded and calls a callback with a 
+  // boolean indicates whether the tab is loaded or not.
+  _isComplete: function TabItems__isComplete(tab, callback) {
+    Utils.assertThrow(tab, "tab");
+
+    let mm = tab.linkedBrowser.messageManager;
+    let message = "Panorama:isDocumentLoaded";
+
+    mm.addMessageListener(message, function onMessage(cx) {
+      mm.removeMessageListener(cx.name, onMessage);
+      callback(cx.json.isLoaded);
+    });
+    mm.sendAsyncMessage(message);
+  },
+
+  // ----------
+  // Function: onMozAfterPaint
+  // Called when a web page is painted.
+  onMozAfterPaint: function TabItems_onMozAfterPaint(cx) {
+    let index = gBrowser.browsers.indexOf(cx.target);
+    if (index == -1)
+      return;
+
+    let tab = gBrowser.tabs[index];
+    if (!tab.pinned)
+      this.update(tab);
+  },
+
+  // ----------
+  // Function: update
+  // Takes in a xul:tab.
+  update: function TabItems_update(tab) {
+    try {
+      Utils.assertThrow(tab, "tab");
+      Utils.assertThrow(!tab.pinned, "shouldn't be an app tab");
+      Utils.assertThrow(tab._tabViewTabItem, "should already be linked");
+
+      let shouldDefer = (
+        this.isPaintingPaused() ||
+        this._tabsWaitingForUpdate.hasItems() ||
+        Date.now() - this._lastUpdateTime < this._heartbeatTiming
+      );
+
+      if (shouldDefer) {
+        this._tabsWaitingForUpdate.push(tab);
+        this.startHeartbeat();
+      } else
+        this._update(tab);
+    } catch(e) {
+      Utils.log(e);
+    }
+  },
+
+  // ----------
+  // Function: _update
+  // Takes in a xul:tab.
+  //
+  // Parameters:
+  //   tab - a xul tab to update
+  //   options - an object with additional parameters, see below
+  //
+  // Possible options:
+  //   force - true to always update the tab item even if it's incomplete
+  _update: function TabItems__update(tab, options) {
+    try {
+      if (this._pauseUpdateForTest)
+        return;
+
+      Utils.assertThrow(tab, "tab");
+
+      // ___ get the TabItem
+      Utils.assertThrow(tab._tabViewTabItem, "must already be linked");
+      let tabItem = tab._tabViewTabItem;
+
+      // Even if the page hasn't loaded, display the favicon and title
+      // ___ icon
+      FavIcons.getFavIconUrlForTab(tab, function TabItems__update_getFavIconUrlCallback(iconUrl) {
+        let favImage = tabItem.$favImage[0];
+        let fav = tabItem.$fav;
+        if (iconUrl) {
+          if (favImage.src != iconUrl)
+            favImage.src = iconUrl;
+          fav.show();
+        } else {
+          if (favImage.hasAttribute("src"))
+            favImage.removeAttribute("src");
+          fav.hide();
+        }
+        tabItem._sendToSubscribers("iconUpdated");
+      });
+
+      // ___ label
+      let label = tab.label;
+      let $name = tabItem.$tabTitle;
+      if ($name.text() != label)
+        $name.text(label);
+
+      // ___ remove from waiting list now that we have no other
+      // early returns
+      this._tabsWaitingForUpdate.remove(tab);
+
+      // ___ URL
+      let tabUrl = tab.linkedBrowser.currentURI.spec;
+      let tooltip = (label == tabUrl ? label : label + "\n" + tabUrl);
+      tabItem.$container.attr("title", tooltip);
+
+      // ___ Make sure the tab is complete and ready for updating.
+      if (options && options.force) {
+        tabItem.updateCanvas();
+        tabItem._sendToSubscribers("updated");
+      } else {
+        this._isComplete(tab, function TabItems__update_isComplete(isComplete) {
+          if (!Utils.isValidXULTab(tab) || tab.pinned)
+            return;
+
+          if (isComplete) {
+            tabItem.updateCanvas();
+            tabItem._sendToSubscribers("updated");
+          } else {
+            this._tabsWaitingForUpdate.push(tab);
+          }
+        }.bind(this));
+      }
+    } catch(e) {
+      Utils.log(e);
+    }
+  },
+
+  // ----------
+  // Function: link
+  // Takes in a xul:tab, creates a TabItem for it and adds it to the scene. 
+  link: function TabItems_link(tab, options) {
+    try {
+      Utils.assertThrow(tab, "tab");
+      Utils.assertThrow(!tab.pinned, "shouldn't be an app tab");
+      Utils.assertThrow(!tab._tabViewTabItem, "shouldn't already be linked");
+      new TabItem(tab, options); // sets tab._tabViewTabItem to itself
+    } catch(e) {
+      Utils.log(e);
+    }
+  },
+
+  // ----------
+  // Function: unlink
+  // Takes in a xul:tab and destroys the TabItem associated with it. 
+  unlink: function TabItems_unlink(tab) {
+    try {
+      Utils.assertThrow(tab, "tab");
+      Utils.assertThrow(tab._tabViewTabItem, "should already be linked");
+      // note that it's ok to unlink an app tab; see .handleTabUnpin
+
+      this.unregister(tab._tabViewTabItem);
+      tab._tabViewTabItem._sendToSubscribers("close");
+      tab._tabViewTabItem.$container.remove();
+      tab._tabViewTabItem.removeTrenches();
+      Items.unsquish(null, tab._tabViewTabItem);
+
+      tab._tabViewTabItem.tab = null;
+      tab._tabViewTabItem.tabCanvas.tab = null;
+      tab._tabViewTabItem.tabCanvas = null;
+      tab._tabViewTabItem = null;
+      Storage.saveTab(tab, null);
+
+      this._tabsWaitingForUpdate.remove(tab);
+    } catch(e) {
+      Utils.log(e);
+    }
+  },
+
+  // ----------
+  // when a tab becomes pinned, destroy its TabItem
+  handleTabPin: function TabItems_handleTabPin(xulTab) {
+    this.unlink(xulTab);
+  },
+
+  // ----------
+  // when a tab becomes unpinned, create a TabItem for it
+  handleTabUnpin: function TabItems_handleTabUnpin(xulTab) {
+    this.link(xulTab);
+    this.update(xulTab);
+  },
+
+  // ----------
+  // Function: startHeartbeat
+  // Start a new heartbeat if there isn't one already started.
+  // The heartbeat is a chain of setTimeout calls that allows us to spread
+  // out update calls over a period of time.
+  // _heartbeat is used to make sure that we don't add multiple 
+  // setTimeout chains.
+  startHeartbeat: function TabItems_startHeartbeat() {
+    if (!this._heartbeat) {
+      let self = this;
+      this._heartbeat = setTimeout(function() {
+        self._checkHeartbeat();
+      }, this._heartbeatTiming);
+    }
+  },
+
+  // ----------
+  // Function: _checkHeartbeat
+  // This periodically checks for tabs waiting to be updated, and calls
+  // _update on them.
+  // Should only be called by startHeartbeat and resumePainting.
+  _checkHeartbeat: function TabItems__checkHeartbeat() {
+    this._heartbeat = null;
+
+    if (this.isPaintingPaused())
+      return;
+
+    // restart the heartbeat to update all waiting tabs once the UI becomes idle
+    if (!UI.isIdle()) {
+      this.startHeartbeat();
+      return;
+    }
+
+    let accumTime = 0;
+    let items = this._tabsWaitingForUpdate.getItems();
+    // Do as many updates as we can fit into a "perceived" amount
+    // of time, which is tunable.
+    while (accumTime < this._maxTimeForUpdating && items.length) {
+      let updateBegin = Date.now();
+      this._update(items.pop());
+      let updateEnd = Date.now();
+
+      // Maintain a simple average of time for each tabitem update
+      // We can use this as a base by which to delay things like
+      // tab zooming, so there aren't any hitches.
+      let deltaTime = updateEnd - updateBegin;
+      accumTime += deltaTime;
+    }
+
+    if (this._tabsWaitingForUpdate.hasItems())
+      this.startHeartbeat();
+  },
+
+  // ----------
+  // Function: pausePainting
+  // Tells TabItems to stop updating thumbnails (so you can do
+  // animations without thumbnail paints causing stutters).
+  // pausePainting can be called multiple times, but every call to
+  // pausePainting needs to be mirrored with a call to <resumePainting>.
+  pausePainting: function TabItems_pausePainting() {
+    this.paintingPaused++;
+    if (this._heartbeat) {
+      clearTimeout(this._heartbeat);
+      this._heartbeat = null;
+    }
+  },
+
+  // ----------
+  // Function: resumePainting
+  // Undoes a call to <pausePainting>. For instance, if you called
+  // pausePainting three times in a row, you'll need to call resumePainting
+  // three times before TabItems will start updating thumbnails again.
+  resumePainting: function TabItems_resumePainting() {
+    this.paintingPaused--;
+    Utils.assert(this.paintingPaused > -1, "paintingPaused should not go below zero");
+    if (!this.isPaintingPaused())
+      this.startHeartbeat();
+  },
+
+  // ----------
+  // Function: isPaintingPaused
+  // Returns a boolean indicating whether painting
+  // is paused or not.
+  isPaintingPaused: function TabItems_isPaintingPaused() {
+    return this.paintingPaused > 0;
+  },
+
+  // ----------
+  // Function: pauseReconnecting
+  // Don't reconnect any new tabs until resume is called.
+  pauseReconnecting: function TabItems_pauseReconnecting() {
+    Utils.assertThrow(!this._reconnectingPaused, "shouldn't already be paused");
+
+    this._reconnectingPaused = true;
+  },
+  
+  // ----------
+  // Function: resumeReconnecting
+  // Reconnect all of the tabs that were created since we paused.
+  resumeReconnecting: function TabItems_resumeReconnecting() {
+    Utils.assertThrow(this._reconnectingPaused, "should already be paused");
+
+    this._reconnectingPaused = false;
+    this.items.forEach(function(item) {
+      if (!item._reconnected)
+        item._reconnect();
+    });
+  },
+  
+  // ----------
+  // Function: reconnectingPaused
+  // Returns true if reconnecting is paused.
+  reconnectingPaused: function TabItems_reconnectingPaused() {
+    return this._reconnectingPaused;
+  },
+  
+  // ----------
+  // Function: register
+  // Adds the given <TabItem> to the master list.
+  register: function TabItems_register(item) {
+    Utils.assert(item && item.isAnItem, 'item must be a TabItem');
+    Utils.assert(this.items.indexOf(item) == -1, 'only register once per item');
+    this.items.push(item);
+  },
+
+  // ----------
+  // Function: unregister
+  // Removes the given <TabItem> from the master list.
+  unregister: function TabItems_unregister(item) {
+    var index = this.items.indexOf(item);
+    if (index != -1)
+      this.items.splice(index, 1);
+  },
+
+  // ----------
+  // Function: getItems
+  // Returns a copy of the master array of <TabItem>s.
+  getItems: function TabItems_getItems() {
+    return Utils.copy(this.items);
+  },
+
+  // ----------
+  // Function: saveAll
+  // Saves all open <TabItem>s.
+  saveAll: function TabItems_saveAll() {
+    let tabItems = this.getItems();
+
+    tabItems.forEach(function TabItems_saveAll_forEach(tabItem) {
+      tabItem.save();
+    });
+  },
+
+  // ----------
+  // Function: storageSanity
+  // Checks the specified data (as returned by TabItem.getStorageData or loaded from storage)
+  // and returns true if it looks valid.
+  // TODO: this is a stub, please implement
+  storageSanity: function TabItems_storageSanity(data) {
+    return true;
+  },
+
+  // ----------
+  // Function: getFontSizeFromWidth
+  // Private method that returns the fontsize to use given the tab's width
+  getFontSizeFromWidth: function TabItem_getFontSizeFromWidth(width) {
+    let widthRange = new Range(0, TabItems.tabWidth);
+    let proportion = widthRange.proportion(width - TabItems.tabItemPadding.x, true);
+    // proportion is in [0,1]
+    return TabItems.fontSizeRange.scale(proportion);
+  },
+
+  // ----------
+  // Function: _getWidthForHeight
+  // Private method that returns the tabitem width given a height.
+  _getWidthForHeight: function TabItems__getWidthForHeight(height) {
+    return height * TabItems.invTabAspect;
+  },
+
+  // ----------
+  // Function: _getHeightForWidth
+  // Private method that returns the tabitem height given a width.
+  _getHeightForWidth: function TabItems__getHeightForWidth(width) {
+    return width * TabItems.tabAspect;
+  },
+
+  // ----------
+  // Function: calcValidSize
+  // Pass in a desired size, and receive a size based on proper title
+  // size and aspect ratio.
+  calcValidSize: function TabItems_calcValidSize(size, options) {
+    Utils.assert(Utils.isPoint(size), 'input is a Point');
+
+    let width = Math.max(TabItems.minTabWidth, size.x);
+    let showTitle = !options || !options.hideTitle;
+    let titleSize = showTitle ? TabItems.fontSizeRange.max : 0;
+    let height = Math.max(TabItems.minTabHeight, size.y - titleSize);
+    let retSize = new Point(width, height);
+
+    if (size.x > -1)
+      retSize.y = this._getHeightForWidth(width);
+    if (size.y > -1)
+      retSize.x = this._getWidthForHeight(height);
+
+    if (size.x > -1 && size.y > -1) {
+      if (retSize.x < size.x)
+        retSize.y = this._getHeightForWidth(retSize.x);
+      else
+        retSize.x = this._getWidthForHeight(retSize.y);
+    }
+
+    if (showTitle)
+      retSize.y += titleSize;
+
+    return retSize;
+  }
+};
+
+// ##########
+// Class: TabPriorityQueue
+// Container that returns tab items in a priority order
+// Current implementation assigns tab to either a high priority
+// or low priority queue, and toggles which queue items are popped
+// from. This guarantees that high priority items which are constantly
+// being added will not eclipse changes for lower priority items.
+function TabPriorityQueue() {
+};
+
+TabPriorityQueue.prototype = {
+  _low: [], // low priority queue
+  _high: [], // high priority queue
+
+  // ----------
+  // Function: toString
+  // Prints [TabPriorityQueue count=count] for debug use
+  toString: function TabPriorityQueue_toString() {
+    return "[TabPriorityQueue count=" + (this._low.length + this._high.length) + "]";
+  },
+
+  // ----------
+  // Function: clear
+  // Empty the update queue
+  clear: function TabPriorityQueue_clear() {
+    this._low = [];
+    this._high = [];
+  },
+
+  // ----------
+  // Function: hasItems
+  // Return whether pending items exist
+  hasItems: function TabPriorityQueue_hasItems() {
+    return (this._low.length > 0) || (this._high.length > 0);
+  },
+
+  // ----------
+  // Function: getItems
+  // Returns all queued items, ordered from low to high priority
+  getItems: function TabPriorityQueue_getItems() {
+    return this._low.concat(this._high);
+  },
+
+  // ----------
+  // Function: push
+  // Add an item to be prioritized
+  push: function TabPriorityQueue_push(tab) {
+    // Push onto correct priority queue.
+    // It's only low priority if it's in a stack, and isn't the top,
+    // and the stack isn't expanded.
+    // If it already exists in the destination queue,
+    // leave it. If it exists in a different queue, remove it first and push
+    // onto new queue.
+    let item = tab._tabViewTabItem;
+    if (item.parent && (item.parent.isStacked() &&
+      !item.parent.isTopOfStack(item) &&
+      !item.parent.expanded)) {
+      let idx = this._high.indexOf(tab);
+      if (idx != -1) {
+        this._high.splice(idx, 1);
+        this._low.unshift(tab);
+      } else if (this._low.indexOf(tab) == -1)
+        this._low.unshift(tab);
+    } else {
+      let idx = this._low.indexOf(tab);
+      if (idx != -1) {
+        this._low.splice(idx, 1);
+        this._high.unshift(tab);
+      } else if (this._high.indexOf(tab) == -1)
+        this._high.unshift(tab);
+    }
+  },
+
+  // ----------
+  // Function: pop
+  // Remove and return the next item in priority order
+  pop: function TabPriorityQueue_pop() {
+    let ret = null;
+    if (this._high.length)
+      ret = this._high.pop();
+    else if (this._low.length)
+      ret = this._low.pop();
+    return ret;
+  },
+
+  // ----------
+  // Function: peek
+  // Return the next item in priority order, without removing it
+  peek: function TabPriorityQueue_peek() {
+    let ret = null;
+    if (this._high.length)
+      ret = this._high[this._high.length-1];
+    else if (this._low.length)
+      ret = this._low[this._low.length-1];
+    return ret;
+  },
+
+  // ----------
+  // Function: remove
+  // Remove the passed item
+  remove: function TabPriorityQueue_remove(tab) {
+    let index = this._high.indexOf(tab);
+    if (index != -1)
+      this._high.splice(index, 1);
+    else {
+      index = this._low.indexOf(tab);
+      if (index != -1)
+        this._low.splice(index, 1);
+    }
+  }
+};
+
+// ##########
+// Class: TabCanvas
+// Takes care of the actual canvas for the tab thumbnail
+// Does not need to be accessed from outside of tabitems.js
+function TabCanvas(tab, canvas) {
+  this.tab = tab;
+  this.canvas = canvas;
+};
+
+TabCanvas.prototype = Utils.extend(new Subscribable(), {
+  // ----------
+  // Function: toString
+  // Prints [TabCanvas (tab)] for debug use
+  toString: function TabCanvas_toString() {
+    return "[TabCanvas (" + this.tab + ")]";
+  },
+
+  // ----------
+  // Function: paint
+  paint: function TabCanvas_paint(evt) {
+    var w = this.canvas.width;
+    var h = this.canvas.height;
+    if (!w || !h)
+      return;
+
+    if (!this.tab.linkedBrowser.contentWindow) {
+      Utils.log('no tab.linkedBrowser.contentWindow in TabCanvas.paint()');
+      return;
+    }
+
+    let ctx = this.canvas.getContext("2d");
+    let tempCanvas = TabItems.tempCanvas;
+    let bgColor = '#fff';
+
+    if (w < tempCanvas.width) {
+      // Small draw case where nearest-neighbor algorithm breaks down in Windows
+      // First draw to a larger canvas (150px wide), and then draw that image
+      // to the destination canvas.
+      let tempCtx = tempCanvas.getContext("2d");
+      this._drawWindow(tempCtx, tempCanvas.width, tempCanvas.height, bgColor);
+
+      // Now copy to tabitem canvas.
+      try {
+        this._fillCanvasBackground(ctx, w, h, bgColor);
+        ctx.drawImage(tempCanvas, 0, 0, w, h);
+      } catch (e) {
+        Utils.error('paint', e);
+      }
+    } else {
+      // General case where nearest neighbor algorithm looks good
+      // Draw directly to the destination canvas
+      this._drawWindow(ctx, w, h, bgColor);
+    }
+
+    this._sendToSubscribers("painted");
+  },
+
+  // ----------
+  // Function: _fillCanvasBackground
+  // Draws a rectangle of <width>x<height> with color <bgColor> to the given
+  // canvas context.
+  _fillCanvasBackground: function TabCanvas__fillCanvasBackground(ctx, width, height, bgColor) {
+    ctx.fillStyle = bgColor;
+    ctx.fillRect(0, 0, width, height);
+  },
+
+  // ----------
+  // Function: _drawWindow
+  // Draws contents of the tabs' browser window to the given canvas context.
+  _drawWindow: function TabCanvas__drawWindow(ctx, width, height, bgColor) {
+    this._fillCanvasBackground(ctx, width, height, bgColor);
+
+    let rect = this._calculateClippingRect(width, height);
+    let scaler = width / rect.width;
+
+    ctx.save();
+    ctx.scale(scaler, scaler);
+
+    try {
+      let win = this.tab.linkedBrowser.contentWindow;
+      ctx.drawWindow(win, rect.left, rect.top, rect.width, rect.height,
+                     bgColor, ctx.DRAWWINDOW_DO_NOT_FLUSH);
+    } catch (e) {
+      Utils.error('paint', e);
+    }
+
+    ctx.restore();
+  },
+
+  // ----------
+  // Function: _calculateClippingRect
+  // Calculate the clipping rect that will be projected to the tab's
+  // thumbnail canvas.
+  _calculateClippingRect: function TabCanvas__calculateClippingRect(origWidth, origHeight) {
+    let win = this.tab.linkedBrowser.contentWindow;
+
+    // TODO BUG 631593: retrieve actual scrollbar width
+    // 25px is supposed to be width of the vertical scrollbar
+    let maxWidth = Math.max(1, win.innerWidth - 25);
+    let maxHeight = win.innerHeight;
+
+    let height = Math.min(maxHeight, Math.floor(origHeight * maxWidth / origWidth));
+    let width = Math.floor(origWidth * height / origHeight);
+
+    // very short pages in combination with a very wide browser window force us
+    // to extend the clipping rect and add some empty space around the thumb
+    let factor = 0.7;
+    if (width < maxWidth * factor) {
+      width = maxWidth * factor;
+      height = Math.floor(origHeight * width / origWidth);
+    }
+
+    let left = win.scrollX + Math.max(0, Math.round((maxWidth - width) / 2));
+    let top = win.scrollY;
+
+    return new Rect(left, top, width, height);
+  },
+
+  // ----------
+  // Function: toImageData
+  toImageData: function TabCanvas_toImageData() {
+    return this.canvas.toDataURL("image/png");
+  }
+});
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+let FavIcons = {
+  // Pref that controls whether to display site icons.
+  PREF_CHROME_SITE_ICONS: "browser.chrome.site_icons",
+
+  // Pref that controls whether to display fav icons.
+  PREF_CHROME_FAVICONS: "browser.chrome.favicons",
+
+  // Lazy getter for pref browser.chrome.site_icons.
+  get _prefSiteIcons() {
+    delete this._prefSiteIcons;
+    this._prefSiteIcons = Services.prefs.getBoolPref(this.PREF_CHROME_SITE_ICONS);
+  },
+
+  // Lazy getter for pref browser.chrome.favicons.
+  get _prefFavicons() {
+    delete this._prefFavicons;
+    this._prefFavicons = Services.prefs.getBoolPref(this.PREF_CHROME_FAVICONS);
+  },
+
+  get defaultFavicon() this._favIconService.defaultFavicon.spec,
+
+  init: function FavIcons_init() {
+    XPCOMUtils.defineLazyServiceGetter(this, "_favIconService",
+      "@mozilla.org/browser/favicon-service;1", "nsIFaviconService");
+
+    Services.prefs.addObserver(this.PREF_CHROME_SITE_ICONS, this, false);
+    Services.prefs.addObserver(this.PREF_CHROME_FAVICONS, this, false);
+  },
+
+  uninit: function FavIcons_uninit() {
+    Services.prefs.removeObserver(this.PREF_CHROME_SITE_ICONS, this);
+    Services.prefs.removeObserver(this.PREF_CHROME_FAVICONS, this);
+  },
+
+  observe: function FavIcons_observe(subject, topic, data) {
+    let value = Services.prefs.getBoolPref(data);
+
+    if (data == this.PREF_CHROME_SITE_ICONS)
+      this._prefSiteIcons = value;
+    else if (data == this.PREF_CHROME_FAVICONS)
+      this._prefFavicons = value;
+  },
+
+  // ----------
+  // Function: getFavIconUrlForTab
+  // Gets the "favicon link URI" for the given xul:tab, or null if unavailable.
+  getFavIconUrlForTab: function FavIcons_getFavIconUrlForTab(tab, callback) {
+    this._isImageDocument(tab, function (isImageDoc) {
+      if (isImageDoc) {
+        callback(tab.pinned ? tab.image : null);
+      } else {
+        this._getFavIconForNonImageDocument(tab, callback);
+      }
+    }.bind(this));
+  },
+
+  // ----------
+  // Function: _getFavIconForNonImageDocument
+  // Retrieves the favicon for a tab containing a non-image document.
+  _getFavIconForNonImageDocument:
+    function FavIcons_getFavIconForNonImageDocument(tab, callback) {
+
+    if (tab.image)
+      this._getFavIconFromTabImage(tab, callback);
+    else if (this._shouldLoadFavIcon(tab))
+      this._getFavIconForHttpDocument(tab, callback);
+    else
+      callback(null);
+  },
+
+  // ----------
+  // Function: _getFavIconFromTabImage
+  // Retrieves the favicon for tab with a tab image.
+  _getFavIconFromTabImage:
+    function FavIcons_getFavIconFromTabImage(tab, callback) {
+
+    let tabImage = tab.image;
+
+    // If the tab image's url starts with http(s), fetch icon from favicon
+    // service via the moz-anno protocol.
+    if (/^https?:/.test(tabImage)) {
+      let tabImageURI = gWindow.makeURI(tabImage);
+      tabImage = this._favIconService.getFaviconLinkForIcon(tabImageURI).spec;
+    }
+
+    callback(tabImage);
+  },
+
+  // ----------
+  // Function: _getFavIconForHttpDocument
+  // Retrieves the favicon for tab containg a http(s) document.
+  _getFavIconForHttpDocument:
+    function FavIcons_getFavIconForHttpDocument(tab, callback) {
+
+    let {currentURI} = tab.linkedBrowser;
+    this._favIconService.getFaviconURLForPage(currentURI, function (uri) {
+      if (uri) {
+        callback(this._favIconService.getFaviconLinkForIcon(uri).spec);
+      } else {
+        callback(this.defaultFavicon);
+      }
+    }.bind(this));
+  },
+
+  // ----------
+  // Function: _isImageDocument
+  // Checks whether an image is loaded into the given tab.
+  _isImageDocument: function UI__isImageDocument(tab, callback) {
+    let mm = tab.linkedBrowser.messageManager;
+    let message = "Panorama:isImageDocument";
+
+    mm.addMessageListener(message, function onMessage(cx) {
+      mm.removeMessageListener(cx.name, onMessage);
+      callback(cx.json.isImageDocument);
+    });
+
+    mm.sendAsyncMessage(message);
+  },
+
+  // ----------
+  // Function: _shouldLoadFavIcon
+  // Checks whether fav icon should be loaded for a given tab.
+  _shouldLoadFavIcon: function FavIcons_shouldLoadFavIcon(tab) {
+    // No need to load a favicon if the user doesn't want site or favicons.
+    if (!this._prefSiteIcons || !this._prefFavicons)
+      return false;
+
+    let uri = tab.linkedBrowser.currentURI;
+
+    // Stop here if we don't have a valid nsIURI.
+    if (!uri || !(uri instanceof Ci.nsIURI))
+      return false;
+
+    // Load favicons for http(s) pages only.
+    return uri.schemeIs("http") || uri.schemeIs("https");
+  }
+};
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// **********
+// Title: drag.js
+
+// ----------
+// Variable: drag
+// The Drag that's currently in process.
+var drag = {
+  info: null,
+  zIndex: 100,
+  lastMoveTime: 0
+};
+
+//----------
+//Variable: resize
+//The resize (actually a Drag) that is currently in process
+var resize = {
+  info: null,
+  lastMoveTime: 0
+};
+
+// ##########
+// Class: Drag (formerly DragInfo)
+// Helper class for dragging <Item>s
+//
+// ----------
+// Constructor: Drag
+// Called to create a Drag in response to an <Item> draggable "start" event.
+// Note that it is also used partially during <Item>'s resizable method as well.
+//
+// Parameters:
+//   item - The <Item> being dragged
+//   event - The DOM event that kicks off the drag
+function Drag(item, event) {
+  Utils.assert(item && (item.isAnItem || item.isAFauxItem), 
+      'must be an item, or at least a faux item');
+
+  this.item = item;
+  this.el = item.container;
+  this.$el = iQ(this.el);
+  this.parent = this.item.parent;
+  this.startPosition = new Point(event.clientX, event.clientY);
+  this.startTime = Date.now();
+
+  this.item.isDragging = true;
+  this.item.setZ(999999);
+
+  this.safeWindowBounds = Items.getSafeWindowBounds();
+
+  Trenches.activateOthersTrenches(this.el);
+};
+
+Drag.prototype = {
+  // ----------
+  // Function: toString
+  // Prints [Drag (item)] for debug use
+  toString: function Drag_toString() {
+    return "[Drag (" + this.item + ")]";
+  },
+
+  // ----------
+  // Function: snapBounds
+  // Adjusts the given bounds according to the currently active trenches. Used by <Drag.snap>
+  //
+  // Parameters:
+  //   bounds             - (<Rect>) bounds
+  //   stationaryCorner   - which corner is stationary? by default, the top left in LTR mode,
+  //                        and top right in RTL mode.
+  //                        "topleft", "bottomleft", "topright", "bottomright"
+  //   assumeConstantSize - (boolean) whether the bounds' dimensions are sacred or not.
+  //   keepProportional   - (boolean) if assumeConstantSize is false, whether we should resize
+  //                        proportionally or not
+  //   checkItemStatus    - (boolean) make sure this is a valid item which should be snapped
+  snapBounds: function Drag_snapBounds(bounds, stationaryCorner, assumeConstantSize, keepProportional, checkItemStatus) {
+    if (!stationaryCorner)
+      stationaryCorner = UI.rtl ? 'topright' : 'topleft';
+    var update = false; // need to update
+    var updateX = false;
+    var updateY = false;
+    var newRect;
+    var snappedTrenches = {};
+
+    // OH SNAP!
+
+    // if we aren't holding down the meta key or have trenches disabled...
+    if (!Keys.meta && !Trenches.disabled) {
+      // snappable = true if we aren't a tab on top of something else, and
+      // there's no active drop site...
+      let snappable = !(this.item.isATabItem &&
+                       this.item.overlapsWithOtherItems()) &&
+                       !iQ(".acceptsDrop").length;
+      if (!checkItemStatus || snappable) {
+        newRect = Trenches.snap(bounds, stationaryCorner, assumeConstantSize,
+                                keepProportional);
+        if (newRect) { // might be false if no changes were made
+          update = true;
+          snappedTrenches = newRect.snappedTrenches || {};
+          bounds = newRect;
+        }
+      }
+    }
+
+    // make sure the bounds are in the window.
+    newRect = this.snapToEdge(bounds, stationaryCorner, assumeConstantSize,
+                              keepProportional);
+    if (newRect) {
+      update = true;
+      bounds = newRect;
+      Utils.extend(snappedTrenches, newRect.snappedTrenches);
+    }
+
+    Trenches.hideGuides();
+    for (var edge in snappedTrenches) {
+      var trench = snappedTrenches[edge];
+      if (typeof trench == 'object') {
+        trench.showGuide = true;
+        trench.show();
+      }
+    }
+
+    return update ? bounds : false;
+  },
+
+  // ----------
+  // Function: snap
+  // Called when a drag or mousemove occurs. Set the bounds based on the mouse move first, then
+  // call snap and it will adjust the item's bounds if appropriate. Also triggers the display of
+  // trenches that it snapped to.
+  //
+  // Parameters:
+  //   stationaryCorner   - which corner is stationary? by default, the top left in LTR mode,
+  //                        and top right in RTL mode.
+  //                        "topleft", "bottomleft", "topright", "bottomright"
+  //   assumeConstantSize - (boolean) whether the bounds' dimensions are sacred or not.
+  //   keepProportional   - (boolean) if assumeConstantSize is false, whether we should resize
+  //                        proportionally or not
+  snap: function Drag_snap(stationaryCorner, assumeConstantSize, keepProportional) {
+    var bounds = this.item.getBounds();
+    bounds = this.snapBounds(bounds, stationaryCorner, assumeConstantSize, keepProportional, true);
+    if (bounds) {
+      this.item.setBounds(bounds, true);
+      return true;
+    }
+    return false;
+  },
+
+  // --------
+  // Function: snapToEdge
+  // Returns a version of the bounds snapped to the edge if it is close enough. If not,
+  // returns false. If <Keys.meta> is true, this function will simply enforce the
+  // window edges.
+  //
+  // Parameters:
+  //   rect - (<Rect>) current bounds of the object
+  //   stationaryCorner   - which corner is stationary? by default, the top left in LTR mode,
+  //                        and top right in RTL mode.
+  //                        "topleft", "bottomleft", "topright", "bottomright"
+  //   assumeConstantSize - (boolean) whether the rect's dimensions are sacred or not
+  //   keepProportional   - (boolean) if we are allowed to change the rect's size, whether the
+  //                                  dimensions should scaled proportionally or not.
+  snapToEdge: function Drag_snapToEdge(rect, stationaryCorner, assumeConstantSize, keepProportional) {
+
+    var swb = this.safeWindowBounds;
+    var update = false;
+    var updateX = false;
+    var updateY = false;
+    var snappedTrenches = {};
+
+    var snapRadius = (Keys.meta ? 0 : Trenches.defaultRadius);
+    if (rect.left < swb.left + snapRadius ) {
+      if (stationaryCorner.indexOf('right') > -1 && !assumeConstantSize)
+        rect.width = rect.right - swb.left;
+      rect.left = swb.left;
+      update = true;
+      updateX = true;
+      snappedTrenches.left = 'edge';
+    }
+
+    if (rect.right > swb.right - snapRadius) {
+      if (updateX || !assumeConstantSize) {
+        var newWidth = swb.right - rect.left;
+        if (keepProportional)
+          rect.height = rect.height * newWidth / rect.width;
+        rect.width = newWidth;
+        update = true;
+      } else if (!updateX || !Trenches.preferLeft) {
+        rect.left = swb.right - rect.width;
+        update = true;
+      }
+      snappedTrenches.right = 'edge';
+      delete snappedTrenches.left;
+    }
+    if (rect.top < swb.top + snapRadius) {
+      if (stationaryCorner.indexOf('bottom') > -1 && !assumeConstantSize)
+        rect.height = rect.bottom - swb.top;
+      rect.top = swb.top;
+      update = true;
+      updateY = true;
+      snappedTrenches.top = 'edge';
+    }
+    if (rect.bottom > swb.bottom - snapRadius) {
+      if (updateY || !assumeConstantSize) {
+        var newHeight = swb.bottom - rect.top;
+        if (keepProportional)
+          rect.width = rect.width * newHeight / rect.height;
+        rect.height = newHeight;
+        update = true;
+      } else if (!updateY || !Trenches.preferTop) {
+        rect.top = swb.bottom - rect.height;
+        update = true;
+      }
+      snappedTrenches.top = 'edge';
+      delete snappedTrenches.bottom;
+    }
+
+    if (update) {
+      rect.snappedTrenches = snappedTrenches;
+      return rect;
+    }
+    return false;
+  },
+
+  // ----------
+  // Function: drag
+  // Called in response to an <Item> draggable "drag" event.
+  drag: function Drag_drag(event) {
+    this.snap(UI.rtl ? 'topright' : 'topleft', true);
+
+    if (this.parent && this.parent.expanded) {
+      var distance = this.startPosition.distance(new Point(event.clientX, event.clientY));
+      if (distance > 100) {
+        this.parent.remove(this.item);
+        this.parent.collapse();
+      }
+    }
+  },
+
+  // ----------
+  // Function: stop
+  // Called in response to an <Item> draggable "stop" event.
+  //
+  // Parameters:
+  //  immediately - bool for doing the pushAway immediately, without animation
+  stop: function Drag_stop(immediately) {
+    Trenches.hideGuides();
+    this.item.isDragging = false;
+
+    if (this.parent && this.parent != this.item.parent)
+      this.parent.closeIfEmpty();
+
+    if (this.parent && this.parent.expanded)
+      this.parent.arrange();
+
+    if (this.item.parent)
+      this.item.parent.arrange();
+
+    if (this.item.isAGroupItem) {
+      this.item.setZ(drag.zIndex);
+      drag.zIndex++;
+
+      this.item.pushAway(immediately);
+    }
+
+    Trenches.disactivate();
+  }
+};
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// **********
+// Title: trench.js
+
+// ##########
+// Class: Trench
+//
+// Class for drag-snapping regions; called "trenches" as they are long and narrow.
+
+// Constructor: Trench
+//
+// Parameters:
+//   element - the DOM element for Item (GroupItem or TabItem) from which the trench is projected
+//   xory - either "x" or "y": whether the trench's <position> is along the x- or y-axis.
+//     In other words, if "x", the trench is vertical; if "y", the trench is horizontal.
+//   type - either "border" or "guide". Border trenches mark the border of an Item.
+//     Guide trenches extend out (unless they are intercepted) and act as "guides".
+//   edge - which edge of the Item that this trench corresponds to.
+//     Either "top", "left", "bottom", or "right".
+function Trench(element, xory, type, edge) {
+  //----------
+  // Variable: id
+  // (integer) The id for the Trench. Set sequentially via <Trenches.nextId>
+  this.id = Trenches.nextId++;
+
+  // ---------
+  // Variables: Initial parameters
+  //   element - (DOMElement)
+  //   parentItem - <Item> which projects this trench; to be set with setParentItem
+  //   xory - (string) "x" or "y"
+  //   type - (string) "border" or "guide"
+  //   edge - (string) "top", "left", "bottom", or "right"
+  this.el = element;
+  this.parentItem = null;
+  this.xory = xory; // either "x" or "y"
+  this.type = type; // "border" or "guide"
+  this.edge = edge; // "top", "left", "bottom", or "right"
+
+  this.$el = iQ(this.el);
+
+  //----------
+  // Variable: dom
+  // (array) DOM elements for visible reflexes of the Trench
+  this.dom = [];
+
+  //----------
+  // Variable: showGuide
+  // (boolean) Whether this trench will project a visible guide (dotted line) or not.
+  this.showGuide = false;
+
+  //----------
+  // Variable: active
+  // (boolean) Whether this trench is currently active or not.
+  // Basically every trench aside for those projected by the Item currently being dragged
+  // all become active.
+  this.active = false;
+  this.gutter = Items.defaultGutter;
+
+  //----------
+  // Variable: position
+  // (integer) position is the position that we should snap to.
+  this.position = 0;
+
+  //----------
+  // Variables: some Ranges
+  //   range - (<Range>) explicit range; this is along the transverse axis
+  //   minRange - (<Range>) the minimum active range
+  //   activeRange - (<Range>) the currently active range
+  this.range = new Range(0,10000);
+  this.minRange = new Range(0,0);
+  this.activeRange = new Range(0,10000);
+};
+
+Trench.prototype = {
+  // ----------
+  // Function: toString
+  // Prints [Trench edge type (parentItem)] for debug use
+  toString: function Trench_toString() {
+    return "[Trench " + this.edge + " " + this.type +
+           (this.parentItem ? " (" + this.parentItem + ")" : "") +
+           "]";
+  },
+
+  //----------
+  // Variable: radius
+  // (integer) radius is how far away we should snap from
+  get radius() this.customRadius || Trenches.defaultRadius,
+
+  setParentItem: function Trench_setParentItem(item) {
+    if (!item.isAnItem) {
+      Utils.assert(false, "parentItem must be an Item");
+      return false;
+    }
+    this.parentItem = item;
+    return true;
+  },
+
+  //----------
+  // Function: setPosition
+  // set the trench's position.
+  //
+  // Parameters:
+  //   position - (integer) px center position of the trench
+  //   range - (<Range>) the explicit active range of the trench
+  //   minRange - (<Range>) the minimum range of the trench
+  setPosition: function Trench_setPosition(position, range, minRange) {
+    this.position = position;
+
+    var page = Items.getPageBounds(true);
+
+    // optionally, set the range.
+    if (Utils.isRange(range)) {
+      this.range = range;
+    } else {
+      this.range = new Range(0, (this.xory == 'x' ? page.height : page.width));
+    }
+
+    // if there's a minRange, set that too.
+    if (Utils.isRange(minRange))
+      this.minRange = minRange;
+
+    // set the appropriate bounds as a rect.
+    if (this.xory == "x") // vertical
+      this.rect = new Rect(this.position - this.radius, this.range.min, 2 * this.radius, this.range.extent);
+    else // horizontal
+      this.rect = new Rect(this.range.min, this.position - this.radius, this.range.extent, 2 * this.radius);
+
+    this.show(); // DEBUG
+  },
+
+  //----------
+  // Function: setActiveRange
+  // set the trench's currently active range.
+  //
+  // Parameters:
+  //   activeRange - (<Range>)
+  setActiveRange: function Trench_setActiveRange(activeRange) {
+    if (!Utils.isRange(activeRange))
+      return false;
+    this.activeRange = activeRange;
+    if (this.xory == "x") { // horizontal
+      this.activeRect = new Rect(this.position - this.radius, this.activeRange.min, 2 * this.radius, this.activeRange.extent);
+      this.guideRect = new Rect(this.position, this.activeRange.min, 0, this.activeRange.extent);
+    } else { // vertical
+      this.activeRect = new Rect(this.activeRange.min, this.position - this.radius, this.activeRange.extent, 2 * this.radius);
+      this.guideRect = new Rect(this.activeRange.min, this.position, this.activeRange.extent, 0);
+    }
+    return true;
+  },
+
+  //----------
+  // Function: setWithRect
+  // Set the trench's position using the given rect. We know which side of the rect we should match
+  // because we've already recorded this information in <edge>.
+  //
+  // Parameters:
+  //   rect - (<Rect>)
+  setWithRect: function Trench_setWithRect(rect) {
+
+    if (!Utils.isRect(rect))
+      Utils.error('argument must be Rect');
+
+    // First, calculate the range for this trench.
+    // Border trenches are always only active for the length of this range.
+    // Guide trenches, however, still use this value as its minRange.
+    if (this.xory == "x")
+      var range = new Range(rect.top - this.gutter, rect.bottom + this.gutter);
+    else
+      var range = new Range(rect.left - this.gutter, rect.right + this.gutter);
+
+    if (this.type == "border") {
+      // border trenches have a range, so set that too.
+      if (this.edge == "left")
+        this.setPosition(rect.left - this.gutter, range);
+      else if (this.edge == "right")
+        this.setPosition(rect.right + this.gutter, range);
+      else if (this.edge == "top")
+        this.setPosition(rect.top - this.gutter, range);
+      else if (this.edge == "bottom")
+        this.setPosition(rect.bottom + this.gutter, range);
+    } else if (this.type == "guide") {
+      // guide trenches have no range, but do have a minRange.
+      if (this.edge == "left")
+        this.setPosition(rect.left, false, range);
+      else if (this.edge == "right")
+        this.setPosition(rect.right, false, range);
+      else if (this.edge == "top")
+        this.setPosition(rect.top, false, range);
+      else if (this.edge == "bottom")
+        this.setPosition(rect.bottom, false, range);
+    }
+  },
+
+  //----------
+  // Function: show
+  //
+  // Show guide (dotted line), if <showGuide> is true.
+  //
+  // If <Trenches.showDebug> is true, we will draw the trench. Active portions are drawn with 0.5
+  // opacity. If <active> is false, the entire trench will be
+  // very translucent.
+  show: function Trench_show() { // DEBUG
+    if (this.active && this.showGuide) {
+      if (!this.dom.guideTrench)
+        this.dom.guideTrench = iQ("<div/>").addClass('guideTrench').css({id: 'guideTrench'+this.id});
+      var guideTrench = this.dom.guideTrench;
+      guideTrench.css(this.guideRect);
+      iQ("body").append(guideTrench);
+    } else {
+      if (this.dom.guideTrench) {
+        this.dom.guideTrench.remove();
+        delete this.dom.guideTrench;
+      }
+    }
+
+    if (!Trenches.showDebug) {
+      this.hide(true); // true for dontHideGuides
+      return;
+    }
+
+    if (!this.dom.visibleTrench)
+      this.dom.visibleTrench = iQ("<div/>")
+        .addClass('visibleTrench')
+        .addClass(this.type) // border or guide
+        .css({id: 'visibleTrench'+this.id});
+    var visibleTrench = this.dom.visibleTrench;
+
+    if (!this.dom.activeVisibleTrench)
+      this.dom.activeVisibleTrench = iQ("<div/>")
+        .addClass('activeVisibleTrench')
+        .addClass(this.type) // border or guide
+        .css({id: 'activeVisibleTrench'+this.id});
+    var activeVisibleTrench = this.dom.activeVisibleTrench;
+
+    if (this.active)
+      activeVisibleTrench.addClass('activeTrench');
+    else
+      activeVisibleTrench.removeClass('activeTrench');
+
+    visibleTrench.css(this.rect);
+    activeVisibleTrench.css(this.activeRect || this.rect);
+    iQ("body").append(visibleTrench);
+    iQ("body").append(activeVisibleTrench);
+  },
+
+  //----------
+  // Function: hide
+  // Hide the trench.
+  hide: function Trench_hide(dontHideGuides) {
+    if (this.dom.visibleTrench)
+      this.dom.visibleTrench.remove();
+    if (this.dom.activeVisibleTrench)
+      this.dom.activeVisibleTrench.remove();
+    if (!dontHideGuides && this.dom.guideTrench)
+      this.dom.guideTrench.remove();
+  },
+
+  //----------
+  // Function: rectOverlaps
+  // Given a <Rect>, compute whether it overlaps with this trench. If it does, return an
+  // adjusted ("snapped") <Rect>; if it does not overlap, simply return false.
+  //
+  // Note that simply overlapping is not all that is required to be affected by this function.
+  // Trenches can only affect certain edges of rectangles... for example, a "left"-edge guide
+  // trench should only affect left edges of rectangles. We don't snap right edges to left-edged
+  // guide trenches. For border trenches, the logic is a bit different, so left snaps to right and
+  // top snaps to bottom.
+  //
+  // Parameters:
+  //   rect - (<Rect>) the rectangle in question
+  //   stationaryCorner   - which corner is stationary? by default, the top left.
+  //                        "topleft", "bottomleft", "topright", "bottomright"
+  //   assumeConstantSize - (boolean) whether the rect's dimensions are sacred or not
+  //   keepProportional - (boolean) if we are allowed to change the rect's size, whether the
+  //                                dimensions should scaled proportionally or not.
+  //
+  // Returns:
+  //   false - if rect does not overlap with this trench
+  //   newRect - (<Rect>) an adjusted version of rect, if it is affected by this trench
+  rectOverlaps: function Trench_rectOverlaps(rect,stationaryCorner,assumeConstantSize,keepProportional) {
+    var edgeToCheck;
+    if (this.type == "border") {
+      if (this.edge == "left")
+        edgeToCheck = "right";
+      else if (this.edge == "right")
+        edgeToCheck = "left";
+      else if (this.edge == "top")
+        edgeToCheck = "bottom";
+      else if (this.edge == "bottom")
+        edgeToCheck = "top";
+    } else { // if trench type is guide or barrier...
+      edgeToCheck = this.edge;
+    }
+
+    rect.adjustedEdge = edgeToCheck;
+
+    switch (edgeToCheck) {
+      case "left":
+        if (this.ruleOverlaps(rect.left, rect.yRange)) {
+          if (stationaryCorner.indexOf('right') > -1)
+            rect.width = rect.right - this.position;
+          rect.left = this.position;
+          return rect;
+        }
+        break;
+      case "right":
+        if (this.ruleOverlaps(rect.right, rect.yRange)) {
+          if (assumeConstantSize) {
+            rect.left = this.position - rect.width;
+          } else {
+            var newWidth = this.position - rect.left;
+            if (keepProportional)
+              rect.height = rect.height * newWidth / rect.width;
+            rect.width = newWidth;
+          }
+          return rect;
+        }
+        break;
+      case "top":
+        if (this.ruleOverlaps(rect.top, rect.xRange)) {
+          if (stationaryCorner.indexOf('bottom') > -1)
+            rect.height = rect.bottom - this.position;
+          rect.top = this.position;
+          return rect;
+        }
+        break;
+      case "bottom":
+        if (this.ruleOverlaps(rect.bottom, rect.xRange)) {
+          if (assumeConstantSize) {
+            rect.top = this.position - rect.height;
+          } else {
+            var newHeight = this.position - rect.top;
+            if (keepProportional)
+              rect.width = rect.width * newHeight / rect.height;
+            rect.height = newHeight;
+          }
+          return rect;
+        }
+    }
+
+    return false;
+  },
+
+  //----------
+  // Function: ruleOverlaps
+  // Computes whether the given "rule" (a line segment, essentially), given by the position and
+  // range arguments, overlaps with the current trench. Note that this function assumes that
+  // the rule and the trench are in the same direction: both horizontal, or both vertical.
+  //
+  // Parameters:
+  //   position - (integer) a position in px
+  //   range - (<Range>) the rule's range
+  ruleOverlaps: function Trench_ruleOverlaps(position, range) {
+    return (this.position - this.radius < position &&
+           position < this.position + this.radius &&
+           this.activeRange.overlaps(range));
+  },
+
+  //----------
+  // Function: adjustRangeIfIntercept
+  // Computes whether the given boundary (given as a position and its active range), perpendicular
+  // to the trench, intercepts the trench or not. If it does, it returns an adjusted <Range> for
+  // the trench. If not, it returns false.
+  //
+  // Parameters:
+  //   position - (integer) the position of the boundary
+  //   range - (<Range>) the target's range, on the trench's transverse axis
+  adjustRangeIfIntercept: function Trench_adjustRangeIfIntercept(position, range) {
+    if (this.position - this.radius > range.min && this.position + this.radius < range.max) {
+      var activeRange = new Range(this.activeRange);
+
+      // there are three ways this can go:
+      // 1. position < minRange.min
+      // 2. position > minRange.max
+      // 3. position >= minRange.min && position <= minRange.max
+
+      if (position < this.minRange.min) {
+        activeRange.min = Math.min(this.minRange.min,position);
+      } else if (position > this.minRange.max) {
+        activeRange.max = Math.max(this.minRange.max,position);
+      } else {
+        // this should be impossible because items can't overlap and we've already checked
+        // that the range intercepts.
+      }
+      return activeRange;
+    }
+    return false;
+  },
+
+  //----------
+  // Function: calculateActiveRange
+  // Computes and sets the <activeRange> for the trench, based on the <GroupItems> around.
+  // This makes it so trenches' active ranges don't extend through other groupItems.
+  calculateActiveRange: function Trench_calculateActiveRange() {
+
+    // set it to the default: just the range itself.
+    this.setActiveRange(this.range);
+
+    // only guide-type trenches need to set a separate active range
+    if (this.type != 'guide')
+      return;
+
+    var groupItems = GroupItems.groupItems;
+    var trench = this;
+    groupItems.forEach(function(groupItem) {
+      if (groupItem.isDragging) // floating groupItems don't block trenches
+        return;
+      if (trench.el == groupItem.container) // groupItems don't block their own trenches
+        return;
+      var bounds = groupItem.getBounds();
+      var activeRange = new Range();
+      if (trench.xory == 'y') { // if this trench is horizontal...
+        activeRange = trench.adjustRangeIfIntercept(bounds.left, bounds.yRange);
+        if (activeRange)
+          trench.setActiveRange(activeRange);
+        activeRange = trench.adjustRangeIfIntercept(bounds.right, bounds.yRange);
+        if (activeRange)
+          trench.setActiveRange(activeRange);
+      } else { // if this trench is vertical...
+        activeRange = trench.adjustRangeIfIntercept(bounds.top, bounds.xRange);
+        if (activeRange)
+          trench.setActiveRange(activeRange);
+        activeRange = trench.adjustRangeIfIntercept(bounds.bottom, bounds.xRange);
+        if (activeRange)
+          trench.setActiveRange(activeRange);
+      }
+    });
+  }
+};
+
+// ##########
+// Class: Trenches
+// Singelton for managing all <Trench>es.
+var Trenches = {
+  // ---------
+  // Variables:
+  //   nextId - (integer) a counter for the next <Trench>'s <Trench.id> value.
+  //   showDebug - (boolean) whether to draw the <Trench>es or not.
+  //   defaultRadius - (integer) the default radius for new <Trench>es.
+  //   disabled - (boolean) whether trench-snapping is disabled or not.
+  nextId: 0,
+  showDebug: false,
+  defaultRadius: 10,
+  disabled: false,
+
+  // ---------
+  // Variables: snapping preferences; used to break ties in snapping.
+  //   preferTop - (boolean) prefer snapping to the top to the bottom
+  //   preferLeft - (boolean) prefer snapping to the left to the right
+  preferTop: true,
+  get preferLeft() { return !UI.rtl; },
+
+  trenches: [],
+
+  // ----------
+  // Function: toString
+  // Prints [Trenches count=count] for debug use
+  toString: function Trenches_toString() {
+    return "[Trenches count=" + this.trenches.length + "]";
+  },
+
+  // ---------
+  // Function: getById
+  // Return the specified <Trench>.
+  //
+  // Parameters:
+  //   id - (integer)
+  getById: function Trenches_getById(id) {
+    return this.trenches[id];
+  },
+
+  // ---------
+  // Function: register
+  // Register a new <Trench> and returns the resulting <Trench> ID.
+  //
+  // Parameters:
+  // See the constructor <Trench.Trench>'s parameters.
+  //
+  // Returns:
+  //   id - (int) the new <Trench>'s ID.
+  register: function Trenches_register(element, xory, type, edge) {
+    var trench = new Trench(element, xory, type, edge);
+    this.trenches[trench.id] = trench;
+    return trench.id;
+  },
+
+  // ---------
+  // Function: registerWithItem
+  // Register a whole set of <Trench>es using an <Item> and returns the resulting <Trench> IDs.
+  //
+  // Parameters:
+  //   item - the <Item> to project trenches
+  //   type - either "border" or "guide"
+  //
+  // Returns:
+  //   ids - array of the new <Trench>es' IDs.
+  registerWithItem: function Trenches_registerWithItem(item, type) {
+    var container = item.container;
+    var ids = {};
+    ids.left = Trenches.register(container,"x",type,"left");
+    ids.right = Trenches.register(container,"x",type,"right");
+    ids.top = Trenches.register(container,"y",type,"top");
+    ids.bottom = Trenches.register(container,"y",type,"bottom");
+
+    this.getById(ids.left).setParentItem(item);
+    this.getById(ids.right).setParentItem(item);
+    this.getById(ids.top).setParentItem(item);
+    this.getById(ids.bottom).setParentItem(item);
+
+    return ids;
+  },
+
+  // ---------
+  // Function: unregister
+  // Unregister one or more <Trench>es.
+  //
+  // Parameters:
+  //   ids - (integer) a single <Trench> ID or (array) a list of <Trench> IDs.
+  unregister: function Trenches_unregister(ids) {
+    if (!Array.isArray(ids))
+      ids = [ids];
+    var self = this;
+    ids.forEach(function(id) {
+      self.trenches[id].hide();
+      delete self.trenches[id];
+    });
+  },
+
+  // ---------
+  // Function: activateOthersTrenches
+  // Activate all <Trench>es other than those projected by the current element.
+  //
+  // Parameters:
+  //   element - (DOMElement) the DOM element of the Item being dragged or resized.
+  activateOthersTrenches: function Trenches_activateOthersTrenches(element) {
+    this.trenches.forEach(function(t) {
+      if (t.el === element)
+        return;
+      if (t.parentItem && (t.parentItem.isAFauxItem || t.parentItem.isDragging))
+        return;
+      t.active = true;
+      t.calculateActiveRange();
+      t.show(); // debug
+    });
+  },
+
+  // ---------
+  // Function: disactivate
+  // After <activateOthersTrenches>, disactivates all the <Trench>es again.
+  disactivate: function Trenches_disactivate() {
+    this.trenches.forEach(function(t) {
+      t.active = false;
+      t.showGuide = false;
+      t.show();
+    });
+  },
+
+  // ---------
+  // Function: hideGuides
+  // Hide all guides (dotted lines) en masse.
+  hideGuides: function Trenches_hideGuides() {
+    this.trenches.forEach(function(t) {
+      t.showGuide = false;
+      t.show();
+    });
+  },
+
+  // ---------
+  // Function: snap
+  // Used to "snap" an object's bounds to active trenches and to the edge of the window.
+  // If the meta key is down (<Key.meta>), it will not snap but will still enforce the rect
+  // not leaving the safe bounds of the window.
+  //
+  // Parameters:
+  //   rect               - (<Rect>) the object's current bounds
+  //   stationaryCorner   - which corner is stationary? by default, the top left.
+  //                        "topleft", "bottomleft", "topright", "bottomright"
+  //   assumeConstantSize - (boolean) whether the rect's dimensions are sacred or not
+  //   keepProportional   - (boolean) if we are allowed to change the rect's size, whether the
+  //                                  dimensions should scaled proportionally or not.
+  //
+  // Returns:
+  //   (<Rect>) - the updated bounds, if they were updated
+  //   false - if the bounds were not updated
+  snap: function Trenches_snap(rect,stationaryCorner,assumeConstantSize,keepProportional) {
+    // hide all the guide trenches, because the correct ones will be turned on later.
+    Trenches.hideGuides();
+
+    var updated = false;
+    var updatedX = false;
+    var updatedY = false;
+
+    var snappedTrenches = {};
+
+    for (var i in this.trenches) {
+      var t = this.trenches[i];
+      if (!t.active)
+        continue;
+      // newRect will be a new rect, or false
+      var newRect = t.rectOverlaps(rect,stationaryCorner,assumeConstantSize,keepProportional);
+
+      if (newRect) { // if rectOverlaps returned an updated rect...
+
+        if (assumeConstantSize && updatedX && updatedY)
+          break;
+        if (assumeConstantSize && updatedX && (newRect.adjustedEdge == "left"||newRect.adjustedEdge == "right"))
+          continue;
+        if (assumeConstantSize && updatedY && (newRect.adjustedEdge == "top"||newRect.adjustedEdge == "bottom"))
+          continue;
+
+        rect = newRect;
+        updated = true;
+
+        // register this trench as the "snapped trench" for the appropriate edge.
+        snappedTrenches[newRect.adjustedEdge] = t;
+
+        // if updatedX, we don't need to update x any more.
+        if (newRect.adjustedEdge == "left" && this.preferLeft)
+          updatedX = true;
+        if (newRect.adjustedEdge == "right" && !this.preferLeft)
+          updatedX = true;
+
+        // if updatedY, we don't need to update x any more.
+        if (newRect.adjustedEdge == "top" && this.preferTop)
+          updatedY = true;
+        if (newRect.adjustedEdge == "bottom" && !this.preferTop)
+          updatedY = true;
+
+      }
+    }
+
+    if (updated) {
+      rect.snappedTrenches = snappedTrenches;
+      return rect;
+    }
+    return false;
+  },
+
+  // ---------
+  // Function: show
+  // <Trench.show> all <Trench>es.
+  show: function Trenches_show() {
+    this.trenches.forEach(function(t) {
+      t.show();
+    });
+  },
+
+  // ---------
+  // Function: toggleShown
+  // Toggle <Trenches.showDebug> and trigger <Trenches.show>
+  toggleShown: function Trenches_toggleShown() {
+    this.showDebug = !this.showDebug;
+    this.show();
+  }
+};
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/* ******************************
+ *
+ * This file incorporates work from:
+ * Quicksilver Score (qs_score):
+ * http://rails-oceania.googlecode.com/svn/lachiecox/qs_score/trunk/qs_score.js
+ * This incorporated work is covered by the following copyright and
+ * permission notice:
+ * Copyright 2008 Lachie Cox
+ * Licensed under the MIT license.
+ * http://jquery.org/license
+ *
+ *  ***************************** */
+
+// **********
+// Title: search.js
+// Implementation for the search functionality of Firefox Panorama.
+
+// ##########
+// Class: TabUtils
+//
+// A collection of helper functions for dealing with both <TabItem>s and
+// <xul:tab>s without having to worry which one is which.
+let TabUtils = {
+  // ----------
+  // Function: toString
+  // Prints [TabUtils] for debug use.
+  toString: function TabUtils_toString() {
+    return "[TabUtils]";
+  },
+
+  // ---------
+  // Function: nameOfTab
+  // Given a <TabItem> or a <xul:tab> returns the tab's name.
+  nameOf: function TabUtils_nameOf(tab) {
+    // We can have two types of tabs: A <TabItem> or a <xul:tab>
+    // because we have to deal with both tabs represented inside
+    // of active Panoramas as well as for windows in which
+    // Panorama has yet to be activated. We uses object sniffing to
+    // determine the type of tab and then returns its name.     
+    return tab.label != undefined ? tab.label : tab.$tabTitle[0].textContent;
+  },
+
+  // ---------
+  // Function: URLOf
+  // Given a <TabItem> or a <xul:tab> returns the URL of tab.
+  URLOf: function TabUtils_URLOf(tab) {
+    // Convert a <TabItem> to <xul:tab>
+    if ("tab" in tab)
+      tab = tab.tab;
+    return tab.linkedBrowser.currentURI.spec;
+  },
+
+  // ---------
+  // Function: faviconURLOf
+  // Given a <TabItem> or a <xul:tab> returns the URL of tab's favicon.
+  faviconURLOf: function TabUtils_faviconURLOf(tab) {
+    return tab.image != undefined ? tab.image : tab.$favImage[0].src;
+  },
+
+  // ---------
+  // Function: focus
+  // Given a <TabItem> or a <xul:tab>, focuses it and it's window.
+  focus: function TabUtils_focus(tab) {
+    // Convert a <TabItem> to a <xul:tab>
+    if ("tab" in tab)
+      tab = tab.tab;
+    tab.ownerDocument.defaultView.gBrowser.selectedTab = tab;
+    tab.ownerDocument.defaultView.focus();
+  }
+};
+
+// ##########
+// Class: TabMatcher
+//
+// A class that allows you to iterate over matching and not-matching tabs, 
+// given a case-insensitive search term.
+function TabMatcher(term) {
+  this.term = term;
+}
+
+TabMatcher.prototype = {
+  // ----------
+  // Function: toString
+  // Prints [TabMatcher (term)] for debug use.
+  toString: function TabMatcher_toString() {
+    return "[TabMatcher (" + this.term + ")]";
+  },
+
+  // ---------
+  // Function: _filterAndSortForMatches
+  // Given an array of <TabItem>s and <xul:tab>s returns a new array
+  // of tabs whose name matched the search term, sorted by lexical
+  // closeness.
+  _filterAndSortForMatches: function TabMatcher__filterAndSortForMatches(tabs) {
+    let self = this;
+    tabs = tabs.filter(function TabMatcher__filterAndSortForMatches_filter(tab) {
+      let name = TabUtils.nameOf(tab);
+      let url = TabUtils.URLOf(tab);
+      return name.match(self.term, "i") || url.match(self.term, "i");
+    });
+
+    tabs.sort(function TabMatcher__filterAndSortForMatches_sort(x, y) {
+      let yScore = self._scorePatternMatch(self.term, TabUtils.nameOf(y));
+      let xScore = self._scorePatternMatch(self.term, TabUtils.nameOf(x));
+      return yScore - xScore;
+    });
+
+    return tabs;
+  },
+
+  // ---------
+  // Function: _filterForUnmatches
+  // Given an array of <TabItem>s returns an unsorted array of tabs whose name
+  // does not match the the search term.
+  _filterForUnmatches: function TabMatcher__filterForUnmatches(tabs) {
+    let self = this;
+    return tabs.filter(function TabMatcher__filterForUnmatches_filter(tab) {
+      let name = tab.$tabTitle[0].textContent;
+      let url = TabUtils.URLOf(tab);
+      return !name.match(self.term, "i") && !url.match(self.term, "i");
+    });
+  },
+
+  // ---------
+  // Function: _getTabsForOtherWindows
+  // Returns an array of <TabItem>s and <xul:tabs>s representing tabs
+  // from all windows but the current window. <TabItem>s will be returned
+  // for windows in which Panorama has been activated at least once, while
+  // <xul:tab>s will be returned for windows in which Panorama has never
+  // been activated.
+  _getTabsForOtherWindows: function TabMatcher__getTabsForOtherWindows() {
+    let enumerator = Services.wm.getEnumerator("navigator:browser");
+    let allTabs = [];
+
+    while (enumerator.hasMoreElements()) {
+      let win = enumerator.getNext();
+      // This function gets tabs from other windows, not from the current window
+      if (win != gWindow)
+        allTabs.push.apply(allTabs, win.gBrowser.tabs);
+    }
+    return allTabs;
+  },
+
+  // ----------
+  // Function: matchedTabsFromOtherWindows
+  // Returns an array of <TabItem>s and <xul:tab>s that match the search term
+  // from all windows but the current window. <TabItem>s will be returned for
+  // windows in which Panorama has been activated at least once, while
+  // <xul:tab>s will be returned for windows in which Panorama has never
+  // been activated.
+  // (new TabMatcher("app")).matchedTabsFromOtherWindows();
+  matchedTabsFromOtherWindows: function TabMatcher_matchedTabsFromOtherWindows() {
+    if (this.term.length < 2)
+      return [];
+
+    let tabs = this._getTabsForOtherWindows();
+    return this._filterAndSortForMatches(tabs);
+  },
+
+  // ----------
+  // Function: matched
+  // Returns an array of <TabItem>s which match the current search term.
+  // If the term is less than 2 characters in length, it returns nothing.
+  matched: function TabMatcher_matched() {
+    if (this.term.length < 2)
+      return [];
+
+    let tabs = TabItems.getItems();
+    return this._filterAndSortForMatches(tabs);
+  },
+
+  // ----------
+  // Function: unmatched
+  // Returns all of <TabItem>s that .matched() doesn't return.
+  unmatched: function TabMatcher_unmatched() {
+    let tabs = TabItems.getItems();
+    if (this.term.length < 2)
+      return tabs;
+
+    return this._filterForUnmatches(tabs);
+  },
+
+  // ----------
+  // Function: doSearch
+  // Performs the search. Lets you provide three functions.
+  // The first is on all matched tabs in the window, the second on all unmatched
+  // tabs in the window, and the third on all matched tabs in other windows.
+  // The first two functions take two parameters: A <TabItem> and its integer index
+  // indicating the absolute rank of the <TabItem> in terms of match to
+  // the search term. The last function also takes two paramaters, but can be
+  // passed both <TabItem>s and <xul:tab>s and the index is offset by the
+  // number of matched tabs inside the window.
+  doSearch: function TabMatcher_doSearch(matchFunc, unmatchFunc, otherFunc) {
+    let matches = this.matched();
+    let unmatched = this.unmatched();
+    let otherMatches = this.matchedTabsFromOtherWindows();
+    
+    matches.forEach(function(tab, i) {
+      matchFunc(tab, i);
+    });
+
+    otherMatches.forEach(function(tab,i) {
+      otherFunc(tab, i+matches.length);
+    });
+
+    unmatched.forEach(function(tab, i) {
+      unmatchFunc(tab, i);
+    });
+  },
+
+  // ----------
+  // Function: _scorePatternMatch
+  // Given a pattern string, returns a score between 0 and 1 of how well
+  // that pattern matches the original string. It mimics the heuristics
+  // of the Mac application launcher Quicksilver.
+  _scorePatternMatch: function TabMatcher__scorePatternMatch(pattern, matched, offset) {
+    offset = offset || 0;
+    pattern = pattern.toLowerCase();
+    matched = matched.toLowerCase();
+
+    if (pattern.length == 0)
+      return 0.9;
+    if (pattern.length > matched.length)
+      return 0.0;
+
+    for (let i = pattern.length; i > 0; i--) {
+      let sub_pattern = pattern.substring(0,i);
+      let index = matched.indexOf(sub_pattern);
+
+      if (index < 0)
+        continue;
+      if (index + pattern.length > matched.length + offset)
+        continue;
+
+      let next_string = matched.substring(index+sub_pattern.length);
+      let next_pattern = null;
+
+      if (i >= pattern.length)
+        next_pattern = '';
+      else
+        next_pattern = pattern.substring(i);
+
+      let remaining_score = this._scorePatternMatch(next_pattern, next_string, offset + index);
+
+      if (remaining_score > 0) {
+        let score = matched.length-next_string.length;
+
+        if (index != 0) {
+          let c = matched.charCodeAt(index-1);
+          if (c == 32 || c == 9) {
+            for (let j = (index - 2); j >= 0; j--) {
+              c = matched.charCodeAt(j);
+              score -= ((c == 32 || c == 9) ? 1 : 0.15);
+            }
+          } else {
+            score -= index;
+          }
+        }
+
+        score += remaining_score * next_string.length;
+        score /= matched.length;
+        return score;
+      }
+    }
+    return 0.0;
+  }
+};
+
+// ##########
+// Class: TabHandlers
+// 
+// A object that handles all of the event handlers.
+let TabHandlers = {
+  _mouseDownLocation: null,
+
+  // ---------
+  // Function: onMatch
+  // Adds styles and event listeners to the matched tab items.
+  onMatch: function TabHandlers_onMatch(tab, index) {
+    tab.addClass("onTop");
+    index != 0 ? tab.addClass("notMainMatch") : tab.removeClass("notMainMatch");
+
+    // Remove any existing handlers before adding the new ones.
+    // If we don't do this, then we may add more handlers than
+    // we remove.
+    tab.$canvas
+      .unbind("mousedown", TabHandlers._hideHandler)
+      .unbind("mouseup", TabHandlers._showHandler);
+
+    tab.$canvas
+      .mousedown(TabHandlers._hideHandler)
+      .mouseup(TabHandlers._showHandler);
+  },
+
+  // ---------
+  // Function: onUnmatch
+  // Removes styles and event listeners from the unmatched tab items.
+  onUnmatch: function TabHandlers_onUnmatch(tab, index) {
+    tab.$container.removeClass("onTop");
+    tab.removeClass("notMainMatch");
+
+    tab.$canvas
+      .unbind("mousedown", TabHandlers._hideHandler)
+      .unbind("mouseup", TabHandlers._showHandler);
+  },
+
+  // ---------
+  // Function: onOther
+  // Removes styles and event listeners from the unmatched tabs.
+  onOther: function TabHandlers_onOther(tab, index) {
+    // Unlike the other on* functions, in this function tab can
+    // either be a <TabItem> or a <xul:tab>. In other functions
+    // it is always a <TabItem>. Also note that index is offset
+    // by the number of matches within the window.
+    let item = iQ("<div/>")
+      .addClass("inlineMatch")
+      .click(function TabHandlers_onOther_click(event) {
+        Search.hide(event);
+        TabUtils.focus(tab);
+      });
+
+    iQ("<img/>")
+      .attr("src", TabUtils.faviconURLOf(tab))
+      .appendTo(item);
+
+    iQ("<span/>")
+      .text(TabUtils.nameOf(tab))
+      .appendTo(item);
+
+    index != 0 ? item.addClass("notMainMatch") : item.removeClass("notMainMatch");
+    item.appendTo("#results");
+    iQ("#otherresults").show();
+  },
+
+  // ---------
+  // Function: _hideHandler
+  // Performs when mouse down on a canvas of tab item.
+  _hideHandler: function TabHandlers_hideHandler(event) {
+    iQ("#search").fadeOut();
+    iQ("#searchshade").fadeOut();
+    TabHandlers._mouseDownLocation = {x:event.clientX, y:event.clientY};
+  },
+
+  // ---------
+  // Function: _showHandler
+  // Performs when mouse up on a canvas of tab item.
+  _showHandler: function TabHandlers_showHandler(event) {
+    // If the user clicks on a tab without moving the mouse then
+    // they are zooming into the tab and we need to exit search
+    // mode.
+    if (TabHandlers._mouseDownLocation.x == event.clientX &&
+        TabHandlers._mouseDownLocation.y == event.clientY) {
+      Search.hide();
+      return;
+    }
+
+    iQ("#searchshade").show();
+    iQ("#search").show();
+    iQ("#searchbox")[0].focus();
+    // Marshal the search.
+    setTimeout(Search.perform, 0);
+  }
+};
+
+// ##########
+// Class: Search
+// 
+// A object that handles the search feature.
+let Search = {
+  _initiatedBy: "",
+  _blockClick: false,
+  _currentHandler: null,
+
+  // ----------
+  // Function: toString
+  // Prints [Search] for debug use.
+  toString: function Search_toString() {
+    return "[Search]";
+  },
+
+  // ----------
+  // Function: init
+  // Initializes the searchbox to be focused, and everything else to be hidden,
+  // and to have everything have the appropriate event handlers.
+  init: function Search_init() {
+    let self = this;
+
+    iQ("#search").hide();
+    iQ("#searchshade").hide().mousedown(function Search_init_shade_mousedown(event) {
+      if (event.target.id != "searchbox" && !self._blockClick)
+        self.hide();
+    });
+
+    iQ("#searchbox").keyup(function Search_init_box_keyup() {
+      self.perform();
+    })
+    .attr("title", tabviewString("button.searchTabs"));
+
+    iQ("#searchbutton").mousedown(function Search_init_button_mousedown() {
+      self._initiatedBy = "buttonclick";
+      self.ensureShown();
+      self.switchToInMode();
+    })
+    .attr("title", tabviewString("button.searchTabs"));
+
+    window.addEventListener("focus", function Search_init_window_focus() {
+      if (self.isEnabled()) {
+        self._blockClick = true;
+        setTimeout(function() {
+          self._blockClick = false;
+        }, 0);
+      }
+    }, false);
+
+    this.switchToBeforeMode();
+  },
+
+  // ----------
+  // Function: _beforeSearchKeyHandler
+  // Handles all keydown before the search interface is brought up.
+  _beforeSearchKeyHandler: function Search__beforeSearchKeyHandler(event) {
+    // Only match reasonable text-like characters for quick search.
+    if (event.altKey || event.ctrlKey || event.metaKey)
+      return;
+
+    if ((event.keyCode > 0 && event.keyCode <= event.DOM_VK_DELETE) ||
+        event.keyCode == event.DOM_VK_CONTEXT_MENU ||
+        event.keyCode == event.DOM_VK_SLEEP ||
+        (event.keyCode >= event.DOM_VK_F1 &&
+         event.keyCode <= event.DOM_VK_SCROLL_LOCK) ||
+        event.keyCode == event.DOM_VK_META ||
+        event.keyCode == 91 || // 91 = left windows key
+        event.keyCode == 92 || // 92 = right windows key
+        (!event.keyCode && !event.charCode)) {
+      return;
+    }
+
+    // If we are already in an input field, allow typing as normal.
+    if (event.target.nodeName == "INPUT")
+      return;
+
+    // / is used to activate the search feature so the key shouldn't be entered 
+    // into the search box.
+    if (event.keyCode == KeyEvent.DOM_VK_SLASH) {
+      event.stopPropagation();
+      event.preventDefault();
+    }
+
+    this.switchToInMode();
+    this._initiatedBy = "keydown";
+    this.ensureShown(true);
+  },
+
+  // ----------
+  // Function: _inSearchKeyHandler
+  // Handles all keydown while search mode.
+  _inSearchKeyHandler: function Search__inSearchKeyHandler(event) {
+    let term = iQ("#searchbox").val();
+    if ((event.keyCode == event.DOM_VK_ESCAPE) ||
+        (event.keyCode == event.DOM_VK_BACK_SPACE && term.length <= 1 &&
+         this._initiatedBy == "keydown")) {
+      this.hide(event);
+      return;
+    }
+
+    let matcher = this.createSearchTabMatcher();
+    let matches = matcher.matched();
+    let others =  matcher.matchedTabsFromOtherWindows();
+    if ((event.keyCode == event.DOM_VK_RETURN ||
+         event.keyCode == event.DOM_VK_ENTER) &&
+         (matches.length > 0 || others.length > 0)) {
+      this.hide(event);
+      if (matches.length > 0) 
+        matches[0].zoomIn();
+      else
+        TabUtils.focus(others[0]);
+    }
+  },
+
+  // ----------
+  // Function: switchToBeforeMode
+  // Make sure the event handlers are appropriate for the before-search mode.
+  switchToBeforeMode: function Search_switchToBeforeMode() {
+    let self = this;
+    if (this._currentHandler)
+      iQ(window).unbind("keydown", this._currentHandler);
+    this._currentHandler = function Search_switchToBeforeMode_handler(event) {
+      self._beforeSearchKeyHandler(event);
+    }
+    iQ(window).keydown(this._currentHandler);
+  },
+
+  // ----------
+  // Function: switchToInMode
+  // Make sure the event handlers are appropriate for the in-search mode.
+  switchToInMode: function Search_switchToInMode() {
+    let self = this;
+    if (this._currentHandler)
+      iQ(window).unbind("keydown", this._currentHandler);
+    this._currentHandler = function Search_switchToInMode_handler(event) {
+      self._inSearchKeyHandler(event);
+    }
+    iQ(window).keydown(this._currentHandler);
+  },
+
+  createSearchTabMatcher: function Search_createSearchTabMatcher() {
+    return new TabMatcher(iQ("#searchbox").val());
+  },
+
+  // ----------
+  // Function: isEnabled
+  // Checks whether search mode is enabled or not.
+  isEnabled: function Search_isEnabled() {
+    return iQ("#search").css("display") != "none";
+  },
+
+  // ----------
+  // Function: hide
+  // Hides search mode.
+  hide: function Search_hide(event) {
+    if (!this.isEnabled())
+      return;
+
+    iQ("#searchbox").val("");
+    iQ("#searchshade").hide();
+    iQ("#search").hide();
+
+    iQ("#searchbutton").css({ opacity:.8 });
+
+//@line 537 "/build/src/mozilla-release/browser/components/tabview/search.js"
+
+    this.perform();
+    this.switchToBeforeMode();
+
+    if (event) {
+      // when hiding the search mode, we need to prevent the keypress handler
+      // in UI__setTabViewFrameKeyHandlers to handle the key press again. e.g. Esc
+      // which is already handled by the key down in this class.
+      if (event.type == "keydown")
+        UI.ignoreKeypressForSearch = true;
+      event.preventDefault();
+      event.stopPropagation();
+    }
+
+    // Return focus to the tab window
+    UI.blurAll();
+    gTabViewFrame.contentWindow.focus();
+
+    let newEvent = document.createEvent("Events");
+    newEvent.initEvent("tabviewsearchdisabled", false, false);
+    dispatchEvent(newEvent);
+  },
+
+  // ----------
+  // Function: perform
+  // Performs a search.
+  perform: function Search_perform() {
+    let matcher =  this.createSearchTabMatcher();
+
+    // Remove any previous other-window search results and
+    // hide the display area.
+    iQ("#results").empty();
+    iQ("#otherresults").hide();
+    iQ("#otherresults>.label").text(tabviewString("search.otherWindowTabs"));
+
+    matcher.doSearch(TabHandlers.onMatch, TabHandlers.onUnmatch, TabHandlers.onOther);
+  },
+
+  // ----------
+  // Function: ensureShown
+  // Ensures the search feature is displayed.  If not, display it.
+  // Parameters:
+  //  - a boolean indicates whether this is triggered by a keypress or not
+  ensureShown: function Search_ensureShown(activatedByKeypress) {
+    let $search = iQ("#search");
+    let $searchShade = iQ("#searchshade");
+    let $searchbox = iQ("#searchbox");
+    iQ("#searchbutton").css({ opacity: 1 });
+
+    // NOTE: when this function is called by keydown handler, next keypress
+    // event or composition events of IME will be fired on the focused editor.
+    function dispatchTabViewSearchEnabledEvent() {
+      let newEvent = document.createEvent("Events");
+      newEvent.initEvent("tabviewsearchenabled", false, false);
+      dispatchEvent(newEvent);
+    };
+
+    if (!this.isEnabled()) {
+      $searchShade.show();
+      $search.show();
+
+//@line 601 "/build/src/mozilla-release/browser/components/tabview/search.js"
+
+      if (activatedByKeypress) {
+        // set the focus so key strokes are entered into the textbox.
+        $searchbox[0].focus();
+        dispatchTabViewSearchEnabledEvent();
+      } else {
+        // marshal the focusing, otherwise it ends up with searchbox[0].focus gets
+        // called before the search button gets the focus after being pressed.
+        setTimeout(function setFocusAndDispatchSearchEnabledEvent() {
+          $searchbox[0].focus();
+          dispatchTabViewSearchEnabledEvent();
+        }, 0);
+      }
+    }
+  }
+};
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/**
+ * Collects telemetry data for Tabview.
+ */
+let Telemetry = {
+  TOPIC_GATHER_TELEMETRY: "gather-telemetry",
+
+  /**
+   * Initializes the object.
+   */
+  init: function Telemetry_init() {
+    Services.obs.addObserver(this, this.TOPIC_GATHER_TELEMETRY, false);
+  }, 
+
+  /**
+   * Uninitializes the object.
+   */
+  uninit: function Telemetry_uninit() {
+    Services.obs.removeObserver(this, this.TOPIC_GATHER_TELEMETRY);
+  },
+
+  /**
+   * Adds telemetry values to gather usage statistics.
+   */
+  _collect: function Telemetry_collect() {
+    let stackedGroupsCount = 0;
+    let childCounts = [];
+
+    GroupItems.groupItems.forEach(function (groupItem) {
+      if (!groupItem.isEmpty()) {
+        childCounts.push(groupItem.getChildren().length);
+
+        if (groupItem.isStacked())
+          stackedGroupsCount++;
+      }
+    });
+
+    function addTelemetryValue(aId, aValue) {
+      Services.telemetry.getHistogramById("PANORAMA_" + aId).add(aValue);
+    }
+    function median(aChildCounts) {
+      aChildCounts.sort(function(x, y) { return x - y; });
+      let middle = Math.floor(aChildCounts.length / 2);
+      return aChildCounts[middle];
+    }
+
+    addTelemetryValue("GROUPS_COUNT", GroupItems.groupItems.length);
+    addTelemetryValue("STACKED_GROUPS_COUNT", stackedGroupsCount);
+    addTelemetryValue("MEDIAN_TABS_IN_GROUPS_COUNT", median(childCounts));
+  },
+
+  /**
+   * Observes for gather telemetry topic.
+   */
+  observe: function Telemetry_observe(aSubject, aTopic, aData) {
+    if (!gPrivateBrowsing.privateBrowsingEnabled)
+      this._collect();
+  }
+}
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// **********
+// Title: ui.js
+
+let Keys = { meta: false };
+
+// ##########
+// Class: UI
+// Singleton top-level UI manager.
+let UI = {
+  // Variable: _frameInitialized
+  // True if the Tab View UI frame has been initialized.
+  _frameInitialized: false,
+
+  // Variable: _pageBounds
+  // Stores the page bounds.
+  _pageBounds: null,
+
+  // Variable: _closedLastVisibleTab
+  // If true, the last visible tab has just been closed in the tab strip.
+  _closedLastVisibleTab: false,
+
+  // Variable: _closedSelectedTabInTabView
+  // If true, a select tab has just been closed in TabView.
+  _closedSelectedTabInTabView: false,
+
+  // Variable: restoredClosedTab
+  // If true, a closed tab has just been restored.
+  restoredClosedTab: false,
+
+  // Variable: _isChangingVisibility
+  // Tracks whether we're currently in the process of showing/hiding the tabview.
+  _isChangingVisibility: false,
+
+  // Variable: _reorderTabItemsOnShow
+  // Keeps track of the <GroupItem>s which their tab items' tabs have been moved
+  // and re-orders the tab items when switching to TabView.
+  _reorderTabItemsOnShow: [],
+
+  // Variable: _reorderTabsOnHide
+  // Keeps track of the <GroupItem>s which their tab items have been moved in
+  // TabView UI and re-orders the tabs when switcing back to main browser.
+  _reorderTabsOnHide: [],
+
+  // Variable: _currentTab
+  // Keeps track of which xul:tab we are currently on.
+  // Used to facilitate zooming down from a previous tab.
+  _currentTab: null,
+
+  // Variable: _eventListeners
+  // Keeps track of event listeners added to the AllTabs object.
+  _eventListeners: {},
+
+  // Variable: _cleanupFunctions
+  // An array of functions to be called at uninit time
+  _cleanupFunctions: [],
+  
+  // Constant: _maxInteractiveWait
+  // If the UI is in the middle of an operation, this is the max amount of
+  // milliseconds to wait between input events before we no longer consider
+  // the operation interactive.
+  _maxInteractiveWait: 250,
+
+  // Variable: _privateBrowsing
+  // Keeps track of info related to private browsing, including: 
+  //   transitionMode - whether we're entering or exiting PB
+  //   wasInTabView - whether TabView was visible before we went into PB
+  _privateBrowsing: {
+    transitionMode: "",
+    wasInTabView: false 
+  },
+  
+  // Variable: _storageBusy
+  // Tells whether the storage is currently busy or not.
+  _storageBusy: false,
+
+  // Variable: isDOMWindowClosing
+  // Tells wether the parent window is about to close
+  isDOMWindowClosing: false,
+
+  // Variable: _browserKeys
+  // Used to keep track of allowed browser keys.
+  _browserKeys: null,
+
+  // Variable: _browserKeysWithShift
+  // Used to keep track of allowed browser keys with Shift key combination.
+  _browserKeysWithShift: null,
+
+  // Variable: ignoreKeypressForSearch
+  // Used to prevent keypress being handled after quitting search mode.
+  ignoreKeypressForSearch: false,
+
+  // Variable: _lastOpenedTab
+  // Used to keep track of the last opened tab.
+  _lastOpenedTab: null,
+
+  // Variable: _originalSmoothScroll
+  // Used to keep track of the tab strip smooth scroll value.
+  _originalSmoothScroll: null,
+
+  // ----------
+  // Function: toString
+  // Prints [UI] for debug use
+  toString: function UI_toString() {
+    return "[UI]";
+  },
+
+  // ----------
+  // Function: init
+  // Must be called after the object is created.
+  init: function UI_init() {
+    try {
+      let self = this;
+
+      // initialize the direction of the page
+      this._initPageDirection();
+
+      // ___ storage
+      Storage.init();
+
+      if (Storage.readWindowBusyState(gWindow))
+        this.storageBusy();
+
+      let data = Storage.readUIData(gWindow);
+      this._storageSanity(data);
+      this._pageBounds = data.pageBounds;
+
+      // ___ search
+      Search.init();
+
+      Telemetry.init();
+
+      // ___ currentTab
+      this._currentTab = gBrowser.selectedTab;
+
+      // ___ exit button
+      iQ("#exit-button").click(function() {
+        self.exit();
+        self.blurAll();
+      })
+      .attr("title", tabviewString("button.exitTabGroups"));
+
+      // When you click on the background/empty part of TabView,
+      // we create a new groupItem.
+      iQ(gTabViewFrame.contentDocument).mousedown(function(e) {
+        if (iQ(":focus").length > 0) {
+          iQ(":focus").each(function(element) {
+            // don't fire blur event if the same input element is clicked.
+            if (e.target != element && element.nodeName == "INPUT")
+              element.blur();
+          });
+        }
+        if (e.originalTarget.id == "content" &&
+            Utils.isLeftClick(e) &&
+            e.detail == 1) {
+          self._createGroupItemOnDrag(e);
+        }
+      });
+
+      iQ(gTabViewFrame.contentDocument).dblclick(function(e) {
+        if (e.originalTarget.id != "content")
+          return;
+
+        // Create a group with one tab on double click
+        let box =
+          new Rect(e.clientX - Math.floor(TabItems.tabWidth/2),
+                   e.clientY - Math.floor(TabItems.tabHeight/2),
+                   TabItems.tabWidth, TabItems.tabHeight);
+        box.inset(-30, -30);
+
+        let opts = {immediately: true, bounds: box};
+        let groupItem = new GroupItem([], opts);
+        groupItem.newTab();
+
+        gTabView.firstUseExperienced = true;
+      });
+
+      iQ(window).bind("unload", function() {
+        self.uninit();
+      });
+
+      // ___ setup DOMWillOpenModalDialog message handler
+      let mm = gWindow.messageManager;
+      let callback = this._onDOMWillOpenModalDialog.bind(this);
+      mm.addMessageListener("Panorama:DOMWillOpenModalDialog", callback);
+
+      this._cleanupFunctions.push(function () {
+        mm.removeMessageListener("Panorama:DOMWillOpenModalDialog", callback);
+      });
+
+      // ___ setup key handlers
+      this._setTabViewFrameKeyHandlers();
+
+      // ___ add tab action handlers
+      this._addTabActionHandlers();
+
+      // ___ groups
+      GroupItems.init();
+      GroupItems.pauseArrange();
+      let hasGroupItemsData = GroupItems.load();
+
+      // ___ tabs
+      TabItems.init();
+      TabItems.pausePainting();
+
+      // ___ favicons
+      FavIcons.init();
+
+      if (!hasGroupItemsData)
+        this.reset();
+
+      // ___ resizing
+      if (this._pageBounds)
+        this._resize(true);
+      else
+        this._pageBounds = Items.getPageBounds();
+
+      iQ(window).resize(function() {
+        self._resize();
+      });
+
+      // ___ setup event listener to save canvas images
+      gWindow.addEventListener("SSWindowClosing", function onWindowClosing() {
+        gWindow.removeEventListener("SSWindowClosing", onWindowClosing, false);
+
+        // XXX bug #635975 - don't unlink the tab if the dom window is closing.
+        self.isDOMWindowClosing = true;
+
+        if (self.isTabViewVisible())
+          GroupItems.removeHiddenGroups();
+
+        TabItems.saveAll();
+
+        self._save();
+      }, false);
+
+      // ___ load frame script
+      let frameScript = "chrome://browser/content/tabview-content.js";
+      gWindow.messageManager.loadFrameScript(frameScript, true);
+
+      // ___ Done
+      this._frameInitialized = true;
+      this._save();
+
+      // fire an iframe initialized event so everyone knows tab view is 
+      // initialized.
+      let event = document.createEvent("Events");
+      event.initEvent("tabviewframeinitialized", true, false);
+      dispatchEvent(event);
+
+      // XXX this can be removed when bug 731868 is fixed
+      event = null;
+    } catch(e) {
+      Utils.log(e);
+    } finally {
+      GroupItems.resumeArrange();
+    }
+  },
+
+  // Function: uninit
+  // Should be called when window is unloaded.
+  uninit: function UI_uninit() {
+    // call our cleanup functions
+    this._cleanupFunctions.forEach(function(func) {
+      func();
+    });
+    this._cleanupFunctions = [];
+
+    // additional clean up
+    TabItems.uninit();
+    GroupItems.uninit();
+    FavIcons.uninit();
+    Storage.uninit();
+    Telemetry.uninit();
+
+    this._removeTabActionHandlers();
+    this._currentTab = null;
+    this._pageBounds = null;
+    this._reorderTabItemsOnShow = null;
+    this._reorderTabsOnHide = null;
+    this._frameInitialized = false;
+  },
+
+  // Property: rtl
+  // Returns true if we are in RTL mode, false otherwise
+  rtl: false,
+
+  // Function: reset
+  // Resets the Panorama view to have just one group with all tabs
+  reset: function UI_reset() {
+    let padding = Trenches.defaultRadius;
+    let welcomeWidth = 300;
+    let pageBounds = Items.getPageBounds();
+    pageBounds.inset(padding, padding);
+
+    let $actions = iQ("#actions");
+    if ($actions) {
+      pageBounds.width -= $actions.width();
+      if (UI.rtl)
+        pageBounds.left += $actions.width() - padding;
+    }
+
+    // ___ make a fresh groupItem
+    let box = new Rect(pageBounds);
+    box.width = Math.min(box.width * 0.667,
+                         pageBounds.width - (welcomeWidth + padding));
+    box.height = box.height * 0.667;
+    if (UI.rtl) {
+      box.left = pageBounds.left + welcomeWidth + 2 * padding;
+    }
+
+    GroupItems.groupItems.forEach(function(group) {
+      group.close();
+    });
+    
+    let options = {
+      bounds: box,
+      immediately: true
+    };
+    let groupItem = new GroupItem([], options);
+    let items = TabItems.getItems();
+    items.forEach(function(item) {
+      if (item.parent)
+        item.parent.remove(item);
+      groupItem.add(item, {immediately: true});
+    });
+    this.setActive(groupItem);
+  },
+
+  // ----------
+  // Function: blurAll
+  // Blurs any currently focused element
+  blurAll: function UI_blurAll() {
+    iQ(":focus").each(function(element) {
+      element.blur();
+    });
+  },
+
+  // ----------
+  // Function: isIdle
+  // Returns true if the last interaction was long enough ago to consider the
+  // UI idle. Used to determine whether interactivity would be sacrificed if 
+  // the CPU was to become busy.
+  //
+  isIdle: function UI_isIdle() {
+    let time = Date.now();
+    let maxEvent = Math.max(drag.lastMoveTime, resize.lastMoveTime);
+    return (time - maxEvent) > this._maxInteractiveWait;
+  },
+
+  // ----------
+  // Function: getActiveTab
+  // Returns the currently active tab as a <TabItem>
+  getActiveTab: function UI_getActiveTab() {
+    return this._activeTab;
+  },
+
+  // ----------
+  // Function: _setActiveTab
+  // Sets the currently active tab. The idea of a focused tab is useful
+  // for keyboard navigation and returning to the last zoomed-in tab.
+  // Hitting return/esc brings you to the focused tab, and using the
+  // arrow keys lets you navigate between open tabs.
+  //
+  // Parameters:
+  //  - Takes a <TabItem>
+  _setActiveTab: function UI__setActiveTab(tabItem) {
+    if (tabItem == this._activeTab)
+      return;
+
+    if (this._activeTab) {
+      this._activeTab.makeDeactive();
+      this._activeTab.removeSubscriber("close", this._onActiveTabClosed);
+    }
+
+    this._activeTab = tabItem;
+
+    if (this._activeTab) {
+      this._activeTab.addSubscriber("close", this._onActiveTabClosed);
+      this._activeTab.makeActive();
+    }
+  },
+
+  // ----------
+  // Function: _onActiveTabClosed
+  // Handles when the currently active tab gets closed.
+  //
+  // Parameters:
+  //  - the <TabItem> that is closed
+  _onActiveTabClosed: function UI__onActiveTabClosed(tabItem){
+    if (UI._activeTab == tabItem)
+      UI._setActiveTab(null);
+  },
+
+  // ----------
+  // Function: setActive
+  // Sets the active tab item or group item
+  // Parameters:
+  //
+  // options
+  //  dontSetActiveTabInGroup bool for not setting active tab in group
+  setActive: function UI_setActive(item, options) {
+    Utils.assert(item, "item must be given");
+
+    if (item.isATabItem) {
+      if (item.parent)
+        GroupItems.setActiveGroupItem(item.parent);
+      if (!options || !options.dontSetActiveTabInGroup)
+        this._setActiveTab(item);
+    } else {
+      GroupItems.setActiveGroupItem(item);
+      if (!options || !options.dontSetActiveTabInGroup) {
+        let activeTab = item.getActiveTab();
+        if (activeTab)
+          this._setActiveTab(activeTab);
+      }
+    }
+  },
+
+  // ----------
+  // Function: clearActiveTab
+  // Sets the active tab to 'null'.
+  clearActiveTab: function UI_clearActiveTab() {
+    this._setActiveTab(null);
+  },
+
+  // ----------
+  // Function: isTabViewVisible
+  // Returns true if the TabView UI is currently shown.
+  isTabViewVisible: function UI_isTabViewVisible() {
+    return gTabViewDeck.selectedPanel == gTabViewFrame;
+  },
+
+  // ---------
+  // Function: _initPageDirection
+  // Initializes the page base direction
+  _initPageDirection: function UI__initPageDirection() {
+    let chromeReg = Cc["@mozilla.org/chrome/chrome-registry;1"].
+                    getService(Ci.nsIXULChromeRegistry);
+    let dir = chromeReg.isLocaleRTL("global");
+    document.documentElement.setAttribute("dir", dir ? "rtl" : "ltr");
+    this.rtl = dir;
+  },
+
+  // ----------
+  // Function: showTabView
+  // Shows TabView and hides the main browser UI.
+  // Parameters:
+  //   zoomOut - true for zoom out animation, false for nothing.
+  showTabView: function UI_showTabView(zoomOut) {
+    if (this.isTabViewVisible() || this._isChangingVisibility)
+      return;
+
+    this._isChangingVisibility = true;
+
+    // store tab strip smooth scroll value and disable it.
+    let tabStrip = gBrowser.tabContainer.mTabstrip;
+    this._originalSmoothScroll = tabStrip.smoothScroll;
+    tabStrip.smoothScroll = false;
+
+    // initialize the direction of the page
+    this._initPageDirection();
+
+    var self = this;
+    var currentTab = this._currentTab;
+
+    this._reorderTabItemsOnShow.forEach(function(groupItem) {
+      groupItem.reorderTabItemsBasedOnTabOrder();
+    });
+    this._reorderTabItemsOnShow = [];
+
+//@line 479 "/build/src/mozilla-release/browser/components/tabview/ui.js"
+    gTabViewDeck.selectedPanel = gTabViewFrame;
+    gWindow.TabsInTitlebar.allowedBy("tabview-open", false);
+    gTabViewFrame.contentWindow.focus();
+
+    gBrowser.updateTitlebar();
+//@line 487 "/build/src/mozilla-release/browser/components/tabview/ui.js"
+    let event = document.createEvent("Events");
+    event.initEvent("tabviewshown", true, false);
+
+    Storage.saveVisibilityData(gWindow, "true");
+
+    if (zoomOut && currentTab && currentTab._tabViewTabItem) {
+      let item = currentTab._tabViewTabItem;
+      // If there was a previous currentTab we want to animate
+      // its thumbnail (canvas) for the zoom out.
+      // Note that we start the animation on the chrome thread.
+
+      // Zoom out!
+      item.zoomOut(function() {
+        if (!currentTab._tabViewTabItem) // if the tab's been destroyed
+          item = null;
+
+        self.setActive(item);
+
+        self._resize(true);
+        self._isChangingVisibility = false;
+        dispatchEvent(event);
+
+        // Flush pending updates
+        GroupItems.flushAppTabUpdates();
+
+        TabItems.resumePainting();
+      });
+    } else {
+      if (!currentTab || !currentTab._tabViewTabItem)
+        self.clearActiveTab();
+      self._isChangingVisibility = false;
+      dispatchEvent(event);
+
+      // Flush pending updates
+      GroupItems.flushAppTabUpdates();
+
+      TabItems.resumePainting();
+    }
+
+    if (gTabView.firstUseExperienced)
+      gTabView.enableSessionRestore();
+  },
+
+  // ----------
+  // Function: hideTabView
+  // Hides TabView and shows the main browser UI.
+  hideTabView: function UI_hideTabView() {
+    if (!this.isTabViewVisible() || this._isChangingVisibility)
+      return;
+
+    this._isChangingVisibility = true;
+
+    // another tab might be select if user decides to stay on a page when
+    // a onclose confirmation prompts.
+    GroupItems.removeHiddenGroups();
+    TabItems.pausePainting();
+
+    this._reorderTabsOnHide.forEach(function(groupItem) {
+      groupItem.reorderTabsBasedOnTabItemOrder();
+    });
+    this._reorderTabsOnHide = [];
+
+//@line 555 "/build/src/mozilla-release/browser/components/tabview/ui.js"
+    gTabViewDeck.selectedPanel = gBrowserPanel;
+    gWindow.TabsInTitlebar.allowedBy("tabview-open", true);
+    gBrowser.selectedBrowser.focus();
+
+    gBrowser.updateTitlebar();
+    gBrowser.tabContainer.mTabstrip.smoothScroll = this._originalSmoothScroll;
+//@line 564 "/build/src/mozilla-release/browser/components/tabview/ui.js"
+    Storage.saveVisibilityData(gWindow, "false");
+
+    this._isChangingVisibility = false;
+
+    let event = document.createEvent("Events");
+    event.initEvent("tabviewhidden", true, false);
+    dispatchEvent(event);
+  },
+
+//@line 598 "/build/src/mozilla-release/browser/components/tabview/ui.js"
+
+  // ----------
+  // Function: storageBusy
+  // Pauses the storage activity that conflicts with sessionstore updates and 
+  // private browsing mode switches. Calls can be nested. 
+  storageBusy: function UI_storageBusy() {
+    if (this._storageBusy)
+      return;
+
+    this._storageBusy = true;
+
+    TabItems.pauseReconnecting();
+    GroupItems.pauseAutoclose();
+  },
+  
+  // ----------
+  // Function: storageReady
+  // Resumes the activity paused by storageBusy, and updates for any new group
+  // information in sessionstore. Calls can be nested. 
+  storageReady: function UI_storageReady() {
+    if (!this._storageBusy)
+      return;
+
+    this._storageBusy = false;
+
+    let hasGroupItemsData = GroupItems.load();
+    if (!hasGroupItemsData)
+      this.reset();
+
+    TabItems.resumeReconnecting();
+    GroupItems._updateTabBar();
+    GroupItems.resumeAutoclose();
+  },
+
+  // ----------
+  // Function: _addTabActionHandlers
+  // Adds handlers to handle tab actions.
+  _addTabActionHandlers: function UI__addTabActionHandlers() {
+    var self = this;
+
+    // session restore events
+    function handleSSWindowStateBusy() {
+      self.storageBusy();
+    }
+    
+    function handleSSWindowStateReady() {
+      self.storageReady();
+    }
+    
+    gWindow.addEventListener("SSWindowStateBusy", handleSSWindowStateBusy, false);
+    gWindow.addEventListener("SSWindowStateReady", handleSSWindowStateReady, false);
+
+    this._cleanupFunctions.push(function() {
+      gWindow.removeEventListener("SSWindowStateBusy", handleSSWindowStateBusy, false);
+      gWindow.removeEventListener("SSWindowStateReady", handleSSWindowStateReady, false);
+    });
+
+    // Private Browsing:
+    // When transitioning to PB, we exit Panorama if necessary (making note of the
+    // fact that we were there so we can return after PB) and make sure we
+    // don't reenter Panorama due to all of the session restore tab
+    // manipulation (which otherwise we might). When transitioning away from
+    // PB, we reenter Panorama if we had been there directly before PB.
+    function pbObserver(subject, topic, data) {
+      if (topic == "private-browsing") {
+        // We could probably do this in private-browsing-change-granted, but
+        // this seems like a nicer spot, right in the middle of the process.
+        if (data == "enter") {
+          // If we are in Tab View, exit. 
+          self._privateBrowsing.wasInTabView = self.isTabViewVisible();
+          if (self.isTabViewVisible())
+            self.goToTab(gBrowser.selectedTab);
+        }
+      } else if (topic == "private-browsing-change-granted") {
+        if (data == "enter" || data == "exit") {
+          Search.hide();
+          self._privateBrowsing.transitionMode = data;
+        }
+      } else if (topic == "private-browsing-transition-complete") {
+        // We use .transitionMode here, as aData is empty.
+        if (self._privateBrowsing.transitionMode == "exit" &&
+            self._privateBrowsing.wasInTabView)
+          self.showTabView(false);
+
+        self._privateBrowsing.transitionMode = "";
+      }
+    }
+
+    Services.obs.addObserver(pbObserver, "private-browsing", false);
+    Services.obs.addObserver(pbObserver, "private-browsing-change-granted", false);
+    Services.obs.addObserver(pbObserver, "private-browsing-transition-complete", false);
+
+    this._cleanupFunctions.push(function() {
+      Services.obs.removeObserver(pbObserver, "private-browsing");
+      Services.obs.removeObserver(pbObserver, "private-browsing-change-granted");
+      Services.obs.removeObserver(pbObserver, "private-browsing-transition-complete");
+    });
+
+    // TabOpen
+    this._eventListeners.open = function (event) {
+      let tab = event.target;
+
+      // if it's an app tab, add it to all the group items
+      if (tab.pinned)
+        GroupItems.addAppTab(tab);
+      else if (self.isTabViewVisible() && !self._storageBusyCount)
+        self._lastOpenedTab = tab;
+    };
+    
+    // TabClose
+    this._eventListeners.close = function (event) {
+      let tab = event.target;
+
+      // if it's an app tab, remove it from all the group items
+      if (tab.pinned)
+        GroupItems.removeAppTab(tab);
+        
+      if (self.isTabViewVisible()) {
+        // just closed the selected tab in the TabView interface.
+        if (self._currentTab == tab)
+          self._closedSelectedTabInTabView = true;
+      } else {
+        // If we're currently in the process of entering private browsing,
+        // we don't want to go to the Tab View UI. 
+        if (self._storageBusy)
+          return;
+
+        // if not closing the last tab
+        if (gBrowser.tabs.length > 1) {
+          // Don't return to TabView if there are any app tabs
+          for (let a = 0; a < gBrowser._numPinnedTabs; a++) {
+            if (Utils.isValidXULTab(gBrowser.tabs[a]))
+              return;
+          }
+
+          let groupItem = GroupItems.getActiveGroupItem();
+
+          // 1) Only go back to the TabView tab when there you close the last
+          // tab of a groupItem.
+          let closingLastOfGroup = (groupItem && 
+              groupItem._children.length == 1 && 
+              groupItem._children[0].tab == tab);
+
+          // 2) When a blank tab is active while restoring a closed tab the
+          // blank tab gets removed. The active group is not closed as this is
+          // where the restored tab goes. So do not show the TabView.
+          let tabItem = tab && tab._tabViewTabItem;
+          let closingBlankTabAfterRestore =
+            (tabItem && tabItem.isRemovedAfterRestore);
+
+          if (closingLastOfGroup && !closingBlankTabAfterRestore) {
+            // for the tab focus event to pick up.
+            self._closedLastVisibleTab = true;
+            self.showTabView();
+          }
+        }
+      }
+    };
+
+    // TabMove
+    this._eventListeners.move = function (event) {
+      let tab = event.target;
+
+      if (GroupItems.groupItems.length > 0) {
+        if (tab.pinned) {
+          if (gBrowser._numPinnedTabs > 1)
+            GroupItems.arrangeAppTab(tab);
+        } else {
+          let activeGroupItem = GroupItems.getActiveGroupItem();
+          if (activeGroupItem)
+            self.setReorderTabItemsOnShow(activeGroupItem);
+        }
+      }
+    };
+
+    // TabSelect
+    this._eventListeners.select = function (event) {
+      self.onTabSelect(event.target);
+    };
+
+    // TabPinned
+    this._eventListeners.pinned = function (event) {
+      let tab = event.target;
+
+      TabItems.handleTabPin(tab);
+      GroupItems.addAppTab(tab);
+    };
+
+    // TabUnpinned
+    this._eventListeners.unpinned = function (event) {
+      let tab = event.target;
+
+      TabItems.handleTabUnpin(tab);
+      GroupItems.removeAppTab(tab);
+
+      let groupItem = tab._tabViewTabItem.parent;
+      if (groupItem)
+        self.setReorderTabItemsOnShow(groupItem);
+    };
+
+    // Actually register the above handlers
+    for (let name in this._eventListeners)
+      AllTabs.register(name, this._eventListeners[name]);
+  },
+
+  // ----------
+  // Function: _removeTabActionHandlers
+  // Removes handlers to handle tab actions.
+  _removeTabActionHandlers: function UI__removeTabActionHandlers() {
+    for (let name in this._eventListeners)
+      AllTabs.unregister(name, this._eventListeners[name]);
+  },
+
+  // ----------
+  // Function: goToTab
+  // Selects the given xul:tab in the browser.
+  goToTab: function UI_goToTab(xulTab) {
+    // If it's not focused, the onFocus listener would handle it.
+    if (gBrowser.selectedTab == xulTab)
+      this.onTabSelect(xulTab);
+    else
+      gBrowser.selectedTab = xulTab;
+  },
+
+  // ----------
+  // Function: onTabSelect
+  // Called when the user switches from one tab to another outside of the TabView UI.
+  onTabSelect: function UI_onTabSelect(tab) {
+    this._currentTab = tab;
+
+    if (this.isTabViewVisible()) {
+      // We want to zoom in if:
+      // 1) we didn't just restore a tab via Ctrl+Shift+T
+      // 2) we're not in the middle of switching from/to private browsing
+      // 3) the currently selected tab is the last created tab and has a tabItem
+      if (!this.restoredClosedTab && !this._privateBrowsing.transitionMode &&
+          this._lastOpenedTab == tab && tab._tabViewTabItem) {
+        tab._tabViewTabItem.zoomIn(true);
+        this._lastOpenedTab = null;
+        return;
+      }
+      if (this._closedLastVisibleTab ||
+          (this._closedSelectedTabInTabView && !this.closedLastTabInTabView) ||
+          this.restoredClosedTab) {
+        if (this.restoredClosedTab) {
+          // when the tab view UI is being displayed, update the thumb for the 
+          // restored closed tab after the page load
+          tab.linkedBrowser.addEventListener("load", function onLoad(event) {
+            tab.linkedBrowser.removeEventListener("load", onLoad, true);
+            TabItems._update(tab);
+          }, true);
+        }
+        this._closedLastVisibleTab = false;
+        this._closedSelectedTabInTabView = false;
+        this.closedLastTabInTabView = false;
+        this.restoredClosedTab = false;
+        return;
+      }
+    }
+    // reset these vars, just in case.
+    this._closedLastVisibleTab = false;
+    this._closedSelectedTabInTabView = false;
+    this.closedLastTabInTabView = false;
+    this.restoredClosedTab = false;
+    this._lastOpenedTab = null;
+
+    // if TabView is visible but we didn't just close the last tab or
+    // selected tab, show chrome.
+    if (this.isTabViewVisible()) {
+      // Unhide the group of the tab the user is activating.
+      if (tab && tab._tabViewTabItem && tab._tabViewTabItem.parent &&
+          tab._tabViewTabItem.parent.hidden)
+        tab._tabViewTabItem.parent._unhide({immediately: true});
+
+      this.hideTabView();
+    }
+
+    // another tab might be selected when hideTabView() is invoked so a
+    // validation is needed.
+    if (this._currentTab != tab)
+      return;
+
+    let newItem = null;
+    // update the tab bar for the new tab's group
+    if (tab && tab._tabViewTabItem) {
+      if (!TabItems.reconnectingPaused()) {
+        newItem = tab._tabViewTabItem;
+        GroupItems.updateActiveGroupItemAndTabBar(newItem);
+      }
+    } else {
+      // No tabItem; must be an app tab. Base the tab bar on the current group.
+      // If no current group, figure it out based on what's already in the tab
+      // bar.
+      if (!GroupItems.getActiveGroupItem()) {
+        for (let a = 0; a < gBrowser.tabs.length; a++) {
+          let theTab = gBrowser.tabs[a];
+          if (!theTab.pinned) {
+            let tabItem = theTab._tabViewTabItem;
+            this.setActive(tabItem.parent);
+            break;
+          }
+        }
+      }
+
+      if (GroupItems.getActiveGroupItem())
+        GroupItems._updateTabBar();
+    }
+  },
+
+  // ----------
+  // Function: _onDOMWillOpenModalDialog
+  // Called when a web page is about to show a modal dialog.
+  _onDOMWillOpenModalDialog: function UI__onDOMWillOpenModalDialog(cx) {
+    if (!this.isTabViewVisible())
+      return;
+
+    let index = gBrowser.browsers.indexOf(cx.target);
+    if (index == -1)
+      return;
+
+    let tab = gBrowser.tabs[index];
+
+    // When TabView is visible, we need to call onTabSelect to make sure that
+    // TabView is hidden and that the correct group is activated. When a modal
+    // dialog is shown for currently selected tab the onTabSelect event handler
+    // is not called, so we need to do it.
+    if (gBrowser.selectedTab == tab && this._currentTab == tab)
+      this.onTabSelect(tab);
+  },
+
+  // ----------
+  // Function: setReorderTabsOnHide
+  // Sets the groupItem which the tab items' tabs should be re-ordered when
+  // switching to the main browser UI.
+  // Parameters:
+  //   groupItem - the groupItem which would be used for re-ordering tabs.
+  setReorderTabsOnHide: function UI_setReorderTabsOnHide(groupItem) {
+    if (this.isTabViewVisible()) {
+      var index = this._reorderTabsOnHide.indexOf(groupItem);
+      if (index == -1)
+        this._reorderTabsOnHide.push(groupItem);
+    }
+  },
+
+  // ----------
+  // Function: setReorderTabItemsOnShow
+  // Sets the groupItem which the tab items should be re-ordered when
+  // switching to the tab view UI.
+  // Parameters:
+  //   groupItem - the groupItem which would be used for re-ordering tab items.
+  setReorderTabItemsOnShow: function UI_setReorderTabItemsOnShow(groupItem) {
+    if (!this.isTabViewVisible()) {
+      var index = this._reorderTabItemsOnShow.indexOf(groupItem);
+      if (index == -1)
+        this._reorderTabItemsOnShow.push(groupItem);
+    }
+  },
+  
+  // ----------
+  updateTabButton: function UI_updateTabButton() {
+    let exitButton = document.getElementById("exit-button");
+    let numberOfGroups = GroupItems.groupItems.length;
+
+    exitButton.setAttribute("groups", numberOfGroups);
+    gTabView.updateGroupNumberBroadcaster(numberOfGroups);
+  },
+
+  // ----------
+  // Function: getClosestTab
+  // Convenience function to get the next tab closest to the entered position
+  getClosestTab: function UI_getClosestTab(tabCenter) {
+    let cl = null;
+    let clDist;
+    TabItems.getItems().forEach(function (item) {
+      if (!item.parent || item.parent.hidden)
+        return;
+      let testDist = tabCenter.distance(item.bounds.center());
+      if (cl==null || testDist < clDist) {
+        cl = item;
+        clDist = testDist;
+      }
+    });
+    return cl;
+  },
+
+  // ----------
+  // Function: _setupBrowserKeys
+  // Sets up the allowed browser keys using key elements.
+  _setupBrowserKeys: function UI__setupKeyWhiteList() {
+    let keys = {};
+
+    [
+//@line 991 "/build/src/mozilla-release/browser/components/tabview/ui.js"
+      "quitApplication",
+//@line 998 "/build/src/mozilla-release/browser/components/tabview/ui.js"
+      "newNavigator", "newNavigatorTab", "undo", "cut", "copy", "paste", 
+      "selectAll", "find"
+    ].forEach(function(key) {
+      let element = gWindow.document.getElementById("key_" + key);
+      let code = element.getAttribute("key").toLocaleLowerCase().charCodeAt(0);
+      keys[code] = key;
+    });
+    this._browserKeys = keys;
+
+    keys = {};
+    // The lower case letters are passed to processBrowserKeys() even with shift 
+    // key when stimulating a key press using EventUtils.synthesizeKey() so need 
+    // to handle both upper and lower cases here.
+    [
+//@line 1013 "/build/src/mozilla-release/browser/components/tabview/ui.js"
+      "redo",
+//@line 1018 "/build/src/mozilla-release/browser/components/tabview/ui.js"
+      "closeWindow", "tabview", "undoCloseTab", "undoCloseWindow",
+      "privatebrowsing"
+    ].forEach(function(key) {
+      let element = gWindow.document.getElementById("key_" + key);
+      let code = element.getAttribute("key").toLocaleLowerCase().charCodeAt(0);
+      keys[code] = key;
+    });
+    this._browserKeysWithShift = keys;
+  },
+
+  // ----------
+  // Function: _setTabViewFrameKeyHandlers
+  // Sets up the key handlers for navigating between tabs within the TabView UI.
+  _setTabViewFrameKeyHandlers: function UI__setTabViewFrameKeyHandlers() {
+    let self = this;
+
+    this._setupBrowserKeys();
+
+    iQ(window).keyup(function(event) {
+      if (!event.metaKey)
+        Keys.meta = false;
+    });
+
+    iQ(window).keypress(function(event) {
+      if (event.metaKey)
+        Keys.meta = true;
+
+      function processBrowserKeys(evt) {
+        // let any keys with alt to pass through
+        if (evt.altKey)
+          return;
+
+//@line 1053 "/build/src/mozilla-release/browser/components/tabview/ui.js"
+        if (evt.ctrlKey) {
+//@line 1055 "/build/src/mozilla-release/browser/components/tabview/ui.js"
+          let preventDefault = true;
+          if (evt.shiftKey) {
+            // when a user presses ctrl+shift+key, upper case letter charCode 
+            // is passed to processBrowserKeys() so converting back to lower 
+            // case charCode before doing the check
+            let lowercaseCharCode =
+              String.fromCharCode(evt.charCode).toLocaleLowerCase().charCodeAt(0);
+            if (lowercaseCharCode in self._browserKeysWithShift) {
+              let key = self._browserKeysWithShift[lowercaseCharCode];
+              if (key == "tabview")
+                self.exit();
+              else
+                preventDefault = false;
+            }
+          } else {
+            if (evt.charCode in self._browserKeys) {
+              let key = self._browserKeys[evt.charCode];
+              if (key == "find")
+                self.enableSearch();
+              else
+                preventDefault = false;
+            }
+          }
+          if (preventDefault) {
+            evt.stopPropagation();
+            evt.preventDefault();
+          }
+        }
+      }
+      if ((iQ(":focus").length > 0 && iQ(":focus")[0].nodeName == "INPUT") ||
+          Search.isEnabled() || self.ignoreKeypressForSearch) {
+        self.ignoreKeypressForSearch = false;
+        processBrowserKeys(event);
+        return;
+      }
+
+      function getClosestTabBy(norm) {
+        if (!self.getActiveTab())
+          return null;
+
+        let activeTab = self.getActiveTab();
+        let activeTabGroup = activeTab.parent;
+        let myCenter = activeTab.bounds.center();
+        let match;
+
+        TabItems.getItems().forEach(function (item) {
+          if (!item.parent.hidden &&
+              (!activeTabGroup.expanded || activeTabGroup.id == item.parent.id)) {
+            let itemCenter = item.bounds.center();
+
+            if (norm(itemCenter, myCenter)) {
+              let itemDist = myCenter.distance(itemCenter);
+              if (!match || match[0] > itemDist)
+                match = [itemDist, item];
+            }
+          }
+        });
+
+        return match && match[1];
+      }
+
+      let preventDefault = true;
+      let activeTab;
+      let activeGroupItem;
+      let norm = null;
+      switch (event.keyCode) {
+        case KeyEvent.DOM_VK_RIGHT:
+          norm = function(a, me){return a.x > me.x};
+          break;
+        case KeyEvent.DOM_VK_LEFT:
+          norm = function(a, me){return a.x < me.x};
+          break;
+        case KeyEvent.DOM_VK_DOWN:
+          norm = function(a, me){return a.y > me.y};
+          break;
+        case KeyEvent.DOM_VK_UP:
+          norm = function(a, me){return a.y < me.y}
+          break;
+      }
+
+      if (norm != null) {
+        let nextTab = getClosestTabBy(norm);
+        if (nextTab) {
+          if (nextTab.isStacked && !nextTab.parent.expanded)
+            nextTab = nextTab.parent.getChild(0);
+          self.setActive(nextTab);
+        }
+      } else {
+        switch(event.keyCode) {
+          case KeyEvent.DOM_VK_ESCAPE:
+            activeGroupItem = GroupItems.getActiveGroupItem();
+            if (activeGroupItem && activeGroupItem.expanded)
+              activeGroupItem.collapse();
+            else
+              self.exit();
+            break;
+          case KeyEvent.DOM_VK_RETURN:
+          case KeyEvent.DOM_VK_ENTER:
+            activeGroupItem = GroupItems.getActiveGroupItem();
+            if (activeGroupItem) {
+              activeTab = self.getActiveTab();
+
+              if (!activeTab || activeTab.parent != activeGroupItem)
+                activeTab = activeGroupItem.getActiveTab();
+
+              if (activeTab)
+                activeTab.zoomIn();
+              else
+                activeGroupItem.newTab();
+            }
+            break;
+          case KeyEvent.DOM_VK_TAB:
+            // tab/shift + tab to go to the next tab.
+            activeTab = self.getActiveTab();
+            if (activeTab) {
+              let tabItems = (activeTab.parent ? activeTab.parent.getChildren() :
+                              [activeTab]);
+              let length = tabItems.length;
+              let currentIndex = tabItems.indexOf(activeTab);
+
+              if (length > 1) {
+                let newIndex;
+                if (event.shiftKey) {
+                  if (currentIndex == 0)
+                    newIndex = (length - 1);
+                  else
+                    newIndex = (currentIndex - 1);
+                } else {
+                  if (currentIndex == (length - 1))
+                    newIndex = 0;
+                  else
+                    newIndex = (currentIndex + 1);
+                }
+                self.setActive(tabItems[newIndex]);
+              }
+            }
+            break;
+          default:
+            processBrowserKeys(event);
+            preventDefault = false;
+        }
+        if (preventDefault) {
+          event.stopPropagation();
+          event.preventDefault();
+        }
+      }
+    });
+  },
+
+  // ----------
+  // Function: enableSearch
+  // Enables the search feature.
+  enableSearch: function UI_enableSearch() {
+    if (!Search.isEnabled()) {
+      Search.ensureShown();
+      Search.switchToInMode();
+    }
+  },
+
+  // ----------
+  // Function: _createGroupItemOnDrag
+  // Called in response to a mousedown in empty space in the TabView UI;
+  // creates a new groupItem based on the user's drag.
+  _createGroupItemOnDrag: function UI__createGroupItemOnDrag(e) {
+    const minSize = 60;
+    const minMinSize = 15;
+
+    let lastActiveGroupItem = GroupItems.getActiveGroupItem();
+
+    var startPos = { x: e.clientX, y: e.clientY };
+    var phantom = iQ("<div>")
+      .addClass("groupItem phantom activeGroupItem dragRegion")
+      .css({
+        position: "absolute",
+        zIndex: -1,
+        cursor: "default"
+      })
+      .appendTo("body");
+
+    var item = { // a faux-Item
+      container: phantom,
+      isAFauxItem: true,
+      bounds: {},
+      getBounds: function FauxItem_getBounds() {
+        return this.container.bounds();
+      },
+      setBounds: function FauxItem_setBounds(bounds) {
+        this.container.css(bounds);
+      },
+      setZ: function FauxItem_setZ(z) {
+        // don't set a z-index because we want to force it to be low.
+      },
+      setOpacity: function FauxItem_setOpacity(opacity) {
+        this.container.css("opacity", opacity);
+      },
+      // we don't need to pushAway the phantom item at the end, because
+      // when we create a new GroupItem, it'll do the actual pushAway.
+      pushAway: function () {},
+    };
+    item.setBounds(new Rect(startPos.y, startPos.x, 0, 0));
+
+    var dragOutInfo = new Drag(item, e);
+
+    function updateSize(e) {
+      var box = new Rect();
+      box.left = Math.min(startPos.x, e.clientX);
+      box.right = Math.max(startPos.x, e.clientX);
+      box.top = Math.min(startPos.y, e.clientY);
+      box.bottom = Math.max(startPos.y, e.clientY);
+      item.setBounds(box);
+
+      // compute the stationaryCorner
+      var stationaryCorner = "";
+
+      if (startPos.y == box.top)
+        stationaryCorner += "top";
+      else
+        stationaryCorner += "bottom";
+
+      if (startPos.x == box.left)
+        stationaryCorner += "left";
+      else
+        stationaryCorner += "right";
+
+      dragOutInfo.snap(stationaryCorner, false, false); // null for ui, which we don't use anyway.
+
+      box = item.getBounds();
+      if (box.width > minMinSize && box.height > minMinSize &&
+         (box.width > minSize || box.height > minSize))
+        item.setOpacity(1);
+      else
+        item.setOpacity(0.7);
+
+      e.preventDefault();
+    }
+
+    let self = this;
+    function collapse() {
+      let center = phantom.bounds().center();
+      phantom.animate({
+        width: 0,
+        height: 0,
+        top: center.y,
+        left: center.x
+      }, {
+        duration: 300,
+        complete: function() {
+          phantom.remove();
+        }
+      });
+      self.setActive(lastActiveGroupItem);
+    }
+
+    function finalize(e) {
+      iQ(window).unbind("mousemove", updateSize);
+      item.container.removeClass("dragRegion");
+      dragOutInfo.stop();
+      let box = item.getBounds();
+      if (box.width > minMinSize && box.height > minMinSize &&
+         (box.width > minSize || box.height > minSize)) {
+        let opts = {bounds: item.getBounds(), focusTitle: true};
+        let groupItem = new GroupItem([], opts);
+        self.setActive(groupItem);
+        phantom.remove();
+        dragOutInfo = null;
+        gTabView.firstUseExperienced = true;
+      } else {
+        collapse();
+      }
+    }
+
+    iQ(window).mousemove(updateSize)
+    iQ(gWindow).one("mouseup", finalize);
+    e.preventDefault();
+    return false;
+  },
+
+  // ----------
+  // Function: _resize
+  // Update the TabView UI contents in response to a window size change.
+  // Won't do anything if it doesn't deem the resize necessary.
+  // Parameters:
+  //   force - true to update even when "unnecessary"; default false
+  _resize: function UI__resize(force) {
+    if (!this._pageBounds)
+      return;
+
+    // Here are reasons why we *won't* resize:
+    // 1. Panorama isn't visible (in which case we will resize when we do display)
+    // 2. the screen dimensions haven't changed
+    // 3. everything on the screen fits and nothing feels cramped
+    if (!force && !this.isTabViewVisible())
+      return;
+
+    let oldPageBounds = new Rect(this._pageBounds);
+    let newPageBounds = Items.getPageBounds();
+    if (newPageBounds.equals(oldPageBounds))
+      return;
+
+    if (!this.shouldResizeItems())
+      return;
+
+    var items = Items.getTopLevelItems();
+
+    // compute itemBounds: the union of all the top-level items' bounds.
+    var itemBounds = new Rect(this._pageBounds);
+    // We start with pageBounds so that we respect the empty space the user
+    // has left on the page.
+    itemBounds.width = 1;
+    itemBounds.height = 1;
+    items.forEach(function(item) {
+      var bounds = item.getBounds();
+      itemBounds = (itemBounds ? itemBounds.union(bounds) : new Rect(bounds));
+    });
+
+    if (newPageBounds.width < this._pageBounds.width &&
+        newPageBounds.width > itemBounds.width)
+      newPageBounds.width = this._pageBounds.width;
+
+    if (newPageBounds.height < this._pageBounds.height &&
+        newPageBounds.height > itemBounds.height)
+      newPageBounds.height = this._pageBounds.height;
+
+    var wScale;
+    var hScale;
+    if (Math.abs(newPageBounds.width - this._pageBounds.width)
+         > Math.abs(newPageBounds.height - this._pageBounds.height)) {
+      wScale = newPageBounds.width / this._pageBounds.width;
+      hScale = newPageBounds.height / itemBounds.height;
+    } else {
+      wScale = newPageBounds.width / itemBounds.width;
+      hScale = newPageBounds.height / this._pageBounds.height;
+    }
+
+    var scale = Math.min(hScale, wScale);
+    var self = this;
+    var pairs = [];
+    items.forEach(function(item) {
+      var bounds = item.getBounds();
+      bounds.left += (UI.rtl ? -1 : 1) * (newPageBounds.left - self._pageBounds.left);
+      bounds.left *= scale;
+      bounds.width *= scale;
+
+      bounds.top += newPageBounds.top - self._pageBounds.top;
+      bounds.top *= scale;
+      bounds.height *= scale;
+
+      pairs.push({
+        item: item,
+        bounds: bounds
+      });
+    });
+
+    Items.unsquish(pairs);
+
+    pairs.forEach(function(pair) {
+      pair.item.setBounds(pair.bounds, true);
+      pair.item.snap();
+    });
+
+    this._pageBounds = Items.getPageBounds();
+    this._save();
+  },
+  
+  // ----------
+  // Function: shouldResizeItems
+  // Returns whether we should resize the items on the screen, based on whether
+  // the top-level items fit in the screen or not and whether they feel
+  // "cramped" or not.
+  // These computations may be done using cached values. The cache can be
+  // cleared with UI.clearShouldResizeItems().
+  shouldResizeItems: function UI_shouldResizeItems() {
+    let newPageBounds = Items.getPageBounds();
+    
+    // If we don't have cached cached values...
+    if (this._minimalRect === undefined || this._feelsCramped === undefined) {
+
+      // Loop through every top-level Item for two operations:
+      // 1. check if it is feeling "cramped" due to squishing (a technical term),
+      // 2. union its bounds with the minimalRect
+      let feelsCramped = false;
+      let minimalRect = new Rect(0, 0, 1, 1);
+      
+      Items.getTopLevelItems()
+        .forEach(function UI_shouldResizeItems_checkItem(item) {
+          let bounds = new Rect(item.getBounds());
+          feelsCramped = feelsCramped || (item.userSize &&
+            (item.userSize.x > bounds.width || item.userSize.y > bounds.height));
+          bounds.inset(-Trenches.defaultRadius, -Trenches.defaultRadius);
+          minimalRect = minimalRect.union(bounds);
+        });
+      
+      // ensure the minimalRect extends to, but not beyond, the origin
+      minimalRect.left = 0;
+      minimalRect.top  = 0;
+  
+      this._minimalRect = minimalRect;
+      this._feelsCramped = feelsCramped;
+    }
+
+    return this._minimalRect.width > newPageBounds.width ||
+      this._minimalRect.height > newPageBounds.height ||
+      this._feelsCramped;
+  },
+  
+  // ----------
+  // Function: clearShouldResizeItems
+  // Clear the cache of whether we should resize the items on the Panorama
+  // screen, forcing a recomputation on the next UI.shouldResizeItems()
+  // call.
+  clearShouldResizeItems: function UI_clearShouldResizeItems() {
+    delete this._minimalRect;
+    delete this._feelsCramped;
+  },
+
+  // ----------
+  // Function: exit
+  // Exits TabView UI.
+  exit: function UI_exit() {
+    let self = this;
+    let zoomedIn = false;
+
+    if (Search.isEnabled()) {
+      let matcher = Search.createSearchTabMatcher();
+      let matches = matcher.matched();
+
+      if (matches.length > 0) {
+        matches[0].zoomIn();
+        zoomedIn = true;
+      }
+      Search.hide();
+    }
+
+    if (!zoomedIn) {
+      let unhiddenGroups = GroupItems.groupItems.filter(function(groupItem) {
+        return (!groupItem.hidden && groupItem.getChildren().length > 0);
+      });
+      // no pinned tabs and no visible groups: open a new group. open a blank
+      // tab and return
+      if (!unhiddenGroups.length) {
+        let emptyGroups = GroupItems.groupItems.filter(function (groupItem) {
+          return (!groupItem.hidden && !groupItem.getChildren().length);
+        });
+        let group = (emptyGroups.length ? emptyGroups[0] : GroupItems.newGroup());
+        if (!gBrowser._numPinnedTabs) {
+          group.newTab(null, { closedLastTab: true });
+          return;
+        }
+      }
+
+      // If there's an active TabItem, zoom into it. If not (for instance when the
+      // selected tab is an app tab), just go there.
+      let activeTabItem = this.getActiveTab();
+      if (!activeTabItem) {
+        let tabItem = gBrowser.selectedTab._tabViewTabItem;
+        if (tabItem) {
+          if (!tabItem.parent || !tabItem.parent.hidden) {
+            activeTabItem = tabItem;
+          } else { // set active tab item if there is at least one unhidden group
+            if (unhiddenGroups.length > 0)
+              activeTabItem = unhiddenGroups[0].getActiveTab();
+          }
+        }
+      }
+
+      if (activeTabItem) {
+        activeTabItem.zoomIn();
+      } else {
+        if (gBrowser._numPinnedTabs > 0) {
+          if (gBrowser.selectedTab.pinned) {
+            self.goToTab(gBrowser.selectedTab);
+          } else {
+            Array.some(gBrowser.tabs, function(tab) {
+              if (tab.pinned) {
+                self.goToTab(tab);
+                return true;
+              }
+              return false
+            });
+          }
+        }
+      }
+    }
+  },
+
+  // ----------
+  // Function: storageSanity
+  // Given storage data for this object, returns true if it looks valid.
+  _storageSanity: function UI__storageSanity(data) {
+    if (Utils.isEmptyObject(data))
+      return true;
+
+    if (!Utils.isRect(data.pageBounds)) {
+      Utils.log("UI.storageSanity: bad pageBounds", data.pageBounds);
+      data.pageBounds = null;
+      return false;
+    }
+
+    return true;
+  },
+
+  // ----------
+  // Function: _save
+  // Saves the data for this object to persistent storage
+  _save: function UI__save() {
+    if (!this._frameInitialized)
+      return;
+
+    var data = {
+      pageBounds: this._pageBounds
+    };
+
+    if (this._storageSanity(data))
+      Storage.saveUIData(gWindow, data);
+  },
+
+  // ----------
+  // Function: _saveAll
+  // Saves all data associated with TabView.
+  _saveAll: function UI__saveAll() {
+    this._save();
+    GroupItems.saveAll();
+    TabItems.saveAll();
+  },
+
+  // ----------
+  // Function: notifySessionRestoreEnabled
+  // Notify the user that session restore has been automatically enabled
+  // by showing a banner that expects no user interaction. It fades out after
+  // some seconds.
+  notifySessionRestoreEnabled: function UI_notifySessionRestoreEnabled() {
+    let brandBundle = gWindow.document.getElementById("bundle_brand");
+    let brandShortName = brandBundle.getString("brandShortName");
+    let notificationText = tabviewBundle.formatStringFromName(
+      "tabview.notification.sessionStore", [brandShortName], 1);
+
+    let banner = iQ("<div>")
+      .text(notificationText)
+      .addClass("banner")
+      .appendTo("body");
+
+    let onFadeOut = function () {
+      banner.remove();
+    };
+
+    let onFadeIn = function () {
+      setTimeout(function () {
+        banner.animate({opacity: 0}, {duration: 1500, complete: onFadeOut});
+      }, 5000);
+    };
+
+    banner.animate({opacity: 0.7}, {duration: 1500, complete: onFadeIn});
+  }
+};
+
+// ----------
+UI.init();
diff -Nuar omni_old/chrome/browser/skin/classic/browser/browser.css omni/chrome/browser/skin/classic/browser/browser.css
--- omni_old/chrome/browser/skin/classic/browser/browser.css	2010-01-01 00:00:00.000000000 +0800
+++ omni/chrome/browser/skin/classic/browser/browser.css	2012-08-30 15:31:47.813721421 +0800
@@ -895,10 +895,6 @@
   -moz-box-align: stretch;
 }
 
-.urlbar-input-box {
-  -moz-margin-start: 0;
-}
-
 .urlbar-history-dropmarker {
   -moz-appearance: toolbarbutton-dropdown;
 }
@@ -980,29 +976,11 @@
 #page-proxy-favicon {
   width: 16px;
   height: 16px;
-  margin-top: 2px;
-  margin-bottom: 2px;
-  -moz-margin-start: 4px;
-  -moz-margin-end: 3px;
-  list-style-image: url(chrome://browser/skin/identity-icons-generic.png);
-  -moz-image-region: rect(0, 16px, 16px, 0);
-}
-
-.verifiedDomain > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon[pageproxystate="valid"] {
-  list-style-image: url(chrome://browser/skin/identity-icons-https.png);
-}
-
-.verifiedIdentity > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon[pageproxystate="valid"] {
-  list-style-image: url(chrome://browser/skin/identity-icons-https-ev.png);
-}
-
-#identity-box:hover > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon {
-  -moz-image-region: rect(0, 32px, 16px, 16px);
+  margin: 2px 4px;
 }
 
-#identity-box:hover:active > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon,
-#identity-box[open=true] > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon {
-  -moz-image-region: rect(0, 48px, 16px, 32px);
+#page-proxy-favicon:not([src]) {
+  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
 }
 
 #page-proxy-favicon[pageproxystate="invalid"] {
@@ -1011,6 +989,9 @@
 
 /* Identity indicator */
 #identity-box {
+  background-image: -moz-linear-gradient(hsl(0,0%,98%), hsl(0,0%,92%));
+  box-shadow: 0 1px 0 hsla(0,0%,0%,.05) inset;
+  -moz-border-end: 1px solid rgba(0,0,0,.1);
   padding: 1px;
   margin: -1px;
   -moz-margin-end: 0;
@@ -1032,22 +1013,44 @@
   outline-offset: -3px;
 }
 
+#identity-box:hover:active,
+#identity-box[open="true"] {
+  background-image: -moz-linear-gradient(hsl(0,0%,92%), hsl(0,0%,82%));
+  box-shadow: 0 1px 1px hsla(0,0%,0%,.3) inset,
+              0 1px 3px hsla(0,0%,0%,.3) inset;
+}
+
 #identity-icon-labels {
   -moz-padding-start: 2px;
   -moz-padding-end: 5px;
 }
 
-#urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity {
-  background-color: #fff;
+#identity-box.verifiedDomain {
+  background-image: -moz-linear-gradient(hsl(215,60%,92%), hsl(215,58%,88%));
+  box-shadow: 0 1px 0 hsla(215,54%,33%,.05) inset;
+  -moz-border-end-color: hsla(215,54%,33%,.2);
+  color: hsl(215,54%,33%);
+}
+
+#identity-box.verifiedDomain:hover:active,
+#identity-box.verifiedDomain[open="true"] {
+  background-image: -moz-linear-gradient(hsl(215,80%,80%), hsl(215,67%,65%));
+  box-shadow: 0 1px 1px hsla(215,54%,33%,.7) inset,
+              0 1px 3px 1px hsla(215,54%,33%,.5) inset;
+}
+
+#identity-box.verifiedIdentity {
+  background-image: -moz-linear-gradient(hsl(91,70%,90%), hsl(93,60%,81%));
+  box-shadow: 0 1px 0 hsla(92,81%,16%,.05) inset;
+  -moz-border-end-color: hsla(92,81%,16%,.2);
   color: hsl(92,100%,30%);
-  -moz-margin-end: 4px;
-  background-image: -moz-linear-gradient(hsla(92,81%,16%,0),
-                                         hsla(92,81%,16%,.2) 25%,
-                                         hsla(92,81%,16%,.2) 75%,
-                                         hsla(92,81%,16%,0));
-  background-position: right;
-  background-size: 1px;
-  background-repeat: no-repeat;
+}
+
+#identity-box.verifiedIdentity:hover:active,
+#identity-box.verifiedIdentity[open="true"] {
+  background-image: -moz-linear-gradient(hsl(92,65%,70%), hsl(92,40%,48%));
+  box-shadow: 0 1px 1px hsla(92,81%,16%,.6) inset,
+              0 1px 3px 1px hsla(92,81%,16%,.5) inset;
 }
 
 #identity-box.verifiedIdentity:-moz-locale-dir(rtl) {
diff -Nuar omni_old/chrome/browser/skin/classic/browser/browser.css.orig omni/chrome/browser/skin/classic/browser/browser.css.orig
--- omni_old/chrome/browser/skin/classic/browser/browser.css.orig	1970-01-01 08:00:00.000000000 +0800
+++ omni/chrome/browser/skin/classic/browser/browser.css.orig	2010-01-01 00:00:00.000000000 +0800
@@ -0,0 +1,2508 @@
+
+@import url("chrome://global/skin/");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace html url("http://www.w3.org/1999/xhtml");
+
+
+
+#menubar-items {
+  -moz-box-orient: vertical; /* for flex hack */
+}
+
+#main-menubar {
+  -moz-box-flex: 1; /* make menu items expand to fill toolbar height */
+}
+
+#navigator-toolbox {
+  -moz-appearance: none;
+  background-color: transparent;
+  border-top: none;
+}
+
+#main-window:not([disablechrome]) #navigator-toolbox[tabsontop=true] {
+  border-bottom: 1px solid ThreeDShadow;
+}
+
+#navigator-toolbox[tabsontop=true] > toolbar:not(:-moz-lwtheme):not(#toolbar-menubar):not(#TabsToolbar),
+#navigator-toolbox[tabsontop=false] > toolbar:not(:-moz-lwtheme):not(#toolbar-menubar) {
+  -moz-appearance: none;
+  border-style: none;
+  background-color: -moz-Dialog;
+}
+
+#navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar) {
+  padding-top: 1px;
+  padding-bottom: 1px;
+}
+
+#nav-bar:not(:-moz-lwtheme),
+#nav-bar[collapsed=true] + toolbar:not(:-moz-lwtheme),
+#nav-bar[collapsed=true] + #customToolbars + #PersonalToolbar:not(:-moz-lwtheme),
+#nav-bar[tabsontop=true],
+#nav-bar[tabsontop=true][collapsed=true]:not([customizing]) + toolbar,
+#nav-bar[tabsontop=true][collapsed=true]:not([customizing]) + #customToolbars + #PersonalToolbar {
+  background-image: -moz-linear-gradient(rgba(255,255,255,.3), rgba(255,255,255,0));
+}
+
+#personal-bookmarks {
+  min-height: 29px;
+}
+
+#browser-bottombox {
+  /* opaque for layers optimization */
+  background-color: -moz-Dialog;
+}
+
+#urlbar:-moz-lwtheme:not([focused="true"]),
+.searchbar-textbox:-moz-lwtheme:not([focused="true"]) {
+  opacity: .85;
+}
+
+/* Places toolbar */
+toolbarbutton.bookmark-item {
+  margin: 0;
+  padding: 2px 3px;
+}
+
+toolbarbutton.bookmark-item:hover:active,
+toolbarbutton.bookmark-item[open="true"] {
+  padding-top: 3px;
+  padding-bottom: 1px;
+  -moz-padding-start: 4px;
+  -moz-padding-end: 2px;
+}
+
+.bookmark-item > .toolbarbutton-icon {
+  width: 16px;
+  height: 16px;
+}
+
+/* Prevent [mode="icons"] from hiding the label */
+.bookmark-item > .toolbarbutton-text {
+  display: -moz-box !important;
+}
+
+.bookmark-item > .toolbarbutton-menu-dropmarker {
+  display: none;
+}
+
+/* Dropmarker for folder bookmarks */
+.bookmark-item[container] > .toolbarbutton-menu-dropmarker {
+  display: -moz-box !important;
+}
+
+#wrapper-personal-bookmarks[place="palette"] > .toolbarpaletteitem-box {
+  background: url("chrome://browser/skin/places/bookmarksToolbar.png") no-repeat center;
+}
+
+.bookmarks-toolbar-customize {
+  max-width: 15em !important;
+  list-style-image: url("chrome://browser/skin/places/bookmarksToolbar.png") !important;
+}
+
+/* Bookmark menus */
+menu.bookmark-item,
+menuitem.bookmark-item {
+  min-width: 0;
+  max-width: 32em;
+}
+
+.bookmark-item > .menu-iconic-left {
+  margin-top: 0;
+  margin-bottom: 0;
+}
+
+.bookmark-item > .menu-iconic-left > .menu-iconic-icon {
+  -moz-padding-start: 0px;
+}
+
+/* Bookmark drag and drop styles */
+.bookmark-item[dragover-into="true"] {
+  background: Highlight !important;
+  color: HighlightText !important;
+}
+
+/* rules for menupopup drop indicators */
+.menupopup-drop-indicator-bar {
+  position: relative;
+  /* these two margins must together compensate the indicator's height */
+  margin-top: -1px;
+  margin-bottom: -1px;
+}
+
+.menupopup-drop-indicator {
+  list-style-image: none;
+  height: 2px;
+  -moz-margin-end: -4em;
+  background-color: Highlight;
+}
+
+/* Bookmarks toolbar */
+#PlacesToolbarDropIndicator {
+  list-style-image: url(chrome://browser/skin/places/toolbarDropMarker.png);
+}
+
+/* Bookmark items */
+.bookmark-item {
+  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
+}
+
+.bookmark-item[container] {
+  list-style-image: url("moz-icon://stock/gtk-directory?size=menu");
+}
+
+.bookmark-item[container][livemark] {
+  list-style-image: url("chrome://browser/skin/feeds/feedIcon16.png");
+}
+
+.bookmark-item[container][livemark] .bookmark-item {
+  list-style-image: url("chrome://browser/skin/places/livemark-item.png");
+  -moz-image-region: rect(0px, 16px, 16px, 0px);
+}
+
+.bookmark-item[container][livemark] .bookmark-item[visited] {
+  -moz-image-region: rect(0px, 32px, 16px, 16px);
+}
+
+.bookmark-item[container][query] {
+  list-style-image: url("chrome://browser/skin/places/query.png");
+}
+
+.bookmark-item[query][tagContainer] {
+  list-style-image: url("chrome://browser/skin/places/tag.png");
+}
+
+.bookmark-item[query][dayContainer] {
+  list-style-image: url("chrome://browser/skin/places/calendar.png");
+}
+
+.bookmark-item[query][hostContainer] {
+  list-style-image: url("moz-icon://stock/gtk-directory?size=menu");
+}
+
+.bookmark-item[query][hostContainer][open] {
+  list-style-image: url("moz-icon://stock/gtk-directory?size=menu");
+}
+
+.bookmark-item[cutting] > .toolbarbutton-icon,
+.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-icon {
+  opacity: 0.5;
+}
+
+.bookmark-item[cutting] > .toolbarbutton-text,
+.bookmark-item[cutting] > .menu-iconic-left > .menu-iconic-text {
+  opacity: 0.7;
+}
+
+/* Stock icons for the menu bar items */
+menuitem:not([type]):not(.menuitem-tooltip):not(.menuitem-iconic-tooltip) {
+  -moz-binding: url("chrome://global/content/bindings/menu.xml#menuitem-iconic");
+}
+
+#appmenu_newNavigator,
+#placesContext_open\:newwindow,
+#menu_newNavigator,
+#context-openlink,
+#context-openframe {
+  list-style-image: url("chrome://browser/skin/Toolbar-small.png");
+  -moz-image-region: rect(0px 80px 16px 64px);
+}
+
+#appmenu_newTab,
+#appmenu_newTab_popup,
+#placesContext_open\:newtab,
+#placesContext_openContainer\:tabs,
+#menu_newNavigatorTab,
+#context-openlinkintab,
+#context-openframeintab {
+  list-style-image: url("chrome://browser/skin/Toolbar-small.png");
+  -moz-image-region: rect(0px 64px 16px 48px);
+}
+
+#appmenu_openFile,
+#menu_openFile {
+  list-style-image: url("moz-icon://stock/gtk-open?size=menu");
+}
+
+#menu_close {
+  list-style-image: url("moz-icon://stock/gtk-close?size=menu");
+}
+
+#context-media-play {
+  list-style-image: url("moz-icon://stock/gtk-media-play?size=menu");
+}
+
+#context-media-pause {
+  list-style-image: url("moz-icon://stock/gtk-media-pause?size=menu");
+}
+
+#appmenu_savePage,
+#menu_savePage,
+#context-savelink,
+#context-saveimage,
+#context-savevideo,
+#context-saveaudio,
+#context-savepage,
+#context-saveframe {
+  list-style-image: url("moz-icon://stock/gtk-save-as?size=menu");
+}
+
+#appmenu_printPreview,
+#menu_printPreview {
+  list-style-image: url("moz-icon://stock/gtk-print-preview?size=menu");
+}
+
+#appmenu_print,
+#appmenu_print_popup,
+#menu_print,
+#context-printframe {
+  list-style-image: url("moz-icon://stock/gtk-print?size=menu");
+}
+
+#appmenu-quit,
+#menu_FileQuitItem {
+  list-style-image: url("moz-icon://stock/gtk-quit?size=menu");
+}
+
+#menu_undo,
+#context-undo {
+  list-style-image: url("moz-icon://stock/gtk-undo?size=menu");
+}
+
+#menu_undo[disabled],
+#context-undo[disabled] {
+  list-style-image: url("moz-icon://stock/gtk-undo?size=menu&state=disabled");
+}
+
+#menu_redo {
+  list-style-image: url("moz-icon://stock/gtk-redo?size=menu");
+}
+
+#menu_redo[disabled] {
+  list-style-image: url("moz-icon://stock/gtk-redo?size=menu&state=disabled");
+}
+
+#menu_cut,
+#placesContext_cut,
+#context-cut {
+  list-style-image: url("moz-icon://stock/gtk-cut?size=menu");
+}
+
+#menu_cut[disabled],
+#placesContext_cut[disabled],
+#context-cut[disabled] {
+  list-style-image: url("moz-icon://stock/gtk-cut?size=menu&state=disabled");
+}
+
+#menu_copy,
+#placesContext_copy,
+#context-copy,
+#context-copyimage,
+#context-copyvideourl,
+#context-copyaudiourl,
+#context-copylink,
+#context-copyemail {
+  list-style-image: url("moz-icon://stock/gtk-copy?size=menu");
+}
+
+#menu_copy[disabled],
+#placesContext_copy[disabled],
+#context-copy[disabled] {
+  list-style-image: url("moz-icon://stock/gtk-copy?size=menu&state=disabled");
+}
+
+#menu_paste,
+#placesContext_paste,
+#context-paste {
+  list-style-image: url("moz-icon://stock/gtk-paste?size=menu");
+}
+
+#menu_paste[disabled],
+#placesContext_paste[disabled],
+#context-paste[disabled] {
+  list-style-image: url("moz-icon://stock/gtk-paste?size=menu&state=disabled");
+}
+
+#menu_delete,
+#placesContext_delete,
+#placesContext_delete_history,
+#context-delete {
+  list-style-image: url("moz-icon://stock/gtk-delete?size=menu");
+}
+
+#menu_delete[disabled],
+#placesContext_delete[disabled],
+#placesContext_delete_history[disabled],
+#context-delete[disabled] {
+  list-style-image: url("moz-icon://stock/gtk-delete?size=menu&state=disabled");
+}
+
+#menu_selectAll,
+#context-selectall {
+  list-style-image: url("moz-icon://stock/gtk-select-all?size=menu");
+}
+
+#appmenu_find,
+#menu_find {
+  list-style-image: url("moz-icon://stock/gtk-find?size=menu");
+}
+
+#menu_find[disabled] {
+  list-style-image: url("moz-icon://stock/gtk-find?size=menu&state=disabled");
+}
+
+#appmenu_customize,
+#appmenu_preferences,
+#menu_preferences {
+  list-style-image: url("moz-icon://stock/gtk-preferences?size=menu");
+}
+
+#menu_stop,
+#context-stop {
+  list-style-image: url("moz-icon://stock/gtk-stop?size=menu");
+}
+
+#menu_stop[disabled],
+#context-stop[disabled] {
+  list-style-image: url("moz-icon://stock/gtk-stop?size=menu&state=disabled");
+}
+
+#menu_reload,
+#placesContext_reload,
+#context-reload,
+#context-reloadframe {
+  list-style-image: url("moz-icon://stock/gtk-refresh?size=menu");
+}
+
+#menu_reload[disabled],
+#context-reload[disabled] {
+  list-style-image: url("moz-icon://stock/gtk-refresh?size=menu&state=disabled");
+}
+
+#menu_zoomEnlarge {
+  list-style-image: url("moz-icon://stock/gtk-zoom-in?size=menu");
+}
+
+#menu_zoomReduce {
+  list-style-image: url("moz-icon://stock/gtk-zoom-out?size=menu");
+}
+
+#menu_zoomReset {
+  list-style-image: url("moz-icon://stock/gtk-zoom-100?size=menu");
+}
+
+#historyMenuBack,
+#context-back {
+  list-style-image: url("moz-icon://stock/gtk-go-back-ltr?size=menu");
+}
+
+#historyMenuBack[disabled],
+#context-back[disabled] {
+  list-style-image: url("moz-icon://stock/gtk-go-back-ltr?size=menu&state=disabled");
+}
+
+#historyMenuBack:-moz-locale-dir(rtl),
+#context-back:-moz-locale-dir(rtl) {
+  list-style-image: url("moz-icon://stock/gtk-go-back-rtl?size=menu");
+}
+
+#historyMenuBack[disabled]:-moz-locale-dir(rtl),
+#context-back[disabled]:-moz-locale-dir(rtl) {
+  list-style-image: url("moz-icon://stock/gtk-go-back-rtl?size=menu&state=disabled");
+}
+
+#historyMenuForward,
+#context-forward {
+  list-style-image: url("moz-icon://stock/gtk-go-forward-ltr?size=menu");
+}
+
+#historyMenuForward[disabled],
+#context-forward[disabled] {
+  list-style-image: url("moz-icon://stock/gtk-go-forward-ltr?size=menu&state=disabled");
+}
+
+#historyMenuForward:-moz-locale-dir(rtl),
+#context-forward:-moz-locale-dir(rtl) {
+  list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=menu");
+}
+
+#historyMenuForward[disabled]:-moz-locale-dir(rtl),
+#context-forward[disabled]:-moz-locale-dir(rtl) {
+  list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=menu&state=disabled");
+}
+
+#historyMenuHome {
+  list-style-image: url("moz-icon://stock/gtk-home?size=menu");
+}
+
+#appmenu_history,
+#appmenu_showAllHistory,
+#menu_showAllHistory {
+  list-style-image: url("chrome://browser/skin/Toolbar-small.png");
+  -moz-image-region: rect(0px 32px 16px 16px);
+}
+
+#appmenu_bookmarks,
+#appmenu_showAllBookmarks,
+#bookmarksShowAll {
+  list-style-image: url("chrome://browser/skin/Toolbar-small.png");
+  -moz-image-region: rect(0px 48px 16px 32px);
+}
+
+#subscribeToPageMenuitem:not([disabled]),
+#subscribeToPageMenupopup,
+#BMB_subscribeToPageMenuitem:not([disabled]),
+#BMB_subscribeToPageMenupopup {
+  list-style-image: url("chrome://browser/skin/page-livemarks.png");
+}
+
+#bookmarksToolbarFolderMenu,
+#BMB_bookmarksToolbar {
+  list-style-image: url("chrome://browser/skin/places/bookmarksToolbar.png");
+}
+
+#BMB_bookmarkThisPage {
+  list-style-image: url("chrome://browser/skin/places/starPage.png");
+}
+
+#BMB_unsortedBookmarks {
+  list-style-image: url("chrome://browser/skin/places/unsortedBookmarks.png");
+}
+
+#appmenu_downloads,
+#menu_openDownloads {
+  list-style-image: url("chrome://browser/skin/Toolbar-small.png");
+  -moz-image-region: rect(0px 16px 16px 0px);
+}
+
+#appmenu_addons,
+#menu_openAddons {
+  list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric-16.png");
+}
+
+#menu_pageInfo,
+#context-viewinfo,
+#context-viewframeinfo {
+  list-style-image: url("moz-icon://stock/gtk-info?size=menu");
+}
+
+#appmenu_privateBrowsing,
+#privateBrowsingItem {
+  list-style-image: url("chrome://browser/skin/Privacy-16.png");
+}
+
+#placesContext_show\:info {
+  list-style-image: url("moz-icon://stock/gtk-properties?size=menu");
+}
+
+#appmenu_sanitizeHistory,
+#sanitizeItem {
+  list-style-image: url("moz-icon://stock/gtk-clear?size=menu");
+}
+
+#appmenu_help,
+#appmenu_openHelp,
+#menu_openHelp {
+  list-style-image: url("moz-icon://stock/gtk-help?size=menu");
+}
+
+#appmenu_about,
+#aboutName {
+  list-style-image: url("moz-icon://stock/gtk-about?size=menu");
+}
+
+#javascriptConsole {
+  list-style-image: url("chrome://global/skin/console/console.png");
+}
+
+/* Primary toolbar buttons */
+.toolbarbutton-1:not([type="menu-button"]) {
+  -moz-box-orient: vertical;
+  min-width: 0;
+  list-style-image: url("chrome://browser/skin/Toolbar.png");
+}
+
+.toolbarbutton-1 > .toolbarbutton-icon {
+  -moz-margin-end: 0;
+}
+
+toolbar[mode="full"] .toolbarbutton-1:not([type="menu-button"]),
+toolbar[mode="full"] .toolbarbutton-1 > .toolbarbutton-menubutton-button {
+  min-width: 57px;
+}
+
+.toolbarbutton-1:not([type="menu-button"]),
+.toolbarbutton-1 > .toolbarbutton-menubutton-button {
+  padding: 5px;
+}
+
+.toolbarbutton-1[checked="true"] {
+  padding: 5px !important;
+}
+
+/* 24px primary toolbar buttons */
+#back-button {
+  list-style-image: url("moz-icon://stock/gtk-go-back-ltr?size=toolbar");
+}
+#back-button[disabled="true"] {
+  list-style-image: url("moz-icon://stock/gtk-go-back-ltr?size=toolbar&state=disabled");
+}
+
+#back-button:-moz-locale-dir(rtl) {
+  list-style-image: url("moz-icon://stock/gtk-go-back-rtl?size=toolbar");
+}
+#back-button[disabled="true"]:-moz-locale-dir(rtl) {
+  list-style-image: url("moz-icon://stock/gtk-go-back-rtl?size=toolbar&state=disabled");
+}
+
+#forward-button,
+      window:not([chromehidden~=toolbar]) :-moz-any(#nav-bar[currentset*="unified-back-forward-button,urlbar-container"][mode=icons],                #nav-bar:not([currentset])[mode=icons])                 > #unified-back-forward-button > #forward-button {
+  list-style-image: url("moz-icon://stock/gtk-go-forward-ltr?size=toolbar");
+}
+#forward-button:-moz-locale-dir(rtl),
+      window:not([chromehidden~=toolbar]) :-moz-any(#nav-bar[currentset*="unified-back-forward-button,urlbar-container"][mode=icons],                #nav-bar:not([currentset])[mode=icons])                 > #unified-back-forward-button > #forward-button:-moz-locale-dir(rtl) {
+  list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=toolbar");
+}
+
+#forward-button[disabled] {
+  list-style-image: url("moz-icon://stock/gtk-go-forward-ltr?size=toolbar&state=disabled");
+}
+#forward-button[disabled]:-moz-locale-dir(rtl) {
+  list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=toolbar&state=disabled");
+}
+
+      window:not([chromehidden~=toolbar]) :-moz-any(#nav-bar[currentset*="unified-back-forward-button,urlbar-container"][mode=icons],                #nav-bar:not([currentset])[mode=icons])                 > #unified-back-forward-button:not([switchingtabs]) > #forward-button {
+  -moz-transition: 150ms ease-out;
+}
+
+      window:not([chromehidden~=toolbar]) :-moz-any(#nav-bar[currentset*="unified-back-forward-button,urlbar-container"][mode=icons],                #nav-bar:not([currentset])[mode=icons])                 > #unified-back-forward-button > #forward-button[disabled] {
+  -moz-transform: scale(0);
+  opacity: 0;
+  pointer-events: none;
+}
+
+#reload-button {
+  list-style-image: url("moz-icon://stock/gtk-refresh?size=toolbar");
+}
+#reload-button[disabled="true"] {
+  list-style-image: url("moz-icon://stock/gtk-refresh?size=toolbar&state=disabled");
+}
+
+#stop-button {
+  list-style-image: url("moz-icon://stock/gtk-stop?size=toolbar");
+}
+#stop-button[disabled="true"] {
+  list-style-image: url("moz-icon://stock/gtk-stop?size=toolbar&state=disabled");
+}
+
+#home-button {
+  list-style-image: url("moz-icon://stock/gtk-home?size=toolbar");
+}
+#home-button[disabled="true"] {
+  list-style-image: url("moz-icon://stock/gtk-home?size=toolbar&state=disabled");
+}
+
+/* tabview button */
+
+#tabview-button,
+#menu_tabview {
+  list-style-image: url(chrome://browser/skin/tabview/tabview.png);
+  -moz-image-region: rect(0, 80px, 16px, 64px);
+}
+
+#tabview-button[groups="0"],
+#menu_tabview[groups="0"] {
+  -moz-image-region: rect(0, 16px, 16px, 0);
+}
+
+#tabview-button[groups="1"],
+#menu_tabview[groups="1"] {
+  -moz-image-region: rect(0, 32px, 16px, 16px);
+}
+
+#tabview-button[groups="2"],
+#menu_tabview[groups="2"] {
+  -moz-image-region: rect(0, 48px, 16px, 32px);
+}
+
+#tabview-button[groups="3"],
+#menu_tabview[groups="3"] {
+  -moz-image-region: rect(0, 64px, 16px, 48px);
+}
+
+#downloads-button {
+  -moz-image-region: rect(0px 24px 24px 0px);
+}
+
+#history-button {
+  -moz-image-region: rect(0px 48px 24px 24px);
+}
+
+#bookmarks-button,
+#bookmarks-menu-button {
+  -moz-image-region: rect(0px 72px 24px 48px);
+}
+
+#bookmarks-menu-button.bookmark-item {
+  list-style-image: url("chrome://browser/skin/Toolbar-small.png");
+}
+
+#bookmarks-menu-button.toolbarbutton-1 {
+  -moz-box-orient: horizontal;
+}
+
+#print-button {
+  list-style-image: url("moz-icon://stock/gtk-print?size=toolbar");
+}
+#print-button[disabled="true"] {
+  list-style-image: url("moz-icon://stock/gtk-print?size=toolbar&state=disabled");
+}
+
+#new-tab-button {
+  -moz-image-region: rect(0px 96px 24px 72px);
+}
+
+#new-window-button {
+  -moz-image-region: rect(0px 120px 24px 96px);
+}
+
+#cut-button {
+  list-style-image: url("moz-icon://stock/gtk-cut?size=toolbar");
+}
+#cut-button[disabled="true"] {
+  list-style-image: url("moz-icon://stock/gtk-cut?size=toolbar&state=disabled");
+}
+
+#copy-button {
+  list-style-image: url("moz-icon://stock/gtk-copy?size=toolbar");
+}
+#copy-button[disabled="true"] {
+  list-style-image: url("moz-icon://stock/gtk-copy?size=toolbar&state=disabled");
+}
+
+#paste-button {
+  list-style-image: url("moz-icon://stock/gtk-paste?size=toolbar");
+}
+#paste-button[disabled="true"] {
+  list-style-image: url("moz-icon://stock/gtk-paste?size=toolbar&state=disabled");
+}
+
+#fullscreen-button {
+  list-style-image: url("moz-icon://stock/gtk-fullscreen?size=toolbar");
+}
+
+#zoom-out-button {
+  list-style-image: url("moz-icon://stock/gtk-zoom-out?size=toolbar");
+}
+
+#zoom-in-button {
+  list-style-image: url("moz-icon://stock/gtk-zoom-in?size=toolbar");
+}
+
+#sync-button {
+  -moz-image-region: rect(0px 144px 24px 120px);
+}
+#sync-button[status="active"] {
+  list-style-image: url("chrome://browser/skin/sync-24-throbber.png");
+  -moz-image-region: rect(0px 24px 24px 0px);
+}
+
+#feed-button {
+  -moz-image-region: rect(0px 168px 24px 144px);
+}
+
+#feed-button[disabled="true"] > .toolbarbutton-icon {
+  opacity: .3;
+}
+
+/* 16px primary toolbar buttons */
+toolbar[iconsize="small"] .toolbarbutton-1:not([type="menu-button"]) {
+  -moz-box-orient: vertical;
+  min-width: 0;
+  list-style-image: url("chrome://browser/skin/Toolbar-small.png");
+}
+
+toolbar[iconsize="small"] .toolbarbutton-1[type="menu-button"] {
+  border: 0 !important;
+}
+
+toolbar[iconsize="small"] #back-button {
+  list-style-image: url("moz-icon://stock/gtk-go-back-ltr?size=menu");
+}
+.unified-nav-back[_moz-menuactive] {
+  list-style-image: url("moz-icon://stock/gtk-go-back-ltr?size=menu") !important;
+}
+toolbar[iconsize="small"] #back-button[disabled="true"] {
+  list-style-image: url("moz-icon://stock/gtk-go-back-ltr?size=menu&state=disabled");
+}
+
+toolbar[iconsize="small"] #back-button:-moz-locale-dir(rtl) {
+  list-style-image: url("moz-icon://stock/gtk-go-back-rtl?size=menu");
+}
+.unified-nav-back[_moz-menuactive]:-moz-locale-dir(rtl) {
+  list-style-image: url("moz-icon://stock/gtk-go-back-rtl?size=menu") !important;
+}
+toolbar[iconsize="small"] #back-button[disabled="true"]:-moz-locale-dir(rtl) {
+  list-style-image: url("moz-icon://stock/gtk-go-back-rtl?size=menu&state=disabled");
+}
+
+toolbar[iconsize=small] #forward-button,
+window:not([chromehidden~=toolbar]) :-moz-any(#nav-bar[currentset*="unified-back-forward-button,urlbar-container"][mode=icons][iconsize=small],#nav-bar:not([currentset])[mode=icons][iconsize=small]) > #unified-back-forward-button > #forward-button {
+  list-style-image: url("moz-icon://stock/gtk-go-forward-ltr?size=menu");
+}
+.unified-nav-forward[_moz-menuactive] {
+  list-style-image: url("moz-icon://stock/gtk-go-forward-ltr?size=menu") !important;
+}
+toolbar[iconsize=small] #forward-button[disabled] {
+  list-style-image: url("moz-icon://stock/gtk-go-forward-ltr?size=menu&state=disabled");
+}
+
+toolbar[iconsize=small] #forward-button:-moz-locale-dir(rtl),
+window:not([chromehidden~=toolbar]) :-moz-any(#nav-bar[currentset*="unified-back-forward-button,urlbar-container"][mode=icons][iconsize=small],#nav-bar:not([currentset])[mode=icons][iconsize=small]) > #unified-back-forward-button > #forward-button:-moz-locale-dir(rtl) {
+  list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=menu");
+}
+.unified-nav-forward[_moz-menuactive]:-moz-locale-dir(rtl) {
+  list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=menu") !important;
+}
+toolbar[iconsize=small] #forward-button[disabled]:-moz-locale-dir(rtl) {
+  list-style-image: url("moz-icon://stock/gtk-go-forward-rtl?size=menu&state=disabled");
+}
+
+toolbar[iconsize="small"] #stop-button {
+  list-style-image: url("moz-icon://stock/gtk-stop?size=menu");
+}
+toolbar[iconsize="small"] #stop-button[disabled="true"] {
+  list-style-image: url("moz-icon://stock/gtk-stop?size=menu&state=disabled");
+}
+
+toolbar[iconsize="small"] #reload-button {
+  list-style-image: url("moz-icon://stock/gtk-refresh?size=menu");
+}
+toolbar[iconsize="small"] #reload-button[disabled="true"] {
+  list-style-image: url("moz-icon://stock/gtk-refresh?size=menu&state=disabled");
+}
+
+toolbar[iconsize="small"] #home-button,
+#home-button.bookmark-item {
+  list-style-image: url("moz-icon://stock/gtk-home?size=menu");
+}
+toolbar[iconsize="small"] #home-button[disabled="true"],
+#home-button.bookmark-item[disabled="true"] {
+  list-style-image: url("moz-icon://stock/gtk-home?size=menu&state=disabled");
+}
+
+toolbar[iconsize="small"] #downloads-button {
+  -moz-image-region: rect(0px 16px 16px 0px);
+}
+
+toolbar[iconsize="small"] #history-button {
+  -moz-image-region: rect(0px 32px 16px 16px);
+}
+
+toolbar[iconsize="small"] #bookmarks-button,
+toolbar[iconsize="small"] #bookmarks-menu-button,
+#bookmarks-menu-button.bookmark-item {
+  -moz-image-region: rect(0px 48px 16px 32px);
+}
+
+toolbar[iconsize="small"] #print-button {
+  list-style-image: url("moz-icon://stock/gtk-print?size=menu");
+}
+toolbar[iconsize="small"] #print-button[disabled="true"] {
+  list-style-image: url("moz-icon://stock/gtk-print?size=menu&state=disabled");
+}
+
+toolbar[iconsize="small"] #new-tab-button {
+  -moz-image-region: rect(0px 64px 16px 48px);
+}
+
+toolbar[iconsize="small"] #new-window-button {
+  -moz-image-region: rect(0px 80px 16px 64px);
+}
+
+toolbar[iconsize="small"] #cut-button {
+  list-style-image: url("moz-icon://stock/gtk-cut?size=menu");
+}
+toolbar[iconsize="small"] #cut-button[disabled="true"] {
+  list-style-image: url("moz-icon://stock/gtk-cut?size=menu&state=disabled");
+}
+
+toolbar[iconsize="small"] #copy-button {
+  list-style-image: url("moz-icon://stock/gtk-copy?size=menu");
+}
+toolbar[iconsize="small"] #copy-button[disabled="true"] {
+  list-style-image: url("moz-icon://stock/gtk-copy?size=menu&state=disabled");
+}
+
+toolbar[iconsize="small"] #paste-button {
+  list-style-image: url("moz-icon://stock/gtk-paste?size=menu");
+}
+toolbar[iconsize="small"] #paste-button[disabled="true"] {
+  list-style-image: url("moz-icon://stock/gtk-paste?size=menu&state=disabled");
+}
+
+toolbar[iconsize="small"] #fullscreen-button {
+  list-style-image: url("moz-icon://stock/gtk-fullscreen?size=menu");
+}
+
+toolbar[iconsize="small"] #zoom-out-button {
+  list-style-image: url("moz-icon://stock/gtk-zoom-out?size=menu");
+}
+
+toolbar[iconsize="small"] #zoom-in-button {
+  list-style-image: url("moz-icon://stock/gtk-zoom-in?size=menu");
+}
+
+toolbar[iconsize="small"] #sync-button {
+  -moz-image-region: rect(0px 96px 16px 80px);
+}
+toolbar[iconsize="small"] #sync-button[status="active"] {
+  list-style-image: url("chrome://browser/skin/sync-16-throbber.png");
+  -moz-image-region: rect(0px 16px 16px 0px);
+}
+
+toolbar[iconsize="small"] #feed-button {
+  -moz-image-region: rect(0px 112px 16px 96px);
+}
+
+/* Fullscreen window controls */
+#window-controls {
+  -moz-box-align: start;
+  -moz-margin-start: 10px;
+}
+
+#minimize-button {
+  list-style-image: url("chrome://global/skin/icons/Minimize.gif");
+}
+#restore-button {
+  list-style-image: url("chrome://global/skin/icons/Restore.gif");
+}
+#close-button {
+  list-style-image: url("chrome://global/skin/icons/Close.gif");
+}
+
+/* Location bar */
+#urlbar {
+  width: 7em;
+  min-width: 7em;
+  -moz-appearance: textfield;
+  padding: 0;
+}
+
+.urlbar-textbox-container {
+  -moz-appearance: none;
+  -moz-box-align: stretch;
+}
+
+.urlbar-input-box {
+  -moz-margin-start: 0;
+}
+
+.urlbar-history-dropmarker {
+  -moz-appearance: toolbarbutton-dropdown;
+}
+
+#urlbar-container {
+  -moz-box-orient: horizontal;
+  -moz-box-align: stretch;
+}
+
+      window:not([chromehidden~=toolbar]) :-moz-any(#nav-bar[currentset*="unified-back-forward-button,urlbar-container"][mode=icons],                #nav-bar:not([currentset])[mode=icons])                 > #unified-back-forward-button + #urlbar-container {
+  -moz-padding-start: 32px;
+  -moz-margin-start: -32px;
+  position: relative;
+  pointer-events: none;
+}
+
+window:not([chromehidden~=toolbar]) :-moz-any(#nav-bar[currentset*="unified-back-forward-button,urlbar-container"][mode=icons][iconsize=small],#nav-bar:not([currentset])[mode=icons][iconsize=small]) > #unified-back-forward-button + #urlbar-container {
+  -moz-padding-start: 24px;
+  -moz-margin-start: -24px;
+}
+
+      window:not([chromehidden~=toolbar]) :-moz-any(#nav-bar[currentset*="unified-back-forward-button,urlbar-container"][mode=icons],                #nav-bar:not([currentset])[mode=icons])                 > #unified-back-forward-button + #urlbar-container > #urlbar {
+  pointer-events: all;
+}
+
+      window:not([chromehidden~=toolbar]) :-moz-any(#nav-bar[currentset*="unified-back-forward-button,urlbar-container"][mode=icons],                #nav-bar:not([currentset])[mode=icons])                 > #unified-back-forward-button:not([switchingtabs]) + #urlbar-container > #urlbar {
+  -moz-transition: margin-left 150ms ease-out,
+                   margin-right 150ms ease-out;
+}
+
+      window:not([chromehidden~=toolbar]) :-moz-any(#nav-bar[currentset*="unified-back-forward-button,urlbar-container"][mode=icons],                #nav-bar:not([currentset])[mode=icons])                 > #unified-back-forward-button[forwarddisabled] + #urlbar-container > #urlbar:-moz-locale-dir(ltr) {
+  margin-left: -32px;
+}
+      window:not([chromehidden~=toolbar]) :-moz-any(#nav-bar[currentset*="unified-back-forward-button,urlbar-container"][mode=icons],                #nav-bar:not([currentset])[mode=icons])                 > #unified-back-forward-button[forwarddisabled] + #urlbar-container > #urlbar:-moz-locale-dir(rtl) {
+  margin-right: -32px;
+}
+window:not([chromehidden~=toolbar]) :-moz-any(#nav-bar[currentset*="unified-back-forward-button,urlbar-container"][mode=icons][iconsize=small],#nav-bar:not([currentset])[mode=icons][iconsize=small]) > #unified-back-forward-button[forwarddisabled] + #urlbar-container > #urlbar:-moz-locale-dir(ltr) {
+  margin-left: -24px;
+}
+window:not([chromehidden~=toolbar]) :-moz-any(#nav-bar[currentset*="unified-back-forward-button,urlbar-container"][mode=icons][iconsize=small],#nav-bar:not([currentset])[mode=icons][iconsize=small]) > #unified-back-forward-button[forwarddisabled] + #urlbar-container > #urlbar:-moz-locale-dir(rtl) {
+  margin-right: -24px;
+}
+
+#urlbar-icons {
+  -moz-box-align: center;
+}
+
+.urlbar-icon {
+  cursor: pointer;
+  padding: 0 3px;
+}
+
+#urlbar-search-splitter {
+  -moz-appearance: none;
+  width: 8px;
+  -moz-margin-start: -4px;
+}
+
+#urlbar-search-splitter + #urlbar-container > #urlbar ,
+#urlbar-search-splitter + #search-container > #searchbar > .searchbar-textbox {
+  -moz-margin-start: 0;
+}
+
+#urlbar-display-box {
+  margin-top: -1px;
+  margin-bottom: -1px;
+  -moz-border-end: 1px solid #AAA;
+  -moz-margin-end: 3px;
+}
+
+#urlbar-display {
+  margin-top: 0;
+  margin-bottom: 0;
+  -moz-margin-start: 0;
+  color: GrayText;
+}
+
+/* Favicon */
+#page-proxy-favicon {
+  width: 16px;
+  height: 16px;
+  margin-top: 2px;
+  margin-bottom: 2px;
+  -moz-margin-start: 4px;
+  -moz-margin-end: 3px;
+  list-style-image: url(chrome://browser/skin/identity-icons-generic.png);
+  -moz-image-region: rect(0, 16px, 16px, 0);
+}
+
+.verifiedDomain > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon[pageproxystate="valid"] {
+  list-style-image: url(chrome://browser/skin/identity-icons-https.png);
+}
+
+.verifiedIdentity > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon[pageproxystate="valid"] {
+  list-style-image: url(chrome://browser/skin/identity-icons-https-ev.png);
+}
+
+#identity-box:hover > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon {
+  -moz-image-region: rect(0, 32px, 16px, 16px);
+}
+
+#identity-box:hover:active > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon,
+#identity-box[open=true] > #identity-box-inner > #page-proxy-stack > #page-proxy-favicon {
+  -moz-image-region: rect(0, 48px, 16px, 32px);
+}
+
+#page-proxy-favicon[pageproxystate="invalid"] {
+  opacity: 0.3;
+}
+
+/* Identity indicator */
+#identity-box {
+  padding: 1px;
+  margin: -1px;
+  -moz-margin-end: 0;
+  font-size: .9em;
+}
+
+#identity-box:-moz-locale-dir(ltr) {
+  border-top-left-radius: 2.5px;
+  border-bottom-left-radius: 2.5px;
+}
+
+#identity-box:-moz-locale-dir(rtl) {
+  border-top-right-radius: 2.5px;
+  border-bottom-right-radius: 2.5px;
+}
+
+#identity-box:-moz-focusring {
+  outline: 1px dotted #000;
+  outline-offset: -3px;
+}
+
+#identity-icon-labels {
+  -moz-padding-start: 2px;
+  -moz-padding-end: 5px;
+}
+
+#urlbar[pageproxystate="valid"] > #identity-box.verifiedIdentity {
+  background-color: #fff;
+  color: hsl(92,100%,30%);
+  -moz-margin-end: 4px;
+  background-image: -moz-linear-gradient(hsla(92,81%,16%,0),
+                                         hsla(92,81%,16%,.2) 25%,
+                                         hsla(92,81%,16%,.2) 75%,
+                                         hsla(92,81%,16%,0));
+  background-position: right;
+  background-size: 1px;
+  background-repeat: no-repeat;
+}
+
+#identity-box.verifiedIdentity:-moz-locale-dir(rtl) {
+  background-position: left;
+}
+
+/* Identity popup icons */
+#identity-popup-icon {
+  height: 64px;
+  width: 64px;
+  padding: 0;
+  list-style-image: url("chrome://browser/skin/identity.png");
+  -moz-image-region: rect(0px, 64px, 64px, 0px);
+}
+
+#identity-popup.verifiedDomain > #identity-popup-container > #identity-popup-icon {
+  -moz-image-region: rect(64px, 64px, 128px, 0px);
+}
+
+#identity-popup.verifiedIdentity > #identity-popup-container > #identity-popup-icon {
+  -moz-image-region: rect(128px, 64px, 192px, 0px);
+}
+
+/* Identity popup body text */
+.identity-popup-description {
+  white-space: pre-wrap;
+  -moz-padding-start: 15px;
+  margin: 2px 0 4px;
+}
+
+.identity-popup-label {
+  white-space: pre-wrap;
+  -moz-padding-start: 15px;
+  margin: 0;
+}
+
+#identity-popup-content-host ,
+#identity-popup-content-owner {
+  font-weight: bold;
+  max-width: 300px;
+}
+
+#identity-popup-content-host ,
+#identity-popup-content-box.verifiedIdentity > #identity-popup-content-owner {
+  font-size: 140%;
+}
+
+#identity-popup-content-owner {
+  margin-bottom: 0 !important;
+}
+
+#identity-popup-content-verifier {
+  margin: 4px 0 2px;
+}
+
+#identity-popup-content-box.verifiedIdentity > #identity-popup-encryption ,
+#identity-popup-content-box.verifiedDomain > #identity-popup-encryption {
+  margin-top: 10px;
+  -moz-margin-start: -18px;
+}
+
+#identity-popup-content-box.verifiedIdentity > #identity-popup-encryption > vbox > #identity-popup-encryption-icon ,
+#identity-popup-content-box.verifiedDomain > #identity-popup-encryption > vbox > #identity-popup-encryption-icon {
+  list-style-image: url("chrome://browser/skin/Secure.png");
+}
+
+/* Identity popup bounding box */
+#identity-popup-container {
+  min-width: 280px;
+}
+
+/* Notification popup */
+#notification-popup {
+  min-width: 280px;
+}
+
+.popup-notification-icon {
+  width: 64px;
+  height: 64px;
+  -moz-margin-end: 10px;
+}
+
+.popup-notification-icon[popupid="geolocation"] {
+  list-style-image: url(chrome://browser/skin/Geolocation-64.png);
+}
+
+.popup-notification-icon[popupid="xpinstall-disabled"],
+.popup-notification-icon[popupid="addon-progress"],
+.popup-notification-icon[popupid="addon-install-cancelled"],
+.popup-notification-icon[popupid="addon-install-blocked"],
+.popup-notification-icon[popupid="addon-install-failed"],
+.popup-notification-icon[popupid="addon-install-complete"] {
+  list-style-image: url(chrome://mozapps/skin/extensions/extensionGeneric.png);
+  width: 32px;
+  height: 32px;
+}
+
+.popup-notification-icon[popupid="click-to-play-plugins"] {
+  list-style-image: url(chrome://mozapps/skin/plugins/pluginGeneric.png);
+  width: 32px;
+  height: 32px;
+}
+
+.addon-progress-description {
+  width: 350px;
+  max-width: 350px;
+}
+
+.popup-progress-label,
+.popup-progress-meter {
+  -moz-margin-start: 0;
+  -moz-margin-end: 0;
+}
+
+.popup-progress-cancel {
+  -moz-appearance: none;
+  background: transparent;
+  border: none;
+  padding: 0;
+  margin: 0;
+  -moz-margin-start: 5px;
+  min-height: 0;
+  min-width: 0;
+  list-style-image: url("moz-icon://stock/gtk-cancel?size=menu");
+}
+
+.popup-notification-icon[popupid="indexedDB-permissions-prompt"],
+.popup-notification-icon[popupid="indexedDB-quota-prompt"] {
+  list-style-image: url(chrome://global/skin/icons/question-64.png);
+}
+
+.popup-notification-icon[popupid="password-save"],
+.popup-notification-icon[popupid="password-change"] {
+  list-style-image: url(chrome://mozapps/skin/passwordmgr/key-64.png);
+}
+
+.popup-notification-icon[popupid="webapps-install"] {
+  list-style-image: url(chrome://browser/skin/webapps-64.png);
+}
+
+/* Notification icon box */
+#notification-popup-box {
+  position: relative;
+  background-color: #fff;
+  background-clip: padding-box;
+  padding-left: 4px;
+  border-radius: 2.5px 0 0 2.5px;
+  border-width: 0 8px 0 0;
+  border-style: solid;
+  -moz-border-image: url("chrome://browser/skin/urlbar-arrow.png") 0 8 0 0 fill;
+  -moz-margin-end: -8px;
+  margin-top: -1px;
+  margin-bottom: -1px;
+}
+
+#notification-popup-box:not([hidden]) + #identity-box {
+  -moz-padding-start: 10px;
+  border-radius: 0;
+}
+
+#notification-popup-box:-moz-locale-dir(rtl),
+.notification-anchor-icon:-moz-locale-dir(rtl) {
+  -moz-transform: scaleX(-1);
+}
+
+.notification-anchor-icon {
+  width: 16px;
+  height: 16px;
+  margin: 0 2px;
+}
+
+.notification-anchor-icon:-moz-focusring {
+  outline: 1px dotted -moz-DialogText;
+}
+
+#default-notification-icon {
+  list-style-image: url(chrome://global/skin/icons/information-16.png);
+}
+
+#geo-notification-icon {
+  list-style-image: url(chrome://browser/skin/Geolocation-16.png);
+}
+
+#addons-notification-icon {
+  list-style-image: url(chrome://mozapps/skin/extensions/extensionGeneric-16.png);
+}
+
+#indexedDB-notification-icon {
+  list-style-image: url(chrome://global/skin/icons/question-16.png);
+}
+
+#password-notification-icon {
+  list-style-image: url(chrome://mozapps/skin/passwordmgr/key-16.png);
+}
+
+#webapps-notification-icon {
+  list-style-image: url(chrome://browser/skin/webapps-16.png);
+}
+
+#plugins-notification-icon {
+  list-style-image: url(chrome://mozapps/skin/plugins/pluginGeneric-16.png);
+}
+
+#treecolAutoCompleteImage {
+  max-width : 36px;
+}
+
+.ac-result-type-bookmark,
+.autocomplete-treebody::-moz-tree-image(bookmark, treecolAutoCompleteImage) {
+  list-style-image: url("chrome://browser/skin/places/pageStarred.png");
+  width: 16px;
+  height: 16px;
+}
+
+.ac-result-type-keyword,
+.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage) {
+  list-style-image: url(moz-icon://stock/gtk-find?size=menu);
+  width: 16px;
+  height: 16px;
+}
+
+.ac-result-type-tag,
+.autocomplete-treebody::-moz-tree-image(tag, treecolAutoCompleteImage) {
+  list-style-image: url("chrome://browser/skin/places/tag.png");
+  width: 16px;
+  height: 16px;
+}
+
+.ac-comment {
+  font-size: 1.15em;
+}
+
+.ac-extra > .ac-comment {
+  font-size: inherit;
+}
+
+.ac-url-text,
+.ac-action-text {
+  color: -moz-nativehyperlinktext;
+}
+
+richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-icon {
+  list-style-image: url("chrome://browser/skin/actionicon-tab.png");
+  padding: 0 3px;
+}
+
+.autocomplete-treebody::-moz-tree-cell-text(treecolAutoCompleteComment) {
+  color: GrayText;
+}
+
+.ac-comment[selected="true"],
+.ac-url-text[selected="true"],
+.ac-action-text[selected="true"] {
+  color: inherit !important;
+}
+
+.autocomplete-treebody::-moz-tree-cell-text(suggesthint, treecolAutoCompleteComment),
+.autocomplete-treebody::-moz-tree-cell-text(suggestfirst, treecolAutoCompleteComment) {
+  color: GrayText;
+  font-size: smaller;
+}
+
+.autocomplete-treebody::-moz-tree-cell(suggesthint) {
+  border-top: 1px solid GrayText;
+}
+
+/* Combined go/reload/stop button in location bar */
+
+#go-button {
+  padding-top: 2px;
+  padding-bottom: 2px;
+}
+
+#urlbar > toolbarbutton {
+  -moz-appearance: none;
+  padding: 0;
+  border: none;
+  cursor: pointer;
+  width: 22px;
+}
+
+#go-button,
+#urlbar-go-button {
+  list-style-image: url("chrome://browser/skin/Go-arrow.png");
+}
+
+#go-button:-moz-locale-dir(rtl) > .toolbarbutton-icon,
+#urlbar-go-button:-moz-locale-dir(rtl) > .toolbarbutton-icon {
+  -moz-transform: scaleX(-1);
+}
+
+#urlbar-reload-button {
+  list-style-image: url("moz-icon://stock/gtk-refresh?size=menu");
+}
+
+#urlbar-stop-button {
+  list-style-image: url("moz-icon://stock/gtk-stop?size=menu");
+}
+
+/* Popup blocker button */
+#page-report-button {
+  list-style-image: url("chrome://browser/skin/Info.png");
+}
+
+/* Star button */
+#star-button {
+  padding: 1px;
+  list-style-image: url("chrome://browser/skin/places/starPage.png");
+}
+
+#star-button[starred="true"] {
+  list-style-image: url("chrome://browser/skin/places/pageStarred.png");
+}
+
+/* Bookmarking panel */
+#editBookmarkPanelStarIcon {
+  list-style-image: url("chrome://browser/skin/places/starred48.png");
+  width: 48px;
+  height: 48px;
+}
+
+#editBookmarkPanelStarIcon[unstarred] {
+  list-style-image: url("chrome://browser/skin/places/unstarred48.png");
+}
+
+#editBookmarkPanelTitle {
+  font-size: 130%;
+}
+
+#editBookmarkPanelHeader,
+#editBookmarkPanelContent {
+  margin-bottom: .5em;
+}
+
+/* Implements editBookmarkPanel resizing on folderTree un-collapse. */
+#editBMPanel_folderTree {
+  min-width: 27em;
+}
+
+.panel-promo-box {
+  margin: 8px -10px -10px -10px;
+  padding: 8px 10px;
+  border-top: 1px solid ThreeDShadow;
+  background-image: -moz-linear-gradient(hsla(0,0%,0%,.15), hsla(0,0%,0%,.08) 6px);
+}
+
+.panel-promo-icon {
+  list-style-image: url("chrome://browser/skin/sync-notification-24.png");
+  -moz-margin-end: 10px;
+  vertical-align: middle;
+}
+
+.panel-promo-closebutton {
+  list-style-image: url("moz-icon://stock/gtk-close?size=menu");
+  margin-top: 0;
+  margin-bottom: 0;
+}
+
+.panel-promo-closebutton > .toolbarbutton-text {
+  padding: 0;
+  margin: 0;
+}
+
+/* Content area */
+#sidebar {
+  background-color: Window;
+}
+
+/* Throbber */
+#navigator-throbber {
+  width: 16px;
+  min-height: 16px;
+  margin: 0 3px;
+}
+
+#navigator-throbber[busy="true"] {
+  list-style-image: url("chrome://global/skin/icons/loading_16.png");
+}
+
+#navigator-throbber,
+#wrapper-navigator-throbber > #navigator-throbber {
+  list-style-image: url("chrome://global/skin/icons/notloading_16.png");
+}
+
+/* Tabstrip */
+
+#TabsToolbar {
+  min-height: 0;
+  padding: 0;
+}
+
+#TabsToolbar[tabsontop=true]:not(:-moz-lwtheme) {
+  -moz-appearance: menubar;
+  color: -moz-menubartext;
+  box-shadow: 0 -1px 0 rgba(0,0,0,.1) inset;
+}
+
+#TabsToolbar[tabsontop=true]:not(:-moz-lwtheme):-moz-system-metric(menubar-drag) {
+  -moz-binding: url("chrome://global/content/bindings/toolbar.xml#toolbar-drag");
+}
+
+#TabsToolbar[tabsontop=false] {
+  background-image:
+    -moz-linear-gradient(bottom, rgba(0,0,0,.3) 1px, rgba(0,0,0,.05) 1px, transparent 50%);
+}
+
+.tabbrowser-tab,
+.tabs-newtab-button {
+  position: static;
+  -moz-appearance: none;
+  background: -moz-linear-gradient(hsla(0,0%,100%,.2), hsla(0,0%,45%,.2) 2px, hsla(0,0%,32%,.2) 80%);
+  background-origin: border-box;
+  background-position: 1px 2px;
+  background-size: 100% -moz-calc(100% - 2px);
+  background-repeat: no-repeat;
+  color: inherit;
+  margin: 0;
+  padding: 0;
+  border-width: 4px 5px 3px 6px;
+  border-style: solid;
+  -moz-border-image: url(tabbrowser/tab.png) 4 5 3 6 fill repeat stretch;
+  border-radius: 10px 8px 0 0;
+  min-height: 25px; /* reserve space for the sometimes hidden close button */
+}
+
+.tabbrowser-tab:hover,
+.tabs-newtab-button:hover {
+  background-image: -moz-linear-gradient(hsla(0,0%,100%,.6), hsla(0,0%,100%,.2) 4px, hsla(0,0%,75%,.2) 80%);
+}
+
+.tabbrowser-tab[selected="true"] {
+  background-image: -moz-linear-gradient(rgba(255,255,255,.8) 1px, rgba(255,255,255,.5) 3px, rgba(255,255,255,.3) 32%),
+                    -moz-linear-gradient(-moz-dialog, -moz-dialog);
+  color: -moz-dialogtext;
+}
+
+#main-window[tabsontop=false]:not([disablechrome]) .tabbrowser-tab[selected=true]:not(:-moz-lwtheme) {
+  background-image: -moz-linear-gradient(bottom, rgba(0,0,0,.3) 1px, transparent 1px),
+                    -moz-linear-gradient(rgba(255,255,255,.8) 1px, rgba(255,255,255,.5) 3px, rgba(255,255,255,.3) 32%),
+                    -moz-linear-gradient(-moz-dialog, -moz-dialog);
+}
+
+.tabbrowser-tab[selected="true"]:-moz-lwtheme {
+  background-image: -moz-linear-gradient(rgba(255,255,255,.8) 1px, rgba(255,255,255,.5) 3px, rgba(255,255,255,.3) 32%);
+  color: inherit;
+}
+
+.tabbrowser-tab:-moz-lwtheme-brighttext:not([selected="true"]),
+.tabs-newtab-button:-moz-lwtheme-brighttext {
+  background-image: -moz-linear-gradient(hsla(0,0%,60%,.6), hsla(0,0%,40%,.6) 4px, hsla(0,0%,30%,.6) 80%);
+}
+
+.tabbrowser-tab:-moz-lwtheme-brighttext:not([selected="true"]):hover,
+.tabs-newtab-button:-moz-lwtheme-brighttext:hover {
+  background-image: -moz-linear-gradient(hsla(0,0%,80%,.6), hsla(0,0%,60%,.6) 4px, hsla(0,0%,45%,.6) 80%);
+}
+
+.tabbrowser-tab:-moz-lwtheme-darktext:not([selected="true"]),
+.tabs-newtab-button:-moz-lwtheme-darktext {
+  background-image: -moz-linear-gradient(hsla(0,0%,100%,.5), hsla(0,0%,60%,.5) 4px, hsla(0,0%,45%,.5) 80%);
+}
+
+.tabbrowser-tab:-moz-lwtheme-darktext:not([selected="true"]):hover,
+.tabs-newtab-button:-moz-lwtheme-darktext:hover {
+  background-image: -moz-linear-gradient(hsla(0,0%,100%,.5), hsla(0,0%,80%,.5) 4px, hsla(0,0%,60%,.5) 80%);
+}
+
+.tabbrowser-tab[pinned][titlechanged]:not([selected="true"]) {
+  background-image: -moz-radial-gradient(center 3px, circle cover, rgba(233,242,252,1) 3%, rgba(172,206,255,.75) 40%, rgba(87,151,201,.5) 80%, rgba(87,151,201,0));
+}
+.tabbrowser-tab[pinned][titlechanged]:not([selected="true"]):hover {
+  background-image: -moz-linear-gradient(hsla(0,0%,100%,.8), hsla(0,0%,100%,.6) 2px, hsla(0,0%,75%,.2) 80%),
+                    -moz-radial-gradient(center 3px, circle cover, rgba(233,242,252,1) 3%, rgba(172,206,255,.75) 40%, rgba(87,151,201,.5) 80%, rgba(87,151,201,0));
+}
+
+#tabbrowser-tabs[positionpinnedtabs] > .tabbrowser-tab[pinned] {
+  min-height: 18px; /* corresponds to the max. height of non-textual tab contents, i.e. the tab close button */
+}
+
+.tabbrowser-tab:focus > .tab-stack {
+  outline: 1px dotted;
+}
+
+.tab-throbber,
+.tab-icon-image {
+  width: 16px;
+  height: 16px;
+  -moz-margin-end: 3px;
+  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
+}
+
+.tab-throbber {
+  list-style-image: url("chrome://browser/skin/tabbrowser/connecting.png");
+}
+
+.tab-throbber[progress] {
+  list-style-image: url("chrome://browser/skin/tabbrowser/loading.png");
+}
+
+.tab-throbber[pinned],
+.tab-icon-image[pinned],
+.tabs-newtab-button > .toolbarbutton-icon {
+  -moz-margin-start: 2px;
+  -moz-margin-end: 2px;
+}
+
+#context_reloadTab {
+  list-style-image: url("moz-icon://stock/gtk-refresh?size=menu");
+}
+
+#context_closeOtherTabs {
+  list-style-image: url("moz-icon://stock/gtk-clear?size=menu");
+}
+
+#context_closeOtherTabs[disabled] {
+  list-style-image: url("moz-icon://stock/gtk-clear?size=menu&state=disabled");
+}
+
+#context_undoCloseTab {
+  list-style-image: url("moz-icon://stock/gtk-undelete?size=menu");
+}
+
+#context_closeTab {
+  list-style-image: url("moz-icon://stock/gtk-close?size=menu");
+}
+
+/* Tab drag and drop */
+.tab-drop-indicator {
+  list-style-image: url(chrome://browser/skin/tabbrowser/tabDragIndicator.png);
+  margin-bottom: -11px;
+}
+
+/* In-tab close button */
+.tab-close-button > .toolbarbutton-icon {
+  /* XXX Buttons have padding in widget/ that we don't want here but can't override with good CSS, so we must
+     use evil CSS to give the impression of smaller content */
+  margin: -4px;
+}
+
+.tab-close-button {
+  padding: 0;
+  list-style-image: url("moz-icon://stock/gtk-close?size=menu");
+  margin-top: -1px;
+  margin-bottom: -1px;
+  -moz-margin-end: -1px;
+}
+
+/* Tabstrip new tab button */
+.tabs-newtab-button,
+#TabsToolbar > #new-tab-button ,
+#TabsToolbar > #wrapper-new-tab-button > #new-tab-button {
+  list-style-image: url("moz-icon://stock/gtk-add?size=menu");
+  -moz-image-region: auto;
+}
+
+/* Tabstrip close button */
+.tabs-closebutton {
+  list-style-image: url("moz-icon://stock/gtk-close?size=menu");
+}
+
+.tabs-closebutton > .toolbarbutton-icon {
+  /* XXX Buttons have padding in widget/ that we don't want here but can't override with good CSS, so we must
+     use evil CSS to give the impression of smaller content */
+  margin: -2px;
+}
+
+/* Tabbrowser arrowscrollbox arrows */
+.tabbrowser-arrowscrollbox > .scrollbutton-up,
+.tabbrowser-arrowscrollbox > .scrollbutton-down {
+  -moz-appearance: none;
+  margin: 0;
+}
+
+.tabbrowser-arrowscrollbox > .scrollbutton-up {
+  -moz-border-start: 0;
+  -moz-border-end: 2px solid transparent;
+}
+
+.tabbrowser-arrowscrollbox > .scrollbutton-down {
+  -moz-border-start: 2px solid transparent;
+  -moz-border-end: 0;
+  -moz-transition: 1s box-shadow ease-out;
+  border-radius: 4px;
+}
+
+.tabbrowser-arrowscrollbox > .scrollbutton-down[notifybgtab] {
+  box-shadow: 0 0 5px 5px Highlight inset;
+  -moz-transition: none;
+}
+
+.tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):-moz-locale-dir(ltr),
+.tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]):-moz-locale-dir(rtl) {
+  border-width: 0 2px 0 0;
+  border-style: solid;
+  -moz-border-image: url("chrome://browser/skin/tabbrowser/tab-overflow-border.png") 0 2 0 2 fill;
+}
+
+.tabbrowser-arrowscrollbox > .scrollbutton-down:not([disabled]):-moz-locale-dir(ltr),
+.tabbrowser-arrowscrollbox > .scrollbutton-up:not([disabled]):-moz-locale-dir(rtl) {
+  border-width: 0 0 0 2px;
+  border-style: solid;
+  -moz-border-image: url("chrome://browser/skin/tabbrowser/tab-overflow-border.png") 0 2 0 2 fill;
+}
+
+#TabsToolbar .toolbarbutton-1 > .toolbarbutton-icon,
+#TabsToolbar .toolbarbutton-1 > .toolbarbutton-menu-dropmarker,
+#TabsToolbar .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
+  margin-top: -2px;
+  margin-bottom: -2px;
+}
+
+#alltabs-button > .toolbarbutton-icon {
+  list-style-image: url("chrome://browser/skin/tabbrowser/alltabs.png");
+  margin: 2px 0 1px;
+}
+
+#alltabs-button[type="menu"] > .toolbarbutton-menu-dropmarker {
+  margin-bottom: -2px;
+}
+
+#alltabs-button[type="menu"] > .toolbarbutton-icon {
+  display: none;
+}
+
+/* All tabs menupopup */
+.alltabs-item > .menu-iconic-left > .menu-iconic-icon {
+  list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
+}
+
+.alltabs-item[selected="true"] {
+  font-weight: bold;
+}
+
+.alltabs-item[busy] > .menu-iconic-left > .menu-iconic-icon {
+  list-style-image: url("chrome://global/skin/icons/loading_16.png");
+}
+
+.alltabs-item[tabIsVisible] {
+  /* box-shadow instead of background-color to work around native styling */
+  box-shadow: inset -5px 0 ThreeDShadow;
+}
+
+/* Sidebar */
+#sidebar-header > .tabs-closebutton {
+  margin-bottom: 0px !important;
+  padding: 0px 2px 0px 2px !important;
+}
+
+#sidebar-throbber[loading="true"] {
+  list-style-image: url("chrome://global/skin/icons/loading_16.png");
+  -moz-margin-end: 4px;
+}
+
+toolbarbutton.chevron {
+  list-style-image: url("chrome://global/skin/toolbar/chevron.gif") !important;
+}
+
+toolbarbutton.chevron:-moz-locale-dir(rtl) > .toolbarbutton-icon {
+  -moz-transform: scaleX(-1);
+}
+
+toolbarbutton.chevron > .toolbarbutton-text,
+toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
+  display: none;
+}
+
+toolbarbutton.chevron > .toolbarbutton-icon {
+  margin: 0;
+}
+
+toolbar[mode="text"] toolbarbutton.chevron > .toolbarbutton-icon {
+  display: -moz-box; /* display chevron icon in text mode */
+}
+
+#download-monitor {
+  list-style-image: url("chrome://browser/skin/Toolbar-small.png");
+  -moz-image-region: rect(0px 16px 16px 0px);
+}
+
+
+/* ::::: Keyboard UI Panel ::::: */
+
+.KUI-panel-closebutton {
+  list-style-image: url(KUI-close.png);
+}
+
+.KUI-panel-closebutton > .toolbarbutton-icon {
+  margin: 0;
+}
+
+/* ::::: Ctrl-Tab and All Tabs Panels ::::: */
+
+.ctrlTab-preview,
+.allTabs-preview {
+  -moz-appearance: toolbarbutton;
+}
+
+.tabPreview-canvas {
+  box-shadow: 0 0 5px ThreeDShadow;
+}
+
+.ctrlTab-preview:focus .tabPreview-canvas,
+.ctrlTab-preview:hover .tabPreview-canvas,
+.allTabs-preview:focus .tabPreview-canvas,
+.allTabs-preview:hover .tabPreview-canvas {
+  box-shadow: none;
+}
+
+.ctrlTab-favicon[src],
+.allTabs-favicon[src] {
+  background-color: white;
+  width: 20px;
+  height: 20px;
+  padding: 2px;
+}
+
+/* Ctrl-Tab */
+
+#ctrlTab-panel {
+  padding: 10px;
+}
+
+.ctrlTab-preview:not(#ctrlTab-showAll) .tabPreview-canvas {
+  margin-bottom: 2px;
+}
+
+#ctrlTab-showAll {
+  -moz-appearance: button;
+  color: ButtonText;
+  padding: 0 3px;
+  margin-top: 10px;
+}
+
+/* All Tabs */
+
+#allTabs-panel {
+  padding-bottom: 10px;
+}
+
+#allTabs-meta {
+  padding: 5px;
+}
+
+#allTabs-filter {
+  -moz-margin-start: 36px;
+  -moz-margin-end: 0;
+}
+
+.allTabs-preview-label {
+  -moz-transform: translate(0, 2px);
+}
+
+/* Application button menu */
+
+.splitmenu-menuitem {
+  -moz-margin-end: 1px;
+}
+
+#appmenu-toolbar-button:not(:hover):not([open]):not(:-moz-lwtheme) {
+  color: inherit;
+}
+
+#appmenu-toolbar-button > .toolbarbutton-text,
+#appmenu-toolbar-button > .toolbarbutton-menu-dropmarker {
+  margin-top: -2px !important;
+  margin-bottom: -2px !important;
+}
+#appmenuSecondaryPane {
+  -moz-border-start: 1px solid ThreeDShadow;
+}
+#appmenuSecondaryPane-spacer {
+  min-height: 1em;
+}
+#appmenu-cut,
+#appmenu-editmenu-cut {
+  list-style-image: url("moz-icon://stock/gtk-cut?size=menu");
+}
+#appmenu-copy,
+#appmenu-editmenu-copy {
+  list-style-image: url("moz-icon://stock/gtk-copy?size=menu");
+}
+#appmenu-paste,
+#appmenu-editmenu-paste {
+  list-style-image: url("moz-icon://stock/gtk-paste?size=menu");
+}
+#wrapper-appmenu-toolbar-button,
+.appmenu-edit-button[disabled="true"] {
+  opacity: .3;
+}
+
+/* Inspector / Highlighter */
+
+#highlighter-panel {
+  -moz-appearance: none;
+  -moz-window-shadow: none;
+  background: -moz-linear-gradient(top -1deg, #ffdd88, #ffeeaa);
+  border: none;
+  opacity: 0.35;
+}
+
+listitem.style-selector {
+  background-color: DarkGray;
+  color: white;
+}
+
+listitem.style-section {
+  background-color: LightGray;
+  color: black;
+  font-weight: bold;
+}
+
+panel[dimmed="true"] {
+  opacity: 0.5;
+}
+
+/* Add-on bar */
+
+#addon-bar {
+  box-shadow: 0 1px 0 rgba(0,0,0,.15) inset;
+  padding: 0;
+  min-height: 20px;
+}
+
+#status-bar {
+  min-height: 0;
+  -moz-appearance: none;
+  background-color: transparent;
+  border: none;
+}
+
+#addon-bar[customizing] > #status-bar {
+  opacity: .5;
+  background-image: -moz-repeating-linear-gradient(-45deg,
+                                                   rgba(255,255,255,.3), rgba(255,255,255,.3) 5px,
+                                                   rgba(0,0,0,.3) 5px, rgba(0,0,0,.3) 10px);
+}
+
+#status-bar > statusbarpanel {
+  border-width: 0;
+  -moz-appearance: none;
+}
+
+#addonbar-closebutton {
+  list-style-image: url("moz-icon://stock/gtk-close?size=menu");
+}
+
+#addonbar-closebutton > .toolbarbutton-icon {
+  margin-top: -2px;
+  margin-bottom: -2px;
+}
+
+/* Status panel */
+
+.statuspanel-label {
+  margin: 0;
+  padding: 2px 4px;
+  background: -moz-linear-gradient(white, #ddd);
+  border: 1px none #ccc;
+  border-top-style: solid;
+  color: #333;
+  text-shadow: none;
+}
+
+.statuspanel-label:-moz-locale-dir(ltr):not([mirror]),
+.statuspanel-label:-moz-locale-dir(rtl)[mirror] {
+  border-right-style: solid;
+  border-top-right-radius: .3em;
+  margin-right: 1em;
+}
+
+.statuspanel-label:-moz-locale-dir(rtl):not([mirror]),
+.statuspanel-label:-moz-locale-dir(ltr)[mirror] {
+  border-left-style: solid;
+  border-top-left-radius: .3em;
+  margin-left: 1em;
+}
+
+/* Highlighter */
+
+.highlighter-veil {
+  background-color: rgba(25, 25, 25, 0.5);
+}
+
+#highlighter-veil-transparentbox {
+  box-shadow: 0 0 0 1px rgba(0,0,0,0.5);
+  outline: 1px dashed rgba(255,255,255,0.5);
+  outline-offset: -1px;
+}
+
+#highlighter-veil-container[locked] > #highlighter-veil-middlebox > #highlighter-veil-transparentbox {
+  box-shadow: 0 0 0 1px black;
+  outline-color: white;
+}
+
+/* Highlighter toolbar */
+
+#inspector-inspect-toolbutton {
+  list-style-image: url("chrome://browser/skin/devtools/inspect-button.png");
+  -moz-image-region: rect(0px 16px 16px 0px);
+}
+
+#inspector-inspect-toolbutton[checked=true] {
+  -moz-image-region: rect(0px 32px 16px 16px);
+}
+
+#inspector-option-toolbarbutton {
+  -moz-appearance: none;
+  -moz-margin-end: 6px;
+  list-style-image: url("chrome://browser/skin/devtools/inspector-option-icon.png");
+  -moz-image-region: rect(0px 16px 16px 0px);
+  border: none;
+}
+
+#inspector-option-toolbarbutton[open] {
+  -moz-image-region: rect(0px 32px 16px 16px);
+}
+
+#inspector-toolbar,
+#developer-toolbar {
+  border-top: 1px solid hsla(210, 8%, 5%, .65);
+}
+
+#devtools-side-splitter {
+  -moz-appearance: none;
+  border: 0;
+  -moz-border-start: 1px solid #242b33;
+  min-width: 0;
+  width: 3px;
+  background-color: transparent;
+  -moz-margin-end: -3px;
+  position: relative;
+}
+
+#devtools-sidebar-box {
+  background-color: -moz-Field;
+}
+
+/* Highlighter - Node Infobar */
+
+#highlighter-nodeinfobar {
+  color: hsl(200, 100%, 65%);
+  border: 1px solid hsla(210, 19%, 63%, .5);
+  border-radius: 3px;
+  background: -moz-linear-gradient(hsl(209, 18%, 30%), hsl(210, 24%, 16%)) no-repeat padding-box;
+}
+
+/* Highlighter - Node Infobar - text */
+
+#highlighter-nodeinfobar-text {
+  /* 100% - size of the buttons and margins */
+  max-width: -moz-calc(100% - 2 * (26px + 6px));
+  padding-bottom: 1px;
+}
+
+html|*#highlighter-nodeinfobar-tagname {
+  color: white;
+}
+
+html|*#highlighter-nodeinfobar-id {
+  color: hsl(90, 79%, 52%);
+}
+
+html|*#highlighter-nodeinfobar-pseudo-classes {
+  color: hsl(20, 100%, 70%);
+}
+
+/* Highlighter - Node Infobar - buttons */
+
+.highlighter-nodeinfobar-button {
+  -moz-appearance: none;
+  border: 0 solid hsla(210,8%,5%,.45);
+  padding: 0;
+  width: 26px;
+  min-height: 26px;
+}
+
+#highlighter-nodeinfobar-inspectbutton {
+  -moz-border-end-width: 1px;
+  box-shadow: 1px 0 0 hsla(210,16%,76%,.15), -1px 0 0 hsla(210,16%,76%,.15) inset;
+  -moz-margin-end: 6px;
+  list-style-image: url("chrome://browser/skin/devtools/inspect-button.png");
+  -moz-image-region: rect(0px 16px 16px 0px);
+}
+
+#highlighter-nodeinfobar-inspectbutton:-moz-locale-dir(rtl) {
+  box-shadow: -1px 0 0 hsla(210,16%,76%,.15), 1px 0 0 hsla(210,16%,76%,.15) inset;
+}
+
+#highlighter-nodeinfobar-inspectbutton:active:hover,
+#highlighter-nodeinfobar-container:not([locked]) >  #highlighter-nodeinfobar >  #highlighter-nodeinfobar-inspectbutton {
+  -moz-image-region: rect(0px 32px 16px 16px);
+}
+
+#highlighter-nodeinfobar-menu {
+  -moz-border-start-width: 1px;
+  box-shadow: -1px 0 0 hsla(210,16%,76%,.15), 1px 0 0 hsla(210,16%,76%,.15) inset;
+  -moz-margin-start: 6px;
+}
+
+#highlighter-nodeinfobar-menu:-moz-locale-dir(rtl) {
+  box-shadow: 1px 0 0 hsla(210,16%,76%,.15), -1px 0 0 hsla(210,16%,76%,.15) inset;
+}
+
+#highlighter-nodeinfobar-menu > .toolbarbutton-menu-dropmarker {
+  -moz-appearance: none !important;
+  list-style-image: url("chrome://browser/skin/devtools/dropmarker.png");
+  -moz-box-align: center;
+  -moz-margin-start: -1px;
+}
+
+/* Highlighter - Node Infobar - box & arrow */
+
+.highlighter-nodeinfobar-arrow {
+  width: 14px;
+  height: 14px;
+  -moz-margin-start: -moz-calc(50% - 7px);
+  -moz-transform: rotate(-45deg);
+  border: 1px solid transparent;
+  background-clip: padding-box;
+  background-repeat: no-repeat;
+}
+
+#highlighter-nodeinfobar-arrow-top {
+  margin-bottom: -8px;
+  margin-top: 8px;
+  border-right-color: hsla(210, 19%, 63%, .5);
+  border-top-color: hsla(210, 19%, 63%, .5);
+  background-image: -moz-linear-gradient(bottom left, transparent 50%, hsl(209, 18%, 30%) 50%);
+}
+
+#highlighter-nodeinfobar-arrow-bottom {
+  margin-top: -8px;
+  margin-bottom: 8px;
+  border-left-color: hsla(210, 19%, 63%, .5);
+  border-bottom-color: hsla(210, 19%, 63%, .5);
+  background-image: -moz-linear-gradient(top right, transparent 50%, hsl(210, 24%, 16%) 50%);
+}
+
+#highlighter-nodeinfobar-container[position="top"] > #highlighter-nodeinfobar,
+#highlighter-nodeinfobar-container[position="overlap"] > #highlighter-nodeinfobar {
+  box-shadow: 0 1px 0 hsla(0, 0%, 100%, .1) inset;
+}
+
+#highlighter-nodeinfobar-container[hide-arrow] > #highlighter-nodeinfobar {
+  margin: 7px 0;
+}
+
+#full-screen-warning-message {
+  background-color: hsl(0,0%,15%);
+  color: white;
+  border-radius: 8px;
+  margin-top: 30px;
+  padding: 30px 50px;
+  box-shadow: 0 0 2px white;
+}
+
+#full-screen-warning-container[obscure-browser] {
+  background-color: rgba(0,0,0,0.3);
+}
+
+.full-screen-description {
+  font-size: 150%;
+}
+
+#full-screen-domain-text {
+  font-size: 300%;
+}
+
+.full-screen-approval-button,
+#full-screen-remember-decision {
+  font-size: 120%;
+}
+
+/* Highlighter toolbar - breadcrumbs */
+
+#inspector-breadcrumbs {
+  -moz-margin-end: 3px;
+  /* A fake 1px-shadow is included in the border-images of the
+     inspector-breadcrumbs-buttons, to match toolbar-buttons style.
+     This negative margin compensate the extra row of pixels created
+     by the shadow.*/
+  margin-bottom: -1px;
+}
+
+#inspector-breadcrumbs > .scrollbutton-up,
+#inspector-breadcrumbs > .scrollbutton-down {
+  -moz-appearance: none;
+  border: 1px solid hsla(210,8%,5%,.45);
+  background: -moz-linear-gradient(hsla(212,7%,57%,.35), hsla(212,7%,57%,.1)) padding-box;
+  box-shadow: 0 1px 0 hsla(210,16%,76%,.15) inset, 0 0 0 1px hsla(210,16%,76%,.15) inset, 0 1px 0 hsla(210,16%,76%,.15);
+  margin: 0 0 1px;
+}
+
+#inspector-breadcrumbs > .scrollbutton-up {
+  -moz-border-start-width: 0;
+}
+
+#inspector-breadcrumbs > .scrollbutton-up:not([disabled]):active:hover,
+#inspector-breadcrumbs > .scrollbutton-down:not([disabled]):active:hover {
+  border-color: hsla(210,8%,5%,.6);
+  background: -moz-linear-gradient(hsla(220,6%,10%,.3), hsla(212,7%,57%,.15) 65%, hsla(212,7%,57%,.3));
+  box-shadow: 0 0 3px hsla(210,8%,5%,.25) inset, 0 1px 3px hsla(210,8%,5%,.25) inset, 0 1px 0 hsla(210,16%,76%,.15);
+}
+
+#inspector-breadcrumbs > .scrollbutton-up:not([disabled]):active:hover > .toolbarbutton-icon,
+#inspector-breadcrumbs > .scrollbutton-down:not([disabled]):active:hover > .toolbarbutton-icon {
+  -moz-image-region: rect(0px 14px 16px 7px);
+}
+
+#inspector-breadcrumbs > .scrollbutton-up > .toolbarbutton-icon,
+#inspector-breadcrumbs > .scrollbutton-down > .toolbarbutton-icon {
+  -moz-appearance: none;
+  list-style-image: url("chrome://browser/skin/devtools/breadcrumbs-scrollbutton.png");
+  -moz-image-region: rect(0px 7px 16px 0px);
+  margin: 0 5px;
+}
+
+#inspector-breadcrumbs > .scrollbutton-up[disabled] > .toolbarbutton-icon,
+#inspector-breadcrumbs > .scrollbutton-down[disabled] > .toolbarbutton-icon {
+  opacity: 0.5;
+}
+
+#inspector-breadcrumbs > .scrollbutton-up > .toolbarbutton-icon:-moz-locale-dir(rtl),
+#inspector-breadcrumbs > .scrollbutton-down > .toolbarbutton-icon:-moz-locale-dir(ltr) {
+  -moz-transform: scaleX(-1);
+}
+
+.inspector-breadcrumbs-button {
+  -moz-appearance: none;
+  background-color: transparent;
+  border-style: solid;
+  border-width: 1px 13px 2px 13px;
+  color: hsl(210,30%,85%);
+  width: 85px; /* Can't use max-width. See bug 723132 */
+  /* The content of the button can be larger than the button */
+  overflow: hidden;
+  min-height: 25px;
+
+  margin: 0 -11px 0 0;
+  padding: 0 9px;
+}
+
+.inspector-breadcrumbs-button:-moz-focusring > label {
+  border-bottom: 1px dotted hsla(210,30%,85%,0.4);
+}
+
+.inspector-breadcrumbs-button[checked] > .inspector-breadcrumbs-tag {
+  color: hsl(208,100%,60%);
+}
+
+.inspector-breadcrumbs-button[checked] > .inspector-breadcrumbs-id {
+  color: hsl(205,100%,70%);
+}
+
+.inspector-breadcrumbs-button[checked] > .inspector-breadcrumbs-pseudo-classes {
+  color: hsl(20, 100%, 70%);
+}
+
+.inspector-breadcrumbs-id,
+.inspector-breadcrumbs-classes {
+  color: #8d99a6;
+}
+
+.inspector-breadcrumbs-pseudo-classes {
+  color: hsl(20, 100%, 85%);
+}
+
+/* Highlighter toolbar - breadcrumbs - LTR */
+
+.inspector-breadcrumbs-button:-moz-locale-dir(ltr):first-of-type {
+  margin-left: 0;
+  border-left-width: 0;
+}
+
+.inspector-breadcrumbs-button {
+  -moz-border-image: url("chrome://browser/skin/devtools/breadcrumbs/ltr-middle.png") 1 13 2 13 fill stretch;
+}
+
+.inspector-breadcrumbs-button[siblings-menu-open]:not([checked]),
+.inspector-breadcrumbs-button:not([checked]):hover:active {
+  -moz-border-image: url("chrome://browser/skin/devtools/breadcrumbs/ltr-middle-pressed.png") 1 13 2 13 fill stretch;
+}
+
+.inspector-breadcrumbs-button[checked] {
+  -moz-border-image: url("chrome://browser/skin/devtools/breadcrumbs/ltr-middle-selected.png") 1 13 2 13 fill stretch;
+}
+
+.inspector-breadcrumbs-button[checked][siblings-menu-open],
+.inspector-breadcrumbs-button[checked]:hover:active {
+  -moz-border-image: url("chrome://browser/skin/devtools/breadcrumbs/ltr-middle-selected-pressed.png") 1 13 2 13 fill stretch;
+}
+
+.inspector-breadcrumbs-button:first-of-type {
+  -moz-border-image: url("chrome://browser/skin/devtools/breadcrumbs/ltr-start.png") 1 13 2 13 fill stretch;
+}
+
+.inspector-breadcrumbs-button[siblings-menu-open]:first-of-type:not([checked]),
+.inspector-breadcrumbs-button:first-of-type:not([checked]):hover:active {
+  -moz-border-image: url("chrome://browser/skin/devtools/breadcrumbs/ltr-start-pressed.png") 1 13 2 13 fill stretch;
+}
+
+.inspector-breadcrumbs-button:first-of-type[checked] {
+  -moz-border-image: url("chrome://browser/skin/devtools/breadcrumbs/ltr-start-selected.png") 1 13 2 13 fill stretch;
+}
+
+.inspector-breadcrumbs-button[siblings-menu-open]:first-of-type[checked],
+.inspector-breadcrumbs-button:first-of-type[checked]:hover:active {
+  -moz-border-image: url("chrome://browser/skin/devtools/breadcrumbs/ltr-start-selected-pressed.png") 1 13 2 13 fill stretch;
+}
+
+.inspector-breadcrumbs-button:last-of-type {
+  -moz-border-image: url("chrome://browser/skin/devtools/breadcrumbs/ltr-end.png") 1 13 2 13 fill stretch;
+}
+
+#inspector-breadcrumbs[overflows] > .inspector-breadcrumbs-button:-moz-locale-dir(ltr):last-of-type {
+  border-right-width: 0;
+}
+
+.inspector-breadcrumbs-button[siblings-menu-open]:last-of-type:not([checked]),
+.inspector-breadcrumbs-button:last-of-type:not([checked]):hover:active {
+  -moz-border-image: url("chrome://browser/skin/devtools/breadcrumbs/ltr-end-pressed.png") 1 13 2 13 fill stretch;
+}
+
+.inspector-breadcrumbs-button:last-of-type[checked] {
+  -moz-border-image: url("chrome://browser/skin/devtools/breadcrumbs/ltr-end-selected.png") 1 13 2 13 fill stretch;
+}
+
+.inspector-breadcrumbs-button[siblings-menu-open]:last-of-type[checked],
+.inspector-breadcrumbs-button:last-of-type[checked]:hover:active {
+  -moz-border-image: url("chrome://browser/skin/devtools/breadcrumbs/ltr-end-selected-pressed.png") 1 13 2 13 fill stretch;
+}
+
+/* Highlighter toolbar - breadcrumbs - RTL */
+
+.inspector-breadcrumbs-button:-moz-locale-dir(rtl):first-of-type {
+  margin-right: 0;
+  border-right-width: 0;
+}
+
+.inspector-breadcrumbs-button:-moz-locale-dir(rtl) {
+  -moz-border-image: url("chrome://browser/skin/devtools/breadcrumbs/rtl-middle.png") 1 13 2 13 fill stretch;
+}
+
+.inspector-breadcrumbs-button[siblings-menu-open]:not([checked]):-moz-locale-dir(rtl),
+.inspector-breadcrumbs-button:not([checked]):hover:active:-moz-locale-dir(rtl) {
+  -moz-border-image: url("chrome://browser/skin/devtools/breadcrumbs/rtl-middle-pressed.png") 1 13 2 13 fill stretch;
+}
+
+.inspector-breadcrumbs-button[checked]:-moz-locale-dir(rtl) {
+  -moz-border-image: url("chrome://browser/skin/devtools/breadcrumbs/rtl-middle-selected.png") 1 13 2 13 fill stretch;
+}
+
+.inspector-breadcrumbs-button[checked][siblings-menu-open]:-moz-locale-dir(rtl),
+.inspector-breadcrumbs-button[checked]:hover:active:-moz-locale-dir(rtl) {
+  -moz-border-image: url("chrome://browser/skin/devtools/breadcrumbs/rtl-middle-selected-pressed.png") 1 13 2 13 fill stretch;
+}
+
+.inspector-breadcrumbs-button:first-of-type:-moz-locale-dir(rtl) {
+  -moz-border-image: url("chrome://browser/skin/devtools/breadcrumbs/rtl-start.png") 1 13 2 13 fill stretch;
+}
+
+.inspector-breadcrumbs-button[siblings-menu-open]:first-of-type:not([checked]):-moz-locale-dir(rtl),
+.inspector-breadcrumbs-button:first-of-type:not([checked]):hover:active:-moz-locale-dir(rtl) {
+  -moz-border-image: url("chrome://browser/skin/devtools/breadcrumbs/rtl-start-pressed.png") 1 13 2 13 fill stretch;
+}
+
+.inspector-breadcrumbs-button:first-of-type[checked]:-moz-locale-dir(rtl) {
+  -moz-border-image: url("chrome://browser/skin/devtools/breadcrumbs/rtl-start-selected.png") 1 13 2 13 fill stretch;
+}
+
+.inspector-breadcrumbs-button[siblings-menu-open]:first-of-type[checked]:-moz-locale-dir(rtl),
+.inspector-breadcrumbs-button:first-of-type[checked]:hover:active:-moz-locale-dir(rtl) {
+  -moz-border-image: url("chrome://browser/skin/devtools/breadcrumbs/rtl-start-selected-pressed.png") 1 13 2 13 fill stretch;
+}
+
+.inspector-breadcrumbs-button:last-of-type:-moz-locale-dir(rtl) {
+  -moz-border-image: url("chrome://browser/skin/devtools/breadcrumbs/rtl-end.png") 1 13 2 13 fill stretch;
+}
+
+#inspector-breadcrumbs[overflows] > .inspector-breadcrumbs-button:last-of-type:-moz-locale-dir(rtl) {
+  border-left-width: 0;
+}
+
+.inspector-breadcrumbs-button[siblings-menu-open]:last-of-type:not([checked]):-moz-locale-dir(rtl),
+.inspector-breadcrumbs-button:last-of-type:not([checked]):hover:active:-moz-locale-dir(rtl) {
+  -moz-border-image: url("chrome://browser/skin/devtools/breadcrumbs/rtl-end-pressed.png") 1 13 2 13 fill stretch;
+}
+
+.inspector-breadcrumbs-button:last-of-type[checked]:-moz-locale-dir(rtl) {
+  -moz-border-image: url("chrome://browser/skin/devtools/breadcrumbs/rtl-end-selected.png") 1 13 2 13 fill stretch;
+}
+
+.inspector-breadcrumbs-button[siblings-menu-open]:last-of-type[checked]:-moz-locale-dir(rtl),
+.inspector-breadcrumbs-button:last-of-type[checked]:hover:active:-moz-locale-dir(rtl) {
+  -moz-border-image: url("chrome://browser/skin/devtools/breadcrumbs/rtl-end-selected-pressed.png") 1 13 2 13 fill stretch;
+}
+
+/* Highlighter toolbar - HTML Tree */
+
+#inspector-treepanel-toolbutton {
+  list-style-image: url("chrome://browser/skin/devtools/treepanel-button.png");
+  -moz-margin-end: 0;
+  -moz-image-region: rect(0px 18px 16px 0px);
+}
+
+#inspector-treepanel-toolbutton[checked] {
+  -moz-image-region: rect(0px 36px 16px 18px);
+}
+
+#inspector-breadcrumbs > .scrollbutton-down:-moz-locale-dir(rtl),
+#inspector-treepanel-toolbutton:-moz-locale-dir(ltr) {
+  border-radius: 3px 0 0 3px;
+}
+
+#inspector-breadcrumbs > .scrollbutton-down:-moz-locale-dir(ltr),
+#inspector-treepanel-toolbutton:-moz-locale-dir(rtl) {
+  border-radius: 0 3px 3px 0;
+}
+
+/* Highlight toolbar - Option menu */
+
+#inspector-option-toolbarbutton:-moz-focusring {
+  outline: 1px dotted hsla(210,30%,85%,0.4);
+  outline-offset: -2px;
+}
+
+html|*#gcli-tooltip-frame,
+html|*#gcli-output-frame {
+  padding: 0;
+  border-width: 0;
+  background-color: transparent;
+}
+
+#gcli-output,
+#gcli-tooltip {
+  border-width: 0;
+  background-color: transparent;
+  margin-bottom: -2px;
+}
+
+.gclitoolbar-input-node,
+.gclitoolbar-complete-node,
+.gclitoolbar-prompt {
+  margin: 0;
+  -moz-margin-end: 3px;
+  -moz-box-align: center;
+  padding-top: 0;
+  padding-bottom: 0;
+  padding-right: 4px;
+  border: 1px solid transparent;
+  border-radius: 3px;
+  text-shadow: none;
+}
+
+.gclitoolbar-input-node {
+  padding-left: 20px;
+  background-color: transparent;
+  -moz-appearance: none;
+  border-color: hsl(210,11%,10%);
+  color: hsl(210,30%,85%);
+  text-shadow: 0 -1px 0 hsla(210,8%,5%,.45);
+  box-shadow: 0 1px 1px hsla(210,8%,5%,.3) inset,
+              0 0 0 1px hsla(210,16%,76%,.1) inset,
+              0 1px 0 hsla(210,16%,76%,.15);
+}
+
+.gclitoolbar-complete-node {
+  padding-left: 21px;
+  background-color: transparent;
+  color: transparent;
+}
+
+.gclitoolbar-prompt {
+  padding-left: 4px;
+  padding-bottom: 2px;
+  font-size: 150%;
+  font-weight: bold;
+  color: hsl(210,30%,85%);
+  background-color: hsl(210,11%,16%);
+}
+
+.gclitoolbar-prompt-label,
+.gcli-in-incomplete,
+.gcli-in-error,
+.gcli-in-ontab,
+.gcli-in-todo,
+.gcli-in-closebrace,
+.gcli-in-param,
+.gcli-in-valid {
+  margin: 0;
+  padding: 0;
+}
+
+.gcli-in-incomplete {
+  border-bottom: 2px dotted #999;
+}
+
+.gcli-in-error {
+  border-bottom: 2px dotted #F00;
+}
+
+.gcli-in-ontab {
+  color: hsl(210,0%,35%);
+}
+
+.gcli-in-todo {
+  color: hsl(210,50%,35%);
+}
+
+.gcli-in-closebrace {
+  color: hsl(0,0%,80%);
+}
+
+/* Responsive Mode */
+
+vbox[anonid=browserContainer][responsivemode] {
+  background: #2a3643 url("chrome://browser/skin/devtools/responsive-background.png");
+  box-shadow: 0 0 7px black inset;
+  padding: 0 20px 20px 20px;
+}
+
+stack[anonid=browserStack][responsivemode] {
+  box-shadow: 0 0 7px black;
+}
+
+.devtools-responsiveui-toolbar {
+  background: transparent;
+  margin: 10px 0;
+  padding: 0;
+  box-shadow: none;
+}
+
+.devtools-responsiveui-toolbar > menulist,
+.devtools-responsiveui-toolbar > toolbarbutton {
+  min-width: 22px;
+  border-radius: 0;
+}
+
+.devtools-responsiveui-toolbar:-moz-locale-dir(ltr) > *:first-child,
+.devtools-responsiveui-toolbar:-moz-locale-dir(rtl) > *:last-child {
+  margin-left: 0;
+}
+
+.devtools-responsiveui-resizebar {
+  width: 7px;
+  height: 24px;
+  cursor: ew-resize;
+  -moz-transform: translate(12px, -12px);
+  background-image: url("chrome://browser/skin/devtools/responsive-vertical-resizer.png");
+}
+
+.devtools-responsiveui-resizehandle {
+  width: 16px;
+  height: 16px;
+  cursor: se-resize;
+  -moz-transform: translate(12px, 12px);
+  background-image: url("chrome://browser/skin/devtools/responsive-se-resizer.png");
+}
diff -Nuar omni_old/defaults/preferences/firefox.js omni/defaults/preferences/firefox.js
--- omni_old/defaults/preferences/firefox.js	2010-01-01 00:00:00.000000000 +0800
+++ omni/defaults/preferences/firefox.js	2012-08-30 14:55:11.095572971 +0800
@@ -796,6 +796,11 @@
 // 2 - pre-populate site URL and pre-fetch certificate
 pref("browser.ssl_override_behavior", 2);
 
+// 0 - do not show domain
+// 1 - show effectiveTLD + 1 (e.g. mozilla.org)
+// 2 - show full domain (e.g. bugzilla.mozilla.org)
+pref("browser.identity.ssl_domain_display", 1);
+
 // True if the user should be prompted when a web application supports
 // offline apps.
 pref("browser.offline-apps.notify", true);
diff -Nuar omni_old/defaults/preferences/firefox.js.orig omni/defaults/preferences/firefox.js.orig
--- omni_old/defaults/preferences/firefox.js.orig	1970-01-01 08:00:00.000000000 +0800
+++ omni/defaults/preferences/firefox.js.orig	2010-01-01 00:00:00.000000000 +0800
@@ -0,0 +1,1092 @@
+//@line 5 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+
+// XXX Toolkit-specific preferences should be moved into toolkit.js
+
+//@line 9 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+
+//@line 17 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+
+//@line 23 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+
+pref("browser.chromeURL","chrome://browser/content/");
+pref("browser.hiddenWindowChromeURL", "chrome://browser/content/hiddenWindow.xul");
+
+// Enables some extra Extension System Logging (can reduce performance)
+pref("extensions.logging.enabled", false);
+
+// Disables strict compatibility, making addons compatible-by-default.
+pref("extensions.strictCompatibility", false);
+
+// Specifies a minimum maxVersion an addon needs to say it's compatible with
+// for it to be compatible by default.
+pref("extensions.minCompatibleAppVersion", "4.0");
+
+// Preferences for AMO integration
+pref("extensions.getAddons.cache.enabled", true);
+pref("extensions.getAddons.maxResults", 15);
+pref("extensions.getAddons.get.url", "https://services.addons.mozilla.org/%LOCALE%/firefox/api/%API_VERSION%/search/guid:%IDS%?src=firefox&appOS=%OS%&appVersion=%VERSION%");
+pref("extensions.getAddons.getWithPerformance.url", "https://services.addons.mozilla.org/%LOCALE%/firefox/api/%API_VERSION%/search/guid:%IDS%?src=firefox&appOS=%OS%&appVersion=%VERSION%&tMain=%TIME_MAIN%&tFirstPaint=%TIME_FIRST_PAINT%&tSessionRestored=%TIME_SESSION_RESTORED%");
+pref("extensions.getAddons.search.browseURL", "https://addons.mozilla.org/%LOCALE%/firefox/search?q=%TERMS%&platform=%OS%&appver=%VERSION%");
+pref("extensions.getAddons.search.url", "https://services.addons.mozilla.org/%LOCALE%/firefox/api/%API_VERSION%/search/%TERMS%/all/%MAX_RESULTS%/%OS%/%VERSION%/%COMPATIBILITY_MODE%?src=firefox");
+pref("extensions.webservice.discoverURL", "https://services.addons.mozilla.org/%LOCALE%/firefox/discovery/pane/%VERSION%/%OS%/%COMPATIBILITY_MODE%");
+
+// Blocklist preferences
+pref("extensions.blocklist.enabled", true);
+pref("extensions.blocklist.interval", 86400);
+// Controls what level the blocklist switches from warning about items to forcibly
+// blocking them.
+pref("extensions.blocklist.level", 2);
+pref("extensions.blocklist.url", "https://addons.mozilla.org/blocklist/3/%APP_ID%/%APP_VERSION%/%PRODUCT%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/%PING_COUNT%/%TOTAL_PING_COUNT%/%DAYS_SINCE_LAST_PING%/");
+pref("extensions.blocklist.detailsURL", "https://www.mozilla.com/%LOCALE%/blocklist/");
+pref("extensions.blocklist.itemURL", "https://addons.mozilla.org/%LOCALE%/%APP%/blocked/%blockID%");
+
+pref("extensions.update.autoUpdateDefault", true);
+
+pref("extensions.hotfix.id", "firefox-hotfix@mozilla.org");
+pref("extensions.hotfix.cert.checkAttributes", true);
+pref("extensions.hotfix.certs.1.sha1Fingerprint", "F1:DB:F9:6A:7B:B8:04:FA:48:3C:16:95:C7:2F:17:C6:5B:C2:9F:45");
+
+// Disable add-ons that are not installed by the user in all scopes by default.
+// See the SCOPE constants in AddonManager.jsm for values to use here.
+pref("extensions.autoDisableScopes", 15);
+
+// Dictionary download preference
+pref("browser.dictionaries.download.url", "https://addons.mozilla.org/%LOCALE%/firefox/dictionaries/");
+
+// The minimum delay in seconds for the timer to fire.
+// default=2 minutes
+pref("app.update.timerMinimumDelay", 120);
+
+// App-specific update preferences
+
+// The interval to check for updates (app.update.interval) is defined in
+// firefox-branding.js
+
+// Alternative windowtype for an application update user interface window. When
+// a window with this windowtype is open the application update service won't
+// open the normal application update user interface window.
+pref("app.update.altwindowtype", "Browser:About");
+
+// Enables some extra Application Update Logging (can reduce performance)
+pref("app.update.log", false);
+
+// The number of general background check failures to allow before notifying the
+// user of the failure. User initiated update checks always notify the user of
+// the failure.
+pref("app.update.backgroundMaxErrors", 10);
+
+// When |app.update.cert.requireBuiltIn| is true or not specified the
+// final certificate and all certificates the connection is redirected to before
+// the final certificate for the url specified in the |app.update.url|
+// preference must be built-in.
+pref("app.update.cert.requireBuiltIn", true);
+
+// When |app.update.cert.checkAttributes| is true or not specified the
+// certificate attributes specified in the |app.update.certs.| preference branch
+// are checked against the certificate for the url specified by the
+// |app.update.url| preference.
+pref("app.update.cert.checkAttributes", true);
+
+// The number of certificate attribute check failures to allow for background
+// update checks before notifying the user of the failure. User initiated update
+// checks always notify the user of the certificate attribute check failure.
+pref("app.update.cert.maxErrors", 5);
+
+// The |app.update.certs.| preference branch contains branches that are
+// sequentially numbered starting at 1 that contain attribute name / value
+// pairs for the certificate used by the server that hosts the update xml file
+// as specified in the |app.update.url| preference. When these preferences are
+// present the following conditions apply for a successful update check:
+// 1. the uri scheme must be https
+// 2. the preference name must exist as an attribute name on the certificate and
+//    the value for the name must be the same as the value for the attribute name
+//    on the certificate.
+// If these conditions aren't met it will be treated the same as when there is
+// no update available. This validation will not be performed when using the
+// |app.update.url.override| preference for update checking.
+pref("app.update.certs.1.issuerName", "OU=Equifax Secure Certificate Authority,O=Equifax,C=US");
+pref("app.update.certs.1.commonName", "aus3.mozilla.org");
+
+pref("app.update.certs.2.issuerName", "CN=Thawte SSL CA,O=\"Thawte, Inc.\",C=US");
+pref("app.update.certs.2.commonName", "aus3.mozilla.org");
+
+// Whether or not app updates are enabled
+pref("app.update.enabled", true);
+
+// This preference turns on app.update.mode and allows automatic download and
+// install to take place. We use a separate boolean toggle for this to make
+// the UI easier to construct.
+pref("app.update.auto", true);
+
+// Defines how the Application Update Service notifies the user about updates:
+//
+// AUM Set to:        Minor Releases:     Major Releases:
+// 0                  download no prompt  download no prompt
+// 1                  download no prompt  download no prompt if no incompatibilities
+// 2                  download no prompt  prompt
+//
+// See chart in nsUpdateService.js source for more details
+//
+pref("app.update.mode", 1);
+
+// If set to true, the Update Service will present no UI for any event.
+pref("app.update.silent", false);
+
+// If set to true, the Update Service will apply updates in the background
+// when it finishes downloading them.
+pref("app.update.stage.enabled", true);
+
+// Update service URL:
+pref("app.update.url", "https://aus3.mozilla.org/update/3/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml");
+// app.update.url.manual is in branding section
+// app.update.url.details is in branding section
+
+// User-settable override to app.update.url for testing purposes.
+//pref("app.update.url.override", "");
+
+// app.update.interval is in branding section
+// app.update.promptWaitTime is in branding section
+
+// Show the Update Checking/Ready UI when the user was idle for x seconds
+pref("app.update.idletime", 60);
+
+// Whether or not we show a dialog box informing the user that the update was
+// successfully applied. This is off in Firefox by default since we show a 
+// upgrade start page instead! Other apps may wish to show this UI, and supply
+// a whatsNewURL field in their brand.properties that contains a link to a page
+// which tells users what's new in this new update.
+pref("app.update.showInstalledUI", false);
+
+// 0 = suppress prompting for incompatibilities if there are updates available
+//     to newer versions of installed addons that resolve them.
+// 1 = suppress prompting for incompatibilities only if there are VersionInfo
+//     updates available to installed addons that resolve them, not newer
+//     versions.
+pref("app.update.incompatible.mode", 0);
+
+// Whether or not to attempt using the service for updates.
+//@line 184 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+
+// Symmetric (can be overridden by individual extensions) update preferences.
+// e.g.
+//  extensions.{GUID}.update.enabled
+//  extensions.{GUID}.update.url
+//  .. etc ..
+//
+pref("extensions.update.enabled", true);
+pref("extensions.update.url", "https://versioncheck.addons.mozilla.org/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%&currentAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%&compatMode=%COMPATIBILITY_MODE%");
+pref("extensions.update.background.url", "https://versioncheck-bg.addons.mozilla.org/update/VersionCheck.php?reqVersion=%REQ_VERSION%&id=%ITEM_ID%&version=%ITEM_VERSION%&maxAppVersion=%ITEM_MAXAPPVERSION%&status=%ITEM_STATUS%&appID=%APP_ID%&appVersion=%APP_VERSION%&appOS=%APP_OS%&appABI=%APP_ABI%&locale=%APP_LOCALE%&currentAppVersion=%CURRENT_APP_VERSION%&updateType=%UPDATE_TYPE%&compatMode=%COMPATIBILITY_MODE%");
+pref("extensions.update.interval", 86400);  // Check for updates to Extensions and 
+                                            // Themes every day
+// Non-symmetric (not shared by extensions) extension-specific [update] preferences
+pref("extensions.getMoreThemesURL", "https://addons.mozilla.org/%LOCALE%/firefox/getpersonas");
+pref("extensions.dss.enabled", false);          // Dynamic Skin Switching                                               
+pref("extensions.dss.switchPending", false);    // Non-dynamic switch pending after next
+                                                // restart.
+
+pref("extensions.{972ce4c6-7e08-4474-a285-3208198ce6fd}.name", "chrome://browser/locale/browser.properties");
+pref("extensions.{972ce4c6-7e08-4474-a285-3208198ce6fd}.description", "chrome://browser/locale/browser.properties");
+
+pref("xpinstall.whitelist.add", "addons.mozilla.org");
+pref("xpinstall.whitelist.add.36", "getpersonas.com");
+
+pref("lightweightThemes.update.enabled", true);
+
+pref("keyword.enabled", true);
+// Override the default keyword.URL. Empty value means
+// "use the search service's default engine"
+pref("keyword.URL", "");
+
+pref("general.useragent.locale", "en-US");
+pref("general.skins.selectedSkin", "classic/1.0");
+
+pref("general.smoothScroll", true);
+//@line 220 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+pref("general.autoScroll", false);
+//@line 224 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+
+// At startup, check if we're the default browser and prompt user if not.
+pref("browser.shell.checkDefaultBrowser", true);
+
+// 0 = blank, 1 = home (browser.startup.homepage), 2 = last visited page, 3 = resume previous browser session
+// The behavior of option 3 is detailed at: http://wiki.mozilla.org/Session_Restore
+pref("browser.startup.page",                1);
+pref("browser.startup.homepage",            "chrome://branding/locale/browserconfig.properties");
+
+// This url, if changed, MUST continue to point to an https url. Pulling arbitrary content to inject into
+// this page over http opens us up to a man-in-the-middle attack that we'd rather not face. If you are a downstream
+// repackager of this code using an alternate snippet url, please keep your users safe
+pref("browser.aboutHomeSnippets.updateUrl", "https://snippets.mozilla.com/%STARTPAGE_VERSION%/%NAME%/%VERSION%/%APPBUILDID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/");
+
+pref("browser.enable_automatic_image_resizing", true);
+pref("browser.chrome.site_icons", true);
+pref("browser.chrome.favicons", true);
+// browser.warnOnQuit == false will override all other possible prompts when quitting or restarting
+pref("browser.warnOnQuit", true);
+pref("browser.warnOnRestart", false);
+// browser.showQuitWarning specifically controls the quit warning dialog. We
+// might still show the window closing dialog with showQuitWarning == false.
+pref("browser.showQuitWarning", false);
+pref("browser.fullscreen.autohide", true);
+pref("browser.fullscreen.animateUp", 1);
+pref("browser.overlink-delay", 80);
+
+//@line 252 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+pref("browser.urlbar.clickSelectsAll", false);
+//@line 257 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+pref("browser.urlbar.doubleClickSelectsAll", true);
+//@line 261 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+pref("browser.urlbar.autoFill", true);
+pref("browser.urlbar.autoFill.typed", true);
+// 0: Match anywhere (e.g., middle of words)
+// 1: Match on word boundaries and then try matching anywhere
+// 2: Match only on word boundaries (e.g., after / or .)
+// 3: Match at the beginning of the url or title
+pref("browser.urlbar.matchBehavior", 1);
+pref("browser.urlbar.filter.javascript", true);
+
+// the maximum number of results to show in autocomplete when doing richResults
+pref("browser.urlbar.maxRichResults", 12);
+// The amount of time (ms) to wait after the user has stopped typing
+// before starting to perform autocomplete.  50 is the default set in
+// autocomplete.xml.
+pref("browser.urlbar.delay", 50);
+
+// The special characters below can be typed into the urlbar to either restrict
+// the search to visited history, bookmarked, tagged pages; or force a match on
+// just the title text or url.
+pref("browser.urlbar.restrict.history", "^");
+pref("browser.urlbar.restrict.bookmark", "*");
+pref("browser.urlbar.restrict.tag", "+");
+pref("browser.urlbar.restrict.openpage", "%");
+pref("browser.urlbar.restrict.typed", "~");
+pref("browser.urlbar.match.title", "#");
+pref("browser.urlbar.match.url", "@");
+
+// The default behavior for the urlbar can be configured to use any combination
+// of the restrict or match filters with each additional filter restricting
+// more (intersection). Add the following values to set the behavior as the
+// default: 1: history, 2: bookmark, 4: tag, 8: title, 16: url, 32: typed,
+//          64: javascript, 128: tabs
+// E.g., 0 = show all results (no filtering), 1 = only visited pages in history,
+// 2 = only bookmarks, 3 = visited bookmarks, 1+16 = history matching in the url
+pref("browser.urlbar.default.behavior", 0);
+
+pref("browser.urlbar.formatting.enabled", true);
+pref("browser.urlbar.trimURLs", true);
+
+pref("browser.altClickSave", false);
+
+// Number of milliseconds to wait for the http headers (and thus
+// the Content-Disposition filename) before giving up and falling back to 
+// picking a filename without that info in hand so that the user sees some
+// feedback from their action.
+pref("browser.download.saveLinkAsFilenameTimeout", 4000);
+
+pref("browser.download.useDownloadDir", true);
+
+pref("browser.download.folderList", 1);
+pref("browser.download.manager.showAlertOnComplete", true);
+pref("browser.download.manager.showAlertInterval", 2000);
+pref("browser.download.manager.retention", 2);
+pref("browser.download.manager.showWhenStarting", true);
+pref("browser.download.manager.closeWhenDone", false);
+pref("browser.download.manager.focusWhenStarting", false);
+pref("browser.download.manager.flashCount", 2);
+pref("browser.download.manager.addToRecentDocs", true);
+pref("browser.download.manager.quitBehavior", 0);
+pref("browser.download.manager.scanWhenDone", true);
+pref("browser.download.manager.resumeOnWakeDelay", 10000);
+
+// This allows disabling the Downloads Panel in favor of the old interface.
+pref("browser.download.useToolkitUI", true);
+
+// This controls retention behavior in the Downloads Panel only.
+pref("browser.download.panel.removeFinishedDownloads", false);
+
+// search engines URL
+pref("browser.search.searchEnginesURL",      "https://addons.mozilla.org/%LOCALE%/firefox/search-engines/");
+
+// pointer to the default engine name
+pref("browser.search.defaultenginename",      "chrome://browser-region/locale/region.properties");
+
+// disable logging for the search service by default
+pref("browser.search.log", false);
+
+// Ordering of Search Engines in the Engine list. 
+pref("browser.search.order.1",                "chrome://browser-region/locale/region.properties");
+pref("browser.search.order.2",                "chrome://browser-region/locale/region.properties");
+pref("browser.search.order.3",                "chrome://browser-region/locale/region.properties");
+
+// search bar results always open in a new tab
+pref("browser.search.openintab", false);
+
+// context menu searches open in the foreground
+pref("browser.search.context.loadInBackground", false);
+
+// send ping to the server to update
+pref("browser.search.update", true);
+
+// disable logging for the search service update system by default
+pref("browser.search.update.log", false);
+
+// Check whether we need to perform engine updates every 6 hours
+pref("browser.search.update.interval", 21600);
+
+// enable search suggestions by default
+pref("browser.search.suggest.enabled", true);
+
+pref("browser.sessionhistory.max_entries", 50);
+
+// handle links targeting new windows
+// 1=current window/tab, 2=new window, 3=new tab in most recent window
+pref("browser.link.open_newwindow", 3);
+
+// handle external links (i.e. links opened from a different application)
+// default: use browser.link.open_newwindow
+// 1-3: see browser.link.open_newwindow for interpretation
+pref("browser.link.open_newwindow.override.external", -1);
+
+// 0: no restrictions - divert everything
+// 1: don't divert window.open at all
+// 2: don't divert window.open with features
+pref("browser.link.open_newwindow.restriction", 2);
+
+// Tabbed browser
+pref("browser.tabs.autoHide", false);
+pref("browser.tabs.closeWindowWithLastTab", true);
+pref("browser.tabs.insertRelatedAfterCurrent", true);
+pref("browser.tabs.warnOnClose", true);
+pref("browser.tabs.warnOnOpen", true);
+pref("browser.tabs.maxOpenBeforeWarn", 15);
+pref("browser.tabs.loadInBackground", true);
+pref("browser.tabs.opentabfor.middleclick", true);
+pref("browser.tabs.loadDivertedInBackground", false);
+pref("browser.tabs.loadBookmarksInBackground", false);
+pref("browser.tabs.tabClipWidth", 140);
+pref("browser.tabs.animate", true);
+pref("browser.tabs.onTop", true);
+pref("browser.tabs.drawInTitlebar", true);
+
+// Where to show tab close buttons:
+// 0  on active tab only
+// 1  on all tabs until tabClipWidth is reached, then active tab only
+// 2  no close buttons at all
+// 3  at the end of the tabstrip
+pref("browser.tabs.closeButtons", 1);
+
+// When tabs opened by links in other tabs via a combination of 
+// browser.link.open_newwindow being set to 3 and target="_blank" etc are
+// closed:
+// true   return to the tab that opened this tab (its owner)
+// false  return to the adjacent tab (old default)
+pref("browser.tabs.selectOwnerOnClose", true);
+
+pref("browser.allTabs.previews", false);
+pref("browser.ctrlTab.previews", false);
+pref("browser.ctrlTab.recentlyUsedLimit", 7);
+
+// By default, do not export HTML at shutdown.
+// If true, at shutdown the bookmarks in your menu and toolbar will
+// be exported as HTML to the bookmarks.html file.
+pref("browser.bookmarks.autoExportHTML",          false);
+
+// The maximum number of daily bookmark backups to 
+// keep in {PROFILEDIR}/bookmarkbackups. Special values:
+// -1: unlimited
+//  0: no backups created (and deletes all existing backups)
+pref("browser.bookmarks.max_backups",             10);
+
+// Scripts & Windows prefs
+pref("dom.disable_open_during_load",              true);
+pref("javascript.options.showInConsole",          true);
+//@line 428 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+
+// This is the pref to control the location bar, change this to true to 
+// force this - this makes the origin of popup windows more obvious to avoid
+// spoofing. We would rather not do it by default because it affects UE for web
+// applications, but without it there isn't a really good way to prevent chrome
+// spoofing, see bug 337344
+pref("dom.disable_window_open_feature.location",  true);
+// prevent JS from setting status messages
+pref("dom.disable_window_status_change",          true);
+// allow JS to move and resize existing windows
+pref("dom.disable_window_move_resize",            false);
+// prevent JS from monkeying with window focus, etc
+pref("dom.disable_window_flip",                   true);
+
+// popups.policy 1=allow,2=reject
+pref("privacy.popups.policy",               1);
+pref("privacy.popups.usecustom",            true);
+pref("privacy.popups.showBrowserMessage",   true);
+
+pref("privacy.item.cookies",                false);
+
+pref("privacy.clearOnShutdown.history",     true);
+pref("privacy.clearOnShutdown.formdata",    true);
+pref("privacy.clearOnShutdown.passwords",   false);
+pref("privacy.clearOnShutdown.downloads",   true);
+pref("privacy.clearOnShutdown.cookies",     true);
+pref("privacy.clearOnShutdown.cache",       true);
+pref("privacy.clearOnShutdown.sessions",    true);
+pref("privacy.clearOnShutdown.offlineApps", false);
+pref("privacy.clearOnShutdown.siteSettings", false);
+
+pref("privacy.cpd.history",                 true);
+pref("privacy.cpd.formdata",                true);
+pref("privacy.cpd.passwords",               false);
+pref("privacy.cpd.downloads",               true);
+pref("privacy.cpd.cookies",                 true);
+pref("privacy.cpd.cache",                   true);
+pref("privacy.cpd.sessions",                true);
+pref("privacy.cpd.offlineApps",             false);
+pref("privacy.cpd.siteSettings",            false);
+
+// What default should we use for the time span in the sanitizer:
+// 0 - Clear everything
+// 1 - Last Hour
+// 2 - Last 2 Hours
+// 3 - Last 4 Hours
+// 4 - Today
+pref("privacy.sanitize.timeSpan", 1);
+pref("privacy.sanitize.sanitizeOnShutdown", false);
+
+pref("privacy.sanitize.migrateFx3Prefs",    false);
+
+pref("network.proxy.share_proxy_settings",  false); // use the same proxy settings for all protocols
+
+// simple gestures support
+pref("browser.gesture.swipe.left", "Browser:BackOrBackDuplicate");
+pref("browser.gesture.swipe.right", "Browser:ForwardOrForwardDuplicate");
+pref("browser.gesture.swipe.up", "cmd_scrollTop");
+pref("browser.gesture.swipe.down", "cmd_scrollBottom");
+//@line 491 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+pref("browser.gesture.pinch.latched", false);
+pref("browser.gesture.pinch.threshold", 25);
+//@line 501 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+// Disabled by default due to issues with track pad input.
+pref("browser.gesture.pinch.out", "");
+pref("browser.gesture.pinch.in", "");
+pref("browser.gesture.pinch.out.shift", "");
+pref("browser.gesture.pinch.in.shift", "");
+//@line 507 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+pref("browser.gesture.twist.latched", false);
+pref("browser.gesture.twist.threshold", 25);
+pref("browser.gesture.twist.right", "");
+pref("browser.gesture.twist.left", "");
+pref("browser.gesture.tap", "cmd_fullZoomReset");
+
+// 0=lines, 1=pages, 2=history , 3=text size
+//@line 528 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+pref("mousewheel.withshiftkey.action",2);
+pref("mousewheel.withshiftkey.sysnumlines",false);
+pref("mousewheel.withshiftkey.numlines",1);
+pref("mousewheel.withaltkey.action",0);
+pref("mousewheel.withaltkey.sysnumlines",false);
+pref("mousewheel.withaltkey.numlines",1);
+pref("mousewheel.withmetakey.action",0);
+pref("mousewheel.withmetakey.sysnumlines",true);
+pref("mousewheel.withmetakey.numlines",1);
+//@line 538 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+pref("mousewheel.withcontrolkey.action",3);
+pref("mousewheel.withcontrolkey.sysnumlines",false);
+pref("mousewheel.withcontrolkey.numlines",1);
+
+// pref to control the alert notification 
+pref("alerts.slideIncrement", 1);
+pref("alerts.slideIncrementTime", 10);
+pref("alerts.totalOpenTime", 4000);
+
+pref("browser.xul.error_pages.enabled", true);
+pref("browser.xul.error_pages.expert_bad_cert", false);
+
+// Work Offline is best manually managed by the user.
+pref("network.manage-offline-status", false);
+
+// We want to make sure mail URLs are handled externally...
+pref("network.protocol-handler.external.mailto", true); // for mail
+pref("network.protocol-handler.external.news", true);   // for news
+pref("network.protocol-handler.external.snews", true);  // for secure news
+pref("network.protocol-handler.external.nntp", true);   // also news
+// ...without warning dialogs
+pref("network.protocol-handler.warn-external.mailto", false);
+pref("network.protocol-handler.warn-external.news", false);
+pref("network.protocol-handler.warn-external.snews", false);
+pref("network.protocol-handler.warn-external.nntp", false);
+
+// By default, all protocol handlers are exposed.  This means that
+// the browser will respond to openURL commands for all URL types.
+// It will also try to open link clicks inside the browser before
+// failing over to the system handlers.
+pref("network.protocol-handler.expose-all", true);
+pref("network.protocol-handler.expose.mailto", false);
+pref("network.protocol-handler.expose.news", false);
+pref("network.protocol-handler.expose.snews", false);
+pref("network.protocol-handler.expose.nntp", false);
+
+// Default security warning dialogs to show once.
+pref("security.warn_entering_secure.show_once", false);
+pref("security.warn_entering_weak.show_once", true);
+pref("security.warn_leaving_secure.show_once", false);
+pref("security.warn_viewing_mixed.show_once", true);
+pref("security.warn_submit_insecure.show_once", false);
+
+pref("accessibility.typeaheadfind", false);
+pref("accessibility.typeaheadfind.timeout", 5000);
+pref("accessibility.typeaheadfind.linksonly", false);
+pref("accessibility.typeaheadfind.flashBar", 1);
+
+// plugin finder service url
+pref("pfs.datasource.url", "https://pfs.mozilla.org/plugins/PluginFinderService.php?mimetype=%PLUGIN_MIMETYPE%&appID=%APP_ID%&appVersion=%APP_VERSION%&clientOS=%CLIENT_OS%&chromeLocale=%CHROME_LOCALE%&appRelease=%APP_RELEASE%");
+
+// by default we show an infobar message when pages require plugins the user has not installed, or are outdated
+pref("plugins.hide_infobar_for_missing_plugin", false);
+pref("plugins.hide_infobar_for_outdated_plugin", false);
+
+//@line 597 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+
+pref("plugins.update.url", "https://www.mozilla.org/%LOCALE%/plugincheck/");
+pref("plugins.update.notifyUser", false);
+
+pref("plugins.click_to_play", false);
+
+//@line 606 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+pref("browser.preferences.instantApply", true);
+//@line 611 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+pref("browser.preferences.animateFadeIn", false);
+//@line 613 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+
+// Toggles between the two Preferences implementations, pop-up window and in-content
+pref("browser.preferences.inContent", false);
+
+pref("browser.download.show_plugins_in_list", true);
+pref("browser.download.hide_plugins_without_extensions", true);
+
+// Backspace and Shift+Backspace behavior
+// 0 goes Back/Forward
+// 1 act like PgUp/PgDown
+// 2 and other values, nothing
+//@line 625 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+pref("browser.backspace_action", 2);
+//@line 629 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+
+// this will automatically enable inline spellchecking (if it is available) for
+// editable elements in HTML
+// 0 = spellcheck nothing
+// 1 = check multi-line controls [default]
+// 2 = check multi/single line controls
+pref("layout.spellcheckDefault", 1);
+
+pref("browser.send_pings", false);
+
+/* initial web feed readers list */
+pref("browser.contentHandlers.types.0.title", "chrome://browser-region/locale/region.properties");
+pref("browser.contentHandlers.types.0.uri", "chrome://browser-region/locale/region.properties");
+pref("browser.contentHandlers.types.0.type", "application/vnd.mozilla.maybe.feed");
+pref("browser.contentHandlers.types.1.title", "chrome://browser-region/locale/region.properties");
+pref("browser.contentHandlers.types.1.uri", "chrome://browser-region/locale/region.properties");
+pref("browser.contentHandlers.types.1.type", "application/vnd.mozilla.maybe.feed");
+pref("browser.contentHandlers.types.2.title", "chrome://browser-region/locale/region.properties");
+pref("browser.contentHandlers.types.2.uri", "chrome://browser-region/locale/region.properties");
+pref("browser.contentHandlers.types.2.type", "application/vnd.mozilla.maybe.feed");
+pref("browser.contentHandlers.types.3.title", "chrome://browser-region/locale/region.properties");
+pref("browser.contentHandlers.types.3.uri", "chrome://browser-region/locale/region.properties");
+pref("browser.contentHandlers.types.3.type", "application/vnd.mozilla.maybe.feed");
+pref("browser.contentHandlers.types.4.title", "chrome://browser-region/locale/region.properties");
+pref("browser.contentHandlers.types.4.uri", "chrome://browser-region/locale/region.properties");
+pref("browser.contentHandlers.types.4.type", "application/vnd.mozilla.maybe.feed");
+pref("browser.contentHandlers.types.5.title", "chrome://browser-region/locale/region.properties");
+pref("browser.contentHandlers.types.5.uri", "chrome://browser-region/locale/region.properties");
+pref("browser.contentHandlers.types.5.type", "application/vnd.mozilla.maybe.feed");
+
+pref("browser.feeds.handler", "ask");
+pref("browser.videoFeeds.handler", "ask");
+pref("browser.audioFeeds.handler", "ask");
+
+// At startup, if the handler service notices that the version number in the
+// region.properties file is newer than the version number in the handler
+// service datastore, it will add any new handlers it finds in the prefs (as
+// seeded by this file) to its datastore.  
+pref("gecko.handlerService.defaultHandlersVersion", "chrome://browser-region/locale/region.properties");
+
+// The default set of web-based protocol handlers shown in the application
+// selection dialog for webcal: ; I've arbitrarily picked 4 default handlers
+// per protocol, but if some locale wants more than that (or defaults for some
+// protocol not currently listed here), we should go ahead and add those.
+
+// webcal
+pref("gecko.handlerService.schemes.webcal.0.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.webcal.0.uriTemplate", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.webcal.1.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.webcal.1.uriTemplate", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.webcal.2.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.webcal.2.uriTemplate", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.webcal.3.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.webcal.3.uriTemplate", "chrome://browser-region/locale/region.properties");
+
+// mailto
+pref("gecko.handlerService.schemes.mailto.0.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.mailto.0.uriTemplate", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.mailto.1.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.mailto.1.uriTemplate", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.mailto.2.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.mailto.2.uriTemplate", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.mailto.3.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.mailto.3.uriTemplate", "chrome://browser-region/locale/region.properties");
+
+// irc
+pref("gecko.handlerService.schemes.irc.0.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.irc.0.uriTemplate", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.irc.1.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.irc.1.uriTemplate", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.irc.2.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.irc.2.uriTemplate", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.irc.3.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.irc.3.uriTemplate", "chrome://browser-region/locale/region.properties");
+
+// ircs
+pref("gecko.handlerService.schemes.ircs.0.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.ircs.0.uriTemplate", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.ircs.1.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.ircs.1.uriTemplate", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.ircs.2.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.ircs.2.uriTemplate", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.ircs.3.name", "chrome://browser-region/locale/region.properties");
+pref("gecko.handlerService.schemes.ircs.3.uriTemplate", "chrome://browser-region/locale/region.properties");
+
+// By default, we don't want protocol/content handlers to be registered from a different host, see bug 402287
+pref("gecko.handlerService.allowRegisterFromDifferentHost", false);
+
+//@line 718 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+// Safe browsing does nothing unless this pref is set
+pref("browser.safebrowsing.enabled", true);
+
+// Prevent loading of pages identified as malware
+pref("browser.safebrowsing.malware.enabled", true);
+
+// Non-enhanced mode (local url lists) URL list to check for updates
+pref("browser.safebrowsing.provider.0.updateURL", "http://safebrowsing.clients.google.com/safebrowsing/downloads?client={moz:client}&appver={moz:version}&pver=2.2");
+
+pref("browser.safebrowsing.dataProvider", 0);
+
+// Does the provider name need to be localizable?
+pref("browser.safebrowsing.provider.0.name", "Google");
+pref("browser.safebrowsing.provider.0.keyURL", "https://sb-ssl.google.com/safebrowsing/newkey?client={moz:client}&appver={moz:version}&pver=2.2");
+pref("browser.safebrowsing.provider.0.reportURL", "http://safebrowsing.clients.google.com/safebrowsing/report?");
+pref("browser.safebrowsing.provider.0.gethashURL", "http://safebrowsing.clients.google.com/safebrowsing/gethash?client={moz:client}&appver={moz:version}&pver=2.2");
+
+// HTML report pages
+pref("browser.safebrowsing.provider.0.reportGenericURL", "http://{moz:locale}.phish-generic.mozilla.com/?hl={moz:locale}");
+pref("browser.safebrowsing.provider.0.reportErrorURL", "http://{moz:locale}.phish-error.mozilla.com/?hl={moz:locale}");
+pref("browser.safebrowsing.provider.0.reportPhishURL", "http://{moz:locale}.phish-report.mozilla.com/?hl={moz:locale}");
+pref("browser.safebrowsing.provider.0.reportMalwareURL", "http://{moz:locale}.malware-report.mozilla.com/?hl={moz:locale}");
+pref("browser.safebrowsing.provider.0.reportMalwareErrorURL", "http://{moz:locale}.malware-error.mozilla.com/?hl={moz:locale}");
+
+// FAQ URLs
+pref("browser.safebrowsing.warning.infoURL", "http://www.mozilla.com/%LOCALE%/firefox/phishing-protection/");
+pref("browser.geolocation.warning.infoURL", "http://www.mozilla.com/%LOCALE%/firefox/geolocation/");
+
+// Name of the about: page contributed by safebrowsing to handle display of error
+// pages on phishing/malware hits.  (bug 399233)
+pref("urlclassifier.alternate_error_page", "blocked");
+
+// The number of random entries to send with a gethash request.
+pref("urlclassifier.gethashnoise", 4);
+
+// The list of tables that use the gethash request to confirm partial results.
+pref("urlclassifier.gethashtables", "goog-phish-shavar,goog-malware-shavar");
+
+// If an urlclassifier table has not been updated in this number of seconds,
+// a gethash request will be forced to check that the result is still in
+// the database.
+pref("urlclassifier.confirm-age", 2700);
+
+// Maximum size of the sqlite3 cache during an update, in bytes
+pref("urlclassifier.updatecachemax", 41943040);
+
+// Maximum size of the sqlite3 cache for lookups, in bytes
+pref("urlclassifier.lookupcachemax", 1048576);
+
+// URL for checking the reason for a malware warning.
+pref("browser.safebrowsing.malware.reportURL", "http://safebrowsing.clients.google.com/safebrowsing/diagnostic?client=%NAME%&hl=%LOCALE%&site=");
+
+//@line 771 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+
+pref("browser.EULA.version", 3);
+pref("browser.rights.version", 3);
+pref("browser.rights.3.shown", false);
+
+//@line 780 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+
+pref("browser.sessionstore.resume_from_crash", true);
+pref("browser.sessionstore.resume_session_once", false);
+
+// minimal interval between two save operations in milliseconds
+pref("browser.sessionstore.interval", 15000);
+// maximum amount of POSTDATA to be saved in bytes per history entry (-1 = all of it)
+// (NB: POSTDATA will be saved either entirely or not at all)
+pref("browser.sessionstore.postdata", 0);
+// on which sites to save text data, POSTDATA and cookies
+// 0 = everywhere, 1 = unencrypted sites, 2 = nowhere
+pref("browser.sessionstore.privacy_level", 0);
+// the same as browser.sessionstore.privacy_level, but for saving deferred session data
+pref("browser.sessionstore.privacy_level_deferred", 1);
+// how many tabs can be reopened (per window)
+pref("browser.sessionstore.max_tabs_undo", 10);
+// how many windows can be reopened (per session) - on non-OS X platforms this
+// pref may be ignored when dealing with pop-up windows to ensure proper startup
+pref("browser.sessionstore.max_windows_undo", 3);
+// number of crashes that can occur before the about:sessionrestore page is displayed
+// (this pref has no effect if more than 6 hours have passed since the last crash)
+pref("browser.sessionstore.max_resumed_crashes", 1);
+// restore_on_demand overrides MAX_CONCURRENT_TAB_RESTORES (sessionstore constant)
+// and restore_hidden_tabs. When true, tabs will not be restored until they are
+// focused (also applies to tabs that aren't visible). When false, the values
+// for MAX_CONCURRENT_TAB_RESTORES and restore_hidden_tabs are respected.
+// Selected tabs are always restored regardless of this pref.
+pref("browser.sessionstore.restore_on_demand", true);
+// Whether to automatically restore hidden tabs (i.e., tabs in other tab groups) or not
+pref("browser.sessionstore.restore_hidden_tabs", false);
+// If restore_on_demand is set, pinned tabs are restored on startup by default.
+// When set to true, this pref overrides that behavior, and pinned tabs will only
+// be restored when they are focused.
+pref("browser.sessionstore.restore_pinned_tabs_on_demand", false);
+
+// allow META refresh by default
+pref("accessibility.blockautorefresh", false);
+
+// Whether history is enabled or not.
+pref("places.history.enabled", true);
+
+// the (maximum) number of the recent visits to sample
+// when calculating frecency
+pref("places.frecency.numVisits", 10);
+
+// buckets (in days) for frecency calculation
+pref("places.frecency.firstBucketCutoff", 4);
+pref("places.frecency.secondBucketCutoff", 14);
+pref("places.frecency.thirdBucketCutoff", 31);
+pref("places.frecency.fourthBucketCutoff", 90);
+
+// weights for buckets for frecency calculations
+pref("places.frecency.firstBucketWeight", 100);
+pref("places.frecency.secondBucketWeight", 70);
+pref("places.frecency.thirdBucketWeight", 50);
+pref("places.frecency.fourthBucketWeight", 30);
+pref("places.frecency.defaultBucketWeight", 10);
+
+// bonus (in percent) for visit transition types for frecency calculations
+pref("places.frecency.embedVisitBonus", 0);
+pref("places.frecency.framedLinkVisitBonus", 0);
+pref("places.frecency.linkVisitBonus", 100);
+pref("places.frecency.typedVisitBonus", 2000);
+pref("places.frecency.bookmarkVisitBonus", 75);
+pref("places.frecency.downloadVisitBonus", 0);
+pref("places.frecency.permRedirectVisitBonus", 0);
+pref("places.frecency.tempRedirectVisitBonus", 0);
+pref("places.frecency.defaultVisitBonus", 0);
+
+// bonus (in percent) for place types for frecency calculations
+pref("places.frecency.unvisitedBookmarkBonus", 140);
+pref("places.frecency.unvisitedTypedBonus", 200);
+
+// Controls behavior of the "Add Exception" dialog launched from SSL error pages
+// 0 - don't pre-populate anything
+// 1 - pre-populate site URL, but don't fetch certificate
+// 2 - pre-populate site URL and pre-fetch certificate
+pref("browser.ssl_override_behavior", 2);
+
+// True if the user should be prompted when a web application supports
+// offline apps.
+pref("browser.offline-apps.notify", true);
+
+// if true, use full page zoom instead of text zoom
+pref("browser.zoom.full", true);
+
+// Whether or not to save and restore zoom levels on a per-site basis.
+pref("browser.zoom.siteSpecific", true);
+
+// Whether or not to update background tabs to the current zoom level.
+pref("browser.zoom.updateBackgroundTabs", true);
+
+// The breakpad report server to link to in about:crashes
+pref("breakpad.reportURL", "http://crash-stats.mozilla.com/report/index/");
+
+// base URL for web-based support pages
+pref("app.support.baseURL", "http://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/");
+
+// Name of alternate about: page for certificate errors (when undefined, defaults to about:neterror)
+pref("security.alternate_certificate_error_page", "certerror");
+
+// Whether to start the private browsing mode at application startup
+pref("browser.privatebrowsing.autostart", false);
+
+// Whether we should skip prompting before starting the private browsing mode
+pref("browser.privatebrowsing.dont_prompt_on_enter", false);
+
+// Don't try to alter this pref, it'll be reset the next time you use the
+// bookmarking dialog
+pref("browser.bookmarks.editDialog.firstEditField", "namePicker");
+
+// Whether to use a panel that looks like an OS X sheet for customization
+//@line 895 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+pref("toolbar.customization.usesheet", false);
+//@line 897 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+
+// The default for this pref reflects whether the build is capable of IPC.
+// (Turning it on in a no-IPC build will have no effect.)
+//@line 910 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+pref("dom.ipc.plugins.enabled", true);
+//@line 912 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+
+//@line 916 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+
+// This pref governs whether we attempt to work around problems caused by
+// plugins using OS calls to manipulate the cursor while running out-of-
+// process.  These workarounds all involve intercepting (hooking) certain
+// OS calls in the plugin process, then arranging to make certain OS calls
+// in the browser process.  Eventually plugins will be required to use the
+// NPAPI to manipulate the cursor, and these workarounds will be removed.
+// See bug 621117.
+//@line 927 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+
+//@line 939 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+
+//@line 941 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+// The sync engines to use.
+pref("services.sync.registerEngines", "Bookmarks,Form,History,Password,Prefs,Tab,Addons");
+// Preferences to be synced by default
+pref("services.sync.prefs.sync.accessibility.blockautorefresh", true);
+pref("services.sync.prefs.sync.accessibility.browsewithcaret", true);
+pref("services.sync.prefs.sync.accessibility.typeaheadfind", true);
+pref("services.sync.prefs.sync.accessibility.typeaheadfind.linksonly", true);
+pref("services.sync.prefs.sync.addons.ignoreUserEnabledChanges", true);
+// The addons prefs related to repository verification are intentionally
+// not synced for security reasons. If a system is compromised, a user
+// could weaken the pref locally, install an add-on from an untrusted
+// source, and this would propagate automatically to other,
+// uncompromised Sync-connected devices.
+pref("services.sync.prefs.sync.app.update.mode", true);
+pref("services.sync.prefs.sync.browser.download.manager.closeWhenDone", true);
+pref("services.sync.prefs.sync.browser.download.manager.retention", true);
+pref("services.sync.prefs.sync.browser.download.manager.scanWhenDone", true);
+pref("services.sync.prefs.sync.browser.download.manager.showWhenStarting", true);
+pref("services.sync.prefs.sync.browser.formfill.enable", true);
+pref("services.sync.prefs.sync.browser.link.open_newwindow", true);
+pref("services.sync.prefs.sync.browser.offline-apps.notify", true);
+pref("services.sync.prefs.sync.browser.safebrowsing.enabled", true);
+pref("services.sync.prefs.sync.browser.safebrowsing.malware.enabled", true);
+pref("services.sync.prefs.sync.browser.search.selectedEngine", true);
+pref("services.sync.prefs.sync.browser.search.update", true);
+pref("services.sync.prefs.sync.browser.sessionstore.restore_on_demand", true);
+pref("services.sync.prefs.sync.browser.startup.homepage", true);
+pref("services.sync.prefs.sync.browser.startup.page", true);
+pref("services.sync.prefs.sync.browser.tabs.autoHide", true);
+pref("services.sync.prefs.sync.browser.tabs.closeButtons", true);
+pref("services.sync.prefs.sync.browser.tabs.loadInBackground", true);
+pref("services.sync.prefs.sync.browser.tabs.warnOnClose", true);
+pref("services.sync.prefs.sync.browser.tabs.warnOnOpen", true);
+pref("services.sync.prefs.sync.browser.urlbar.autocomplete.enabled", true);
+pref("services.sync.prefs.sync.browser.urlbar.default.behavior", true);
+pref("services.sync.prefs.sync.browser.urlbar.maxRichResults", true);
+pref("services.sync.prefs.sync.dom.disable_open_during_load", true);
+pref("services.sync.prefs.sync.dom.disable_window_flip", true);
+pref("services.sync.prefs.sync.dom.disable_window_move_resize", true);
+pref("services.sync.prefs.sync.dom.event.contextmenu.enabled", true);
+pref("services.sync.prefs.sync.extensions.personas.current", true);
+pref("services.sync.prefs.sync.extensions.update.enabled", true);
+pref("services.sync.prefs.sync.intl.accept_languages", true);
+pref("services.sync.prefs.sync.javascript.enabled", true);
+pref("services.sync.prefs.sync.layout.spellcheckDefault", true);
+pref("services.sync.prefs.sync.lightweightThemes.isThemeSelected", true);
+pref("services.sync.prefs.sync.lightweightThemes.usedThemes", true);
+pref("services.sync.prefs.sync.network.cookie.cookieBehavior", true);
+pref("services.sync.prefs.sync.network.cookie.lifetimePolicy", true);
+pref("services.sync.prefs.sync.permissions.default.image", true);
+pref("services.sync.prefs.sync.pref.advanced.images.disable_button.view_image", true);
+pref("services.sync.prefs.sync.pref.advanced.javascript.disable_button.advanced", true);
+pref("services.sync.prefs.sync.pref.downloads.disable_button.edit_actions", true);
+pref("services.sync.prefs.sync.pref.privacy.disable_button.cookie_exceptions", true);
+pref("services.sync.prefs.sync.privacy.clearOnShutdown.cache", true);
+pref("services.sync.prefs.sync.privacy.clearOnShutdown.cookies", true);
+pref("services.sync.prefs.sync.privacy.clearOnShutdown.downloads", true);
+pref("services.sync.prefs.sync.privacy.clearOnShutdown.formdata", true);
+pref("services.sync.prefs.sync.privacy.clearOnShutdown.history", true);
+pref("services.sync.prefs.sync.privacy.clearOnShutdown.offlineApps", true);
+pref("services.sync.prefs.sync.privacy.clearOnShutdown.passwords", true);
+pref("services.sync.prefs.sync.privacy.clearOnShutdown.sessions", true);
+pref("services.sync.prefs.sync.privacy.clearOnShutdown.siteSettings", true);
+pref("services.sync.prefs.sync.privacy.donottrackheader.enabled", true);
+pref("services.sync.prefs.sync.privacy.sanitize.sanitizeOnShutdown", true);
+pref("services.sync.prefs.sync.security.OCSP.disable_button.managecrl", true);
+pref("services.sync.prefs.sync.security.OCSP.enabled", true);
+pref("services.sync.prefs.sync.security.OCSP.require", true);
+pref("services.sync.prefs.sync.security.default_personal_cert", true);
+pref("services.sync.prefs.sync.security.enable_ssl3", true);
+pref("services.sync.prefs.sync.security.enable_tls", true);
+pref("services.sync.prefs.sync.security.warn_entering_secure", true);
+pref("services.sync.prefs.sync.security.warn_entering_weak", true);
+pref("services.sync.prefs.sync.security.warn_leaving_secure", true);
+pref("services.sync.prefs.sync.security.warn_submit_insecure", true);
+pref("services.sync.prefs.sync.security.warn_viewing_mixed", true);
+pref("services.sync.prefs.sync.signon.rememberSignons", true);
+pref("services.sync.prefs.sync.spellchecker.dictionary", true);
+pref("services.sync.prefs.sync.xpinstall.whitelist.required", true);
+//@line 1021 "/build/src/mozilla-release/browser/app/profile/firefox.js"
+
+// Disable the error console
+pref("devtools.errorconsole.enabled", false);
+
+// Developer toolbar and GCLI preferences
+pref("devtools.toolbar.enabled", false);
+pref("devtools.gcli.allowSet", false);
+
+// Enable the Inspector
+pref("devtools.inspector.enabled", true);
+pref("devtools.inspector.htmlHeight", 112);
+pref("devtools.inspector.htmlPanelOpen", false);
+pref("devtools.inspector.sidebarOpen", false);
+pref("devtools.inspector.activeSidebar", "ruleview");
+pref("devtools.inspector.highlighterShowVeil", true);
+pref("devtools.inspector.highlighterShowInfobar", true);
+
+// Enable the Layout View
+pref("devtools.layoutview.enabled", true);
+pref("devtools.layoutview.open", false);
+
+// Enable the Responsive UI tool
+pref("devtools.responsiveUI.enabled", true);
+
+// Enable the Debugger
+pref("devtools.debugger.enabled", true);
+pref("devtools.debugger.chrome-enabled", false);
+pref("devtools.debugger.remote-host", "localhost");
+pref("devtools.debugger.remote-autoconnect", false);
+pref("devtools.debugger.remote-connection-retries", 3);
+pref("devtools.debugger.remote-timeout", 3000);
+
+// The default Debugger UI settings
+pref("devtools.debugger.ui.height", 250);
+pref("devtools.debugger.ui.remote-win.width", 900);
+pref("devtools.debugger.ui.remote-win.height", 400);
+pref("devtools.debugger.ui.stackframes-width", 200);
+pref("devtools.debugger.ui.variables-width", 300);
+
+// Enable the style inspector
+pref("devtools.styleinspector.enabled", true);
+
+// Enable the Tilt inspector
+pref("devtools.tilt.enabled", true);
+pref("devtools.tilt.intro_transition", true);
+pref("devtools.tilt.outro_transition", true);
+
+// Enable the rules view
+pref("devtools.ruleview.enabled", true);
+
+// Enable the Scratchpad tool.
+pref("devtools.scratchpad.enabled", true);
+
+// Enable the Style Editor.
+pref("devtools.styleeditor.enabled", true);
+pref("devtools.styleeditor.transitions", true);
+
+// Enable tools for Chrome development.
+pref("devtools.chrome.enabled", false);
+
+// Display the introductory text
+pref("devtools.gcli.hideIntro", false);
+
+// How eager are we to show help: never=1, sometimes=2, always=3
+pref("devtools.gcli.eagerHelper", 2);
+
+// Do we allow the 'pref set' command
+pref("devtools.gcli.allowSet", false);
+
+// The last Web Console height. This is initially 0 which means that the Web
+// Console will use the default height next time it shows.
+// Change to -1 if you do not want the Web Console to remember its last height.
+pref("devtools.hud.height", 0);
+
+// Remember the Web Console position. Possible values:
+//   above - above the web page,
+//   below - below the web page,
+//   window - in a separate window/popup panel.
+pref("devtools.webconsole.position", "above");
+
+// Remember the Web Console filters
+pref("devtools.webconsole.filter.network", true);
+pref("devtools.webconsole.filter.networkinfo", true);
+pref("devtools.webconsole.filter.csserror", true);
+pref("devtools.webconsole.filter.cssparser", true);
+pref("devtools.webconsole.filter.exception", true);
+pref("devtools.webconsole.filter.jswarn", true);
+pref("devtools.webconsole.filter.error", true);
+pref("devtools.webconsole.filter.warn", true);
+pref("devtools.webconsole.filter.info", true);
+pref("devtools.webconsole.filter.log", true);
+
+// The number of lines that are displayed in the web console for the Net,
+// CSS, JS and Web Developer categories.
+pref("devtools.hud.loglimit.network", 200);
+pref("devtools.hud.loglimit.cssparser", 200);
+pref("devtools.hud.loglimit.exception", 200);
+pref("devtools.hud.loglimit.console", 200);
+
+// The developer tools editor configuration:
+// - tabsize: how many spaces to use when a Tab character is displayed.
+// - expandtab: expand Tab characters to spaces.
+pref("devtools.editor.tabsize", 4);
+pref("devtools.editor.expandtab", true);
+
+// Tells which component you want to use for source editing in developer tools.
+//
+// Available components:
+//   "orion" - this is the Orion source code editor from the Eclipse project. It
+//   provides programmer-specific editor features such as syntax highlighting,
+//   indenting and bracket recognition.
+pref("devtools.editor.component", "orion");
+
+// Whether the character encoding menu is under the main Firefox button. This
+// preference is a string so that localizers can alter it.
+pref("browser.menu.showCharacterEncoding", "chrome://browser/locale/browser.properties");
+
+// Allow using tab-modal prompts when possible.
+pref("prompts.tab_modal.enabled", true);
+// Whether the Panorama should animate going in/out of tabs
+pref("browser.panorama.animate_zoom", true);
+
+// Defines the url to be used for new tabs.
+pref("browser.newtab.url", "about:newtab");
+
+// Toggles the content of 'about:newtab'. Shows the grid when enabled.
+pref("browser.newtabpage.enabled", true);
+
+// Enable the DOM fullscreen API.
+pref("full-screen-api.enabled", true);
+
+// True if the fullscreen API requires approval upon a domain entering fullscreen.
+// Domains that have already had fullscreen permission granted won't re-request
+// approval.
+pref("full-screen-api.approval-required", true);
+
+// Startup Crash Tracking
+// number of startup crashes that can occur before starting into safe mode automatically
+// (this pref has no effect if more than 6 hours have passed since the last crash)
+pref("toolkit.startup.max_resumed_crashes", 3);
+
+// Completely disable pdf.js as an option to preview pdfs within firefox.
+// Note: if this is not disabled it does not necessarily mean pdf.js is the pdf
+// handler just that it is an option.
+pref("pdfjs.disabled", true);
+// Used by pdf.js to know the first time firefox is run with it installed so it
+// can become the default pdf viewer.
+pref("pdfjs.firstRun", true);
+// The values of preferredAction and alwaysAskBeforeHandling before pdf.js
+// became the default.
+pref("pdfjs.previousHandler.preferredAction", 0);
+pref("pdfjs.previousHandler.alwaysAskBeforeHandling", false);
+
+// The maximum amount of decoded image data we'll willingly keep around (we
+// might keep around more than this, but we'll try to get down to this value).
+// (This is intentionally on the high side; see bug 746055.)
+pref("image.mem.max_decoded_image_kb", 256000);
