ephy-shell.c 35.1 KB
Newer Older
Xan Lopez's avatar
Xan Lopez committed
1
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
2
/*
Christian Persch's avatar
Christian Persch committed
3
4
 *  Copyright © 2000-2004 Marco Pesenti Gritti
 *  Copyright © 2003, 2004, 2006 Christian Persch
Xan Lopez's avatar
Xan Lopez committed
5
 *  Copyright © 2011 Igalia S.L.
Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
6
7
8
9
10
11
12
13
14
15
16
17
18
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2, or (at your option)
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
19
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
Christian Persch's avatar
Christian Persch committed
20
 *
Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
21
22
 */

Christian Persch's avatar
Christian Persch committed
23
#include "config.h"
Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
24
#include "ephy-shell.h"
25
26
27
28

#include "ephy-bookmarks-editor.h"
#include "ephy-bookmarks-import.h"
#include "ephy-debug.h"
Xan Lopez's avatar
Xan Lopez committed
29
#include "ephy-embed-container.h"
30
#include "ephy-embed-utils.h"
31
32
#include "ephy-file-helpers.h"
#include "ephy-gui.h"
33
#include "ephy-history-window.h"
34
#include "ephy-home-action.h"
35
#include "ephy-lockdown.h"
36
#include "ephy-prefs.h"
37
#include "ephy-private.h"
38
39
40
#include "ephy-session.h"
#include "ephy-settings.h"
#include "ephy-type-builtins.h"
41
#include "ephy-web-view.h"
42
43
#include "ephy-window.h"
#include "prefs-dialog.h"
44
#include "window-commands.h"
45
46

#include <glib/gi18n.h>
47
#include <gdk/gdkx.h>
48
#include <gtk/gtk.h>
Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
49

50
51
52
#define WNCK_I_KNOW_THIS_IS_UNSTABLE
#include <libwnck/libwnck.h>

53
54
#define EPHY_SHELL_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE ((object), EPHY_TYPE_SHELL, EphyShellPrivate))

Xan Lopez's avatar
Xan Lopez committed
55
56
struct _EphyShellPrivate {
  EphySession *session;
57
  GList *windows;
Xan Lopez's avatar
Xan Lopez committed
58
59
  GObject *lockdown;
  EphyBookmarks *bookmarks;
60
  GNetworkMonitor *network_monitor;
Xan Lopez's avatar
Xan Lopez committed
61
62
63
64
65
  GtkWidget *bme;
  GtkWidget *history_window;
  GObject *prefs_dialog;
  GList *del_on_exit;
  EphyShellStartupContext *startup_context;
66
  guint open_uris_idle_id;
Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
67
68
};

69
EphyShell *ephy_shell = NULL;
Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
70

Xan Lopez's avatar
Xan Lopez committed
71
72
73
74
static void ephy_shell_class_init (EphyShellClass *klass);
static void ephy_shell_init   (EphyShell *shell);
static void ephy_shell_dispose    (GObject *object);
static void ephy_shell_finalize   (GObject *object);
Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
75

76
G_DEFINE_TYPE (EphyShell, ephy_shell, EPHY_TYPE_EMBED_SHELL)
Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
77

78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/**
 * ephy_shell_startup_context_new:
 * @bookmarks_filename: A bookmarks file to import.
 * @session_filename: A session to restore.
 * @bookmark_url: A URL to be added to the bookmarks.
 * @arguments: A %NULL-terminated array of URLs and file URIs to be opened.
 * @user_time: The user time when the EphyShell startup was invoked.
 *
 * Creates a new startup context. All string parameters, including
 * @arguments, are copied.
 *
 * Returns: a newly allocated #EphyShellStartupContext
 **/
EphyShellStartupContext *
ephy_shell_startup_context_new (EphyStartupFlags startup_flags,
Xan Lopez's avatar
Xan Lopez committed
93
94
95
96
97
                                char *bookmarks_filename,
                                char *session_filename,
                                char *bookmark_url,
                                char **arguments,
                                guint32 user_time)
98
{
Xan Lopez's avatar
Xan Lopez committed
99
  EphyShellStartupContext *ctx = g_slice_new0 (EphyShellStartupContext);
100

Xan Lopez's avatar
Xan Lopez committed
101
  ctx->startup_flags = startup_flags;
102

Xan Lopez's avatar
Xan Lopez committed
103
104
105
  ctx->bookmarks_filename = g_strdup (bookmarks_filename);
  ctx->session_filename = g_strdup (session_filename);
  ctx->bookmark_url = g_strdup (bookmark_url);
106

Xan Lopez's avatar
Xan Lopez committed
107
  ctx->arguments = g_strdupv (arguments);
108

Xan Lopez's avatar
Xan Lopez committed
109
  ctx->user_time = user_time;
110

Xan Lopez's avatar
Xan Lopez committed
111
  return ctx;
112
113
114
}

static void
115
ephy_shell_startup_continue (EphyShell *shell)
116
{
Xan Lopez's avatar
Xan Lopez committed
117
118
119
  EphyShellStartupContext *ctx;
  EphySession *session;

120
  session = ephy_shell_get_session (shell);
Xan Lopez's avatar
Xan Lopez committed
121
122
123
124
  g_assert (session != NULL);

  ctx = shell->priv->startup_context;

125
  if (ctx->session_filename != NULL)
126
127
    ephy_session_load (session, (const char *)ctx->session_filename,
                       ctx->user_time, NULL, NULL, NULL);
Xan Lopez's avatar
Xan Lopez committed
128
129
130
  else if (ctx->arguments != NULL) {
    /* Don't queue any window openings if no extra arguments given, */
    /* since session autoresume will open one for us. */
131
132
    ephy_shell_open_uris (shell, (const char **)ctx->arguments,
                          ctx->startup_flags, ctx->user_time);
Xan Lopez's avatar
Xan Lopez committed
133
  }
134
135
136
137
138

  /* Get an initial update on our windows and their workspaces,
   * otherwise our first call to ephy_window_is_on_current_workspace
   * will be unreliable.
   */
139
140
  if (GDK_IS_X11_DISPLAY (gdk_display_get_default ()))
    wnck_screen_force_update (wnck_screen_get_default ());
141
142
}

