ephy-embed.c 28.8 KB
Newer Older
1
/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
2 3
/*
 *  Copyright © 2007 Xan Lopez
4
 *  Copyright © 2008 Jan Alonzo
5
 *  Copyright © 2009 Gustavo Noronha Silva
6
 *  Copyright © 2009 Igalia S.L.
7
 *  Copyright © 2009 Collabora Ltd.
8
 *
Michael Catanzaro's avatar
Michael Catanzaro committed
9 10 11
 *  This file is part of Epiphany.
 *
 *  Epiphany is free software: you can redistribute it and/or modify
12
 *  it under the terms of the GNU General Public License as published by
Michael Catanzaro's avatar
Michael Catanzaro committed
13 14
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
15
 *
Michael Catanzaro's avatar
Michael Catanzaro committed
16
 *  Epiphany is distributed in the hope that it will be useful,
17 18 19 20 21
 *  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
Michael Catanzaro's avatar
Michael Catanzaro committed
22
 *  along with Epiphany.  If not, see <http://www.gnu.org/licenses/>.
23 24 25 26 27 28 29 30
 */

#include "config.h"
#include "ephy-embed.h"

#include "ephy-debug.h"
#include "ephy-embed-prefs.h"
#include "ephy-embed-shell.h"
31
#include "ephy-embed-utils.h"
32
#include "ephy-find-toolbar.h"
33
#include "ephy-notification-container.h"
34 35
#include "ephy-prefs.h"
#include "ephy-settings.h"
36
#include "ephy-string.h"
37 38 39 40 41 42
#include "ephy-web-view.h"
#include "nautilus-floating-bar.h"

#include <glib/gi18n.h>
#include <webkit2/webkit2.h>

Michael Catanzaro's avatar
Michael Catanzaro committed
43 44 45
static void     ephy_embed_constructed (GObject *object);
static void     ephy_embed_restored_window_cb (EphyEmbedShell *shell,
                                               EphyEmbed      *embed);
46 47

#define EPHY_EMBED_STATUSBAR_TAB_MESSAGE_CONTEXT_DESCRIPTION "tab_message"
48
#define MAX_TITLE_LENGTH 512 /* characters */
49 50 51 52 53 54 55

typedef struct {
  gchar *text;
  guint context_id;
  guint message_id;
} EphyEmbedStatusbarMsg;

56 57 58
struct _EphyEmbed {
  GtkBox parent_instance;

59 60 61 62 63
  EphyFindToolbar *find_toolbar;
  GtkBox *top_widgets_vbox;
  GtkPaned *paned;
  WebKitWebView *web_view;
  GSList *destroy_on_transition_list;
64
  GtkWidget *overlay;
65
  GtkWidget *floating_bar;
66
  GtkWidget *progress;
67 68
  GtkWidget *fullscreen_message_label;

69
  char *title;
70
  WebKitURIRequest *delayed_request;
71
  WebKitWebViewSessionState *delayed_state;
72
  guint delayed_request_source_id;
73 74 75 76 77 78 79 80 81 82 83 84

  GSList *messages;
  GSList *keys;

  guint seq_context_id;
  guint seq_message_id;

  guint tab_message_id;
  guint pop_statusbar_later_source_id;

  guint fullscreen_message_id;

85 86
  guint clear_progress_source_id;

87
  gulong status_handler_id;
88
  gulong progress_update_handler_id;
89
  gboolean inspector_loaded;
90 91
};

92 93
G_DEFINE_TYPE (EphyEmbed, ephy_embed, GTK_TYPE_BOX)

Michael Catanzaro's avatar
Michael Catanzaro committed
94
enum {
95
  PROP_0,
96
  PROP_WEB_VIEW,
97
  PROP_TITLE,
98
  LAST_PROP
99 100
};

101
static GParamSpec *obj_properties[LAST_PROP];
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123

/* Portions of the following code based on GTK+.
 * License block as follows:
 *
 * GTK - The GIMP Toolkit
 * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
 * GtkStatusbar Copyright (C) 1998 Shawn T. Amundson
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
124 125 126 127 128 129 130 131 132 133 134 135 136 137
 *
 * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
 * file for a list of people on the GTK+ Team.  See the ChangeLog
 * files for a list of changes.  These files are distributed with
 * GTK+ at ftp://ftp.gtk.org/pub/gtk/.
 *
 */

static guint
ephy_embed_statusbar_get_context_id (EphyEmbed *embed, const char  *context_description)
{
  char *string;
  guint id;

138 139
  g_assert (EPHY_IS_EMBED (embed));
  g_assert (context_description != NULL);
140 141 142 143 144 145

  /* we need to preserve namespaces on object datas */
  string = g_strconcat ("ephy-embed-status-bar-context:", context_description, NULL);

  id = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (embed), string));
  if (id == 0) {
146
    id = embed->seq_context_id++;
147
    g_object_set_data_full (G_OBJECT (embed), string, GUINT_TO_POINTER (id), NULL);
148
    embed->keys = g_slist_prepend (embed->keys, string);
149 150 151 152 153 154
  } else
    g_free (string);

  return id;
}

