1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /*
3 * Copyright © 2012 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-about-handler.h"
23
24 #include "ephy-embed-shell.h"
25 #include "ephy-embed-prefs.h"
26 #include "ephy-embed-utils.h"
27 #include "ephy-file-helpers.h"
28 #include "ephy-flatpak-utils.h"
29 #include "ephy-history-service.h"
30 #include "ephy-prefs.h"
31 #include "ephy-settings.h"
32 #include "ephy-smaps.h"
33 #include "ephy-snapshot-service.h"
34 #include "ephy-web-app-utils.h"
35
36 #include <gio/gio.h>
37 #include <gtk/gtk.h>
38 #include <glib/gi18n.h>
39
40 struct _EphyAboutHandler {
41 GObject parent_instance;
42
43 EphySMaps *smaps;
44 };
45
G_DEFINE_TYPE(EphyAboutHandler,ephy_about_handler,G_TYPE_OBJECT)46 G_DEFINE_TYPE (EphyAboutHandler, ephy_about_handler, G_TYPE_OBJECT)
47
48
49 #define EPHY_ABOUT_OVERVIEW_MAX_ITEMS 9
50
51 #define EPHY_PAGE_TEMPLATE_ABOUT_CSS "ephy-resource:///org/gnome/epiphany/page-templates/about.css"
52
53 static void
54 ephy_about_handler_finalize (GObject *object)
55 {
56 EphyAboutHandler *handler = EPHY_ABOUT_HANDLER (object);
57
58 g_clear_object (&handler->smaps);
59
60 G_OBJECT_CLASS (ephy_about_handler_parent_class)->finalize (object);
61 }
62
63 static void
ephy_about_handler_init(EphyAboutHandler * handler)64 ephy_about_handler_init (EphyAboutHandler *handler)
65 {
66 }
67
68 static void
ephy_about_handler_class_init(EphyAboutHandlerClass * klass)69 ephy_about_handler_class_init (EphyAboutHandlerClass *klass)
70 {
71 GObjectClass *object_class = G_OBJECT_CLASS (klass);
72
73 object_class->finalize = ephy_about_handler_finalize;
74 }
75
76 static EphySMaps *
ephy_about_handler_get_smaps(EphyAboutHandler * handler)77 ephy_about_handler_get_smaps (EphyAboutHandler *handler)
78 {
79 if (!handler->smaps)
80 handler->smaps = ephy_smaps_new ();
81
82 return handler->smaps;
83 }
84
85 static void
ephy_about_handler_finish_request(WebKitURISchemeRequest * request,gchar * data,gssize data_length)86 ephy_about_handler_finish_request (WebKitURISchemeRequest *request,
87 gchar *data,
88 gssize data_length)
89 {
90 GInputStream *stream;
91
92 data_length = data_length != -1 ? data_length : (gssize)strlen (data);
93 stream = g_memory_input_stream_new_from_data (data, data_length, g_free);
94 webkit_uri_scheme_request_finish (request, stream, data_length, "text/html");
95 g_object_unref (stream);
96 }
97
98 static void
handle_memory_finished_cb(EphyAboutHandler * handler,GAsyncResult * result,WebKitURISchemeRequest * request)99 handle_memory_finished_cb (EphyAboutHandler *handler,
100 GAsyncResult *result,
101 WebKitURISchemeRequest *request)
102 {
103 GString *data_str;
104 gsize data_length;
105 char *memory;
106
107 data_str = g_string_new ("<html>");
108
109 memory = g_task_propagate_pointer (G_TASK (result), NULL);
110 if (memory) {
111 g_string_append_printf (data_str, "<head><title>%s</title>"
112 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />"
113 "<link href=\""EPHY_PAGE_TEMPLATE_ABOUT_CSS "\" rel=\"stylesheet\" type=\"text/css\">"
114 "</head><body>"
115 "<div id='memory'>",
116 _("Memory usage"));
117
118 g_string_append_printf (data_str, "<h1>%s</h1>", _("Memory usage"));
119 g_string_append (data_str, memory);
120 g_free (memory);
121
122 g_string_append (data_str, "</div>");
123 }
124
125 g_string_append (data_str, "</html>");
126
127 data_length = data_str->len;
128 ephy_about_handler_finish_request (request, g_string_free (data_str, FALSE), data_length);
129 g_object_unref (request);
130 }
131
132 static void
handle_memory_sync(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)133 handle_memory_sync (GTask *task,
134 gpointer source_object,
135 gpointer task_data,
136 GCancellable *cancellable)
137 {
138 EphyAboutHandler *handler = EPHY_ABOUT_HANDLER (source_object);
139
140 g_task_return_pointer (task,
141 ephy_smaps_to_html (ephy_about_handler_get_smaps (handler)),
142 g_free);
143 }
144
145 static gboolean
ephy_about_handler_handle_memory(EphyAboutHandler * handler,WebKitURISchemeRequest * request)146 ephy_about_handler_handle_memory (EphyAboutHandler *handler,
147 WebKitURISchemeRequest *request)
148 {
149 GTask *task;
150
151 task = g_task_new (handler, NULL,
152 (GAsyncReadyCallback)handle_memory_finished_cb,
153 g_object_ref (request));
154 g_task_run_in_thread (task, handle_memory_sync);
155 g_object_unref (task);
156
157 return TRUE;
158 }
159
160 static gboolean
ephy_about_handler_handle_about(EphyAboutHandler * handler,WebKitURISchemeRequest * request)161 ephy_about_handler_handle_about (EphyAboutHandler *handler,
162 WebKitURISchemeRequest *request)
163 {
164 char *data;
165 char *version;
166 GtkIconInfo *icon_info;
167
168 version = g_strdup_printf (_("Version %s"), VERSION);
169
170 icon_info = gtk_icon_theme_lookup_icon (gtk_icon_theme_get_default (),
171 APPLICATION_ID,
172 256,
173 GTK_ICON_LOOKUP_FORCE_SVG);
174
175 data = g_strdup_printf ("<html><head><title>%s</title>"
176 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />"
177 "<link href=\""EPHY_PAGE_TEMPLATE_ABOUT_CSS "\" rel=\"stylesheet\" type=\"text/css\">"
178 "</head><body>"
179 "<div id=\"about-app\">"
180 "<div class=\"dialog\">"
181 "<img id=\"about-icon\" src=\"file://%s\"/>"
182 "<h1 id=\"about-title\">%s</h1>"
183 "<h2 id=\"about-subtitle\">%s</h2>"
184 "<p id=\"about-tagline\">%s</p>"
185 "<table class=\"properties\">"
186 "<tr><td class=\"prop-label\">%s</td><td class=\"prop-value\">%d.%d.%d</td></tr>"
187 "</table>"
188 "</div></div></body></html>",
189 _("About Web"),
190 icon_info ? gtk_icon_info_get_filename (icon_info) : "",
191 #if !TECH_PREVIEW
192 _("Web"),
193 #else
194 _("Epiphany Technology Preview"),
195 #endif
196 version,
197 _("A simple, clean, beautiful view of the web"),
198 "WebKitGTK", webkit_get_major_version (), webkit_get_minor_version (), webkit_get_micro_version ());
199 g_free (version);
200 if (icon_info)
201 g_object_unref (icon_info);
202
203 ephy_about_handler_finish_request (request, data, -1);
204
205 return TRUE;
206 }
207
208 static gboolean
ephy_about_handler_handle_epiphany(EphyAboutHandler * handler,WebKitURISchemeRequest * request)209 ephy_about_handler_handle_epiphany (EphyAboutHandler *handler,
210 WebKitURISchemeRequest *request)
211 {
212 char *data;
213
214 data = g_strdup_printf ("<html class=\"epiphany-html\"><head><title>%s</title>"
215 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />"
216 "<link href=\""EPHY_PAGE_TEMPLATE_ABOUT_CSS "\" rel=\"stylesheet\" type=\"text/css\">"
217 "</head><body class=\"epiphany-body\">"
218 "<div id=\"ephytext\">"
219 "« Il semble que la perfection soit atteinte non quand il n'y a plus rien à"
220 " ajouter, mais quand il n'y a plus rien à retrancher. »"
221 "</div>"
222 "<div id=\"from\">"
223 "<!-- Terre des Hommes, III: L'Avion, p. 60 -->"
224 "Antoine de Saint-Exupéry"
225 "</div></body></html>",
226 _("Web"));
227
228 ephy_about_handler_finish_request (request, data, -1);
229
230 return TRUE;
231 }
232
233 static void
handle_applications_finished_cb(EphyAboutHandler * handler,GAsyncResult * result,WebKitURISchemeRequest * request)234 handle_applications_finished_cb (EphyAboutHandler *handler,
235 GAsyncResult *result,
236 WebKitURISchemeRequest *request)
237 {
238 GString *data_str;
239 gsize data_length;
240 GList *applications, *p;
241
242 data_str = g_string_new (NULL);
243 applications = g_task_propagate_pointer (G_TASK (result), NULL);
244
245 if (g_list_length (applications) > 0) {
246 g_string_append_printf (data_str, "<html><head><title>%s</title>"
247 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />"
248 "<link href=\""EPHY_PAGE_TEMPLATE_ABOUT_CSS "\" rel=\"stylesheet\" type=\"text/css\">"
249 "<script>"
250 " function deleteWebApp(appID) {"
251 " window.webkit.messageHandlers.aboutApps.postMessage(appID);"
252 " var row = document.getElementById(appID);"
253 " row.parentNode.removeChild(row);"
254 " }"
255 "</script>"
256 "</head><div id=\"applications\"><body class=\"applications-body\"><h1>%s</h1>"
257 "<p>%s</p>",
258 _("Applications"),
259 _("Applications"),
260 _("List of installed web applications"));
261
262 g_string_append (data_str, "<table>");
263
264 for (p = applications; p; p = p->next) {
265 EphyWebApplication *app = (EphyWebApplication *)p->data;
266
267 if (ephy_web_application_is_system (app))
268 continue;
269
270 g_string_append_printf (data_str,
271 "<tbody><tr id =\"%s\">"
272 "<td class=\"icon\"><img width=64 height=64 src=\"file://%s\"></img></td>"
273 "<td class=\"data\"><div class=\"appname\">%s</div><div class=\"appurl\">%s</div></td>"
274 "<td class=\"input\"><input type=\"button\" value=\"%s\" onclick=\"deleteWebApp('%s');\"></td>"
275 "<td class=\"date\">%s <br /> %s</td></tr></tbody>",
276 app->id, app->icon_url, app->name, app->url, _("Delete"), app->id,
277 /* Note for translators: this refers to the installation date. */
278 _("Installed on:"), app->install_date);
279 }
280
281 g_string_append (data_str, "</table></div></body></html>");
282 } else {
283 g_autoptr (GtkIconInfo) icon_info = NULL;
284 g_autofree gchar *icon = g_strconcat ("application-x-addon-symbolic", NULL);
285
286 g_string_append_printf (data_str, "<html><head><title>%s</title>"
287 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />"
288 "<link href=\""EPHY_PAGE_TEMPLATE_ABOUT_CSS "\" rel=\"stylesheet\" type=\"text/css\">"
289 "</head><body class=\"applications-body\">",
290 _("Applications"));
291
292 icon_info = gtk_icon_theme_lookup_icon (gtk_icon_theme_get_default (),
293 icon,
294 128,
295 0);
296 g_string_append_printf (data_str,
297 " <div id=\"overview\" class=\"overview-empty\">\n"
298 " <img src=\"file://%s\"/>\n"
299 " <div><h1>%s</h1></div>\n"
300 " <div><p>%s</p></div>\n"
301 " </div>\n"
302 "</body></html>\n",
303 icon_info ? gtk_icon_info_get_filename (icon_info) : "",
304 /* Displayed when opening applications without any installed web apps. */
305 _("Applications"), _("You can add your favorite website by clicking <b>Install Site as Web Application…</b> within the page menu."));
306 }
307
308 ephy_web_application_free_application_list (applications);
309
310 data_length = data_str->len;
311 ephy_about_handler_finish_request (request, g_string_free (data_str, FALSE), data_length);
312 g_object_unref (request);
313 }
314
315 static void
handle_applications_sync(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)316 handle_applications_sync (GTask *task,
317 gpointer source_object,
318 gpointer task_data,
319 GCancellable *cancellable)
320 {
321 g_task_return_pointer (task,
322 ephy_web_application_get_application_list (),
323 (GDestroyNotify)ephy_web_application_free_application_list);
324 }
325
326 static gboolean
ephy_about_handler_handle_applications(EphyAboutHandler * handler,WebKitURISchemeRequest * request)327 ephy_about_handler_handle_applications (EphyAboutHandler *handler,
328 WebKitURISchemeRequest *request)
329 {
330 GTask *task;
331
332 task = g_task_new (handler, NULL,
333 (GAsyncReadyCallback)handle_applications_finished_cb,
334 g_object_ref (request));
335 g_task_run_in_thread (task, handle_applications_sync);
336 g_object_unref (task);
337
338 return TRUE;
339 }
340
341 static void
history_service_query_urls_cb(EphyHistoryService * history,gboolean success,GList * urls,WebKitURISchemeRequest * request)342 history_service_query_urls_cb (EphyHistoryService *history,
343 gboolean success,
344 GList *urls,
345 WebKitURISchemeRequest *request)
346 {
347 EphySnapshotService *snapshot_service;
348 EphyEmbedShell *shell;
349 GString *data_str;
350 gsize data_length;
351 char *lang;
352 GList *l;
353 guint list_length;
354
355 snapshot_service = ephy_snapshot_service_get_default ();
356 shell = ephy_embed_shell_get_default ();
357
358 data_str = g_string_new (NULL);
359
360 lang = g_strdup (pango_language_to_string (gtk_get_default_language ()));
361 g_strdelimit (lang, "_-@", '\0');
362
363 g_string_append_printf (data_str,
364 "<html xml:lang=\"%s\" lang=\"%s\" dir=\"%s\">\n"
365 "<head>\n"
366 " <title>%s</title>\n"
367 " <meta http-equiv=\"content-type\" content=\"text/html; charset=utf-8\" />\n"
368 " <meta name=\"viewport\" content=\"width=device-width\">"
369 " <link href=\""EPHY_PAGE_TEMPLATE_ABOUT_CSS "\" rel=\"stylesheet\" type=\"text/css\">\n"
370 " <script> </script>\n"
371 "</head>\n"
372 "<body>\n",
373 lang, lang,
374 ((gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL) ? "rtl" : "ltr"),
375 _(OVERVIEW_PAGE_TITLE));
376 g_free (lang);
377
378 list_length = g_list_length (urls);
379
380 if (list_length == 0 || !success) {
381 GtkIconInfo *icon_info;
382 g_autofree gchar *icon = g_strconcat (APPLICATION_ID, "-symbolic", NULL);
383
384 icon_info = gtk_icon_theme_lookup_icon (gtk_icon_theme_get_default (),
385 icon,
386 128,
387 0);
388 g_string_append_printf (data_str,
389 " <div id=\"overview\" class=\"overview-empty\">\n"
390 " <img src=\"file://%s\"/>\n"
391 " <div><h1>%s</h1></div>\n"
392 " <div><p>%s</p></div>\n"
393 " </div>\n"
394 "</body></html>\n",
395 icon_info ? gtk_icon_info_get_filename (icon_info) : "",
396 /* Displayed when opening the browser for the first time. */
397 _("Welcome to Web"), _("Start browsing and your most-visited sites will appear here."));
398 if (icon_info)
399 g_object_unref (icon_info);
400 goto out;
401 }
402
403 g_string_append (data_str,
404 "<div id=\"overview\">\n");
405
406 g_string_append (data_str,
407 "<div id=\"most-visited-grid\">\n");
408
409 for (l = urls; l; l = g_list_next (l)) {
410 EphyHistoryURL *url = (EphyHistoryURL *)l->data;
411 const char *snapshot;
412 g_autofree char *thumbnail_style = NULL;
413 g_autofree char *markup = NULL;
414
415 snapshot = ephy_snapshot_service_lookup_cached_snapshot_path (snapshot_service, url->url);
416 if (snapshot)
417 thumbnail_style = g_strdup_printf (" style=\"background: url(file://%s) no-repeat; background-size: 100%%;\"", snapshot);
418 else
419 ephy_embed_shell_schedule_thumbnail_update (shell, url);
420
421 markup = g_markup_escape_text (url->title, -1);
422 g_string_append_printf (data_str,
423 "<a class=\"overview-item\" title=\"%s\" href=\"%s\">"
424 " <div class=\"overview-close-button\" title=\"%s\"></div>"
425 " <span class=\"overview-thumbnail\"%s></span>"
426 " <span class=\"overview-title\">%s</span>"
427 "</a>",
428 markup, url->url, _("Remove from overview"),
429 thumbnail_style ? thumbnail_style : "", url->title);
430 }
431
432 data_str = g_string_append (data_str,
433 " </div>\n"
434 " </div>\n"
435 "</body></html>\n");
436
437 out:
438 data_length = data_str->len;
439 ephy_about_handler_finish_request (request, g_string_free (data_str, FALSE), data_length);
440 g_object_unref (request);
441 }
442
443 EphyHistoryQuery *
ephy_history_query_new_for_overview(void)444 ephy_history_query_new_for_overview (void)
445 {
446 EphyHistoryQuery *query;
447
448 query = ephy_history_query_new ();
449 query->sort_type = EPHY_HISTORY_SORT_MOST_VISITED;
450 query->limit = EPHY_ABOUT_OVERVIEW_MAX_ITEMS;
451 query->ignore_hidden = TRUE;
452 query->ignore_local = TRUE;
453
454 return query;
455 }
456
457 static gboolean
ephy_about_handler_handle_html_overview(EphyAboutHandler * handler,WebKitURISchemeRequest * request)458 ephy_about_handler_handle_html_overview (EphyAboutHandler *handler,
459 WebKitURISchemeRequest *request)
460 {
461 EphyHistoryService *history;
462 EphyHistoryQuery *query;
463
464 history = ephy_embed_shell_get_global_history_service (ephy_embed_shell_get_default ());
465 query = ephy_history_query_new_for_overview ();
466 ephy_history_service_query_urls (history, query, NULL,
467 (EphyHistoryJobCallback)history_service_query_urls_cb,
468 g_object_ref (request));
469 ephy_history_query_free (query);
470
471 return TRUE;
472 }
473
474 static gboolean
ephy_about_handler_handle_incognito(EphyAboutHandler * handler,WebKitURISchemeRequest * request)475 ephy_about_handler_handle_incognito (EphyAboutHandler *handler,
476 WebKitURISchemeRequest *request)
477 {
478 char *data;
479
480 if (ephy_embed_shell_get_mode (ephy_embed_shell_get_default ()) != EPHY_EMBED_SHELL_MODE_INCOGNITO)
481 return FALSE;
482
483 data = g_strdup_printf ("<html>\n"
484 "<div dir=\"%s\">\n"
485 "<head>\n"
486 "<title>%s</title>\n"
487 "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />"
488 "<link href=\""EPHY_PAGE_TEMPLATE_ABOUT_CSS "\" rel=\"stylesheet\" type=\"text/css\">\n"
489 "</head>\n"
490 "<body class=\"incognito-body\">\n"
491 " <img class=\"incognito-body-image\" src=\"ephy-resource:///org/gnome/epiphany/page-icons/private-mode.svg\">\n" \
492 " <br/>\n"
493 " <h1>%s</h1>\n"
494 " <p>%s</p>\n"
495 " <p><strong>%s</strong> %s</p>\n"
496 "</body>\n"
497 "</div>\n"
498 "</html>\n",
499 gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL ? "rtl" : "ltr",
500 _("Private Browsing"),
501 _("Private Browsing"),
502 _("You are currently browsing incognito. Pages viewed in this "
503 "mode will not show up in your browsing history and all stored "
504 "information will be cleared when you close the window. Files you "
505 "download will be kept."),
506 _("Incognito mode hides your activity only from people using this "
507 "computer."),
508 _("It will not hide your activity from your employer if you are at "
509 "work. Your internet service provider, your government, other "
510 "governments, the websites that you visit, and advertisers on "
511 "these websites may still be tracking you."));
512
513 ephy_about_handler_finish_request (request, data, -1);
514
515 return TRUE;
516 }
517
518 static void
ephy_about_handler_handle_blank(EphyAboutHandler * handler,WebKitURISchemeRequest * request)519 ephy_about_handler_handle_blank (EphyAboutHandler *handler,
520 WebKitURISchemeRequest *request)
521 {
522 ephy_about_handler_finish_request (request, g_strdup ("<html></html>"), -1);
523 }
524
525 EphyAboutHandler *
ephy_about_handler_new(void)526 ephy_about_handler_new (void)
527 {
528 return EPHY_ABOUT_HANDLER (g_object_new (EPHY_TYPE_ABOUT_HANDLER, NULL));
529 }
530
531 void
ephy_about_handler_handle_request(EphyAboutHandler * handler,WebKitURISchemeRequest * request)532 ephy_about_handler_handle_request (EphyAboutHandler *handler,
533 WebKitURISchemeRequest *request)
534 {
535 const char *path;
536 gboolean handled = FALSE;
537
538 path = webkit_uri_scheme_request_get_path (request);
539
540 if (!g_strcmp0 (path, "memory"))
541 handled = ephy_about_handler_handle_memory (handler, request);
542 else if (!g_strcmp0 (path, "epiphany"))
543 handled = ephy_about_handler_handle_epiphany (handler, request);
544 else if (!g_strcmp0 (path, "applications") && !ephy_is_running_inside_flatpak ())
545 handled = ephy_about_handler_handle_applications (handler, request);
546 else if (!g_strcmp0 (path, "overview"))
547 handled = ephy_about_handler_handle_html_overview (handler, request);
548 else if (!g_strcmp0 (path, "incognito"))
549 handled = ephy_about_handler_handle_incognito (handler, request);
550 else if (path == NULL || path[0] == '\0' || !g_strcmp0 (path, "Web") || !g_strcmp0 (path, "web"))
551 handled = ephy_about_handler_handle_about (handler, request);
552
553 if (!handled)
554 ephy_about_handler_handle_blank (handler, request);
555 }
556