home *** CD-ROM | disk | FTP | other *** search
/ Freelog 112 / FreelogNo112-NovembreDecembre2012.iso / Multimedia / Songbird / Songbird_2.0.0-2311_windows-i686-msvc8.exe / xulrunner / modules / LightweightThemeManager.jsm < prev    next >
Text File  |  2010-07-22  |  11KB  |  378 lines

  1. /* ***** BEGIN LICENSE BLOCK *****
  2.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3.  *
  4.  * The contents of this file are subject to the Mozilla Public License Version
  5.  * 1.1 (the "License"); you may not use this file except in compliance with
  6.  * the License. You may obtain a copy of the License at
  7.  * http://www.mozilla.org/MPL/
  8.  *
  9.  * Software distributed under the License is distributed on an "AS IS" basis,
  10.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  11.  * for the specific language governing rights and limitations under the
  12.  * License.
  13.  *
  14.  * The Original Code is mozilla.org Code.
  15.  *
  16.  * The Initial Developer of the Original Code is
  17.  * Dao Gottwald <dao@mozilla.com>.
  18.  * Portions created by the Initial Developer are Copyright (C) 2009
  19.  * the Initial Developer. All Rights Reserved.
  20.  *
  21.  * Contributor(s):
  22.  *
  23.  * Alternatively, the contents of this file may be used under the terms of
  24.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  25.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  26.  * in which case the provisions of the GPL or the LGPL are applicable instead
  27.  * of those above. If you wish to allow use of your version of this file only
  28.  * under the terms of either the GPL or the LGPL, and not to allow others to
  29.  * use your version of this file under the terms of the MPL, indicate your
  30.  * decision by deleting the provisions above and replace them with the notice
  31.  * and other provisions required by the GPL or the LGPL. If you do not delete
  32.  * the provisions above, a recipient may use your version of this file under
  33.  * the terms of any one of the MPL, the GPL or the LGPL.
  34.  *
  35.  * ***** END LICENSE BLOCK ***** */
  36.  
  37. var EXPORTED_SYMBOLS = ["LightweightThemeManager"];
  38.  
  39. const Cc = Components.classes;
  40. const Ci = Components.interfaces;
  41.  
  42. const MAX_USED_THEMES_COUNT = 8;
  43.  
  44. const MAX_PREVIEW_SECONDS = 30;
  45.  
  46. const MANDATORY = ["id", "name", "headerURL"];
  47. const OPTIONAL = ["footerURL", "textcolor", "accentcolor", "iconURL",
  48.                   "previewURL", "author", "description", "homepageURL",
  49.                   "updateURL", "version"];
  50.  
  51. const PERSIST_ENABLED = true;
  52. const PERSIST_BYPASS_CACHE = false;
  53. const PERSIST_FILES = {
  54.   headerURL: "lightweighttheme-header",
  55.   footerURL: "lightweighttheme-footer"
  56. };
  57.  
  58. __defineGetter__("_prefs", function () {
  59.   delete this._prefs;
  60.   return this._prefs =
  61.          Cc["@mozilla.org/preferences-service;1"]
  62.            .getService(Ci.nsIPrefService).getBranch("lightweightThemes.");
  63. });
  64.  
  65. __defineGetter__("_observerService", function () {
  66.   delete this._observerService;
  67.   return this._observerService =
  68.          Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
  69. });
  70.  
  71. __defineGetter__("_ioService", function () {
  72.   delete this._ioService;
  73.   return this._ioService =
  74.          Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
  75. });
  76.  
  77. var LightweightThemeManager = {
  78.   get usedThemes () {
  79.     try {
  80.       return JSON.parse(_prefs.getCharPref("usedThemes"));
  81.     } catch (e) {
  82.       return [];
  83.     }
  84.   },
  85.  
  86.   get currentTheme () {
  87.     try {
  88.       if (_prefs.getBoolPref("isThemeSelected"))
  89.         var data = this.usedThemes[0];
  90.     } catch (e) {}
  91.  
  92.     return data || null;
  93.   },
  94.  
  95.   get currentThemeForDisplay () {
  96.     var data = this.currentTheme;
  97.  
  98.     if (data && PERSIST_ENABLED) {
  99.       for (let key in PERSIST_FILES) {
  100.         try {
  101.           if (data[key] && _prefs.getBoolPref("persisted." + key))
  102.             data[key] = _getLocalImageURI(PERSIST_FILES[key]).spec
  103.                         + "?" + data.id + ";" + _version(data);
  104.         } catch (e) {}
  105.       }
  106.     }
  107.  
  108.     return data;
  109.   },
  110.  
  111.   set currentTheme (aData) {
  112.     aData = _sanitizeTheme(aData);
  113.  
  114.     let cancel = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
  115.     cancel.data = false;
  116.     _observerService.notifyObservers(cancel, "lightweight-theme-change-requested",
  117.                                      JSON.stringify(aData));
  118.  
  119.     if (aData) {
  120.       let usedThemes = _usedThemesExceptId(aData.id);
  121.       if (cancel.data && _prefs.getBoolPref("isThemeSelected"))
  122.         usedThemes.splice(1, 0, aData);
  123.       else
  124.         usedThemes.unshift(aData);
  125.       _updateUsedThemes(usedThemes);
  126.     }
  127.  
  128.     if (cancel.data)
  129.       return null;
  130.  
  131.     if (_previewTimer) {
  132.       _previewTimer.cancel();
  133.       _previewTimer = null;
  134.     }
  135.  
  136.     _prefs.setBoolPref("isThemeSelected", aData != null);
  137.     _notifyWindows(aData);
  138.     _observerService.notifyObservers(null, "lightweight-theme-changed", null);
  139.  
  140.     if (PERSIST_ENABLED && aData)
  141.       _persistImages(aData);
  142.  
  143.     return aData;
  144.   },
  145.  
  146.   getUsedTheme: function (aId) {
  147.     var usedThemes = this.usedThemes;
  148.     for (let i = 0; i < usedThemes.length; i++) {
  149.       if (usedThemes[i].id == aId)
  150.         return usedThemes[i];
  151.     }
  152.     return null;
  153.   },
  154.  
  155.   forgetUsedTheme: function (aId) {
  156.     var currentTheme = this.currentTheme;
  157.     if (currentTheme && currentTheme.id == aId)
  158.       this.currentTheme = null;
  159.  
  160.     _updateUsedThemes(_usedThemesExceptId(aId));
  161.   },
  162.  
  163.   previewTheme: function (aData) {
  164.     if (!aData)
  165.       return;
  166.  
  167.     let cancel = Cc["@mozilla.org/supports-PRBool;1"].createInstance(Ci.nsISupportsPRBool);
  168.     cancel.data = false;
  169.     _observerService.notifyObservers(cancel, "lightweight-theme-preview-requested",
  170.                                      JSON.stringify(aData));
  171.     if (cancel.data)
  172.       return;
  173.  
  174.     if (_previewTimer)
  175.       _previewTimer.cancel();
  176.     else
  177.       _previewTimer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
  178.     _previewTimer.initWithCallback(_previewTimerCallback,
  179.                                    MAX_PREVIEW_SECONDS * 1000,
  180.                                    _previewTimer.TYPE_ONE_SHOT);
  181.  
  182.     _notifyWindows(aData);
  183.   },
  184.  
  185.   resetPreview: function () {
  186.     if (_previewTimer) {
  187.       _previewTimer.cancel();
  188.       _previewTimer = null;
  189.       _notifyWindows(this.currentThemeForDisplay);
  190.     }
  191.   },
  192.  
  193.   parseTheme: function (aString, aBaseURI) {
  194.     try {
  195.       return _sanitizeTheme(JSON.parse(aString), aBaseURI);
  196.     } catch (e) {
  197.       return null;
  198.     }
  199.   },
  200.  
  201.   updateCurrentTheme: function () {
  202.     try {
  203.       if (!_prefs.getBoolPref("update.enabled"))
  204.         return;
  205.     } catch (e) {
  206.       return;
  207.     }
  208.  
  209.     var theme = this.currentTheme;
  210.     if (!theme || !theme.updateURL)
  211.       return;
  212.  
  213.     var req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
  214.                 .createInstance(Ci.nsIXMLHttpRequest);
  215.  
  216.     req.mozBackgroundRequest = true;
  217.     req.overrideMimeType("text/plain");
  218.     req.open("GET", theme.updateURL, true);
  219.  
  220.     var self = this;
  221.     req.onload = function () {
  222.       if (req.status != 200)
  223.         return;
  224.  
  225.       let newData = self.parseTheme(req.responseText, theme.updateURL);
  226.       if (!newData ||
  227.           newData.id != theme.id ||
  228.           _version(newData) == _version(theme))
  229.         return;
  230.  
  231.       var currentTheme = self.currentTheme;
  232.       if (currentTheme && currentTheme.id == theme.id)
  233.         self.currentTheme = newData;
  234.     };
  235.  
  236.     req.send(null);
  237.   }
  238. };
  239.  
  240. function _sanitizeTheme(aData, aBaseURI) {
  241.   if (!aData || typeof aData != "object")
  242.     return null;
  243.  
  244.   function sanitizeProperty(prop) {
  245.     if (!(prop in aData))
  246.       return null;
  247.     if (typeof aData[prop] != "string")
  248.       return null;
  249.     let val = aData[prop].trim();
  250.     if (!val)
  251.       return null;
  252.  
  253.     if (!/URL$/.test(prop))
  254.       return val;
  255.  
  256.     try {
  257.       val = _makeURI(val, aBaseURI ? _makeURI(aBaseURI) : null).spec;
  258.       if (/^https:/.test(val))
  259.         return val;
  260.       if (prop != "updateURL" && /^http:/.test(val))
  261.         return val;
  262.       return null;
  263.     }
  264.     catch (e) {
  265.       return null;
  266.     }
  267.   }
  268.  
  269.   let result = {};
  270.   for (let i = 0; i < MANDATORY.length; i++) {
  271.     let val = sanitizeProperty(MANDATORY[i]);
  272.     if (!val)
  273.       throw Components.results.NS_ERROR_INVALID_ARG;
  274.     result[MANDATORY[i]] = val;
  275.   }
  276.  
  277.   for (let i = 0; i < OPTIONAL.length; i++) {
  278.     let val = sanitizeProperty(OPTIONAL[i]);
  279.     if (!val)
  280.       continue;
  281.     result[OPTIONAL[i]] = val;
  282.   }
  283.  
  284.   return result;
  285. }
  286.  
  287. function _usedThemesExceptId(aId)
  288.   LightweightThemeManager.usedThemes.filter(function (t) "id" in t && t.id != aId);
  289.  
  290. function _version(aThemeData)
  291.   aThemeData.version || "";
  292.  
  293. function _makeURI(aURL, aBaseURI)
  294.   _ioService.newURI(aURL, null, aBaseURI);
  295.  
  296. function _updateUsedThemes(aList) {
  297.   if (aList.length > MAX_USED_THEMES_COUNT)
  298.     aList.length = MAX_USED_THEMES_COUNT;
  299.  
  300.   _prefs.setCharPref("usedThemes", JSON.stringify(aList));
  301.  
  302.   _observerService.notifyObservers(null, "lightweight-theme-list-changed", null);
  303. }
  304.  
  305. function _notifyWindows(aThemeData) {
  306.   _observerService.notifyObservers(null, "lightweight-theme-styling-update",
  307.                                    JSON.stringify(aThemeData));
  308. }
  309.  
  310. var _previewTimer;
  311. var _previewTimerCallback = {
  312.   notify: function () {
  313.     LightweightThemeManager.resetPreview();
  314.   }
  315. };
  316.  
  317. function _persistImages(aData) {
  318.   function onSuccess(key) function () {
  319.     let current = LightweightThemeManager.currentTheme;
  320.     if (current && current.id == aData.id)
  321.       _prefs.setBoolPref("persisted." + key, true);
  322.   };
  323.  
  324.   for (let key in PERSIST_FILES) {
  325.     _prefs.setBoolPref("persisted." + key, false);
  326.     if (aData[key])
  327.       _persistImage(aData[key], PERSIST_FILES[key], onSuccess(key));
  328.   }
  329. }
  330.  
  331. function _getLocalImageURI(localFileName) {
  332.   var localFile = Cc["@mozilla.org/file/directory_service;1"]
  333.                      .getService(Ci.nsIProperties)
  334.                      .get("ProfD", Ci.nsILocalFile);
  335.   localFile.append(localFileName);
  336.   return _ioService.newFileURI(localFile);
  337. }
  338.  
  339. function _persistImage(sourceURL, localFileName, callback) {
  340.   var targetURI = _getLocalImageURI(localFileName);
  341.   var sourceURI = _makeURI(sourceURL);
  342.  
  343.   var persist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"]
  344.                   .createInstance(Ci.nsIWebBrowserPersist);
  345.  
  346.   persist.persistFlags =
  347.     Ci.nsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES |
  348.     Ci.nsIWebBrowserPersist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION |
  349.     (PERSIST_BYPASS_CACHE ?
  350.        Ci.nsIWebBrowserPersist.PERSIST_FLAGS_BYPASS_CACHE :
  351.        Ci.nsIWebBrowserPersist.PERSIST_FLAGS_FROM_CACHE);
  352.  
  353.   persist.progressListener = new _persistProgressListener(callback);
  354.  
  355.   persist.saveURI(sourceURI, null, null, null, null, targetURI);
  356. }
  357.  
  358. function _persistProgressListener(callback) {
  359.   this.onLocationChange = function () {};
  360.   this.onProgressChange = function () {};
  361.   this.onStatusChange   = function () {};
  362.   this.onSecurityChange = function () {};
  363.   this.onStateChange    = function (aWebProgress, aRequest, aStateFlags, aStatus) {
  364.     if (aRequest &&
  365.         aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK &&
  366.         aStateFlags & Ci.nsIWebProgressListener.STATE_STOP) {
  367.       try {
  368.         if (aRequest.QueryInterface(Ci.nsIHttpChannel).requestSucceeded) {
  369.           // success
  370.           callback();
  371.           return;
  372.         }
  373.       } catch (e) { }
  374.       // failure
  375.     }
  376.   };
  377. }
  378.