155 156 157
static void
ephy_embed_set_statusbar_label (EphyEmbed *embed, const char *label)
{
158
  nautilus_floating_bar_set_primary_label (NAUTILUS_FLOATING_BAR (embed->floating_bar), label);
159 160

  if (label == NULL || label[0] == '\0') {
161 162
    gtk_widget_hide (embed->floating_bar);
    gtk_widget_set_halign (embed->floating_bar, GTK_ALIGN_START);
163
  } else
164
    gtk_widget_show (embed->floating_bar);
165 166
}

167 168 169
static void
ephy_embed_statusbar_update (EphyEmbed *embed, const char *text)
{
170
  g_assert (EPHY_IS_EMBED (embed));
171

172
  ephy_embed_set_statusbar_label (embed, text);
173 174 175 176 177 178 179
}

static guint
ephy_embed_statusbar_push (EphyEmbed *embed, guint context_id, const char *text)
{
  EphyEmbedStatusbarMsg *msg;

180 181 182
  g_assert (EPHY_IS_EMBED (embed));
  g_assert (context_id != 0);
  g_assert (text != NULL);
183 184 185 186

  msg = g_slice_new (EphyEmbedStatusbarMsg);
  msg->text = g_strdup (text);
  msg->context_id = context_id;
187
  msg->message_id = embed->seq_message_id++;
188

189
  embed->messages = g_slist_prepend (embed->messages, msg);
190 191 192 193 194 195 196 197 198 199 200 201 202 203

  ephy_embed_statusbar_update (embed, text);

  return msg->message_id;
}

/* End of code based on GTK+ GtkStatusbar. */

static void
ephy_embed_statusbar_pop (EphyEmbed *embed, guint context_id)
{
  EphyEmbedStatusbarMsg *msg;
  GSList *list;

204 205
  g_assert (EPHY_IS_EMBED (embed));
  g_assert (context_id != 0);
206

207
  for (list = embed->messages; list; list = list->next) {
208
    msg = list->data;
209 210

    if (msg->context_id == context_id) {
211
      embed->messages = g_slist_remove_link (embed->messages, list);
212 213 214 215 216 217 218
      g_free (msg->text);
      g_slice_free (EphyEmbedStatusbarMsg, msg);
      g_slist_free_1 (list);
      break;
    }
  }

219
  msg = embed->messages ? embed->messages->data : NULL;
220 221 222
  ephy_embed_statusbar_update (embed, msg ? msg->text : NULL);
}

223 224 225 226 227
static void
remove_from_destroy_list_cb (GtkWidget *widget, EphyEmbed *embed)
{
  GSList *list;

228
  list = embed->destroy_on_transition_list;
229
  list = g_slist_remove (list, widget);
230
  embed->destroy_on_transition_list = list;
231 232
}

233 234 235 236 237 238 239 240 241 242 243 244 245
static void
ephy_embed_destroy_top_widgets (EphyEmbed *embed)
{
  GSList *iter;

  for (iter = embed->destroy_on_transition_list; iter; iter = iter->next) {
    g_signal_handlers_disconnect_by_func (iter->data, remove_from_destroy_list_cb, embed);
    gtk_widget_destroy (GTK_WIDGET (iter->data));
  }

  embed->destroy_on_transition_list = NULL;
}

246
static void
Michael Catanzaro's avatar
Michael Catanzaro committed
247
ephy_embed_set_title (EphyEmbed  *embed,
248 249 250 251 252 253 254 255 256 257 258
                      const char *title)
{
  char *new_title;

  new_title = g_strdup (title);
  if (new_title == NULL || g_strstrip (new_title)[0] == '\0') {
    const char *address;

    g_free (new_title);
    new_title = NULL;

259
    address = ephy_web_view_get_address (EPHY_WEB_VIEW (embed->web_view));
260 261 262 263 264
    if (address && strcmp (address, "about:blank") != 0)
      new_title = ephy_embed_utils_get_title_from_address (address);

    if (new_title == NULL || new_title[0] == '\0') {
      g_free (new_title);
265
      new_title = g_strdup (_(BLANK_PAGE_TITLE));
266 267 268
    }
  }

269 270
  g_free (embed->title);
  embed->title = ephy_string_shorten (new_title, MAX_TITLE_LENGTH);
271

272
  g_object_notify_by_pspec (G_OBJECT (embed), obj_properties[PROP_TITLE]);
273 274 275 276
}

static void
web_view_title_changed_cb (WebKitWebView *web_view,
Michael Catanzaro's avatar
Michael Catanzaro committed
277 278
                           GParamSpec    *spec,
                           EphyEmbed     *embed)
279 280 281 282
{
  ephy_embed_set_title (embed, webkit_web_view_get_title (web_view));
}

