1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
19 * Portions created by the Initial Developer are Copyright (C) 2009
20 * the Initial Developer. All Rights Reserved.
23 * Jim Mathies <jmathies@mozilla.com>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either the GNU General Public License Version 2 or later (the "GPL"), or
27 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
39 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
45 const Cc = Components.classes;
46 const Ci = Components.interfaces;
49 const PREF_TASKBAR_BRANCH = "browser.taskbar.lists.";
50 const PREF_TASKBAR_ENABLED = "enabled";
51 const PREF_TASKBAR_ITEMCOUNT = "maxListItemCount";
52 const PREF_TASKBAR_FREQUENT = "frequent.enabled";
53 const PREF_TASKBAR_RECENT = "recent.enabled";
54 const PREF_TASKBAR_TASKS = "tasks.enabled";
56 // The amount of time between updates for jump lists
57 const TIMER_TASKBAR_REFRESH = 1000*60*2; // 2 min.
63 let EXPORTED_SYMBOLS = [
71 XPCOMUtils.defineLazyGetter(this, "_prefs", function() {
72 return Cc["@mozilla.org/preferences-service;1"]
73 .getService(Ci.nsIPrefService)
74 .getBranch(PREF_TASKBAR_BRANCH)
75 .QueryInterface(Ci.nsIPrefBranch2);
78 XPCOMUtils.defineLazyGetter(this, "_stringBundle", function() {
79 return Cc["@mozilla.org/intl/stringbundle;1"]
80 .getService(Ci.nsIStringBundleService)
81 .createBundle("chrome://browser/locale/taskbar.properties");
84 XPCOMUtils.defineLazyServiceGetter(this, "_taskbarService",
85 "@mozilla.org/windows-taskbar;1",
88 XPCOMUtils.defineLazyServiceGetter(this, "_navHistoryService",
89 "@mozilla.org/browser/nav-history-service;1",
90 "nsINavHistoryService");
92 XPCOMUtils.defineLazyServiceGetter(this, "_observerService",
93 "@mozilla.org/observer-service;1",
94 "nsIObserverService");
96 XPCOMUtils.defineLazyServiceGetter(this, "_directoryService",
97 "@mozilla.org/file/directory_service;1",
100 XPCOMUtils.defineLazyServiceGetter(this, "_ioService",
101 "@mozilla.org/network/io-service;1",
108 function _getString(name) {
109 return _stringBundle.GetStringFromName(name);
112 /////////////////////////////////////////////////////////////////////////////
113 // Task list configuration data object.
117 * Task configuration options: title, description, args, iconIndex, open, close.
119 * title - Task title displayed in the list. (strings in the table are temp fillers.)
120 * description - Tooltip description on the list item.
121 * args - Command line args to invoke the task.
122 * iconIndex - Optional win icon index into the main application for the
124 * open - Boolean indicates if the command should be visible after the browser opens.
125 * close - Boolean indicates if the command should be visible after the browser closes.
129 get title() _getString("taskbar.tasks.newTab.label"),
130 get description() _getString("taskbar.tasks.newTab.description"),
131 args: "-new-tab about:blank",
132 iconIndex: 0, // Fx app icon
134 close: false, // The jump list already has an app launch icon
139 get title() _getString("taskbar.tasks.newWindow.label"),
140 get description() _getString("taskbar.tasks.newWindow.description"),
142 iconIndex: 0, // Fx app icon
144 close: false, // no point
148 /////////////////////////////////////////////////////////////////////////////
151 var WinTaskbarJumpList =
155 _shuttingDown: false,
158 * Startup, shutdown, and update
161 startup: function WTBJL_startup() {
162 // exit if this isn't win7 or higher.
163 if (!this._initTaskbar())
166 // Store our task list config data
167 this._tasks = tasksCfg;
169 // retrieve taskbar related prefs.
170 this._refreshPrefs();
172 // observer for private browsing and our prefs branch
175 // jump list refresh timer
182 update: function WTBJL_update() {
183 // are we disabled via prefs? don't do anything!
187 // hide jump lists when we're enabled and in private browsing mode
188 if (this._inPrivateBrowsing) {
189 this._deleteActiveJumpList();
193 // do what we came here to do, update the taskbar jumplist
197 _shutdown: function WTBJL__shutdown() {
198 this._shuttingDown = true;
207 _buildList: function WTBJL__buildList() {
208 // anything to build?
209 if (!this._showFrequent && !this._showRecent && !this._showTasks) {
210 // don't leave the last list hanging on the taskbar.
211 this._deleteActiveJumpList();
215 if (!this._startBuild())
218 if (this._showTasks && !this._buildTasks())
221 // Space for frequent items takes priority over recent.
223 if (this._showFrequent && !this._buildFrequent())
226 if (this._showRecent && !this._buildRecent())
233 * Taskbar api wrappers
236 _startBuild: function WTBJL__startBuild() {
237 var removedItems = Cc["@mozilla.org/array;1"].
238 createInstance(Ci.nsIMutableArray);
239 this._builder.abortListBuild();
240 if (this._builder.initListBuild(removedItems)) {
241 // Prior to building, delete removed items from history.
242 this._clearHistory(removedItems);
248 _commitBuild: function WTBJL__commitBuild() {
249 if (!this._builder.commitListBuild())
250 this._builder.abortListBuild();
253 _buildTasks: function WTBJL__buildTasks() {
254 var items = Cc["@mozilla.org/array;1"].
255 createInstance(Ci.nsIMutableArray);
256 this._tasks.forEach(function (task) {
257 if ((this._shuttingDown && !task.close) || (!this._shuttingDown && !task.open))
259 var item = this._getHandlerAppItem(task.title, task.description,
260 task.args, task.iconIndex);
261 items.appendElement(item, false);
264 if (items.length == 0)
267 return this._builder.addListToBuild(this._builder.JUMPLIST_CATEGORY_TASKS, items);
270 _buildCustom: function WTBJL__buildCustom(title, items) {
271 if (items.length == 0)
273 return this._builder.addListToBuild(this._builder.JUMPLIST_CATEGORY_CUSTOMLIST, items, title);
276 _buildFrequent: function WTBJL__buildFrequent() {
277 // Windows supports default frequent and recent lists,
278 // but those depend on internal windows visit tracking
279 // which we don't populate. So we build our own custom
280 // frequent and recent lists using our nav history data.
282 var items = Cc["@mozilla.org/array;1"].
283 createInstance(Ci.nsIMutableArray);
284 var list = this._getNavFrequent(this._maxItemCount);
286 if (!list || list.length == 0)
289 // track frequent items so that we don't add them to
291 this._frequentHashList = [];
293 list.forEach(function (entry) {
294 let shortcut = this._getHandlerAppItem(entry.title, entry.title, entry.uri, 1);
295 items.appendElement(shortcut, false);
296 this._frequentHashList.push(entry.uri);
298 return this._buildCustom(_getString("taskbar.frequent.label"), items);
301 _buildRecent: function WTBJL__buildRecent() {
302 var items = Cc["@mozilla.org/array;1"].
303 createInstance(Ci.nsIMutableArray);
304 var list = this._getNavRecent(this._maxItemCount*2);
306 if (!list || list.length == 0)
309 for (let idx = 0; idx < list.length; idx++) {
310 let entry = list[idx];
311 let shortcut = this._getHandlerAppItem(entry.title, entry.title, entry.uri, 1);
312 if (idx >= this._maxItemCount)
314 // do not add items to recent that have already been added
316 if (this._frequentHashList &&
317 this._frequentHashList.indexOf(entry.uri) != -1)
319 items.appendElement(shortcut, false);
321 return this._buildCustom(_getString("taskbar.recent.label"), items);
324 _deleteActiveJumpList: function WTBJL__deleteAJL() {
325 return this._builder.deleteActiveList();
329 * Jump list item creation helpers
332 _getHandlerAppItem: function WTBJL__getHandlerAppItem(name, description, args, icon) {
333 var file = _directoryService.get("XCurProcD", Ci.nsILocalFile);
335 // XXX where can we grab this from in the build? Do we need to?
336 file.append("firefox.exe");
338 var handlerApp = Cc["@mozilla.org/uriloader/local-handler-app;1"].
339 createInstance(Ci.nsILocalHandlerApp);
340 handlerApp.executable = file;
341 // handlers default to the leaf name if a name is not specified
342 if (name.length != 0)
343 handlerApp.name = name;
344 handlerApp.detailedDescription = description;
345 handlerApp.appendParameter(args);
347 var item = Cc["@mozilla.org/windows-jumplistshortcut;1"].
348 createInstance(Ci.nsIJumpListShortcut);
349 item.app = handlerApp;
350 item.iconIndex = icon;
354 _getSeparatorItem: function WTBJL__getSeparatorItem() {
355 var item = Cc["@mozilla.org/windows-jumplistseparator;1"].
356 createInstance(Ci.nsIJumpListSeparator);
361 * Nav history helpers
364 _getNavFrequent: function WTBJL__getNavFrequent(depth) {
365 var options = _navHistoryService.getNewQueryOptions();
366 var query = _navHistoryService.getNewQuery();
368 query.beginTimeReference = query.TIME_RELATIVE_NOW;
369 query.beginTime = -24 * 30 * 60 * 60 * 1000000; // one month
370 query.endTimeReference = query.TIME_RELATIVE_NOW;
372 options.maxResults = depth;
373 options.queryType = options.QUERY_TYPE_HISTORY;
374 options.sortingMode = options.SORT_BY_VISITCOUNT_DESCENDING;
375 options.resultType = options.RESULT_TYPE_URI;
377 var result = _navHistoryService.executeQuery(query, options);
381 var rootNode = result.root;
382 rootNode.containerOpen = true;
384 for (let idx = 0; idx < rootNode.childCount; idx++) {
385 let node = rootNode.getChild(idx);
386 list.push({uri: node.uri, title: node.title});
388 rootNode.containerOpen = false;
393 _getNavRecent: function WTBJL__getNavRecent(depth) {
394 var options = _navHistoryService.getNewQueryOptions();
395 var query = _navHistoryService.getNewQuery();
397 query.beginTimeReference = query.TIME_RELATIVE_NOW;
398 query.beginTime = -48 * 60 * 60 * 1000000; // two days
399 query.endTimeReference = query.TIME_RELATIVE_NOW;
401 options.maxResults = depth;
402 options.queryType = options.QUERY_TYPE_HISTORY;
403 options.sortingMode = options.SORT_BY_LASTMODIFIED_DESCENDING;
404 options.resultType = options.RESULT_TYPE_URI;
406 var result = _navHistoryService.executeQuery(query, options);
410 var rootNode = result.root;
411 rootNode.containerOpen = true;
413 for (var idx = 0; idx < rootNode.childCount; idx++) {
414 var node = rootNode.getChild(idx);
415 list.push({uri: node.uri, title: node.title});
417 rootNode.containerOpen = false;
422 _clearHistory: function WTBJL__clearHistory(items) {
425 var enum = items.enumerate();
426 while (enum.hasMoreElements()) {
427 let oldItem = enum.getNext().QueryInterface(Ci.nsIJumpListShortcut);
429 try { // in case we get a bad uri
430 let uriSpec = oldItem.app.getParameter(0);
431 _navHistoryService.QueryInterface(Ci.nsIBrowserHistory).removePage(
432 _ioService.newURI(uriSpec));
442 _refreshPrefs: function WTBJL__refreshPrefs() {
443 this._enabled = _prefs.getBoolPref(PREF_TASKBAR_ENABLED);
444 this._showFrequent = _prefs.getBoolPref(PREF_TASKBAR_FREQUENT);
445 this._showRecent = _prefs.getBoolPref(PREF_TASKBAR_RECENT);
446 this._showTasks = _prefs.getBoolPref(PREF_TASKBAR_TASKS);
447 this._maxItemCount = _prefs.getIntPref(PREF_TASKBAR_ITEMCOUNT);
449 // retrieve the initial status of the Private Browsing mode.
450 this._inPrivateBrowsing = Cc["@mozilla.org/privatebrowsing;1"].
451 getService(Ci.nsIPrivateBrowsingService).
452 privateBrowsingEnabled;
456 * Init and shutdown utilities
459 _initTaskbar: function WTBJL__initTaskbar() {
460 this._builder = _taskbarService.createJumpListBuilder();
461 if (!this._builder || !this._builder.available)
467 _initObs: function WTBJL__initObs() {
468 _observerService.addObserver(this, "private-browsing", false);
469 _observerService.addObserver(this, "quit-application-granted", false);
470 _prefs.addObserver("", this, false);
473 _freeObs: function WTBJL__freeObs() {
474 _observerService.removeObserver(this, "private-browsing");
475 _observerService.removeObserver(this, "quit-application-granted");
476 _prefs.removeObserver("", this);
479 _initTimer: function WTBJL__initTimer(aTimer) {
480 this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
481 this._timer.initWithCallback(this, TIMER_TASKBAR_REFRESH, this._timer.TYPE_REPEATING_SLACK);
484 _free: function WTBJL__free() {
486 delete this._builder;
491 * Notification handlers
494 notify: function WTBJL_notify(aTimer) {
498 observe: function WTBJL_observe(aSubject, aTopic, aData) {
500 case "nsPref:changed":
501 this._refreshPrefs();
505 case "quit-application-granted":
509 case "browser:purge-session-history":
513 case "private-browsing":
516 this._inPrivateBrowsing = true;
519 this._inPrivateBrowsing = false;