143
144
145
146
147
static void
new_window (GSimpleAction *action,
            GVariant *parameter,
            gpointer user_data)
{
148
  window_cmd_file_new_window (NULL, NULL);
149
150
}

151
152
153
154
155
156
157
158
static void
new_incognito_window (GSimpleAction *action,
                      GVariant *parameter,
                      gpointer user_data)
{
  window_cmd_file_new_incognito_window (NULL, NULL);
}

159
160
161
162
163
164
165
166
static void
reopen_closed_tab (GSimpleAction *action,
                   GVariant *parameter,
                   gpointer user_data)
{
  window_cmd_undo_close_tab (NULL, NULL);
}

167
168
169
170
171
static void
show_bookmarks (GSimpleAction *action,
                GVariant *parameter,
                gpointer user_data)
{
172
  window_cmd_edit_bookmarks (NULL, NULL);
173
174
175
176
177
178
179
}

static void
show_history (GSimpleAction *action,
              GVariant *parameter,
              gpointer user_data)
{
180
  window_cmd_edit_history (NULL, NULL);
181
182
183
184
185
186
187
}

static void
show_preferences (GSimpleAction *action,
                  GVariant *parameter,
                  gpointer user_data)
{
188
189
190
191
192
  GtkWindow *window;

  window = gtk_application_get_active_window (GTK_APPLICATION (ephy_shell));

  window_cmd_edit_preferences (NULL, EPHY_WINDOW (window));
193
194
}

Kat's avatar
Kat committed
195
196
197
198
199
200
201
202
203
204
205
206
static void
show_help (GSimpleAction *action,
           GVariant *parameter,
           gpointer user_data)
{
  GtkWindow *window;

  window = gtk_application_get_active_window (GTK_APPLICATION (ephy_shell));

  window_cmd_help_contents (NULL, GTK_WIDGET (window));
}

207
208
209
210
211
static void
show_about (GSimpleAction *action,
            GVariant *parameter,
            gpointer user_data)
{
212
  GtkWindow *window;
213

214
  window = gtk_application_get_active_window (GTK_APPLICATION (ephy_shell));
215
216

  window_cmd_help_about (NULL, GTK_WIDGET (window));
217
218
219
220
221
222
223
}

static void
quit_application (GSimpleAction *action,
                  GVariant *parameter,
                  gpointer user_data)
{
224
  window_cmd_file_quit (NULL, NULL);
225
226
227
228
}

static GActionEntry app_entries[] = {
  { "new", new_window, NULL, NULL, NULL },
229
  { "new-incognito", new_incognito_window, NULL, NULL, NULL },
230
231
232
  { "bookmarks", show_bookmarks, NULL, NULL, NULL },
  { "history", show_history, NULL, NULL, NULL },
  { "preferences", show_preferences, NULL, NULL, NULL },
Kat's avatar
Kat committed
233
  { "help", show_help, NULL, NULL, NULL },
234
235
236
237
  { "about", show_about, NULL, NULL, NULL },
  { "quit", quit_application, NULL, NULL, NULL },
};

238
239
240
241
static GActionEntry app_normal_mode_entries[] = {
  { "reopen-closed-tab", reopen_closed_tab, NULL, NULL, NULL },
};

242
243
244
static void
ephy_shell_startup (GApplication* application)
{
245
246
  EphyEmbedShellMode mode;

247
248
  G_APPLICATION_CLASS (ephy_shell_parent_class)->startup (application);

249
  /* We're not remoting; start our services */
250
  mode = ephy_embed_shell_get_mode (EPHY_EMBED_SHELL (application));
251

252
253
  if (mode != EPHY_EMBED_SHELL_MODE_APPLICATION) {
    GtkBuilder *builder;
254
255
256
257
258

    g_action_map_add_action_entries (G_ACTION_MAP (application),
                                     app_entries, G_N_ELEMENTS (app_entries),
                                     application);

259
260
261
262
263
264
265
266
267
268
269
270
    if (mode != EPHY_EMBED_SHELL_MODE_INCOGNITO) {
      g_action_map_add_action_entries (G_ACTION_MAP (application),
                                       app_normal_mode_entries, G_N_ELEMENTS (app_normal_mode_entries),
                                       application);
      g_object_bind_property (G_OBJECT (ephy_shell_get_session (EPHY_SHELL (application))),
                              "can-undo-tab-closed",
                              g_action_map_lookup_action (G_ACTION_MAP (application),
                                                          "reopen-closed-tab"),
                              "enabled",
                              G_BINDING_SYNC_CREATE);
    }

271
    builder = gtk_builder_new ();
272
273
274
    gtk_builder_add_from_resource (builder,
                                   "/org/gnome/epiphany/epiphany-application-menu.ui",
                                   NULL);
275
276
277
    gtk_application_set_app_menu (GTK_APPLICATION (application),
                                  G_MENU_MODEL (gtk_builder_get_object (builder, "app-menu")));
    g_object_unref (builder);
278
279
280
  }
}