283
static void
Michael Catanzaro's avatar
Michael Catanzaro committed
284
load_changed_cb (WebKitWebView  *web_view,
285
                 WebKitLoadEvent load_event,
Michael Catanzaro's avatar
Michael Catanzaro committed
286
                 EphyEmbed      *embed)
287
{
288
  switch (load_event) {
Michael Catanzaro's avatar
Michael Catanzaro committed
289 290 291 292 293 294 295 296 297 298 299 300 301
    case WEBKIT_LOAD_COMMITTED:
      ephy_embed_destroy_top_widgets (embed);
      break;
    case WEBKIT_LOAD_FINISHED: {
      const char *title = webkit_web_view_get_title (web_view);
      if (ephy_web_view_get_is_blank (EPHY_WEB_VIEW (web_view)) || !title || !*title)
        ephy_embed_set_title (embed, NULL);
      break;
    }
    case WEBKIT_LOAD_STARTED:
    case WEBKIT_LOAD_REDIRECTED:
    default:
      break;
302
  }
303
}
304

305 306 307 308 309
static void
ephy_embed_grab_focus (GtkWidget *widget)
{
  GtkWidget *child;

310
  child = GTK_WIDGET (ephy_embed_get_web_view (EPHY_EMBED (widget)));
311 312 313 314 315

  if (child)
    gtk_widget_grab_focus (child);
}

316 317 318 319

static gboolean
fullscreen_message_label_hide (EphyEmbed *embed)
{
320 321 322 323
  if (embed->fullscreen_message_id) {
    gtk_widget_hide (embed->fullscreen_message_label);
    g_source_remove (embed->fullscreen_message_id);
    embed->fullscreen_message_id = 0;
324 325 326 327 328 329 330 331
  }

  return FALSE;
}

void
ephy_embed_entering_fullscreen (EphyEmbed *embed)
{
332
  if (!g_settings_get_boolean (EPHY_SETTINGS_LOCKDOWN, EPHY_PREFS_LOCKDOWN_FULLSCREEN)) {
333
    gtk_widget_show (embed->fullscreen_message_label);
334

335 336
    if (embed->fullscreen_message_id)
      g_source_remove (embed->fullscreen_message_id);
337

338
    embed->fullscreen_message_id = g_timeout_add_seconds (5,
Michael Catanzaro's avatar
Michael Catanzaro committed
339 340
                                                          (GSourceFunc)fullscreen_message_label_hide,
                                                          embed);
341
    g_source_set_name_by_id (embed->fullscreen_message_id, "[epiphany] fullscreen_message_label_hide");
342
  }
343 344 345 346 347 348 349 350
}

void
ephy_embed_leaving_fullscreen (EphyEmbed *embed)
{
  fullscreen_message_label_hide (embed);
}

351 352 353 354 355
static void
ephy_embed_dispose (GObject *object)
{
  EphyEmbed *embed = EPHY_EMBED (object);

356 357 358
  if (embed->pop_statusbar_later_source_id) {
    g_source_remove (embed->pop_statusbar_later_source_id);
    embed->pop_statusbar_later_source_id = 0;
359 360
  }

361 362 363 364 365
  if (embed->clear_progress_source_id) {
    g_source_remove (embed->clear_progress_source_id);
    embed->clear_progress_source_id = 0;
  }

366 367 368
  if (embed->delayed_request_source_id) {
    g_source_remove (embed->delayed_request_source_id);
    embed->delayed_request_source_id = 0;
369 370
  }

371 372
  /* Do not listen to status message notifications anymore, if we try
   * to update the statusbar after dispose we might crash. */
373 374 375
  if (embed->status_handler_id) {
    g_signal_handler_disconnect (embed->web_view, embed->status_handler_id);
    embed->status_handler_id = 0;
376 377
  }

378 379 380 381 382
  if (embed->progress_update_handler_id) {
    g_signal_handler_disconnect (embed->web_view, embed->progress_update_handler_id);
    embed->progress_update_handler_id = 0;
  }

383 384 385
  if (embed->fullscreen_message_id) {
    g_source_remove (embed->fullscreen_message_id);
    embed->fullscreen_message_id = 0;
386 387
  }

388
  g_clear_object (&embed->delayed_request);
389
  g_clear_pointer (&embed->delayed_state, webkit_web_view_session_state_unref);
390

391 392 393
  G_OBJECT_CLASS (ephy_embed_parent_class)->dispose (object);
}

