diff --git a/browser/metro/base/content/TopSites.js b/browser/metro/base/content/TopSites.js index 753433b4593..e5cd48b2c29 100644 --- a/browser/metro/base/content/TopSites.js +++ b/browser/metro/base/content/TopSites.js @@ -3,10 +3,6 @@ * 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"); -Cu.import("resource:///modules/colorUtils.jsm"); /** * singleton to provide data-level functionality to the views @@ -163,290 +159,3 @@ let TopSites = { } }; -function TopSitesView(aGrid, aMaxSites) { - this._set = aGrid; - this._set.controller = this; - this._topSitesMax = aMaxSites; - - // clean up state when the appbar closes - window.addEventListener('MozAppbarDismissing', this, false); - let history = Cc["@mozilla.org/browser/nav-history-service;1"]. - getService(Ci.nsINavHistoryService); - history.addObserver(this, false); - - PageThumbs.addExpirationFilter(this); - Services.obs.addObserver(this, "Metro:RefreshTopsiteThumbnail", false); - Services.obs.addObserver(this, "metro_viewstate_changed", false); - - 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, - - handleItemClick: function tabview_handleItemClick(aItem) { - let url = aItem.getAttribute("value"); - BrowserUI.goToURI(url); - }, - - doActionOnSelectedTiles: function(aActionName, aEvent) { - let tileGroup = this._set; - let selectedTiles = tileGroup.selectedItems; - let sites = Array.map(selectedTiles, TopSites._linkFromNode); - let nextContextActions = new Set(); - - switch (aActionName){ - case "delete": - for (let aNode of selectedTiles) { - // add some class to transition element before deletion? - aNode.contextActions.delete('delete'); - // we need new context buttons to show (the tile node will go away though) - } - this._lastSelectedSites = (this._lastSelectedSites || []).concat(sites); - // stop the appbar from dismissing - aEvent.preventDefault(); - nextContextActions.add('restore'); - TopSites.hideSites(sites); - break; - case "restore": - // usually restore is an undo action, so we have to recreate the tiles and grid selection - if (this._lastSelectedSites) { - let selectedUrls = this._lastSelectedSites.map((site) => site.url); - // re-select the tiles once the tileGroup is done populating and arranging - tileGroup.addEventListener("arranged", function _onArranged(aEvent){ - for (let url of selectedUrls) { - let tileNode = tileGroup.querySelector("richgriditem[value='"+url+"']"); - if (tileNode) { - tileNode.setAttribute("selected", true); - } - } - tileGroup.removeEventListener("arranged", _onArranged, false); - // we can't just call selectItem n times on tileGroup as selecting means trigger the default action - // for seltype="single" grids. - // so we toggle the attributes and raise the selectionchange "manually" - let event = tileGroup.ownerDocument.createEvent("Events"); - event.initEvent("selectionchange", true, true); - tileGroup.dispatchEvent(event); - }, false); - - TopSites.restoreSites(this._lastSelectedSites); - // stop the appbar from dismissing, - // the selectionchange event will trigger re-population of the context appbar - aEvent.preventDefault(); - } - break; - case "pin": - let pinIndices = []; - Array.forEach(selectedTiles, function(aNode) { - pinIndices.push( Array.indexOf(aNode.control.children, aNode) ); - aNode.contextActions.delete('pin'); - aNode.contextActions.add('unpin'); - }); - TopSites.pinSites(sites, pinIndices); - break; - case "unpin": - Array.forEach(selectedTiles, function(aNode) { - aNode.contextActions.delete('unpin'); - aNode.contextActions.add('pin'); - }); - TopSites.unpinSites(sites); - break; - // default: no action - } - if (nextContextActions.size) { - // at next tick, re-populate the context appbar - setTimeout(function(){ - // fire a MozContextActionsChange event to update the context appbar - let event = document.createEvent("Events"); - event.actions = [...nextContextActions]; - event.initEvent("MozContextActionsChange", true, false); - tileGroup.dispatchEvent(event); - },0); - } - }, - - handleEvent: function(aEvent) { - switch (aEvent.type){ - case "MozAppbarDismissing": - // clean up when the context appbar is dismissed - we don't remember selections - this._lastSelectedSites = null; - } - }, - - update: function() { - // called by the NewTabUtils.allPages.update, notifying us of data-change in topsites - let grid = this._set, - dirtySites = TopSites.dirty(); - - if (dirtySites.size) { - // we can just do a partial update and refresh the node representing each dirty tile - for (let site of dirtySites) { - let tileNode = grid.querySelector("[value='"+site.url+"']"); - if (tileNode) { - this.updateTile(tileNode, new Site(site)); - } - } - } else { - // flush, recreate all - this.isUpdating = true; - // destroy and recreate all item nodes, skip calling arrangeItems - grid.clearAll(true); - this.populateGrid(); - } - }, - - updateTile: function(aTileNode, aSite, aArrangeGrid) { - this._updateFavicon(aTileNode, Util.makeURI(aSite.url)); - - Task.spawn(function() { - let filepath = PageThumbsStorage.getFilePathForURL(aSite.url); - if (yield OS.File.exists(filepath)) { - aSite.backgroundImage = 'url("'+PageThumbs.getThumbnailURL(aSite.url)+'")'; - aTileNode.setAttribute("customImage", aSite.backgroundImage); - if (aTileNode.refresh) { - aTileNode.refresh() - } - } - }); - - aSite.applyToTileNode(aTileNode); - if (aArrangeGrid) { - this._set.arrangeItems(); - } - }, - - populateGrid: function populateGrid() { - this.isUpdating = true; - - let sites = TopSites.getSites(); - let length = Math.min(sites.length, this._topSitesMax || Infinity); - let tileset = this._set; - - // if we're updating with a collection that is smaller than previous - // remove any extra tiles - while (tileset.children.length > length) { - tileset.removeChild(tileset.children[tileset.children.length -1]); - } - - for (let idx=0; idx < length; idx++) { - let isNew = !tileset.children[idx], - site = sites[idx]; - let item = isNew ? tileset.createItemElement(site.title, site.url) : tileset.children[idx]; - - this.updateTile(item, site); - if (isNew) { - tileset.appendChild(item); - } - } - tileset.arrangeItems(); - this.isUpdating = false; - }, - - forceReloadOfThumbnail: function forceReloadOfThumbnail(url) { - let nodes = this._set.querySelectorAll('richgriditem[value="'+url+'"]'); - for (let item of nodes) { - if ("isBound" in item && item.isBound) { - item.refreshBackgroundImage(); - } - } - }, - - filterForThumbnailExpiration: function filterForThumbnailExpiration(aCallback) { - aCallback([item.getAttribute("value") for (item of this._set.children)]); - }, - - isFirstRun: function isFirstRun() { - return prefs.getBoolPref("browser.firstrun.show.localepicker"); - }, - - destruct: function destruct() { - Services.obs.removeObserver(this, "Metro:RefreshTopsiteThumbnail"); - Services.obs.removeObserver(this, "metro_viewstate_changed"); - PageThumbs.removeExpirationFilter(this); - window.removeEventListener('MozAppbarDismissing', this, false); - }, - - // nsIObservers - observe: function (aSubject, aTopic, aState) { - switch(aTopic) { - case "Metro:RefreshTopsiteThumbnail": - this.forceReloadOfThumbnail(aState); - break; - case "metro_viewstate_changed": - this.onViewStateChange(aState); - for (let item of this._set.children) { - if (aState == "snapped") { - item.removeAttribute("tiletype"); - } else { - item.setAttribute("tiletype", "thumbnail"); - } - } - break; - } - }, - - // nsINavHistoryObserver - onBeginUpdateBatch: function() { - }, - - onEndUpdateBatch: function() { - }, - - onVisit: function(aURI, aVisitID, aTime, aSessionID, - aReferringID, aTransitionType) { - }, - - onTitleChanged: function(aURI, aPageTitle) { - }, - - onDeleteURI: function(aURI) { - }, - - onClearHistory: function() { - this._set.clearAll(); - }, - - onPageChanged: function(aURI, aWhat, aValue) { - }, - - onDeleteVisits: function (aURI, aVisitTime, aGUID, aReason, aTransitionType) { - }, - - QueryInterface: function(iid) { - if (iid.equals(Components.interfaces.nsINavHistoryObserver) || - iid.equals(Components.interfaces.nsISupports)) { - return this; - } - throw Cr.NS_ERROR_NO_INTERFACE; - } - -}); - -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"); - } - }, - - uninit: function uninit() { - this._view.destruct(); - }, - - show: function show() { - this._grid.arrangeItems(); - } -}; diff --git a/browser/metro/base/content/bookmarks.js b/browser/metro/base/content/bookmarks.js index 29cbe5c7578..193b0edb838 100644 --- a/browser/metro/base/content/bookmarks.js +++ b/browser/metro/base/content/bookmarks.js @@ -65,372 +65,3 @@ var Bookmarks = { }); } }; - -/** - * 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) { - this._set = aSet; - this._set.controller = this; - this._inBatch = false; // batch up grid updates to avoid redundant arrangeItems calls - - this._limit = aLimit; - 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); - Services.obs.addObserver(this, "metro_viewstate_changed", false); - window.addEventListener('MozAppbarDismissing', this, false); - window.addEventListener('BookmarksNeedsRefresh', this, false); - - 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, - - get sort() { - return this._sort; - }, - - set sort(aSort) { - this._sort = aSort; - this.clearBookmarks(); - this.getBookmarks(); - }, - - get root() { - return this._root; - }, - - set root(aRoot) { - this._root = aRoot; - }, - - handleItemClick: function bv_handleItemClick(aItem) { - let url = aItem.getAttribute("value"); - BrowserUI.goToURI(url); - }, - - _getItemForBookmarkId: function bv__getItemForBookmark(aBookmarkId) { - return this._set.querySelector("richgriditem[bookmarkId='" + aBookmarkId + "']"); - }, - - _getBookmarkIdForItem: function bv__getBookmarkForItem(aItem) { - return +aItem.getAttribute("bookmarkId"); - }, - - _updateItemWithAttrs: function dv__updateItemWithAttrs(anItem, aAttrs) { - for (let name in aAttrs) - anItem.setAttribute(name, aAttrs[name]); - }, - - 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 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; - - this._inBatch = true; // batch up grid updates to avoid redundant arrangeItems calls - - for (let i = 0, addedCount = 0; i < childCount && addedCount < limit; i++) { - let node = rootNode.getChild(i); - - // Ignore folders, separators, undefined item types, etc. - if (node.type != node.RESULT_TYPE_URI) - continue; - - // If item is marked for deletion, skip it. - if (this._toRemove && this._toRemove.indexOf(node.itemId) !== -1) - continue; - - let item = this._getItemForBookmarkId(node.itemId); - - // Item has been unpinned. - if (this._filterUnpinned && !this._pinHelper.isPinned(node.itemId)) { - if (item) - this.removeBookmark(node.itemId); - - continue; - } - - if (!aRefresh || !item) { - // If we're not refreshing or the item is not in the grid, add it. - this.addBookmark(node.itemId, addedCount); - } else if (aRefresh && item) { - // Update context action in case it changed in another view. - this._setContextActions(item); - } - - addedCount++; - } - - // Remove extra items in case a refresh added more than the limit. - // This can happen when undoing a delete. - if (aRefresh) { - while (this._set.itemCount > limit) - this._set.removeItemAt(this._set.itemCount - 1, true); - } - this._set.arrangeItems(); - this._inBatch = false; - rootNode.containerOpen = false; - }, - - inCurrentView: function bv_inCurrentView(aParentId, aItemId) { - if (this._root && aParentId != this._root) - return false; - - return !!this._getItemForBookmarkId(aItemId); - }, - - clearBookmarks: function bv_clearBookmarks() { - this._set.clearAll(); - }, - - addBookmark: function bv_addBookmark(aBookmarkId, aPos) { - let index = this._bookmarkService.getItemIndex(aBookmarkId); - let uri = this._bookmarkService.getBookmarkURI(aBookmarkId); - let title = this._bookmarkService.getItemTitle(aBookmarkId) || uri.spec; - let item = this._set.insertItemAt(aPos || index, title, uri.spec, this._inBatch); - item.setAttribute("bookmarkId", aBookmarkId); - this._setContextActions(item); - this._updateFavicon(item, uri); - }, - - _setContextActions: function bv__setContextActions(aItem) { - let itemId = this._getBookmarkIdForItem(aItem); - aItem.setAttribute("data-contextactions", "delete," + (this._pinHelper.isPinned(itemId) ? "unpin" : "pin")); - if (aItem.refresh) aItem.refresh(); - }, - - _sendNeedsRefresh: function bv__sendNeedsRefresh(){ - // Event sent when all view instances need to refresh. - let event = document.createEvent("Events"); - event.initEvent("BookmarksNeedsRefresh", true, false); - window.dispatchEvent(event); - }, - - updateBookmark: function bv_updateBookmark(aBookmarkId) { - let item = this._getItemForBookmarkId(aBookmarkId); - - if (!item) - return; - - let oldIndex = this._set.getIndexOfItem(item); - let index = this._bookmarkService.getItemIndex(aBookmarkId); - - if (oldIndex != index) { - this.removeBookmark(aBookmarkId); - this.addBookmark(aBookmarkId); - return; - } - - let uri = this._bookmarkService.getBookmarkURI(aBookmarkId); - let title = this._bookmarkService.getItemTitle(aBookmarkId) || uri.spec; - - item.setAttribute("value", uri.spec); - item.setAttribute("label", title); - - this._updateFavicon(item, uri); - }, - - removeBookmark: function bv_removeBookmark(aBookmarkId) { - let item = this._getItemForBookmarkId(aBookmarkId); - let index = this._set.getIndexOfItem(item); - this._set.removeItemAt(index, this._inBatch); - }, - - destruct: function bv_destruct() { - this._bookmarkService.removeObserver(this._changes); - Services.obs.removeObserver(this, "metro_viewstate_changed"); - window.removeEventListener('MozAppbarDismissing', this, false); - window.removeEventListener('BookmarksNeedsRefresh', this, false); - }, - - doActionOnSelectedTiles: function bv_doActionOnSelectedTiles(aActionName, aEvent) { - let tileGroup = this._set; - let selectedTiles = tileGroup.selectedItems; - - switch (aActionName){ - case "delete": - Array.forEach(selectedTiles, function(aNode) { - if (!this._toRemove) { - this._toRemove = []; - } - - let itemId = this._getBookmarkIdForItem(aNode); - - this._toRemove.push(itemId); - this.removeBookmark(itemId); - }, this); - - // stop the appbar from dismissing - aEvent.preventDefault(); - - // at next tick, re-populate the context appbar. - setTimeout(function(){ - // fire a MozContextActionsChange event to update the context appbar - let event = document.createEvent("Events"); - // we need the restore button to show (the tile node will go away though) - event.actions = ["restore"]; - event.initEvent("MozContextActionsChange", true, false); - tileGroup.dispatchEvent(event); - }, 0); - break; - - case "restore": - // clear toRemove and let _sendNeedsRefresh update the items. - this._toRemove = null; - break; - - case "unpin": - Array.forEach(selectedTiles, function(aNode) { - let itemId = this._getBookmarkIdForItem(aNode); - - if (this._filterUnpinned) - this.removeBookmark(itemId); - - this._pinHelper.setUnpinned(itemId); - }, this); - break; - - case "pin": - Array.forEach(selectedTiles, function(aNode) { - let itemId = this._getBookmarkIdForItem(aNode); - - this._pinHelper.setPinned(itemId); - }, this); - break; - - default: - return; - } - - // Send refresh event so all view are in sync. - this._sendNeedsRefresh(); - }, - - // nsIObservers - observe: function (aSubject, aTopic, aState) { - switch(aTopic) { - case "metro_viewstate_changed": - this.onViewStateChange(aState); - break; - } - }, - - handleEvent: function bv_handleEvent(aEvent) { - switch (aEvent.type){ - case "MozAppbarDismissing": - // If undo wasn't pressed, time to do definitive actions. - if (this._toRemove) { - for (let bookmarkId of this._toRemove) { - this._bookmarkService.removeItem(bookmarkId); - } - this._toRemove = null; - } - break; - - case "BookmarksNeedsRefresh": - this.getBookmarks(true); - break; - } - } -}); - -var 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.getBookmarks(); - }, - - uninit: function uninit() { - this._view.destruct(); - }, - - show: function show() { - this._grid.arrangeItems(); - } -}; - -/** - * Observes bookmark changes and keeps a linked BookmarksView updated. - * - * @param aView An instance of BookmarksView. - */ -function BookmarkChangeListener(aView) { - this._view = aView; -} - -BookmarkChangeListener.prototype = { - ////////////////////////////////////////////////////////////////////////////// - //// nsINavBookmarkObserver - onBeginUpdateBatch: function () { }, - onEndUpdateBatch: function () { }, - - onItemAdded: function bCL_onItemAdded(aItemId, aParentId, aIndex, aItemType, aURI, aTitle, aDateAdded, aGUID, aParentGUID) { - this._view.getBookmarks(true); - }, - - onItemChanged: function bCL_onItemChanged(aItemId, aProperty, aIsAnnotationProperty, aNewValue, aLastModified, aItemType, aParentId, aGUID, aParentGUID) { - let itemIndex = PlacesUtils.bookmarks.getItemIndex(aItemId); - if (!this._view.inCurrentView(aParentId, aItemId)) - return; - - this._view.updateBookmark(aItemId); - }, - - onItemMoved: function bCL_onItemMoved(aItemId, aOldParentId, aOldIndex, aNewParentId, aNewIndex, aItemType, aGUID, aOldParentGUID, aNewParentGUID) { - let wasInView = this._view.inCurrentView(aOldParentId, aItemId); - let nowInView = this._view.inCurrentView(aNewParentId, aItemId); - - if (!wasInView && nowInView) - this._view.addBookmark(aItemId); - - if (wasInView && !nowInView) - this._view.removeBookmark(aItemId); - - this._view.getBookmarks(true); - }, - - onBeforeItemRemoved: function (aItemId, aItemType, aParentId, aGUID, aParentGUID) { }, - onItemRemoved: function bCL_onItemRemoved(aItemId, aParentId, aIndex, aItemType, aURI, aGUID, aParentGUID) { - if (!this._view.inCurrentView(aParentId, aItemId)) - return; - - this._view.removeBookmark(aItemId); - this._view.getBookmarks(true); - }, - - onItemVisited: function(aItemId, aVisitId, aTime, aTransitionType, aURI, aParentId, aGUID, aParentGUID) { }, - - ////////////////////////////////////////////////////////////////////////////// - //// nsISupports - QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarkObserver]) -}; diff --git a/browser/metro/base/content/browser-scripts.js b/browser/metro/base/content/browser-scripts.js index 5e5537d1b9f..da46dd48c12 100644 --- a/browser/metro/base/content/browser-scripts.js +++ b/browser/metro/base/content/browser-scripts.js @@ -34,13 +34,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "Promise", XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "CrossSlide", - "resource:///modules/CrossSlide.jsm"); - XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "View", - "resource:///modules/View.jsm"); /* * Services @@ -123,13 +118,8 @@ let ScriptContexts = {}; ["Bookmarks", "chrome://browser/content/bookmarks.js"], ["Downloads", "chrome://browser/content/downloads.js"], ["ConsolePanelView", "chrome://browser/content/console.js"], - ["BookmarksStartView", "chrome://browser/content/bookmarks.js"], - ["HistoryView", "chrome://browser/content/history.js"], - ["HistoryStartView", "chrome://browser/content/history.js"], ["Site", "chrome://browser/content/Site.js"], ["TopSites", "chrome://browser/content/TopSites.js"], - ["TopSitesView", "chrome://browser/content/TopSites.js"], - ["TopSitesStartView", "chrome://browser/content/TopSites.js"], ["Sanitizer", "chrome://browser/content/sanitize.js"], ["SanitizeUI", "chrome://browser/content/sanitizeUI.js"], ["SSLExceptions", "chrome://browser/content/exceptions.js"], @@ -137,10 +127,6 @@ let ScriptContexts = {}; ["NavButtonSlider", "chrome://browser/content/NavButtonSlider.js"], ["ContextUI", "chrome://browser/content/ContextUI.js"], ["FlyoutPanelsUI", "chrome://browser/content/flyoutpanels/FlyoutPanelsUI.js"], -#ifdef MOZ_SERVICES_SYNC - ["RemoteTabsView", "chrome://browser/content/RemoteTabs.js"], - ["RemoteTabsStartView", "chrome://browser/content/RemoteTabs.js"], -#endif ].forEach(function (aScript) { let [name, script] = aScript; XPCOMUtils.defineLazyGetter(window, name, function() { diff --git a/browser/metro/base/content/browser-ui.js b/browser/metro/base/content/browser-ui.js index f3b4ee49e6c..eb3a9e8dba4 100644 --- a/browser/metro/base/content/browser-ui.js +++ b/browser/metro/base/content/browser-ui.js @@ -1113,139 +1113,6 @@ var BrowserUI = { } }; -var StartUI = { - get isVisible() { return this.isStartPageVisible; }, - get isStartPageVisible() { return Elements.windowState.hasAttribute("startpage"); }, - - get maxResultsPerSection() { - return Services.prefs.getIntPref("browser.display.startUI.maxresults"); - }, - - sections: [ - "TopSitesStartView", - "BookmarksStartView", - "HistoryStartView", - "RemoteTabsStartView" - ], - - init: function init() { - Elements.startUI.addEventListener("contextmenu", this, false); - Elements.startUI.addEventListener("click", this, false); - Elements.startUI.addEventListener("MozMousePixelScroll", this, false); - - this.sections.forEach(function (sectionName) { - let section = window[sectionName]; - if (section.init) - section.init(); - }); - }, - - uninit: function() { - this.sections.forEach(function (sectionName) { - let section = window[sectionName]; - if (section.uninit) - section.uninit(); - }); - }, - - /** Show the Firefox start page / "new tab" page */ - show: function show() { - if (this.isStartPageVisible) - return false; - - ContextUI.displayNavbar(); - - Elements.contentShowing.setAttribute("disabled", "true"); - Elements.windowState.setAttribute("startpage", "true"); - - this.sections.forEach(function (sectionName) { - let section = window[sectionName]; - if (section.show) - section.show(); - }); - return true; - }, - - /** Hide the Firefox start page */ - hide: function hide(aURI) { - aURI = aURI || Browser.selectedBrowser.currentURI.spec; - if (!this.isStartPageVisible || this.isStartURI(aURI)) - return false; - - Elements.contentShowing.removeAttribute("disabled"); - Elements.windowState.removeAttribute("startpage"); - return true; - }, - - /** Is the current tab supposed to show the Firefox start page? */ - isStartURI: function isStartURI(aURI) { - aURI = aURI || Browser.selectedBrowser.currentURI.spec; - return aURI == kStartOverlayURI || aURI == "about:home"; - }, - - /** Call this to show or hide the start page when switching tabs or pages */ - update: function update(aURI) { - aURI = aURI || Browser.selectedBrowser.currentURI.spec; - if (this.isStartURI(aURI)) { - this.show(); - } else if (aURI != "about:blank") { // about:blank is loaded briefly for new tabs; ignore it - this.hide(aURI); - } - }, - - onClick: function onClick(aEvent) { - // If someone clicks / taps in empty grid space, take away - // focus from the nav bar edit so the soft keyboard will hide. - if (BrowserUI.blurNavBar()) { - // Advanced notice to CAO, so we can shuffle the nav bar in advance - // of the keyboard transition. - ContentAreaObserver.navBarWillBlur(); - } - - if (aEvent.button == 0) - ContextUI.dismissTabs(); - }, - - onNarrowTitleClick: function onNarrowTitleClick(sectionId) { - let section = document.getElementById(sectionId); - - if (section.hasAttribute("expanded")) - return; - - for (let expandedSection of Elements.startUI.querySelectorAll(".meta-section[expanded]")) - expandedSection.removeAttribute("expanded") - - section.setAttribute("expanded", "true"); - }, - - handleEvent: function handleEvent(aEvent) { - switch (aEvent.type) { - case "contextmenu": - let event = document.createEvent("Events"); - event.initEvent("MozEdgeUICompleted", true, false); - window.dispatchEvent(event); - break; - case "click": - this.onClick(aEvent); - break; - - case "MozMousePixelScroll": - let startBox = document.getElementById("start-scrollbox"); - let [, scrollInterface] = ScrollUtils.getScrollboxFromElement(startBox); - - if (Elements.windowState.getAttribute("viewstate") == "snapped") { - scrollInterface.scrollBy(0, aEvent.detail); - } else { - scrollInterface.scrollBy(aEvent.detail, 0); - } - - aEvent.preventDefault(); - aEvent.stopPropagation(); - break; - } - } -}; - var PanelUI = { get _panels() { return document.getElementById("panel-items"); }, diff --git a/browser/metro/base/content/browser.xul b/browser/metro/base/content/browser.xul index ad3e852cfcb..100a1b54675 100644 --- a/browser/metro/base/content/browser.xul +++ b/browser/metro/base/content/browser.xul @@ -190,46 +190,6 @@ - - - - - - - - - - - - - - - - - - - - diff --git a/browser/metro/base/content/startui/BookmarksView.js b/browser/metro/base/content/startui/BookmarksView.js new file mode 100644 index 00000000000..34ab1454862 --- /dev/null +++ b/browser/metro/base/content/startui/BookmarksView.js @@ -0,0 +1,374 @@ +/* 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"; + +/** + * 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) { + this._set = aSet; + this._set.controller = this; + this._inBatch = false; // batch up grid updates to avoid redundant arrangeItems calls + + this._limit = aLimit; + 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); + Services.obs.addObserver(this, "metro_viewstate_changed", false); + window.addEventListener('MozAppbarDismissing', this, false); + window.addEventListener('BookmarksNeedsRefresh', this, false); + + 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, + + get sort() { + return this._sort; + }, + + set sort(aSort) { + this._sort = aSort; + this.clearBookmarks(); + this.getBookmarks(); + }, + + get root() { + return this._root; + }, + + set root(aRoot) { + this._root = aRoot; + }, + + handleItemClick: function bv_handleItemClick(aItem) { + let url = aItem.getAttribute("value"); + BrowserUI.goToURI(url); + }, + + _getItemForBookmarkId: function bv__getItemForBookmark(aBookmarkId) { + return this._set.querySelector("richgriditem[bookmarkId='" + aBookmarkId + "']"); + }, + + _getBookmarkIdForItem: function bv__getBookmarkForItem(aItem) { + return +aItem.getAttribute("bookmarkId"); + }, + + _updateItemWithAttrs: function dv__updateItemWithAttrs(anItem, aAttrs) { + for (let name in aAttrs) + anItem.setAttribute(name, aAttrs[name]); + }, + + 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 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; + + this._inBatch = true; // batch up grid updates to avoid redundant arrangeItems calls + + for (let i = 0, addedCount = 0; i < childCount && addedCount < limit; i++) { + let node = rootNode.getChild(i); + + // Ignore folders, separators, undefined item types, etc. + if (node.type != node.RESULT_TYPE_URI) + continue; + + // If item is marked for deletion, skip it. + if (this._toRemove && this._toRemove.indexOf(node.itemId) !== -1) + continue; + + let item = this._getItemForBookmarkId(node.itemId); + + // Item has been unpinned. + if (this._filterUnpinned && !this._pinHelper.isPinned(node.itemId)) { + if (item) + this.removeBookmark(node.itemId); + + continue; + } + + if (!aRefresh || !item) { + // If we're not refreshing or the item is not in the grid, add it. + this.addBookmark(node.itemId, addedCount); + } else if (aRefresh && item) { + // Update context action in case it changed in another view. + this._setContextActions(item); + } + + addedCount++; + } + + // Remove extra items in case a refresh added more than the limit. + // This can happen when undoing a delete. + if (aRefresh) { + while (this._set.itemCount > limit) + this._set.removeItemAt(this._set.itemCount - 1, true); + } + this._set.arrangeItems(); + this._inBatch = false; + rootNode.containerOpen = false; + }, + + inCurrentView: function bv_inCurrentView(aParentId, aItemId) { + if (this._root && aParentId != this._root) + return false; + + return !!this._getItemForBookmarkId(aItemId); + }, + + clearBookmarks: function bv_clearBookmarks() { + this._set.clearAll(); + }, + + addBookmark: function bv_addBookmark(aBookmarkId, aPos) { + let index = this._bookmarkService.getItemIndex(aBookmarkId); + let uri = this._bookmarkService.getBookmarkURI(aBookmarkId); + let title = this._bookmarkService.getItemTitle(aBookmarkId) || uri.spec; + let item = this._set.insertItemAt(aPos || index, title, uri.spec, this._inBatch); + item.setAttribute("bookmarkId", aBookmarkId); + this._setContextActions(item); + this._updateFavicon(item, uri); + }, + + _setContextActions: function bv__setContextActions(aItem) { + let itemId = this._getBookmarkIdForItem(aItem); + aItem.setAttribute("data-contextactions", "delete," + (this._pinHelper.isPinned(itemId) ? "unpin" : "pin")); + if (aItem.refresh) aItem.refresh(); + }, + + _sendNeedsRefresh: function bv__sendNeedsRefresh(){ + // Event sent when all view instances need to refresh. + let event = document.createEvent("Events"); + event.initEvent("BookmarksNeedsRefresh", true, false); + window.dispatchEvent(event); + }, + + updateBookmark: function bv_updateBookmark(aBookmarkId) { + let item = this._getItemForBookmarkId(aBookmarkId); + + if (!item) + return; + + let oldIndex = this._set.getIndexOfItem(item); + let index = this._bookmarkService.getItemIndex(aBookmarkId); + + if (oldIndex != index) { + this.removeBookmark(aBookmarkId); + this.addBookmark(aBookmarkId); + return; + } + + let uri = this._bookmarkService.getBookmarkURI(aBookmarkId); + let title = this._bookmarkService.getItemTitle(aBookmarkId) || uri.spec; + + item.setAttribute("value", uri.spec); + item.setAttribute("label", title); + + this._updateFavicon(item, uri); + }, + + removeBookmark: function bv_removeBookmark(aBookmarkId) { + let item = this._getItemForBookmarkId(aBookmarkId); + let index = this._set.getIndexOfItem(item); + this._set.removeItemAt(index, this._inBatch); + }, + + destruct: function bv_destruct() { + this._bookmarkService.removeObserver(this._changes); + Services.obs.removeObserver(this, "metro_viewstate_changed"); + window.removeEventListener('MozAppbarDismissing', this, false); + window.removeEventListener('BookmarksNeedsRefresh', this, false); + }, + + doActionOnSelectedTiles: function bv_doActionOnSelectedTiles(aActionName, aEvent) { + let tileGroup = this._set; + let selectedTiles = tileGroup.selectedItems; + + switch (aActionName){ + case "delete": + Array.forEach(selectedTiles, function(aNode) { + if (!this._toRemove) { + this._toRemove = []; + } + + let itemId = this._getBookmarkIdForItem(aNode); + + this._toRemove.push(itemId); + this.removeBookmark(itemId); + }, this); + + // stop the appbar from dismissing + aEvent.preventDefault(); + + // at next tick, re-populate the context appbar. + setTimeout(function(){ + // fire a MozContextActionsChange event to update the context appbar + let event = document.createEvent("Events"); + // we need the restore button to show (the tile node will go away though) + event.actions = ["restore"]; + event.initEvent("MozContextActionsChange", true, false); + tileGroup.dispatchEvent(event); + }, 0); + break; + + case "restore": + // clear toRemove and let _sendNeedsRefresh update the items. + this._toRemove = null; + break; + + case "unpin": + Array.forEach(selectedTiles, function(aNode) { + let itemId = this._getBookmarkIdForItem(aNode); + + if (this._filterUnpinned) + this.removeBookmark(itemId); + + this._pinHelper.setUnpinned(itemId); + }, this); + break; + + case "pin": + Array.forEach(selectedTiles, function(aNode) { + let itemId = this._getBookmarkIdForItem(aNode); + + this._pinHelper.setPinned(itemId); + }, this); + break; + + default: + return; + } + + // Send refresh event so all view are in sync. + this._sendNeedsRefresh(); + }, + + // nsIObservers + observe: function (aSubject, aTopic, aState) { + switch(aTopic) { + case "metro_viewstate_changed": + this.onViewStateChange(aState); + break; + } + }, + + handleEvent: function bv_handleEvent(aEvent) { + switch (aEvent.type){ + case "MozAppbarDismissing": + // If undo wasn't pressed, time to do definitive actions. + if (this._toRemove) { + for (let bookmarkId of this._toRemove) { + this._bookmarkService.removeItem(bookmarkId); + } + this._toRemove = null; + } + break; + + case "BookmarksNeedsRefresh": + this.getBookmarks(true); + break; + } + } +}); + +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.getBookmarks(); + }, + + uninit: function uninit() { + this._view.destruct(); + }, + + show: function show() { + this._grid.arrangeItems(); + } +}; + +/** + * Observes bookmark changes and keeps a linked BookmarksView updated. + * + * @param aView An instance of BookmarksView. + */ +function BookmarkChangeListener(aView) { + this._view = aView; +} + +BookmarkChangeListener.prototype = { + ////////////////////////////////////////////////////////////////////////////// + //// nsINavBookmarkObserver + onBeginUpdateBatch: function () { }, + onEndUpdateBatch: function () { }, + + onItemAdded: function bCL_onItemAdded(aItemId, aParentId, aIndex, aItemType, aURI, aTitle, aDateAdded, aGUID, aParentGUID) { + this._view.getBookmarks(true); + }, + + onItemChanged: function bCL_onItemChanged(aItemId, aProperty, aIsAnnotationProperty, aNewValue, aLastModified, aItemType, aParentId, aGUID, aParentGUID) { + let itemIndex = PlacesUtils.bookmarks.getItemIndex(aItemId); + if (!this._view.inCurrentView(aParentId, aItemId)) + return; + + this._view.updateBookmark(aItemId); + }, + + onItemMoved: function bCL_onItemMoved(aItemId, aOldParentId, aOldIndex, aNewParentId, aNewIndex, aItemType, aGUID, aOldParentGUID, aNewParentGUID) { + let wasInView = this._view.inCurrentView(aOldParentId, aItemId); + let nowInView = this._view.inCurrentView(aNewParentId, aItemId); + + if (!wasInView && nowInView) + this._view.addBookmark(aItemId); + + if (wasInView && !nowInView) + this._view.removeBookmark(aItemId); + + this._view.getBookmarks(true); + }, + + onBeforeItemRemoved: function (aItemId, aItemType, aParentId, aGUID, aParentGUID) { }, + onItemRemoved: function bCL_onItemRemoved(aItemId, aParentId, aIndex, aItemType, aURI, aGUID, aParentGUID) { + if (!this._view.inCurrentView(aParentId, aItemId)) + return; + + this._view.removeBookmark(aItemId); + this._view.getBookmarks(true); + }, + + onItemVisited: function(aItemId, aVisitId, aTime, aTransitionType, aURI, aParentId, aGUID, aParentGUID) { }, + + ////////////////////////////////////////////////////////////////////////////// + //// nsISupports + QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarkObserver]) +}; diff --git a/browser/metro/base/content/history.js b/browser/metro/base/content/startui/HistoryView.js similarity index 100% rename from browser/metro/base/content/history.js rename to browser/metro/base/content/startui/HistoryView.js diff --git a/browser/metro/base/content/RemoteTabs.js b/browser/metro/base/content/startui/RemoteTabsView.js similarity index 100% rename from browser/metro/base/content/RemoteTabs.js rename to browser/metro/base/content/startui/RemoteTabsView.js diff --git a/browser/metro/base/content/startui/Start.xul b/browser/metro/base/content/startui/Start.xul new file mode 100644 index 00000000000..e037755d734 --- /dev/null +++ b/browser/metro/base/content/startui/Start.xul @@ -0,0 +1,72 @@ + + + + + + + + + + +%globalDTD; + +%browserDTD; + +%brandDTD; +#ifdef MOZ_SERVICES_SYNC + +%syncBrandDTD; + +%syncDTD; +#endif +]> + + + +