281
282
283
284
285
286
287
288
static void
session_load_cb (GObject *object,
                 GAsyncResult *result,
                 gpointer user_data)
{
  EphySession *session = EPHY_SESSION (object);
  EphyShell *shell = EPHY_SHELL (user_data);

289
290
291
  if (ephy_session_resume_finish (session, result, NULL))
    shell->priv->startup_context->startup_flags |= EPHY_STARTUP_RESUMING_SESSION;

292
  ephy_shell_startup_continue (shell);
293
294
}

295
296
297
298
299
300
301
302
303
304
305
306
307
308
static void
ephy_shell_start_headless (EphyShell *shell)
{
  /* A bit of a hack: because we don't pass IS_SERVICE to GApplication
     (because otherwise we would need a separate launcher binary), we
     don't get the 10 seconds timeout for the first DBus call.
     So just hold/release to get the inactivity timeout started instead.
  */

  g_application_hold (G_APPLICATION (shell));
  g_application_release (G_APPLICATION (shell));
  ephy_shell_startup_continue (shell);
}

309
310
311
static void
ephy_shell_activate (GApplication *application)
{
312
313
  EphyShell *shell = EPHY_SHELL (application);

Xan Lopez's avatar
Xan Lopez committed
314
  /*
315
   * We get here on each new instance (remote or not). Autoresume the
316
   * session unless we are in application or headless mode and queue the
Xan Lopez's avatar
Xan Lopez committed
317
318
   * commands.
   */
319
320
321
322
  if (ephy_embed_shell_get_mode (EPHY_EMBED_SHELL (shell)) != EPHY_EMBED_SHELL_MODE_APPLICATION) {
    EphyShellStartupContext *ctx;

    ctx = shell->priv->startup_context;
323
    if (ctx->startup_flags != EPHY_STARTUP_OPEN_NOTHING) {
324
325
      ephy_session_resume (ephy_shell_get_session (shell),
                           ctx->user_time, NULL, session_load_cb, shell);
326
    } else
327
      ephy_shell_start_headless (shell);
328
329
  } else
    ephy_shell_startup_continue (shell);
330
331
332
333
334
335
336
}

/*
 * We use this enumeration to conveniently fill and read from the
 * dictionary variant that is sent from the remote to the primary
 * instance.
 */
Xan Lopez's avatar
Xan Lopez committed
337
338
339
340
341
342
343
typedef enum {
  CTX_STARTUP_FLAGS,
  CTX_BOOKMARKS_FILENAME,
  CTX_SESSION_FILENAME,
  CTX_BOOKMARK_URL,
  CTX_ARGUMENTS,
  CTX_USER_TIME
344
345
346
347
} CtxEnum;

static void
ephy_shell_add_platform_data (GApplication *application,
Xan Lopez's avatar
Xan Lopez committed
348
                              GVariantBuilder *builder)
349
{
Xan Lopez's avatar
Xan Lopez committed
350
351
352
  EphyShell *app;
  EphyShellStartupContext *ctx;
  GVariantBuilder *ctx_builder;
353
354
  static const char *empty_arguments[] = { "", NULL };
  const char* const * arguments;
Xan Lopez's avatar
Xan Lopez committed
355
356
357
358
359
360
361
362
363
364
365
366
367

  app = EPHY_SHELL (application);

  G_APPLICATION_CLASS (ephy_shell_parent_class)->add_platform_data (application,
                                                                    builder);

  if (app->priv->startup_context) {
    /*
     * We create an array variant that contains only the elements in
     * ctx that are non-NULL.
     */
    ctx_builder = g_variant_builder_new (G_VARIANT_TYPE_ARRAY);
    ctx = app->priv->startup_context;
368

Xan Lopez's avatar
Xan Lopez committed
369
370
371
372
    if (ctx->startup_flags)
      g_variant_builder_add (ctx_builder, "{iv}",
                             CTX_STARTUP_FLAGS,
                             g_variant_new_byte (ctx->startup_flags));
373

Xan Lopez's avatar
Xan Lopez committed
374
375
376
377
    if (ctx->bookmarks_filename)
      g_variant_builder_add (ctx_builder, "{iv}",
                             CTX_BOOKMARKS_FILENAME,
                             g_variant_new_string (ctx->bookmarks_filename));
378

Xan Lopez's avatar
Xan Lopez committed
379
380
381
382
    if (ctx->session_filename)
      g_variant_builder_add (ctx_builder, "{iv}",
                             CTX_SESSION_FILENAME,
                             g_variant_new_string (ctx->session_filename));
383

Xan Lopez's avatar
Xan Lopez committed
384
385
386
387
    if (ctx->bookmark_url)
      g_variant_builder_add (ctx_builder, "{iv}",
                             CTX_BOOKMARK_URL,
                             g_variant_new_string (ctx->bookmark_url));
388

389
390
391
392
    /*
     * If there are no URIs specified, pass an empty string, so that
     * the primary instance opens a new window.
     */
Xan Lopez's avatar
Xan Lopez committed
393
    if (ctx->arguments)
394
395
396
397
398
399
400
      arguments = (const gchar * const *)ctx->arguments;
    else
      arguments = empty_arguments;

    g_variant_builder_add (ctx_builder, "{iv}",
                           CTX_ARGUMENTS,
                           g_variant_new_strv (arguments, -1));
401

Xan Lopez's avatar
Xan Lopez committed
402
403
404
    g_variant_builder_add (ctx_builder, "{iv}",
                           CTX_USER_TIME,
                           g_variant_new_uint32 (ctx->user_time));
405

Xan Lopez's avatar
Xan Lopez committed
406
407
408
    g_variant_builder_add (builder, "{sv}",
                           "ephy-shell-startup-context",
                           g_variant_builder_end (ctx_builder));
409

Xan Lopez's avatar
Xan Lopez committed
410
411
    g_variant_builder_unref (ctx_builder);
  }
412
413
414
415
416
}

