Bug 977814 - Control the number of tiles displayed in each about:start compartment using prefs. r=sfoster
authorJim Mathies <jmathies@mozilla.com>
Thu, 06 Mar 2014 08:32:55 -0600
changeset 189476 be56a571e0a588418f4ab6bccd5e4ff96e613565
parent 189475 0c2a1ea6a296157561fc14563dc388a558b50d86
child 189477 bb950168dbdafb484f7ad9ee423bbe5ebbda044b
push id3503
push userraliiev@mozilla.com
push dateMon, 28 Apr 2014 18:51:11 +0000
treeherdermozilla-beta@c95ac01e332e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssfoster
bugs977814
milestone30.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 977814 - Control the number of tiles displayed in each about:start compartment using prefs. r=sfoster
browser/metro/base/content/startui/BookmarksView.js
browser/metro/base/content/startui/HistoryView.js
browser/metro/base/content/startui/StartUI.js
browser/metro/base/content/startui/TopSitesView.js
browser/metro/base/tests/mochitest/browser_bookmarks.js
browser/metro/base/tests/mochitest/browser_history.js
browser/metro/base/tests/mochitest/helpers/BookmarksHelper.js
browser/metro/base/tests/mochitest/helpers/HistoryHelper.js
browser/metro/modules/View.jsm
browser/metro/profile/metro.js
--- a/browser/metro/base/content/startui/BookmarksView.js
+++ b/browser/metro/base/content/startui/BookmarksView.js
@@ -4,47 +4,53 @@
 
 "use strict";
 
 /**
  * Wraps a list/grid control implementing nsIDOMXULSelectControlElement and
  * fills it with the user's bookmarks.
  *
  * @param           aSet    Control implementing nsIDOMXULSelectControlElement.
- * @param {Number}  aLimit  Maximum number of items to show in the view.
  * @param           aRoot   Bookmark root to show in the view.
  */
