Merge lp:~jamesh/bindwood/recording-observer into lp:bindwood

Proposed by James Henstridge
Status: Merged
Approved by: dobey
Approved revision: 35
Merged at revision: 29
Proposed branch: lp:~jamesh/bindwood/recording-observer
Merge into: lp:bindwood
Prerequisite: lp:~jamesh/bindwood/refactor-desktopcouch-access
Diff against target: 312 lines (+288/-0)
4 files modified
modules/sync.jsm (+119/-0)
mozmill/shared-modules/bookmarks.js (+1/-0)
mozmill/test-files/feed.atom (+18/-0)
mozmill/tests/test_sync_observer.js (+150/-0)
To merge this branch: bzr merge lp:~jamesh/bindwood/recording-observer
Reviewer Review Type Date Requested Status
Zachery Bir (community) Approve
Eric Casteleijn (community) Approve
Review via email: mp+49335@code.launchpad.net

Commit message

Add a bookmark observer implementation that just records the GUIDs of changed items.

Description of the change

This is part of the refactoring work on Bindwood. Rather than directly updating CouchDB as bookmarks change, I want to delay those updates so they can be done together with the main synchronisation process.

This branch includes a new bookmarks observer implementation that simply records the GUIDs of changed items as the bookmarks change. Unlike the existing Bindwood schema, I want to store parent GUID references with bookmarks, so reparenting a bookmark should imply that the bookmark has changed, rather than just its parent.

The observer depends on another object to handle the local ID -> GUID mapping, which is not included in this branch: that will come later. For now, I've just worked with a fake implementation in the tests that derives GUIDs from local IDs in a simple way.

To post a comment you must log in.
Revision history for this message
Eric Casteleijn (thisfred) :
review: Approve
Revision history for this message
Zachery Bir (urbanape) wrote :

This looks great

review: Approve
Revision history for this message
Ubuntu One Auto Pilot (otto-pilot) wrote :