static void
ephy_shell_free_startup_context (EphyShell *shell)
{
Xan Lopez's avatar
Xan Lopez committed
417
  EphyShellStartupContext *ctx = shell->priv->startup_context;
418

Xan Lopez's avatar
Xan Lopez committed
419
  g_assert (ctx != NULL);
420

Xan Lopez's avatar
Xan Lopez committed
421
422
423
  g_free (ctx->bookmarks_filename);
  g_free (ctx->session_filename);
  g_free (ctx->bookmark_url);
424

Xan Lopez's avatar
Xan Lopez committed
425
  g_strfreev (ctx->arguments);
426

Xan Lopez's avatar
Xan Lopez committed
427
  g_slice_free (EphyShellStartupContext, ctx);
428

Xan Lopez's avatar
Xan Lopez committed
429
  shell->priv->startup_context = NULL;
430
431
432
433
}

static void
ephy_shell_before_emit (GApplication *application,
Xan Lopez's avatar
Xan Lopez committed
434
                        GVariant *platform_data)
435
{
Xan Lopez's avatar
Xan Lopez committed
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
  GVariantIter iter, ctx_iter;
  const char *key;
  CtxEnum ctx_key;
  GVariant *value, *ctx_value;
  EphyShellStartupContext *ctx = NULL;

  EphyShell *shell = EPHY_SHELL (application);

  g_variant_iter_init (&iter, platform_data);
  while (g_variant_iter_loop (&iter, "{&sv}", &key, &value)) {
    if (strcmp (key, "ephy-shell-startup-context") == 0) {
      ctx = g_slice_new0 (EphyShellStartupContext);

      /*
       * Iterate over the startup context variant and fill the members
       * that were wired. Everything else is just NULL.
       */
      g_variant_iter_init (&ctx_iter, value);
      while (g_variant_iter_loop (&ctx_iter, "{iv}", &ctx_key, &ctx_value)) {
        switch (ctx_key) {
        case CTX_STARTUP_FLAGS:
          ctx->startup_flags = g_variant_get_byte (ctx_value);
          break;
        case CTX_BOOKMARKS_FILENAME:
          ctx->bookmarks_filename = g_variant_dup_string (ctx_value, NULL);
          break;
        case CTX_SESSION_FILENAME:
          ctx->session_filename = g_variant_dup_string (ctx_value, NULL);
          break;
        case CTX_BOOKMARK_URL:
          ctx->bookmark_url = g_variant_dup_string (ctx_value, NULL);
          break;
        case CTX_ARGUMENTS:
          ctx->arguments = g_variant_dup_strv (ctx_value, NULL);
          break;
        case CTX_USER_TIME:
          ctx->user_time = g_variant_get_uint32 (ctx_value);
          break;
        default:
          g_assert_not_reached ();
          break;
        }
      }
    }
  }
481

Xan Lopez's avatar
Xan Lopez committed
482
483
484
485
486
487
  if (shell->priv->startup_context)
    ephy_shell_free_startup_context (shell);
  shell->priv->startup_context = ctx;

  G_APPLICATION_CLASS (ephy_shell_parent_class)->before_emit (application,
                                                              platform_data);
488
489
}

490
491
492
493
494
495
496
497
498
499
500
static GObject *
ephy_shell_get_lockdown (EphyShell *shell)
{
  g_return_val_if_fail (EPHY_IS_SHELL (shell), NULL);

  if (shell->priv->lockdown == NULL)
    shell->priv->lockdown = g_object_new (EPHY_TYPE_LOCKDOWN, NULL);

  return G_OBJECT (shell->priv->session);
}

501
502
503
static void
ephy_shell_constructed (GObject *object)
{
Xan Lopez's avatar
Xan Lopez committed
504
505
  if (ephy_embed_shell_get_mode (EPHY_EMBED_SHELL (object)) != EPHY_EMBED_SHELL_MODE_BROWSER) {
    GApplicationFlags flags;
506

Xan Lopez's avatar
Xan Lopez committed
507
508
509
510
    flags = g_application_get_flags (G_APPLICATION (object));
    flags |= G_APPLICATION_NON_UNIQUE;
    g_application_set_flags (G_APPLICATION (object), flags);
  }
511

512
513
  /* FIXME: not sure if this is the best place to put this stuff. */
  ephy_shell_get_lockdown (EPHY_SHELL (object));
514

Xan Lopez's avatar
Xan Lopez committed
515
516
  if (G_OBJECT_CLASS (ephy_shell_parent_class)->constructed)
    G_OBJECT_CLASS (ephy_shell_parent_class)->constructed (object);
517
518
}

Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
519
520
521
static void
ephy_shell_class_init (EphyShellClass *klass)
{
Xan Lopez's avatar
Xan Lopez committed
522
523
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
  GApplicationClass *application_class = G_APPLICATION_CLASS (klass);
Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
524

Xan Lopez's avatar
Xan Lopez committed
525
526
527
  object_class->dispose = ephy_shell_dispose;
  object_class->finalize = ephy_shell_finalize;
  object_class->constructed = ephy_shell_constructed;
528

Xan Lopez's avatar
Xan Lopez committed
529
530
531
532
  application_class->startup = ephy_shell_startup;
  application_class->activate = ephy_shell_activate;
  application_class->before_emit = ephy_shell_before_emit;
  application_class->add_platform_data = ephy_shell_add_platform_data;
533

Xan Lopez's avatar
Xan Lopez committed
534
  g_type_class_add_private (object_class, sizeof(EphyShellPrivate));
Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
535
536
}

