From af655d694e56d8c5dac3dff93597b708e33cc0bd Mon Sep 17 00:00:00 2001 From: Anant Narayanan Date: Thu, 22 May 2008 15:36:44 -0700 Subject: [PATCH 1/2] Sync form data: bug #434818, r=thunder --- services/sync/locales/en-US/preferences.dtd | 1 + services/sync/modules/engines.js | 26 +++++++- services/sync/modules/service.js | 1 + services/sync/modules/stores.js | 70 ++++++++++++++++++++- services/sync/modules/syncCores.js | 47 +++++++++++++- services/sync/services-sync.js | 1 + 6 files changed, 143 insertions(+), 3 deletions(-) diff --git a/services/sync/locales/en-US/preferences.dtd b/services/sync/locales/en-US/preferences.dtd index 041820157db..0f00ad43eb3 100644 --- a/services/sync/locales/en-US/preferences.dtd +++ b/services/sync/locales/en-US/preferences.dtd @@ -23,6 +23,7 @@ + diff --git a/services/sync/modules/engines.js b/services/sync/modules/engines.js index 45e5e75c96c..68f29334a0c 100644 --- a/services/sync/modules/engines.js +++ b/services/sync/modules/engines.js @@ -36,7 +36,7 @@ const EXPORTED_SYMBOLS = ['Engines', 'Engine', 'BookmarksEngine', 'HistoryEngine', 'CookieEngine', - 'PasswordEngine']; + 'PasswordEngine', 'FormEngine']; const Cc = Components.classes; const Ci = Components.interfaces; @@ -1042,3 +1042,27 @@ PasswordEngine.prototype = { } }; PasswordEngine.prototype.__proto__ = new Engine(); + +function FormEngine(pbeId) { + this._init(pbeId); +} +FormEngine.prototype = { + get name() { return "forms"; }, + get logName() { return "FormEngine"; }, + get serverPrefix() { return "user-data/forms/"; }, + + __core: null, + get _core() { + if (!this.__core) + this.__core = new FormSyncCore(); + return this.__core; + }, + + __store: null, + get _store() { + if (!this.__store) + this.__store = new FormStore(); + return this.__store; + } +}; +FormEngine.prototype.__proto__ = new Engine(); \ No newline at end of file diff --git a/services/sync/modules/service.js b/services/sync/modules/service.js index 23c5be3e1fa..6e41b6f0a41 100644 --- a/services/sync/modules/service.js +++ b/services/sync/modules/service.js @@ -91,6 +91,7 @@ function WeaveSvc() { Engines.register(new HistoryEngine()); Engines.register(new CookieEngine()); Engines.register(new PasswordEngine()); + Engines.register(new FormEngine()); // Other misc startup Utils.prefs.addObserver("", this, false); diff --git a/services/sync/modules/stores.js b/services/sync/modules/stores.js index 5d9f2913c76..cc8fd775fbe 100644 --- a/services/sync/modules/stores.js +++ b/services/sync/modules/stores.js @@ -35,7 +35,7 @@ * ***** END LICENSE BLOCK ***** */ const EXPORTED_SYMBOLS = ['Store', 'SnapshotStore', 'BookmarksStore', - 'HistoryStore', 'CookieStore', 'PasswordStore']; + 'HistoryStore', 'CookieStore', 'PasswordStore', 'FormStore']; const Cc = Components.classes; const Ci = Components.interfaces; @@ -1033,3 +1033,71 @@ PasswordStore.prototype = { } }; PasswordStore.prototype.__proto__ = new Store(); + +function FormStore() { + this._init(); +} +FormStore.prototype = { + _logName: "FormStore", + + __formDB: null, + get _formDB() { + if (!this.__formDB) { + var file = Cc["@mozilla.org/file/directory_service;1"]. + getService(Ci.nsIProperties). + get("ProfD", Ci.nsIFile); + file.append("formhistory.sqlite"); + var stor = Cc["@mozilla.org/storage/service;1"]. + getService(Ci.mozIStorageService); + this.__formDB = stor.openDatabase(file); + } + return this.__formDB; + }, + + __formHistory: null, + get _formHistory() { + if (!this.__formHistory) + this.__formHistory = Cc["@mozilla.org/satchel/form-history;1"]. + getService(Ci.nsIFormHistory2); + return this.__formHistory; + }, + + _createCommand: function FormStore__createCommand(command) { + this._log.info("FormStore got createCommand: " + command ); + this._formHistory.addEntry(command.data.name, command.data.value); + }, + + _removeCommand: function FormStore__removeCommand(command) { + this._log.info("FormStore got removeCommand: " + command ); + this._formHistory.removeEntry(command.data.name, command.data.value); + }, + + _editCommand: function FormStore__editCommand(command) { + this._log.info("FormStore got editCommand: " + command ); + this._log.warn("Form syncs are expected to only be create/remove!"); + }, + + wrap: function FormStore_wrap() { + var items = []; + var stmnt = this._formDB.createStatement("SELECT * FROM moz_formhistory"); + + while (stmnt.executeStep()) { + var nam = stmnt.getUTF8String(1); + var val = stmnt.getUTF8String(2); + var key = Utils.sha1(nam + val); + + items[key] = { name: nam, value: val }; + } + + return items; + }, + + wipe: function FormStore_wipe() { + this._formHistory.removeAllEntries(); + }, + + resetGUIDs: function FormStore_resetGUIDs() { + // Not needed. + } +}; +FormStore.prototype.__proto__ = new Store(); diff --git a/services/sync/modules/syncCores.js b/services/sync/modules/syncCores.js index 370b5c9d835..1f9493533ad 100644 --- a/services/sync/modules/syncCores.js +++ b/services/sync/modules/syncCores.js @@ -35,7 +35,7 @@ * ***** END LICENSE BLOCK ***** */ const EXPORTED_SYMBOLS = ['SyncCore', 'BookmarksSyncCore', 'HistorySyncCore', - 'CookieSyncCore', 'PasswordSyncCore']; + 'CookieSyncCore', 'PasswordSyncCore', 'FormSyncCore']; const Cc = Components.classes; const Ci = Components.interfaces; @@ -537,3 +537,48 @@ PasswordSyncCore.prototype = { } }; PasswordSyncCore.prototype.__proto__ = new SyncCore(); + +function FormSyncCore() { + this._init(); +} +FormSyncCore.prototype = { + _logName: "FormSync", + + __formDB: null, + get _formDB() { + if (!this.__formDB) { + var file = Cc["@mozilla.org/file/directory_service;1"]. + getService(Ci.nsIProperties). + get("ProfD", Ci.nsIFile); + file.append("formhistory.sqlite"); + var stor = Cc["@mozilla.org/storage/service;1"]. + getService(Ci.mozIStorageService); + this.__formDB = stor.openDatabase(file); + } + return this.__formDB; + }, + + _itemExists: function FSC__itemExists(GUID) { + var found = false; + var stmnt = this._formDB.createStatement("SELECT * FROM moz_formhistory"); + + /* Same performance restrictions as PasswordSyncCore apply here: + caching required */ + while (stmnt.executeStep()) { + var nam = stmnt.getUTF8String(1); + var val = stmnt.getUTF8String(2); + var key = Utils.sha1(nam + val); + + if (key == GUID) + found = true; + } + + return found; + }, + + _commandLike: function FSC_commandLike(a, b) { + /* Not required as GUIDs for similar data sets will be the same */ + return false; + } +}; +FormSyncCore.prototype.__proto__ = new SyncCore(); diff --git a/services/sync/services-sync.js b/services/sync/services-sync.js index c8362f00619..9890ce14e46 100644 --- a/services/sync/services-sync.js +++ b/services/sync/services-sync.js @@ -16,6 +16,7 @@ pref("extensions.weave.engine.bookmarks", true); pref("extensions.weave.engine.history", true); pref("extensions.weave.engine.cookies", false ); pref("extensions.weave.engine.passwords", false ); +pref("extensions.weave.engine.forms", false ); pref("extensions.weave.log.appender.console", "Warn"); pref("extensions.weave.log.appender.dump", "Error"); From 1c421141bc519398ecd6a25f58934ddbc0a217a9 Mon Sep 17 00:00:00 2001 From: Anant Narayanan Date: Thu, 22 May 2008 15:58:29 -0700 Subject: [PATCH 2/2] Add support for engine 'scores'. Bug #434812, r=thunder --- services/sync/modules/engines.js | 17 +++- services/sync/modules/trackers.js | 143 ++++++++++++++++++++++++++++++ 2 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 services/sync/modules/trackers.js diff --git a/services/sync/modules/engines.js b/services/sync/modules/engines.js index 68f29334a0c..47c82d01e3c 100644 --- a/services/sync/modules/engines.js +++ b/services/sync/modules/engines.js @@ -52,6 +52,7 @@ Cu.import("resource://weave/dav.js"); Cu.import("resource://weave/identity.js"); Cu.import("resource://weave/stores.js"); Cu.import("resource://weave/syncCores.js"); +Cu.import("resource://weave/trackers.js"); Cu.import("resource://weave/async.js"); Function.prototype.async = Async.sugar; @@ -123,7 +124,7 @@ Engine.prototype = { return this.__json; }, - // _core, and _store need to be overridden in subclasses + // _core, _store and _tracker need to be overridden in subclasses __core: null, get _core() { if (!this.__core) @@ -137,6 +138,13 @@ Engine.prototype = { this.__store = new Store(); return this.__store; }, + + __tracker: null, + get _tracker() { + if (!this.__tracker) + this.__tracker = new Tracker(); + return this.__tracker; + }, __snapshot: null, get _snapshot() { @@ -857,6 +865,13 @@ BookmarksEngine.prototype = { return this.__store; }, + __tracker: null, + get _tracker() { + if (!this.__tracker) + this.__tracker = new BookmarksTracker(); + return this.__tracker; + }, + syncMounts: function BmkEngine_syncMounts(onComplete) { this._syncMounts.async(this, onComplete); }, diff --git a/services/sync/modules/trackers.js b/services/sync/modules/trackers.js new file mode 100644 index 00000000000..118fcc716ff --- /dev/null +++ b/services/sync/modules/trackers.js @@ -0,0 +1,143 @@ +/* ***** 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 Bookmarks Sync. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2007 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Anant Narayanan + * + * 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 ***** */ + +const EXPORTED_SYMBOLS = ['Tracker', 'BookmarksTracker']; + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cr = Components.results; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); +Cu.import("resource://weave/log4moz.js"); +Cu.import("resource://weave/constants.js"); +Cu.import("resource://weave/util.js"); +Cu.import("resource://weave/async.js"); + +Function.prototype.async = Async.sugar; + +/* + * Trackers are associated with a single engine and deal with + * listening for changes to their particular data type + * and updating their 'score', indicating how urgently they + * want to sync. + * + * 'score's range from 0 (Nothing's changed) + * to 100 (I need to sync now!) + * -1 is also a valid score + * (don't sync me unless the user specifically requests it) + * + * Setting a score outside of this range will raise an exception. + * Well not yet, but it will :) + */ + function Tracker() { + this._init(); + } + Tracker.prototype = { + _logName: "Tracker", + _score: 0, + + _init: function T__init() { + this._log = Log4Moz.Service.getLogger("Service." + this._logName); + this._score = 0; + }, + + get score() { + if (this._score >= 100) + return 100; + else + return this._score; + }, + + /* Should be called by service everytime a sync + * has been done for an engine + */ + resetScore: function T_resetScore() { + this._score = 0; + } + }; + + /* + * Tracker objects for each engine may need to subclass the + * getScore routine, which returns the current 'score' for that + * engine. How the engine decides to set the score is upto it, + * as long as the value between 0 and 100 actually corresponds + * to its urgency to sync. + * + * Here's an example BookmarksTracker. We don't subclass getScore + * because the observer methods take care of updating _score which + * getScore returns by default. + */ + function BookmarksTracker() { + this._init(); + } + BookmarksTracker.prototype = { + _logName: "BMTracker", + + /* We don't care about the first three */ + onBeginUpdateBatch: function BMT_onBeginUpdateBatch() { + + }, + onEndUpdateBatch: function BMT_onEndUpdateBatch() { + + }, + onItemVisited: function BMT_onItemChanged() { + + }, + /* Every add or remove is worth 4 points, + * on the basis that adding or removing 20 bookmarks + * means its time to sync? + */ + onItemAdded: function BMT_onEndUpdateBatch() { + this._score += 4; + }, + onItemRemoved: function BMT_onItemRemoved() { + this._score += 4; + }, + /* Changes are worth 2 points? */ + onItemChanged: function BMT_onItemChanged() { + this._score += 2; + }, + + _init: function BMT__init() { + super._init(); + Cc["@mozilla.org/browser/nav-bookmarks-service;1"]. + getService(Ci.nsINavBookmarksService). + addObserver(this, false); + } + } + BookmarksTracker.prototype.__proto__ = new Tracker(); +