Bug 678374 - Panorama/App Tabs: not shortcut icon; no icon if browser.chrome.favicons=false; r=ttaubert
authorRaymond Lee <raymond@raysquare.com>
Thu, 15 Mar 2012 00:24:03 +0800
changeset 89447 e7f495b11aa6e49bbd0bb24dcc5a6c55d70ac9ed
parent 89004 5cddc46dcaf3b8b869685a840a61ad4604702f61
child 89448 63612df321939448993c5d150c2a11880ea9ff3d
push id22256
push usergsharp@mozilla.com
push dateFri, 16 Mar 2012 04:41:46 +0000
treeherdermozilla-central@e5f6caa40409 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersttaubert
bugs678374
milestone14.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 678374 - Panorama/App Tabs: not shortcut icon; no icon if browser.chrome.favicons=false; r=ttaubert
browser/components/tabview/content.js
browser/components/tabview/groupitems.js
browser/components/tabview/tabitems.js
browser/components/tabview/test/Makefile.in
browser/components/tabview/test/browser_tabview_apptabs.js
browser/components/tabview/test/browser_tabview_bug595965.js
browser/components/tabview/test/browser_tabview_bug600645.js
browser/components/tabview/test/browser_tabview_bug610242.js
browser/components/tabview/test/browser_tabview_bug640765.js
browser/components/tabview/test/browser_tabview_bug678374.js
browser/components/tabview/test/browser_tabview_bug685476.js
browser/components/tabview/test/head.js
browser/components/tabview/test/test_bug678374.html
browser/components/tabview/test/test_bug678374_icon16.png
browser/components/tabview/ui.js
--- a/browser/components/tabview/content.js
+++ b/browser/components/tabview/content.js
@@ -89,21 +89,31 @@ let WindowMessageHandler = {
   // ----------
   // Function: isDocumentLoaded
   // Checks if the currently active document is loaded.
   isDocumentLoaded: function WMH_isDocumentLoaded(cx) {
     let isLoaded = (content.document.readyState == "complete" &&
                     !webProgress.isLoadingDocument);
 
     sendAsyncMessage(cx.name, {isLoaded: isLoaded});
+  },
+
+  // ----------
+  // Function: isImageDocument
+  // Checks if the currently active document is an image document or not.
+  isImageDocument: function WMH_isImageDocument(cx) {
+    let isImageDocument = (content.document instanceof Ci.nsIImageDocument);
+
+    sendAsyncMessage(cx.name, {isImageDocument: isImageDocument});
   }
 };
 
 // add message listeners
 addMessageListener("Panorama:isDocumentLoaded", WindowMessageHandler.isDocumentLoaded);