537
538
539
540
541
static void
download_started_cb (WebKitWebContext *web_context,
                     WebKitDownload *download,
                     EphyShell *shell)
{
542
543
  GtkWindow *window = NULL;
  WebKitWebView *web_view;
544
  gboolean ephy_download_set;
545
546
547
548
549
550
551
552

  /* Is download locked down? */
  if (g_settings_get_boolean (EPHY_SETTINGS_LOCKDOWN,
                              EPHY_PREFS_LOCKDOWN_SAVE_TO_DISK)) {
    webkit_download_cancel (download);
    return;
  }

553
554
555
556
557
558
559
560
561
  /* Only create an EphyDownload for the WebKitDownload if it doesn't exist yet.
   * This can happen when the download has been started automatically by WebKit,
   * due to a context menu action or policy checker decision. Downloads started
   * explicitly by Epiphany are marked with ephy-download-set GObject data.
   */
  ephy_download_set = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (download), "ephy-download-set"));
  if (ephy_download_set)
    return;

562
563
564
565
566
567
568
569
570
571
572
573
  web_view = webkit_download_get_web_view (download);
  if (web_view) {
    GtkWidget *toplevel;

    toplevel = gtk_widget_get_toplevel (GTK_WIDGET (web_view));
    if (GTK_IS_WINDOW (toplevel))
      window = GTK_WINDOW (toplevel);
  }

  if (!window)
    window = gtk_application_get_active_window (GTK_APPLICATION (shell));

574
  ephy_download_new_for_download (download, window);
575
576
}

Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
577
static void
578
ephy_shell_init (EphyShell *shell)
Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
579
{
Xan Lopez's avatar
Xan Lopez committed
580
  EphyShell **ptr = &ephy_shell;
581
582
583
584
  WebKitWebContext *web_context;
  EphyEmbedShellMode mode;
  char *favicon_db_path;

Xan Lopez's avatar
Xan Lopez committed
585
  shell->priv = EPHY_SHELL_GET_PRIVATE (shell);
586

Xan Lopez's avatar
Xan Lopez committed
587
588
589
  /* globally accessible singleton */
  g_assert (ephy_shell == NULL);
  ephy_shell = shell;
590
  g_object_add_weak_pointer (G_OBJECT (ephy_shell),
Xan Lopez's avatar
Xan Lopez committed
591
                             (gpointer *)ptr);
592

593
594
  web_context = webkit_web_context_get_default ();
  g_signal_connect (web_context, "download-started",
595
596
                    G_CALLBACK (download_started_cb),
                    shell);
597
598
599
600

  /* Initialize the favicon cache as early as possible, or further
     calls to webkit_web_context_get_favicon_database will fail. */
  mode = ephy_embed_shell_get_mode (ephy_embed_shell_get_default ());
601
  favicon_db_path = g_build_filename (EPHY_EMBED_SHELL_MODE_HAS_PRIVATE_PROFILE (mode) ?
602
603
604
605
606
                                      ephy_dot_dir () : g_get_user_cache_dir (),
                                      g_get_prgname (), "icondatabase", NULL);

  webkit_web_context_set_favicon_database_directory (web_context, favicon_db_path);
  g_free (favicon_db_path);
607
608
}

Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
609
static void
610
ephy_shell_dispose (GObject *object)
Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
611
{
612
  EphyShellPrivate *priv = EPHY_SHELL (object)->priv;
Xan Lopez's avatar
Xan Lopez committed
613
614
615

  LOG ("EphyShell disposing");

616
617
618
619
620
621
622
  g_clear_object (&priv->session);
  g_clear_object (&priv->lockdown);
  g_clear_pointer (&priv->bme, gtk_widget_destroy);
  g_clear_pointer (&priv->history_window, gtk_widget_destroy);
  g_clear_object (&priv->prefs_dialog);
  g_clear_object (&priv->bookmarks);
  g_clear_object (&priv->network_monitor);
Xan Lopez's avatar
Xan Lopez committed
623

624
625
626
627
628
  if (priv->open_uris_idle_id > 0)  {
    g_source_remove (priv->open_uris_idle_id);
    priv->open_uris_idle_id = 0;
  }

Xan Lopez's avatar
Xan Lopez committed
629
  G_OBJECT_CLASS (ephy_shell_parent_class)->dispose (object);
630
}
631

632
633
634
static void
ephy_shell_finalize (GObject *object)
{
Xan Lopez's avatar
Xan Lopez committed
635
  EphyShell *shell = EPHY_SHELL (object);
636

Xan Lopez's avatar
Xan Lopez committed
637
638
  if (shell->priv->startup_context)
    ephy_shell_free_startup_context (shell);
639

Xan Lopez's avatar
Xan Lopez committed
640
  G_OBJECT_CLASS (ephy_shell_parent_class)->finalize (object);
Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
641

Xan Lopez's avatar
Xan Lopez committed
642
  LOG ("Ephy shell finalised");
Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
643
644
}

645
646
647
648
649
/**
 * ephy_shell_get_default:
 *
 * Retrieve the default #EphyShell object
 *
650
 * Return value: (transfer none): the default #EphyShell
651
652
653
654
 **/
EphyShell *
ephy_shell_get_default (void)
{
Xan Lopez's avatar
Xan Lopez committed
655
  return ephy_shell;
656
657
}

Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
658
/**
659
 * ephy_shell_new_tab_full:
Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
660
661
 * @shell: a #EphyShell
 * @parent_window: the target #EphyWindow or %NULL
Christian Persch's avatar
Christian Persch committed
662
 * @previous_embed: the referrer embed, or %NULL
663
 * @request: a #WebKitNetworkRequest to load or %NULL
664
 * @chrome: a #EphyEmbedChrome mask to use if creating a new window
665
 * @is_popup: whether the new window is a popup
666
 * @user_time: a timestamp, or 0
Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
667
668
 *
 * Create a new tab and the parent window when necessary.
669
 * Use this function to open urls in new window/tabs.
Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
670
 *
671
 * Return value: (transfer none): the created #EphyEmbed
Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
672
 **/