394 395 396 397
static void
ephy_embed_finalize (GObject *object)
{
  EphyEmbed *embed = EPHY_EMBED (object);
398
  EphyEmbedShell *shell = ephy_embed_shell_get_default ();
399
  GSList *list;
400

Michael Catanzaro's avatar
Michael Catanzaro committed
401
  g_signal_handlers_disconnect_by_func (shell, ephy_embed_restored_window_cb, embed);
402

403
  list = embed->destroy_on_transition_list;
404 405 406 407
  for (; list; list = list->next) {
    GtkWidget *widget = GTK_WIDGET (list->data);
    g_signal_handlers_disconnect_by_func (widget, remove_from_destroy_list_cb, embed);
  }
408
  g_slist_free (embed->destroy_on_transition_list);
409

410
  for (list = embed->messages; list; list = list->next) {
411 412 413 414 415 416 417
    EphyEmbedStatusbarMsg *msg;

    msg = list->data;
    g_free (msg->text);
    g_slice_free (EphyEmbedStatusbarMsg, msg);
  }

418 419
  g_slist_free (embed->messages);
  embed->messages = NULL;
420

421
  for (list = embed->keys; list; list = list->next)
422 423
    g_free (list->data);

424 425
  g_slist_free (embed->keys);
  embed->keys = NULL;
426

427
  g_free (embed->title);
428

429 430 431
  G_OBJECT_CLASS (ephy_embed_parent_class)->finalize (object);
}

432
static void
Michael Catanzaro's avatar
Michael Catanzaro committed
433 434
ephy_embed_set_property (GObject      *object,
                         guint         prop_id,
435
                         const GValue *value,
Michael Catanzaro's avatar
Michael Catanzaro committed
436
                         GParamSpec   *pspec)
437 438 439
{
  EphyEmbed *embed = EPHY_EMBED (object);

Michael Catanzaro's avatar
Michael Catanzaro committed
440 441 442 443 444 445 446 447 448 449
  switch (prop_id) {
    case PROP_WEB_VIEW:
      embed->web_view = g_value_get_object (value);
      break;
    case PROP_TITLE:
      ephy_embed_set_title (embed, g_value_get_string (value));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
450 451 452 453
  }
}

static void
Michael Catanzaro's avatar
Michael Catanzaro committed
454 455 456
ephy_embed_get_property (GObject    *object,
                         guint       prop_id,
                         GValue     *value,
457 458 459 460
                         GParamSpec *pspec)
{
  EphyEmbed *embed = EPHY_EMBED (object);

Michael Catanzaro's avatar
Michael Catanzaro committed
461 462 463 464 465 466 467 468 469 470
  switch (prop_id) {
    case PROP_WEB_VIEW:
      g_value_set_object (value, ephy_embed_get_web_view (embed));
      break;
    case PROP_TITLE:
      g_value_set_string (value, ephy_embed_get_title (embed));
      break;
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
471 472 473
  }
}

474 475
static void
ephy_embed_find_toolbar_close_cb (EphyFindToolbar *toolbar,
Michael Catanzaro's avatar
Michael Catanzaro committed
476
                                  EphyEmbed       *embed)
477
{
478
  ephy_find_toolbar_close (embed->find_toolbar);
479 480 481 482

  gtk_widget_grab_focus (GTK_WIDGET (embed));
}

483
static void
484
ephy_embed_class_init (EphyEmbedClass *klass)
485
{
Xan Lopez's avatar
Xan Lopez committed
486
  GObjectClass *object_class = (GObjectClass *)klass;
487 488
  GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;

Xan Lopez's avatar
Xan Lopez committed
489
  object_class->constructed = ephy_embed_constructed;
490
  object_class->finalize = ephy_embed_finalize;
491
  object_class->dispose = ephy_embed_dispose;
492 493
  object_class->set_property = ephy_embed_set_property;
  object_class->get_property = ephy_embed_get_property;
494 495
  widget_class->grab_focus = ephy_embed_grab_focus;

496 497 498 499 500 501 502 503 504 505 506 507 508 509 510
  obj_properties[PROP_WEB_VIEW] =
    g_param_spec_object ("web-view",
                         "Web View",
                         "The WebView contained in the embed",
                         EPHY_TYPE_WEB_VIEW,
                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);

  obj_properties[PROP_TITLE] =
    g_param_spec_string ("title",
                         "Title",
                         "The embed's title",
                         NULL,
                         G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);

  g_object_class_install_properties (object_class, LAST_PROP, obj_properties);
511 512
}

513 514
static gboolean
ephy_embed_attach_inspector_cb (WebKitWebInspector *inspector,
Michael Catanzaro's avatar
Michael Catanzaro committed
515
                                EphyEmbed          *embed)
516
{
517 518
  embed->inspector_loaded = TRUE;

519
  return FALSE;
520 521 522 523 524 525 526
}

static gboolean
ephy_embed_close_inspector_cb (WebKitWebInspector *inspector,
                                EphyEmbed          *embed)
{
  embed->inspector_loaded = FALSE;
527 528 529

  return TRUE;
}
Xan Lopez's avatar
Xan Lopez committed
530

531 532
static void
ephy_embed_set_fullscreen_message (EphyEmbed *embed,
Michael Catanzaro's avatar
Michael Catanzaro committed
533
                                   gboolean   is_html5_fullscreen)
534 535 536 537
{
  char *message;

  /* Translators: 'ESC' and 'F11' are keyboard keys. */
538
  message = g_strdup_printf (_("Press %s to exit fullscreen"), is_html5_fullscreen ? _("ESC") : _("F11"));
539
  gtk_label_set_text (GTK_LABEL (embed->fullscreen_message_label),
540 541 542 543 544 545
                      message);
  g_free (message);
}

