ephy-embed.c 30.1 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
  gboolean progress_bar_enabled;
91 92
};

93 94
G_DEFINE_TYPE (EphyEmbed, ephy_embed, GTK_TYPE_BOX)

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

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

/* 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.
126 127 128 129 130 131 132 133 134 135 136 137 138 139
 *
 * 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;

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

  /* 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) {
148
    id = embed->seq_context_id++;
149
    g_object_set_data_full (G_OBJECT (embed), string, GUINT_TO_POINTER (id), NULL);
150
    embed->keys = g_slist_prepend (embed->keys, string);
151 152 153 154 155 156
  } else
    g_free (string);

  return id;
}

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

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

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

174
  ephy_embed_set_statusbar_label (embed, text);
175 176 177 178 179 180 181
}

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

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

186
  msg = g_new (EphyEmbedStatusbarMsg, 1);
187 188
  msg->text = g_strdup (text);
  msg->context_id = context_id;
189
  msg->message_id = embed->seq_message_id++;
190

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

  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;

206 207
  g_assert (EPHY_IS_EMBED (embed));
  g_assert (context_id != 0);
208

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

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

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

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

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

235 236 237 238 239 240 241 242 243 244 245 246 247
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;
}

248
static void
Michael Catanzaro's avatar
Michael Catanzaro committed
249
ephy_embed_set_title (EphyEmbed  *embed,
250 251 252 253 254 255 256 257 258 259 260
                      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;

261
    address = ephy_web_view_get_address (EPHY_WEB_VIEW (embed->web_view));
262 263 264 265 266
    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);
267
      new_title = g_strdup (_(BLANK_PAGE_TITLE));
268 269 270
    }
  }

271 272
  g_free (embed->title);
  embed->title = ephy_string_shorten (new_title, MAX_TITLE_LENGTH);
273

274
  g_object_notify_by_pspec (G_OBJECT (embed), obj_properties[PROP_TITLE]);
275 276 277 278
}

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

285
static void
Michael Catanzaro's avatar
Michael Catanzaro committed
286
load_changed_cb (WebKitWebView  *web_view,
287
                 WebKitLoadEvent load_event,
Michael Catanzaro's avatar
Michael Catanzaro committed
288
                 EphyEmbed      *embed)
289
{
290
  switch (load_event) {
Michael Catanzaro's avatar
Michael Catanzaro committed
291 292 293 294 295 296 297 298 299 300 301 302 303
    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;
304
  }
305
}
306

307 308 309 310 311
static void
ephy_embed_grab_focus (GtkWidget *widget)
{
  GtkWidget *child;

312
  child = GTK_WIDGET (ephy_embed_get_web_view (EPHY_EMBED (widget)));
313 314 315 316 317

  if (child)
    gtk_widget_grab_focus (child);
}

318 319 320 321

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

  return FALSE;
}

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

337 338
    if (embed->fullscreen_message_id)
      g_source_remove (embed->fullscreen_message_id);
339

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

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

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

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

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

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

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

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

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

390
  g_clear_object (&embed->delayed_request);
391
  g_clear_pointer (&embed->delayed_state, webkit_web_view_session_state_unref);
392

393 394 395
  G_OBJECT_CLASS (ephy_embed_parent_class)->dispose (object);
}

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

Michael Catanzaro's avatar
Michael Catanzaro committed
403
  g_signal_handlers_disconnect_by_func (shell, ephy_embed_restored_window_cb, embed);
404

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

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

    msg = list->data;
    g_free (msg->text);
417
    g_free (msg);
418 419
  }

420 421
  g_slist_free (embed->messages);
  embed->messages = NULL;
422

423
  for (list = embed->keys; list; list = list->next)
424 425
    g_free (list->data);

426 427
  g_slist_free (embed->keys);
  embed->keys = NULL;
428

429
  g_free (embed->title);
430

431 432 433
  G_OBJECT_CLASS (ephy_embed_parent_class)->finalize (object);
}

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

Michael Catanzaro's avatar
Michael Catanzaro committed
442 443 444 445 446 447 448
  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;
449 450 451
    case PROP_PROGRESS_BAR_ENABLED:
      embed->progress_bar_enabled = g_value_get_boolean (value);
      break;
Michael Catanzaro's avatar
Michael Catanzaro committed
452 453 454
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
455 456 457 458
  }
}

static void
Michael Catanzaro's avatar
Michael Catanzaro committed
459 460 461
ephy_embed_get_property (GObject    *object,
                         guint       prop_id,
                         GValue     *value,
462 463 464 465
                         GParamSpec *pspec)
{
  EphyEmbed *embed = EPHY_EMBED (object);

Michael Catanzaro's avatar
Michael Catanzaro committed
466 467 468 469 470 471 472
  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;
473 474 475
    case PROP_PROGRESS_BAR_ENABLED:
      g_value_set_boolean (value, embed->progress_bar_enabled);
      break;
Michael Catanzaro's avatar
Michael Catanzaro committed
476 477 478
    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
      break;
479 480 481
  }
}

482 483
static void
ephy_embed_find_toolbar_close_cb (EphyFindToolbar *toolbar,
Michael Catanzaro's avatar
Michael Catanzaro committed
484
                                  EphyEmbed       *embed)
485
{
486
  ephy_find_toolbar_close (embed->find_toolbar);
487 488 489 490

  gtk_widget_grab_focus (GTK_WIDGET (embed));
}

491
static void
492
ephy_embed_class_init (EphyEmbedClass *klass)
493
{
Xan Lopez's avatar
Xan Lopez committed
494
  GObjectClass *object_class = (GObjectClass *)klass;
495 496
  GtkWidgetClass *widget_class = (GtkWidgetClass *)klass;

Xan Lopez's avatar
Xan Lopez committed
497
  object_class->constructed = ephy_embed_constructed;
498
  object_class->finalize = ephy_embed_finalize;
499
  object_class->dispose = ephy_embed_dispose;
500 501
  object_class->set_property = ephy_embed_set_property;
  object_class->get_property = ephy_embed_get_property;
502 503
  widget_class->grab_focus = ephy_embed_grab_focus;

504 505 506 507 508 509 510 511 512 513 514 515 516 517
  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);

518 519 520 521 522 523 524
  obj_properties[PROP_PROGRESS_BAR_ENABLED] =
    g_param_spec_boolean ("progress-bar-enabled",
                          "Progress bar",
                          "Whether to show progress bar within embed",
                          TRUE,
                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);

525
  g_object_class_install_properties (object_class, LAST_PROP, obj_properties);
526 527
}

528 529
static gboolean
ephy_embed_attach_inspector_cb (WebKitWebInspector *inspector,
Michael Catanzaro's avatar
Michael Catanzaro committed
530
                                EphyEmbed          *embed)
531
{
532 533
  embed->inspector_loaded = TRUE;

534
  return FALSE;
535 536 537 538 539 540 541
}

static gboolean
ephy_embed_close_inspector_cb (WebKitWebInspector *inspector,
                                EphyEmbed          *embed)
{
  embed->inspector_loaded = FALSE;
542 543 544

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

546 547
static void
ephy_embed_set_fullscreen_message (EphyEmbed *embed,
Michael Catanzaro's avatar
Michael Catanzaro committed
548
                                   gboolean   is_html5_fullscreen)
549 550 551 552
{
  char *message;

  /* Translators: 'ESC' and 'F11' are keyboard keys. */