Christian Persch's avatar
Christian Persch committed
673
EphyEmbed *
674
ephy_shell_new_tab_full (EphyShell *shell,
Xan Lopez's avatar
Xan Lopez committed
675
676
                         EphyWindow *parent_window,
                         EphyEmbed *previous_embed,
Carlos Garcia Campos's avatar
Carlos Garcia Campos committed
677
                         WebKitURIRequest *request,
Xan Lopez's avatar
Xan Lopez committed
678
679
680
681
                         EphyNewTabFlags flags,
                         EphyWebViewChrome chrome,
                         gboolean is_popup,
                         guint32 user_time)
Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
682
{
683
  EphyEmbedShell *embed_shell;
Xan Lopez's avatar
Xan Lopez committed
684
685
  EphyWindow *window;
  EphyEmbed *embed = NULL;
686
  gboolean fullscreen_lockdown = FALSE;
Xan Lopez's avatar
Xan Lopez committed
687
688
  gboolean in_new_window = TRUE;
  gboolean open_page = FALSE;
689
  gboolean delayed_open_page = FALSE;
690
  gboolean jump_to = FALSE;
Xan Lopez's avatar
Xan Lopez committed
691
692
693
  gboolean active_is_blank = FALSE;
  gboolean copy_history = TRUE;
  gboolean is_empty = FALSE;
694
  int position = -1;
Xan Lopez's avatar
Xan Lopez committed
695

696
697
698
699
700
  g_return_val_if_fail (EPHY_IS_SHELL (shell), NULL);
  g_return_val_if_fail (EPHY_IS_WINDOW (parent_window) || !parent_window, NULL);
  g_return_val_if_fail (EPHY_IS_EMBED (previous_embed) || !previous_embed, NULL);
  g_return_val_if_fail (WEBKIT_IS_URI_REQUEST (request) || !request, NULL);

701
  embed_shell = EPHY_EMBED_SHELL (shell);
702

Xan Lopez's avatar
Xan Lopez committed
703
  if (flags & EPHY_NEW_TAB_OPEN_PAGE) open_page = TRUE;
704
  if (flags & EPHY_NEW_TAB_DELAYED_OPEN_PAGE) delayed_open_page = TRUE;
Xan Lopez's avatar
Xan Lopez committed
705
706
707
  if (flags & EPHY_NEW_TAB_IN_NEW_WINDOW) in_new_window = TRUE;
  if (flags & EPHY_NEW_TAB_IN_EXISTING_WINDOW) in_new_window = FALSE;
  if (flags & EPHY_NEW_TAB_DONT_COPY_HISTORY) copy_history = FALSE;
708
  if (flags & EPHY_NEW_TAB_JUMP) jump_to = TRUE;
Xan Lopez's avatar
Xan Lopez committed
709

710
711
712
  fullscreen_lockdown = g_settings_get_boolean (EPHY_SETTINGS_LOCKDOWN,
                                                EPHY_PREFS_LOCKDOWN_FULLSCREEN);
  in_new_window = in_new_window && !fullscreen_lockdown;
713
  g_return_val_if_fail ((open_page || delayed_open_page) == (gboolean)(request != NULL), NULL);
Xan Lopez's avatar
Xan Lopez committed
714
715
716
717

  LOG ("Opening new tab parent-window %p parent-embed %p in-new-window:%s jump-to:%s",
       parent_window, previous_embed, in_new_window ? "t" : "f", jump_to ? "t" : "f");

718
  if (!in_new_window && parent_window != NULL)
Xan Lopez's avatar
Xan Lopez committed
719
    window = parent_window;
720
  else
Xan Lopez's avatar
Xan Lopez committed
721
    window = ephy_window_new_with_chrome (chrome, is_popup);
722

723
724
  if (flags & EPHY_NEW_TAB_APPEND_AFTER) {
    if (previous_embed) {
725
      GtkWidget *nb = ephy_window_get_notebook (window);
726
727
728
729
730
731
      /* FIXME this assumes the tab is the  direct notebook child */
      position = gtk_notebook_page_num (GTK_NOTEBOOK (nb),
                                        GTK_WIDGET (previous_embed)) + 1;
    } else
      g_warning ("Requested to append new tab after parent, but 'previous_embed' was NULL");
  }
732

733
734
735
  if (flags & EPHY_NEW_TAB_FIRST)
    position = 0;

Xan Lopez's avatar
Xan Lopez committed
736
  if (flags & EPHY_NEW_TAB_FROM_EXTERNAL) {
737
    /* If the active embed is blank, use that to open the url and jump to it */
Xan Lopez's avatar
Xan Lopez committed
738
739
    embed = ephy_embed_container_get_active_child (EPHY_EMBED_CONTAINER (window));
    if (embed != NULL) {
740
741
742
      EphyWebView *view = ephy_embed_get_web_view (embed);
      if ((ephy_web_view_get_is_blank (view) || ephy_embed_get_overview_mode (embed)) &&
          ephy_web_view_is_loading (view) == FALSE) {
Xan Lopez's avatar
Xan Lopez committed
743
744
745
746
747
748
749
750
751
        active_is_blank = TRUE;
      }
    }
  }

  if (active_is_blank == FALSE) {
    embed = EPHY_EMBED (g_object_new (EPHY_TYPE_EMBED, NULL));
    g_assert (embed != NULL);
    gtk_widget_show (GTK_WIDGET (embed));
752

Xan Lopez's avatar
Xan Lopez committed
753
754
    ephy_embed_container_add_child (EPHY_EMBED_CONTAINER (window), embed, position, jump_to);
  }
755

Xan Lopez's avatar
Xan Lopez committed
756
757
758
759
  if (copy_history && previous_embed != NULL) {
    ephy_web_view_copy_back_history (ephy_embed_get_web_view (previous_embed),
                                     ephy_embed_get_web_view (embed));
  }
760

Xan Lopez's avatar
Xan Lopez committed
761
762
  ephy_gui_window_update_user_time (GTK_WIDGET (window), user_time);

763
764
  if ((flags & EPHY_NEW_TAB_DONT_SHOW_WINDOW) == 0 &&
      ephy_embed_shell_get_mode (embed_shell) != EPHY_EMBED_SHELL_MODE_TEST) {
Xan Lopez's avatar
Xan Lopez committed
765
766
767
768
769
770
    gtk_widget_show (GTK_WIDGET (window));
  }

  if (flags & EPHY_NEW_TAB_FULLSCREEN_MODE) {
    gtk_window_fullscreen (GTK_WINDOW (window));
  }
771

Xan Lopez's avatar
Xan Lopez committed
772
773
774
775
  if (flags & EPHY_NEW_TAB_HOME_PAGE ||
      flags & EPHY_NEW_TAB_NEW_PAGE) {
    EphyWebView *view = ephy_embed_get_web_view (embed);
    ephy_web_view_set_typed_address (view, "");
776
    ephy_window_activate_location (window);
777
778
    ephy_web_view_load_homepage (view);
    is_empty = TRUE;
779
  } else if (open_page) {
Xan Lopez's avatar
Xan Lopez committed
780
781
    ephy_web_view_load_request (ephy_embed_get_web_view (embed),
                                request);
782

Carlos Garcia Campos's avatar
Carlos Garcia Campos committed
783
    is_empty = ephy_embed_utils_url_is_empty (webkit_uri_request_get_uri (request));
784
785
  } else if (delayed_open_page)
      ephy_embed_set_delayed_load_request (embed, request);
Xan Lopez's avatar
Xan Lopez committed
786
787
788
789
790
791
792
793
794

  /* Make sure the initial focus is somewhere sensible and not, for
   * example, on the reload button.
   */
  if (in_new_window || jump_to) {
    /* If the location entry is blank, focus that, except if the
     * page was a copy */
    if (is_empty) {
      /* empty page, focus location entry */
795
      ephy_window_activate_location (window);
796
797
    } else if ((flags & EPHY_NEW_TAB_DONT_SHOW_WINDOW) == 0 && embed != NULL &&
               ephy_embed_shell_get_mode (embed_shell) != EPHY_EMBED_SHELL_MODE_TEST) {
Xan Lopez's avatar
Xan Lopez committed
798
799
800
801
802
      /* non-empty page, focus the page. but make sure the widget is realised first! */
      gtk_widget_realize (GTK_WIDGET (embed));
      gtk_widget_grab_focus (GTK_WIDGET (embed));
    }
  }
803

804
805
  if (flags & EPHY_NEW_TAB_PRESENT_WINDOW &&
      ephy_embed_shell_get_mode (EPHY_EMBED_SHELL (embed_shell)) != EPHY_EMBED_SHELL_MODE_TEST)
806
807
    gtk_window_present_with_time (GTK_WINDOW (window), user_time);

Xan Lopez's avatar
Xan Lopez committed
808
  return embed;
Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
809
810
}

