michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: const Ci = Components.interfaces; michael@0: const Cc = Components.classes; michael@0: const Cr = Components.results; michael@0: const Cu = Components.utils; michael@0: michael@0: Cu.import("resource://services-sync/main.js"); michael@0: Cu.import("resource://gre/modules/DownloadUtils.jsm"); michael@0: michael@0: let gSyncQuota = { michael@0: michael@0: init: function init() { michael@0: this.bundle = document.getElementById("quotaStrings"); michael@0: let caption = document.getElementById("treeCaption"); michael@0: caption.firstChild.nodeValue = this.bundle.getString("quota.treeCaption.label"); michael@0: michael@0: gUsageTreeView.init(); michael@0: this.tree = document.getElementById("usageTree"); michael@0: this.tree.view = gUsageTreeView; michael@0: michael@0: this.loadData(); michael@0: }, michael@0: michael@0: loadData: function loadData() { michael@0: this._usage_req = Weave.Service.getStorageInfo(Weave.INFO_COLLECTION_USAGE, michael@0: function (error, usage) { michael@0: delete gSyncQuota._usage_req; michael@0: // displayUsageData handles null values, so no need to check 'error'. michael@0: gUsageTreeView.displayUsageData(usage); michael@0: }); michael@0: michael@0: let usageLabel = document.getElementById("usageLabel"); michael@0: let bundle = this.bundle; michael@0: michael@0: this._quota_req = Weave.Service.getStorageInfo(Weave.INFO_QUOTA, michael@0: function (error, quota) { michael@0: delete gSyncQuota._quota_req; michael@0: michael@0: if (error) { michael@0: usageLabel.value = bundle.getString("quota.usageError.label"); michael@0: return; michael@0: } michael@0: let used = gSyncQuota.convertKB(quota[0]); michael@0: if (!quota[1]) { michael@0: // No quota on the server. michael@0: usageLabel.value = bundle.getFormattedString( michael@0: "quota.usageNoQuota.label", used); michael@0: return; michael@0: } michael@0: let percent = Math.round(100 * quota[0] / quota[1]); michael@0: let total = gSyncQuota.convertKB(quota[1]); michael@0: usageLabel.value = bundle.getFormattedString( michael@0: "quota.usagePercentage.label", [percent].concat(used).concat(total)); michael@0: }); michael@0: }, michael@0: michael@0: onCancel: function onCancel() { michael@0: if (this._usage_req) { michael@0: this._usage_req.abort(); michael@0: } michael@0: if (this._quota_req) { michael@0: this._quota_req.abort(); michael@0: } michael@0: return true; michael@0: }, michael@0: michael@0: onAccept: function onAccept() { michael@0: let engines = gUsageTreeView.getEnginesToDisable(); michael@0: for each (let engine in engines) { michael@0: Weave.Service.engineManager.get(engine).enabled = false; michael@0: } michael@0: if (engines.length) { michael@0: // The 'Weave' object will disappear once the window closes. michael@0: let Service = Weave.Service; michael@0: Weave.Utils.nextTick(function() { Service.sync(); }); michael@0: } michael@0: return this.onCancel(); michael@0: }, michael@0: michael@0: convertKB: function convertKB(value) { michael@0: return DownloadUtils.convertByteUnits(value * 1024); michael@0: } michael@0: michael@0: }; michael@0: michael@0: let gUsageTreeView = { michael@0: michael@0: _ignored: {keys: true, michael@0: meta: true, michael@0: clients: true}, michael@0: michael@0: /* michael@0: * Internal data structures underlaying the tree. michael@0: */ michael@0: _collections: [], michael@0: _byname: {}, michael@0: michael@0: init: function init() { michael@0: let retrievingLabel = gSyncQuota.bundle.getString("quota.retrieving.label"); michael@0: for each (let engine in Weave.Service.engineManager.getEnabled()) { michael@0: if (this._ignored[engine.name]) michael@0: continue; michael@0: michael@0: // Some engines use the same pref, which means they can only be turned on michael@0: // and off together. We need to combine them here as well. michael@0: let existing = this._byname[engine.prefName]; michael@0: if (existing) { michael@0: existing.engines.push(engine.name); michael@0: continue; michael@0: } michael@0: michael@0: let obj = {name: engine.prefName, michael@0: title: this._collectionTitle(engine), michael@0: engines: [engine.name], michael@0: enabled: true, michael@0: sizeLabel: retrievingLabel}; michael@0: this._collections.push(obj); michael@0: this._byname[engine.prefName] = obj; michael@0: } michael@0: }, michael@0: michael@0: _collectionTitle: function _collectionTitle(engine) { michael@0: try { michael@0: return gSyncQuota.bundle.getString( michael@0: "collection." + engine.prefName + ".label"); michael@0: } catch (ex) { michael@0: return engine.Name; michael@0: } michael@0: }, michael@0: michael@0: /* michael@0: * Process the quota information as returned by info/collection_usage. michael@0: */ michael@0: displayUsageData: function displayUsageData(data) { michael@0: for each (let coll in this._collections) { michael@0: coll.size = 0; michael@0: // If we couldn't retrieve any data, just blank out the label. michael@0: if (!data) { michael@0: coll.sizeLabel = ""; michael@0: continue; michael@0: } michael@0: michael@0: for each (let engineName in coll.engines) michael@0: coll.size += data[engineName] || 0; michael@0: let sizeLabel = ""; michael@0: sizeLabel = gSyncQuota.bundle.getFormattedString( michael@0: "quota.sizeValueUnit.label", gSyncQuota.convertKB(coll.size)); michael@0: coll.sizeLabel = sizeLabel; michael@0: } michael@0: let sizeColumn = this.treeBox.columns.getNamedColumn("size"); michael@0: this.treeBox.invalidateColumn(sizeColumn); michael@0: }, michael@0: michael@0: /* michael@0: * Handle click events on the tree. michael@0: */ michael@0: onTreeClick: function onTreeClick(event) { michael@0: if (event.button == 2) michael@0: return; michael@0: michael@0: let row = {}, col = {}; michael@0: this.treeBox.getCellAt(event.clientX, event.clientY, row, col, {}); michael@0: if (col.value && col.value.id == "enabled") michael@0: this.toggle(row.value); michael@0: }, michael@0: michael@0: /* michael@0: * Toggle enabled state of an engine. michael@0: */ michael@0: toggle: function toggle(row) { michael@0: // Update the tree michael@0: let collection = this._collections[row]; michael@0: collection.enabled = !collection.enabled; michael@0: this.treeBox.invalidateRow(row); michael@0: michael@0: // Display which ones will be removed michael@0: let freeup = 0; michael@0: let toremove = []; michael@0: for each (collection in this._collections) { michael@0: if (collection.enabled) michael@0: continue; michael@0: toremove.push(collection.name); michael@0: freeup += collection.size; michael@0: } michael@0: michael@0: let caption = document.getElementById("treeCaption"); michael@0: if (!toremove.length) { michael@0: caption.className = ""; michael@0: caption.firstChild.nodeValue = gSyncQuota.bundle.getString( michael@0: "quota.treeCaption.label"); michael@0: return; michael@0: } michael@0: michael@0: toremove = [this._byname[coll].title for each (coll in toremove)]; michael@0: toremove = toremove.join(gSyncQuota.bundle.getString("quota.list.separator")); michael@0: caption.firstChild.nodeValue = gSyncQuota.bundle.getFormattedString( michael@0: "quota.removal.label", [toremove]); michael@0: if (freeup) michael@0: caption.firstChild.nodeValue += gSyncQuota.bundle.getFormattedString( michael@0: "quota.freeup.label", gSyncQuota.convertKB(freeup)); michael@0: caption.className = "captionWarning"; michael@0: }, michael@0: michael@0: /* michael@0: * Return a list of engines (or rather their pref names) that should be michael@0: * disabled. michael@0: */ michael@0: getEnginesToDisable: function getEnginesToDisable() { michael@0: return [coll.name for each (coll in this._collections) if (!coll.enabled)]; michael@0: }, michael@0: michael@0: // nsITreeView michael@0: michael@0: get rowCount() { michael@0: return this._collections.length; michael@0: }, michael@0: michael@0: getRowProperties: function(index) { return ""; }, michael@0: getCellProperties: function(row, col) { return ""; }, michael@0: getColumnProperties: function(col) { return ""; }, michael@0: isContainer: function(index) { return false; }, michael@0: isContainerOpen: function(index) { return false; }, michael@0: isContainerEmpty: function(index) { return false; }, michael@0: isSeparator: function(index) { return false; }, michael@0: isSorted: function() { return false; }, michael@0: canDrop: function(index, orientation, dataTransfer) { return false; }, michael@0: drop: function(row, orientation, dataTransfer) {}, michael@0: getParentIndex: function(rowIndex) {}, michael@0: hasNextSibling: function(rowIndex, afterIndex) { return false; }, michael@0: getLevel: function(index) { return 0; }, michael@0: getImageSrc: function(row, col) {}, michael@0: michael@0: getCellValue: function(row, col) { michael@0: return this._collections[row].enabled; michael@0: }, michael@0: michael@0: getCellText: function getCellText(row, col) { michael@0: let collection = this._collections[row]; michael@0: switch (col.id) { michael@0: case "collection": michael@0: return collection.title; michael@0: case "size": michael@0: return collection.sizeLabel; michael@0: default: michael@0: return ""; michael@0: } michael@0: }, michael@0: michael@0: setTree: function setTree(tree) { michael@0: this.treeBox = tree; michael@0: }, michael@0: michael@0: toggleOpenState: function(index) {}, michael@0: cycleHeader: function(col) {}, michael@0: selectionChanged: function() {}, michael@0: cycleCell: function(row, col) {}, michael@0: isEditable: function(row, col) { return false; }, michael@0: isSelectable: function (row, col) { return false; }, michael@0: setCellValue: function(row, col, value) {}, michael@0: setCellText: function(row, col, value) {}, michael@0: performAction: function(action) {}, michael@0: performActionOnRow: function(action, row) {}, michael@0: performActionOnCell: function(action, row, col) {} michael@0: michael@0: };