-function BookmarksView(aSet, aLimit, aRoot, aFilterUnpinned) {
+function BookmarksView(aSet, aRoot, aFilterUnpinned) {
   View.call(this, aSet);
 
   this._inBatch = false; // batch up grid updates to avoid redundant arrangeItems calls
 
-  this._limit = aLimit;
+  // View monitors this for maximum tile display counts
+  this.tilePrefName = "browser.display.startUI.bookmarks.maxresults";
+  this.showing = this.maxTiles > 0;
+
   this._filterUnpinned = aFilterUnpinned;
   this._bookmarkService = PlacesUtils.bookmarks;
   this._navHistoryService = gHistSvc;
 
   this._changes = new BookmarkChangeListener(this);
   this._pinHelper = new ItemPinHelper("metro.bookmarks.unpinned");
   this._bookmarkService.addObserver(this._changes, false);
   StartUI.chromeWin.addEventListener('MozAppbarDismissing', this, false);
   StartUI.chromeWin.addEventListener('BookmarksNeedsRefresh', this, false);
   window.addEventListener("TabClose", this, true);
 
   this.root = aRoot;
 }
 
 BookmarksView.prototype = Util.extend(Object.create(View.prototype), {
-  _limit: null,
   _set: null,
   _changes: null,
   _root: null,
   _sort: 0, // Natural bookmark order.
   _toRemove: null,
 
+  // For View's showing property
+  get vbox() {
+    return document.getElementById("start-bookmarks");
+  },
+
   get sort() {
     return this._sort;
   },
 
   set sort(aSort) {
     this._sort = aSort;
     this.clearBookmarks();
     this.getBookmarks();
@@ -62,16 +68,21 @@ BookmarksView.prototype = Util.extend(Ob
     this._bookmarkService.removeObserver(this._changes);
     if (StartUI.chromeWin) {
       StartUI.chromeWin.removeEventListener('MozAppbarDismissing', this, false);
       StartUI.chromeWin.removeEventListener('BookmarksNeedsRefresh', this, false);
     }
     View.prototype.destruct.call(this);
   },
 
+  refreshView: function () {
+    this.clearBookmarks();
+    this.getBookmarks();
+  },
+
   handleItemClick: function bv_handleItemClick(aItem) {
     let url = aItem.getAttribute("value");
     StartUI.goToURI(url);
   },
 
   _getItemForBookmarkId: function bv__getItemForBookmark(aBookmarkId) {
     return this._set.querySelector("richgriditem[anonid='" + aBookmarkId + "']");
   },
@@ -86,17 +97,17 @@ BookmarksView.prototype = Util.extend(Ob
   },
 
   getBookmarks: function bv_getBookmarks(aRefresh) {
     let options = this._navHistoryService.getNewQueryOptions();
     options.queryType = options.QUERY_TYPE_BOOKMARKS;
     options.excludeQueries = true; // Don't include "smart folders"
     options.sortingMode = this._sort;
 
-    let limit = this._limit || Infinity;
+    let limit = this.maxTiles;
 
     let query = this._navHistoryService.getNewQuery();
     query.setFolders([Bookmarks.metroRoot], 1);
 
     let result = this._navHistoryService.executeQuery(query, options);
     let rootNode = result.root;
     rootNode.containerOpen = true;
     let childCount = rootNode.childCount;
@@ -300,17 +311,17 @@ BookmarksView.prototype = Util.extend(Ob
   }
 });
 
 let BookmarksStartView = {
   _view: null,
   get _grid() { return document.getElementById("start-bookmarks-grid"); },
 
   init: function init() {
-    this._view = new BookmarksView(this._grid, StartUI.maxResultsPerSection, Bookmarks.metroRoot, true);
+    this._view = new BookmarksView(this._grid, Bookmarks.metroRoot, true);
     this._view.getBookmarks();
     this._grid.removeAttribute("fade");
   },
 
   uninit: function uninit() {
     if (this._view) {
       this._view.destruct();
     }
--- a/browser/metro/base/content/startui/HistoryView.js
+++ b/browser/metro/base/content/startui/HistoryView.js
@@ -1,59 +1,72 @@
 /* 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';
 
-function HistoryView(aSet, aLimit, aFilterUnpinned) {
+function HistoryView(aSet, aFilterUnpinned) {
   View.call(this, aSet);
 
   this._inBatch = 0;
 
-  this._limit = aLimit;
+  // View monitors this for maximum tile display counts
+  this.tilePrefName = "browser.display.startUI.history.maxresults";
+  this.showing = this.maxTiles > 0;
+
   this._filterUnpinned = aFilterUnpinned;
   this._historyService = PlacesUtils.history;
   this._navHistoryService = gHistSvc;
 
   this._pinHelper = new ItemPinHelper("metro.history.unpinned");
   this._historyService.addObserver(this, false);
   StartUI.chromeWin.addEventListener('MozAppbarDismissing', this, false);
   StartUI.chromeWin.addEventListener('HistoryNeedsRefresh', this, false);
   window.addEventListener("TabClose", this, true);
 }
 
 HistoryView.prototype = Util.extend(Object.create(View.prototype), {
   _set: null,
   _toRemove: null,
 
+  // For View's showing property
+  get vbox() {
+    return document.getElementById("start-history");
+  },
+
   destruct: function destruct() {
     this._historyService.removeObserver(this);
     if (StartUI.chromeWin) {
       StartUI.chromeWin.removeEventListener('MozAppbarDismissing', this, false);
       StartUI.chromeWin.removeEventListener('HistoryNeedsRefresh', this, false);
     }
     View.prototype.destruct.call(this);
   },
 
+  refreshView: function () {
+    this.onClearHistory();
+    this.populateGrid();
+  },
+
   handleItemClick: function tabview_handleItemClick(aItem) {
     let url = aItem.getAttribute("value");
     StartUI.goToURI(url);
   },
 
   populateGrid: function populateGrid(aRefresh) {
     this._inBatch++; // always batch up grid updates to avoid redundant arrangeItems calls
     let query = this._navHistoryService.getNewQuery();
     let options = this._navHistoryService.getNewQueryOptions();
     options.excludeQueries = true;
     options.queryType = options.QUERY_TYPE_HISTORY;
     options.resultType = options.RESULTS_AS_URI;
     options.sortingMode = options.SORT_BY_DATE_DESCENDING;
 
-    let limit = this._limit || Infinity;
+    let limit = this.maxTiles;
     let result = this._navHistoryService.executeQuery(query, options);
     let rootNode = result.root;
     rootNode.containerOpen = true;
     let childCount = rootNode.childCount;
 
     for (let i = 0, addedCount = 0; i < childCount && addedCount < limit; i++) {
       let node = rootNode.getChild(i);
       let uri = node.uri;
@@ -292,17 +305,17 @@ HistoryView.prototype = Util.extend(Obje
   }
 });
 
 let HistoryStartView = {
   _view: null,
   get _grid() { return document.getElementById("start-history-grid"); },
 
   init: function init() {
-    this._view = new HistoryView(this._grid, StartUI.maxResultsPerSection, true);
+    this._view = new HistoryView(this._grid, true);
     this._view.populateGrid();
     this._grid.removeAttribute("fade");
   },
 
   uninit: function uninit() {
     if (this._view) {
       this._view.destruct();
     }
--- a/browser/metro/base/content/startui/StartUI.js
+++ b/browser/metro/base/content/startui/StartUI.js
@@ -4,20 +4,16 @@
 
 "use strict";
 
 Cu.import("resource://gre/modules/Services.jsm");
 
 var StartUI = {
   get startUI() { return document.getElementById("start-container"); },
 
-  get maxResultsPerSection() {
-    return Services.prefs.getIntPref("browser.display.startUI.maxresults");
-  },
-
   get chromeWin() {
     // XXX Not e10s friendly. We use this in a few places.
     return Services.wm.getMostRecentWindow('navigator:browser');
   },
 
   init: function init() {
     this.startUI.addEventListener("click", this, false);
     this.startUI.addEventListener("MozMousePixelScroll", this, false);
--- a/browser/metro/base/content/startui/TopSitesView.js
+++ b/browser/metro/base/content/startui/TopSitesView.js
@@ -1,23 +1,21 @@
 /* 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"
 
-let prefs = Components.classes["@mozilla.org/preferences-service;1"].
-      getService(Components.interfaces.nsIPrefBranch);
-
 Cu.import("resource://gre/modules/PageThumbs.jsm");
 
-function TopSitesView(aGrid, aMaxSites) {
+function TopSitesView(aGrid) {
   View.call(this, aGrid);
-
-  this._topSitesMax = aMaxSites;
+  // View monitors this for maximum tile display counts
+  this.tilePrefName = "browser.display.startUI.topsites.maxresults";
+  this.showing = this.maxTiles > 0 && !this.isFirstRun();
 
   // clean up state when the appbar closes
   StartUI.chromeWin.addEventListener('MozAppbarDismissing', this, false);
   let history = Cc["@mozilla.org/browser/nav-history-service;1"].
                 getService(Ci.nsINavHistoryService);
   history.addObserver(this, false);
 
   Services.obs.addObserver(this, "Metro:RefreshTopsiteThumbnail", false);
@@ -25,22 +23,26 @@ function TopSitesView(aGrid, aMaxSites) 
   NewTabUtils.allPages.register(this);
   TopSites.prepareCache().then(function(){
     this.populateGrid();
   }.bind(this));
 }
 
 TopSitesView.prototype = Util.extend(Object.create(View.prototype), {
   _set:null,
-  _topSitesMax: null,
   // _lastSelectedSites used to temporarily store blocked/removed sites for undo/restore-ing
   _lastSelectedSites: null,
   // isUpdating used only for testing currently
   isUpdating: false,
 
+  // For View's showing property
+  get vbox() {
+    return document.getElementById("start-topsites");
+  },
+
   destruct: function destruct() {
     Services.obs.removeObserver(this, "Metro:RefreshTopsiteThumbnail");
     NewTabUtils.allPages.unregister(this);
     if (StartUI.chromeWin) {
       StartUI.chromeWin.removeEventListener('MozAppbarDismissing', this, false);
     }
     View.prototype.destruct.call(this);
   },
@@ -185,22 +187,27 @@ TopSitesView.prototype = Util.extend(Obj
       this._set.arrangeItems();
     }
   },
 
   populateGrid: function populateGrid() {
     this.isUpdating = true;
 
     let sites = TopSites.getSites();
-    if (this._topSitesMax) {
-      sites = sites.slice(0, this._topSitesMax);
-    }
+
     let tileset = this._set;
     tileset.clearAll(true);
 
+    if (!this.maxTiles) {
+      this.isUpdating = false;
+      return;
+    } else {
+      sites = sites.slice(0, this.maxTiles);
+    }
+
     for (let site of sites) {
       let slot = tileset.nextSlot();
       this.updateTile(slot, site);
     }
     tileset.arrangeItems();
     this.isUpdating = false;
   },
 
@@ -209,17 +216,17 @@ TopSitesView.prototype = Util.extend(Obj
     for (let item of nodes) {
       if ("isBound" in item && item.isBound) {
         item.refreshBackgroundImage();
       }
     }
   },
 
   isFirstRun: function isFirstRun() {
-    return prefs.getBoolPref("browser.firstrun.show.localepicker");
+    return Services.prefs.getBoolPref("browser.firstrun.show.localepicker");
   },
 
   _adjustDOMforViewState: function _adjustDOMforViewState(aState) {
     if (!this._set)
       return;
     if (!aState)
       aState = this._set.getAttribute("viewstate");
 
@@ -238,23 +245,29 @@ TopSitesView.prototype = Util.extend(Obj
       if (tileType) {
         item.setAttribute("tiletype", tileType);
       } else {
         item.removeAttribute("tiletype");
       }
     }
   },
 
+  refreshView: function () {
+    this.populateGrid();
+  },
+
   // nsIObservers
   observe: function (aSubject, aTopic, aState) {
     switch (aTopic) {
       case "Metro:RefreshTopsiteThumbnail":
         this.forceReloadOfThumbnail(aState);
         break;
     }
+    View.prototype.observe.call(this, aSubject, aTopic, aState);
+    this.showing = this.maxTiles > 0 && !this.isFirstRun();
   },
 
   // nsINavHistoryObserver
   onBeginUpdateBatch: function() {
   },
 
   onEndUpdateBatch: function() {
   },
@@ -290,21 +303,17 @@ TopSitesView.prototype = Util.extend(Obj
 
 });
 
 let TopSitesStartView = {
   _view: null,
   get _grid() { return document.getElementById("start-topsites-grid"); },
 
   init: function init() {
-    this._view = new TopSitesView(this._grid, 8);
-    if (this._view.isFirstRun()) {
-      let topsitesVbox = document.getElementById("start-topsites");
-      topsitesVbox.setAttribute("hidden", "true");
-    }
+    this._view = new TopSitesView(this._grid);
     this._grid.removeAttribute("fade");
   },
 
   uninit: function uninit() {
     if (this._view) {
       this._view.destruct();
     }
   },
--- a/browser/metro/base/tests/mochitest/browser_bookmarks.js
+++ b/browser/metro/base/tests/mochitest/browser_bookmarks.js
@@ -51,17 +51,17 @@ gTests.push({
     let promise = waitForEvent(Elements.contextappbar, "transitionend", null, Elements.contextappbar);
     hideButton.click();
     yield promise;
 
     item = gStartView._getItemForBookmarkId(2);
 
     ok(!item, "Item not in grid");
     ok(!gStartView._pinHelper.isPinned(2), "Item hidden");
-    ok(gStartView._set.itemCount === gStartView._limit, "Grid repopulated");
+    ok(gStartView._set.itemCount === gStartView.maxTiles, "Grid repopulated");
 
     // --------- hide multiple items
 
     let item1 = gStartView._getItemForBookmarkId(0);
     let item2 = gStartView._getItemForBookmarkId(5);
     let item3 = gStartView._getItemForBookmarkId(12);
 
     let promise = waitForEvent(Elements.contextappbar, "transitionend", null, Elements.contextappbar);
@@ -77,17 +77,17 @@ gTests.push({
     yield promise;
 
     item1 = gStartView._getItemForBookmarkId(0);
     item2 = gStartView._getItemForBookmarkId(5);
     item3 = gStartView._getItemForBookmarkId(12);
 
     ok(!item1 && !item2 && !item3, "Items are not in grid");
     ok(!gStartView._pinHelper.isPinned(0) && !gStartView._pinHelper.isPinned(5) && !gStartView._pinHelper.isPinned(12) , "Items hidden");
-    ok(gStartView._set.itemCount === gStartView._limit - 1, "Grid repopulated");
+    ok(gStartView._set.itemCount === gStartView.maxTiles - 1, "Grid repopulated");
   }
 });
 
 gTests.push({
   desc: "Test bookmarks StartUI delete",
   setUp: setup,
   tearDown: tearDown,
   run: function testBookmarksStartDelete() {
@@ -109,17 +109,17 @@ gTests.push({
     EventUtils.synthesizeMouse(deleteButton, 10, 10, {}, window);
     yield promise;
 
     item = gStartView._getItemForBookmarkId(2);
 
     ok(!item, "Item not in grid");
     ok(BookmarksTestHelper._nodes[2], "Item not deleted yet");
     ok(!restoreButton.hidden, "Restore button is visible.");
-    ok(gStartView._set.itemCount === gStartView._limit, "Grid repopulated");
+    ok(gStartView._set.itemCount === gStartView.maxTiles, "Grid repopulated");
 
     let promise = waitForEvent(Elements.contextappbar, "transitionend", null, Elements.contextappbar);
     EventUtils.synthesizeMouse(restoreButton, 10, 10, {}, window);
     yield promise;
 
     item = gStartView._getItemForBookmarkId(2);
     ok(item, "Item back in grid");
     ok(gStartView._set.getIndexOfItem(item) === initialLocation, "Back in same position.");
@@ -147,17 +147,17 @@ gTests.push({
     let promise = waitForEvent(Elements.contextappbar, "transitionend", null, Elements.contextappbar);
     Elements.contextappbar.dismiss();
     yield promise;
 
     item = gStartView._getItemForBookmarkId(2);
 
     ok(!item, "Item not in grid");
     ok(!BookmarksTestHelper._nodes[2], "Item RIP");
-    ok(gStartView._set.itemCount === gStartView._limit, "Grid repopulated");
+    ok(gStartView._set.itemCount === gStartView.maxTiles, "Grid repopulated");
 
     // --------- delete multiple items and restore
 
     let item1 = gStartView._getItemForBookmarkId(0);
     let item2 = gStartView._getItemForBookmarkId(5);
     let item3 = gStartView._getItemForBookmarkId(12);
 
     let initialLocation1 = gStartView._set.getIndexOfItem(item1);
@@ -179,31 +179,31 @@ gTests.push({
     item1 = gStartView._getItemForBookmarkId(0);
     item2 = gStartView._getItemForBookmarkId(5);
     item3 = gStartView._getItemForBookmarkId(12);
 
     ok(!item1 && !item2 && !item3, "Items are not in grid");
     ok(BookmarksTestHelper._nodes[0] && BookmarksTestHelper._nodes[5] && BookmarksTestHelper._nodes[12],
       "Items not deleted yet");
     ok(!restoreButton.hidden, "Restore button is visible.");
-    ok(gStartView._set.itemCount === gStartView._limit - 1, "Grid repopulated");
+    ok(gStartView._set.itemCount === gStartView.maxTiles - 1, "Grid repopulated");
 
     let promise = waitForEvent(Elements.contextappbar, "transitionend", null, Elements.contextappbar);
     EventUtils.synthesizeMouse(restoreButton, 10, 10, {}, window);
     yield promise;
 
     item1 = gStartView._getItemForBookmarkId(0);
     item2 = gStartView._getItemForBookmarkId(5);
     item3 = gStartView._getItemForBookmarkId(12);
 
     ok(item1 && item2 && item3, "Items are back in grid");
     ok(gStartView._set.getIndexOfItem(item1) === initialLocation1 &&
       gStartView._set.getIndexOfItem(item2) === initialLocation2 &&
       gStartView._set.getIndexOfItem(item3) === initialLocation3, "Items back in the same position.");
-    ok(gStartView._set.itemCount === gStartView._limit, "Grid repopulated");
+    ok(gStartView._set.itemCount === gStartView.maxTiles, "Grid repopulated");
 
     // --------- delete multiple items for good
 
     let item1 = gStartView._getItemForBookmarkId(0);
     let item2 = gStartView._getItemForBookmarkId(5);
     let item3 = gStartView._getItemForBookmarkId(12);
 
     let promise = waitForEvent(Elements.contextappbar, "transitionend", null, Elements.contextappbar);
@@ -235,11 +235,11 @@ gTests.push({
 
     item1 = gStartView._getItemForBookmarkId(0);
     item2 = gStartView._getItemForBookmarkId(5);
     item3 = gStartView._getItemForBookmarkId(12);
 
     ok(!item1 && !item2 && !item3, "Items are not in grid");
     ok(!BookmarksTestHelper._nodes[0] && !BookmarksTestHelper._nodes[5] && !BookmarksTestHelper._nodes[12],
       "Items are gone");
-    ok(gStartView._set.itemCount === gStartView._limit - 1, "Grid repopulated");
+    ok(gStartView._set.itemCount === gStartView.maxTiles - 1, "Grid repopulated");
   }
 });
--- a/browser/metro/base/tests/mochitest/browser_history.js
+++ b/browser/metro/base/tests/mochitest/browser_history.js
@@ -62,17 +62,17 @@ gTests.push({
     let promise = waitForEvent(Elements.contextappbar, "transitionend", null, Elements.contextappbar);
     hideButton.click();
     yield promise;
 
     item = gStartView._set.getItemsByUrl(uriFromIndex(2))[0];
 
     ok(!item, "Item not in grid");
     ok(!gStartView._pinHelper.isPinned(uriFromIndex(2)), "Item hidden");
-    is(gStartView._set.itemCount, gStartView._limit, "Grid repopulated");
+    is(gStartView._set.itemCount, gStartView.maxTiles, "Grid repopulated");
 
     // --------- hide multiple items
 
     let item1 = gStartView._set.getItemsByUrl(uriFromIndex(0))[0];
     let item2 = gStartView._set.getItemsByUrl(uriFromIndex(5))[0];
     let item3 = gStartView._set.getItemsByUrl(uriFromIndex(12))[0];
 
     scrollToEnd();
@@ -89,17 +89,17 @@ gTests.push({
     yield promise;
 
     item1 = gStartView._set.getItemsByUrl(uriFromIndex(0))[0];
     item2 = gStartView._set.getItemsByUrl(uriFromIndex(5))[0];
     item3 = gStartView._set.getItemsByUrl(uriFromIndex(12))[0];
 
     ok(!item1 && !item2 && !item3, "Items are not in grid");
     ok(!gStartView._pinHelper.isPinned(uriFromIndex(0)) && !gStartView._pinHelper.isPinned(uriFromIndex(5)) && !gStartView._pinHelper.isPinned(uriFromIndex(12)) , "Items hidden");
-    ok(gStartView._set.itemCount === gStartView._limit - 1, "Grid repopulated");
+    ok(gStartView._set.itemCount === gStartView.maxTiles - 1, "Grid repopulated");
   }
 });
 
 gTests.push({
   desc: "Test history StartUI delete",
   setUp: setup,
   tearDown: tearDown,
   run: function testHistoryStartDelete() {
@@ -121,26 +121,26 @@ gTests.push({
     EventUtils.synthesizeMouse(deleteButton, 10, 10, {}, window);
     yield promise;
 
     item = gStartView._set.getItemsByUrl(uriFromIndex(2))[0];
 
     ok(!item, "Item not in grid");
     ok(HistoryTestHelper._nodes[uriFromIndex(2)], "Item not actually deleted yet");
     ok(!restoreButton.hidden, "Restore button is visible.");
-    ok(gStartView._set.itemCount === gStartView._limit, "Grid repopulated");
+    ok(gStartView._set.itemCount === gStartView.maxTiles, "Grid repopulated");
 
     let promise = waitForEvent(Elements.contextappbar, "transitionend", null, Elements.contextappbar);
     EventUtils.synthesizeMouse(restoreButton, 10, 10, {}, window);
     yield promise;
 
     item = gStartView._set.getItemsByUrl(uriFromIndex(2))[0];
     ok(item, "Item back in grid");
     ok(gStartView._set.getIndexOfItem(item) === initialLocation, "Back in same position.");
-    ok(gStartView._set.itemCount === gStartView._limit, "Grid repopulated");
+    ok(gStartView._set.itemCount === gStartView.maxTiles, "Grid repopulated");
 
     // --------- delete item 2 for realz
 
     let item = gStartView._set.getItemsByUrl(uriFromIndex(2))[0];
 
     let promise = waitForEvent(Elements.contextappbar, "transitionend", null, Elements.contextappbar);
     sendContextMenuClickToElement(window, item, 10, 10);
     yield promise;
@@ -169,17 +169,17 @@ gTests.push({
 
     is(populateGridSpy.callCount, 1, "populateGrid not called when a removed item is actually deleted");
     populateGridSpy.restore();
 
     item = gStartView._set.getItemsByUrl(uriFromIndex(2))[0];
 
     ok(!item, "Item not in grid");
     ok(!HistoryTestHelper._nodes[uriFromIndex(2)], "Item RIP");
-    is(gStartView._set.itemCount, gStartView._limit, "Grid repopulated");
+    is(gStartView._set.itemCount, gStartView.maxTiles, "Grid repopulated");
 
     // --------- delete multiple items and restore
 
     let item1 = gStartView._set.getItemsByUrl(uriFromIndex(0))[0];
     let item2 = gStartView._set.getItemsByUrl(uriFromIndex(5))[0];
     let item3 = gStartView._set.getItemsByUrl(uriFromIndex(12))[0];
 
     let initialLocation1 = gStartView._set.getIndexOfItem(item1);
@@ -204,31 +204,31 @@ gTests.push({
     item1 = gStartView._set.getItemsByUrl(uriFromIndex(0))[0];
     item2 = gStartView._set.getItemsByUrl(uriFromIndex(5))[0];
     item3 = gStartView._set.getItemsByUrl(uriFromIndex(12))[0];
 
     ok(!item1 && !item2 && !item3, "Items are not in grid");
     ok(HistoryTestHelper._nodes[uriFromIndex(0)] && HistoryTestHelper._nodes[uriFromIndex(5)] && HistoryTestHelper._nodes[uriFromIndex(12)],
       "Items not deleted yet");
     ok(!restoreButton.hidden, "Restore button is visible.");
-    ok(gStartView._set.itemCount === gStartView._limit - 1, "Grid repopulated");
+    ok(gStartView._set.itemCount === gStartView.maxTiles - 1, "Grid repopulated");
 
     let promise = waitForEvent(Elements.contextappbar, "transitionend", null, Elements.contextappbar);
     EventUtils.synthesizeMouse(restoreButton, 10, 10, {}, window);
     yield promise;
 
     item1 = gStartView._set.getItemsByUrl(uriFromIndex(0))[0];
     item2 = gStartView._set.getItemsByUrl(uriFromIndex(5))[0];
     item3 = gStartView._set.getItemsByUrl(uriFromIndex(12))[0];
 
     ok(item1 && item2 && item3, "Items are back in grid");
     ok(gStartView._set.getIndexOfItem(item1) === initialLocation1 &&
       gStartView._set.getIndexOfItem(item2) === initialLocation2 &&
       gStartView._set.getIndexOfItem(item3) === initialLocation3, "Items back in the same position.");
-    ok(gStartView._set.itemCount === gStartView._limit, "Grid repopulated");
+    ok(gStartView._set.itemCount === gStartView.maxTiles, "Grid repopulated");
 
     // --------- delete multiple items for good
 
     let item1 = gStartView._set.getItemsByUrl(uriFromIndex(0))[0];
     let item2 = gStartView._set.getItemsByUrl(uriFromIndex(5))[0];
     let item3 = gStartView._set.getItemsByUrl(uriFromIndex(12))[0];
 
     scrollToEnd();
@@ -249,24 +249,24 @@ gTests.push({
     item1 = gStartView._set.getItemsByUrl(uriFromIndex(0))[0];
     item2 = gStartView._set.getItemsByUrl(uriFromIndex(5))[0];
     item3 = gStartView._set.getItemsByUrl(uriFromIndex(12))[0];
 
     ok(!item1 && !item2 && !item3, "Items are not in grid");
     ok(HistoryTestHelper._nodes[uriFromIndex(0)] && HistoryTestHelper._nodes[uriFromIndex(5)] && HistoryTestHelper._nodes[uriFromIndex(12)],
       "Items not deleted yet");
     ok(!restoreButton.hidden, "Restore button is visible.");
-    ok(gStartView._set.itemCount === gStartView._limit - 1, "Grid repopulated");
+    ok(gStartView._set.itemCount === gStartView.maxTiles - 1, "Grid repopulated");
 
     let promise = waitForEvent(Elements.contextappbar, "transitionend", null, Elements.contextappbar);
     Elements.contextappbar.dismiss();
     yield promise;
 
     item1 = gStartView._set.getItemsByUrl(uriFromIndex(0))[0];
     item2 = gStartView._set.getItemsByUrl(uriFromIndex(5))[0];
     item3 = gStartView._set.getItemsByUrl(uriFromIndex(12))[0];
 
     ok(!item1 && !item2 && !item3, "Items are not in grid");
     ok(!HistoryTestHelper._nodes[uriFromIndex(0)] && !HistoryTestHelper._nodes[uriFromIndex(5)] && !HistoryTestHelper._nodes[uriFromIndex(12)],
       "Items are gone");
-    ok(gStartView._set.itemCount === gStartView._limit - 1, "Grid repopulated");
+    ok(gStartView._set.itemCount === gStartView.maxTiles - 1, "Grid repopulated");
   }
 });
--- a/browser/metro/base/tests/mochitest/helpers/BookmarksHelper.js
+++ b/browser/metro/base/tests/mochitest/helpers/BookmarksHelper.js
@@ -66,17 +66,17 @@ var BookmarksTestHelper = {
   },
 
   _originalUpdateFavicon: null,
   setup: function setup() {
     this._startView = Browser.selectedBrowser.contentWindow.BookmarksStartView._view;
 
     // Just enough items so that there will be one less then the limit
     // after removing 4 items.
-    this.createNodes(this._startView._limit + 3);
+    this.createNodes(this._startView.maxTiles + 3);
 
     this._originalNavHistoryService = this._startView._navHistoryService;
     this._startView._navHistoryService = this.MockNavHistoryService;
 
     this._originalBookmarkService = this._startView._bookmarkService;
     this._startView._bookmarkService= this.MockBookmarkService;
 
     this._originalPinHelper = this._startView._pinHelper;
--- a/browser/metro/base/tests/mochitest/helpers/HistoryHelper.js
+++ b/browser/metro/base/tests/mochitest/helpers/HistoryHelper.js
@@ -63,17 +63,17 @@ var HistoryTestHelper = {
     setPinned: function (aItem) HistoryTestHelper._nodes[aItem].pinned = true,
   },
 
   setup: function setup() {
     this._startView = Browser.selectedBrowser.contentWindow.HistoryStartView._view;
 
     // Just enough items so that there will be one less then the limit
     // after removing 4 items.
-    this.createNodes(this._startView._limit + 3);
+    this.createNodes(this._startView.maxTiles + 3);
 
     this._originalNavHistoryService = this._startView._navHistoryService;
     this._startView._navHistoryService = this.MockNavHistoryService;
 
     this._originalHistoryService = this._startView._historyService;
     this._startView._historyService= this.MockHistoryService;
 
     this._originalPinHelper = this._startView._pinHelper;
--- a/browser/metro/modules/View.jsm
+++ b/browser/metro/modules/View.jsm
@@ -23,27 +23,52 @@ function makeURI(aURL, aOriginCharset, a
 
 // --------------------------------
 // View prototype for shared functionality
 
 function View(aSet) {
   this._set = aSet;
   this._set.controller = this;
   this._window = aSet.ownerDocument.defaultView;
+  this._maxTiles = 8;
+  this._tilePrefName = "unknown";
 
   this.onResize = () => this._adjustDOMforViewState();
   this._window.addEventListener("resize", this.onResize);
 
   ColorUtils.init();
   this._adjustDOMforViewState();
 }
 
 View.prototype = {
+  set maxTiles(aVal) {
+    this._maxTiles = aVal;
+  },
+
+  get maxTiles() {
+    return this._maxTiles;
+  },
+
+  set showing(aFlag) {
+    // 'vbox' must be defined on objects that inherit from us
+    this.vbox.setAttribute("hidden", aFlag ? "false" : "true");
+  },
+
+  set tilePrefName(aStr) {
+    // Should be called once on init by objects that inherit from us
+    this._tilePrefName = aStr;
+    this._maxTiles = Services.prefs.getIntPref(this._tilePrefName);
+    Services.prefs.addObserver(this._tilePrefName, this, false);
+  },
+
   destruct: function () {
     this._window.removeEventListener("resize", this.onResize);
+    if (this._tilePrefName != "unknown") {
+      Services.prefs.removeObserver(this._tilePrefName, this);
+    }
   },
 
   _adjustDOMforViewState: function _adjustDOMforViewState(aState) {
     let grid = this._set;
     if (!grid) {
       return;
     }
     if (!aState) {
@@ -106,11 +131,27 @@ View.prototype = {
       // get the rgb value that represents this color at given opacity over a white matte
       let tintColor = ColorUtils.addRgbColors(matteColor, ColorUtils.createDecimalColorWord(r,g,b,alpha));
       aItem.setAttribute("tintColor", ColorUtils.convertDecimalToRgbColor(tintColor));
       // when bound, use the setter to propogate the color change through the tile
       if ('color' in aItem) {
         aItem.color = background;
       }
     });
-  }
+  },
 
+  refreshView: function () {
+  },
+
+  observe: function (aSubject, aTopic, aState) {
+    switch (aTopic) {
+      case "nsPref:changed": {
+        if (aState == this._tilePrefName) {
+          let count = Services.prefs.getIntPref(this._tilePrefName);
+          this.maxTiles = count;
+          this.showing = this.maxTiles > 0;
+          this.refreshView();
+        }
+        break;
+      }
+    }
+  }
 };
--- a/browser/metro/profile/metro.js
+++ b/browser/metro/profile/metro.js
@@ -107,50 +107,50 @@ pref("toolkit.screen.lock", false);
 // From libpref/src/init/all.js. Disabling text zoom in favor of APZ zoom. See bug 936940.
 pref("zoom.minPercent", 100);
 pref("zoom.maxPercent", 100);
 pref("toolkit.zoomManager.zoomValues", "1");
 
 // Device pixel to CSS px ratio, in percent. Set to -1 to calculate based on display density.
 pref("browser.viewport.scaleRatio", -1);
 
-/* use long press to display a context menu */
+// use long press to display a context menu
 pref("ui.click_hold_context_menus", false);
 
-/* offline cache prefs */
+// offline cache prefs
 pref("browser.offline-apps.notify", true);
 
-/* protocol warning prefs */
+// protocol warning prefs
 pref("network.protocol-handler.warn-external.tel", false);
 pref("network.protocol-handler.warn-external.mailto", false);
 pref("network.protocol-handler.warn-external.vnd.youtube", false);
 pref("network.protocol-handler.warn-external.ms-windows-store", false);
 pref("network.protocol-handler.external.ms-windows-store", true);
 
+/* startui prefs */
 // display the overlay nav buttons
 pref("browser.display.overlaynavbuttons", true);
-
-/* history max results display */
-pref("browser.display.history.maxresults", 100);
-
-/* max items per section of the startui */
-pref("browser.display.startUI.maxresults", 16);
-
+// max number of top site tiles to display in the startui
+pref("browser.display.startUI.topsites.maxresults", 8);
+// max items for the bookmarks compartment in the startui
+pref("browser.display.startUI.bookmarks.maxresults", 16);
+// max items for the history compartment in the startui
+pref("browser.display.startUI.history.maxresults", 16);
 // Number of times to display firstrun instructions on new tab page
 pref("browser.firstrun.count", 3);
 // Has the content first run been dismissed
 pref("browser.firstrun-content.dismissed", false);
 
 // Backspace and Shift+Backspace behavior
 // 0 goes Back/Forward
 // 1 act like PgUp/PgDown
 // 2 and other values, nothing
 pref("browser.backspace_action", 0);
 
-/* session history */
+// session history
 pref("browser.sessionhistory.max_entries", 50);
 
 // On startup, don't automatically restore tabs
 pref("browser.startup.page", 1);
 
 /* session store */
 pref("browser.sessionstore.resume_from_crash", true);
 pref("browser.sessionstore.resume_session_once", false);
@@ -636,23 +636,16 @@ pref("urlclassifier.updatecachemax", 419
 // URL for checking the reason for a malware warning.
 pref("browser.safebrowsing.malware.reportURL", "https://safebrowsing.google.com/safebrowsing/diagnostic?client=%NAME%&hl=%LOCALE%&site=");
 #endif
 
 // True if this is the first time we are showing about:firstrun
 pref("browser.firstrun.show.localepicker", false);
 
 // True if you always want dump() to work
-//
-// On Android, you also need to do the following for the output
-// to show up in logcat:
-//
-// $ adb shell stop
-// $ adb shell setprop log.redirect-stdio true
-// $ adb shell start
 pref("javascript.options.showInConsole", true);
 pref("browser.dom.window.dump.enabled", true);
 
 // controls if we want camera support
 pref("device.camera.enabled", true);
 pref("media.realtime_decoder.enabled", true);
 
 // Metro manages state by autodetection