static gboolean
entering_fullscreen_cb (WebKitWebView *web_view,
Michael Catanzaro's avatar
Michael Catanzaro committed
546
                        EphyEmbed     *embed)
547 548 549 550 551 552 553
{
  ephy_embed_set_fullscreen_message (embed, TRUE);
  return FALSE;
}

static gboolean
leaving_fullscreen_cb (WebKitWebView *web_view,
Michael Catanzaro's avatar
Michael Catanzaro committed
554
                       EphyEmbed     *embed)
555 556 557 558 559
{
  ephy_embed_set_fullscreen_message (embed, FALSE);
  return FALSE;
}

560 561 562 563 564
static gboolean
pop_statusbar_later_cb (gpointer data)
{
  EphyEmbed *embed = EPHY_EMBED (data);

565 566
  ephy_embed_statusbar_pop (embed, embed->tab_message_id);
  embed->pop_statusbar_later_source_id = 0;
567 568 569
  return FALSE;
}

570 571 572 573 574 575 576
static void
status_message_notify_cb (EphyWebView *view, GParamSpec *pspec, EphyEmbed *embed)
{
  const char *message;

  message = ephy_web_view_get_status_message (view);

577
  if (message) {
578 579 580
    if (embed->pop_statusbar_later_source_id) {
      g_source_remove (embed->pop_statusbar_later_source_id);
      embed->pop_statusbar_later_source_id = 0;
581
    }
582

583 584
    ephy_embed_statusbar_pop (embed, embed->tab_message_id);
    ephy_embed_statusbar_push (embed, embed->tab_message_id, message);
585 586
  } else {
    /* A short timeout before hiding the statusbar ensures that while moving
Michael Catanzaro's avatar
Michael Catanzaro committed
587
       over a series of links, the overlay widget doesn't flicker on and off. */
588 589 590
    if (embed->pop_statusbar_later_source_id == 0) {
      embed->pop_statusbar_later_source_id = g_timeout_add (250, pop_statusbar_later_cb, embed);
      g_source_set_name_by_id (embed->pop_statusbar_later_source_id, "[epiphany] pop_statusbar_later_cb");
591
    }
592
  }
593 594
}

595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637
static gboolean
clear_progress_cb (EphyEmbed *embed)
{
  gtk_widget_hide (embed->progress);
  embed->clear_progress_source_id = 0;

  return FALSE;
}

static void
progress_update (EphyWebView *view, GParamSpec *pspec, EphyEmbed *embed)
{
  gdouble progress;
  gboolean loading;
  const char *uri;

  if (embed->clear_progress_source_id) {
    g_source_remove (embed->clear_progress_source_id);
    embed->clear_progress_source_id = 0;
  }

  uri = webkit_web_view_get_uri (embed->web_view);
  if (!uri || g_str_has_prefix (uri, "ephy-about:") ||
      g_str_has_prefix (uri, "about:")) {
    gtk_widget_hide (embed->progress);
    return;
  }

  progress = webkit_web_view_get_estimated_load_progress (embed->web_view);
  loading = ephy_web_view_is_loading (EPHY_WEB_VIEW (embed->web_view));

  if (progress == 1.0 || !loading) {
    embed->clear_progress_source_id = g_timeout_add (500,
                                                     (GSourceFunc)clear_progress_cb,
                                                     embed);
    g_source_set_name_by_id (embed->clear_progress_source_id, "[epiphany] clear_progress_cb");
  } else
    gtk_widget_show (embed->progress);

  gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (embed->progress),
                                 (loading || progress == 1.0) ? progress : 0.0);
}

638 639
static gboolean
load_delayed_request_if_mapped (gpointer user_data)
640
{
641
  EphyEmbed *embed = EPHY_EMBED (user_data);
642
  EphyWebView *web_view;
643
  WebKitBackForwardListItem *item;
644

645
  embed->delayed_request_source_id = 0;
646

647 648
  if (!gtk_widget_get_mapped (GTK_WIDGET (embed)))
    return G_SOURCE_REMOVE;
649

650
  web_view = ephy_embed_get_web_view (embed);
651 652 653 654 655 656 657 658 659
  if (embed->delayed_state)
    webkit_web_view_restore_session_state (WEBKIT_WEB_VIEW (web_view), embed->delayed_state);

  item = webkit_back_forward_list_get_current_item (webkit_web_view_get_back_forward_list (WEBKIT_WEB_VIEW (web_view)));
  if (item)
    webkit_web_view_go_to_back_forward_list_item (WEBKIT_WEB_VIEW (web_view), item);
  else
    ephy_web_view_load_request (web_view, embed->delayed_request);

660
  g_clear_object (&embed->delayed_request);
661
  g_clear_pointer (&embed->delayed_state, webkit_web_view_session_state_unref);
662 663 664 665 666

  /* This is to allow UI elements watching load status to show that the page is
   * loading as soon as possible.
   */
  g_signal_emit_by_name (web_view, "load-changed", WEBKIT_LOAD_STARTED);
667 668 669 670 671 672 673

  return G_SOURCE_REMOVE;
}