553
  message = g_strdup_printf (_("Press %s to exit fullscreen"), is_html5_fullscreen ? _("ESC") : _("F11"));
554
  gtk_label_set_text (GTK_LABEL (embed->fullscreen_message_label),
555 556 557 558 559 560
                      message);
  g_free (message);
}

static gboolean
entering_fullscreen_cb (WebKitWebView *web_view,
Michael Catanzaro's avatar
Michael Catanzaro committed
561
                        EphyEmbed     *embed)
562 563 564 565 566 567 568
{
  ephy_embed_set_fullscreen_message (embed, TRUE);
  return FALSE;
}

static gboolean
leaving_fullscreen_cb (WebKitWebView *web_view,
Michael Catanzaro's avatar
Michael Catanzaro committed
569
                       EphyEmbed     *embed)
570 571 572 573 574
{
  ephy_embed_set_fullscreen_message (embed, FALSE);
  return FALSE;
}

575 576 577 578 579
static gboolean
pop_statusbar_later_cb (gpointer data)
{
  EphyEmbed *embed = EPHY_EMBED (data);

580 581
  ephy_embed_statusbar_pop (embed, embed->tab_message_id);
  embed->pop_statusbar_later_source_id = 0;
582 583 584
  return FALSE;
}

585 586 587 588 589 590 591
static void
status_message_notify_cb (EphyWebView *view, GParamSpec *pspec, EphyEmbed *embed)
{
  const char *message;

  message = ephy_web_view_get_status_message (view);

592
  if (message) {
593 594 595
    if (embed->pop_statusbar_later_source_id) {
      g_source_remove (embed->pop_statusbar_later_source_id);
      embed->pop_statusbar_later_source_id = 0;
596
    }
597

598 599
    ephy_embed_statusbar_pop (embed, embed->tab_message_id);
    ephy_embed_statusbar_push (embed, embed->tab_message_id, message);
600 601
  } else {
    /* A short timeout before hiding the statusbar ensures that while moving
Michael Catanzaro's avatar
Michael Catanzaro committed
602
       over a series of links, the overlay widget doesn't flicker on and off. */
603 604 605
    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");
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 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652
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);
}

