home *** CD-ROM | disk | FTP | other *** search
/ Freelog 100 / FreelogNo100-NovembreDecembre2010.iso / Multimedia / pspvideo9 / pspvideo9-600-setup.exe / components / nsPlacesDBFlush.js < prev    next >
Text File  |  2009-09-21  |  13KB  |  361 lines

  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  * vim: sw=2 ts=2 sts=2 expandtab
  3.  * ***** BEGIN LICENSE BLOCK *****
  4.  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  5.  *
  6.  * The contents of this file are subject to the Mozilla Public License Version
  7.  * 1.1 (the "License"); you may not use this file except in compliance with
  8.  * the License. You may obtain a copy of the License at
  9.  * http://www.mozilla.org/MPL/
  10.  *
  11.  * Software distributed under the License is distributed on an "AS IS" basis,
  12.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  13.  * for the specific language governing rights and limitations under the
  14.  * License.
  15.  *
  16.  * The Original Code is mozilla.org code.
  17.  *
  18.  * The Initial Developer of the Original Code is
  19.  * Mozilla Corporation.
  20.  * Portions created by the Initial Developer are Copyright (C) 2008
  21.  * the Initial Developer. All Rights Reserved.
  22.  *
  23.  * Contributor(s):
  24.  *   Shawn Wilsher <me@shawnwilsher.com> (Original Author)
  25.  *   Marco Bonardo <mak77@bonardo.net>
  26.  *
  27.  * Alternatively, the contents of this file may be used under the terms of
  28.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  29.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  30.  * in which case the provisions of the GPL or the LGPL are applicable instead
  31.  * of those above. If you wish to allow use of your version of this file only
  32.  * under the terms of either the GPL or the LGPL, and not to allow others to
  33.  * use your version of this file under the terms of the MPL, indicate your
  34.  * decision by deleting the provisions above and replace them with the notice
  35.  * and other provisions required by the GPL or the LGPL. If you do not delete
  36.  * the provisions above, a recipient may use your version of this file under
  37.  * the terms of any one of the MPL, the GPL or the LGPL.
  38.  *
  39.  * ***** END LICENSE BLOCK ***** */
  40.  
  41. Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
  42.  
  43. ////////////////////////////////////////////////////////////////////////////////
  44. //// Constants
  45.  
  46. const Cc = Components.classes;
  47. const Ci = Components.interfaces;
  48. const Cr = Components.results;
  49. const Cu = Components.utils;
  50.  
  51. const kQuitApplication = "quit-application";
  52. const kSyncFinished = "places-sync-finished";
  53.  
  54. const kSyncPrefName = "syncDBTableIntervalInSecs";
  55. const kDefaultSyncInterval = 120;
  56.  
  57. ////////////////////////////////////////////////////////////////////////////////
  58. //// nsPlacesDBFlush class
  59.  
  60. function nsPlacesDBFlush()
  61. {
  62.   this._prefs = Cc["@mozilla.org/preferences-service;1"].
  63.                 getService(Ci.nsIPrefService).
  64.                 getBranch("places.");
  65.  
  66.   // Get our sync interval
  67.   try {
  68.     // We want to silently fail if the preference does not exist, and use a
  69.     // default to fallback to.
  70.     this._syncInterval = this._prefs.getIntPref(kSyncPrefName);
  71.     if (this._syncInterval <= 0)
  72.       this._syncInterval = kDefaultSyncInterval;
  73.   }
  74.   catch (e) {
  75.     // The preference did not exist, so default to two minutes.
  76.     this._syncInterval = kDefaultSyncInterval;
  77.   }
  78.  
  79.   // Register observers
  80.   this._os = Cc["@mozilla.org/observer-service;1"].
  81.              getService(Ci.nsIObserverService);
  82.   this._os.addObserver(this, kQuitApplication, false);
  83.  
  84.   this._prefs.QueryInterface(Ci.nsIPrefBranch2)
  85.              .addObserver("", this, false);
  86.  
  87.   // Create our timer to update everything
  88.   this._timer = this._newTimer();
  89.  
  90.   //////////////////////////////////////////////////////////////////////////////
  91.   //// Smart Getters
  92.  
  93.   this.__defineGetter__("_db", function() {
  94.     delete this._db;
  95.     return this._db = Cc["@mozilla.org/browser/nav-history-service;1"].
  96.                       getService(Ci.nsPIPlacesDatabase).
  97.                       DBConnection;
  98.   });
  99.  
  100.   this.__defineGetter__("_bs", function() {
  101.     delete this._bs;
  102.     return this._bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
  103.                       getService(Ci.nsINavBookmarksService);
  104.   });
  105. }
  106.  
  107. nsPlacesDBFlush.prototype = {
  108.   //////////////////////////////////////////////////////////////////////////////
  109.   //// nsIObserver
  110.  
  111.   observe: function DBFlush_observe(aSubject, aTopic, aData)
  112.   {
  113.     if (aTopic == kQuitApplication) {
  114.       this._os.removeObserver(this, kQuitApplication);
  115.       this._prefs.QueryInterface(Ci.nsIPrefBranch2).removeObserver("", this);
  116.       this._timer.cancel();
  117.       this._timer = null;
  118.       // Other components could still make changes to history at this point,
  119.       // for example to clear private data on shutdown, so here we dispatch
  120.       // an event to the main thread so that we will sync after
  121.       // quit-application ensuring all data have been saved.
  122.       let tm = Cc["@mozilla.org/thread-manager;1"].
  123.           getService(Ci.nsIThreadManager);
  124.       tm.mainThread.dispatch({
  125.         _self: this,
  126.         run: function() {
  127.           let pip = Cc["@mozilla.org/browser/nav-history-service;1"].
  128.                     getService(Ci.nsPIPlacesDatabase);
  129.           pip.commitPendingChanges();
  130.           this._self._syncTables(["places", "historyvisits"]);
  131.           // Close the database connection, this was the last sync and we can't
  132.           // ensure database coherence from now on.
  133.           pip.finalizeInternalStatements();
  134.           this._self._finalizeInternalStatements();
  135.           this._self._db.close();
  136.         }
  137.       }, Ci.nsIThread.DISPATCH_NORMAL);
  138.  
  139.     }
  140.     else if (aTopic == "nsPref:changed" && aData == kSyncPrefName) {
  141.       // Get the new pref value, and then update our timer
  142.       this._syncInterval = aSubject.getIntPref(kSyncPrefName);
  143.       if (this._syncInterval <= 0)
  144.         this._syncInterval = kDefaultSyncInterval;
  145.  
  146.       // We may have canceled the timer already for batch updates, so we want to
  147.       // exit early.
  148.       if (!this._timer)
  149.         return;
  150.  
  151.       this._timer.cancel();
  152.       this._timer = this._newTimer();
  153.     }
  154.   },
  155.  
  156.   //////////////////////////////////////////////////////////////////////////////
  157.   //// nsINavBookmarkObserver
  158.  
  159.   onBeginUpdateBatch: function DBFlush_onBeginUpdateBatch()
  160.   {
  161.     this._inBatchMode = true;
  162.  
  163.     // We do not want to sync while we are doing batch work.
  164.     this._timer.cancel();
  165.     this._timer = null;
  166.   },
  167.  
  168.   onEndUpdateBatch: function DBFlush_onEndUpdateBatch()
  169.   {
  170.     this._inBatchMode = false;
  171.  
  172.     // Restore our timer
  173.     this._timer = this._newTimer();
  174.  
  175.     // We need to sync now
  176.     this._syncTables(["places", "historyvisits"]);
  177.   },
  178.  
  179.   onItemAdded: function(aItemId, aParentId, aIndex)
  180.   {
  181.     // Sync only if we added a TYPE_BOOKMARK item
  182.     if (!this._inBatchMode &&
  183.         this._bs.getItemType(aItemId) == this._bs.TYPE_BOOKMARK)
  184.       this._syncTables(["places"]);
  185.   },
  186.  
  187.   onItemChanged: function DBFlush_onItemChanged(aItemId, aProperty,
  188.                                                          aIsAnnotationProperty,
  189.                                                          aValue)
  190.   {
  191.     if (!this._inBatchMode && aProperty == "uri")
  192.       this._syncTables(["places"]);
  193.   },
  194.  
  195.   onItemRemoved: function() { },
  196.   onItemVisited: function() { },
  197.   onItemMoved: function() { },
  198.  
  199.   //////////////////////////////////////////////////////////////////////////////
  200.   //// nsINavHistoryObserver
  201.  
  202.   // We currently only use the history observer to know when the history service
  203.   // is activated.  At that point, we actually get initialized, and our timer
  204.   // to sync history is added.
  205.  
  206.   // These methods share the name of the ones on nsINavBookmarkObserver, so
  207.   // the implementations can be found above.
  208.   //onBeginUpdateBatch: function() { },
  209.   //onEndUpdateBatch: function() { },
  210.   onVisit: function(aURI, aVisitID, aTime, aSessionID, aReferringID, aTransitionType) { },
  211.   onTitleChanged: function(aURI, aPageTitle) { },
  212.   onDeleteURI: function(aURI) { },
  213.   onClearHistory: function() { },
  214.   onPageChanged: function(aURI, aWhat, aValue) { },
  215.   onPageExpired: function(aURI, aVisitTime, aWholeEntry) { },
  216.  
  217.   //////////////////////////////////////////////////////////////////////////////
  218.   //// nsITimerCallback
  219.  
  220.   notify: function() this._syncTables(["places", "historyvisits"]),
  221.  
  222.   //////////////////////////////////////////////////////////////////////////////
  223.   //// mozIStorageStatementCallback
  224.  
  225.   handleError: function DBFlush_handleError(aError)
  226.   {
  227.     Cu.reportError("Async statement execution returned with '" +
  228.                    aError.result + "', '" + aError.message + "'");
  229.   },
  230.  
  231.   handleCompletion: function DBFlush_handleCompletion(aReason)
  232.   {
  233.     if (aReason == Ci.mozIStorageStatementCallback.REASON_FINISHED) {
  234.       // Dispatch a notification that sync has finished.
  235.       this._os.notifyObservers(null, kSyncFinished, null);
  236.     }
  237.   },
  238.  
  239.   //////////////////////////////////////////////////////////////////////////////
  240.   //// nsPlacesDBFlush
  241.   _syncInterval: kDefaultSyncInterval,
  242.  
  243.   /**
  244.    * Execute async statements to sync temporary places table.
  245.    * @param aTableNames
  246.    *        array of table names that should be synced, as moz_{TableName}_temp.
  247.    */
  248.   _syncTables: function DBFlush_syncTables(aTableNames)
  249.   {
  250.     // No need to do extra work if we are in batch mode
  251.     if (this._inBatchMode)
  252.       return;
  253.  
  254.     let statements = [];
  255.     for (let i = 0; i < aTableNames.length; i++)
  256.       statements.push(this._getSyncTableStatement(aTableNames[i]));
  257.  
  258.     // Execute sync statements async in a transaction
  259.     this._db.executeAsync(statements, statements.length, this);
  260.   },
  261.  
  262.   /**
  263.    * Finalizes all of our mozIStorageStatements so we can properly close the
  264.    * database.
  265.    */
  266.   _finalizeInternalStatements: function DBFlush_finalizeInternalStatements()
  267.   {
  268.     for each (let stmt in this._cachedStatements)
  269.       if (stmt instanceof Ci.mozIStorageStatement)
  270.         stmt.finalize();
  271.   },
  272.  
  273.   /**
  274.    * Generate the statement to synchronizes the moz_{aTableName} and
  275.    * moz_{aTableName}_temp by copying all the data from the temporary table
  276.    * into the permanent one.
  277.    * Most of the work is done through triggers defined in nsPlacesTriggers.h,
  278.    * they sync back to disk, then delete the data in the temporary table.
  279.    * @param aTableName
  280.    *        name of the table to build statement for, as moz_{TableName}_temp.
  281.    */
  282.   _cachedStatements: {},
  283.   _getSyncTableStatement: function DBFlush_getSyncTableStatement(aTableName)
  284.   {
  285.     // Statement creating can be expensive, so always cache if we can.
  286.     if (aTableName in this._cachedStatements)
  287.       return this._cachedStatements[aTableName];
  288.  
  289.     // Delete all the data in the temp table.
  290.     // We have triggers setup that ensure that the data is transferred over
  291.     // upon deletion.
  292.     let condition = "";
  293.     switch(aTableName) {
  294.       case "historyvisits":
  295.         // For history table we want to leave embed visits in memory, since
  296.         // those are expired with current session, so we are filtering them out.
  297.         condition = "WHERE visit_type <> " + Ci.nsINavHistoryService.TRANSITION_EMBED;
  298.         break;
  299.       case "places":
  300.         // For places table we want to leave places associated with embed visits
  301.         // in memory, they usually have hidden = 1 and at least an embed visit
  302.         // in historyvisits_temp table.
  303.         condition = "WHERE id IN (SELECT id FROM moz_places_temp h " +
  304.                                   "WHERE h.hidden <> 1 OR NOT EXISTS ( " +
  305.                                     "SELECT id FROM moz_historyvisits_temp " +
  306.                                     "WHERE place_id = h.id AND visit_type = " +
  307.                                     Ci.nsINavHistoryService.TRANSITION_EMBED +
  308.                                     " LIMIT 1) " +
  309.                                   ")";
  310.         break;
  311.     }
  312.  
  313.     let sql = "DELETE FROM moz_" + aTableName + "_temp " + condition;
  314.     return this._cachedStatements[aTableName] = this._db.createStatement(sql);
  315.   },
  316.  
  317.   /**
  318.    * Creates a new timer based on this._syncInterval.
  319.    *
  320.    * @returns a REPEATING_SLACK nsITimer that runs every this._syncInterval.
  321.    */
  322.   _newTimer: function DBFlush_newTimer()
  323.   {
  324.     let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
  325.     timer.initWithCallback(this, this._syncInterval * 1000,
  326.                            Ci.nsITimer.TYPE_REPEATING_SLACK);
  327.     return timer;
  328.   },
  329.  
  330.   //////////////////////////////////////////////////////////////////////////////
  331.   //// nsISupports
  332.  
  333.   classDescription: "Used to synchronize the temporary and permanent tables of Places",
  334.   classID: Components.ID("c1751cfc-e8f1-4ade-b0bb-f74edfb8ef6a"),
  335.   contractID: "@mozilla.org/places/sync;1",
  336.  
  337.   // Registering in these categories makes us get initialized when either of
  338.   // those listeners would be notified.
  339.   _xpcom_categories: [
  340.     { category: "bookmark-observers" },
  341.     { category: "history-observers" },
  342.   ],
  343.  
  344.   QueryInterface: XPCOMUtils.generateQI([
  345.     Ci.nsIObserver,
  346.     Ci.nsINavBookmarkObserver,
  347.     Ci.nsINavHistoryObserver,
  348.     Ci.nsITimerCallback,
  349.     Ci.mozIStorageStatementCallback,
  350.   ])
  351. };
  352.  
  353. ////////////////////////////////////////////////////////////////////////////////
  354. //// Module Registration
  355.  
  356. let components = [nsPlacesDBFlush];
  357. function NSGetModule(compMgr, fileSpec)
  358. {
  359.   return XPCOMUtils.generateModule(components);
  360. }
  361.