1
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
2
Cu.import("resource://services-sync/engines/history.js");
3
Cu.import("resource://services-sync/util.js");
5
const TIMESTAMP1 = (Date.now() - 103406528) * 1000;
6
const TIMESTAMP2 = (Date.now() - 6592903) * 1000;
7
const TIMESTAMP3 = (Date.now() - 123894) * 1000;
9
function queryPlaces(uri, options) {
10
let query = Svc.History.getNewQuery();
12
let res = Svc.History.executeQuery(query, options);
13
res.root.containerOpen = true;
16
for (let i = 0; i < res.root.childCount; i++)
17
results.push(res.root.getChild(i));
21
function queryHistoryVisits(uri) {
22
let options = Svc.History.getNewQueryOptions();
23
options.queryType = Ci.nsINavHistoryQueryOptions.QUERY_TYPE_HISTORY;
24
options.resultType = Ci.nsINavHistoryQueryOptions.RESULTS_AS_VISIT;
25
options.sortingMode = Ci.nsINavHistoryQueryOptions.SORT_BY_DATE_ASCENDING;
26
return queryPlaces(uri, options);
29
function onNextTitleChanged(callback) {
30
Svc.History.addObserver({
31
onBeginUpdateBatch: function onBeginUpdateBatch() {},
32
onEndUpdateBatch: function onEndUpdateBatch() {},
33
onPageChanged: function onPageChanged() {},
34
onTitleChanged: function onTitleChanged() {
35
Svc.History.removeObserver(this);
36
Utils.delay(callback, 0, this);
38
onVisit: function onVisit() {},
39
onDeleteVisits: function onDeleteVisits() {},
40
onPageExpired: function onPageExpired() {},
41
onBeforeDeleteURI: function onBeforeDeleteURI() {},
42
onDeleteURI: function onDeleteURI() {},
43
onClearHistory: function onClearHistory() {},
44
QueryInterface: XPCOMUtils.generateQI([
45
Ci.nsINavHistoryObserver,
46
Ci.nsINavHistoryObserver_MOZILLA_1_9_1_ADDITIONS,
47
Ci.nsISupportsWeakReference
52
// Ensure exceptions from inside callbacks leads to test failures while
53
// we still clean up properly.
54
function ensureThrows(func) {
57
func.apply(this, arguments);
59
Svc.History.removeAllPages();
66
initTestLogging("Trace");
68
let store = new HistoryEngine()._store;
69
function applyEnsureNoFailures(records) {
70
do_check_eq(store.applyIncomingBatch(records).length, 0);
73
_("Verify that we've got an empty store to work with.");
74
do_check_eq([id for (id in store.getAllIDs())].length, 0);
76
let fxuri, fxguid, tburi, tbguid;
78
Utils.asyncChain(function (next) {
80
_("Let's create an entry in the database.");
81
fxuri = Utils.makeURI("http://getfirefox.com/");
82
Svc.History.addPageWithDetails(fxuri, "Get Firefox!", TIMESTAMP1);
84
_("Verify that the entry exists.");
85
let ids = [id for (id in store.getAllIDs())];
86
do_check_eq(ids.length, 1);
88
do_check_true(store.itemExists(fxguid));
90
_("If we query a non-existent record, it's marked as deleted.");
91
let record = store.createRecord("non-existent");
92
do_check_true(record.deleted);
94
_("Verify createRecord() returns a complete record.");
95
record = store.createRecord(fxguid);
96
do_check_eq(record.histUri, fxuri.spec);
97
do_check_eq(record.title, "Get Firefox!");
98
do_check_eq(record.visits.length, 1);
99
do_check_eq(record.visits[0].date, TIMESTAMP1);
100
do_check_eq(record.visits[0].type, Ci.nsINavHistoryService.TRANSITION_LINK);
102
_("Let's modify the record and have the store update the database.");
103
let secondvisit = {date: TIMESTAMP2,
104
type: Ci.nsINavHistoryService.TRANSITION_TYPED};
105
onNextTitleChanged(ensureThrows(function() {
106
let queryres = queryHistoryVisits(fxuri);
107
do_check_eq(queryres.length, 2);
108
do_check_eq(queryres[0].time, TIMESTAMP1);
109
do_check_eq(queryres[0].title, "Hol Dir Firefox!");
110
do_check_eq(queryres[1].time, TIMESTAMP2);
111
do_check_eq(queryres[1].title, "Hol Dir Firefox!");
114
applyEnsureNoFailures([
116
histUri: record.histUri,
117
title: "Hol Dir Firefox!",
118
visits: [record.visits[0], secondvisit]}
123
_("Create a brand new record through the store.");
124
tbguid = Utils.makeGUID();
125
tburi = Utils.makeURI("http://getthunderbird.com");
126
onNextTitleChanged(ensureThrows(function() {
127
do_check_eq([id for (id in store.getAllIDs())].length, 2);
128
let queryres = queryHistoryVisits(tburi);
129
do_check_eq(queryres.length, 1);
130
do_check_eq(queryres[0].time, TIMESTAMP3);
131
do_check_eq(queryres[0].title, "The bird is the word!");
134
applyEnsureNoFailures([
137
title: "The bird is the word!",
138
visits: [{date: TIMESTAMP3,
139
type: Ci.nsINavHistoryService.TRANSITION_TYPED}]}
144
_("Make sure we handle a null title gracefully (it can happen in some cases, e.g. for resource:// URLs)");
145
let resguid = Utils.makeGUID();
146
let resuri = Utils.makeURI("unknown://title");
147
applyEnsureNoFailures([
149
histUri: resuri.spec,
151
visits: [{date: TIMESTAMP3,
152
type: Ci.nsINavHistoryService.TRANSITION_TYPED}]}
154
do_check_eq([id for (id in store.getAllIDs())].length, 3);
155
let queryres = queryHistoryVisits(resuri);
156
do_check_eq(queryres.length, 1);
157
do_check_eq(queryres[0].time, TIMESTAMP3);
162
_("Make sure we handle invalid URLs in places databases gracefully.");
163
let query = "INSERT INTO moz_places "
164
+ "(url, title, rev_host, visit_count, last_visit_date) "
165
+ "VALUES ('invalid-uri', 'Invalid URI', '.', 1, " + TIMESTAMP3 + ")";
166
let stmt = Svc.History.DBConnection.createAsyncStatement(query);
167
let result = Utils.queryAsync(stmt);
168
do_check_eq([id for (id in store.getAllIDs())].length, 4);
170
_("Make sure we report records with invalid URIs.");
171
let invalid_uri_guid = Utils.makeGUID();
172
let failed = store.applyIncomingBatch([{
173
id: invalid_uri_guid,
174
histUri: ":::::::::::::::",
175
title: "Doesn't have a valid URI",
176
visits: [{date: TIMESTAMP3,
177
type: Ci.nsINavHistoryService.TRANSITION_EMBED}]}
179
do_check_eq(failed.length, 1);
180
do_check_eq(failed[0], invalid_uri_guid);
182
_("Make sure we handle records with invalid GUIDs gracefully (ignore).");
183
applyEnsureNoFailures([
185
histUri: "http://invalid.guid/",
186
title: "Doesn't have a valid GUID",
187
visits: [{date: TIMESTAMP3,
188
type: Ci.nsINavHistoryService.TRANSITION_EMBED}]}
191
_("Make sure we report records with invalid visits, gracefully handle non-integer dates.");
192
let no_date_visit_guid = Utils.makeGUID();
193
let no_type_visit_guid = Utils.makeGUID();
194
let invalid_type_visit_guid = Utils.makeGUID();
195
let non_integer_visit_guid = Utils.makeGUID();
196
failed = store.applyIncomingBatch([
197
{id: no_date_visit_guid,
198
histUri: "http://no.date.visit/",
199
title: "Visit has no date",
200
visits: [{date: TIMESTAMP3}]},
201
{id: no_type_visit_guid,
202
histUri: "http://no.type.visit/",
203
title: "Visit has no type",
204
visits: [{type: Ci.nsINavHistoryService.TRANSITION_EMBED}]},
205
{id: invalid_type_visit_guid,
206
histUri: "http://invalid.type.visit/",
207
title: "Visit has invalid type",
208
visits: [{date: TIMESTAMP3,
209
type: Ci.nsINavHistoryService.TRANSITION_LINK - 1}]},
210
{id: non_integer_visit_guid,
211
histUri: "http://non.integer.visit/",
212
title: "Visit has non-integer date",
213
visits: [{date: 1234.567,
214
type: Ci.nsINavHistoryService.TRANSITION_EMBED}]}
216
do_check_eq(failed.length, 3);
218
let expected = [no_date_visit_guid,
220
invalid_type_visit_guid].sort();
221
for (let i = 0; i < expected.length; i++) {
222
do_check_eq(failed[i], expected[i]);
225
_("Make sure we handle records with javascript: URLs gracefully.");
226
applyEnsureNoFailures([
227
{id: Utils.makeGUID(),
228
histUri: "javascript:''",
229
title: "javascript:''",
230
visits: [{date: TIMESTAMP3,
231
type: Ci.nsINavHistoryService.TRANSITION_EMBED}]}
234
_("Make sure we handle records without any visits gracefully.");
235
applyEnsureNoFailures([
236
{id: Utils.makeGUID(),
237
histUri: "http://getfirebug.com",
238
title: "Get Firebug!",
242
_("Remove an existent record and a non-existent from the store.");
243
applyEnsureNoFailures([{id: fxguid, deleted: true},
244
{id: Utils.makeGUID(), deleted: true}]);
245
do_check_false(store.itemExists(fxguid));
246
let queryres = queryHistoryVisits(fxuri);
247
do_check_eq(queryres.length, 0);
249
_("Make sure wipe works.");
251
do_check_eq([id for (id in store.getAllIDs())].length, 0);
252
queryres = queryHistoryVisits(fxuri);
253
do_check_eq(queryres.length, 0);
254
queryres = queryHistoryVisits(tburi);
255
do_check_eq(queryres.length, 0);
258
Svc.History.removeAllPages();