1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
2 /*
3 * Copyright © 2011 Igalia S.L.
4 *
5 * This file is part of Epiphany.
6 *
7 * Epiphany is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * Epiphany is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with Epiphany. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "config.h"
22 #include "ephy-history-service.h"
23
24 #include "ephy-history-service-private.h"
25 #include "ephy-history-types.h"
26 #include "ephy-lib-type-builtins.h"
27 #include "ephy-prefs.h"
28 #include "ephy-settings.h"
29 #include "ephy-sqlite-connection.h"
30 #include "ephy-sync-utils.h"
31
32 #include <errno.h>
33 #include <glib.h>
34 #include <glib/gstdio.h>
35
36 typedef gboolean (*EphyHistoryServiceMethod) (EphyHistoryService *self,
37 gpointer data,
38 gpointer *result);
39
40 typedef enum {
41 /* WRITE */
42 SET_URL_TITLE,
43 SET_URL_ZOOM_LEVEL,
44 SET_URL_HIDDEN,
45 ADD_VISIT,
46 ADD_VISITS,
47 DELETE_URLS,
48 DELETE_HOST,
49 CLEAR,
50 /* QUIT */
51 QUIT,
52 /* READ */
53 GET_URL,
54 GET_HOST_FOR_URL,
55 QUERY_URLS,
56 QUERY_VISITS,
57 GET_HOSTS,
58 QUERY_HOSTS
59 } EphyHistoryServiceMessageType;
60
61 enum {
62 VISIT_URL,
63 URLS_VISITED,
64 CLEARED,
65 URL_TITLE_CHANGED,
66 URL_DELETED,
67 HOST_DELETED,
68 LAST_SIGNAL
69 };
70
71 static guint signals[LAST_SIGNAL];
72
73 typedef struct _EphyHistoryServiceMessage {
74 EphyHistoryService *service;
75 EphyHistoryServiceMessageType type;
76 gpointer *method_argument;
77 gboolean success;
78 gpointer result;
79 gpointer user_data;
80 GCancellable *cancellable;
81 GDestroyNotify method_argument_cleanup;
82 GDestroyNotify result_cleanup;
83 EphyHistoryJobCallback callback;
84 } EphyHistoryServiceMessage;
85
86 static gpointer run_history_service_thread (EphyHistoryService *self);
87 static void ephy_history_service_process_message (EphyHistoryService *self,
88 EphyHistoryServiceMessage *message);
89 static gboolean ephy_history_service_execute_quit (EphyHistoryService *self,
90 gpointer data,
91 gpointer *result);
92 static void ephy_history_service_quit (EphyHistoryService *self,
93 EphyHistoryJobCallback callback,
94 gpointer user_data);
95
96 enum {
97 PROP_0,
98 PROP_HISTORY_FILENAME,
99 PROP_MEMORY,
100 LAST_PROP
101 };
102
103 static GParamSpec *obj_properties[LAST_PROP];
104
105 G_DEFINE_TYPE (EphyHistoryService, ephy_history_service, G_TYPE_OBJECT);
106
107 static void
ephy_history_service_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)108 ephy_history_service_set_property (GObject *object,
109 guint property_id,
110 const GValue *value,
111 GParamSpec *pspec)
112 {
113 EphyHistoryService *self = EPHY_HISTORY_SERVICE (object);
114
115 switch (property_id) {
116 case PROP_HISTORY_FILENAME:
117 g_free (self->history_filename);
118 self->history_filename = g_value_dup_string (value);
119 break;
120 case PROP_MEMORY:
121 self->in_memory = g_value_get_boolean (value);
122 break;
123 default:
124 G_OBJECT_WARN_INVALID_PROPERTY_ID (self, property_id, pspec);
125 break;
126 }
127 }
128
129 static void
ephy_history_service_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)130 ephy_history_service_get_property (GObject *object,
131 guint property_id,
132 GValue *value,
133 GParamSpec *pspec)
134 {
135 EphyHistoryService *self = EPHY_HISTORY_SERVICE (object);
136 switch (property_id) {
137 case PROP_HISTORY_FILENAME:
138 g_value_set_string (value, self->history_filename);
139 break;
140 default:
141 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
142 break;
143 }
144 }
145
146 static void
ephy_history_service_finalize(GObject * object)147 ephy_history_service_finalize (GObject *object)
148 {
149 EphyHistoryService *self = EPHY_HISTORY_SERVICE (object);
150
151 ephy_history_service_quit (self, NULL, NULL);
152
153 if (self->history_thread)
154 g_thread_join (self->history_thread);
155
156 g_free (self->history_filename);
157
158 G_OBJECT_CLASS (ephy_history_service_parent_class)->finalize (object);
159 }
160
161 static void
ephy_history_service_dispose(GObject * object)162 ephy_history_service_dispose (GObject *object)
163 {
164 EphyHistoryService *self = EPHY_HISTORY_SERVICE (object);
165
166 g_clear_handle_id (&self->queue_urls_visited_id, g_source_remove);
167
168 G_OBJECT_CLASS (ephy_history_service_parent_class)->dispose (object);
169 }
170
171 static void
ephy_history_service_constructed(GObject * object)172 ephy_history_service_constructed (GObject *object)
173 {
174 EphyHistoryService *self = EPHY_HISTORY_SERVICE (object);
175
176 G_OBJECT_CLASS (ephy_history_service_parent_class)->constructed (object);
177
178 self->queue = g_async_queue_new ();
179
180 /* This value is checked in several functions to verify that they are only
181 * ever run on the history thread. Accordingly, we'd better be sure it's set
182 * before it is checked for the first time. That requires a lock here. */
183 g_mutex_lock (&self->history_thread_mutex);
184 self->history_thread = g_thread_new ("EphyHistoryService", (GThreadFunc)run_history_service_thread, self);
185
186 /* Additionally, make sure the SQLite connection has really been opened before
187 * returning. We need this so that we can test that using a read-only service
188 * at the same time as a read/write service does not cause the read/write
189 * service to break. This delay is required because we need to be sure the
190 * read/write service has completed initialization before attempting to open
191 * the read-only service, or initializing the read-only service will fail.
192 * This isn't needed except in test mode, because only tests might run
193 * multiple history services, but it's harmless and cleaner to do always.
194 */
195 while (!self->history_thread_initialized)
196 g_cond_wait (&self->history_thread_initialized_condition, &self->history_thread_mutex);
197
198 g_mutex_unlock (&self->history_thread_mutex);
199 }
200
201 static gboolean
emit_urls_visited(EphyHistoryService * self)202 emit_urls_visited (EphyHistoryService *self)
203 {
204 g_signal_emit (self, signals[URLS_VISITED], 0);
205 self->queue_urls_visited_id = 0;
206
207 return FALSE;
208 }
209
210 static void
ephy_history_service_queue_urls_visited(EphyHistoryService * self)211 ephy_history_service_queue_urls_visited (EphyHistoryService *self)
212 {
213 if (self->queue_urls_visited_id)
214 return;
215
216 self->queue_urls_visited_id =
217 g_idle_add_full (G_PRIORITY_LOW, (GSourceFunc)emit_urls_visited, self, NULL);
218 }
219
220 static void
ephy_history_service_class_init(EphyHistoryServiceClass * klass)221 ephy_history_service_class_init (EphyHistoryServiceClass *klass)
222 {
223 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
224
225 gobject_class->finalize = ephy_history_service_finalize;
226 gobject_class->dispose = ephy_history_service_dispose;
227 gobject_class->constructed = ephy_history_service_constructed;
228 gobject_class->get_property = ephy_history_service_get_property;
229 gobject_class->set_property = ephy_history_service_set_property;
230
231 signals[VISIT_URL] =
232 g_signal_new ("visit-url",
233 G_OBJECT_CLASS_TYPE (gobject_class),
234 G_SIGNAL_RUN_LAST,
235 0, NULL, NULL, NULL,
236 G_TYPE_NONE,
237 1,
238 G_TYPE_POINTER | G_SIGNAL_TYPE_STATIC_SCOPE);
239
240 /**
241 * EphyHistoryService::urls-visited:
242 * @service: the #EphyHistoryService that received the signal
243 *
244 * The ::urls-visited signal is emitted after one or more visits to
245 * URLS have taken place. This signal is intended for use-cases when
246 * precise information of the actual URLS visited is not important and
247 * there is only interest in the fact that there have been changes in
248 * the history. For more precise information, you can use ::visit-url
249 **/
250 signals[URLS_VISITED] =
251 g_signal_new ("urls-visited",
252 G_OBJECT_CLASS_TYPE (gobject_class),
253 G_SIGNAL_RUN_LAST,
254 0, NULL, NULL, NULL,
255 G_TYPE_NONE,
256 0);
257
258 signals[CLEARED] =
259 g_signal_new ("cleared",
260 G_OBJECT_CLASS_TYPE (gobject_class),
261 G_SIGNAL_RUN_LAST,
262 0, NULL, NULL, NULL,
263 G_TYPE_NONE,
264 0);
265
266 signals[URL_TITLE_CHANGED] =
267 g_signal_new ("url-title-changed",
268 G_OBJECT_CLASS_TYPE (gobject_class),
269 G_SIGNAL_RUN_LAST,
270 0, NULL, NULL, NULL,
271 G_TYPE_NONE,
272 2,
273 G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE,
274 G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE);
275
276 signals[URL_DELETED] =
277 g_signal_new ("url-deleted",
278 G_OBJECT_CLASS_TYPE (gobject_class),
279 G_SIGNAL_RUN_LAST,
280 0, NULL, NULL, NULL,
281 G_TYPE_NONE,
282 1,
283 G_TYPE_POINTER | G_SIGNAL_TYPE_STATIC_SCOPE);
284
285 signals[HOST_DELETED] =
286 g_signal_new ("host-deleted",
287 G_OBJECT_CLASS_TYPE (gobject_class),
288 G_SIGNAL_RUN_LAST,
289 0, NULL, NULL, NULL,
290 G_TYPE_NONE,
291 1,
292 G_TYPE_STRING | G_SIGNAL_TYPE_STATIC_SCOPE);
293
294 obj_properties[PROP_HISTORY_FILENAME] =
295 g_param_spec_string ("history-filename",
296 "History filename",
297 "The filename of the SQLite file holding containing history",
298 NULL,
299 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
300
301 obj_properties[PROP_MEMORY] =
302 g_param_spec_boolean ("memory",
303 "In memory mode",
304 "Whether the history service works in memory mode",
305 FALSE,
306 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS);
307
308 g_object_class_install_properties (gobject_class, LAST_PROP, obj_properties);
309 }
310
311 static void
ephy_history_service_init(EphyHistoryService * self)312 ephy_history_service_init (EphyHistoryService *self)
313 {
314 }
315
316 EphyHistoryService *
ephy_history_service_new(const char * history_filename,EphySQLiteConnectionMode mode)317 ephy_history_service_new (const char *history_filename,
318 EphySQLiteConnectionMode mode)
319 {
320 return EPHY_HISTORY_SERVICE (g_object_new (EPHY_TYPE_HISTORY_SERVICE,
321 "history-filename", history_filename,
322 "memory", mode == EPHY_SQLITE_CONNECTION_MODE_MEMORY,
323 NULL));
324 }
325
326 static gint
sort_messages(EphyHistoryServiceMessage * a,EphyHistoryServiceMessage * b,gpointer user_data)327 sort_messages (EphyHistoryServiceMessage *a,
328 EphyHistoryServiceMessage *b,
329 gpointer user_data)
330 {
331 return a->type > b->type ? 1 : a->type == b->type ? 0 : -1;
332 }
333
334 static EphyHistoryServiceMessage *
ephy_history_service_message_new(EphyHistoryService * service,EphyHistoryServiceMessageType type,gpointer method_argument,GDestroyNotify method_argument_cleanup,GDestroyNotify result_cleanup,GCancellable * cancellable,EphyHistoryJobCallback callback,gpointer user_data)335 ephy_history_service_message_new (EphyHistoryService *service,
336 EphyHistoryServiceMessageType type,
337 gpointer method_argument,
338 GDestroyNotify method_argument_cleanup,
339 GDestroyNotify result_cleanup,
340 GCancellable *cancellable,
341 EphyHistoryJobCallback callback,
342 gpointer user_data)
343 {
344 EphyHistoryServiceMessage *message = g_new0 (EphyHistoryServiceMessage, 1);
345
346 message->service = service;
347 message->type = type;
348 message->method_argument = method_argument;
349 message->method_argument_cleanup = method_argument_cleanup;
350 message->result_cleanup = result_cleanup;
351 message->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
352 message->callback = callback;
353 message->user_data = user_data;
354
355 return message;
356 }
357
358 static void
ephy_history_service_message_free(EphyHistoryServiceMessage * message)359 ephy_history_service_message_free (EphyHistoryServiceMessage *message)
360 {
361 if (message->method_argument_cleanup)
362 message->method_argument_cleanup (message->method_argument);
363
364 if (message->result_cleanup)
365 message->result_cleanup (message->result);
366
367 if (message->cancellable)
368 g_object_unref (message->cancellable);
369
370 g_free (message);
371 }
372
373 static void
ephy_history_service_send_message(EphyHistoryService * self,EphyHistoryServiceMessage * message)374 ephy_history_service_send_message (EphyHistoryService *self,
375 EphyHistoryServiceMessage *message)
376 {
377 g_async_queue_push_sorted (self->queue, message, (GCompareDataFunc)sort_messages, NULL);
378 }
379
380 static void
ephy_history_service_open_transaction(EphyHistoryService * self)381 ephy_history_service_open_transaction (EphyHistoryService *self)
382 {
383 GError *error = NULL;
384 g_assert (self->history_thread == g_thread_self ());
385
386 if (self->history_database == NULL)
387 return;
388
389 ephy_sqlite_connection_begin_transaction (self->history_database, &error);
390 if (error != NULL) {
391 g_warning ("Could not open history database transaction: %s", error->message);
392 g_error_free (error);
393 }
394 }
395
396 static void
ephy_history_service_commit_transaction(EphyHistoryService * self)397 ephy_history_service_commit_transaction (EphyHistoryService *self)
398 {
399 GError *error = NULL;
400 g_assert (self->history_thread == g_thread_self ());
401
402 if (self->history_database == NULL)
403 return;
404
405 ephy_sqlite_connection_commit_transaction (self->history_database, &error);
406 if (error != NULL) {
407 g_warning ("Could not commit history database transaction: %s", error->message);
408 g_error_free (error);
409 }
410 }
411
412 static gboolean
ephy_history_service_open_database_connections(EphyHistoryService * self)413 ephy_history_service_open_database_connections (EphyHistoryService *self)
414 {
415 GError *error = NULL;
416
417 g_assert (self->history_thread == g_thread_self ());
418
419 if (self->history_database != NULL)
420 g_object_unref (self->history_database);
421
422 self->history_database = ephy_sqlite_connection_new (self->in_memory ? EPHY_SQLITE_CONNECTION_MODE_MEMORY
423 : EPHY_SQLITE_CONNECTION_MODE_READWRITE,
424 self->history_filename);
425 ephy_sqlite_connection_open (self->history_database, &error);
426 if (error) {
427 g_object_unref (self->history_database);
428 self->history_database = NULL;
429
430 if (!g_error_matches (error, EPHY_SQLITE_ERROR, SQLITE_CANTOPEN) ||
431 g_file_test (self->history_filename, G_FILE_TEST_EXISTS)) {
432 g_warning ("Could not open history database at %s: %s", self->history_filename, error->message);
433 }
434 g_error_free (error);
435 return FALSE;
436 } else {
437 ephy_sqlite_connection_enable_foreign_keys (self->history_database);
438 }
439
440 return (ephy_history_service_initialize_hosts_table (self) &&
441 ephy_history_service_initialize_urls_table (self) &&
442 ephy_history_service_initialize_visits_table (self));
443 }
444
445 static void
ephy_history_service_close_database_connections(EphyHistoryService * self)446 ephy_history_service_close_database_connections (EphyHistoryService *self)
447 {
448 g_assert (self->history_thread == g_thread_self ());
449
450 ephy_sqlite_connection_close (self->history_database);
451 g_object_unref (self->history_database);
452 self->history_database = NULL;
453 }
454
455 static gboolean
ephy_history_service_execute_quit(EphyHistoryService * self,gpointer data,gpointer * result)456 ephy_history_service_execute_quit (EphyHistoryService *self,
457 gpointer data,
458 gpointer *result)
459 {
460 g_assert (self->history_thread == g_thread_self ());
461
462 g_async_queue_unref (self->queue);
463
464 self->scheduled_to_quit = TRUE;
465
466 return FALSE;
467 }
468
469 static gpointer
run_history_service_thread(EphyHistoryService * self)470 run_history_service_thread (EphyHistoryService *self)
471 {
472 EphyHistoryServiceMessage *message;
473 gboolean success;
474
475 /* Note that self->history_thread is only written once, and that's guaranteed
476 * to have occurred before we enter this critical section due to this mutex.
477 * Accordingly, we do not need to use the mutex when performing these
478 * assertions in other functions.
479 */
480 g_mutex_lock (&self->history_thread_mutex);
481 g_assert (self->history_thread == g_thread_self ());
482
483 success = ephy_history_service_open_database_connections (self);
484
485 self->history_thread_initialized = TRUE;
486 g_cond_signal (&self->history_thread_initialized_condition);
487 g_mutex_unlock (&self->history_thread_mutex);
488
489 if (!success)
490 return NULL;
491
492 do {
493 message = g_async_queue_try_pop (self->queue);
494 if (!message) {
495 /* Block the thread until there's data in the queue. */
496 message = g_async_queue_pop (self->queue);
497 }
498
499 /* Process item. */
500 ephy_history_service_process_message (self, message);
501 } while (!self->scheduled_to_quit);
502
503 ephy_history_service_close_database_connections (self);
504
505 return NULL;
506 }
507
508 static gboolean
ephy_history_service_execute_job_callback(gpointer data)509 ephy_history_service_execute_job_callback (gpointer data)
510 {
511 EphyHistoryServiceMessage *message = (EphyHistoryServiceMessage *)data;
512
513 g_assert (message->callback || message->type == CLEAR);
514
515 if (g_cancellable_is_cancelled (message->cancellable)) {
516 ephy_history_service_message_free (message);
517 return FALSE;
518 }
519
520 if (message->callback)
521 message->callback (message->service, message->success, message->result, message->user_data);
522
523 if (message->type == CLEAR)
524 g_signal_emit (message->service, signals[CLEARED], 0);
525
526 ephy_history_service_message_free (message);
527
528 return FALSE;
529 }
530
531 typedef struct {
532 EphyHistoryService *service;
533 gpointer user_data;
534 GDestroyNotify destroy_func;
535 } SignalEmissionContext;
536
537 static void
signal_emission_context_free(SignalEmissionContext * ctx)538 signal_emission_context_free (SignalEmissionContext *ctx)
539 {
540 g_object_unref (ctx->service);
541 if (ctx->destroy_func && ctx->user_data)
542 ctx->destroy_func (ctx->user_data);
543 g_free (ctx);
544 }
545
546 static SignalEmissionContext *
signal_emission_context_new(EphyHistoryService * service,gpointer user_data,GDestroyNotify destroy_func)547 signal_emission_context_new (EphyHistoryService *service,
548 gpointer user_data,
549 GDestroyNotify destroy_func)
550 {
551 SignalEmissionContext *ctx = g_new0 (SignalEmissionContext, 1);
552
553 ctx->service = g_object_ref (service);
554 ctx->user_data = user_data;
555 ctx->destroy_func = destroy_func;
556
557 return ctx;
558 }
559
560 static gboolean
ephy_history_service_execute_add_visit_helper(EphyHistoryService * self,EphyHistoryPageVisit * visit)561 ephy_history_service_execute_add_visit_helper (EphyHistoryService *self,
562 EphyHistoryPageVisit *visit)
563 {
564 if (visit->url->host == NULL)
565 visit->url->host = ephy_history_service_get_host_row_from_url (self, visit->url->url);
566 else if (visit->url->host->id == -1) {
567 /* This will happen when we migrate the old history to the new
568 * format. We need to store a zoom level for a not-yet-created
569 * host, so we'll end up here. Ugly, but it works. */
570 double zoom_level = visit->url->host->zoom_level;
571 ephy_history_host_free (visit->url->host);
572 visit->url->host = ephy_history_service_get_host_row_from_url (self, visit->url->url);
573 visit->url->host->zoom_level = zoom_level;
574 }
575
576 visit->url->host->visit_count++;
577 ephy_history_service_update_host_row (self, visit->url->host);
578
579 /* A NULL return here means that the URL does not yet exist in the database.
580 * This overwrites visit->url so we have to test the sync id against NULL on
581 * both branches. */
582 if (ephy_history_service_get_url_row (self, visit->url->url, visit->url) == NULL) {
583 visit->url->last_visit_time = visit->visit_time;
584 visit->url->visit_count = 1;
585
586 if (!visit->url->sync_id)
587 visit->url->sync_id = ephy_sync_utils_get_random_sync_id ();
588
589 ephy_history_service_add_url_row (self, visit->url);
590
591 if (!self->in_memory && visit->url->id == -1) {
592 g_warning ("Adding visit failed after failed URL addition.");
593 return FALSE;
594 }
595 } else {
596 visit->url->visit_count++;
597
598 if (visit->visit_time > visit->url->last_visit_time)
599 visit->url->last_visit_time = visit->visit_time;
600
601 if (!visit->url->sync_id)
602 visit->url->sync_id = ephy_sync_utils_get_random_sync_id ();
603
604 ephy_history_service_update_url_row (self, visit->url);
605 }
606
607 if (visit->url->notify_visit)
608 g_signal_emit (self, signals[VISIT_URL], 0, visit->url);
609
610 ephy_history_service_add_visit_row (self, visit);
611 return visit->id != -1;
612 }
613
614 static gboolean
ephy_history_service_execute_add_visit(EphyHistoryService * self,EphyHistoryPageVisit * visit,gpointer * result)615 ephy_history_service_execute_add_visit (EphyHistoryService *self,
616 EphyHistoryPageVisit *visit,
617 gpointer *result)
618 {
619 gboolean success;
620 g_assert (self->history_thread == g_thread_self ());
621
622 success = ephy_history_service_execute_add_visit_helper (self, visit);
623 return success;
624 }
625
626 static gboolean
ephy_history_service_execute_add_visits(EphyHistoryService * self,GList * visits,gpointer * result)627 ephy_history_service_execute_add_visits (EphyHistoryService *self,
628 GList *visits,
629 gpointer *result)
630 {
631 gboolean success = TRUE;
632 g_assert (self->history_thread == g_thread_self ());
633
634 while (visits) {
635 success = success && ephy_history_service_execute_add_visit_helper (self, (EphyHistoryPageVisit *)visits->data);
636 visits = visits->next;
637 }
638
639 return success;
640 }
641
642 static gboolean
ephy_history_service_execute_find_visits(EphyHistoryService * self,EphyHistoryQuery * query,gpointer * result)643 ephy_history_service_execute_find_visits (EphyHistoryService *self,
644 EphyHistoryQuery *query,
645 gpointer *result)
646 {
647 GList *visits = ephy_history_service_find_visit_rows (self, query);
648 GList *current = visits;
649
650 /* FIXME: We don't have a good way to tell the difference between failures and empty returns */
651 while (current) {
652 EphyHistoryPageVisit *visit = (EphyHistoryPageVisit *)current->data;
653 if (ephy_history_service_get_url_row (self, NULL, visit->url) == NULL) {
654 ephy_history_page_visit_list_free (visits);
655 g_warning ("Tried to process an orphaned page visit");
656 return FALSE;
657 }
658
659 current = current->next;
660 }
661
662 *result = visits;
663 return TRUE;
664 }
665
666 static gboolean
ephy_history_service_execute_get_hosts(EphyHistoryService * self,gpointer pointer,gpointer * results)667 ephy_history_service_execute_get_hosts (EphyHistoryService *self,
668 gpointer pointer,
669 gpointer *results)
670 {
671 GList *hosts;
672
673 hosts = ephy_history_service_get_all_hosts (self);
674 *results = hosts;
675
676 return TRUE;
677 }
678
679 static gboolean
ephy_history_service_execute_query_hosts(EphyHistoryService * self,EphyHistoryQuery * query,gpointer * results)680 ephy_history_service_execute_query_hosts (EphyHistoryService *self,
681 EphyHistoryQuery *query,
682 gpointer *results)
683 {
684 GList *hosts;
685
686 hosts = ephy_history_service_find_host_rows (self, query);
687 *results = hosts;
688
689 return TRUE;
690 }
691
692 void
ephy_history_service_add_visit(EphyHistoryService * self,EphyHistoryPageVisit * visit,GCancellable * cancellable,EphyHistoryJobCallback callback,gpointer user_data)693 ephy_history_service_add_visit (EphyHistoryService *self,
694 EphyHistoryPageVisit *visit,
695 GCancellable *cancellable,
696 EphyHistoryJobCallback callback,
697 gpointer user_data)
698 {
699 EphyHistoryServiceMessage *message;
700
701 g_assert (EPHY_IS_HISTORY_SERVICE (self));
702 g_assert (visit != NULL);
703
704 message = ephy_history_service_message_new (self, ADD_VISIT,
705 ephy_history_page_visit_copy (visit),
706 (GDestroyNotify)ephy_history_page_visit_free,
707 NULL,
708 cancellable, callback, user_data);
709 ephy_history_service_send_message (self, message);
710 }
711
712 void
ephy_history_service_add_visits(EphyHistoryService * self,GList * visits,GCancellable * cancellable,EphyHistoryJobCallback callback,gpointer user_data)713 ephy_history_service_add_visits (EphyHistoryService *self,
714 GList *visits,
715 GCancellable *cancellable,
716 EphyHistoryJobCallback callback,
717 gpointer user_data)
718 {
719 EphyHistoryServiceMessage *message;
720
721 g_assert (EPHY_IS_HISTORY_SERVICE (self));
722 g_assert (visits != NULL);
723
724 message = ephy_history_service_message_new (self, ADD_VISITS,
725 ephy_history_page_visit_list_copy (visits),
726 (GDestroyNotify)ephy_history_page_visit_list_free,
727 NULL,
728 cancellable, callback, user_data);
729 ephy_history_service_send_message (self, message);
730 }
731
732 void
ephy_history_service_find_visits_in_time(EphyHistoryService * self,gint64 from,gint64 to,GCancellable * cancellable,EphyHistoryJobCallback callback,gpointer user_data)733 ephy_history_service_find_visits_in_time (EphyHistoryService *self,
734 gint64 from,
735 gint64 to,
736 GCancellable *cancellable,
737 EphyHistoryJobCallback callback,
738 gpointer user_data)
739 {
740 EphyHistoryQuery *query;
741
742 g_assert (EPHY_IS_HISTORY_SERVICE (self));
743
744 query = ephy_history_query_new ();
745 query->from = from;
746 query->to = to;
747
748 ephy_history_service_query_visits (self, query, cancellable, callback, user_data);
749 ephy_history_query_free (query);
750 }
751
752 void
ephy_history_service_query_visits(EphyHistoryService * self,EphyHistoryQuery * query,GCancellable * cancellable,EphyHistoryJobCallback callback,gpointer user_data)753 ephy_history_service_query_visits (EphyHistoryService *self,
754 EphyHistoryQuery *query,
755 GCancellable *cancellable,
756 EphyHistoryJobCallback callback,
757 gpointer user_data)
758 {
759 EphyHistoryServiceMessage *message;
760
761 g_assert (EPHY_IS_HISTORY_SERVICE (self));
762 g_assert (query != NULL);
763
764 message = ephy_history_service_message_new (self, QUERY_VISITS,
765 ephy_history_query_copy (query),
766 (GDestroyNotify)ephy_history_query_free,
767 (GDestroyNotify)ephy_history_page_visit_list_free,
768 cancellable, callback, user_data);
769 ephy_history_service_send_message (self, message);
770 }
771
772 static gboolean
ephy_history_service_execute_query_urls(EphyHistoryService * self,EphyHistoryQuery * query,gpointer * result)773 ephy_history_service_execute_query_urls (EphyHistoryService *self,
774 EphyHistoryQuery *query,
775 gpointer *result)
776 {
777 GList *urls = ephy_history_service_find_url_rows (self, query);
778
779 *result = urls;
780
781 return TRUE;
782 }
783
784 void
ephy_history_service_query_urls(EphyHistoryService * self,EphyHistoryQuery * query,GCancellable * cancellable,EphyHistoryJobCallback callback,gpointer user_data)785 ephy_history_service_query_urls (EphyHistoryService *self,
786 EphyHistoryQuery *query,
787 GCancellable *cancellable,
788 EphyHistoryJobCallback callback,
789 gpointer user_data)
790 {
791 EphyHistoryServiceMessage *message;
792
793 g_assert (EPHY_IS_HISTORY_SERVICE (self));
794 g_assert (query != NULL);
795
796 message = ephy_history_service_message_new (self, QUERY_URLS,
797 ephy_history_query_copy (query),
798 (GDestroyNotify)ephy_history_query_free,
799 (GDestroyNotify)ephy_history_url_list_free,
800 cancellable, callback, user_data);
801 ephy_history_service_send_message (self, message);
802 }
803
804 void
ephy_history_service_get_hosts(EphyHistoryService * self,GCancellable * cancellable,EphyHistoryJobCallback callback,gpointer user_data)805 ephy_history_service_get_hosts (EphyHistoryService *self,
806 GCancellable *cancellable,
807 EphyHistoryJobCallback callback,
808 gpointer user_data)
809 {
810 EphyHistoryServiceMessage *message;
811
812 g_assert (EPHY_IS_HISTORY_SERVICE (self));
813
814 message = ephy_history_service_message_new (self, GET_HOSTS,
815 NULL, NULL,
816 (GDestroyNotify)ephy_history_host_list_free,
817 cancellable, callback, user_data);
818 ephy_history_service_send_message (self, message);
819 }
820
821 void
ephy_history_service_query_hosts(EphyHistoryService * self,EphyHistoryQuery * query,GCancellable * cancellable,EphyHistoryJobCallback callback,gpointer user_data)822 ephy_history_service_query_hosts (EphyHistoryService *self,
823 EphyHistoryQuery *query,
824 GCancellable *cancellable,
825 EphyHistoryJobCallback callback,
826 gpointer user_data)
827 {
828 EphyHistoryServiceMessage *message;
829
830 g_assert (EPHY_IS_HISTORY_SERVICE (self));
831
832 message = ephy_history_service_message_new (self, QUERY_HOSTS,
833 ephy_history_query_copy (query),
834 (GDestroyNotify)ephy_history_query_free,
835 (GDestroyNotify)ephy_history_host_list_free,
836 cancellable, callback, user_data);
837 ephy_history_service_send_message (self, message);
838 }
839
840 static gboolean
set_url_title_signal_emit(SignalEmissionContext * ctx)841 set_url_title_signal_emit (SignalEmissionContext *ctx)
842 {
843 EphyHistoryURL *url = (EphyHistoryURL *)ctx->user_data;
844
845 g_signal_emit (ctx->service, signals[URL_TITLE_CHANGED], 0, url->url, url->title);
846
847 return FALSE;
848 }
849
850 static gboolean
ephy_history_service_execute_set_url_title(EphyHistoryService * self,EphyHistoryURL * url,gpointer * result)851 ephy_history_service_execute_set_url_title (EphyHistoryService *self,
852 EphyHistoryURL *url,
853 gpointer *result)
854 {
855 char *title = g_strdup (url->title);
856
857 if (ephy_history_service_get_url_row (self, NULL, url) == NULL) {
858 /* The URL is not yet in the database, so we can't update it.. */
859 g_free (title);
860 return FALSE;
861 } else {
862 SignalEmissionContext *ctx;
863
864 g_free (url->title);
865 url->title = title;
866 ephy_history_service_update_url_row (self, url);
867
868 ctx = signal_emission_context_new (self,
869 ephy_history_url_copy (url),
870 (GDestroyNotify)ephy_history_url_free);
871 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
872 (GSourceFunc)set_url_title_signal_emit,
873 ctx, (GDestroyNotify)signal_emission_context_free);
874 return TRUE;
875 }
876 }
877
878 void
ephy_history_service_set_url_title(EphyHistoryService * self,const char * orig_url,const char * title,GCancellable * cancellable,EphyHistoryJobCallback callback,gpointer user_data)879 ephy_history_service_set_url_title (EphyHistoryService *self,
880 const char *orig_url,
881 const char *title,
882 GCancellable *cancellable,
883 EphyHistoryJobCallback callback,
884 gpointer user_data)
885 {
886 EphyHistoryURL *url;
887 EphyHistoryServiceMessage *message;
888
889 g_assert (EPHY_IS_HISTORY_SERVICE (self));
890 g_assert (orig_url != NULL);
891 g_assert (title != NULL);
892 g_assert (*title != '\0');
893
894 url = ephy_history_url_new (orig_url, title, 0, 0, 0);
895 message = ephy_history_service_message_new (self, SET_URL_TITLE,
896 url, (GDestroyNotify)ephy_history_url_free,
897 NULL, cancellable, callback, user_data);
898 ephy_history_service_send_message (self, message);
899 }
900
901 static gboolean
ephy_history_service_execute_set_url_zoom_level(EphyHistoryService * self,GVariant * variant,gpointer * result)902 ephy_history_service_execute_set_url_zoom_level (EphyHistoryService *self,
903 GVariant *variant,
904 gpointer *result)
905 {
906 char *url_string;
907 double zoom_level;
908 EphyHistoryHost *host;
909
910 g_variant_get (variant, "(sd)", &url_string, &zoom_level);
911
912 host = ephy_history_service_get_host_row_from_url (self, url_string);
913 g_free (url_string);
914
915 g_assert (host != NULL);
916
917 host->zoom_level = zoom_level;
918 ephy_history_service_update_host_row (self, host);
919
920 return TRUE;
921 }
922
923 void
ephy_history_service_set_url_zoom_level(EphyHistoryService * self,const char * url,double zoom_level,GCancellable * cancellable,EphyHistoryJobCallback callback,gpointer user_data)924 ephy_history_service_set_url_zoom_level (EphyHistoryService *self,
925 const char *url,
926 double zoom_level,
927 GCancellable *cancellable,
928 EphyHistoryJobCallback callback,
929 gpointer user_data)
930 {
931 EphyHistoryServiceMessage *message;
932 GVariant *variant;
933
934 g_assert (EPHY_IS_HISTORY_SERVICE (self));
935 g_assert (url != NULL);
936
937 /* Ensure that a change value which equals default zoom level is stored as 0.0 */
938 if (zoom_level == g_settings_get_double (EPHY_SETTINGS_WEB, EPHY_PREFS_WEB_DEFAULT_ZOOM_LEVEL))
939 zoom_level = 0.0f;
940
941 variant = g_variant_new ("(sd)", url, zoom_level);
942
943 message = ephy_history_service_message_new (self, SET_URL_ZOOM_LEVEL,
944 variant, (GDestroyNotify)g_variant_unref,
945 NULL, cancellable, callback, user_data);
946 ephy_history_service_send_message (self, message);
947 }
948
949 static gboolean
ephy_history_service_execute_set_url_hidden(EphyHistoryService * self,EphyHistoryURL * url,gpointer * result)950 ephy_history_service_execute_set_url_hidden (EphyHistoryService *self,
951 EphyHistoryURL *url,
952 gpointer *result)
953 {
954 gboolean hidden;
955
956 hidden = url->hidden;
957
958 if (ephy_history_service_get_url_row (self, NULL, url) == NULL) {
959 /* The URL is not yet in the database, so we can't update it.. */
960 return FALSE;
961 } else {
962 url->hidden = hidden;
963 ephy_history_service_update_url_row (self, url);
964 return TRUE;
965 }
966 }
967
968 void
ephy_history_service_set_url_hidden(EphyHistoryService * self,const char * orig_url,gboolean hidden,GCancellable * cancellable,EphyHistoryJobCallback callback,gpointer user_data)969 ephy_history_service_set_url_hidden (EphyHistoryService *self,
970 const char *orig_url,
971 gboolean hidden,
972 GCancellable *cancellable,
973 EphyHistoryJobCallback callback,
974 gpointer user_data)
975 {
976 EphyHistoryServiceMessage *message;
977 EphyHistoryURL *url;
978
979 g_assert (EPHY_IS_HISTORY_SERVICE (self));
980 g_assert (orig_url != NULL);
981
982 url = ephy_history_url_new (orig_url, NULL, 0, 0, 0);
983 url->hidden = hidden;
984
985 message = ephy_history_service_message_new (self, SET_URL_HIDDEN,
986 url, (GDestroyNotify)ephy_history_url_free,
987 NULL, cancellable, callback, user_data);
988 ephy_history_service_send_message (self, message);
989 }
990
991 static gboolean
ephy_history_service_execute_get_url(EphyHistoryService * self,const gchar * orig_url,gpointer * result)992 ephy_history_service_execute_get_url (EphyHistoryService *self,
993 const gchar *orig_url,
994 gpointer *result)
995 {
996 EphyHistoryURL *url;
997
998 url = ephy_history_service_get_url_row (self, orig_url, NULL);
999
1000 *result = url;
1001
1002 return url != NULL;
1003 }
1004
1005 void
ephy_history_service_get_url(EphyHistoryService * self,const char * url,GCancellable * cancellable,EphyHistoryJobCallback callback,gpointer user_data)1006 ephy_history_service_get_url (EphyHistoryService *self,
1007 const char *url,
1008 GCancellable *cancellable,
1009 EphyHistoryJobCallback callback,
1010 gpointer user_data)
1011 {
1012 EphyHistoryServiceMessage *message;
1013
1014 g_assert (EPHY_IS_HISTORY_SERVICE (self));
1015 g_assert (url != NULL);
1016
1017 message = ephy_history_service_message_new (self, GET_URL,
1018 g_strdup (url), g_free, (GDestroyNotify)ephy_history_url_free,
1019 cancellable, callback, user_data);
1020 ephy_history_service_send_message (self, message);
1021 }
1022
1023 static gboolean
ephy_history_service_execute_get_host_for_url(EphyHistoryService * self,const gchar * url,gpointer * result)1024 ephy_history_service_execute_get_host_for_url (EphyHistoryService *self,
1025 const gchar *url,
1026 gpointer *result)
1027 {
1028 EphyHistoryHost *host;
1029
1030 host = ephy_history_service_get_host_row_from_url (self, url);
1031 g_assert (host != NULL);
1032
1033 *result = host;
1034
1035 return host != NULL;
1036 }
1037
1038 void
ephy_history_service_get_host_for_url(EphyHistoryService * self,const char * url,GCancellable * cancellable,EphyHistoryJobCallback callback,gpointer user_data)1039 ephy_history_service_get_host_for_url (EphyHistoryService *self,
1040 const char *url,
1041 GCancellable *cancellable,
1042 EphyHistoryJobCallback callback,
1043 gpointer user_data)
1044 {
1045 EphyHistoryServiceMessage *message;
1046
1047 g_assert (EPHY_IS_HISTORY_SERVICE (self));
1048 g_assert (url != NULL);
1049
1050 message = ephy_history_service_message_new (self, GET_HOST_FOR_URL,
1051 g_strdup (url), g_free, (GDestroyNotify)ephy_history_host_free,
1052 cancellable, callback, user_data);
1053 ephy_history_service_send_message (self, message);
1054 }
1055
1056 static gboolean
delete_urls_signal_emit(SignalEmissionContext * ctx)1057 delete_urls_signal_emit (SignalEmissionContext *ctx)
1058 {
1059 EphyHistoryURL *url = (EphyHistoryURL *)ctx->user_data;
1060
1061 g_signal_emit (ctx->service, signals[URL_DELETED], 0, url);
1062
1063 return FALSE;
1064 }
1065
1066 static gboolean
ephy_history_service_execute_delete_urls(EphyHistoryService * self,GList * urls,gpointer * result)1067 ephy_history_service_execute_delete_urls (EphyHistoryService *self,
1068 GList *urls,
1069 gpointer *result)
1070 {
1071 GList *l;
1072 EphyHistoryURL *url;
1073 SignalEmissionContext *ctx;
1074
1075 for (l = urls; l != NULL; l = l->next) {
1076 url = l->data;
1077 ephy_history_service_delete_url (self, url);
1078
1079 if (url->notify_delete) {
1080 ctx = signal_emission_context_new (self, ephy_history_url_copy (url),
1081 (GDestroyNotify)ephy_history_url_free);
1082 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
1083 (GSourceFunc)delete_urls_signal_emit,
1084 ctx,
1085 (GDestroyNotify)signal_emission_context_free);
1086 }
1087 }
1088
1089 ephy_history_service_delete_orphan_hosts (self);
1090
1091 return TRUE;
1092 }
1093
1094 static gboolean
delete_host_signal_emit(SignalEmissionContext * ctx)1095 delete_host_signal_emit (SignalEmissionContext *ctx)
1096 {
1097 char *host = (char *)ctx->user_data;
1098
1099 g_signal_emit (ctx->service, signals[HOST_DELETED], 0, host);
1100
1101 return FALSE;
1102 }
1103
1104 static gboolean
ephy_history_service_execute_delete_host(EphyHistoryService * self,EphyHistoryHost * host,EphyHistoryJobCallback callback,gpointer user_data)1105 ephy_history_service_execute_delete_host (EphyHistoryService *self,
1106 EphyHistoryHost *host,
1107 EphyHistoryJobCallback callback,
1108 gpointer user_data)
1109 {
1110 SignalEmissionContext *ctx;
1111
1112 ephy_history_service_delete_host_row (self, host);
1113
1114 ctx = signal_emission_context_new (self, g_strdup (host->url),
1115 (GDestroyNotify)g_free);
1116 g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
1117 (GSourceFunc)delete_host_signal_emit,
1118 ctx,
1119 (GDestroyNotify)signal_emission_context_free);
1120
1121 return TRUE;
1122 }
1123
1124 static gboolean
ephy_history_service_execute_clear(EphyHistoryService * self,gpointer pointer,gpointer * result)1125 ephy_history_service_execute_clear (EphyHistoryService *self,
1126 gpointer pointer,
1127 gpointer *result)
1128 {
1129 if (self->history_database == NULL)
1130 return FALSE;
1131
1132 ephy_history_service_commit_transaction (self);
1133 ephy_sqlite_connection_close (self->history_database);
1134 ephy_sqlite_connection_delete_database (self->history_database);
1135
1136 ephy_history_service_open_database_connections (self);
1137 ephy_history_service_open_transaction (self);
1138
1139 return TRUE;
1140 }
1141
1142 void
ephy_history_service_delete_urls(EphyHistoryService * self,GList * urls,GCancellable * cancellable,EphyHistoryJobCallback callback,gpointer user_data)1143 ephy_history_service_delete_urls (EphyHistoryService *self,
1144 GList *urls,
1145 GCancellable *cancellable,
1146 EphyHistoryJobCallback callback,
1147 gpointer user_data)
1148 {
1149 EphyHistoryServiceMessage *message;
1150
1151 g_assert (EPHY_IS_HISTORY_SERVICE (self));
1152 g_assert (urls != NULL);
1153
1154 message = ephy_history_service_message_new (self, DELETE_URLS,
1155 ephy_history_url_list_copy (urls), (GDestroyNotify)ephy_history_url_list_free,
1156 NULL, cancellable, callback, user_data);
1157 ephy_history_service_send_message (self, message);
1158 }
1159
1160 void
ephy_history_service_delete_host(EphyHistoryService * self,EphyHistoryHost * host,GCancellable * cancellable,EphyHistoryJobCallback callback,gpointer user_data)1161 ephy_history_service_delete_host (EphyHistoryService *self,
1162 EphyHistoryHost *host,
1163 GCancellable *cancellable,
1164 EphyHistoryJobCallback callback,
1165 gpointer user_data)
1166 {
1167 EphyHistoryServiceMessage *message =
1168 ephy_history_service_message_new (self, DELETE_HOST,
1169 ephy_history_host_copy (host), (GDestroyNotify)ephy_history_host_free,
1170 NULL, cancellable, callback, user_data);
1171 ephy_history_service_send_message (self, message);
1172 }
1173
1174 void
ephy_history_service_clear(EphyHistoryService * self,GCancellable * cancellable,EphyHistoryJobCallback callback,gpointer user_data)1175 ephy_history_service_clear (EphyHistoryService *self,
1176 GCancellable *cancellable,
1177 EphyHistoryJobCallback callback,
1178 gpointer user_data)
1179 {
1180 EphyHistoryServiceMessage *message;
1181
1182 g_assert (EPHY_IS_HISTORY_SERVICE (self));
1183
1184 message = ephy_history_service_message_new (self, CLEAR,
1185 NULL, NULL, NULL,
1186 cancellable, callback, user_data);
1187 ephy_history_service_send_message (self, message);
1188 }
1189
1190 static void
ephy_history_service_quit(EphyHistoryService * self,EphyHistoryJobCallback callback,gpointer user_data)1191 ephy_history_service_quit (EphyHistoryService *self,
1192 EphyHistoryJobCallback callback,
1193 gpointer user_data)
1194 {
1195 EphyHistoryServiceMessage *message =
1196 ephy_history_service_message_new (self, QUIT,
1197 NULL, NULL, NULL, NULL,
1198 callback, user_data);
1199 ephy_history_service_send_message (self, message);
1200 }
1201
1202 static EphyHistoryServiceMethod methods[] = {
1203 (EphyHistoryServiceMethod)ephy_history_service_execute_set_url_title,
1204 (EphyHistoryServiceMethod)ephy_history_service_execute_set_url_zoom_level,
1205 (EphyHistoryServiceMethod)ephy_history_service_execute_set_url_hidden,
1206 (EphyHistoryServiceMethod)ephy_history_service_execute_add_visit,
1207 (EphyHistoryServiceMethod)ephy_history_service_execute_add_visits,
1208 (EphyHistoryServiceMethod)ephy_history_service_execute_delete_urls,
1209 (EphyHistoryServiceMethod)ephy_history_service_execute_delete_host,
1210 (EphyHistoryServiceMethod)ephy_history_service_execute_clear,
1211 (EphyHistoryServiceMethod)ephy_history_service_execute_quit,
1212 (EphyHistoryServiceMethod)ephy_history_service_execute_get_url,
1213 (EphyHistoryServiceMethod)ephy_history_service_execute_get_host_for_url,
1214 (EphyHistoryServiceMethod)ephy_history_service_execute_query_urls,
1215 (EphyHistoryServiceMethod)ephy_history_service_execute_find_visits,
1216 (EphyHistoryServiceMethod)ephy_history_service_execute_get_hosts,
1217 (EphyHistoryServiceMethod)ephy_history_service_execute_query_hosts
1218 };
1219
1220 static gboolean
ephy_history_service_message_is_write(EphyHistoryServiceMessage * message)1221 ephy_history_service_message_is_write (EphyHistoryServiceMessage *message)
1222 {
1223 return message->type < QUIT;
1224 }
1225
1226 static void
ephy_history_service_process_message(EphyHistoryService * self,EphyHistoryServiceMessage * message)1227 ephy_history_service_process_message (EphyHistoryService *self,
1228 EphyHistoryServiceMessage *message)
1229 {
1230 EphyHistoryServiceMethod method;
1231
1232 g_assert (self->history_thread == g_thread_self ());
1233
1234 if (g_cancellable_is_cancelled (message->cancellable) &&
1235 !ephy_history_service_message_is_write (message)) {
1236 ephy_history_service_message_free (message);
1237 return;
1238 }
1239
1240 method = methods[message->type];
1241 message->result = NULL;
1242 if (message->service->history_database) {
1243 ephy_history_service_open_transaction (self);
1244 message->success = method (message->service, message->method_argument, &message->result);
1245 ephy_history_service_commit_transaction (self);
1246 } else {
1247 message->success = FALSE;
1248 }
1249
1250 if (message->callback || message->type == CLEAR)
1251 g_idle_add ((GSourceFunc)ephy_history_service_execute_job_callback, message);
1252 else
1253 ephy_history_service_message_free (message);
1254
1255 return;
1256 }
1257
1258 /* Public API. */
1259
1260 void
ephy_history_service_find_urls(EphyHistoryService * self,gint64 from,gint64 to,guint limit,gint host,GList * substring_list,EphyHistorySortType sort_type,GCancellable * cancellable,EphyHistoryJobCallback callback,gpointer user_data)1261 ephy_history_service_find_urls (EphyHistoryService *self,
1262 gint64 from,
1263 gint64 to,
1264 guint limit,
1265 gint host,
1266 GList *substring_list,
1267 EphyHistorySortType sort_type,
1268 GCancellable *cancellable,
1269 EphyHistoryJobCallback callback,
1270 gpointer user_data)
1271 {
1272 EphyHistoryQuery *query;
1273
1274 g_assert (EPHY_IS_HISTORY_SERVICE (self));
1275
1276 query = ephy_history_query_new ();
1277 query->from = from;
1278 query->to = to;
1279 query->substring_list = substring_list;
1280 query->sort_type = sort_type;
1281 query->host = host;
1282
1283 if (limit != 0)
1284 query->limit = limit;
1285
1286 ephy_history_service_query_urls (self,
1287 query, cancellable,
1288 callback, user_data);
1289 ephy_history_query_free (query);
1290 }
1291
1292 void
ephy_history_service_visit_url(EphyHistoryService * self,const char * url,const char * sync_id,gint64 visit_time,EphyHistoryPageVisitType visit_type,gboolean should_notify)1293 ephy_history_service_visit_url (EphyHistoryService *self,
1294 const char *url,
1295 const char *sync_id,
1296 gint64 visit_time,
1297 EphyHistoryPageVisitType visit_type,
1298 gboolean should_notify)
1299 {
1300 EphyHistoryPageVisit *visit;
1301
1302 g_assert (EPHY_IS_HISTORY_SERVICE (self));
1303 g_assert (url != NULL);
1304 g_assert (visit_time > 0);
1305
1306 visit = ephy_history_page_visit_new (url, visit_time, visit_type);
1307 visit->url->sync_id = g_strdup (sync_id);
1308 visit->url->notify_visit = should_notify;
1309 ephy_history_service_add_visit (self, visit, NULL, NULL, NULL);
1310 ephy_history_page_visit_free (visit);
1311
1312 ephy_history_service_queue_urls_visited (self);
1313 }
1314
1315 void
ephy_history_service_find_hosts(EphyHistoryService * self,gint64 from,gint64 to,GCancellable * cancellable,EphyHistoryJobCallback callback,gpointer user_data)1316 ephy_history_service_find_hosts (EphyHistoryService *self,
1317 gint64 from,
1318 gint64 to,
1319 GCancellable *cancellable,
1320 EphyHistoryJobCallback callback,
1321 gpointer user_data)
1322 {
1323 EphyHistoryQuery *query;
1324
1325 g_assert (EPHY_IS_HISTORY_SERVICE (self));
1326
1327 query = ephy_history_query_new ();
1328
1329 query->from = from;
1330 query->to = to;
1331
1332 ephy_history_service_query_hosts (self, query,
1333 cancellable, callback, user_data);
1334 ephy_history_query_free (query);
1335 }
1336