811
812
813
814
/**
 * ephy_shell_new_tab:
 * @shell: a #EphyShell
 * @parent_window: the target #EphyWindow or %NULL
Christian Persch's avatar
Christian Persch committed
815
 * @previous_embed: the referrer embed, or %NULL
816
817
818
819
820
 * @url: an url to load or %NULL
 *
 * Create a new tab and the parent window when necessary.
 * Use this function to open urls in new window/tabs.
 *
821
 * Return value: (transfer none): the created #EphyEmbed
822
 **/
Christian Persch's avatar
Christian Persch committed
823
EphyEmbed *
824
ephy_shell_new_tab (EphyShell *shell,
Xan Lopez's avatar
Xan Lopez committed
825
826
827
828
                    EphyWindow *parent_window,
                    EphyEmbed *previous_embed,
                    const char *url,
                    EphyNewTabFlags flags)
829
{
Xan Lopez's avatar
Xan Lopez committed
830
  EphyEmbed *embed;
Carlos Garcia Campos's avatar
Carlos Garcia Campos committed
831
  WebKitURIRequest *request = url ? webkit_uri_request_new (url) : NULL;
832

Xan Lopez's avatar
Xan Lopez committed
833
834
835
  embed = ephy_shell_new_tab_full (shell, parent_window,
                                   previous_embed, request, flags,
                                   EPHY_WEB_VIEW_CHROME_ALL, FALSE, 0);
836

Xan Lopez's avatar
Xan Lopez committed
837
838
  if (request)
    g_object_unref (request);
839

Xan Lopez's avatar
Xan Lopez committed
840
  return embed;
841
842
}

Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
843
844
/**
 * ephy_shell_get_session:
845
 * @shell: the #EphyShell
Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
846
847
848
 *
 * Returns current session.
 *
849
 * Return value: (transfer none): the current session.
Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
850
 **/
851
EphySession *
852
ephy_shell_get_session (EphyShell *shell)
Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
853
{
Xan Lopez's avatar
Xan Lopez committed
854
855
  g_return_val_if_fail (EPHY_IS_SHELL (shell), NULL);

856
  if (shell->priv->session == NULL)
Xan Lopez's avatar
Xan Lopez committed
857
    shell->priv->session = g_object_new (EPHY_TYPE_SESSION, NULL);
858

859
  return shell->priv->session;
Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
860
861
}