653 654
static gboolean
load_delayed_request_if_mapped (gpointer user_data)
655
{
656
  EphyEmbed *embed = EPHY_EMBED (user_data);
657
  EphyWebView *web_view;
658
  WebKitBackForwardListItem *item;
659

660
  embed->delayed_request_source_id = 0;
661

662 663
  if (!gtk_widget_get_mapped (GTK_WIDGET (embed)))
    return G_SOURCE_REMOVE;
664

665
  web_view = ephy_embed_get_web_view (embed);
666 667 668 669 670 671 672 673 674
  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);

675
  g_clear_object (&embed->delayed_request);
676
  g_clear_pointer (&embed->delayed_state, webkit_web_view_session_state_unref);
677 678 679 680 681

  /* 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);
682 683 684 685 686 687 688

  return G_SOURCE_REMOVE;
}

static void
ephy_embed_maybe_load_delayed_request (EphyEmbed *embed)
{
689
  if (!embed->delayed_request || embed->delayed_request_source_id != 0)
690 691 692 693 694 695
    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.
   */
696 697
  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");
698 699 700 701 702 703 704 705 706 707 708 709 710 711
}

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
712
  ephy_embed_maybe_load_delayed_request ((EphyEmbed *)widget);
713 714
}

715
static void
Xan Lopez's avatar
Xan Lopez committed
716
ephy_embed_constructed (GObject *object)
717
{
Michael Catanzaro's avatar
Michael Catanzaro committed
718
  EphyEmbed *embed = (EphyEmbed *)object;
719
  EphyEmbedShell *shell = ephy_embed_shell_get_default ();
720
  GtkWidget *paned;
Xan Lopez's avatar
Xan Lopez committed
721
  WebKitWebInspector *inspector;
722

723 724 725 726 727 728
  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);

729
  /* Skeleton */
730
  embed->overlay = gtk_overlay_new ();
731

732
  gtk_widget_add_events (embed->overlay,
733 734
                         GDK_ENTER_NOTIFY_MASK |
                         GDK_LEAVE_NOTIFY_MASK);
735
  gtk_container_add (GTK_CONTAINER (embed->overlay), GTK_WIDGET (embed->web_view));
736

737
  /* Floating message popup for fullscreen mode. */
738 739 740 741 742
  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);
743
  gtk_overlay_add_overlay (GTK_OVERLAY (embed->overlay), embed->fullscreen_message_label);
744 745
  ephy_embed_set_fullscreen_message (embed, FALSE);

746
  /* statusbar is hidden by default */
747 748 749 750
  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);
751

752
  gtk_overlay_add_overlay (GTK_OVERLAY (embed->overlay), embed->floating_bar);
753
  gtk_overlay_set_overlay_pass_through (GTK_OVERLAY (embed->overlay), embed->floating_bar, TRUE);
754

755 756 757 758 759 760 761 762
  if (embed->progress_bar_enabled) {
    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);
  }
763

764 765
  embed->find_toolbar = ephy_find_toolbar_new (embed->web_view);
  g_signal_connect (embed->find_toolbar, "close",
766 767 768 769
                    G_CALLBACK (ephy_embed_find_toolbar_close_cb),
                    embed);

  gtk_box_pack_start (GTK_BOX (embed),
770
                      GTK_WIDGET (embed->find_toolbar),
771 772
                      FALSE, FALSE, 0);

773
  paned = GTK_WIDGET (embed->paned);
774

775 776 777 778
  if (embed->progress_bar_enabled)
    embed->progress_update_handler_id = g_signal_connect (embed->web_view, "notify::estimated-load-progress",
                                                          G_CALLBACK (progress_update), object);

