Moved all of the bookmark-share stuff out of the bookmarkEngine class into a new BookmarksSharingManager class.
authorjonathandicarlo@jonathan-dicarlos-macbook-pro.local
Tue, 24 Jun 2008 21:15:14 -0700
changeset 44731 d3c67942281be44c167c751b9b1084331b8283a2
parent 44730 1ed6147ab1f9a7eccdcccc931e43c980ad32be0f
child 44732 adaaa4e6751c71279186b704220481a36befdfc6
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
Moved all of the bookmark-share stuff out of the bookmarkEngine class into a new BookmarksSharingManager class.
services/sync/modules/engines/bookmarks.js
--- a/services/sync/modules/engines/bookmarks.js
+++ b/services/sync/modules/engines/bookmarks.js
@@ -47,82 +47,52 @@ const OUTGOING_SHARED_ANNO = "weave/shar
 const SERVER_PATH_ANNO = "weave/shared-server-path";
 // Standard names for shared files on the server
 const KEYRING_FILE_NAME = "keyring";
 const SHARED_BOOKMARK_FILE_NAME = "shared_bookmarks";
 // Information for the folder that contains all incoming shares
 const INCOMING_SHARE_ROOT_ANNO = "weave/mounted-shares-folder";
 const INCOMING_SHARE_ROOT_NAME = "Shared Folders";
 
-
 Cu.import("resource://weave/log4moz.js");
 Cu.import("resource://weave/dav.js");
 Cu.import("resource://weave/util.js");
 Cu.import("resource://weave/crypto.js");
 Cu.import("resource://weave/async.js");
 Cu.import("resource://weave/engines.js");
 Cu.import("resource://weave/syncCores.js");
 Cu.import("resource://weave/stores.js");
 Cu.import("resource://weave/trackers.js");
 Cu.import("resource://weave/identity.js");
-
-/* LONGTERM TODO: when we start working on the ability to share other types
-of data besides bookmarks, the xmppClient instance should be moved to hang
-off of Weave.Service instead of hanging off the BookmarksEngine.  But for
-now this is the easiest place to deal with it. */
 Cu.import("resource://weave/xmpp/xmppClient.js");
 
 Function.prototype.async = Async.sugar;
 