The prerequisite lp:~jamesh/bindwood/refactor-desktopcouch-access has not yet been merged into lp:bindwood.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'modules/sync.jsm'
2--- modules/sync.jsm 1970-01-01 00:00:00 +0000
3+++ modules/sync.jsm 2011-02-11 03:41:22 +0000
4@@ -0,0 +1,119 @@
5+/*
6+ * Copyright 2009-2011 Canonical Ltd.
7+ *
8+ * This program is free software: you can redistribute it and/or modify it
9+ * under the terms of the GNU General Public License version 3, as published
10+ * by the Free Software Foundation.
11+ *
12+ * This program is distributed in the hope that it will be useful, but
13+ * WITHOUT ANY WARRANTY; without even the implied warranties of
14+ * MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
15+ * PURPOSE. See the GNU General Public License for more details.
16+ *
17+ * You should have received a copy of the GNU General Public License along
18+ * with this program. If not, see <http://www.gnu.org/licenses/>.
19+ */
20+
21+const EXPORTED_SYMBOLS = ["BookmarksObserver"];
22+
23+const Cc = Components.classes;
24+const Ci = Components.interfaces;
25+const Cr = Components.results;
26+
27+var bookmarksService = Cc["@mozilla.org/browser/nav-bookmarks-service;1"]
28+ .getService(Ci.nsINavBookmarksService);
29+var livemarkService = Cc["@mozilla.org/browser/livemark-service;2"]
30+ .getService(Ci.nsILivemarkService);
31+var annotationService = Cc["@mozilla.org/browser/annotation-service;1"]
32+ .getService(Ci.nsIAnnotationService);
33+
34+
35+function BookmarksObserver(sync) {
36+ this.sync = sync;
37+ this.changed_guids = {}
38+}
39+
40+BookmarksObserver.prototype = {
41+ QueryInterface: function(iid) {
42+ if (iid.equals(Ci.nsINavBookmarkObserver) ||
43+ iid.equals(Ci.nsINavBookmarkObserver_MOZILLA_1_9_1_ADDITIONS) ||
44+ iid.equals(Ci.nsISupports)) {
45+ return this;
46+ }
47+ throw Cr.NS_ERROR_NO_INTERFACE;
48+ },
49+
50+ should_ignore: function(item_id, folder_id) {
51+ if (arguments.length < 2) {
52+ folder_id = bookmarksService.getFolderIdForItem(item_id);
53+ }
54+ // Ignore children of Livemarks, since they are automatically
55+ // created.
56+ if (livemarkService.isLivemark(folder_id)) {
57+ return true;
58+ }
59+ // XXX: Should also verify that the item is a child of a
60+ // folder we care about.
61+ return false;
62+ },
63+
64+ record_change: function(item_id) {
65+ var guid = this.sync.guid_from_id(item_id);
66+ this.changed_guids[guid] = true;
67+ },
68+
69+ clear_changes: function() {
70+ this.changed_guids = {};
71+ },
72+
73+ onItemAdded: function(item_id, folder_id, child_index) {
74+ if (this.should_ignore(item_id, folder_id))
75+ return;
76+
77+ // The parent folder is modified, since its child list has
78+ // changed.
79+ this.record_change(item_id);
80+ this.record_change(folder_id);
81+ },
82+
83+ onBeforeItemRemoved: function(item_id) {
84+ folder_id = bookmarksService.getFolderIdForItem(item_id);
85+ if (this.should_ignore(item_id, folder_id))
86+ return;
87+ this.record_change(item_id);
88+ this.record_change(folder_id);
89+ },
90+
91+ onItemChanged: function(item_id, property, is_annotation, value) {
92+ if (this.should_ignore(item_id))
93+ return;
94+
95+ // XXX: We could optimise the list of property changes we
96+ // respond to like Firefox Sync here.
97+
98+ // Ignore icon changes.
99+ if (property == "favicon")
100+ return;
101+
102+ this.record_change(item_id);
103+ },
104+
105+ onItemMoved: function(item_id, old_parent, old_index,
106+ new_parent, new_index) {
107+ if (this.should_ignore(item_id, new_parent))
108+ return;
109+
110+ this.record_change(old_parent);
111+ if (old_parent != new_parent) {
112+ // The item has been moved to a different folder, rather
113+ // than just to a new position within the same folder.
114+ this.record_change(item_id);
115+ this.record_change(new_parent);
116+ }
117+ },
118+
119+ // Currently unhandled
120+ onBeginUpdateBatch: function() {},
121+ onEndUpdateBatch: function() {},
122+ onItemVisited: function(aBookmarkId, aVisitID, time) {},
123+};
124
125=== modified file 'mozmill/shared-modules/bookmarks.js'
126--- mozmill/shared-modules/bookmarks.js 2011-02-11 03:41:22 +0000
127+++ mozmill/shared-modules/bookmarks.js 2011-02-11 03:41:22 +0000
128@@ -42,5 +42,6 @@
129 }
130
131 exports.bookmarksService = bookmarksService;
132+exports.livemarkService = livemarkService;
133 exports.createURI = createURI;
134 exports.clearBookmarks = clearBookmarks;
135
136=== added file 'mozmill/test-files/feed.atom'
137--- mozmill/test-files/feed.atom 1970-01-01 00:00:00 +0000
138+++ mozmill/test-files/feed.atom 2011-02-11 03:41:22 +0000
139@@ -0,0 +1,18 @@
140+<?xml version="1.0" encoding="UTF-8"?>
141+<feed xmlns="http://www.w3.org/2005/Atom">
142+ <id>urn:uuid:518ac9fe-646b-42de-8e80-4d7372f15d00</id>
143+ <title>Example feed</title>
144+ <link href="http://www.example.com/"/>
145+ <updated>2011-02-11T02:30:00Z</updated>
146+ <author>
147+ <name>Joe Smith</name>
148+ </author>
149+
150+ <entry>
151+ <id>urn:uuid:314128f7-542d-44b8-91cd-4f03ad5bddf1</id>
152+ <title>Feed item</title>
153+ <link href="test.html"/>
154+ <updated>2011-02-11T02:30:00Z</updated>
155+ <summary>Feed item description</summary>
156+ </entry>
157+</feed>
158
159=== added file 'mozmill/tests/test_sync_observer.js'
160--- mozmill/tests/test_sync_observer.js 1970-01-01 00:00:00 +0000
161+++ mozmill/tests/test_sync_observer.js 2011-02-11 03:41:22 +0000
162@@ -0,0 +1,150 @@
163+var bm = require("../shared-modules/bookmarks");
164+
165+const TIMEOUT = 5000;
166+
167+const LOCAL_TEST_FOLDER = collector.addHttpResource('../test-files/');
168+const LOCAL_TEST_PAGE = LOCAL_TEST_FOLDER + 'test.html';
169+const LOCAL_TEST_FEED = LOCAL_TEST_FOLDER + 'feed.atom';
170+
171+var setupModule = function(module) {
172+ module.controller = mozmill.getBrowserController();
173+ module.jum = {}
174+ module.sync = {}
175+ module.fake_synchroniser = {
176+ guid_from_id: function (item_id) {
177+ return "guid:" + item_id;
178+ }
179+ }
180+ module.observer = null;
181+ Cu.import("resource://mozmill/modules/jum.js", module.jum);
182+ Cu.import("resource://bindwood/sync.jsm", module.sync);
183+};
184+
185+
186+var setupTest = function(test) {
187+ observer = new sync.BookmarksObserver(fake_synchroniser);
188+ bm.bookmarksService.addObserver(observer, false);
189+};
190+
191+var teardownTest = function(test) {
192+ bm.bookmarksService.removeObserver(observer);
193+ bm.clearBookmarks();
194+};
195+
196+var testQueryInterface = function() {
197+ jum.assertEquals(
198+ observer.QueryInterface(Ci.nsISupports), observer);
199+ jum.assertEquals(
200+ observer.QueryInterface(Ci.nsINavBookmarkObserver), observer);
201+ if (Ci.nsINavBookmarkObserver_MOZILLA_1_9_1_ADDITIONS) {
202+ jum.assertEquals(
203+ observer.QueryInterface(Ci.nsINavBookmarkObserver_MOZILLA_1_9_1_ADDITIONS), observer);
204+ }
205+};
206+
207+var assertChanged = function (item_id) {
208+ var guid = fake_synchroniser.guid_from_id(item_id);
209+ return observer.changed_guids[guid] != null;
210+};
211+
212+var assertNotChanged = function (item_id) {
213+ var guid = fake_synchroniser.guid_from_id(item_id);
214+ return observer.changed_guids[guid] == null;
215+};
216+
217+var testClearChanges = function() {
218+ var dummy_id = 42;
219+ observer.record_change(dummy_id);
220+ assertChanged(dummy_id);
221+ observer.clear_changes(dummy_id);
222+ assertNotChanged(dummy_id);
223+};
224+
225+var testAddBookmark = function() {
226+ var bookmark_id = bm.bookmarksService.insertBookmark(
227+ bm.bookmarksService.toolbarFolder,
228+ bm.createURI(LOCAL_TEST_PAGE),
229+ bm.bookmarksService.DEFAULT_INDEX,
230+ "Bookmark title");
231+ assertChanged(bookmark_id);
232+ assertChanged(bm.bookmarksService.toolbarFolder);
233+};
234+
235+var testRemoveBookmark = function() {
236+ var bookmark_id = bm.bookmarksService.insertBookmark(
237+ bm.bookmarksService.toolbarFolder,
238+ bm.createURI(LOCAL_TEST_PAGE),
239+ bm.bookmarksService.DEFAULT_INDEX,
240+ "Bookmark title");
241+ observer.clear_changes();
242+
243+ bm.bookmarksService.removeItem(bookmark_id);
244+ assertChanged(bookmark_id);
245+ assertChanged(bm.bookmarksService.toolbarFolder);
246+};
247+
248+var testMoveBookmarkSameFolder = function() {
249+ var folder_id = bm.bookmarksService.createFolder(
250+ bm.bookmarksService.toolbarFolder,
251+ "Folder", bm.bookmarksService.DEFAULT_INDEX);
252+ var bookmark1_id = bm.bookmarksService.insertBookmark(
253+ folder_id, bm.createURI(LOCAL_TEST_PAGE + "#one"),
254+ bm.bookmarksService.DEFAULT_INDEX, "Bookmark 1");
255+ var bookmark2_id = bm.bookmarksService.insertBookmark(
256+ folder_id, bm.createURI(LOCAL_TEST_PAGE + "#two"),
257+ bm.bookmarksService.DEFAULT_INDEX, "Bookmark 2");
258+ observer.clear_changes();
259+
260+ // If we move a bookmark within the same folder (i.e. no parent
261+ // change), then only the parent folder has changed.
262+ bm.bookmarksService.moveItem(bookmark2_id, folder_id, 0);
263+ assertChanged(folder_id);
264+ assertNotChanged(bookmark2_id);
265+};
266+
267+var testMoveBookmarkDifferentFolder = function() {
268+ var folder1_id = bm.bookmarksService.createFolder(
269+ bm.bookmarksService.toolbarFolder,
270+ "Folder 1", bm.bookmarksService.DEFAULT_INDEX);
271+ var folder2_id = bm.bookmarksService.createFolder(
272+ bm.bookmarksService.toolbarFolder,
273+ "Folder 2", bm.bookmarksService.DEFAULT_INDEX);
274+ var bookmark_id = bm.bookmarksService.insertBookmark(
275+ folder1_id, bm.createURI(LOCAL_TEST_PAGE),
276+ bm.bookmarksService.DEFAULT_INDEX, "Bookmark");
277+ observer.clear_changes();
278+
279+ bm.bookmarksService.moveItem(
280+ bookmark_id, folder2_id, bm.bookmarksService.DEFAULT_INDEX);
281+ // The item is changed because it has a new parent. The two
282+ // folders have changed because their child lists have changed.
283+ assertChanged(bookmark_id);
284+ assertChanged(folder1_id);
285+ assertChanged(folder2_id);
286+};
287+
288+var testLivemarkChildrenIgnored = function() {
289+ var livemark_id = bm.livemarkService.createLivemark(
290+ bm.bookmarksService.toolbarFolder,
291+ "Test Feed",
292+ bm.createURI("http://www.example.com/"),
293+ bm.createURI(LOCAL_TEST_FEED),
294+ bm.bookmarksService.DEFAULT_INDEX);
295+
296+ // Wait for the livemark to load, then check that it has been
297+ // loaded correctly.
298+ var livemark_child_id;
299+ controller.waitFor(function() {
300+ livemark_child_id = bm.bookmarksService.getIdForItemAt(
301+ livemark_id, 0);
302+ return livemark_child_id >= 0;
303+ }, TIMEOUT);
304+ var livemark_child_uri = bm.bookmarksService.getBookmarkURI(
305+ livemark_child_id);
306+ jum.assertEquals(livemark_child_uri.spec, LOCAL_TEST_PAGE);
307+
308+ // Only the livemark itself is recorded as changed: the children
309+ // are ignored.
310+ assertChanged(livemark_id);
311+ assertNotChanged(livemark_child_id);
312+}

Subscribers

People subscribed via source and target branches