static void
ephy_embed_maybe_load_delayed_request (EphyEmbed *embed)
{
674
  if (!embed->delayed_request || embed->delayed_request_source_id != 0)
675 676 677 678 679 680
    return;

  /* Add a very small delay before loading the request, so that if the user
   * is scrolling rapidly through a bunch of delayed tabs, we don't start
   * loading them all.
   */
681 682
  embed->delayed_request_source_id = g_timeout_add (300, load_delayed_request_if_mapped, embed);
  g_source_set_name_by_id (embed->delayed_request_source_id, "[epiphany] load_delayed_request_if_mapped");
683 684 685 686 687 688 689 690 691 692 693 694 695 696
}

static void
ephy_embed_restored_window_cb (EphyEmbedShell *shell, EphyEmbed *embed)
{
  if (!gtk_widget_get_mapped (GTK_WIDGET (embed)))
    return;

  ephy_embed_maybe_load_delayed_request (embed);
}

static void
ephy_embed_mapped_cb (GtkWidget *widget, gpointer data)
{
Michael Catanzaro's avatar
Michael Catanzaro committed
697
  ephy_embed_maybe_load_delayed_request ((EphyEmbed *)widget);
698 699
}

700
static void
Xan Lopez's avatar
Xan Lopez committed
701
ephy_embed_constructed (GObject *object)
702
{
Michael Catanzaro's avatar
Michael Catanzaro committed
703
  EphyEmbed *embed = (EphyEmbed *)object;
704
  EphyEmbedShell *shell = ephy_embed_shell_get_default ();
705
  GtkWidget *paned;
Xan Lopez's avatar
Xan Lopez committed
706
  WebKitWebInspector *inspector;
707

708 709 710 711 712 713
  g_signal_connect (shell, "window-restored",
                    G_CALLBACK (ephy_embed_restored_window_cb), embed);

  g_signal_connect (embed, "map",
                    G_CALLBACK (ephy_embed_mapped_cb), NULL);

714
  /* Skeleton */
715
  embed->overlay = gtk_overlay_new ();
716

717
  gtk_widget_add_events (embed->overlay,
718 719
                         GDK_ENTER_NOTIFY_MASK |
                         GDK_LEAVE_NOTIFY_MASK);
720
  gtk_container_add (GTK_CONTAINER (embed->overlay), GTK_WIDGET (embed->web_view));
721

722
  /* Floating message popup for fullscreen mode. */
723 724 725 726 727
  embed->fullscreen_message_label = gtk_label_new (NULL);
  gtk_widget_set_name (embed->fullscreen_message_label, "fullscreen-popup");
  gtk_widget_set_halign (embed->fullscreen_message_label, GTK_ALIGN_CENTER);
  gtk_widget_set_valign (embed->fullscreen_message_label, GTK_ALIGN_CENTER);
  gtk_widget_set_no_show_all (embed->fullscreen_message_label, TRUE);
728
  gtk_overlay_add_overlay (GTK_OVERLAY (embed->overlay), embed->fullscreen_message_label);
729 730
  ephy_embed_set_fullscreen_message (embed, FALSE);

731
  /* statusbar is hidden by default */
732 733 734 735
  embed->floating_bar = nautilus_floating_bar_new (NULL, NULL, FALSE);
  gtk_widget_set_halign (embed->floating_bar, GTK_ALIGN_START);
  gtk_widget_set_valign (embed->floating_bar, GTK_ALIGN_END);
  gtk_widget_set_no_show_all (embed->floating_bar, TRUE);
736

737
  gtk_overlay_add_overlay (GTK_OVERLAY (embed->overlay), embed->floating_bar);
738

739 740 741 742 743 744 745
  embed->progress = gtk_progress_bar_new ();
  gtk_style_context_add_class (gtk_widget_get_style_context (embed->progress),
                               GTK_STYLE_CLASS_OSD);
  gtk_widget_set_halign (embed->progress, GTK_ALIGN_FILL);
  gtk_widget_set_valign (embed->progress, GTK_ALIGN_START);
  gtk_overlay_add_overlay (GTK_OVERLAY (embed->overlay), embed->progress);

746 747
  embed->find_toolbar = ephy_find_toolbar_new (embed->web_view);
  g_signal_connect (embed->find_toolbar, "close",
748 749 750 751
                    G_CALLBACK (ephy_embed_find_toolbar_close_cb),
                    embed);

  gtk_box_pack_start (GTK_BOX (embed),
752
                      GTK_WIDGET (embed->find_toolbar),
753 754
                      FALSE, FALSE, 0);

755
  paned = GTK_WIDGET (embed->paned);
756

757 758
  embed->progress_update_handler_id = g_signal_connect (embed->web_view, "notify::estimated-load-progress",
                                                        G_CALLBACK (progress_update), object);
759
  gtk_paned_pack1 (GTK_PANED (paned), GTK_WIDGET (embed->overlay),
760 761 762
                   TRUE, FALSE);

  gtk_box_pack_start (GTK_BOX (embed),
763
                      GTK_WIDGET (embed->top_widgets_vbox),
764
                      FALSE, FALSE, 0);
765
  gtk_box_pack_start (GTK_BOX (embed), paned, TRUE, TRUE, 0);
766

767 768
  gtk_widget_show (GTK_WIDGET (embed->top_widgets_vbox));
  gtk_widget_show (GTK_WIDGET (embed->web_view));
769
  gtk_widget_show_all (paned);
770

771
  g_object_connect (embed->web_view,
772
                    "signal::notify::title", G_CALLBACK (web_view_title_changed_cb), embed,
773 774 775 776
                    "signal::load-changed", G_CALLBACK (load_changed_cb), embed,
                    "signal::enter-fullscreen", G_CALLBACK (entering_fullscreen_cb), embed,
                    "signal::leave-fullscreen", G_CALLBACK (leaving_fullscreen_cb), embed,
                    NULL);
777

778 779 780
  embed->status_handler_id = g_signal_connect (embed->web_view, "notify::status-message",
                                               G_CALLBACK (status_message_notify_cb),
                                               embed);
781

782
  /* The inspector */
783
  inspector = webkit_web_view_get_inspector (embed->web_view);
Xan Lopez's avatar
Xan Lopez committed
784

785 786 787
  g_signal_connect (inspector, "attach",
                    G_CALLBACK (ephy_embed_attach_inspector_cb),
                    embed);
788 789 790
  g_signal_connect (inspector, "closed",
                    G_CALLBACK (ephy_embed_close_inspector_cb),
                    embed);
791 792
}