-function BookmarksEngine(pbeId) {
-  this._init(pbeId);
+function BookmarksSharingManager(engine) {
+  this._init(engine);
 }
-BookmarksEngine.prototype = {
-  get name() { return "bookmarks"; },
-  get logName() { return "BmkEngine"; },
-  get serverPrefix() { return "user-data/bookmarks/"; },
-
-  __core: null,
-  get _core() {
-    if (!this.__core)
-      this.__core = new BookmarksSyncCore();
-    return this.__core;
-  },
-
-  __store: null,
-  get _store() {
-    if (!this.__store)
-      this.__store = new BookmarksStore();
-    return this.__store;
-  },
-
-  __tracker: null,
-  get _tracker() {
-    if (!this.__tracker)
-      this.__tracker = new BookmarksTracker();
-    return this.__tracker;
-  },
-
+BookmarksSharingManager.prototype = {
   __annoSvc: null,
   get _annoSvc() {
     if (!this.__anoSvc)
       this.__annoSvc = Cc["@mozilla.org/browser/annotation-service;1"].
         getService(Ci.nsIAnnotationService);
     return this.__annoSvc;
   },
 
-  _init: function BmkEngine__init( pbeId ) {
-    this.__proto__.__proto__._init.call( this, pbeId );
+  _init: function SharingManager__init(engine) {
+    this._engine = engine;
+    this._log = Log4Moz.Service.getLogger("Bookmark Share");
     if ( Utils.prefs.getBoolPref( "xmpp.enabled" ) ) {
       dump( "Starting XMPP client for bookmark engine..." );
       this._startXmppClient.async(this);
     }
   },
 
-  _startXmppClient: function BmkEngine__startXmppClient() {
+  _startXmppClient: function BmkSharing__startXmppClient() {
     // To be called asynchronously.
     let self = yield;
 
     // Get serverUrl and realm of the jabber server from preferences:
     let serverUrl = Utils.prefs.getCharPref( "xmpp.server.url" );
     let realm = Utils.prefs.getCharPref( "xmpp.server.realm" );
 
     /* Username/password for XMPP are the same; as the ones for Weave,
@@ -135,17 +105,17 @@ BookmarksEngine.prototype = {
     /* LONGTERM TODO would prefer to use MD5Authenticator instead,
         once we get it working, but since we are connecting over SSL, the
         Plain auth is probably fine for now. */
     this._xmppClient = new XmppClient( clientName,
                                        realm,
                                        clientPassword,
 				       transport,
                                        auth );
-    let bmkEngine = this;
+    let bmkSharing = this;
     let messageHandler = {
       handle: function ( messageText, from ) {
         /* The callback function for incoming xmpp messages.
            We expect message text to be in the form of:
 	   <command> <serverpath> <foldername>.
 	   Where command is one of:
            "share" (sender offers to share directory dir with us)
            or "stop" (sender has stopped sharing directory dir with us.)
@@ -157,77 +127,63 @@ BookmarksEngine.prototype = {
 	   on the server to the directory where the data is stored:
 	   only the machine seese this, and it can't have spaces.
         */
  	let words = messageText.split(" ");
 	let commandWord = words[0];
 	let serverPath = words[1];
 	let directoryName = words.slice(2).join(" ");
         if ( commandWord == "share" ) {
-	  bmkEngine._incomingShareOffer(from, serverPath, folderName);
+	  bmkSharing._incomingShareOffer(from, serverPath, folderName);
 	} else if ( commandWord == "stop" ) {
-	  bmkEngine._incomingShareWithdrawn(from, serverPath, folderName);
+	  bmkSharing._incomingShareWithdrawn(from, serverPath, folderName);
 	}
       }
     };
 
     this._xmppClient.registerMessageHandler( messageHandler );
     this._xmppClient.connect( realm, self.cb );
     yield;
     if ( this._xmppClient._connectionStatus == this._xmppClient.FAILED ) {
       this._log.warn( "Weave can't log in to xmpp server: xmpp disabled." );
     } else if ( this._xmppClient._connectionStatus == this._xmppClient.CONNECTED ) {
       this._log.info( "Weave logged into xmpp OK." );
     }
     self.done();
   },
 
-  _incomingShareOffer: function BmkEngine__incomingShareOffer(user,
+  _incomingShareOffer: function BmkSharing__incomingShareOffer(user,
                                                               serverPath,
                                                               folderName) {
     /* Called when we receive an offer from another user to share a
        folder.
 
        TODO what should happen is that we add a notification to the queue
        telling that the incoming share has been offered; when the offer
        is accepted we will call createIncomingShare and then
        updateIncomingShare.
 
        But since we don't have notification in place yet, I'm going to skip
        right ahead to creating the incoming share.
     */
     this._log.info("User " + user + " offered to share folder " + folderName);
-    this._createIncomingShare( user, serverPath, folderName );
+    this._createIncomingShare(user, serverPath, folderName);
   },
 
-  _incomingShareWithdrawn: function BmkEngine__incomingShareStop(user,
+  _incomingShareWithdrawn: function BmkSharing__incomingShareStop(user,
                                                                  serverPath,
                                                                  folderName) {
     /* Called when we receive a message telling us that a user who has
        already shared a directory with us has chosen to stop sharing
        the directory.
     */
     this._log.info("User " + user + " stopped sharing folder " + folderName);
     this._stopIncomingShare(user, serverPath, folderName);
   },
-
-  _sync: function BmkEngine__sync() {
-    /* After syncing, also call syncMounts to get the
-       incoming shared bookmark folder contents. */
-    let self = yield;
-    this.__proto__.__proto__._sync.async(this, self.cb );
-    yield;
-    this.updateAllOutgoingShares(self.cb);
-    yield;
-    this.updateAllIncomingShares(self.cb);
-    yield;
-    self.done();
-  },
-
-  _share: function BmkEngine__share( selectedFolder, username ) {
+  _share: function BmkSharing__share( selectedFolder, username ) {
     // Return true if success, false if failure.
     let ret = false;
     let self = yield;
 
     /* TODO What should the behavior be if i'm already sharing it with user
        A and I ask to share it with user B?  (This should be prevented by
        the UI. */
 
@@ -261,17 +217,17 @@ BookmarksEngine.prototype = {
        with many people, the value of the annotation can be a whole list
        of usernames instead of just one. */
 
     this._log.info("Shared " + folderName +" with " + username);
     ret = true;
     self.done( ret );
   },
 
-  _stopSharing: function BmkEngine__stopSharing( selectedFolder, username ) {
+  _stopSharing: function BmkSharing__stopSharing( selectedFolder, username ) {
     let self = yield;
     let folderName = selectedFolder.getAttribute( "label" );
     let serverPath = this._annoSvc.getItemAnnotation(folderNode,
                                                      SERVER_PATH_ANNO);
 
     /* LONGTERM TODO: when we move to being able to share one folder with
      * multiple people, this needs to be modified so we can stop sharing with
      * one person but keep sharing with others.
@@ -291,59 +247,59 @@ BookmarksEngine.prototype = {
 	this._log.warn( "No XMPP connection for share notification." );
       }
     }
 
     this._log.info("Stopped sharing " + folderName + "with " + username);
     self.done( true );
   },
 
-  updateAllIncomingShares: function BmkEngine_updateAllIncoming(onComplete) {
+  updateAllIncomingShares: function BmkSharing_updateAllIncoming(onComplete) {
     this._updateAllIncomingShares.async(this, onComplete);
   },
-  _updateAllIncomingShares: function BmkEngine__updateAllIncoming() {
+  _updateAllIncomingShares: function BmkSharing__updateAllIncoming() {
     /* For every bookmark folder in my tree that has the annotation
        marking it as an incoming shared folder, pull down its latest
        contents from its owner's account on the server.  (This is
        a one-way data transfer because I can't modify bookmarks that
        are owned by someone else but shared to me; any changes I make
        to the folder contents are simply wiped out by the latest
        server contents.) */
     let self = yield;
-    let mounts = this._store.findIncomingShares();
+    let mounts = this._engine._store.findIncomingShares();
 
     for (let i = 0; i < mounts.length; i++) {
       try {
         this._updateIncomingShare.async(this, self.cb, mounts[i]);
         yield;
       } catch (e) {
         this._log.warn("Could not sync shared folder from " + mounts[i].userid);
         this._log.trace(Utils.stackTrace(e));
       }
     }
   },
 
-  updateAllOutgoingShares: function BmkEngine_updateAllOutgoing(onComplete) {
+  updateAllOutgoingShares: function BmkSharing_updateAllOutgoing(onComplete) {
     this._updateAllOutgoingShares.async(this, onComplete);
   },
-  _updateAllOutgoingShares: function BmkEngine__updateAllOutgoing() {
+  _updateAllOutgoingShares: function BmkSharing__updateAllOutgoing() {
     let self = yield;
     let shares = this._annoSvc.getItemsWithAnnotation(OUTGOING_SHARED_ANNO,
                                                       {});
     for ( let i=0; i < shares.length; i++ ) {
       /* TODO only update the shares that have changed.  Perhaps we can
       do this by checking whether there's a corresponding entry in the
       diff produced by the latest sync. */
       this._updateOutgoingShare.async(this, self.cb, shares[i]);
       yield;
     }
     self.done();
   },
 
-  _createOutgoingShare: function BmkEngine__createOutgoing(folder, username) {
+  _createOutgoingShare: function BmkSharing__createOutgoing(folder, username) {
     /* To be called asynchronously.  Folder is a node indicating the bookmark
        folder that is being shared; username is a string indicating the user
        that it is to be shared with.  This function creates the directory and
        keyring on the server in which the shared data will be put, but it
        doesn't actually put the bookmark data there (that's done in
        _updateOutgoingShare().) */
     let self = yield;
     let myUserName = ID.get('WeaveID').username;
@@ -401,29 +357,30 @@ BookmarksEngine.prototype = {
     let keys = {
                  ring   : { },
                  bulkIV : bulkIV
                };
     keys.ring[myUserName] = encryptedForMe;
     keys.ring[username]   = encryptedForYou;
 
     let keyringFile = new Resource( serverPath + "/" + KEYRING_FILE_NAME );
-    keyringFile.put( self.cb, this._json.encode( keys ) );
+    let jsonService = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON);
+    keyringFile.put( self.cb, jsonService.encode( keys ) );
     yield;
 
     // Call Atul's js api for setting htaccess:
     let sharingApi = new Sharing.Api( DAV );
     sharingApi.shareWithUsers( serverPath, [username], self.cb );
     let result = yield;
 
     // return the server path:
     self.done( serverPath );
   },
 
-  _updateOutgoingShare: function BmkEngine__updateOutgoing(folderNode) {
+  _updateOutgoingShare: function BmkSharing__updateOutgoing(folderNode) {
     /* Puts all the bookmark data from the specified bookmark folder,
        encrypted, onto the shared directory on the server (replacing
        anything that was already there).
        To be called asynchronously.
        TODO: error handling*/
     let self = yield;
     let myUserName = ID.get('WeaveID').username;
     // The folder has an annotation specifying the server path to the
@@ -440,17 +397,17 @@ BookmarksEngine.prototype = {
 
     // Unwrap (decrypt) the key with the user's private key.
     let idRSA = ID.get('WeaveCryptoID');
     let bulkKey = yield Crypto.unwrapKey.async(Crypto, self.cb,
                            keys.ring[myUserName], idRSA);
     let bulkIV = keys.bulkIV;
 
     // Get the json-wrapped contents of everything in the folder:
-    let json = this._store._wrapMount( folderNode, myUserName );
+    let json = this._engine._store._wrapMount( folderNode, myUserName );
     /* TODO what does wrapMount do with this username?  Should I be passing
        in my own or that of the person I share with? */
 
     // Encrypt it with the symkey and put it into the shared-bookmark file.
     let bmkFile = new Resource(serverPath + "/" + SHARED_BOOKMARK_FILE_NAME);
     let tmpIdentity = {
                         realm   : "temp ID",
                         bulkKey : bulkKey,
@@ -458,21 +415,20 @@ BookmarksEngine.prototype = {
                       };
     Crypto.encryptData.async( Crypto, self.cb, json, tmpIdentity );
     let cyphertext = yield;
     bmkFile.put( self.cb, cyphertext );
     yield;
     self.done();
   },
 
-  _stopOutgoingShare: function BmkEngine__stopOutgoingShare(folderNode) {
+  _stopOutgoingShare: function BmkSharing__stopOutgoingShare(folderNode) {
     /* Stops sharing the specified folder.  Deletes its data from the
        server, deletes the annotations that mark it as shared, and sends
        a message to the shar-ee to let them know it's been withdrawn. */
-    // TODO: currently not called from anywhere.
     let self = yield;
     let serverPath = this._annoSvc.getItemAnnotation( folderNode,
                                                       SERVER_PATH_ANNO );
     let username = this._annoSvc.getItemAnnotation( folderNode,
                                                     OUTGOING_SHARED_ANNO );
 
     // Delete the share from the server:
     let keyringFile = new Resource(serverPath + "/" + KEYRING_FILE_NAME);
@@ -557,17 +513,17 @@ BookmarksEngine.prototype = {
       this._annoSvc.setItemAnnotation(newId,
                                       SERVER_PATH_ANNO,
                                       serverPath,
                                       0,
                                       this._annoSvc.EXPIRE_NEVER);
     }
   },
 
-  _updateIncomingShare: function BmkEngine__updateIncomingShare(mountData) {
+  _updateIncomingShare: function BmkSharing__updateIncomingShare(mountData) {
     /* Pull down bookmarks from the server for a single incoming
        shared folder, obliterating whatever was in that folder before.
 
        mountData is an object that's expected to have member data:
        userid: weave id of the user sharing the folder with us,
        rootGUID: guid in our bookmark store of the share mount point,
        node: the bookmark menu node for the share mount point folder,
        snapshot: the json-wrapped current contents of the share. */
@@ -604,28 +560,28 @@ BookmarksEngine.prototype = {
         delete json[guid];
       else
         json[guid].parentGUID = mountData.rootGUID;
     }
 
     /* Create diff between the json from server and the current contents;
        then apply the diff. */
     this._log.trace("Got bookmarks from " + user + ", comparing with local copy");
-    this._core.detectUpdates(self.cb, mountData.snapshot, snap.data);
+    this._engine._core.detectUpdates(self.cb, mountData.snapshot, snap.data);
     let diff = yield;
 
     // FIXME: should make sure all GUIDs here live under the mountpoint
     this._log.trace("Applying changes to folder from " + user);
-    this._store.applyCommands.async(this._store, self.cb, diff);
+    this._engine._store.applyCommands.async(this._engine._store, self.cb, diff);
     yield;
 
     this._log.trace("Shared folder from " + user + " successfully synced!");
   },
 
-  _stopIncomingShare: function BmkEngine__stopIncomingShare(user,
+  _stopIncomingShare: function BmkSharing__stopIncomingShare(user,
                                                             serverPath,
                                                             folderName)
   {
   /* Delete the incoming share folder.  Since the update of incoming folders
    * is triggered when the engine spots a folder with a certain annotation on
    * it, just getting rid of this folder is all we need to do.
    */
     let bms = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
@@ -635,16 +591,69 @@ BookmarksEngine.prototype = {
     for (let i = 0; i < a.length; i++) {
       let creator = this._annoSvc.getItemAnnotation(a[i], OUTGOING_SHARED_ANNO);
       let path = this._annoSvc.getItemAnnotation(a[i], SERVER_PATH_ANNO);
       if ( creator == user && path == serverPath ) {
         bms.removeFolder( a[i]);
       }
     }
   }
+}
+
+
+
+function BookmarksEngine(pbeId) {
+  this._init(pbeId);
+}
+BookmarksEngine.prototype = {
+  get name() { return "bookmarks"; },
+  get logName() { return "BmkEngine"; },
+  get serverPrefix() { return "user-data/bookmarks/"; },
+
+  __core: null,
+  get _core() {
+    if (!this.__core)
+      this.__core = new BookmarksSyncCore();
+    return this.__core;
+  },
+
+  __store: null,
+  get _store() {
+    if (!this.__store)
+      this.__store = new BookmarksStore();
+    return this.__store;
+  },
+
+  __tracker: null,
+  get _tracker() {
+    if (!this.__tracker)
+      this.__tracker = new BookmarksTracker();
+    return this.__tracker;
+  },
+
+  __sharing: null,
+  get _sharing() {
+    if (!this.__sharing)
+      this.__sharing = new BookmarksSharingManager(this);
+    return this.__sharing;
+  },
+
+  _sync: function BmkEngine__sync() {
+    /* After syncing the regular bookmark folder contents,
+     * also update both the incoming and outgoing shared folders. */
+    let self = yield;
+    this.__proto__.__proto__._sync.async(this, self.cb );
+    yield;
+    this._sharing.updateAllOutgoingShares(self.cb);
+    yield;
+    this._sharing.updateAllIncomingShares(self.cb);
+    yield;
+    self.done();
+  }
+
 };
 BookmarksEngine.prototype.__proto__ = new Engine();
 
 function BookmarksSyncCore() {
   this._init();
 }
 BookmarksSyncCore.prototype = {
   _logName: "BMSync",