1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /*
3  *  Copyright © 2000-2003 Marco Pesenti Gritti
4  *  Copyright © 2011 Igalia S.L.
5  *
6  *  This file is part of Epiphany.
7  *
8  *  Epiphany is free software: you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation, either version 3 of the License, or
11  *  (at your option) any later version.
12  *
13  *  Epiphany is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with Epiphany.  If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include <config.h>
23 #include "ephy-embed-shell.h"
24 
25 #include "ephy-about-handler.h"
26 #include "ephy-debug.h"
27 #include "ephy-downloads-manager.h"
28 #include "ephy-embed-container.h"
29 #include "ephy-embed-prefs.h"
30 #include "ephy-embed-type-builtins.h"
31 #include "ephy-embed-utils.h"
32 #include "ephy-encodings.h"
33 #include "ephy-file-helpers.h"
34 #include "ephy-filters-manager.h"
35 #include "ephy-flatpak-utils.h"
36 #include "ephy-history-service.h"
37 #include "ephy-password-manager.h"
38 #include "ephy-pdf-handler.h"
39 #include "ephy-profile-utils.h"
40 #include "ephy-reader-handler.h"
41 #include "ephy-settings.h"
42 #include "ephy-snapshot-service.h"
43 #include "ephy-tabs-catalog.h"
44 #include "ephy-uri-helpers.h"
45 #include "ephy-view-source-handler.h"
46 #include "ephy-web-app-utils.h"
47 
48 #include <glib/gi18n.h>
49 #include <gtk/gtk.h>
50 #include <stdlib.h>
51 
52 #define PAGE_SETUP_FILENAME "page-setup-gtk.ini"
53 #define PRINT_SETTINGS_FILENAME "print-settings.ini"
54 
55 typedef struct {
56   WebKitWebContext *web_context;
57   EphyHistoryService *global_history_service;
58   EphyGSBService *global_gsb_service;
59   EphyEncodings *encodings;
60   GtkPageSetup *page_setup;
61   GtkPrintSettings *print_settings;
62   EphyEmbedShellMode mode;
63   EphyDownloadsManager *downloads_manager;
64   EphyPermissionsManager *permissions_manager;
65   EphyPasswordManager *password_manager;
66   EphyAboutHandler *about_handler;
67   EphyViewSourceHandler *source_handler;
68   EphyReaderHandler *reader_handler;
69   EphyPDFHandler *pdf_handler;
70   char *guid;
71   EphyFiltersManager *filters_manager;
72   EphySearchEngineManager *search_engine_manager;
73   GCancellable *cancellable;
74 } EphyEmbedShellPrivate;
75 
76 enum {
77   RESTORED_WINDOW,
78   WEB_VIEW_CREATED,
79   ALLOW_TLS_CERTIFICATE,
80   ALLOW_UNSAFE_BROWSING,
81   PASSWORD_FORM_FOCUSED,
82 
83   LAST_SIGNAL
84 };
85 
86 static guint signals[LAST_SIGNAL];
87 
88 enum {
89   PROP_0,
90   PROP_MODE,
91   N_PROPERTIES
92 };
93 
94 static GParamSpec *object_properties[N_PROPERTIES] = { NULL, };
95 
96 static EphyEmbedShell *embed_shell = NULL;
97 
98 static void ephy_embed_shell_tabs_catalog_iface_init (EphyTabsCatalogInterface *iface);
99 
G_DEFINE_TYPE_WITH_CODE(EphyEmbedShell,ephy_embed_shell,DZL_TYPE_APPLICATION,G_ADD_PRIVATE (EphyEmbedShell)G_IMPLEMENT_INTERFACE (EPHY_TYPE_TABS_CATALOG,ephy_embed_shell_tabs_catalog_iface_init))100 G_DEFINE_TYPE_WITH_CODE (EphyEmbedShell, ephy_embed_shell, DZL_TYPE_APPLICATION,
101                          G_ADD_PRIVATE (EphyEmbedShell)
102                          G_IMPLEMENT_INTERFACE (EPHY_TYPE_TABS_CATALOG,
103                                                 ephy_embed_shell_tabs_catalog_iface_init))
104 
105 static EphyWebView *
106 ephy_embed_shell_get_view_for_page_id (EphyEmbedShell *self,
107                                        guint64         page_id,
108                                        const char     *origin)
109 {
110   GList *windows = gtk_application_get_windows (GTK_APPLICATION (self));
111 
112   for (GList *l = windows; l && l->data; l = l->next) {
113     g_autoptr (GList) tabs = ephy_embed_container_get_children (l->data);
114 
115     for (GList *t = tabs; t && t->data; t = t->next) {
116       EphyWebView *ephy_view = ephy_embed_get_web_view (t->data);
117       WebKitWebView *web_view = WEBKIT_WEB_VIEW (ephy_view);
118       g_autofree char *real_origin = NULL;
119 
120       if (webkit_web_view_get_page_id (web_view) != page_id)
121         continue;
122 
123       real_origin = ephy_uri_to_security_origin (webkit_web_view_get_uri (web_view));
124 
125       if (g_strcmp0 (real_origin, origin)) {
126         g_debug ("Extension's origin '%s' doesn't match real origin '%s'", origin, real_origin);
127         return NULL;
128       }
129 
130       return ephy_view;
131     }
132   }
133 
134   return NULL;
135 }
136 
137 static GList *
tabs_catalog_get_tabs_info(EphyTabsCatalog * catalog)138 tabs_catalog_get_tabs_info (EphyTabsCatalog *catalog)
139 {
140   WebKitFaviconDatabase *database;
141   GList *windows;
142   g_autoptr (GList) tabs = NULL;
143   GList *tabs_info = NULL;
144   const char *title;
145   const char *url;
146   g_autofree char *favicon = NULL;
147 
148   g_assert ((gpointer)catalog == (gpointer)embed_shell);
149 
150   windows = gtk_application_get_windows (GTK_APPLICATION (embed_shell));
151   database = webkit_web_context_get_favicon_database (ephy_embed_shell_get_web_context (embed_shell));
152 
153   for (GList *l = windows; l && l->data; l = l->next) {
154     tabs = ephy_embed_container_get_children (l->data);
155 
156     for (GList *t = tabs; t && t->data; t = t->next) {
157       title = ephy_embed_get_title (t->data);
158 
159       if (!g_strcmp0 (title, _(BLANK_PAGE_TITLE)) || !g_strcmp0 (title, _(OVERVIEW_PAGE_TITLE)))
160         continue;
161 
162       url = ephy_web_view_get_display_address (ephy_embed_get_web_view (t->data));
163       favicon = webkit_favicon_database_get_favicon_uri (database, url);
164 
165       tabs_info = g_list_prepend (tabs_info,
166                                   ephy_tab_info_new (title, url, favicon));
167     }
168   }
169 
170   return tabs_info;
171 }
172 
173 static void
ephy_embed_shell_tabs_catalog_iface_init(EphyTabsCatalogInterface * iface)174 ephy_embed_shell_tabs_catalog_iface_init (EphyTabsCatalogInterface *iface)
175 {
176   iface->get_tabs_info = tabs_catalog_get_tabs_info;
177 }
178 
179 static void
ephy_embed_shell_dispose(GObject * object)180 ephy_embed_shell_dispose (GObject *object)
181 {
182   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (EPHY_EMBED_SHELL (object));
183 
184   if (priv->cancellable) {
185     g_cancellable_cancel (priv->cancellable);
186     g_clear_object (&priv->cancellable);
187   }
188 
189   g_clear_object (&priv->encodings);
190   g_clear_object (&priv->page_setup);
191   g_clear_object (&priv->print_settings);
192   g_clear_object (&priv->global_history_service);
193   g_clear_object (&priv->global_gsb_service);
194   g_clear_object (&priv->about_handler);
195   g_clear_object (&priv->reader_handler);
196   g_clear_object (&priv->source_handler);
197   g_clear_object (&priv->pdf_handler);
198   g_clear_object (&priv->downloads_manager);
199   g_clear_object (&priv->password_manager);
200   g_clear_object (&priv->permissions_manager);
201   g_clear_object (&priv->web_context);
202   g_clear_pointer (&priv->guid, g_free);
203   g_clear_object (&priv->filters_manager);
204   g_clear_object (&priv->search_engine_manager);
205 
206   G_OBJECT_CLASS (ephy_embed_shell_parent_class)->dispose (object);
207 }
208 
209 static void
web_process_extension_password_form_focused_message_received_cb(WebKitUserContentManager * manager,WebKitJavascriptResult * message,EphyEmbedShell * shell)210 web_process_extension_password_form_focused_message_received_cb (WebKitUserContentManager *manager,
211                                                                  WebKitJavascriptResult   *message,
212                                                                  EphyEmbedShell           *shell)
213 {
214   guint64 page_id;
215   gboolean insecure_form_action;
216   g_autoptr (GVariant) variant = NULL;
217   g_autofree char *message_str = NULL;
218 
219   message_str = jsc_value_to_string (webkit_javascript_result_get_js_value (message));
220   variant = g_variant_parse (G_VARIANT_TYPE ("(tb)"), message_str, NULL, NULL, NULL);
221 
222   g_variant_get (variant, "(tb)", &page_id, &insecure_form_action);
223   g_signal_emit (shell, signals[PASSWORD_FORM_FOCUSED], 0,
224                  page_id, insecure_form_action);
225 }
226 
227 static void
history_service_query_urls_cb(EphyHistoryService * service,gboolean success,GList * urls,EphyEmbedShell * shell)228 history_service_query_urls_cb (EphyHistoryService *service,
229                                gboolean            success,
230                                GList              *urls,
231                                EphyEmbedShell     *shell)
232 {
233   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
234   GList *l;
235   GVariantBuilder builder;
236 
237   if (!success)
238     return;
239 
240   g_variant_builder_init (&builder, G_VARIANT_TYPE ("a(ss)"));
241   for (l = urls; l; l = g_list_next (l)) {
242     EphyHistoryURL *url = (EphyHistoryURL *)l->data;
243 
244     g_variant_builder_add (&builder, "(ss)", url->url, url->title);
245     ephy_embed_shell_schedule_thumbnail_update (shell, (EphyHistoryURL *)l->data);
246   }
247 
248   webkit_web_context_send_message_to_all_extensions (priv->web_context,
249                                                      webkit_user_message_new ("History.SetURLs",
250                                                                               g_variant_builder_end (&builder)));
251 }
252 
253 static void
ephy_embed_shell_update_overview_urls(EphyEmbedShell * shell)254 ephy_embed_shell_update_overview_urls (EphyEmbedShell *shell)
255 {
256   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
257   g_autoptr (EphyHistoryQuery) query = NULL;
258 
259   query = ephy_history_query_new_for_overview ();
260   ephy_history_service_query_urls (priv->global_history_service, query, NULL,
261                                    (EphyHistoryJobCallback)history_service_query_urls_cb,
262                                    shell);
263 }
264 
265 static void
history_service_urls_visited_cb(EphyHistoryService * history,EphyEmbedShell * shell)266 history_service_urls_visited_cb (EphyHistoryService *history,
267                                  EphyEmbedShell     *shell)
268 {
269   ephy_embed_shell_update_overview_urls (shell);
270 }
271 
272 static void
history_set_url_hidden_cb(EphyHistoryService * service,gboolean success,gpointer result_data,EphyEmbedShell * shell)273 history_set_url_hidden_cb (EphyHistoryService *service,
274                            gboolean            success,
275                            gpointer            result_data,
276                            EphyEmbedShell     *shell)
277 {
278   if (!success)
279     return;
280 
281   ephy_embed_shell_update_overview_urls (shell);
282 }
283 
284 static void
web_process_extension_overview_message_received_cb(WebKitUserContentManager * manager,WebKitJavascriptResult * message,EphyEmbedShell * shell)285 web_process_extension_overview_message_received_cb (WebKitUserContentManager *manager,
286                                                     WebKitJavascriptResult   *message,
287                                                     EphyEmbedShell           *shell)
288 {
289   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
290   g_autofree char *url_to_remove = NULL;
291 
292   url_to_remove = jsc_value_to_string (webkit_javascript_result_get_js_value (message));
293 
294   ephy_history_service_set_url_hidden (priv->global_history_service,
295                                        url_to_remove, TRUE, NULL,
296                                        (EphyHistoryJobCallback)history_set_url_hidden_cb,
297                                        shell);
298 }
299 
300 static void
web_process_extension_tls_error_page_message_received_cb(WebKitUserContentManager * manager,WebKitJavascriptResult * message,EphyEmbedShell * shell)301 web_process_extension_tls_error_page_message_received_cb (WebKitUserContentManager *manager,
302                                                           WebKitJavascriptResult   *message,
303                                                           EphyEmbedShell           *shell)
304 {
305   guint64 page_id;
306 
307   page_id = jsc_value_to_double (webkit_javascript_result_get_js_value (message));
308   g_signal_emit (shell, signals[ALLOW_TLS_CERTIFICATE], 0, page_id);
309 }
310 
311 static void
web_process_extension_unsafe_browsing_error_page_message_received_cb(WebKitUserContentManager * manager,WebKitJavascriptResult * message,EphyEmbedShell * shell)312 web_process_extension_unsafe_browsing_error_page_message_received_cb (WebKitUserContentManager *manager,
313                                                                       WebKitJavascriptResult   *message,
314                                                                       EphyEmbedShell           *shell)
315 {
316   guint64 page_id;
317 
318   page_id = jsc_value_to_double (webkit_javascript_result_get_js_value (message));
319   g_signal_emit (shell, signals[ALLOW_UNSAFE_BROWSING], 0, page_id);
320 }
321 
322 static void
web_process_extension_about_apps_message_received_cb(WebKitUserContentManager * manager,WebKitJavascriptResult * message,EphyEmbedShell * shell)323 web_process_extension_about_apps_message_received_cb (WebKitUserContentManager *manager,
324                                                       WebKitJavascriptResult   *message,
325                                                       EphyEmbedShell           *shell)
326 {
327   g_autofree char *app_id = NULL;
328 
329   app_id = jsc_value_to_string (webkit_javascript_result_get_js_value (message));
330   ephy_web_application_delete (app_id);
331 }
332 
333 static char *
property_to_string_or_null(JSCValue * value,const char * name)334 property_to_string_or_null (JSCValue   *value,
335                             const char *name)
336 {
337   g_autoptr (JSCValue) prop = jsc_value_object_get_property (value, name);
338   if (jsc_value_is_null (prop) || jsc_value_is_undefined (prop))
339     return NULL;
340   return jsc_value_to_string (prop);
341 }
342 
343 static int
property_to_uint64(JSCValue * value,const char * name)344 property_to_uint64 (JSCValue   *value,
345                     const char *name)
346 {
347   g_autoptr (JSCValue) prop = jsc_value_object_get_property (value, name);
348   return (guint64)jsc_value_to_double (prop);
349 }
350 
351 typedef struct {
352   EphyPasswordManager *password_manager;
353   EphyPermissionsManager *permissions_manager;
354   char *origin;
355   char *target_origin;
356   char *username;
357   char *password;
358   char *username_field;
359   char *password_field;
360   gboolean is_new;
361 } SaveAuthRequest;
362 
363 static void
save_auth_request_free(SaveAuthRequest * request)364 save_auth_request_free (SaveAuthRequest *request)
365 {
366   g_object_unref (request->password_manager);
367   g_object_unref (request->permissions_manager);
368   g_free (request->origin);
369   g_free (request->target_origin);
370   g_free (request->username);
371   g_free (request->password);
372   g_free (request->username_field);
373   g_free (request->password_field);
374   g_free (request);
375 }
376 
377 static void
save_auth_request_response_cb(gint response_id,SaveAuthRequest * data)378 save_auth_request_response_cb (gint             response_id,
379                                SaveAuthRequest *data)
380 {
381   if (response_id == GTK_RESPONSE_REJECT) {
382     ephy_permissions_manager_set_permission (data->permissions_manager,
383                                              EPHY_PERMISSION_TYPE_SAVE_PASSWORD,
384                                              data->origin,
385                                              EPHY_PERMISSION_DENY);
386   } else if (response_id == GTK_RESPONSE_YES) {
387     ephy_password_manager_save (data->password_manager, data->origin, data->target_origin,
388                                 data->username, data->password, data->username_field,
389                                 data->password_field, data->is_new);
390   }
391 }
392 
393 static void
web_process_extension_password_manager_save_real(EphyEmbedShell * shell,JSCValue * value,gboolean is_request)394 web_process_extension_password_manager_save_real (EphyEmbedShell *shell,
395                                                   JSCValue       *value,
396                                                   gboolean        is_request)
397 {
398   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
399   g_autofree char *origin = property_to_string_or_null (value, "origin");
400   g_autofree char *target_origin = property_to_string_or_null (value, "targetOrigin");
401   g_autofree char *username = property_to_string_or_null (value, "username");
402   g_autofree char *password = property_to_string_or_null (value, "password");
403   g_autofree char *username_field = property_to_string_or_null (value, "usernameField");
404   g_autofree char *password_field = property_to_string_or_null (value, "passwordField");
405   g_autoptr (JSCValue) is_new_prop = jsc_value_object_get_property (value, "isNew");
406   gboolean is_new = jsc_value_to_boolean (is_new_prop);
407   guint64 page_id = property_to_uint64 (value, "pageID");
408   EphyWebView *view;
409   SaveAuthRequest *request;
410 
411   /* Both origin and target origin are required. */
412   if (!origin || !target_origin)
413     return;
414 
415   /* Both password and password field are required. */
416   if (!password || !password_field)
417     return;
418 
419   /* The username field is required if username is present. */
420   if (username && !username_field)
421     g_clear_pointer (&username, g_free);
422 
423   /* The username is required if username field is present. */
424   if (!username && username_field)
425     g_clear_pointer (&username_field, g_free);
426 
427   /* This also sanity checks that a page isn't saving websites for
428    * other origins. Remember the request comes from the untrusted web
429    * process and we have to make sure it's not being evil here. This
430    * could also happen even without malice if the origin of a subframe
431    * doesn't match the origin of the main frame (in which case we'll
432    * refuse to save the password).
433    */
434   view = ephy_embed_shell_get_view_for_page_id (shell, page_id, origin);
435   if (!view)
436     return;
437 
438   if (!is_request) {
439     ephy_password_manager_save (priv->password_manager, origin, target_origin, username,
440                                 password, username_field, password_field, is_new);
441     return;
442   }
443 
444   request = g_new (SaveAuthRequest, 1);
445   request->password_manager = g_object_ref (priv->password_manager);
446   request->permissions_manager = g_object_ref (priv->permissions_manager);
447   request->origin = g_steal_pointer (&origin);
448   request->target_origin = g_steal_pointer (&target_origin);
449   request->username = g_steal_pointer (&username);
450   request->password = g_steal_pointer (&password);
451   request->username_field = g_steal_pointer (&username_field);
452   request->password_field = g_steal_pointer (&password_field);
453   request->is_new = is_new;
454   ephy_web_view_show_auth_form_save_request (view, request->origin, request->username,
455                                              (EphyPasswordSaveRequestCallback)save_auth_request_response_cb,
456                                              request, (GDestroyNotify)save_auth_request_free);
457 }
458 
459 static void
web_process_extension_password_manager_save_received_cb(WebKitUserContentManager * manager,WebKitJavascriptResult * message,EphyEmbedShell * shell)460 web_process_extension_password_manager_save_received_cb (WebKitUserContentManager *manager,
461                                                          WebKitJavascriptResult   *message,
462                                                          EphyEmbedShell           *shell)
463 {
464   JSCValue *value = webkit_javascript_result_get_js_value (message);
465   web_process_extension_password_manager_save_real (shell, value, FALSE);
466 }
467 
468 static void
web_process_extension_password_manager_request_save_received_cb(WebKitUserContentManager * manager,WebKitJavascriptResult * message,EphyEmbedShell * shell)469 web_process_extension_password_manager_request_save_received_cb (WebKitUserContentManager *manager,
470                                                                  WebKitJavascriptResult   *message,
471                                                                  EphyEmbedShell           *shell)
472 {
473   JSCValue *value = webkit_javascript_result_get_js_value (message);
474   web_process_extension_password_manager_save_real (shell, value, TRUE);
475 }
476 
477 static void
history_service_url_title_changed_cb(EphyHistoryService * service,const char * url,const char * title,EphyEmbedShell * shell)478 history_service_url_title_changed_cb (EphyHistoryService *service,
479                                       const char         *url,
480                                       const char         *title,
481                                       EphyEmbedShell     *shell)
482 {
483   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
484 
485   webkit_web_context_send_message_to_all_extensions (priv->web_context,
486                                                      webkit_user_message_new ("History.SetURLTitle",
487                                                                               g_variant_new ("(ss)", url, title)));
488 }
489 
490 static void
history_service_url_deleted_cb(EphyHistoryService * service,EphyHistoryURL * url,EphyEmbedShell * shell)491 history_service_url_deleted_cb (EphyHistoryService *service,
492                                 EphyHistoryURL     *url,
493                                 EphyEmbedShell     *shell)
494 {
495   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
496 
497   webkit_web_context_send_message_to_all_extensions (priv->web_context,
498                                                      webkit_user_message_new ("History.DeleteURL",
499                                                                               g_variant_new ("s", url->url)));
500 }
501 
502 static void
history_service_host_deleted_cb(EphyHistoryService * service,const char * deleted_url,EphyEmbedShell * shell)503 history_service_host_deleted_cb (EphyHistoryService *service,
504                                  const char         *deleted_url,
505                                  EphyEmbedShell     *shell)
506 {
507   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
508   g_autoptr (GUri) deleted_uri = NULL;
509 
510   deleted_uri = g_uri_parse (deleted_url, G_URI_FLAGS_NONE, NULL);
511   webkit_web_context_send_message_to_all_extensions (priv->web_context,
512                                                      webkit_user_message_new ("History.DeleteHost",
513                                                                               g_variant_new ("s", g_uri_get_host (deleted_uri))));
514 }
515 
516 static void
history_service_cleared_cb(EphyHistoryService * service,EphyEmbedShell * shell)517 history_service_cleared_cb (EphyHistoryService *service,
518                             EphyEmbedShell     *shell)
519 {
520   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
521 
522   webkit_web_context_send_message_to_all_extensions (priv->web_context,
523                                                      webkit_user_message_new ("History.Clear",
524                                                                               NULL));
525 }
526 
527 void
ephy_embed_shell_set_thumbnail_path(EphyEmbedShell * shell,const char * url,const char * path)528 ephy_embed_shell_set_thumbnail_path (EphyEmbedShell *shell,
529                                      const char     *url,
530                                      const char     *path)
531 {
532   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
533 
534   webkit_web_context_send_message_to_all_extensions (priv->web_context,
535                                                      webkit_user_message_new ("History.SetURLThumbnail",
536                                                                               g_variant_new ("(ss)", url, path)));
537 }
538 
539 static void
got_snapshot_path_for_url_cb(EphySnapshotService * service,GAsyncResult * result,char * url)540 got_snapshot_path_for_url_cb (EphySnapshotService *service,
541                               GAsyncResult        *result,
542                               char                *url)
543 {
544   g_autofree char *snapshot = NULL;
545   g_autoptr (GError) error = NULL;
546 
547   snapshot = ephy_snapshot_service_get_snapshot_path_for_url_finish (service, result, &error);
548   if (snapshot) {
549     ephy_embed_shell_set_thumbnail_path (ephy_embed_shell_get_default (), url, snapshot);
550   } else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
551     /* Bad luck, not something to warn about. */
552     g_info ("Failed to get snapshot for URL %s: %s", url, error->message);
553   }
554 
555   g_free (url);
556 }
557 
558 void
ephy_embed_shell_schedule_thumbnail_update(EphyEmbedShell * shell,EphyHistoryURL * url)559 ephy_embed_shell_schedule_thumbnail_update (EphyEmbedShell *shell,
560                                             EphyHistoryURL *url)
561 {
562   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
563   EphySnapshotService *service;
564   const char *snapshot;
565 
566   service = ephy_snapshot_service_get_default ();
567   snapshot = ephy_snapshot_service_lookup_cached_snapshot_path (service, url->url);
568 
569   if (snapshot) {
570     ephy_embed_shell_set_thumbnail_path (shell, url->url, snapshot);
571   } else {
572     ephy_snapshot_service_get_snapshot_path_for_url_async (service,
573                                                            url->url,
574                                                            priv->cancellable,
575                                                            (GAsyncReadyCallback)got_snapshot_path_for_url_cb,
576                                                            g_strdup (url->url));
577   }
578 }
579 
580 /**
581  * ephy_embed_shell_get_global_history_service:
582  * @shell: the #EphyEmbedShell
583  *
584  * Return value: (transfer none): the global #EphyHistoryService
585  **/
586 EphyHistoryService *
ephy_embed_shell_get_global_history_service(EphyEmbedShell * shell)587 ephy_embed_shell_get_global_history_service (EphyEmbedShell *shell)
588 {
589   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
590 
591   g_assert (EPHY_IS_EMBED_SHELL (shell));
592 
593   if (!priv->global_history_service) {
594     g_autofree char *filename = NULL;
595     EphySQLiteConnectionMode mode;
596 
597     if (priv->mode == EPHY_EMBED_SHELL_MODE_INCOGNITO ||
598         priv->mode == EPHY_EMBED_SHELL_MODE_AUTOMATION ||
599         priv->mode == EPHY_EMBED_SHELL_MODE_SEARCH_PROVIDER)
600       mode = EPHY_SQLITE_CONNECTION_MODE_MEMORY;
601     else
602       mode = EPHY_SQLITE_CONNECTION_MODE_READWRITE;
603 
604     filename = g_build_filename (ephy_profile_dir (), EPHY_HISTORY_FILE, NULL);
605     priv->global_history_service = ephy_history_service_new (filename, mode);
606 
607     g_signal_connect_object (priv->global_history_service, "urls-visited",
608                              G_CALLBACK (history_service_urls_visited_cb),
609                              shell, 0);
610     g_signal_connect_object (priv->global_history_service, "url-title-changed",
611                              G_CALLBACK (history_service_url_title_changed_cb),
612                              shell, 0);
613     g_signal_connect_object (priv->global_history_service, "url-deleted",
614                              G_CALLBACK (history_service_url_deleted_cb),
615                              shell, 0);
616     g_signal_connect_object (priv->global_history_service, "host-deleted",
617                              G_CALLBACK (history_service_host_deleted_cb),
618                              shell, 0);
619     g_signal_connect_object (priv->global_history_service, "cleared",
620                              G_CALLBACK (history_service_cleared_cb),
621                              shell, 0);
622   }
623 
624   return priv->global_history_service;
625 }
626 
627 /**
628  * ephy_embed_shell_get_global_gsb_service:
629  * @shell: the #EphyEmbedShell
630  *
631  * Return value: (transfer none): the global #EphyGSBService
632  **/
633 EphyGSBService *
ephy_embed_shell_get_global_gsb_service(EphyEmbedShell * shell)634 ephy_embed_shell_get_global_gsb_service (EphyEmbedShell *shell)
635 {
636   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
637 
638   g_assert (EPHY_IS_EMBED_SHELL (shell));
639 
640   if (!priv->global_gsb_service) {
641     g_autofree char *db_path = NULL;
642     g_autofree char *default_cache_dir = ephy_default_cache_dir ();
643 
644     db_path = g_build_filename (default_cache_dir, EPHY_GSB_FILE, NULL);
645     priv->global_gsb_service = ephy_gsb_service_new (GSB_API_KEY, db_path);
646   }
647 
648   return priv->global_gsb_service;
649 }
650 
651 /**
652  * ephy_embed_shell_get_encodings:
653  * @shell: the #EphyEmbedShell
654  *
655  * Return value: (transfer none):
656  **/
657 EphyEncodings *
ephy_embed_shell_get_encodings(EphyEmbedShell * shell)658 ephy_embed_shell_get_encodings (EphyEmbedShell *shell)
659 {
660   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
661 
662   g_assert (EPHY_IS_EMBED_SHELL (shell));
663 
664   if (!priv->encodings)
665     priv->encodings = ephy_encodings_new ();
666 
667   return priv->encodings;
668 }
669 
670 void
ephy_embed_shell_restored_window(EphyEmbedShell * shell)671 ephy_embed_shell_restored_window (EphyEmbedShell *shell)
672 {
673   g_signal_emit (shell, signals[RESTORED_WINDOW], 0);
674 }
675 
676 static void
about_request_cb(WebKitURISchemeRequest * request,EphyEmbedShell * shell)677 about_request_cb (WebKitURISchemeRequest *request,
678                   EphyEmbedShell         *shell)
679 {
680   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
681 
682   ephy_about_handler_handle_request (priv->about_handler, request);
683 }
684 
685 static void
source_request_cb(WebKitURISchemeRequest * request,EphyEmbedShell * shell)686 source_request_cb (WebKitURISchemeRequest *request,
687                    EphyEmbedShell         *shell)
688 {
689   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
690 
691   ephy_view_source_handler_handle_request (priv->source_handler, request);
692 }
693 
694 static void
reader_request_cb(WebKitURISchemeRequest * request,EphyEmbedShell * shell)695 reader_request_cb (WebKitURISchemeRequest *request,
696                    EphyEmbedShell         *shell)
697 {
698   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
699 
700   ephy_reader_handler_handle_request (priv->reader_handler, request);
701 }
702 
703 static void
pdf_request_cb(WebKitURISchemeRequest * request,EphyEmbedShell * shell)704 pdf_request_cb (WebKitURISchemeRequest *request,
705                 EphyEmbedShell         *shell)
706 {
707   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
708 
709   ephy_pdf_handler_handle_request (priv->pdf_handler, request);
710 }
711 
712 static void
ephy_resource_request_cb(WebKitURISchemeRequest * request)713 ephy_resource_request_cb (WebKitURISchemeRequest *request)
714 {
715   const char *path;
716   gsize size;
717   WebKitWebView *request_view;
718   const char *uri;
719   g_autoptr (GInputStream) stream = NULL;
720   g_autoptr (GError) error = NULL;
721 
722   path = webkit_uri_scheme_request_get_path (request);
723   if (!g_resources_get_info (path, 0, &size, NULL, &error)) {
724     webkit_uri_scheme_request_finish_error (request, error);
725     return;
726   }
727 
728   request_view = webkit_uri_scheme_request_get_web_view (request);
729   uri = webkit_web_view_get_uri (request_view);
730 
731   /* ephy-resource:// requests bypass CORS in order to allow ephy-pdf:// to
732    * access ephy-resource://. Accordingly, we need some custom security to
733    * prevent websites from directly accessing ephy-resource://.
734    *
735    * We'll have to leave open /page-icons and /page-templates since they are
736    * needed for our alternate HTML error pages.
737    */
738   if (g_str_has_prefix (uri, "ephy-resource:") ||
739       g_str_has_prefix (path, "/org/gnome/epiphany/page-icons/") ||
740       g_str_has_prefix (path, "/org/gnome/epiphany/page-templates/") ||
741       (g_str_has_prefix (uri, "ephy-pdf:") && g_str_has_prefix (path, "/org/gnome/epiphany/pdfjs/")) ||
742       (g_str_has_prefix (uri, "ephy-reader:") && g_str_has_prefix (path, "/org/gnome/epiphany/readability/")) ||
743       (g_str_has_prefix (uri, "ephy-source:") && g_str_has_prefix (path, "/org/gnome/epiphany/highlightjs/"))) {
744     stream = g_resources_open_stream (path, 0, &error);
745     if (stream)
746       webkit_uri_scheme_request_finish (request, stream, size, NULL);
747     else
748       webkit_uri_scheme_request_finish_error (request, error);
749     return;
750   }
751 
752   error = g_error_new (WEBKIT_NETWORK_ERROR, WEBKIT_NETWORK_ERROR_FAILED,
753                        _("URI %s not authorized to access Epiphany resource %s"),
754                        uri, path);
755   webkit_uri_scheme_request_finish_error (request, error);
756 }
757 
758 static void
initialize_web_process_extensions(WebKitWebContext * web_context,EphyEmbedShell * shell)759 initialize_web_process_extensions (WebKitWebContext *web_context,
760                                    EphyEmbedShell   *shell)
761 {
762   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
763   g_autoptr (GVariant) user_data = NULL;
764   gboolean private_profile;
765 
766 #if DEVELOPER_MODE
767   webkit_web_context_set_web_extensions_directory (web_context, BUILD_ROOT "/embed/web-process-extension");
768 #else
769   webkit_web_context_set_web_extensions_directory (web_context, EPHY_WEB_PROCESS_EXTENSIONS_DIR);
770 #endif
771 
772   private_profile = priv->mode == EPHY_EMBED_SHELL_MODE_PRIVATE || priv->mode == EPHY_EMBED_SHELL_MODE_INCOGNITO || priv->mode == EPHY_EMBED_SHELL_MODE_AUTOMATION;
773   user_data = g_variant_new ("(smsbb)",
774                              priv->guid,
775                              ephy_profile_dir_is_default () ? NULL : ephy_profile_dir (),
776                              g_settings_get_boolean (EPHY_SETTINGS_WEB, EPHY_PREFS_WEB_REMEMBER_PASSWORDS),
777                              private_profile);
778   webkit_web_context_set_web_extensions_initialization_user_data (web_context, g_steal_pointer (&user_data));
779 }
780 
781 static void
initialize_notification_permissions(WebKitWebContext * web_context,EphyEmbedShell * shell)782 initialize_notification_permissions (WebKitWebContext *web_context,
783                                      EphyEmbedShell   *shell)
784 {
785   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
786   GList *permitted_origins;
787   GList *denied_origins;
788 
789   permitted_origins = ephy_permissions_manager_get_permitted_origins (priv->permissions_manager,
790                                                                       EPHY_PERMISSION_TYPE_SHOW_NOTIFICATIONS);
791   denied_origins = ephy_permissions_manager_get_denied_origins (priv->permissions_manager,
792                                                                 EPHY_PERMISSION_TYPE_SHOW_NOTIFICATIONS);
793   webkit_web_context_initialize_notification_permissions (web_context, permitted_origins, denied_origins);
794 }
795 
796 static void
ephy_embed_shell_create_web_context(EphyEmbedShell * shell)797 ephy_embed_shell_create_web_context (EphyEmbedShell *shell)
798 {
799   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
800   g_autoptr (WebKitWebsiteDataManager) manager = NULL;
801 
802   if (priv->mode == EPHY_EMBED_SHELL_MODE_INCOGNITO || priv->mode == EPHY_EMBED_SHELL_MODE_AUTOMATION) {
803     manager = webkit_website_data_manager_new_ephemeral ();
804   } else {
805     manager = webkit_website_data_manager_new ("base-data-directory", ephy_profile_dir (),
806                                                "base-cache-directory", ephy_cache_dir (),
807                                                NULL);
808     webkit_website_data_manager_set_persistent_credential_storage_enabled (manager, FALSE);
809   }
810 
811   webkit_website_data_manager_set_itp_enabled (manager,
812                                                g_settings_get_boolean (EPHY_SETTINGS_WEB,
813                                                                        EPHY_PREFS_WEB_ENABLE_ITP));
814 
815   priv->web_context = g_object_new (WEBKIT_TYPE_WEB_CONTEXT,
816                                     "website-data-manager", manager,
817                                     "process-swap-on-cross-site-navigation-enabled", TRUE,
818                                     NULL);
819 
820   if (priv->mode == EPHY_EMBED_SHELL_MODE_AUTOMATION)
821     webkit_web_context_set_automation_allowed (priv->web_context, TRUE);
822 }
823 
824 static void
download_started_cb(WebKitWebContext * web_context,WebKitDownload * download,EphyEmbedShell * shell)825 download_started_cb (WebKitWebContext *web_context,
826                      WebKitDownload   *download,
827                      EphyEmbedShell   *shell)
828 {
829   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
830   g_autoptr (EphyDownload) ephy_download = NULL;
831   gboolean ephy_download_set;
832   WebKitWebView *web_view;
833 
834   /* Is download locked down? */
835   if (g_settings_get_boolean (EPHY_SETTINGS_LOCKDOWN,
836                               EPHY_PREFS_LOCKDOWN_SAVE_TO_DISK)) {
837     webkit_download_cancel (download);
838     return;
839   }
840 
841   /* Only create an EphyDownload for the WebKitDownload if it doesn't exist yet.
842    * This can happen when the download has been started automatically by WebKit,
843    * due to a context menu action or policy checker decision. Downloads started
844    * explicitly by Epiphany are marked with ephy-download-set GObject data.
845    */
846   ephy_download_set = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (download), "ephy-download-set"));
847   if (ephy_download_set)
848     return;
849 
850   ephy_download = ephy_download_new (download);
851 
852   web_view = webkit_download_get_web_view (download);
853   if (EPHY_IS_WEB_VIEW (web_view)) {
854     if (ephy_web_view_get_document_type (EPHY_WEB_VIEW (web_view)) != EPHY_WEB_VIEW_DOCUMENT_PDF)
855       ephy_downloads_manager_add_download (priv->downloads_manager, ephy_download);
856   } else {
857     ephy_downloads_manager_add_download (priv->downloads_manager, ephy_download);
858   }
859 }
860 
861 static void
remember_passwords_setting_changed_cb(GSettings * settings,char * key,EphyEmbedShell * shell)862 remember_passwords_setting_changed_cb (GSettings      *settings,
863                                        char           *key,
864                                        EphyEmbedShell *shell)
865 {
866   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
867   gboolean should_remember_passwords;
868 
869   should_remember_passwords = g_settings_get_boolean (EPHY_SETTINGS_WEB, EPHY_PREFS_WEB_REMEMBER_PASSWORDS);
870 
871   webkit_web_context_send_message_to_all_extensions (priv->web_context,
872                                                      webkit_user_message_new ("PasswordManager.SetShouldRememberPasswords",
873                                                                               g_variant_new ("b", should_remember_passwords)));
874 }
875 
876 static void
enable_itp_setting_changed_cb(GSettings * settings,char * key,EphyEmbedShell * shell)877 enable_itp_setting_changed_cb (GSettings      *settings,
878                                char           *key,
879                                EphyEmbedShell *shell)
880 {
881   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
882   WebKitWebsiteDataManager *manager;
883 
884   manager = webkit_web_context_get_website_data_manager (priv->web_context);
885   webkit_website_data_manager_set_itp_enabled (manager,
886                                                g_settings_get_boolean (EPHY_SETTINGS_WEB,
887                                                                        EPHY_PREFS_WEB_ENABLE_ITP));
888 }
889 
890 static void
update_system_scrollbars(EphyEmbedShell * shell)891 update_system_scrollbars (EphyEmbedShell *shell)
892 {
893   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
894   const char *theme_name;
895   gboolean enabled;
896 
897   g_object_get (gtk_settings_get_default (),
898                 "gtk-theme_name", &theme_name,
899                 NULL);
900 
901   /* Don't enable system scrollbars for Adwaita */
902   enabled = g_strcmp0 (theme_name, "Adwaita") &&
903             g_strcmp0 (theme_name, "Adwaita-dark") &&
904             g_strcmp0 (theme_name, "HighContrast") &&
905             g_strcmp0 (theme_name, "HighContrastInverse");
906 
907   webkit_web_context_set_use_system_appearance_for_scrollbars (priv->web_context,
908                                                                enabled);
909 }
910 
911 static void
ephy_embed_shell_startup(GApplication * application)912 ephy_embed_shell_startup (GApplication *application)
913 {
914   EphyEmbedShell *shell = EPHY_EMBED_SHELL (application);
915   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
916   g_autofree char *favicon_db_path = NULL;
917   WebKitCookieManager *cookie_manager;
918   g_autofree char *filename = NULL;
919 
920   G_APPLICATION_CLASS (ephy_embed_shell_parent_class)->startup (application);
921 
922   webkit_web_context_set_process_model (priv->web_context, WEBKIT_PROCESS_MODEL_MULTIPLE_SECONDARY_PROCESSES);
923 
924   webkit_web_context_set_sandbox_enabled (priv->web_context, TRUE);
925   webkit_web_context_add_path_to_sandbox (priv->web_context, ephy_profile_dir (), TRUE);
926   webkit_web_context_add_path_to_sandbox (priv->web_context, ephy_cache_dir (), TRUE);
927   webkit_web_context_add_path_to_sandbox (priv->web_context, ephy_config_dir (), TRUE);
928 
929 #if DEVELOPER_MODE
930   webkit_web_context_add_path_to_sandbox (priv->web_context, BUILD_ROOT, TRUE);
931 #endif
932 
933   g_signal_connect_object (priv->web_context, "initialize-web-extensions",
934                            G_CALLBACK (initialize_web_process_extensions),
935                            shell, 0);
936 
937   g_signal_connect_object (priv->web_context, "initialize-notification-permissions",
938                            G_CALLBACK (initialize_notification_permissions),
939                            shell, 0);
940 
941   priv->password_manager = ephy_password_manager_new ();
942 
943   /* Do not cache favicons in automation mode. Don't change the TLS policy either, since that's
944    * handled by session capabilities in automation mode.
945    */
946   if (priv->mode != EPHY_EMBED_SHELL_MODE_AUTOMATION) {
947     /* Favicon Database */
948     favicon_db_path = g_build_filename (ephy_cache_dir (), "icondatabase", NULL);
949     webkit_web_context_set_favicon_database_directory (priv->web_context, favicon_db_path);
950   }
951 
952   /* about: URIs handler */
953   priv->about_handler = ephy_about_handler_new ();
954   webkit_web_context_register_uri_scheme (priv->web_context,
955                                           EPHY_ABOUT_SCHEME,
956                                           (WebKitURISchemeRequestCallback)about_request_cb,
957                                           shell, NULL);
958 
959   /* Register about scheme as local so that it can contain file resources */
960   webkit_security_manager_register_uri_scheme_as_local (webkit_web_context_get_security_manager (priv->web_context),
961                                                         EPHY_ABOUT_SCHEME);
962 
963   /* view source handler */
964   priv->source_handler = ephy_view_source_handler_new ();
965   webkit_web_context_register_uri_scheme (priv->web_context, EPHY_VIEW_SOURCE_SCHEME,
966                                           (WebKitURISchemeRequestCallback)source_request_cb,
967                                           shell, NULL);
968   /* pdf handler */
969   priv->pdf_handler = ephy_pdf_handler_new ();
970   webkit_web_context_register_uri_scheme (priv->web_context, EPHY_PDF_SCHEME,
971                                           (WebKitURISchemeRequestCallback)pdf_request_cb,
972                                           shell, NULL);
973 
974   /* reader mode handler */
975   priv->reader_handler = ephy_reader_handler_new ();
976   webkit_web_context_register_uri_scheme (priv->web_context, EPHY_READER_SCHEME,
977                                           (WebKitURISchemeRequestCallback)reader_request_cb,
978                                           shell, NULL);
979 
980   /* ephy-resource handler */
981   webkit_web_context_register_uri_scheme (priv->web_context, "ephy-resource",
982                                           (WebKitURISchemeRequestCallback)ephy_resource_request_cb,
983                                           NULL, NULL);
984   webkit_security_manager_register_uri_scheme_as_secure (webkit_web_context_get_security_manager (priv->web_context),
985                                                          "ephy-resource");
986 
987   /* Store cookies in moz-compatible SQLite format */
988   cookie_manager = webkit_web_context_get_cookie_manager (priv->web_context);
989   if (!webkit_web_context_is_ephemeral (priv->web_context)) {
990     filename = g_build_filename (ephy_profile_dir (), "cookies.sqlite", NULL);
991     webkit_cookie_manager_set_persistent_storage (cookie_manager, filename,
992                                                   WEBKIT_COOKIE_PERSISTENT_STORAGE_SQLITE);
993   }
994 
995   g_signal_connect_object (priv->web_context, "download-started",
996                            G_CALLBACK (download_started_cb), shell, 0);
997 
998   g_signal_connect_object (EPHY_SETTINGS_WEB, "changed::remember-passwords",
999                            G_CALLBACK (remember_passwords_setting_changed_cb), shell, 0);
1000 
1001   g_signal_connect_object (EPHY_SETTINGS_WEB, "changed::enable-itp",
1002                            G_CALLBACK (enable_itp_setting_changed_cb), shell, 0);
1003 
1004   update_system_scrollbars (shell);
1005 
1006   g_signal_connect_swapped (gtk_settings_get_default (),
1007                             "notify::gtk-theme-name",
1008                             G_CALLBACK (update_system_scrollbars),
1009                             shell);
1010 }
1011 
1012 static void
ephy_embed_shell_shutdown(GApplication * application)1013 ephy_embed_shell_shutdown (GApplication *application)
1014 {
1015   G_APPLICATION_CLASS (ephy_embed_shell_parent_class)->shutdown (application);
1016 
1017   g_object_unref (ephy_embed_prefs_get_settings ());
1018   ephy_embed_utils_shutdown ();
1019 }
1020 
1021 static void
ephy_embed_shell_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)1022 ephy_embed_shell_set_property (GObject      *object,
1023                                guint         prop_id,
1024                                const GValue *value,
1025                                GParamSpec   *pspec)
1026 {
1027   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (EPHY_EMBED_SHELL (object));
1028 
1029   switch (prop_id) {
1030     case PROP_MODE:
1031       priv->mode = g_value_get_enum (value);
1032       break;
1033     default:
1034       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1035   }
1036 }
1037 
1038 static void
ephy_embed_shell_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)1039 ephy_embed_shell_get_property (GObject    *object,
1040                                guint       prop_id,
1041                                GValue     *value,
1042                                GParamSpec *pspec)
1043 {
1044   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (EPHY_EMBED_SHELL (object));
1045 
1046   switch (prop_id) {
1047     case PROP_MODE:
1048       g_value_set_enum (value, priv->mode);
1049       break;
1050     default:
1051       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1052   }
1053 }
1054 
1055 static void
ephy_embed_shell_constructed(GObject * object)1056 ephy_embed_shell_constructed (GObject *object)
1057 {
1058   EphyEmbedShell *shell;
1059   EphyEmbedShellPrivate *priv;
1060 
1061   G_OBJECT_CLASS (ephy_embed_shell_parent_class)->constructed (object);
1062 
1063   shell = EPHY_EMBED_SHELL (object);
1064   priv = ephy_embed_shell_get_instance_private (shell);
1065   priv->guid = g_dbus_generate_guid ();
1066 
1067   ephy_embed_shell_create_web_context (shell);
1068 
1069   priv->permissions_manager = ephy_permissions_manager_new ();
1070   priv->filters_manager = ephy_filters_manager_new (NULL);
1071 }
1072 
1073 static void
ephy_embed_shell_init(EphyEmbedShell * shell)1074 ephy_embed_shell_init (EphyEmbedShell *shell)
1075 {
1076   /* globally accessible singleton */
1077   g_assert (!embed_shell);
1078   embed_shell = shell;
1079 }
1080 
1081 static void
ephy_embed_shell_class_init(EphyEmbedShellClass * klass)1082 ephy_embed_shell_class_init (EphyEmbedShellClass *klass)
1083 {
1084   GObjectClass *object_class = G_OBJECT_CLASS (klass);
1085   GApplicationClass *application_class = G_APPLICATION_CLASS (klass);
1086 
1087   object_class->dispose = ephy_embed_shell_dispose;
1088   object_class->set_property = ephy_embed_shell_set_property;
1089   object_class->get_property = ephy_embed_shell_get_property;
1090   object_class->constructed = ephy_embed_shell_constructed;
1091 
1092   application_class->startup = ephy_embed_shell_startup;
1093   application_class->shutdown = ephy_embed_shell_shutdown;
1094 
1095   object_properties[PROP_MODE] =
1096     g_param_spec_enum ("mode",
1097                        "Mode",
1098                        "The  global mode for this instance.",
1099                        EPHY_TYPE_EMBED_SHELL_MODE,
1100                        EPHY_EMBED_SHELL_MODE_BROWSER,
1101                        G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
1102 
1103   g_object_class_install_properties (object_class,
1104                                      N_PROPERTIES,
1105                                      object_properties);
1106 
1107 /**
1108  * EphyEmbedShell::finished-restoring-window:
1109  * @shell: the #EphyEmbedShell
1110  *
1111  * The ::finished-restoring-window signal is emitted when the
1112  * session finishes restoring a window.
1113  **/
1114   signals[RESTORED_WINDOW] =
1115     g_signal_new ("window-restored",
1116                   EPHY_TYPE_EMBED_SHELL,
1117                   G_SIGNAL_RUN_FIRST,
1118                   G_STRUCT_OFFSET (EphyEmbedShellClass, restored_window),
1119                   NULL, NULL, NULL,
1120                   G_TYPE_NONE,
1121                   0);
1122 
1123   /**
1124    * EphyEmbedShell::web-view-created:
1125    * @shell: the #EphyEmbedShell
1126    * @view: the newly created #EphyWebView
1127    *
1128    * The ::web-view-created signal will be emitted every time a new
1129    * #EphyWebView is created.
1130    *
1131    **/
1132   signals[WEB_VIEW_CREATED] =
1133     g_signal_new ("web-view-created",
1134                   EPHY_TYPE_EMBED_SHELL,
1135                   G_SIGNAL_RUN_LAST,
1136                   0, NULL, NULL, NULL,
1137                   G_TYPE_NONE, 1,
1138                   EPHY_TYPE_WEB_VIEW);
1139 
1140   /**
1141    * EphyEmbedShell::allow-tls-certificate:
1142    * @shell: the #EphyEmbedShell
1143    * @page_id: the identifier of the web page
1144    *
1145    * Emitted when the web process extension requests an exception be
1146    * permitted for the invalid TLS certificate on the given page
1147    */
1148   signals[ALLOW_TLS_CERTIFICATE] =
1149     g_signal_new ("allow-tls-certificate",
1150                   EPHY_TYPE_EMBED_SHELL,
1151                   G_SIGNAL_RUN_FIRST,
1152                   0, NULL, NULL, NULL,
1153                   G_TYPE_NONE, 1,
1154                   G_TYPE_UINT64);
1155 
1156   /**
1157    * EphyEmbedShell::allow-unsafe-browsing:
1158    * @shell: the #EphyEmbedShell
1159    * @page_id: the identifier of the web page
1160    *
1161    * Emitted when the web process extension requests an exception be
1162    * permitted for the unsafe browsing warning on the given page
1163    */
1164   signals[ALLOW_UNSAFE_BROWSING] =
1165     g_signal_new ("allow-unsafe-browsing",
1166                   EPHY_TYPE_EMBED_SHELL,
1167                   G_SIGNAL_RUN_FIRST,
1168                   0, NULL, NULL, NULL,
1169                   G_TYPE_NONE, 1,
1170                   G_TYPE_UINT64);
1171 
1172   /**
1173    * EphyEmbedShell::password-form-focused
1174    * @shell: the #EphyEmbedShell
1175    * @page_id: the identifier of the web page
1176    * @insecure_action: whether the target of the form is http://
1177    *
1178    * Emitted when a form in a web page gains focus.
1179    */
1180   signals[PASSWORD_FORM_FOCUSED] =
1181     g_signal_new ("password-form-focused",
1182                   EPHY_TYPE_EMBED_SHELL,
1183                   G_SIGNAL_RUN_FIRST,
1184                   0, NULL, NULL, NULL,
1185                   G_TYPE_NONE, 2,
1186                   G_TYPE_UINT64,
1187                   G_TYPE_BOOLEAN);
1188 }
1189 
1190 /**
1191  * ephy_embed_shell_get_default:
1192  *
1193  * Retrieves the default #EphyEmbedShell object
1194  *
1195  * Return value: (transfer none): the default #EphyEmbedShell
1196  **/
1197 EphyEmbedShell *
ephy_embed_shell_get_default(void)1198 ephy_embed_shell_get_default (void)
1199 {
1200   return embed_shell;
1201 }
1202 
1203 void
ephy_embed_shell_set_page_setup(EphyEmbedShell * shell,GtkPageSetup * page_setup)1204 ephy_embed_shell_set_page_setup (EphyEmbedShell *shell,
1205                                  GtkPageSetup   *page_setup)
1206 {
1207   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
1208   g_autofree char *path = NULL;
1209 
1210   g_assert (EPHY_IS_EMBED_SHELL (shell));
1211 
1212   if (page_setup)
1213     g_object_ref (page_setup);
1214   else
1215     page_setup = gtk_page_setup_new ();
1216 
1217   if (priv->page_setup)
1218     g_object_unref (priv->page_setup);
1219 
1220   priv->page_setup = page_setup;
1221 
1222   path = g_build_filename (ephy_profile_dir (), PAGE_SETUP_FILENAME, NULL);
1223   gtk_page_setup_to_file (page_setup, path, NULL);
1224 }
1225 
1226 /**
1227  * ephy_embed_shell_get_page_setup:
1228  *
1229  * Return value: (transfer none):
1230  **/
1231 GtkPageSetup *
ephy_embed_shell_get_page_setup(EphyEmbedShell * shell)1232 ephy_embed_shell_get_page_setup (EphyEmbedShell *shell)
1233 {
1234   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
1235 
1236   g_assert (EPHY_IS_EMBED_SHELL (shell));
1237 
1238   if (!priv->page_setup) {
1239     g_autofree char *path = NULL;
1240 
1241     path = g_build_filename (ephy_profile_dir (), PAGE_SETUP_FILENAME, NULL);
1242     priv->page_setup = gtk_page_setup_new_from_file (path, NULL);
1243 
1244     /* If that still didn't work, create a new, empty one */
1245     if (!priv->page_setup)
1246       priv->page_setup = gtk_page_setup_new ();
1247   }
1248 
1249   return priv->page_setup;
1250 }
1251 
1252 /**
1253  * ephy_embed_shell_set_print_settings:
1254  * @shell: the #EphyEmbedShell
1255  * @settings: the new #GtkPrintSettings object
1256  *
1257  * Sets the global #GtkPrintSettings object.
1258  *
1259  **/
1260 void
ephy_embed_shell_set_print_settings(EphyEmbedShell * shell,GtkPrintSettings * settings)1261 ephy_embed_shell_set_print_settings (EphyEmbedShell   *shell,
1262                                      GtkPrintSettings *settings)
1263 {
1264   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
1265   g_autofree char *path = NULL;
1266 
1267   g_assert (EPHY_IS_EMBED_SHELL (shell));
1268 
1269   if (settings)
1270     g_object_ref (settings);
1271 
1272   if (priv->print_settings)
1273     g_object_unref (priv->print_settings);
1274 
1275   priv->print_settings = settings ? settings : gtk_print_settings_new ();
1276 
1277   path = g_build_filename (ephy_profile_dir (), PRINT_SETTINGS_FILENAME, NULL);
1278   gtk_print_settings_to_file (settings, path, NULL);
1279 }
1280 
1281 /**
1282  * ephy_embed_shell_get_print_settings:
1283  * @shell: the #EphyEmbedShell
1284  *
1285  * Gets the global #GtkPrintSettings object.
1286  *
1287  * Returns: (transfer none): a #GtkPrintSettings object
1288  **/
1289 GtkPrintSettings *
ephy_embed_shell_get_print_settings(EphyEmbedShell * shell)1290 ephy_embed_shell_get_print_settings (EphyEmbedShell *shell)
1291 {
1292   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
1293 
1294   g_assert (EPHY_IS_EMBED_SHELL (shell));
1295 
1296   if (!priv->print_settings) {
1297     g_autofree char *path = NULL;
1298 
1299     path = g_build_filename (ephy_profile_dir (), PRINT_SETTINGS_FILENAME, NULL);
1300     priv->print_settings = gtk_print_settings_new_from_file (path, NULL);
1301 
1302     /* Note: the gtk print settings file format is the same as our
1303      * legacy one, so no need to migrate here.
1304      */
1305 
1306     if (!priv->print_settings)
1307       priv->print_settings = gtk_print_settings_new ();
1308   }
1309 
1310   return priv->print_settings;
1311 }
1312 
1313 /**
1314  * ephy_embed_shell_get_mode:
1315  * @shell: an #EphyEmbedShell
1316  *
1317  * Returns: the global mode of the @shell
1318  **/
1319 EphyEmbedShellMode
ephy_embed_shell_get_mode(EphyEmbedShell * shell)1320 ephy_embed_shell_get_mode (EphyEmbedShell *shell)
1321 {
1322   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
1323 
1324   g_assert (EPHY_IS_EMBED_SHELL (shell));
1325 
1326   return priv->mode;
1327 }
1328 
1329 /**
1330  * ephy_embed_shell_clear_cache:
1331  * @shell: an #EphyEmbedShell
1332  *
1333  * Clears the HTTP cache (temporarily saved web pages).
1334  **/
1335 void
ephy_embed_shell_clear_cache(EphyEmbedShell * shell)1336 ephy_embed_shell_clear_cache (EphyEmbedShell *shell)
1337 {
1338   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
1339 
1340   webkit_web_context_clear_cache (priv->web_context);
1341 }
1342 
1343 EphyFiltersManager *
ephy_embed_shell_get_filters_manager(EphyEmbedShell * shell)1344 ephy_embed_shell_get_filters_manager (EphyEmbedShell *shell)
1345 {
1346   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
1347 
1348   return priv->filters_manager;
1349 }
1350 
1351 const char *
ephy_embed_shell_get_guid(EphyEmbedShell * shell)1352 ephy_embed_shell_get_guid (EphyEmbedShell *shell)
1353 {
1354   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
1355 
1356   return priv->guid;
1357 }
1358 
1359 WebKitWebContext *
ephy_embed_shell_get_web_context(EphyEmbedShell * shell)1360 ephy_embed_shell_get_web_context (EphyEmbedShell *shell)
1361 {
1362   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
1363 
1364   return priv->web_context;
1365 }
1366 
1367 EphyDownloadsManager *
ephy_embed_shell_get_downloads_manager(EphyEmbedShell * shell)1368 ephy_embed_shell_get_downloads_manager (EphyEmbedShell *shell)
1369 {
1370   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
1371 
1372   if (!priv->downloads_manager)
1373     priv->downloads_manager = EPHY_DOWNLOADS_MANAGER (g_object_new (EPHY_TYPE_DOWNLOADS_MANAGER, NULL));
1374   return priv->downloads_manager;
1375 }
1376 
1377 EphyPermissionsManager *
ephy_embed_shell_get_permissions_manager(EphyEmbedShell * shell)1378 ephy_embed_shell_get_permissions_manager (EphyEmbedShell *shell)
1379 {
1380   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
1381 
1382   return priv->permissions_manager;
1383 }
1384 
1385 EphySearchEngineManager *
ephy_embed_shell_get_search_engine_manager(EphyEmbedShell * shell)1386 ephy_embed_shell_get_search_engine_manager (EphyEmbedShell *shell)
1387 {
1388   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
1389 
1390   if (!priv->search_engine_manager)
1391     priv->search_engine_manager = ephy_search_engine_manager_new ();
1392   return priv->search_engine_manager;
1393 }
1394 
1395 EphyPasswordManager *
ephy_embed_shell_get_password_manager(EphyEmbedShell * shell)1396 ephy_embed_shell_get_password_manager (EphyEmbedShell *shell)
1397 {
1398   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
1399 
1400   return priv->password_manager;
1401 }
1402 
1403 void
ephy_embed_shell_register_ucm_handler(EphyEmbedShell * shell,WebKitUserContentManager * ucm)1404 ephy_embed_shell_register_ucm_handler (EphyEmbedShell           *shell,
1405                                        WebKitUserContentManager *ucm)
1406 {
1407   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
1408 
1409   /* User content manager */
1410   webkit_user_content_manager_register_script_message_handler_in_world (ucm,
1411                                                                         "overview",
1412                                                                         priv->guid);
1413   g_signal_connect_object (ucm, "script-message-received::overview",
1414                            G_CALLBACK (web_process_extension_overview_message_received_cb),
1415                            shell, 0);
1416 
1417   webkit_user_content_manager_register_script_message_handler (ucm,
1418                                                                "tlsErrorPage");
1419   g_signal_connect_object (ucm, "script-message-received::tlsErrorPage",
1420                            G_CALLBACK (web_process_extension_tls_error_page_message_received_cb),
1421                            shell, 0);
1422 
1423   webkit_user_content_manager_register_script_message_handler (ucm,
1424                                                                "unsafeBrowsingErrorPage");
1425   g_signal_connect_object (ucm, "script-message-received::unsafeBrowsingErrorPage",
1426                            G_CALLBACK (web_process_extension_unsafe_browsing_error_page_message_received_cb),
1427                            shell, 0);
1428 
1429   webkit_user_content_manager_register_script_message_handler_in_world (ucm,
1430                                                                         "passwordFormFocused",
1431                                                                         priv->guid);
1432   g_signal_connect_object (ucm, "script-message-received::passwordFormFocused",
1433                            G_CALLBACK (web_process_extension_password_form_focused_message_received_cb),
1434                            shell, 0);
1435 
1436   webkit_user_content_manager_register_script_message_handler (ucm,
1437                                                                "aboutApps");
1438   g_signal_connect_object (ucm, "script-message-received::aboutApps",
1439                            G_CALLBACK (web_process_extension_about_apps_message_received_cb),
1440                            shell, 0);
1441 
1442   webkit_user_content_manager_register_script_message_handler_in_world (ucm,
1443                                                                         "passwordManagerSave",
1444                                                                         priv->guid);
1445   g_signal_connect_object (ucm, "script-message-received::passwordManagerSave",
1446                            G_CALLBACK (web_process_extension_password_manager_save_received_cb),
1447                            shell, 0);
1448 
1449   webkit_user_content_manager_register_script_message_handler_in_world (ucm,
1450                                                                         "passwordManagerRequestSave",
1451                                                                         priv->guid);
1452   g_signal_connect_object (ucm, "script-message-received::passwordManagerRequestSave",
1453                            G_CALLBACK (web_process_extension_password_manager_request_save_received_cb),
1454                            shell, 0);
1455 
1456   /* Filter Manager */
1457   g_signal_connect_object (priv->filters_manager, "filters-disabled",
1458                            G_CALLBACK (webkit_user_content_manager_remove_all_filters),
1459                            ucm,
1460                            G_CONNECT_SWAPPED);
1461   g_signal_connect_object (priv->filters_manager, "filter-ready",
1462                            G_CALLBACK (webkit_user_content_manager_add_filter),
1463                            ucm,
1464                            G_CONNECT_SWAPPED);
1465   g_signal_connect_object (priv->filters_manager, "filter-removed",
1466                            G_CALLBACK (webkit_user_content_manager_remove_filter_by_id),
1467                            ucm,
1468                            G_CONNECT_SWAPPED);
1469 
1470   /* User Scripts */
1471   ephy_embed_prefs_apply_user_style (ucm);
1472   ephy_embed_prefs_apply_user_javascript (ucm);
1473 }
1474 
1475 void
ephy_embed_shell_unregister_ucm_handler(EphyEmbedShell * shell,WebKitUserContentManager * ucm)1476 ephy_embed_shell_unregister_ucm_handler (EphyEmbedShell           *shell,
1477                                          WebKitUserContentManager *ucm)
1478 {
1479   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
1480 
1481   webkit_user_content_manager_unregister_script_message_handler_in_world (ucm,
1482                                                                           "overview",
1483                                                                           priv->guid);
1484   webkit_user_content_manager_unregister_script_message_handler (ucm,
1485                                                                  "tlsErrorPage");
1486   webkit_user_content_manager_unregister_script_message_handler (ucm,
1487                                                                  "unsafeBrowsingErrorPage");
1488   webkit_user_content_manager_unregister_script_message_handler_in_world (ucm,
1489                                                                           "passwordManagerRequestSave",
1490                                                                           priv->guid);
1491   webkit_user_content_manager_unregister_script_message_handler_in_world (ucm,
1492                                                                           "passwordFormFocused",
1493                                                                           priv->guid);
1494   webkit_user_content_manager_unregister_script_message_handler (ucm, "aboutApps");
1495   webkit_user_content_manager_unregister_script_message_handler_in_world (ucm,
1496                                                                           "passwordManagerSave",
1497                                                                           priv->guid);
1498 }
1499 
1500 void
ephy_embed_shell_pdf_handler_stop(EphyEmbedShell * shell,WebKitWebView * web_view)1501 ephy_embed_shell_pdf_handler_stop (EphyEmbedShell *shell,
1502                                    WebKitWebView  *web_view)
1503 {
1504   EphyEmbedShellPrivate *priv = ephy_embed_shell_get_instance_private (shell);
1505 
1506   ephy_pdf_handler_stop (priv->pdf_handler, web_view);
1507 }
1508