Xan Lopez's avatar
Xan Lopez committed
793 794 795
static void
ephy_embed_init (EphyEmbed *embed)
{
796 797 798
  gtk_orientable_set_orientation (GTK_ORIENTABLE (embed),
                                  GTK_ORIENTATION_VERTICAL);

799 800 801 802 803
  embed->paned = GTK_PANED (gtk_paned_new (GTK_ORIENTATION_VERTICAL));
  embed->top_widgets_vbox = GTK_BOX (gtk_box_new (GTK_ORIENTATION_VERTICAL, 0));
  embed->seq_context_id = 1;
  embed->seq_message_id = 1;
  embed->tab_message_id = ephy_embed_statusbar_get_context_id (embed, EPHY_EMBED_STATUSBAR_TAB_MESSAGE_CONTEXT_DESCRIPTION);
804
  embed->inspector_loaded = FALSE;
Xan Lopez's avatar
Xan Lopez committed
805 806
}

807 808 809
/**
 * ephy_embed_get_web_view:
 * @embed: and #EphyEmbed
Michael Catanzaro's avatar
Michael Catanzaro committed
810
 *
811
 * Returns the #EphyWebView wrapped by @embed.
Michael Catanzaro's avatar
Michael Catanzaro committed
812
 *
813
 * Returns: (transfer none): an #EphyWebView
814
 **/
Michael Catanzaro's avatar
Michael Catanzaro committed
815
EphyWebView *
816 817
ephy_embed_get_web_view (EphyEmbed *embed)
{
818
  g_assert (EPHY_IS_EMBED (embed));
819

820
  return EPHY_WEB_VIEW (embed->web_view);
821
}
822

823 824 825
/**
 * ephy_embed_get_find_toolbar:
 * @embed: and #EphyEmbed
Michael Catanzaro's avatar
Michael Catanzaro committed
826
 *
827
 * Returns the #EphyFindToolbar wrapped by @embed.
Michael Catanzaro's avatar
Michael Catanzaro committed
828
 *
829 830
 * Returns: (transfer none): an #EphyFindToolbar
 **/
Michael Catanzaro's avatar
Michael Catanzaro committed
831
EphyFindToolbar *
832 833
ephy_embed_get_find_toolbar (EphyEmbed *embed)
{
834
  g_assert (EPHY_IS_EMBED (embed));
835

836
  return EPHY_FIND_TOOLBAR (embed->find_toolbar);
837 838 839
}


840 841 842 843
/**
 * ephy_embed_add_top_widget:
 * @embed: an #EphyEmbed
 * @widget: a #GtkWidget
844
 * @policy: whether the widget be automatically
845 846 847 848 849
 * destroyed on page transitions
 *
 * Adds a #GtkWidget to the top of the embed.
 */
void
850 851 852
ephy_embed_add_top_widget (EphyEmbed                *embed,
                           GtkWidget                *widget,
                           EphyEmbedTopWidgetPolicy  policy)