779
  gtk_paned_pack1 (GTK_PANED (paned), GTK_WIDGET (embed->overlay),
780 781 782
                   TRUE, FALSE);

  gtk_box_pack_start (GTK_BOX (embed),
783
                      GTK_WIDGET (embed->top_widgets_vbox),
784
                      FALSE, FALSE, 0);
785
  gtk_box_pack_start (GTK_BOX (embed), paned, TRUE, TRUE, 0);
786

787 788
  gtk_widget_show (GTK_WIDGET (embed->top_widgets_vbox));
  gtk_widget_show (GTK_WIDGET (embed->web_view));
789
  gtk_widget_show_all (paned);
790

791
  g_object_connect (embed->web_view,
792
                    "signal::notify::title", G_CALLBACK (web_view_title_changed_cb), embed,
793 794 795 796
                    "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);
797

798 799 800
  embed->status_handler_id = g_signal_connect (embed->web_view, "notify::status-message",
                                               G_CALLBACK (status_message_notify_cb),
                                               embed);
801

802
  /* The inspector */
803
  inspector = webkit_web_view_get_inspector (embed->web_view);
Xan Lopez's avatar
Xan Lopez committed
804

805 806 807
  g_signal_connect (inspector, "attach",
                    G_CALLBACK (ephy_embed_attach_inspector_cb),
                    embed);
808 809 810
  g_signal_connect (inspector, "closed",
                    G_CALLBACK (ephy_embed_close_inspector_cb),
                    embed);
811 812 813 814 815 816 817 818 819 820 821 822 823 824

  if (webkit_web_view_is_controlled_by_automation (embed->web_view)) {
    GtkWidget *info_bar;
    GtkWidget *label;

    info_bar = gtk_info_bar_new ();
    gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_INFO);
    label = gtk_label_new (_("Web is being controlled by automation"));
    gtk_box_pack_start (GTK_BOX (gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar))), label, FALSE, FALSE, 0);
    gtk_widget_show (label);

    ephy_embed_add_top_widget (embed, info_bar, EPHY_EMBED_TOP_WIDGET_POLICY_RETAIN_ON_TRANSITION);
    gtk_widget_show (info_bar);
  }
825 826
}

Xan Lopez's avatar
Xan Lopez committed
827 828 829
static void
ephy_embed_init (EphyEmbed *embed)
{
830 831 832
  gtk_orientable_set_orientation (GTK_ORIENTABLE (embed),
                                  GTK_ORIENTATION_VERTICAL);

833 834 835 836 837
  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);
838
  embed->inspector_loaded = FALSE;
Xan Lopez's avatar
Xan Lopez committed
839 840
}

841 842 843
/**
 * ephy_embed_get_web_view:
 * @embed: and #EphyEmbed
Michael Catanzaro's avatar
Michael Catanzaro committed
844
 *
845
 * Returns the #EphyWebView wrapped by @embed.
Michael Catanzaro's avatar
Michael Catanzaro committed
846
 *
847
 * Returns: (transfer none): an #EphyWebView
848
 **/
Michael Catanzaro's avatar
Michael Catanzaro committed
849
EphyWebView *
850 851
ephy_embed_get_web_view (EphyEmbed *embed)
{
852
  g_assert (EPHY_IS_EMBED (embed));
853

854
  return EPHY_WEB_VIEW (embed->web_view);
855
}
856

857 858 859
/**
 * ephy_embed_get_find_toolbar:
 * @embed: and #EphyEmbed
Michael Catanzaro's avatar
Michael Catanzaro committed
860
 *
861
 * Returns the #EphyFindToolbar wrapped by @embed.
Michael Catanzaro's avatar
Michael Catanzaro committed
862
 *
863 864
 * Returns: (transfer none): an #EphyFindToolbar
 **/
Michael Catanzaro's avatar
Michael Catanzaro committed
865
EphyFindToolbar *
866 867
ephy_embed_get_find_toolbar (EphyEmbed *embed)
{
868
  g_assert (EPHY_IS_EMBED (embed));
869

870
  return EPHY_FIND_TOOLBAR (embed->find_toolbar);
871 872 873
}


874 875 876 877
/**
 * ephy_embed_add_top_widget:
 * @embed: an #EphyEmbed
 * @widget: a #GtkWidget
878
 * @policy: whether the widget be automatically
879 880 881 882 883
 * destroyed on page transitions
 *
 * Adds a #GtkWidget to the top of the embed.
 */
