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