853 854 855
{
  GSList *list;

856
  if (policy == EPHY_EMBED_TOP_WIDGET_POLICY_DESTROY_ON_TRANSITION) {
857
    list = embed->destroy_on_transition_list;
858
    list = g_slist_prepend (list, widget);
859
    embed->destroy_on_transition_list = list;
860 861 862 863

    g_signal_connect (widget, "destroy", G_CALLBACK (remove_from_destroy_list_cb), embed);
  }

864
  gtk_box_pack_end (embed->top_widgets_vbox,
865
                    GTK_WIDGET (widget), FALSE, FALSE, 0);
866 867 868 869 870 871 872 873
}

/**
 * ephy_embed_remove_top_widget:
 * @embed: an #EphyEmbed
 * @widget: a #GtkWidget
 *
 * Removes an #GtkWidget from the top of the embed. The #GtkWidget
874
 * must have been added using ephy_embed_add_top_widget(), and not
875 876 877 878 879 880
 * have been removed by other means. See gtk_container_remove() for
 * details.
 */
void
ephy_embed_remove_top_widget (EphyEmbed *embed, GtkWidget *widget)
{
881
  if (g_slist_find (embed->destroy_on_transition_list, widget)) {
882 883 884
    GSList *list;
    g_signal_handlers_disconnect_by_func (widget, remove_from_destroy_list_cb, embed);

885
    list = embed->destroy_on_transition_list;
886
    list = g_slist_remove (list, widget);
887
    embed->destroy_on_transition_list = list;
888 889
  }

890
  gtk_container_remove (GTK_CONTAINER (embed->top_widgets_vbox),
891 892
                        GTK_WIDGET (widget));
}
893

894 895 896 897
/**
 * ephy_embed_set_delayed_load_request:
 * @embed: a #EphyEmbed
 * @request: a #WebKitNetworkRequest
898
 * @state: (nullable): a #WebKitWebViewSessionState
899 900 901 902
 *
 * Sets the #WebKitNetworkRequest that should be loaded when the tab this embed
 * is on is switched to.
 */
903
void
904
ephy_embed_set_delayed_load_request (EphyEmbed *embed, WebKitURIRequest *request, WebKitWebViewSessionState *state)
905
{
906 907
  g_assert (EPHY_IS_EMBED (embed));
  g_assert (WEBKIT_IS_URI_REQUEST (request));
908

909
  g_clear_pointer (&embed->delayed_state, webkit_web_view_session_state_unref);
910
  g_clear_object (&embed->delayed_request);
911

912 913 914
  embed->delayed_request = g_object_ref (request);
  if (state)
    embed->delayed_state = webkit_web_view_session_state_ref (state);
915 916 917 918 919 920 921 922 923 924 925 926 927
}

/**
 * ephy_embed_has_load_pending:
 * @embed: a #EphyEmbed
 *
 * Checks whether a load has been delayed for this #EphyEmbed.
 *
 * Returns: %TRUE or %FALSE
 */
gboolean
ephy_embed_has_load_pending (EphyEmbed *embed)
{
928
  g_assert (EPHY_IS_EMBED (embed));
929

930
  return !!embed->delayed_request;
931 932
}

933 934 935
const char *
ephy_embed_get_title (EphyEmbed *embed)
{
936
  g_assert (EPHY_IS_EMBED (embed));
937

938
  return embed->title;
939
}
940 941 942 943 944 945 946 947 948 949 950 951 952


/**
 * ephy_embed_inspector_is_loaded:
 * @embed: a #EphyEmbed
 *
 * Checks if the Web Inspector is loaded in this #EphyEmbed.
 *
 * Returns: %TRUE or %FALSE
 */
gboolean
ephy_embed_inspector_is_loaded (EphyEmbed *embed)
{
953
  g_assert (EPHY_IS_EMBED (embed));
954 955 956

  return embed->inspector_loaded;
}
957 958

void
959
ephy_embed_attach_notification_container (EphyEmbed *embed)
960
{
961
  EphyNotificationContainer *container;
962

963
  g_assert (EPHY_IS_EMBED (embed));
964

965
  container = ephy_notification_container_get_default ();
966 967
  if (gtk_widget_get_parent (GTK_WIDGET (container)) == NULL)
    gtk_overlay_add_overlay (GTK_OVERLAY (embed->overlay), GTK_WIDGET (container));
968 969 970
}

void
971
ephy_embed_detach_notification_container (EphyEmbed *embed)
972
{
973
  EphyNotificationContainer *container;
974

975
  g_assert (EPHY_IS_EMBED (embed));
976

977
  container = ephy_notification_container_get_default ();
978 979 980 981 982
  if (gtk_widget_get_parent (GTK_WIDGET (container)) == embed->overlay) {
    /* Since the overlay container will own the one and only reference to the
     * notification widget, removing it from the container will destroy the
     * singleton. To prevent this, add a reference to it before removing it
     * from the container. */
983
    gtk_container_remove (GTK_CONTAINER (embed->overlay), g_object_ref (GTK_WIDGET (container)));
984
  }
985
}