+addMessageListener("Panorama:isImageDocument", WindowMessageHandler.isImageDocument);
 
 // ----------
 // WebProgressListener
 //
 // Observe the web progress of content pages loaded into this browser. When the
 // state of a page changes we check if we're still allowed to store page
 // information permanently.
 let WebProgressListener = {
--- a/browser/components/tabview/groupitems.js
+++ b/browser/components/tabview/groupitems.js
@@ -229,20 +229,22 @@ function GroupItem(listOfEls, options) {
   // ___ 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);
 
-  AllTabs.tabs.forEach(function(xulTab) {
+  let pinnedTabCount = gBrowser._numPinnedTabs;
+  AllTabs.tabs.forEach(function (xulTab, index) {
+    // only adjust tray when it's the last app tab.
     if (xulTab.pinned)
-      self.addAppTab(xulTab, {dontAdjustTray: true});
-  });
+      this.addAppTab(xulTab, {dontAdjustTray: index + 1 < pinnedTabCount});
+  }, this);
 
   // ___ Undo Close
   this.$undoContainer = null;
   this._undoButtonTimeoutId = null;
 
   // ___ Superclass initialization
   this._init($container[0]);
 
@@ -753,17 +755,17 @@ GroupItem.prototype = Utils.extend(new I
     let self = this;
 
     let finalize = function () {
       self._children.forEach(function(child) {
         iQ(child.container).show();
       });
 
       UI.setActive(self);
-      self._sendToSubscribers("groupShown", { groupItemId: self.id });
+      self._sendToSubscribers("groupShown");
     };
 
     let $container = iQ(this.container).show();
 
     if (!options || !options.immediately) {
       $container.animate({
         "-moz-transform": "scale(1)",
         "opacity": 1
@@ -922,17 +924,17 @@ GroupItem.prototype = Utils.extend(new I
     setTimeout(function() {
       self.$undoContainer.animate({
         "-moz-transform": "scale(1)",
         "opacity": 1
       }, {
         easing: "tabviewBounce",
         duration: 170,
         complete: function() {
-          self._sendToSubscribers("groupHidden", { groupItemId: self.id });
+          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])
@@ -1047,17 +1049,17 @@ GroupItem.prototype = Utils.extend(new I
             (!GroupItems.getActiveGroupItem() && !item.tab.hidden))
           UI.setActive(this);
       }
 
       if (!options.dontArrange)
         this.arrange({animate: !options.immediately});
 
       this._unfreezeItemSize({dontArrange: true});
-      this._sendToSubscribers("childAdded",{ groupItemId: this.id, item: item });
+      this._sendToSubscribers("childAdded", { item: item });
 
       UI.setReorderTabsOnHide(this);
     } catch(e) {
       Utils.log('GroupItem.add error', e);
     }
   },
 
   // ----------
@@ -1150,17 +1152,17 @@ GroupItem.prototype = Utils.extend(new I
           (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",{ groupItemId: this.id, item: item });
+      this._sendToSubscribers("childRemoved", { item: item });
     } catch(e) {
       Utils.log(e);
     }
   },
 
   // ----------
   // Function: removeAll
   // Removes all of the groupItem's children.
@@ -1176,46 +1178,56 @@ GroupItem.prototype = Utils.extend(new I
       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:
-  //   dontAdjustTray - (boolean) if true, the $appTabTray size is not adjusted,
-  //                    which means that the adjustAppTabTray() method is not
-  //                    called.
+  //   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) {
-    let self = this;
+    GroupItems.getAppTabFavIconUrl(xulTab, function(iconUrl) {
+      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;
 
-    let iconUrl = GroupItems.getAppTabFavIconUrl(xulTab);
-    let $appTab = iQ("<img>")
-      .addClass("appTabIcon")
-      .attr("src", iconUrl)
-      .data("xulTab", xulTab)
-      .appendTo(this.$appTabTray)
-      .mousedown(function onAppTabMousedown(event) {
-        // stop mousedown propagation to disable group dragging on app tabs
-        event.stopPropagation();
-      })
-      .click(function(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;
 
-        UI.setActive(self, { dontSetActiveTabInGroup: true });
-        UI.goToTab(iQ(this).data("xulTab"));
-      });
+        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);
 
-    // adjust the tray, if needed.
-    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);
@@ -2073,42 +2085,32 @@ let GroupItems = {
 
   // ----------
   // Function: _updateAppTabIcons
   // Update images of any apptab icons that point to passed in xultab 
   _updateAppTabIcons: function GroupItems__updateAppTabIcons(xulTab) {
     if (!xulTab.pinned)
       return;
 
-    let iconUrl = this.getAppTabFavIconUrl(xulTab);
-    this.groupItems.forEach(function(groupItem) {
-      iQ(".appTabIcon", groupItem.$appTabTray).each(function(icon) {
-        let $icon = iQ(icon);
-        if ($icon.data("xulTab") != xulTab)
-          return true;
-
-        if (iconUrl != $icon.attr("src"))
-          $icon.attr("src", iconUrl);
-        return false;
+    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) {
-    let iconUrl;
-
-    if (UI.shouldLoadFavIcon(xulTab.linkedBrowser))
-      iconUrl = UI.getFavIconUrlForTab(xulTab);
-    else
-      iconUrl = gFavIconService.defaultFavicon.spec;
-
-    return iconUrl;
+  getAppTabFavIconUrl: function GroupItems_getAppTabFavIconUrl(xulTab, callback) {
+    UI.getFavIconUrlForTab(xulTab, function GroupItems_getAppTabFavIconUrl_getFavIconUrlForTab(iconUrl) {
+      callback(iconUrl || gFavIconService.defaultFavicon.spec);
+    });
   },
 
   // ----------
   // 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);
--- a/browser/components/tabview/tabitems.js
+++ b/browser/components/tabview/tabitems.js
@@ -980,30 +980,31 @@ let TabItems = {
 
       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
-      if (UI.shouldLoadFavIcon(tab.linkedBrowser)) {
-        let iconUrl = UI.getFavIconUrlForTab(tab);
-
-        if (tabItem.$favImage[0].src != iconUrl)
-          tabItem.$favImage[0].src = iconUrl;
-
-        iQ(tabItem.$fav[0]).show();
-      } else {
-        if (tabItem.$favImage[0].hasAttribute("src"))
-          tabItem.$favImage[0].removeAttribute("src");
-        iQ(tabItem.$fav[0]).hide();
-      }
+      UI.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
--- a/browser/components/tabview/test/Makefile.in
+++ b/browser/components/tabview/test/Makefile.in
@@ -153,16 +153,17 @@ include $(topsrcdir)/config/rules.mk
                  browser_tabview_bug656913.js \
                  browser_tabview_bug662266.js \
                  browser_tabview_bug663421.js \
                  browser_tabview_bug665502.js \
                  browser_tabview_bug669694.js \
                  browser_tabview_bug673196.js \
                  browser_tabview_bug673729.js \
                  browser_tabview_bug677310.js \
+                 browser_tabview_bug678374.js \
                  browser_tabview_bug679853.js \
                  browser_tabview_bug681599.js \
                  browser_tabview_bug685476.js \
                  browser_tabview_bug685692.js \
                  browser_tabview_bug686654.js \
                  browser_tabview_bug696602.js \
                  browser_tabview_bug697390.js \
                  browser_tabview_bug705621.js \
@@ -187,14 +188,16 @@ include $(topsrcdir)/config/rules.mk
                  browser_tabview_thumbnail_storage.js \
                  browser_tabview_undo_group.js \
                  dummy_page.html \
                  head.js \
                  search1.html \
                  search2.html \
                  test_bug600645.html \
                  test_bug644097.html \
+                 test_bug678374.html \
+                 test_bug678374_icon16.png \
                  $(NULL)
 
 # browser_tabview_bug597980.js is disabled for leaking, see bug 711907
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
--- a/browser/components/tabview/test/browser_tabview_apptabs.js
+++ b/browser/components/tabview/test/browser_tabview_apptabs.js
@@ -1,23 +1,21 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function test() {
   waitForExplicitFinish();
 
-  window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
-  TabView.toggle();
+  showTabView(onTabViewWindowLoaded);
 }
 
 function onTabViewWindowLoaded() {
-  window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
   ok(TabView.isVisible(), "Tab View is visible");
 
-  let contentWindow = document.getElementById("tab-view").contentWindow;
+  let contentWindow = TabView.getContentWindow();
 
   // establish initial state
   is(contentWindow.GroupItems.groupItems.length, 1,
       "we start with one group (the default)");
   is(gBrowser.tabs.length, 1, "we start with one tab");
   let originalTab = gBrowser.tabs[0];
 
   // create a group
@@ -31,67 +29,68 @@ function onTabViewWindowLoaded() {
   let xulTab = gBrowser.loadOneTab("about:blank");
   is(gBrowser.tabs.length, 2, "we now have two tabs");
   is(groupItemOne._children.length, 1, "the new tab was added to the group");
 
   // make sure the group has no app tabs
   is(appTabCount(groupItemOne), 0, "there are no app tab icons");
 
   // pin the tab, make sure the TabItem goes away and the icon comes on
-  gBrowser.pinTab(xulTab);
-  is(groupItemOne._children.length, 0,
-      "the app tab's TabItem was removed from the group");
-  is(appTabCount(groupItemOne), 1, "there's now one app tab icon");
+  whenAppTabIconAdded(function() {
+    is(groupItemOne._children.length, 0,
+       "the app tab's TabItem was removed from the group");
+    is(appTabCount(groupItemOne), 1, "there's now one app tab icon");
 
-  // create a second group and make sure it gets the icon too
-  box.offset(box.width + 20, 0);
-  let groupItemTwo = new contentWindow.GroupItem([],
-      { bounds: box, title: "test2" });
-  is(contentWindow.GroupItems.groupItems.length, 3, "we now have three groups");
-  is(appTabCount(groupItemTwo), 1,
-      "there's an app tab icon in the second group");
+    // create a second group and make sure it gets the icon too
+    box.offset(box.width + 20, 0);
+    let groupItemTwo = new contentWindow.GroupItem([],
+        { bounds: box, title: "test2" });
+    whenAppTabIconAdded(function() {
+      is(contentWindow.GroupItems.groupItems.length, 3, "we now have three groups");
+      is(appTabCount(groupItemTwo), 1,
+         "there's an app tab icon in the second group");
 
-  // When the tab was pinned, the last active group with an item got the focus.
-  // Therefore, switching the focus back to group item one.
-  contentWindow.UI.setActive(groupItemOne);
+      // When the tab was pinned, the last active group with an item got the focus.
+      // Therefore, switching the focus back to group item one.
+      contentWindow.UI.setActive(groupItemOne);
 
-  // unpin the tab, make sure the icon goes away and the TabItem comes on
-  gBrowser.unpinTab(xulTab);
-  is(groupItemOne._children.length, 1, "the app tab's TabItem is back");
-  is(appTabCount(groupItemOne), 0, "the icon is gone from group one");
-  is(appTabCount(groupItemTwo), 0, "the icon is gone from group 2");
+      // unpin the tab, make sure the icon goes away and the TabItem comes on
+      gBrowser.unpinTab(xulTab);
+      is(groupItemOne._children.length, 1, "the app tab's TabItem is back");
+      is(appTabCount(groupItemOne), 0, "the icon is gone from group one");
+      is(appTabCount(groupItemTwo), 0, "the icon is gone from group two");
 
-  // pin the tab again
-  gBrowser.pinTab(xulTab);
+      whenAppTabIconAdded(function() {
+        // close the second group
+        groupItemTwo.close();
 
-  // close the second group
-  groupItemTwo.close();
+        // find app tab in group and hit it
+        whenTabViewIsHidden(function() {
+          ok(!TabView.isVisible(),
+             "Tab View is hidden because we clicked on the app tab");
 
-  // find app tab in group and hit it
-  let onTabViewHidden = function() {
-    window.removeEventListener("tabviewhidden", onTabViewHidden, false);
-    ok(!TabView.isVisible(),
-        "Tab View is hidden because we clicked on the app tab");
+          // delete the app tab and make sure its icon goes away
+          gBrowser.removeTab(xulTab);
+          is(appTabCount(groupItemOne), 0, "closing app tab removes its icon");
 
-    // delete the app tab and make sure its icon goes away
-    gBrowser.removeTab(xulTab);
-    is(appTabCount(groupItemOne), 0, "closing app tab removes its icon");
+          // clean up
+          groupItemOne.close();
 
-    // clean up
-    groupItemOne.close();
+          is(contentWindow.GroupItems.groupItems.length, 1,
+             "we finish with one group");
+          is(gBrowser.tabs.length, 1, "we finish with one tab");
+          ok(!TabView.isVisible(), "we finish with Tab View not visible");
 
-    is(contentWindow.GroupItems.groupItems.length, 1,
-        "we finish with one group");
-    is(gBrowser.tabs.length, 1, "we finish with one tab");
-    ok(!TabView.isVisible(), "we finish with Tab View not visible");
+          finish();
+        });
 
-    finish();
-  };
-
-  window.addEventListener("tabviewhidden", onTabViewHidden, false);
-
-  let appTabIcons = groupItemOne.container.getElementsByClassName("appTabIcon");
-  EventUtils.sendMouseEvent({ type: "click" }, appTabIcons[0], contentWindow);
+        let appTabIcons = groupItemOne.container.getElementsByClassName("appTabIcon");
+        EventUtils.sendMouseEvent({ type: "click" }, appTabIcons[0], contentWindow);
+      });
+      gBrowser.pinTab(xulTab);
+    });
+  });
+  gBrowser.pinTab(xulTab);
 }
 
 function appTabCount(groupItem) {
   return groupItem.container.getElementsByClassName("appTabIcon").length;
 }
--- a/browser/components/tabview/test/browser_tabview_bug595965.js
+++ b/browser/components/tabview/test/browser_tabview_bug595965.js
@@ -48,95 +48,106 @@ function onTabViewShown(win) {
 
   let tray = groupItem.$appTabTray;
   let trayContainer = iQ(tray[0].parentNode);
 
   is(parseInt(trayContainer.css("width")), 0,
      "$appTabTray container is not visible");
 
   // pin the tab, make sure the TabItem goes away and the icon comes on
-  gBrowser.pinTab(xulTabs[0]);
-  is(groupItem._children.length, 0,
-     "the app tab's TabItem was removed from the group");
-  is(appTabCount(groupItem), 1, "there's now one app tab icon");
+  whenAppTabIconAdded(function() {
+    is(groupItem._children.length, 0,
+       "the app tab's TabItem was removed from the group");
+    is(appTabCount(groupItem), 1, "there's now one app tab icon");
+
+    is(tray.css("-moz-column-count"), 1,
+       "$appTabTray column count is 1");
+    isnot(parseInt(trayContainer.css("width")), 0,
+       "$appTabTray container is visible");
+
 
-  is(tray.css("-moz-column-count"), 1,
-     "$appTabTray column count is 1");
-  isnot(parseInt(trayContainer.css("width")), 0,
-     "$appTabTray container is visible");
+    let iconHeight = iQ(iQ(".appTabIcon", tray)[0]).height();
+    let trayHeight = parseInt(trayContainer.css("height"));
+    let rows = Math.floor(trayHeight / iconHeight);
+    let icons = rows * 2;
 
-  let iconHeight = iQ(iQ(".appTabIcon", tray)[0]).height();
-  let trayHeight = parseInt(trayContainer.css("height"));
-  let rows = Math.floor(trayHeight / iconHeight);
-  let icons = rows * 2;
+    function pinnedSomeTabs() {
+      is(appTabCount(groupItem), icons, "number of app tab icons is correct");
+
+      is(tray.css("-moz-column-count"), 2,
+         "$appTabTray column count is 2");
 
-  // add enough tabs to have two columns
-  for (let i = 1; i < icons; i++) {
-    xulTabs.push(gBrowser.loadOneTab("about:blank"));
-    gBrowser.pinTab(xulTabs[i]);
-  }
+      ok(!trayContainer.hasClass("appTabTrayContainerTruncated"),
+         "$appTabTray container does not have .appTabTrayContainerTruncated");
 
-  is(appTabCount(groupItem), icons, "number of app tab icons is correct");
+      // add one more tab
+      xulTabs.push(gBrowser.loadOneTab("about:blank"));
+      whenAppTabIconAdded(function() {
+        is(tray.css("-moz-column-count"), 3,
+           "$appTabTray column count is 3");
 
-  is(tray.css("-moz-column-count"), 2,
-     "$appTabTray column count is 2");
+        ok(trayContainer.hasClass("appTabTrayContainerTruncated"),
+           "$appTabTray container hasClass .appTabTrayContainerTruncated");
 
-  ok(!trayContainer.hasClass("appTabTrayContainerTruncated"),
-     "$appTabTray container does not have .appTabTrayContainerTruncated");
+        // remove all but one app tabs
+        for (let i = 1; i < xulTabs.length; i++)
+          gBrowser.removeTab(xulTabs[i]);
 
-  // add one more tab
-  xulTabs.push(gBrowser.loadOneTab("about:blank"));
-  gBrowser.pinTab(xulTabs[xulTabs.length-1]);
+        is(tray.css("-moz-column-count"), 1,
+           "$appTabTray column count is 1");
 
-  is(tray.css("-moz-column-count"), 3,
-     "$appTabTray column count is 3");
+        is(appTabCount(groupItem), 1, "there's now one app tab icon");
 
-  ok(trayContainer.hasClass("appTabTrayContainerTruncated"),
-     "$appTabTray container hasClass .appTabTrayContainerTruncated");
+        ok(!trayContainer.hasClass("appTabTrayContainerTruncated"),
+           "$appTabTray container does not have .appTabTrayContainerTruncated");
 
-  // remove all but one app tabs
-  for (let i = 1; i < xulTabs.length; i++)
-    gBrowser.removeTab(xulTabs[i]);
+        // unpin the last remaining tab
+        gBrowser.unpinTab(xulTabs[0]);
 
-  is(tray.css("-moz-column-count"), 1,
-     "$appTabTray column count is 1");
+        is(parseInt(trayContainer.css("width")), 0,
+           "$appTabTray container is not visible");
 
-  is(appTabCount(groupItem), 1, "there's now one app tab icon");
+        // When the tab was pinned, the last active group with an item got the focus.
+        // Therefore, switching the focus back to group item one.
+        contentWindow.UI.setActive(groupItem);
 
-  ok(!trayContainer.hasClass("appTabTrayContainerTruncated"),
-     "$appTabTray container does not have .appTabTrayContainerTruncated");
+        is(appTabCount(groupItem), 0, "there are no app tab icons");
+
+        is(groupItem._children.length, 1, "the normal tab shows in the group");
+
+        gBrowser.removeTab(xulTabs[0]);
 
-  // When the tab was pinned, the last active group with an item got the focus.
-  // Therefore, switching the focus back to group item one.
-  contentWindow.UI.setActive(groupItem);
+        // close the group
+        groupItem.close();
 
-  // unpin the last remaining tab
-  gBrowser.unpinTab(xulTabs[0]);
+        hideTabView(function() {
+          ok(!TabView.isVisible(), "Tab View is hidden");
 
-  is(parseInt(trayContainer.css("width")), 0,
-     "$appTabTray container is not visible");
+          is(contentWindow.GroupItems.groupItems.length, 1,
+             "we finish with one group");
+          is(gBrowser.tabs.length, 1, "we finish with one tab");
 
-  is(appTabCount(groupItem), 0, "there are no app tab icons");
+          win.close();
 
-  is(groupItem._children.length, 1, "the normal tab shows in the group");
-
-  gBrowser.removeTab(xulTabs[0]);
-
-  // close the group
-  groupItem.close();
+          executeSoon(finish);
+        }, win);
+      }, win);
+      win.gBrowser.pinTab(xulTabs[xulTabs.length-1]);
+    };
 
-  hideTabView(function() {
-    ok(!TabView.isVisible(), "Tab View is hidden");
-
-    is(contentWindow.GroupItems.groupItems.length, 1,
-       "we finish with one group");
-    is(gBrowser.tabs.length, 1, "we finish with one tab");
-
-    win.close();
-
-    executeSoon(finish);
+    // add enough tabs to have two columns
+    let returnCount = 0;
+    for (let i = 1; i < icons; i++) {
+      xulTabs.push(gBrowser.loadOneTab("about:blank"));
+      whenAppTabIconAdded(function() {
+        if (++returnCount == (icons - 1))
+          executeSoon(pinnedSomeTabs);
+      }, win);
+      win.gBrowser.pinTab(xulTabs[i]);
+    }
   }, win);
+  win.gBrowser.pinTab(xulTabs[0]);
 }
 
 function appTabCount(groupItem) {
   return groupItem.container.getElementsByClassName("appTabIcon").length;
 }
 
--- a/browser/components/tabview/test/browser_tabview_bug600645.js
+++ b/browser/components/tabview/test/browser_tabview_bug600645.js
@@ -5,25 +5,24 @@ const fi = Cc["@mozilla.org/browser/favi
            getService(Ci.nsIFaviconService);
 
 let newTab;
 
 function test() {
   waitForExplicitFinish();
 
   newTab = gBrowser.addTab();
-  gBrowser.pinTab(newTab);
 
-  window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
-  TabView.toggle();
+  showTabView(function() {
+    whenAppTabIconAdded(onTabPinned);
+    gBrowser.pinTab(newTab);
+  })
 }
 
-function onTabViewWindowLoaded() {
-  window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
-
+function onTabPinned() {
   let contentWindow = document.getElementById("tab-view").contentWindow;
   is(contentWindow.GroupItems.groupItems.length, 1, 
      "There is one group item on startup");
 
   let groupItem = contentWindow.GroupItems.groupItems[0];
   let icon = contentWindow.iQ(".appTabIcon", groupItem.$appTabTray)[0];
   let $icon = contentWindow.iQ(icon);
 
--- a/browser/components/tabview/test/browser_tabview_bug610242.js
+++ b/browser/components/tabview/test/browser_tabview_bug610242.js
@@ -24,16 +24,17 @@ function onTabViewWindowLoaded(win) {
   contentWindow.UI.setActive(group);
   is(contentWindow.GroupItems.getActiveGroupItem(), group, "new group is active");
   
   // Create a bunch of tabs in the group
   let bg = {inBackground: true};
   let datatext = win.gBrowser.loadOneTab("data:text/plain,bug610242", bg);
   let datahtml = win.gBrowser.loadOneTab("data:text/html,<blink>don't blink!</blink>", bg);
   let mozilla  = win.gBrowser.loadOneTab("about:mozilla", bg);
+  let robots   = win.gBrowser.loadOneTab("about:robots", bg);
   let html     = win.gBrowser.loadOneTab("http://example.com", bg);
   let png      = win.gBrowser.loadOneTab("http://mochi.test:8888/browser/browser/base/content/test/moz.png", bg);
   let svg      = win.gBrowser.loadOneTab("http://mochi.test:8888/browser/browser/base/content/test/title_test.svg", bg);
   
   ok(!group.shouldStack(group._children.length), "Group should not stack.");
   
   // PREPARE FINISH:
   group.addSubscriber("close", function onClose() {
@@ -41,44 +42,55 @@ function onTabViewWindowLoaded(win) {
 
     ok(group.isEmpty(), "The group is empty again");
 
     contentWindow.UI.setActive(currentGroup);
     isnot(contentWindow.GroupItems.getActiveGroupItem(), null, "There is an active group");
     is(win.gBrowser.tabs.length, 1, "There is only one tab left");
     is(win.gBrowser.visibleTabs.length, 1, "There is also only one visible tab");
 
-    let onTabViewHidden = function() {
-      win.removeEventListener("tabviewhidden", onTabViewHidden, false);
+    whenTabViewIsHidden(function() {
       win.close();
       ok(win.closed, "new window is closed");
       finish();
-    };
-    win.addEventListener("tabviewhidden", onTabViewHidden, false);
+    }, win);
     win.gBrowser.selectedTab = originalTab;
 
     win.TabView.hide();
   });
 
   function check(tab, label, visible) {
     let display = contentWindow.getComputedStyle(tab._tabViewTabItem.$fav[0], null).getPropertyValue("display");
     if (visible) {
       is(display, "block", label + " has favicon");
     } else {
       is(display, "none", label + " has no favicon");
     }
   }
 
-  afterAllTabsLoaded(function() {
-    afterAllTabItemsUpdated(function() {
-      check(datatext, "datatext", false);
-      check(datahtml, "datahtml", false);
-      check(mozilla, "about:mozilla", true);
-      check(html, "html", true);
-      check(png, "png", false);
-      check(svg, "svg", true);
-  
-      // Get rid of the group and its children
-      // The group close will trigger a finish().
-      closeGroupItem(group);
-    }, win);  
-  }, win);
+  let children = group.getChildren();
+  let len = children.length;
+  let iconUpdateCounter = 0;
+
+  children.forEach(function(tabItem) {
+    tabItem.addSubscriber("iconUpdated", function onIconUpdated() {
+      // the tab is not loaded completely so ignore it.
+      if (tabItem.tab.linkedBrowser.currentURI.spec == "about:blank")
+        return;
+
+      tabItem.removeSubscriber("iconUpdated", onIconUpdated);
+
+      if (++iconUpdateCounter == len) {
+        check(datatext, "datatext", false);
+        check(datahtml, "datahtml", false);
+        check(mozilla, "about:mozilla", false);
+        check(robots, "about:robots", true);
+        check(html, "html", true);
+        check(png, "png", false);
+        check(svg, "svg", true);
+
+        // Get rid of the group and its children
+        // The group close will trigger a finish().
+        closeGroupItem(group);
+      }
+    });
+  });
 }
--- a/browser/components/tabview/test/browser_tabview_bug640765.js
+++ b/browser/components/tabview/test/browser_tabview_bug640765.js
@@ -6,63 +6,69 @@ let groupItem;
 
 function test() {
   waitForExplicitFinish();
 
   let newTabOne = gBrowser.addTab();
   let newTabTwo = gBrowser.addTab();
   let newTabThree = gBrowser.addTab();
 
-  gBrowser.pinTab(newTabOne);
-  gBrowser.pinTab(newTabTwo);
-  gBrowser.pinTab(newTabThree);
-
   registerCleanupFunction(function() {
     TabView.hide();
     while (gBrowser.tabs.length > 1)
       gBrowser.removeTab(gBrowser.tabs[0]);
   });
 
   showTabView(function() {
     contentWindow = document.getElementById("tab-view").contentWindow;
     is(contentWindow.GroupItems.groupItems.length, 1, "Has only one group");
 
     groupItem = contentWindow.GroupItems.groupItems[0];
 
-    is(xulTabForAppTabIcon(0), newTabOne,
-       "New tab one matches the first app tab icon in tabview");
-    is(xulTabForAppTabIcon(1), newTabTwo,
-       "New tab two matches the second app tab icon in tabview");
-    is(xulTabForAppTabIcon(2), newTabThree,
-       "New tab three matches the third app tab icon in tabview");
+    whenAppTabIconAdded(function() {
+      whenAppTabIconAdded(function() {
+        whenAppTabIconAdded(function() {
 
-    // move the last tab to the first position
-    gBrowser.moveTabTo(newTabThree, 0);
-    is(xulTabForAppTabIcon(0), newTabThree,
-       "New tab three matches the first app tab icon in tabview");
-    is(xulTabForAppTabIcon(1), newTabOne,
-       "New tab one matches the second app tab icon in tabview");
-    is(xulTabForAppTabIcon(2), newTabTwo,
-       "New tab two matches the third app tab icon in tabview");
+          is(xulTabForAppTabIcon(0), newTabOne,
+            "New tab one matches the first app tab icon in tabview");
+          is(xulTabForAppTabIcon(1), newTabTwo,
+            "New tab two matches the second app tab icon in tabview");
+          is(xulTabForAppTabIcon(2), newTabThree,
+            "New tab three matches the third app tab icon in tabview");
+
+          // move the last tab to the first position
+          gBrowser.moveTabTo(newTabThree, 0);
+          is(xulTabForAppTabIcon(0), newTabThree,
+            "New tab three matches the first app tab icon in tabview");
+          is(xulTabForAppTabIcon(1), newTabOne,
+            "New tab one matches the second app tab icon in tabview");
+          is(xulTabForAppTabIcon(2), newTabTwo,
+            "New tab two matches the third app tab icon in tabview");
 
-    // move the first tab to the second position
-    gBrowser.moveTabTo(newTabThree, 1);
-    is(xulTabForAppTabIcon(0), newTabOne,
-       "New tab one matches the first app tab icon in tabview");
-    is(xulTabForAppTabIcon(1), newTabThree,
-       "New tab three matches the second app tab icon in tabview");
-    is(xulTabForAppTabIcon(2), newTabTwo,
-       "New tab two matches the third app tab icon in tabview");
+          // move the first tab to the second position
+          gBrowser.moveTabTo(newTabThree, 1);
+          is(xulTabForAppTabIcon(0), newTabOne,
+            "New tab one matches the first app tab icon in tabview");
+          is(xulTabForAppTabIcon(1), newTabThree,
+            "New tab three matches the second app tab icon in tabview");
+          is(xulTabForAppTabIcon(2), newTabTwo,
+            "New tab two matches the third app tab icon in tabview");
 
-    hideTabView(function() {
-      gBrowser.removeTab(newTabOne);
-      gBrowser.removeTab(newTabTwo);
-      gBrowser.removeTab(newTabThree);
-      finish();
+          hideTabView(function() {
+            gBrowser.removeTab(newTabOne);
+            gBrowser.removeTab(newTabTwo);
+            gBrowser.removeTab(newTabThree);
+            finish();
+          });
+        });
+        gBrowser.pinTab(newTabThree);
+      });
+      gBrowser.pinTab(newTabTwo);
     });
+    gBrowser.pinTab(newTabOne);
   });
 }
 
 function xulTabForAppTabIcon(index) {
     return contentWindow.iQ(
              contentWindow.iQ(".appTabIcon", 
                               groupItem.$appTabTray)[index]).data("xulTab");
 }
new file mode 100644
--- /dev/null
+++ b/browser/components/tabview/test/browser_tabview_bug678374.js
@@ -0,0 +1,45 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const ICON_URL = "moz-anno:favicon:http://example.com/browser/browser/components/tabview/test/test_bug678374_icon16.png";
+const TEST_URL = "http://example.com/browser/browser/components/tabview/test/test_bug678374.html";
+
+function test() {
+  Services.prefs.setBoolPref("browser.chrome.favicons", false);
+
+  waitForExplicitFinish();
+
+  newWindowWithTabView(function(win) {
+    is(win.gBrowser.tabs.length, 3, "There are 3 tabs")
+
+    let newTabOne = win.gBrowser.tabs[1];
+    let newTabTwo = win.gBrowser.tabs[2];
+    let cw = win.TabView.getContentWindow();
+    let groupItem = cw.GroupItems.groupItems[0];
+
+    // test tab item
+    let newTabItemOne = newTabOne._tabViewTabItem;
+
+    newTabItemOne.addSubscriber("iconUpdated", function onIconUpdated() {
+      newTabItemOne.removeSubscriber("iconUpdated", onIconUpdated);
+      is(newTabItemOne.$favImage[0].src, ICON_URL, "The tab item is showing the right icon.");
+
+      // test pin tab
+      whenAppTabIconAdded(function() {
+        let icon = cw.iQ(".appTabIcon", groupItem.$appTabTray)[0];
+        is(icon.src, ICON_URL, "The app tab is showing the right icon");
+
+        finish();
+      }, win);
+      win.gBrowser.pinTab(newTabTwo);
+    });
+  }, function(win) {
+    registerCleanupFunction(function() { 
+      Services.prefs.clearUserPref("browser.chrome.favicons");
+      win.close(); 
+   });
+
+    win.gBrowser.loadOneTab(TEST_URL);
+    win.gBrowser.loadOneTab(TEST_URL);
+  });
+}
--- a/browser/components/tabview/test/browser_tabview_bug685476.js
+++ b/browser/components/tabview/test/browser_tabview_bug685476.js
@@ -1,24 +1,26 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function test() {
   waitForExplicitFinish();
 
   showTabView(function () {
     let tab = gBrowser.addTab();
-    gBrowser.pinTab(tab);
     registerCleanupFunction(function () gBrowser.removeTab(tab));
 
-    let cw = TabView.getContentWindow();
-    let body = cw.document.body;
-    let [appTabIcon] = cw.iQ(".appTabTray .appTabIcon");
+    whenAppTabIconAdded(function() {
+      let cw = TabView.getContentWindow();
+      let body = cw.document.body;
+      let [appTabIcon] = cw.iQ(".appTabTray .appTabIcon");
 
-    EventUtils.synthesizeMouseAtCenter(appTabIcon, {type: "mousedown"}, cw);
-    EventUtils.synthesizeMouse(body, 500, 100, {type: "mousemove"}, cw);
-    EventUtils.synthesizeMouse(body, 500, 100, {type: "mouseup"}, cw);
+      EventUtils.synthesizeMouseAtCenter(appTabIcon, {type: "mousedown"}, cw);
+      EventUtils.synthesizeMouse(body, 500, 100, {type: "mousemove"}, cw);
+      EventUtils.synthesizeMouse(body, 500, 100, {type: "mouseup"}, cw);
 
-    ok(TabView.isVisible(), "tabview is still visible");
+      ok(TabView.isVisible(), "tabview is still visible");
 
-    hideTabView(finish);
+      hideTabView(finish);
+    });
+    gBrowser.pinTab(tab);
   });
 }
--- a/browser/components/tabview/test/head.js
+++ b/browser/components/tabview/test/head.js
@@ -380,8 +380,22 @@ function togglePrivateBrowsing(callback)
     executeSoon(function () afterAllTabsLoaded(callback));
   }, topic, false);
 
   let pb = Cc["@mozilla.org/privatebrowsing;1"].
            getService(Ci.nsIPrivateBrowsingService);
 
   pb.privateBrowsingEnabled = !pb.privateBrowsingEnabled;
 }
+
+// ----------
+function whenAppTabIconAdded(callback, win) {
+  win = win || window;
+
+  let contentWindow = win.TabView.getContentWindow();
+  let groupItems = contentWindow.GroupItems.groupItems;
+  let groupItem = groupItems[(groupItems.length - 1)];
+
+  groupItem.addSubscriber("appTabIconAdded", function onAppTabIconAdded() {
+    groupItem.removeSubscriber("appTabIconAdded", onAppTabIconAdded);
+    callback();
+  });
+}
new file mode 100644
--- /dev/null
+++ b/browser/components/tabview/test/test_bug678374.html
@@ -0,0 +1,7 @@
+<html>
+  <head>
+    <title>Bug 678374</title>
+    <link rel="icon" type="image/png" id="favicon" href="test_bug678374_icon16.png" />
+  <body>
+  </body>
+</html>
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..bc317a8d5fc3121c939e2bc9a17f5d024b709a12
GIT binary patch
literal 924
zc$@*817rM&P)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV000AHNkl<Zc-mc(
zeMsB|7{{NxvwQL8{qFtMd-Hd{J3a3_@AQ0|Zb~^TaRz2987(%p1>1t&a4q(ah$gW%
z8~dwZ5J60-O=1aaVdNh|ZlkDy8`LrwLK_O@pcwJ#7ZFl_e0lzPKHu;6fk%M<E4y&}
z-uS66ZY`c!TK)OMmGwLG_a9#Q{O;!H9}xO?Z{qrWF}`<O{sXUU{QlO!<W&-H&a?C4
zb@qPwJx_nOOwXmS2~J!kw*SI|^H*-Y1jx+VpCadOZW%26@XMTK*CdAWG=&2n(J^(I
z>SO0|Jo7eH{o{oC$B^hSV(sDJiF4<UEN@6+k$((+THl;76ptc~L89F!P_`dKY21gW
zw4Xrfc|wgt#2X(aQE11n77>XdyT@MBCNGKri5)X*Nb_mDp)UMQeK?A{aAjKY*0$iy
zwiC#;<Ew4LSJ#X)QAa%Af<4uRsdczoZv4uW41J8$jNp#uN#sk!3p%I+POS+?s)Q|;
z$5Nfcmu<lsOyLSA@fUiq^&UPgCl?<qi{~gNF;S@mX=v*}Eq7t9&gx)}%6J~Xo~*>P
z_%bE5awie39=BRT5}h|?czkY6#6l>lQE2Z#iKGyZhB=tQ9Livg)Df<4rn&bCwB`<y
zdOLThhN^&yWcoH_;Pu(NA~q5!mF|Hj$+qo4suCF8Nh|>kqo;;Pd@0I(1El{pXei@~
z)*%&M47vW_<j|RKz7pv}O(zK_Yp5+X5l-tjdsR%%gg$F9DlxW&G_+z1s@}lvj$pD!
zFcbz?<-)@1aglz4@j8;VDuHMmHJ8;>Y7tA23TIrOAwAJYEL*~0^&(<Ilr+*hG9wSs
zPimV)#6f+dMoi1-%Zi>5MvPvp&L~yBYCMqy4!?>R6h!P8)4fRltDOSK{EhX4V(TT~
zbK(xf5vkJULJVGPb`QnvWikzU6h*;ev}5*Wsd((jB^f%o^~%pqUps4bw&KzOt2L<0
zhuPxCY<3cfso3@ECNZ-uki|MM`kjnkHp?rYY#F?`v?($&JL<~q!5=Q+^QZ87V|d(A
z?3Dprfm)o!-B<_Tn3coto8_Gqk$3NG8H~*RC~|yBtS^6>wmmgA<=A=Xnyc-(d#<)U
y>yGXND?0drY454WW#6>O3ui^fmPDq$mA?Qdy0Sx%{P*|(0000<MNUMnLSTZyBg9Go
--- a/browser/components/tabview/ui.js
+++ b/browser/components/tabview/ui.js
@@ -46,16 +46,22 @@
 // Title: ui.js
 
 let Keys = { meta: false };
 
 // ##########
 // Class: UI
 // Singleton top-level UI manager.
 let UI = {
+  // 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",
+
   // Variable: _frameInitialized
   // True if the Tab View UI frame has been initialized.
   _frameInitialized: false,
 
   // Variable: _pageBounds
   // Stores the page bounds.
   _pageBounds: null,
 
@@ -136,16 +142,22 @@ let UI = {
   // 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,
 
+  // Used to keep track of the browser.chrome.site_icons pref value.
+  _prefSiteIcons: null,
+
+  // Used to keep track of the browser.chrome.favicons pref value.
+  _prefFavicons: null,
+
   // ----------
   // Function: toString
   // Prints [UI] for debug use
   toString: function UI_toString() {
     return "[UI]";
   },
 
   // ----------
@@ -236,16 +248,20 @@ let UI = {
       });
 
       // ___ setup key handlers
       this._setTabViewFrameKeyHandlers();
 
       // ___ add tab action handlers
       this._addTabActionHandlers();
 
+      // ___ add preference observers
+      Services.prefs.addObserver(this.PREF_CHROME_SITE_ICONS, this, false);
+      Services.prefs.addObserver(this.PREF_CHROME_FAVICONS, this, false);
+
       // ___ groups
       GroupItems.init();
       GroupItems.pauseArrange();
       let hasGroupItemsData = GroupItems.load();
 
       // ___ tabs
       TabItems.init();
       TabItems.pausePainting();
@@ -309,16 +325,19 @@ let UI = {
     this._cleanupFunctions = [];
 
     // additional clean up
     TabItems.uninit();
     GroupItems.uninit();
     Storage.uninit();
     StoragePolicy.uninit();
 
+    Services.prefs.removeObserver(this.PREF_CHROME_SITE_ICONS, this);
+    Services.prefs.removeObserver(this.PREF_CHROME_FAVICONS, this);
+
     this._removeTabActionHandlers();
     this._currentTab = null;
     this._pageBounds = null;
     this._reorderTabItemsOnShow = null;
     this._reorderTabsOnHide = null;
     this._frameInitialized = false;
   },
 
@@ -847,16 +866,29 @@ let UI = {
   // 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: observe
+  // Observes different preference value changes.
+  observe: function UI_observe(subject, topic, data) {
+    if (data == this.PREF_CHROME_SITE_ICONS) {
+      this._prefSiteIcons =
+        Services.prefs.getBoolPref(this.PREF_CHROME_SITE_ICONS);
+    } else if (data == this.PREF_CHROME_FAVICONS) {
+      this._prefFavicons =
+        Services.prefs.getBoolPref(this.PREF_CHROME_FAVICONS);
+    }
+  },
+
+  // ----------
   // 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;
@@ -1599,49 +1631,85 @@ let UI = {
 
     if (this._storageSanity(data))
       Storage.saveUIData(gWindow, data);
   },
 
   // ----------
   // Function: _saveAll
   // Saves all data associated with TabView.
-  // TODO: Save info items
   _saveAll: function UI__saveAll() {
     this._save();
     GroupItems.saveAll();
     TabItems.saveAll();
   },
 
   // ----------
-  // Function: shouldLoadFavIcon
-  // Takes a xul:browser and checks whether we should display a favicon for it.
-  shouldLoadFavIcon: function UI_shouldLoadFavIcon(browser) {
-    return !(browser.contentDocument instanceof window.ImageDocument) &&
-            (browser.currentURI.schemeIs("about") ||
-             gBrowser.shouldLoadFavIcon(browser.contentDocument.documentURIObject));
+  // 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 UI__shouldLoadFavIcon(tab) {
+    let uri = tab.linkedBrowser.currentURI;
+
+    if (!uri)
+      return false;
+
+    if (this._prefSiteIcons == null)
+      this._prefSiteIcons =
+        Services.prefs.getBoolPref(this.PREF_CHROME_SITE_ICONS);
+
+    if (!this._prefSiteIcons)
+      return false;
+
+    if (this._prefFavicons == null)
+      this._prefFavicons =
+        Services.prefs.getBoolPref(this.PREF_CHROME_FAVICONS);
+
+    return (this._prefFavicons && ("schemeIs" in uri) &&
+            (uri.schemeIs("http") || uri.schemeIs("https")));
   },
 
   // ----------
   // Function: getFavIconUrlForTab
   // Gets fav icon url for the given xul:tab.
-  getFavIconUrlForTab: function UI_getFavIconUrlForTab(tab) {
-    let url;
+  getFavIconUrlForTab: function UI_getFavIconUrlForTab(tab, callback) {
+    this._isImageDocument(tab, function(isImageDoc) {
+      if (isImageDoc) {
+        callback(tab.pinned ? tab.image : null);
+      } else {
+        let tabImage = tab.image;
+        if (tabImage) {
+          // if starts with http/https, fetch icon from favicon service via the moz-anno protocal
+          if (/^https?:/.test(tabImage))
+            tabImage = gFavIconService.getFaviconLinkForIcon(gWindow.makeURI(tab.image)).spec;
 
-    if (tab.image) {
-      // if starts with http/https, fetch icon from favicon service via the moz-anno protocal
-      if (/^https?:/.test(tab.image))
-        url = gFavIconService.getFaviconLinkForIcon(gWindow.makeURI(tab.image)).spec;
-      else
-        url = tab.image;
-    } else {
-      url = gFavIconService.getFaviconImageForPage(tab.linkedBrowser.currentURI).spec;
-    }
-
-    return url;
+          callback(tabImage);
+        } else {
+          // determine to load the default/cached icon or not and also ensure we don't show the default icon
+          // for about:-style error pages
+          let url = null;
+          if (this._shouldLoadFavIcon(tab))
+            url = gFavIconService.getFaviconImageForPage(tab.linkedBrowser.currentURI).spec;
+          callback(url);
+        }
+      }
+    }.bind(this));
   },
 
   // ----------
   // 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() {