/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is mozilla.org code. * * The Initial Developer of the Original Code is * Mozilla Foundation. * Portions created by the Initial Developer are Copyright (C) 2009 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Jim Mathies * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); /** * Constants */ const Cc = Components.classes; const Ci = Components.interfaces; // Prefs const PREF_TASKBAR_BRANCH = "browser.taskbar.lists."; const PREF_TASKBAR_ENABLED = "enabled"; const PREF_TASKBAR_ITEMCOUNT = "maxListItemCount"; const PREF_TASKBAR_FREQUENT = "frequent.enabled"; const PREF_TASKBAR_RECENT = "recent.enabled"; const PREF_TASKBAR_TASKS = "tasks.enabled"; // The amount of time between updates for jump lists const TIMER_TASKBAR_REFRESH = 1000*60*2; // 2 min. /** * Exports */ let EXPORTED_SYMBOLS = [ "WinTaskbarJumpList", ]; /** * Smart getters */ XPCOMUtils.defineLazyGetter(this, "_prefs", function() { return Cc["@mozilla.org/preferences-service;1"] .getService(Ci.nsIPrefService) .getBranch(PREF_TASKBAR_BRANCH) .QueryInterface(Ci.nsIPrefBranch2); }); XPCOMUtils.defineLazyGetter(this, "_stringBundle", function() { return Cc["@mozilla.org/intl/stringbundle;1"] .getService(Ci.nsIStringBundleService) .createBundle("chrome://browser/locale/taskbar.properties"); }); XPCOMUtils.defineLazyServiceGetter(this, "_taskbarService", "@mozilla.org/windows-taskbar;1", "nsIWinTaskbar"); XPCOMUtils.defineLazyServiceGetter(this, "_navHistoryService", "@mozilla.org/browser/nav-history-service;1", "nsINavHistoryService"); XPCOMUtils.defineLazyServiceGetter(this, "_observerService", "@mozilla.org/observer-service;1", "nsIObserverService"); XPCOMUtils.defineLazyServiceGetter(this, "_directoryService", "@mozilla.org/file/directory_service;1", "nsIProperties"); XPCOMUtils.defineLazyServiceGetter(this, "_ioService", "@mozilla.org/network/io-service;1", "nsIIOService"); /** * Global functions */ function _getString(name) { return _stringBundle.GetStringFromName(name); } ///////////////////////////////////////////////////////////////////////////// // Task list configuration data object. var tasksCfg = [ /** * Task configuration options: title, description, args, iconIndex, open, close. * * title - Task title displayed in the list. (strings in the table are temp fillers.) * description - Tooltip description on the list item. * args - Command line args to invoke the task. * iconIndex - Optional win icon index into the main application for the * list item. * open - Boolean indicates if the command should be visible after the browser opens. * close - Boolean indicates if the command should be visible after the browser closes. */ // Open new window { get title() _getString("taskbar.tasks.newTab.label"), get description() _getString("taskbar.tasks.newTab.description"), args: "-new-tab about:blank", iconIndex: 0, // Fx app icon open: true, close: false, // The jump list already has an app launch icon }, // Open new tab { get title() _getString("taskbar.tasks.newWindow.label"), get description() _getString("taskbar.tasks.newWindow.description"), args: "-browser", iconIndex: 0, // Fx app icon open: true, close: false, // no point }, ]; ///////////////////////////////////////////////////////////////////////////// // Implementation var WinTaskbarJumpList = { _builder: null, _tasks: null, _shuttingDown: false, /** * Startup, shutdown, and update */ startup: function WTBJL_startup() { // exit if this isn't win7 or higher. if (!this._initTaskbar()) return; // Store our task list config data this._tasks = tasksCfg; // retrieve taskbar related prefs. this._refreshPrefs(); // observer for private browsing and our prefs branch this._initObs(); // jump list refresh timer this._initTimer(); // build the list this.update(); }, update: function WTBJL_update() { // are we disabled via prefs? don't do anything! if (!this._enabled) return; // hide jump lists when we're enabled and in private browsing mode if (this._inPrivateBrowsing) { this._deleteActiveJumpList(); return; } // do what we came here to do, update the taskbar jumplist this._buildList(); }, _shutdown: function WTBJL__shutdown() { this._shuttingDown = true; this.update(); this._free(); }, /** * List building */ _buildList: function WTBJL__buildList() { // anything to build? if (!this._showFrequent && !this._showRecent && !this._showTasks) { // don't leave the last list hanging on the taskbar. this._deleteActiveJumpList(); return; } if (!this._startBuild()) return; if (this._showTasks && !this._buildTasks()) return; // Space for frequent items takes priority over recent. if (this._showFrequent && !this._buildFrequent()) return; if (this._showRecent && !this._buildRecent()) return; this._commitBuild(); }, /** * Taskbar api wrappers */ _startBuild: function WTBJL__startBuild() { var removedItems = Cc["@mozilla.org/array;1"]. createInstance(Ci.nsIMutableArray); this._builder.abortListBuild(); if (this._builder.initListBuild(removedItems)) { // Prior to building, delete removed items from history. this._clearHistory(removedItems); return true; } return false; }, _commitBuild: function WTBJL__commitBuild() { if (!this._builder.commitListBuild()) this._builder.abortListBuild(); }, _buildTasks: function WTBJL__buildTasks() { var items = Cc["@mozilla.org/array;1"]. createInstance(Ci.nsIMutableArray); this._tasks.forEach(function (task) { if ((this._shuttingDown && !task.close) || (!this._shuttingDown && !task.open)) return; var item = this._getHandlerAppItem(task.title, task.description, task.args, task.iconIndex); items.appendElement(item, false); }, this); if (items.length == 0) return true; return this._builder.addListToBuild(this._builder.JUMPLIST_CATEGORY_TASKS, items); }, _buildCustom: function WTBJL__buildCustom(title, items) { if (items.length == 0) return true; return this._builder.addListToBuild(this._builder.JUMPLIST_CATEGORY_CUSTOMLIST, items, title); }, _buildFrequent: function WTBJL__buildFrequent() { // Windows supports default frequent and recent lists, // but those depend on internal windows visit tracking // which we don't populate. So we build our own custom // frequent and recent lists using our nav history data. var items = Cc["@mozilla.org/array;1"]. createInstance(Ci.nsIMutableArray); var list = this._getNavFrequent(this._maxItemCount); if (!list || list.length == 0) return true; // track frequent items so that we don't add them to // the recent list. this._frequentHashList = []; list.forEach(function (entry) { let shortcut = this._getHandlerAppItem(entry.title, entry.title, entry.uri, 1); items.appendElement(shortcut, false); this._frequentHashList.push(entry.uri); }, this); return this._buildCustom(_getString("taskbar.frequent.label"), items); }, _buildRecent: function WTBJL__buildRecent() { var items = Cc["@mozilla.org/array;1"]. createInstance(Ci.nsIMutableArray); var list = this._getNavRecent(this._maxItemCount*2); if (!list || list.length == 0) return true; let count = 0; for (let idx = 0; idx < list.length; idx++) { if (count >= this._maxItemCount) break; let entry = list[idx]; // do not add items to recent that have already been added // to frequent. if (this._frequentHashList && this._frequentHashList.indexOf(entry.uri) != -1) continue; let shortcut = this._getHandlerAppItem(entry.title, entry.title, entry.uri, 1); items.appendElement(shortcut, false); count++; } return this._buildCustom(_getString("taskbar.recent.label"), items); }, _deleteActiveJumpList: function WTBJL__deleteAJL() { return this._builder.deleteActiveList(); }, /** * Jump list item creation helpers */ _getHandlerAppItem: function WTBJL__getHandlerAppItem(name, description, args, icon) { var file = _directoryService.get("XCurProcD", Ci.nsILocalFile); // XXX where can we grab this from in the build? Do we need to? file.append("firefox.exe"); var handlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"]. createInstance(Ci.nsILocalHandlerApp); handlerApp.executable = file; // handlers default to the leaf name if a name is not specified if (name && name.length != 0) handlerApp.name = name; handlerApp.detailedDescription = description; handlerApp.appendParameter(args); var item = Cc["@mozilla.org/windows-jumplistshortcut;1"]. createInstance(Ci.nsIJumpListShortcut); item.app = handlerApp; item.iconIndex = icon; return item; }, _getSeparatorItem: function WTBJL__getSeparatorItem() { var item = Cc["@mozilla.org/windows-jumplistseparator;1"]. createInstance(Ci.nsIJumpListSeparator); return item; }, /** * Nav history helpers */ _getNavFrequent: function WTBJL__getNavFrequent(depth) { var options = _navHistoryService.getNewQueryOptions(); var query = _navHistoryService.getNewQuery(); query.beginTimeReference = query.TIME_RELATIVE_NOW; query.beginTime = -24 * 30 * 60 * 60 * 1000000; // one month query.endTimeReference = query.TIME_RELATIVE_NOW; options.maxResults = depth; options.queryType = options.QUERY_TYPE_HISTORY; options.sortingMode = options.SORT_BY_VISITCOUNT_DESCENDING; options.resultType = options.RESULT_TYPE_URI; var result = _navHistoryService.executeQuery(query, options); var list = []; var rootNode = result.root; rootNode.containerOpen = true; for (let idx = 0; idx < rootNode.childCount; idx++) { let node = rootNode.getChild(idx); list.push({uri: node.uri, title: node.title}); } rootNode.containerOpen = false; return list; }, _getNavRecent: function WTBJL__getNavRecent(depth) { var options = _navHistoryService.getNewQueryOptions(); var query = _navHistoryService.getNewQuery(); query.beginTimeReference = query.TIME_RELATIVE_NOW; query.beginTime = -48 * 60 * 60 * 1000000; // two days query.endTimeReference = query.TIME_RELATIVE_NOW; options.maxResults = depth; options.queryType = options.QUERY_TYPE_HISTORY; options.sortingMode = options.SORT_BY_LASTMODIFIED_DESCENDING; options.resultType = options.RESULT_TYPE_URI; var result = _navHistoryService.executeQuery(query, options); var list = []; var rootNode = result.root; rootNode.containerOpen = true; for (var idx = 0; idx < rootNode.childCount; idx++) { var node = rootNode.getChild(idx); list.push({uri: node.uri, title: node.title}); } rootNode.containerOpen = false; return list; }, _clearHistory: function WTBJL__clearHistory(items) { if (!items) return; var enum = items.enumerate(); while (enum.hasMoreElements()) { let oldItem = enum.getNext().QueryInterface(Ci.nsIJumpListShortcut); if (oldItem) { try { // in case we get a bad uri let uriSpec = oldItem.app.getParameter(0); _navHistoryService.QueryInterface(Ci.nsIBrowserHistory).removePage( _ioService.newURI(uriSpec)); } catch (err) { } } } }, /** * Prefs utilities */ _refreshPrefs: function WTBJL__refreshPrefs() { this._enabled = _prefs.getBoolPref(PREF_TASKBAR_ENABLED); this._showFrequent = _prefs.getBoolPref(PREF_TASKBAR_FREQUENT); this._showRecent = _prefs.getBoolPref(PREF_TASKBAR_RECENT); this._showTasks = _prefs.getBoolPref(PREF_TASKBAR_TASKS); this._maxItemCount = _prefs.getIntPref(PREF_TASKBAR_ITEMCOUNT); // retrieve the initial status of the Private Browsing mode. this._inPrivateBrowsing = Cc["@mozilla.org/privatebrowsing;1"]. getService(Ci.nsIPrivateBrowsingService). privateBrowsingEnabled; }, /** * Init and shutdown utilities */ _initTaskbar: function WTBJL__initTaskbar() { this._builder = _taskbarService.createJumpListBuilder(); if (!this._builder || !this._builder.available) return false; return true; }, _initObs: function WTBJL__initObs() { _observerService.addObserver(this, "private-browsing", false); _observerService.addObserver(this, "quit-application-granted", false); _prefs.addObserver("", this, false); }, _freeObs: function WTBJL__freeObs() { _observerService.removeObserver(this, "private-browsing"); _observerService.removeObserver(this, "quit-application-granted"); _prefs.removeObserver("", this); }, _initTimer: function WTBJL__initTimer(aTimer) { this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); this._timer.initWithCallback(this, TIMER_TASKBAR_REFRESH, this._timer.TYPE_REPEATING_SLACK); }, _free: function WTBJL__free() { this._freeObs(); delete this._builder; delete this._timer; }, /** * Notification handlers */ notify: function WTBJL_notify(aTimer) { this.update(); }, observe: function WTBJL_observe(aSubject, aTopic, aData) { switch (aTopic) { case "nsPref:changed": this._refreshPrefs(); this.update(); break; case "quit-application-granted": this._shutdown(); break; case "browser:purge-session-history": this.update(); break; case "private-browsing": switch (aData) { case "enter": this._inPrivateBrowsing = true; break; case "exit": this._inPrivateBrowsing = false; break; } this.update(); break; } }, };