void
884 885 886
ephy_embed_add_top_widget (EphyEmbed                *embed,
                           GtkWidget                *widget,
                           EphyEmbedTopWidgetPolicy  policy)
887 888 889
{
  GSList *list;

890
  if (policy == EPHY_EMBED_TOP_WIDGET_POLICY_DESTROY_ON_TRANSITION) {
891
    list = embed->destroy_on_transition_list;
892
    list = g_slist_prepend (list, widget);
893
    embed->destroy_on_transition_list = list;
894 895 896 897

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

898
  gtk_box_pack_end (embed->top_widgets_vbox,
899
                    GTK_WIDGET (widget), FALSE, FALSE, 0);
900 901 902 903 904 905 906 907
}

/**
 * ephy_embed_remove_top_widget:
 * @embed: an #EphyEmbed
 * @widget: a #GtkWidget
 *
 * Removes an #GtkWidget from the top of the embed. The #GtkWidget
908
 * must have been added using ephy_embed_add_top_widget(), and not
909 910 911 912 913 914
 * have been removed by other means. See gtk_container_remove() for
 * details.
 */
void
ephy_embed_remove_top_widget (EphyEmbed *embed, GtkWidget *widget)
{
915
  if (g_slist_find (embed->destroy_on_transition_list, widget)) {
916 917 918
    GSList *list;
    g_signal_handlers_disconnect_by_func (widget, remove_from_destroy_list_cb, embed);

919
    list = embed->destroy_on_transition_list;
920
    list = g_slist_remove (list, widget);
921
    embed->destroy_on_transition_list = list;
922 923
  }

924
  gtk_container_remove (GTK_CONTAINER (embed->top_widgets_vbox),
925 926
                        GTK_WIDGET (widget));
}
927

928 929 930 931
/**
 * ephy_embed_set_delayed_load_request:
 * @embed: a #EphyEmbed
 * @request: a #WebKitNetworkRequest
932
 * @state: (nullable): a #WebKitWebViewSessionState
933 934 935 936
 *
 * Sets the #WebKitNetworkRequest that should be loaded when the tab this embed
 * is on is switched to.
 */
937
void
938
ephy_embed_set_delayed_load_request (EphyEmbed *embed, WebKitURIRequest *request, WebKitWebViewSessionState *state)
939
{
940 941
  g_assert (EPHY_IS_EMBED (embed));
  g_assert (WEBKIT_IS_URI_REQUEST (request));
942

943
  g_clear_pointer (&embed->delayed_state, webkit_web_view_session_state_unref);
944
  g_clear_object (&embed->delayed_request);
945

946 947 948
  embed->delayed_request = g_object_ref (request);
  if (state)
    embed->delayed_state = webkit_web_view_session_state_ref (state);
949 950 951 952 953 954 955 956 957 958 959 960 961
}

/**
 * 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)
{
962
  g_assert (EPHY_IS_EMBED (embed));
963

964
  return !!embed->delayed_request;
965 966
}

967 968 969
const char *
ephy_embed_get_title (EphyEmbed *embed)
{
970
  g_assert (EPHY_IS_EMBED (embed));
971

972
  return embed->title;
973
}
974 975 976 977 978 979 980 981 982 983 984 985 986


/**
 * 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)
{
987
  g_assert (EPHY_IS_EMBED (embed));
988 989 990

  return embed->inspector_loaded;
}
991 992

void
993
ephy_embed_attach_notification_container (EphyEmbed *embed)
994
{
995
  EphyNotificationContainer *container;
996

997
  g_assert (EPHY_IS_EMBED (embed));
998

999
  container = ephy_notification_container_get_default ();
1000 1001
  if (gtk_widget_get_parent (GTK_WIDGET (container)) == NULL)
    gtk_overlay_add_overlay (GTK_OVERLAY (embed->overlay), GTK_WIDGET (container));
1002 1003 1004
}

void
1005
ephy_embed_detach_notification_container (EphyEmbed *embed)
1006
{
1007
  EphyNotificationContainer *container;
1008

1009
  g_assert (EPHY_IS_EMBED (embed));
1010

1011
  container = ephy_notification_container_get_default ();
1012 1013 1014 1015 1016
  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. */
1017
    gtk_container_remove (GTK_CONTAINER (embed->overlay), g_object_ref (GTK_WIDGET (container)));
1018
  }
1019
}