862
863
864
865
866
/**
 * ephy_shell_get_bookmarks:
 *
 * Return value: (transfer none):
 **/
Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
867
EphyBookmarks *
868
ephy_shell_get_bookmarks (EphyShell *shell)
Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
869
{
Xan Lopez's avatar
Xan Lopez committed
870
871
872
  if (shell->priv->bookmarks == NULL) {
    shell->priv->bookmarks = ephy_bookmarks_new ();
  }
Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
873

Xan Lopez's avatar
Xan Lopez committed
874
  return shell->priv->bookmarks;
Marco Pesenti Gritti's avatar
Marco Pesenti Gritti committed
875
}
876

877
878
879
880
881
/**
 * ephy_shell_get_net_monitor:
 *
 * Return value: (transfer none):
 **/
882
GNetworkMonitor *
883
884
ephy_shell_get_net_monitor (EphyShell *shell)
{
Xan Lopez's avatar
Xan Lopez committed
885
886
  EphyShellPrivate *priv = shell->priv;

887
  if (priv->network_monitor == NULL)
888
    priv->network_monitor = g_network_monitor_get_default ();
889

890
  return priv->network_monitor;
891
892
}

893
894
895
896
897
/**
 * ephy_shell_get_bookmarks_editor:
 *
 * Return value: (transfer none):
 **/
898
GtkWidget *
899
ephy_shell_get_bookmarks_editor (EphyShell *shell)
900
{
Xan Lopez's avatar
Xan Lopez committed
901
902
903
904
905
906
907
  EphyBookmarks *bookmarks;

  if (shell->priv->bme == NULL) {
    bookmarks = ephy_shell_get_bookmarks (ephy_shell);
    g_assert (bookmarks != NULL);
    shell->priv->bme = ephy_bookmarks_editor_new (bookmarks);
  }
908

Xan Lopez's avatar
Xan Lopez committed
909
  return shell->priv->bme;
910
911
}

912
913
914
915
916
/**
 * ephy_shell_get_history_window:
 *
 * Return value: (transfer none):
 **/
917
GtkWidget *
918
ephy_shell_get_history_window (EphyShell *shell)
919
{
920
  EphyEmbedShell *embed_shell;
921
  EphyHistoryService *service;
Xan Lopez's avatar
Xan Lopez committed
922

923
924
  embed_shell = ephy_embed_shell_get_default ();

Xan Lopez's avatar
Xan Lopez committed
925
  if (shell->priv->history_window == NULL) {
926
927
    service = EPHY_HISTORY_SERVICE
      (ephy_embed_shell_get_global_history_service (embed_shell));
928
    shell->priv->history_window = ephy_history_window_new (service);
Xan Lopez's avatar
Xan Lopez committed
929
  }
930

Xan Lopez's avatar
Xan Lopez committed
931
  return shell->priv->history_window;
932
}
933

934
935
936
937
938
/**
 * ephy_shell_get_prefs_dialog:
 *
 * Return value: (transfer none):
 **/
939
940
941
GObject *
ephy_shell_get_prefs_dialog (EphyShell *shell)
{
Xan Lopez's avatar
Xan Lopez committed
942
943
  if (shell->priv->prefs_dialog == NULL) {
    shell->priv->prefs_dialog = g_object_new (EPHY_TYPE_PREFS_DIALOG, NULL);
944
945
946
947
    g_signal_connect (shell->priv->prefs_dialog,
                      "destroy",
                      G_CALLBACK (gtk_widget_destroyed),
                      &shell->priv->prefs_dialog);
Xan Lopez's avatar
Xan Lopez committed
948
  }
949

Xan Lopez's avatar
Xan Lopez committed
950
  return shell->priv->prefs_dialog;
951
952
}

953
void
954
_ephy_shell_create_instance (EphyEmbedShellMode mode)
955
{
Xan Lopez's avatar
Xan Lopez committed
956
957
958
959
960
961
962
963
  g_assert (ephy_shell == NULL);

  ephy_shell = EPHY_SHELL (g_object_new (EPHY_TYPE_SHELL,
                                         "application-id", "org.gnome.Epiphany",
                                         "mode", mode,
                                         NULL));
  /* FIXME weak ref */
  g_assert (ephy_shell != NULL);
964
965
}

966
967
968
969
970
971
972
973
/**
 * ephy_shell_set_startup_context:
 * @shell: A #EphyShell
 * @ctx: (transfer full): a #EphyShellStartupContext
 *
 * Sets the startup context to be used during activation of a new instance.
 * See ephy_shell_set_startup_new().
 **/
974
void
975
ephy_shell_set_startup_context (EphyShell *shell,
Xan Lopez's avatar
Xan Lopez committed
976
                                EphyShellStartupContext *ctx)
977
{
Xan Lopez's avatar
Xan Lopez committed
978
  g_return_if_fail (EPHY_IS_SHELL (shell));
979

Xan Lopez's avatar
Xan Lopez committed
980
981
  if (shell->priv->startup_context)
    ephy_shell_free_startup_context (shell);
982

Xan Lopez's avatar
Xan Lopez committed
983
  shell->priv->startup_context = ctx;
984
}
985
986
987
988

guint
ephy_shell_get_n_windows (EphyShell *shell)
{
989
990
  GList *list;

991
992
  g_return_val_if_fail (EPHY_IS_SHELL (shell), 0);

993
994
  list = gtk_application_get_windows (GTK_APPLICATION (shell));
  return g_list_length (list);
995
996
}

997
998
999
1000
EphyWindow*
ephy_shell_get_main_window (EphyShell *shell)
{
  EphyWindow *window = NULL;
For faster browsing, not all history is shown. View entire blame