%s
%s
", first_paragraph, second_paragraph); formatted_reason = g_strdup_printf ("%s", reason); g_free (first_paragraph); /* Technical details when a site cannot be loaded due to a network error. */ first_paragraph = g_strdup_printf (_("The precise error was: %s"), formatted_reason); *message_details = g_strdup_printf ("%s
", first_paragraph); /* The button on the network error page. DO NOT ADD MNEMONICS HERE. */ *button_label = g_strdup (_("Reload")); *button_action = g_strdup_printf ("window.location = '%s';", uri); /* Mnemonic for the Reload button on browser error pages. */ *button_accesskey = C_("reload-access-key", "R"); *icon_name = "network-error-symbolic.svg"; *style = "default"; g_free (formatted_origin); g_free (formatted_reason); g_free (first_paragraph); } static void format_crash_error_page (const char *uri, char **page_title, char **message_title, char **message_body, char **button_label, char **button_action, const char **button_accesskey, const char **icon_name, const char **style) { char *formatted_uri; char *formatted_distributor; char *first_paragraph; char *second_paragraph; /* Page title when a site cannot be loaded due to a page crash error. */ *page_title = g_strdup_printf (_("Problem Loading Page")); /* Message title when a site cannot be loaded due to a page crash error. */ *message_title = g_strdup (_("Oops! There may be a problem")); formatted_uri = g_strdup_printf ("%s", uri); /* Error details when a site cannot be loaded due to a page crash error. */ first_paragraph = g_strdup_printf (_("The page %s may have caused Web to " "close unexpectedly."), formatted_uri); formatted_distributor = g_strdup_printf ("%s", DISTRIBUTOR_NAME); /* Further error details when a site cannot be loaded due to a page crash error. */ second_paragraph = g_strdup_printf (_("If this happens again, please report " "the problem to the %s developers."), formatted_distributor); *message_body = g_strdup_printf ("%s
%s
", first_paragraph, second_paragraph); /* The button on the page crash error page. DO NOT ADD MNEMONICS HERE. */ *button_label = g_strdup (_("Reload")); *button_action = g_strdup_printf ("window.location = '%s';", uri); /* Mnemonic for the Reload button on browser error pages. */ *button_accesskey = C_("reload-access-key", "R"); *icon_name = "computer-fail-symbolic.svg"; *style = "default"; g_free (formatted_uri); g_free (formatted_distributor); g_free (first_paragraph); g_free (second_paragraph); } static void format_process_crash_error_page (const char *uri, char **page_title, char **message_title, char **message_body, char **button_label, char **button_action, const char **button_accesskey, const char **icon_name, const char **style) { const char *first_paragraph; /* Page title when a site cannot be loaded due to a process crash error. */ *page_title = g_strdup_printf (_("Problem Displaying Page")); /* Message title when a site cannot be loaded due to a process crash error. */ *message_title = g_strdup (_("Oops!")); /* Error details when a site cannot be loaded due to a process crash error. */ first_paragraph = _("Something went wrong while displaying this page. Please reload or visit a different page to continue."); *message_body = g_strdup_printf ("%s
", first_paragraph); /* The button on the process crash error page. DO NOT ADD MNEMONICS HERE. */ *button_label = g_strdup (_("Reload")); *button_action = g_strdup_printf ("window.location = '%s';", uri); /* Mnemonic for the Reload button on browser error pages. */ *button_accesskey = C_("reload-access-key", "R"); *icon_name = "computer-fail-symbolic.svg"; *style = "default"; } static void format_tls_error_page (EphyWebView *view, const char *origin, char **page_title, char **message_title, char **message_body, char **message_details, char **button_label, char **button_action, const char **button_accesskey, char **hidden_button_label, char **hidden_button_action, const char **hidden_button_accesskey, const char **icon_name, const char **style) { char *formatted_origin; char *first_paragraph; /* Page title when a site is not loaded due to an invalid TLS certificate. */ *page_title = g_strdup_printf (_("Security Violation")); /* Message title when a site is not loaded due to an invalid TLS certificate. */ *message_title = g_strdup (_("This Connection is Not Secure")); formatted_origin = g_strdup_printf ("%s", origin); /* Error details when a site is not loaded due to an invalid TLS certificate. */ first_paragraph = g_strdup_printf (_("This does not look like the real %s. " "Attackers might be trying to steal or " "alter information going to or from " "this site."), formatted_origin); *message_body = g_strdup_printf ("%s
", first_paragraph); *message_details = detailed_message_from_tls_errors (view->tls_errors); /* The button on the invalid TLS certificate error page. DO NOT ADD MNEMONICS HERE. */ *button_label = g_strdup (_("Go Back")); *button_action = g_strdup ("window.history.back();"); /* Mnemonic for the Go Back button on the invalid TLS certificate error page. */ *button_accesskey = C_("back-access-key", "B"); /* The hidden button on the invalid TLS certificate error page. Do not add mnemonics here. */ *hidden_button_label = g_strdup (_("Accept Risk and Proceed")); *hidden_button_action = g_strdup_printf ("window.webkit.messageHandlers.tlsErrorPage.postMessage(%"G_GUINT64_FORMAT ");", webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (view))); /* Mnemonic for the Accept Risk and Proceed button on the invalid TLS certificate error page. */ *hidden_button_accesskey = C_("proceed-anyway-access-key", "P"); *icon_name = "channel-insecure-symbolic.svg"; *style = "danger"; g_free (formatted_origin); g_free (first_paragraph); } static void format_unsafe_browsing_error_page (EphyWebView *view, const char *origin, const char *threat_type, char **page_title, char **message_title, char **message_body, char **message_details, char **button_label, char **button_action, const char **button_accesskey, char **hidden_button_label, char **hidden_button_action, const char **hidden_button_accesskey, const char **icon_name, const char **style) { char *formatted_origin; char *first_paragraph; /* Page title when a site is flagged by Google Safe Browsing verification. */ *page_title = g_strdup_printf (_("Security Warning")); /* Message title on the unsafe browsing error page. */ *message_title = g_strdup (_("Unsafe website detected!")); formatted_origin = g_strdup_printf ("%s", origin); /* Error details on the unsafe browsing error page. * https://developers.google.com/safe-browsing/v4/usage-limits#UserWarnings */ if (!g_strcmp0 (threat_type, GSB_THREAT_TYPE_MALWARE)) { first_paragraph = g_strdup_printf (_("Visiting %s may harm your computer. This " "page appears to contain malicious code that could " "be downloaded to your computer without your consent."), formatted_origin); *message_details = g_strdup_printf (_("You can learn more about harmful web content " "including viruses and other malicious code " "and how to protect your computer at %s."), "" "www.stopbadware.org" ""); } else if (!g_strcmp0 (threat_type, GSB_THREAT_TYPE_SOCIAL_ENGINEERING)) { first_paragraph = g_strdup_printf (_("Attackers on %s may trick you into doing " "something dangerous like installing software or " "revealing your personal information (for example, " "passwords, phone numbers, or credit cards)."), formatted_origin); *message_details = g_strdup_printf (_("You can find out more about social engineering " "(phishing) at %s or from %s."), "" "Social Engineering (Phishing and Deceptive Sites)" "", "" "www.antiphishing.org" ""); } else { first_paragraph = g_strdup_printf (_("%s may contain harmful programs. Attackers might " "attempt to trick you into installing programs that " "harm your browsing experience (for example, by changing " "your homepage or showing extra ads on sites you visit)."), formatted_origin); *message_details = g_strdup_printf (_("You can learn more about unwanted software at %s."), "" "Unwanted Software Policy" ""); } *message_body = g_strdup_printf ("%s
", first_paragraph); /* The button on unsafe browsing error page. DO NOT ADD MNEMONICS HERE. */ *button_label = g_strdup (_("Go Back")); *button_action = g_strdup ("window.history.back();"); /* Mnemonic for the Go Back button on the unsafe browsing error page. */ *button_accesskey = C_("back-access-key", "B"); /* The hidden button on the unsafe browsing error page. Do not add mnemonics here. */ *hidden_button_label = g_strdup (_("Accept Risk and Proceed")); *hidden_button_action = g_strdup_printf ("window.webkit.messageHandlers.unsafeBrowsingErrorPage.postMessage(%"G_GUINT64_FORMAT ");", webkit_web_view_get_page_id (WEBKIT_WEB_VIEW (view))); /* Mnemonic for the Accept Risk and Proceed button on the unsafe browsing error page. */ *hidden_button_accesskey = C_("proceed-anyway-access-key", "P"); *icon_name = "security-high-symbolic.svg"; *style = "danger"; g_free (formatted_origin); g_free (first_paragraph); } static void format_no_such_file_error_page (EphyWebView *view, char **page_title, char **message_title, char **message_body, char **button_label, char **button_action, const char **button_accesskey, const char **icon_name, const char **style) { g_autofree gchar *formatted_origin = NULL; g_autofree gchar *first_paragraph = NULL; g_autofree gchar *second_paragraph = NULL; /* Page title on no such file error page */ *page_title = g_strdup_printf (_("File not found")); /* Message title on the no such file error page. */ *message_title = g_strdup (_("File not found")); formatted_origin = g_strdup_printf ("%s", view->address); first_paragraph = g_strdup_printf (_("%s could not be found."), formatted_origin); second_paragraph = g_strdup_printf (_("Please check the file name for " "capitalization or other typing errors. Also check if " "it has been moved, renamed, or deleted.")); *message_body = g_strdup_printf ("%s
%s
", first_paragraph, second_paragraph); /* The button on no such file error page. DO NOT ADD MNEMONICS HERE. */ *button_label = g_strdup (_("Go Back")); *button_action = g_strdup ("window.history.back();"); /* Mnemonic for the Go Back button on the no such file error page. */ *button_accesskey = C_("back-access-key", "B"); *icon_name = "computer-fail-symbolic.svg"; *style = "default"; } /** * ephy_web_view_load_error_page: * @view: an #EphyWebView * @uri: uri that caused the failure * @page: one of #EphyWebViewErrorPage * @error: a GError to inspect, or %NULL * @user_data: a pointer to additional data * * Loads an error page appropiate for @page in @view. * **/ void ephy_web_view_load_error_page (EphyWebView *view, const char *uri, EphyWebViewErrorPage page, GError *error, gpointer user_data) { GBytes *html_file; GString *html = g_string_new (""); char *origin = NULL; char *lang = NULL; char *page_title = NULL; char *msg_title = NULL; char *msg_body = NULL; char *msg_details = NULL; char *button_label = NULL; char *hidden_button_label = NULL; char *button_action = NULL; char *hidden_button_action = NULL; char *style_sheet = NULL; const char *button_accesskey = NULL; const char *hidden_button_accesskey = NULL; const char *icon_name = NULL; const char *style = NULL; const char *reason = NULL; g_assert (page != EPHY_WEB_VIEW_ERROR_PAGE_NONE); view->loading_error_page = TRUE; view->error_page = page; if (page == EPHY_WEB_VIEW_ERROR_INVALID_TLS_CERTIFICATE) ephy_web_view_set_security_level (view, EPHY_SECURITY_LEVEL_UNACCEPTABLE_CERTIFICATE); else ephy_web_view_set_security_level (view, EPHY_SECURITY_LEVEL_LOCAL_PAGE); reason = error ? error->message : _("None specified"); origin = ephy_uri_to_security_origin (uri); if (origin == NULL) origin = g_strdup (uri); lang = g_strdup (pango_language_to_string (gtk_get_default_language ())); g_strdelimit (lang, "_-@", '\0'); html_file = g_resources_lookup_data (EPHY_PAGE_TEMPLATE_ERROR, 0, NULL); switch (page) { case EPHY_WEB_VIEW_ERROR_PAGE_NETWORK_ERROR: format_network_error_page (uri, origin, reason, &page_title, &msg_title, &msg_body, &msg_details, &button_label, &button_action, &button_accesskey, &icon_name, &style); break; case EPHY_WEB_VIEW_ERROR_PAGE_CRASH: format_crash_error_page (uri, &page_title, &msg_title, &msg_body, &button_label, &button_action, &button_accesskey, &icon_name, &style); break; case EPHY_WEB_VIEW_ERROR_PROCESS_CRASH: format_process_crash_error_page (uri, &page_title, &msg_title, &msg_body, &button_label, &button_action, &button_accesskey, &icon_name, &style); break; case EPHY_WEB_VIEW_ERROR_INVALID_TLS_CERTIFICATE: format_tls_error_page (view, origin, &page_title, &msg_title, &msg_body, &msg_details, &button_label, &button_action, &button_accesskey, &hidden_button_label, &hidden_button_action, &hidden_button_accesskey, &icon_name, &style); break; case EPHY_WEB_VIEW_ERROR_UNSAFE_BROWSING: format_unsafe_browsing_error_page (view, origin, user_data, &page_title, &msg_title, &msg_body, &msg_details, &button_label, &button_action, &button_accesskey, &hidden_button_label, &hidden_button_action, &hidden_button_accesskey, &icon_name, &style); break; case EPHY_WEB_VIEW_ERROR_NO_SUCH_FILE: format_no_such_file_error_page (view, &page_title, &msg_title, &msg_body, &button_label, &button_action, &button_accesskey, &icon_name, &style); break; case EPHY_WEB_VIEW_ERROR_PAGE_NONE: default: g_assert_not_reached (); } _ephy_web_view_update_icon (view); style_sheet = get_style_sheet (); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wformat-nonliteral" /* The HTML file is trusted input. */ g_string_printf (html, g_bytes_get_data (html_file, NULL), lang, lang, ((gtk_widget_get_default_direction () == GTK_TEXT_DIR_RTL) ? "rtl" : "ltr"), page_title, style_sheet, button_action, hidden_button_action, icon_name, style, msg_title, msg_body, msg_details ? "visible" : "hidden", _("Technical information"), msg_details, hidden_button_label ? "visible" : "hidden", hidden_button_accesskey, hidden_button_label, button_accesskey, button_label); #pragma GCC diagnostic pop g_bytes_unref (html_file); g_free (origin); g_free (lang); g_free (page_title); g_free (msg_title); g_free (msg_body); g_free (msg_details); g_free (button_label); g_free (button_action); g_free (hidden_button_label); g_free (hidden_button_action); g_free (style_sheet); /* Make our history backend ignore the next page load, since it will be an error page. */ ephy_web_view_freeze_history (view); webkit_web_view_load_alternate_html (WEBKIT_WEB_VIEW (view), html->str, uri, 0); g_string_free (html, TRUE); } static gboolean load_failed_cb (WebKitWebView *web_view, WebKitLoadEvent load_event, const char *uri, GError *error, gpointer user_data) { EphyWebView *view = EPHY_WEB_VIEW (web_view); view->load_failed = TRUE; ephy_web_view_set_link_message (view, NULL); if (error->domain != WEBKIT_NETWORK_ERROR && error->domain != WEBKIT_POLICY_ERROR && error->domain != WEBKIT_PLUGIN_ERROR) { if (view->address && g_str_has_prefix (view->address, "file:")) ephy_web_view_load_error_page (view, uri, EPHY_WEB_VIEW_ERROR_NO_SUCH_FILE, error, NULL); else ephy_web_view_load_error_page (view, uri, EPHY_WEB_VIEW_ERROR_PAGE_NETWORK_ERROR, error, NULL); return TRUE; } switch (error->code) { case WEBKIT_NETWORK_ERROR_FAILED: case WEBKIT_NETWORK_ERROR_TRANSPORT: case WEBKIT_NETWORK_ERROR_UNKNOWN_PROTOCOL: case WEBKIT_NETWORK_ERROR_FILE_DOES_NOT_EXIST: case WEBKIT_POLICY_ERROR_FAILED: case WEBKIT_POLICY_ERROR_CANNOT_SHOW_MIME_TYPE: case WEBKIT_POLICY_ERROR_CANNOT_SHOW_URI: case WEBKIT_POLICY_ERROR_CANNOT_USE_RESTRICTED_PORT: case WEBKIT_PLUGIN_ERROR_FAILED: case WEBKIT_PLUGIN_ERROR_CANNOT_FIND_PLUGIN: case WEBKIT_PLUGIN_ERROR_CANNOT_LOAD_PLUGIN: case WEBKIT_PLUGIN_ERROR_JAVA_UNAVAILABLE: case WEBKIT_PLUGIN_ERROR_CONNECTION_CANCELLED: ephy_web_view_load_error_page (view, uri, EPHY_WEB_VIEW_ERROR_PAGE_NETWORK_ERROR, error, NULL); return TRUE; case WEBKIT_NETWORK_ERROR_CANCELLED: { if (!view->typed_address) { const char *prev_uri; prev_uri = webkit_web_view_get_uri (web_view); ephy_web_view_set_address (view, prev_uri); } } break; case WEBKIT_POLICY_ERROR_FRAME_LOAD_INTERRUPTED_BY_POLICY_CHANGE: /* If we are going to download something, and this is the first * page to load in this tab, we may want to close it down. */ if (!view->ever_committed) g_signal_emit_by_name (view, "download-only-load", NULL); break; /* In case the resource is going to be showed with a plugin just let * WebKit do it */ case WEBKIT_PLUGIN_ERROR_WILL_HANDLE_LOAD: default: break; } return FALSE; } static gboolean load_failed_with_tls_error_cb (WebKitWebView *web_view, const char *uri, GTlsCertificate *certificate, GTlsCertificateFlags errors, gpointer user_data) { EphyWebView *view = EPHY_WEB_VIEW (web_view); g_clear_object (&view->certificate); g_clear_pointer (&view->tls_error_failing_uri, g_free); view->certificate = g_object_ref (certificate); view->tls_errors = errors; view->tls_error_failing_uri = g_strdup (uri); ephy_web_view_load_error_page (EPHY_WEB_VIEW (web_view), uri, EPHY_WEB_VIEW_ERROR_INVALID_TLS_CERTIFICATE, NULL, NULL); return TRUE; } static void mixed_content_detected_cb (WebKitWebView *web_view, WebKitInsecureContentEvent event, gpointer user_data) { EphyWebView *view = EPHY_WEB_VIEW (web_view); if (view->security_level != EPHY_SECURITY_LEVEL_UNACCEPTABLE_CERTIFICATE) ephy_web_view_set_security_level (view, EPHY_SECURITY_LEVEL_MIXED_CONTENT); } static void close_web_view_cb (WebKitWebView *web_view, gpointer user_data) { GtkWidget *widget = gtk_widget_get_toplevel (GTK_WIDGET (web_view)); LOG ("close web view"); if (EPHY_IS_EMBED_CONTAINER (widget)) ephy_embed_container_remove_child (EPHY_EMBED_CONTAINER (widget), EPHY_GET_EMBED_FROM_EPHY_WEB_VIEW (web_view)); else gtk_widget_destroy (widget); } static void zoom_changed_cb (WebKitWebView *web_view, GParamSpec *pspec, gpointer user_data) { const char *address; double zoom; zoom = webkit_web_view_get_zoom_level (web_view); if (EPHY_WEB_VIEW (web_view)->is_setting_zoom) return; address = ephy_web_view_get_address (EPHY_WEB_VIEW (web_view)); if (ephy_embed_utils_address_has_web_scheme (address)) { ephy_history_service_set_url_zoom_level (EPHY_WEB_VIEW (web_view)->history_service, address, zoom, NULL, NULL, NULL); } } static gboolean script_dialog_cb (WebKitWebView *web_view, WebKitScriptDialog *dialog) { if (webkit_script_dialog_get_dialog_type (dialog) != WEBKIT_SCRIPT_DIALOG_BEFORE_UNLOAD_CONFIRM) return FALSE; /* Ignore beforeunload events for now until we properly support webkit_web_view_try_close() * See https://bugzilla.gnome.org/show_bug.cgi?id=722032. */ webkit_script_dialog_confirm_set_confirmed (dialog, TRUE); return TRUE; } static const char * enum_nick (GType enum_type, int value) { GEnumClass *enum_class; const GEnumValue *enum_value; const char *nick = NULL; enum_class = g_type_class_ref (enum_type); enum_value = g_enum_get_value (enum_class, value); if (enum_value) nick = enum_value->value_nick; g_type_class_unref (enum_class); return nick; } static void reader_setting_changed_cb (GSettings *settings, gchar *key, EphyWebView *web_view) { const gchar *font_style; const gchar *color_scheme; gchar *js_snippet; if (!g_str_has_prefix (web_view->address, EPHY_READER_SCHEME)) return; font_style = enum_nick (EPHY_TYPE_PREFS_READER_FONT_STYLE, g_settings_get_enum (settings, EPHY_PREFS_READER_FONT_STYLE)); color_scheme = enum_nick (EPHY_TYPE_PREFS_READER_COLOR_SCHEME, g_settings_get_enum (settings, EPHY_PREFS_READER_COLOR_SCHEME)); js_snippet = g_strdup_printf ("document.body.className = '%s %s'", font_style, color_scheme); webkit_web_view_run_javascript_in_world (WEBKIT_WEB_VIEW (web_view), js_snippet, ephy_embed_shell_get_guid (ephy_embed_shell_get_default ()), NULL, NULL, NULL); g_free (js_snippet); } typedef struct { EphyWebView *web_view; WebKitAuthenticationRequest *request; } AuthenticationData; static AuthenticationData * authentication_data_new (EphyWebView *web_view, WebKitAuthenticationRequest *request) { AuthenticationData *data; data = g_new (AuthenticationData, 1); data->web_view = g_object_ref (web_view); data->request = g_object_ref (request); return data; } static void authentication_data_free (AuthenticationData *data) { g_object_unref (data->web_view); g_object_unref (data->request); g_free (data); } static void auth_password_query_finished_cb (GList *records, AuthenticationData *data) { EphyPasswordRecord *record; g_autoptr (WebKitCredential) credential = NULL; record = records && records->data ? EPHY_PASSWORD_RECORD (records->data) : NULL; if (record) { credential = webkit_credential_new (ephy_password_record_get_username (record), ephy_password_record_get_password (record), WEBKIT_CREDENTIAL_PERSISTENCE_NONE); } else { /* Provide a non-empty wrong credential to force a retry */ credential = webkit_credential_new (" ", "", WEBKIT_CREDENTIAL_PERSISTENCE_NONE); } webkit_authentication_request_authenticate (data->request, credential); authentication_data_free (data); } static void authenticate_succeeded_cb (WebKitAuthenticationRequest *request, WebKitCredential *credential) { EphyPasswordManager *password_manager; g_autoptr (WebKitSecurityOrigin) security_origin = NULL; g_autofree char *origin = NULL; if (webkit_credential_get_persistence (credential) != WEBKIT_CREDENTIAL_PERSISTENCE_PERMANENT) return; security_origin = webkit_authentication_request_get_security_origin (request); origin = webkit_security_origin_to_string (security_origin); password_manager = ephy_embed_shell_get_password_manager (ephy_embed_shell_get_default ()); ephy_password_manager_save (password_manager, origin, origin, webkit_credential_get_username (credential), webkit_credential_get_password (credential), "org.gnome.Epiphany.HTTPAuthCredentials.Username", "org.gnome.Epiphany.HTTPAuthCredentials.Password", TRUE); } static gboolean authenticate_cb (WebKitWebView *web_view, WebKitAuthenticationRequest *request, gpointer user_data) { EphyWebView *ephy_web_view = EPHY_WEB_VIEW (web_view); EphyPasswordManager *password_manager; AuthenticationData *data; g_autoptr (WebKitSecurityOrigin) security_origin = NULL; g_autofree char *origin = NULL; if (webkit_authentication_request_is_retry (request)) { webkit_authentication_request_set_can_save_credentials (request, TRUE); g_signal_connect_object (request, "authenticated", G_CALLBACK (authenticate_succeeded_cb), ephy_web_view, 0); ephy_web_view->in_auth_dialog = 1; return FALSE; } data = authentication_data_new (ephy_web_view, request); security_origin = webkit_authentication_request_get_security_origin (request); origin = webkit_security_origin_to_string (security_origin); password_manager = ephy_embed_shell_get_password_manager (ephy_embed_shell_get_default ()); ephy_password_manager_query (password_manager, NULL, origin, origin, NULL, "org.gnome.Epiphany.HTTPAuthCredentials.Username", "org.gnome.Epiphany.HTTPAuthCredentials.Password", (EphyPasswordManagerQueryCallback)auth_password_query_finished_cb, data); return TRUE; } typedef struct { WebKitWebView *web_view; char *origin; WebKitUserMessage *message; } PasswordManagerData; static void password_manager_data_free (PasswordManagerData *data) { g_object_unref (data->web_view); g_object_unref (data->message); g_free (data); } static void password_manager_query_finished_cb (GList *records, PasswordManagerData *data) { EphyPasswordRecord *record; const char *origin; const char *username = NULL; const char *password = NULL; g_autofree char *real_origin = NULL; record = records && records->data ? EPHY_PASSWORD_RECORD (records->data) : NULL; if (record) { username = ephy_password_record_get_username (record); password = ephy_password_record_get_password (record); } g_variant_get (webkit_user_message_get_parameters (data->message), "(&s@sm@sm@s@s)", &origin, NULL, NULL, NULL, NULL); real_origin = ephy_uri_to_security_origin (webkit_web_view_get_uri (data->web_view)); if (g_strcmp0 (real_origin, origin) != 0) { g_debug ("Extension's origin '%s' doesn't match real origin '%s'", origin, real_origin); password_manager_data_free (data); return; } webkit_user_message_send_reply (data->message, webkit_user_message_new ("PasswordManager.QueryPasswordResponse", g_variant_new ("(msms)", username, password))); password_manager_data_free (data); } static gboolean password_manager_handle_query_usernames_message (WebKitWebView *web_view, WebKitUserMessage *message) { GVariant *parameters; const char *origin; EphyPasswordManager *password_manager; GList *usernames, *l; GVariantBuilder builder; g_autofree char *real_origin = NULL; parameters = webkit_user_message_get_parameters (message); if (!parameters) return FALSE; g_variant_get (parameters, "&s", &origin); real_origin = ephy_uri_to_security_origin (webkit_web_view_get_uri (web_view)); if (g_strcmp0 (real_origin, origin) != 0) { g_debug ("Extension's origin '%s' doesn't match real origin '%s'", origin, real_origin); return FALSE; } password_manager = ephy_embed_shell_get_password_manager (ephy_embed_shell_get_default ()); usernames = ephy_password_manager_get_usernames_for_origin (password_manager, origin); g_variant_builder_init (&builder, G_VARIANT_TYPE_STRING_ARRAY); for (l = usernames; l != NULL; l = g_list_next (l)) g_variant_builder_add (&builder, "s", l->data); webkit_user_message_send_reply (message, webkit_user_message_new ("PasswordManager.QueryUsernamesResponse", g_variant_builder_end (&builder))); return TRUE; } static gboolean password_manager_handle_query_password_message (WebKitWebView *web_view, WebKitUserMessage *message) { GVariant *parameters; const char *origin; const char *target_origin; const char *username; const char *username_field; const char *password_field; EphyPasswordManager *password_manager; PasswordManagerData *data; parameters = webkit_user_message_get_parameters (message); if (!parameters) return FALSE; g_variant_get (parameters, "(&s&sm&sm&s&s)", &origin, &target_origin, &username, &username_field, &password_field); /* Don't include username_field in queries unless we actually have a username * to go along with it, or the query will fail because we don't save * username_field without a corresponding username. */ if (!username && username_field) username_field = NULL; data = g_new (PasswordManagerData, 1); data->web_view = g_object_ref (web_view); data->message = g_object_ref (message); password_manager = ephy_embed_shell_get_password_manager (ephy_embed_shell_get_default ()); ephy_password_manager_query (password_manager, NULL, origin, target_origin, username, username_field, password_field, (EphyPasswordManagerQueryCallback)password_manager_query_finished_cb, data); return TRUE; } static gboolean user_message_received_cb (WebKitWebView *web_view, WebKitUserMessage *message) { const char *name; name = webkit_user_message_get_name (message); if (g_strcmp0 (name, "PasswordManager.QueryUsernames") == 0) return password_manager_handle_query_usernames_message (web_view, message); if (g_strcmp0 (name, "PasswordManager.QueryPassword") == 0) return password_manager_handle_query_password_message (web_view, message); return FALSE; } /** * ephy_web_view_load_request: * @view: the #EphyWebView in which to load the request * @request: the #WebKitNetworkRequest to be loaded * * Loads the given #WebKitNetworkRequest in the given #EphyWebView. **/ void ephy_web_view_load_request (EphyWebView *view, WebKitURIRequest *request) { const char *url; char *effective_url; g_assert (EPHY_IS_WEB_VIEW (view)); g_assert (WEBKIT_IS_URI_REQUEST (request)); url = webkit_uri_request_get_uri (request); effective_url = ephy_embed_utils_normalize_address (url); webkit_uri_request_set_uri (request, effective_url); g_free (effective_url); webkit_web_view_load_request (WEBKIT_WEB_VIEW (view), request); } /** * ephy_web_view_load_url: * @view: an #EphyWebView * @url: a URL * * Loads @url in @view. **/ void ephy_web_view_load_url (EphyWebView *view, const char *url) { char *effective_url; g_assert (EPHY_IS_WEB_VIEW (view)); g_assert (url); effective_url = ephy_embed_utils_normalize_address (url); if (g_str_has_prefix (effective_url, "javascript:")) { char *decoded_url; decoded_url = soup_uri_decode (effective_url); webkit_web_view_run_javascript (WEBKIT_WEB_VIEW (view), decoded_url, NULL, NULL, NULL); g_free (decoded_url); } else webkit_web_view_load_uri (WEBKIT_WEB_VIEW (view), effective_url); g_free (effective_url); } /** * ephy_web_view_get_is_blank: * @view: an #EphyWebView * * Returns whether the @view's address is "blank". * * Return value: %TRUE if the @view's address is "blank" **/ gboolean ephy_web_view_get_is_blank (EphyWebView *view) { return view->is_blank; } gboolean ephy_web_view_is_overview (EphyWebView *view) { if (!view->address) return FALSE; return (!strcmp (view->address, EPHY_ABOUT_SCHEME ":overview") || !strcmp (view->address, "about:overview")); } /** * ephy_web_view_get_address: * @view: an #EphyWebView * * Returns the address of the currently-loaded page, percent-encoded. * This URI should not be displayed to the user; to do that, use * ephy_web_view_get_display_address(). * * Return value: @view's address. Will never be %NULL. **/ const char * ephy_web_view_get_address (EphyWebView *view) { if (view->address) { if (g_str_has_prefix (view->address, EPHY_READER_SCHEME)) return view->address + strlen (EPHY_READER_SCHEME) + 1; return view->address; } return "about:blank"; } /** * ephy_web_view_get_display_address: * @view: an #EphyWebView * * Returns the display address of the currently-loaded page. This is a * decoded URI suitable for display to the user. To get a URI suitable * for sending to a server, e.g. for storage in the bookmarks or history * database, use ephy_web_view_get_address(). * * Return value: @view's address. Will never be %NULL. */ const char * ephy_web_view_get_display_address (EphyWebView *view) { return view->display_address ? view->display_address : "about:blank"; } /** * ephy_web_view_is_loading: * @view: an #EphyWebView * * Returns whether the web page in @view has finished loading. A web * page is only finished loading after all images, styles, and other * dependencies have been downloaded and rendered, or when the load * has failed for some reason. * * Return value: %TRUE if the page is still loading, %FALSE if complete **/ gboolean ephy_web_view_is_loading (EphyWebView *view) { return webkit_web_view_is_loading (WEBKIT_WEB_VIEW (view)); } /** * ephy_web_view_load_failed: * @view: an #EphyWebView * * Returns whether the web page in @view has failed to load. * * Return value: %TRUE if the page failed to load, %FALSE if it's loading * or load finished successfully **/ gboolean ephy_web_view_load_failed (EphyWebView *view) { return view->load_failed; } /** * ephy_web_view_get_icon: * @view: an #EphyWebView * * Returns the view's site icon as a #GdkPixbuf, * or %NULL if it is not available. * * Return value: (transfer none): a the view's site icon **/ GdkPixbuf * ephy_web_view_get_icon (EphyWebView *view) { return view->icon; } /** * ephy_web_view_get_document_type: * @view: an #EphyWebView * * Returns the type of document loaded in the @view * * Return value: the #EphyWebViewDocumentType **/ EphyWebViewDocumentType ephy_web_view_get_document_type (EphyWebView *view) { return view->document_type; } /** * ephy_web_view_get_navigation_flags: * @view: an #EphyWebView * * Returns @view's navigation flags. * * Return value: @view's navigation flags **/ EphyWebViewNavigationFlags ephy_web_view_get_navigation_flags (EphyWebView *view) { return view->nav_flags; } /** * ephy_web_view_get_status_message: * @view: an #EphyWebView * * Returns the message displayed in @view's #EphyWindow's * #EphyStatusbar. If the user is hovering the mouse over a hyperlink, * this function will return the same value as * ephy_web_view_get_link_message(). Otherwise, it will return a network * status message, or NULL. * * The message returned has a limited lifetime, and so should be copied with * g_strdup() if it must be stored. * * Return value: The current statusbar message **/ const char * ephy_web_view_get_status_message (EphyWebView *view) { g_assert (EPHY_IS_WEB_VIEW (view)); if (view->link_message && view->link_message[0] != '\0') return view->link_message; if (view->loading_message) return view->loading_message; return NULL; } /** * ephy_web_view_get_link_message: * @view: an #EphyWebView * * When the user is hovering the mouse over a hyperlink, returns the URL of the * hyperlink. * * Return value: the URL of the link over which the mouse is hovering **/ const char * ephy_web_view_get_link_message (EphyWebView *view) { g_assert (EPHY_IS_WEB_VIEW (view)); return view->link_message; } /** * ephy_web_view_set_link_message: * @view: an #EphyWebView * @address: new value for link-message in @view * * Sets the value of link-message property which tells the URL of the hovered * link. **/ void ephy_web_view_set_link_message (EphyWebView *view, const char *address) { char *decoded_address; g_assert (EPHY_IS_WEB_VIEW (view)); g_free (view->link_message); if (address) { decoded_address = ephy_uri_decode (address); view->link_message = ephy_embed_utils_link_message_parse (decoded_address); g_free (decoded_address); } else { view->link_message = NULL; } g_object_notify_by_pspec (G_OBJECT (view), obj_properties[PROP_STATUS_MESSAGE]); g_object_notify_by_pspec (G_OBJECT (view), obj_properties[PROP_LINK_MESSAGE]); } /** * ephy_web_view_set_security_level: * @view: an #EphyWebView * @level: the new #EphySecurityLevel for @view * * Sets @view's security-level property to @level. **/ void ephy_web_view_set_security_level (EphyWebView *view, EphySecurityLevel level) { g_assert (EPHY_IS_WEB_VIEW (view)); if (view->security_level != level) { view->security_level = level; g_object_notify_by_pspec (G_OBJECT (view), obj_properties[PROP_SECURITY]); } } /** * ephy_web_view_get_typed_address: * @view: an #EphyWebView * * Returns the text that the user introduced in the @view's * #EphyWindow location entry, if any. * * This is not guaranteed to be the same as @view's location, * available through ephy_web_view_get_address(). As the user types a * new address into the location entry, * ephy_web_view_get_typed_address()'s returned string will * change. When the load starts, ephy_web_view_get_typed_address() * will return %NULL, and ephy_web_view_get_address() will return the * new page being loaded. Note that the typed_address can be changed * again while a load is in progress (in case the user starts to type * again in the location entry); in that case * ephy_web_view_get_typed_address() will be again non-%NULL, and the * contents of the entry will not be overwritten. * * Return value: @view's #EphyWindow's location entry text when @view * is selected. **/ const char * ephy_web_view_get_typed_address (EphyWebView *view) { g_assert (EPHY_IS_WEB_VIEW (view)); return view->typed_address; } /** * ephy_web_view_set_typed_address: * @view: an #EphyWebView * @address: the new typed address, or %NULL to clear it * * Sets the text that @view's #EphyWindow will display in its location toolbar * entry when @view is selected. **/ void ephy_web_view_set_typed_address (EphyWebView *view, const char *address) { g_assert (EPHY_IS_WEB_VIEW (view)); g_free (view->typed_address); view->typed_address = g_strdup (address); g_object_notify_by_pspec (G_OBJECT (view), obj_properties[PROP_TYPED_ADDRESS]); } gboolean ephy_web_view_get_should_bypass_safe_browsing (EphyWebView *view) { g_assert (EPHY_IS_WEB_VIEW (view)); return view->bypass_safe_browsing; } void ephy_web_view_set_should_bypass_safe_browsing (EphyWebView *view, gboolean bypass_safe_browsing) { g_assert (EPHY_IS_WEB_VIEW (view)); view->bypass_safe_browsing = bypass_safe_browsing; } static void has_modified_forms_cb (WebKitWebView *view, GAsyncResult *result, GTask *task) { WebKitJavascriptResult *js_result; gboolean retval = FALSE; GError *error = NULL; js_result = webkit_web_view_run_javascript_in_world_finish (view, result, &error); if (!js_result) { g_task_return_error (task, error); } else { retval = jsc_value_to_boolean (webkit_javascript_result_get_js_value (js_result)); g_task_return_boolean (task, retval); webkit_javascript_result_unref (js_result); } g_object_unref (task); } /** * ephy_web_view_has_modified_forms: * @view: an #EphyWebView * * A small heuristic is used here. If there's only one input element modified * and it does not have a lot of text the user is likely not very interested in * saving this work, so it returns %FALSE in this case (eg, google search * input). * * Returns %TRUE if the user has modified <input> or <textarea> * values in @view's loaded document. * * Return value: %TRUE if @view has user-modified forms **/ void ephy_web_view_has_modified_forms (EphyWebView *view, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; g_assert (EPHY_IS_WEB_VIEW (view)); task = g_task_new (view, cancellable, callback, user_data); webkit_web_view_run_javascript_in_world (WEBKIT_WEB_VIEW (view), "Ephy.hasModifiedForms();", ephy_embed_shell_get_guid (ephy_embed_shell_get_default ()), cancellable, (GAsyncReadyCallback)has_modified_forms_cb, task); } gboolean ephy_web_view_has_modified_forms_finish (EphyWebView *view, GAsyncResult *result, GError **error) { g_assert (g_task_is_valid (result, view)); return g_task_propagate_boolean (G_TASK (result), error); } typedef struct { char *icon_uri; char *icon_color; } GetBestWebAppIconAsyncData; static void get_best_web_app_icon_async_data_free (GetBestWebAppIconAsyncData *data) { g_free (data->icon_uri); g_free (data->icon_color); g_free (data); } static void get_best_web_app_icon_cb (WebKitWebView *view, GAsyncResult *result, GTask *task) { WebKitJavascriptResult *js_result; GError *error = NULL; js_result = webkit_web_view_run_javascript_in_world_finish (view, result, &error); if (js_result) { JSCValue *js_value, *js_uri, *js_color; GetBestWebAppIconAsyncData *data; data = g_new0 (GetBestWebAppIconAsyncData, 1); js_value = webkit_javascript_result_get_js_value (js_result); g_assert (jsc_value_is_object (js_value)); js_uri = jsc_value_object_get_property (js_value, "url"); data->icon_uri = jsc_value_to_string (js_uri); g_object_unref (js_uri); js_color = jsc_value_object_get_property (js_value, "icon"); data->icon_color = jsc_value_is_null (js_color) || jsc_value_is_undefined (js_color) ? NULL : jsc_value_to_string (js_color); g_object_unref (js_color); g_task_return_pointer (task, data, (GDestroyNotify)get_best_web_app_icon_async_data_free); webkit_javascript_result_unref (js_result); } else g_task_return_error (task, error); g_object_unref (task); } void ephy_web_view_get_best_web_app_icon (EphyWebView *view, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { WebKitWebView *wk_view; GTask *task; char *script; g_assert (EPHY_IS_WEB_VIEW (view)); wk_view = WEBKIT_WEB_VIEW (view); task = g_task_new (view, cancellable, callback, user_data); script = g_strdup_printf ("Ephy.getWebAppIcon(\"%s\");", webkit_web_view_get_uri (wk_view)); webkit_web_view_run_javascript_in_world (wk_view, script, ephy_embed_shell_get_guid (ephy_embed_shell_get_default ()), cancellable, (GAsyncReadyCallback)get_best_web_app_icon_cb, task); g_free (script); } gboolean ephy_web_view_get_best_web_app_icon_finish (EphyWebView *view, GAsyncResult *result, char **icon_uri, GdkRGBA *icon_color, GError **error) { GetBestWebAppIconAsyncData *data; GTask *task = G_TASK (result); g_assert (g_task_is_valid (result, view)); data = g_task_propagate_pointer (task, error); if (!data) return FALSE; if (data->icon_uri != NULL && data->icon_uri[0] != '\0') { *icon_uri = data->icon_uri; data->icon_uri = NULL; } if (data->icon_color != NULL && data->icon_color[0] != '\0') gdk_rgba_parse (icon_color, data->icon_color); get_best_web_app_icon_async_data_free (data); return TRUE; } static void get_web_app_title_cb (WebKitWebView *view, GAsyncResult *result, GTask *task) { WebKitJavascriptResult *js_result; GError *error = NULL; js_result = webkit_web_view_run_javascript_in_world_finish (view, result, &error); if (js_result) { JSCValue *js_value; char *retval = NULL; js_value = webkit_javascript_result_get_js_value (js_result); if (!jsc_value_is_null (js_value) && !jsc_value_is_undefined (js_value)) retval = jsc_value_to_string (js_value); g_task_return_pointer (task, retval, (GDestroyNotify)g_free); webkit_javascript_result_unref (js_result); } else g_task_return_error (task, error); g_object_unref (task); } void ephy_web_view_get_web_app_title (EphyWebView *view, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; g_assert (EPHY_IS_WEB_VIEW (view)); task = g_task_new (view, cancellable, callback, user_data); webkit_web_view_run_javascript_in_world (WEBKIT_WEB_VIEW (view), "Ephy.getWebAppTitle();", ephy_embed_shell_get_guid (ephy_embed_shell_get_default ()), cancellable, (GAsyncReadyCallback)get_web_app_title_cb, task); } char * ephy_web_view_get_web_app_title_finish (EphyWebView *view, GAsyncResult *result, GError **error) { g_assert (g_task_is_valid (result, view)); return g_task_propagate_pointer (G_TASK (result), error); } static void get_web_app_mobile_capable_cb (WebKitWebView *view, GAsyncResult *result, GTask *task) { WebKitJavascriptResult *js_result; GError *error = NULL; js_result = webkit_web_view_run_javascript_in_world_finish (view, result, &error); if (js_result) { JSCValue *js_value; gboolean retval = FALSE; js_value = webkit_javascript_result_get_js_value (js_result); retval = jsc_value_to_boolean (js_value); g_task_return_boolean (task, retval); webkit_javascript_result_unref (js_result); } else g_task_return_error (task, error); g_object_unref (task); } void ephy_web_view_get_web_app_mobile_capable (EphyWebView *view, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; g_assert (EPHY_IS_WEB_VIEW (view)); task = g_task_new (view, cancellable, callback, user_data); webkit_web_view_run_javascript_in_world (WEBKIT_WEB_VIEW (view), "Ephy.getAppleMobileWebAppCapable();", ephy_embed_shell_get_guid (ephy_embed_shell_get_default ()), cancellable, (GAsyncReadyCallback)get_web_app_mobile_capable_cb, task); } gboolean ephy_web_view_get_web_app_mobile_capable_finish (EphyWebView *view, GAsyncResult *result, GError **error) { g_assert (g_task_is_valid (result, view)); return g_task_propagate_boolean (G_TASK (result), error); } /** * ephy_web_view_get_security_level: * @view: an #EphyWebView * @level: (out): return value of security level * @address: (out) (transfer none): the URI to which the security level corresponds * @certificate: (out) (transfer none): return value of TLS certificate * @errors: (out): return value of TLS errors * * Fetches the #EphySecurityLevel and a #GTlsCertificate associated * with @view and a #GTlsCertificateFlags showing what problems, if any, * have been found with that certificate. **/ void ephy_web_view_get_security_level (EphyWebView *view, EphySecurityLevel *level, const char **address, GTlsCertificate **certificate, GTlsCertificateFlags *errors) { g_assert (EPHY_IS_WEB_VIEW (view)); if (level) *level = view->security_level; if (address) *address = view->last_committed_address; if (certificate) *certificate = view->certificate; if (errors) *errors = view->tls_errors; } static void ephy_web_view_print_failed (EphyWebView *view, GError *error) { GtkWidget *info_bar; GtkWidget *label; GtkContainer *content_area; EphyEmbed *embed = EPHY_GET_EMBED_FROM_EPHY_WEB_VIEW (view); info_bar = gtk_info_bar_new_with_buttons (_("_OK"), GTK_RESPONSE_OK, NULL); label = gtk_label_new (error->message); content_area = GTK_CONTAINER (gtk_info_bar_get_content_area (GTK_INFO_BAR (info_bar))); gtk_info_bar_set_message_type (GTK_INFO_BAR (info_bar), GTK_MESSAGE_ERROR); gtk_container_add (content_area, label); g_signal_connect (info_bar, "response", G_CALLBACK (gtk_widget_destroy), NULL); ephy_embed_add_top_widget (embed, info_bar, EPHY_EMBED_TOP_WIDGET_POLICY_RETAIN_ON_TRANSITION); gtk_widget_show_all (info_bar); } static void print_operation_finished_cb (WebKitPrintOperation *operation, EphyWebView *view) { ephy_embed_shell_set_page_setup (ephy_embed_shell_get_default (), webkit_print_operation_get_page_setup (operation)); } static void print_operation_failed_cb (WebKitPrintOperation *operation, GError *error, EphyWebView *view) { g_signal_handlers_disconnect_by_func (operation, print_operation_finished_cb, view); ephy_web_view_print_failed (view, error); } /** * ephy_web_view_print: * @view: an #EphyWebView * * Opens a dialog to print the specified view. * * Since: 2.30 **/ void ephy_web_view_print (EphyWebView *view) { WebKitPrintOperation *operation; EphyEmbedShell *shell; GtkPrintSettings *settings; g_assert (EPHY_IS_WEB_VIEW (view)); shell = ephy_embed_shell_get_default (); operation = webkit_print_operation_new (WEBKIT_WEB_VIEW (view)); g_signal_connect (operation, "finished", G_CALLBACK (print_operation_finished_cb), view); g_signal_connect (operation, "failed", G_CALLBACK (print_operation_failed_cb), view); webkit_print_operation_set_page_setup (operation, ephy_embed_shell_get_page_setup (shell)); settings = ephy_embed_shell_get_print_settings (shell); gtk_print_settings_set (settings, GTK_PRINT_SETTINGS_OUTPUT_BASENAME, webkit_web_view_get_title (WEBKIT_WEB_VIEW (view))); webkit_print_operation_set_print_settings (operation, settings); if (webkit_print_operation_run_dialog (operation, NULL) == WEBKIT_PRINT_OPERATION_RESPONSE_PRINT) ephy_embed_shell_set_print_settings (shell, webkit_print_operation_get_print_settings (operation)); g_object_unref (operation); } static void web_resource_get_data_cb (WebKitWebResource *resource, GAsyncResult *result, GOutputStream *output_stream) { guchar *data; gsize data_length; GInputStream *input_stream; GError *error = NULL; data = webkit_web_resource_get_data_finish (resource, result, &data_length, &error); if (!data) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_warning ("Failed to save page: %s", error->message); g_error_free (error); g_object_unref (output_stream); return; } input_stream = g_memory_input_stream_new_from_data (data, data_length, g_free); g_output_stream_splice_async (output_stream, input_stream, G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, G_PRIORITY_DEFAULT, NULL, NULL, NULL); g_object_unref (input_stream); g_object_unref (output_stream); } static void ephy_web_view_save_main_resource_cb (GFile *file, GAsyncResult *result, WebKitWebView *view) { GFileOutputStream *output_stream; WebKitWebResource *resource; GError *error = NULL; output_stream = g_file_replace_finish (file, result, &error); if (!output_stream) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_warning ("Failed to save page: %s", error->message); g_error_free (error); return; } resource = webkit_web_view_get_main_resource (view); webkit_web_resource_get_data (resource, EPHY_WEB_VIEW (view)->cancellable, (GAsyncReadyCallback)web_resource_get_data_cb, output_stream); } /** * ephy_web_view_save: * @view: an #EphyWebView * @uri: location to store the saved page * * Saves the currently loaded page of @view to @uri. **/ void ephy_web_view_save (EphyWebView *view, const char *uri) { GFile *file; g_assert (EPHY_IS_WEB_VIEW (view)); g_assert (uri); file = g_file_new_for_uri (uri); if (g_str_has_suffix (uri, ".mhtml")) webkit_web_view_save_to_file (WEBKIT_WEB_VIEW (view), file, WEBKIT_SAVE_MODE_MHTML, NULL, NULL, NULL); else g_file_replace_async (file, NULL, FALSE, G_FILE_CREATE_REPLACE_DESTINATION | G_FILE_CREATE_PRIVATE, G_PRIORITY_DEFAULT, view->cancellable, (GAsyncReadyCallback)ephy_web_view_save_main_resource_cb, view); g_object_unref (file); } /** * ephy_web_view_load_homepage: * @view: an #EphyWebView * * Loads the homepage set by the user in @view. **/ void ephy_web_view_load_homepage (EphyWebView *view) { EphyEmbedShell *shell; EphyEmbedShellMode mode; char *home; g_assert (EPHY_IS_WEB_VIEW (view)); shell = ephy_embed_shell_get_default (); mode = ephy_embed_shell_get_mode (shell); if (mode == EPHY_EMBED_SHELL_MODE_INCOGNITO || mode == EPHY_EMBED_SHELL_MODE_AUTOMATION) { ephy_web_view_load_new_tab_page (view); return; } home = g_settings_get_string (EPHY_SETTINGS_MAIN, EPHY_PREFS_HOMEPAGE_URL); if (home == NULL || home[0] == '\0') { ephy_web_view_load_new_tab_page (view); } else { ephy_web_view_freeze_history (view); ephy_web_view_set_visit_type (view, EPHY_PAGE_VISIT_HOMEPAGE); ephy_web_view_load_url (view, home); } g_free (home); } void ephy_web_view_load_new_tab_page (EphyWebView *view) { EphyEmbedShell *shell; EphyEmbedShellMode mode; g_assert (EPHY_IS_WEB_VIEW (view)); shell = ephy_embed_shell_get_default (); mode = ephy_embed_shell_get_mode (shell); ephy_web_view_freeze_history (view); ephy_web_view_set_visit_type (view, EPHY_PAGE_VISIT_HOMEPAGE); if (mode == EPHY_EMBED_SHELL_MODE_INCOGNITO) ephy_web_view_load_url (view, "about:incognito"); else if (mode == EPHY_EMBED_SHELL_MODE_AUTOMATION) ephy_web_view_load_url (view, "about:blank"); else ephy_web_view_load_url (view, "about:overview"); } /** * ephy_web_view_get_visit_type: * @view: an #EphyWebView * * Returns: the @view #EphyWebViewVisitType **/ EphyHistoryPageVisitType ephy_web_view_get_visit_type (EphyWebView *view) { g_assert (EPHY_IS_WEB_VIEW (view)); return view->visit_type; } /** * ephy_web_view_set_visit_type: * @view: an #EphyWebView * @visit_type: an #EphyHistoryPageVisitType * * Sets the @visit_type for @view, so that the URI can be * properly weighted in the history backend. **/ void ephy_web_view_set_visit_type (EphyWebView *view, EphyHistoryPageVisitType visit_type) { g_assert (EPHY_IS_WEB_VIEW (view)); view->visit_type = visit_type; } /** * ephy_web_view_toggle_reader_mode: * @view: an #EphyWebView * @active: active flag * * Sets reader mode state to @active if necessary. **/ void ephy_web_view_toggle_reader_mode (EphyWebView *view, gboolean active) { WebKitWebView *web_view = WEBKIT_WEB_VIEW (view); char *reader_uri = NULL; const gchar *address; gboolean view_active = g_str_has_prefix (view->address, EPHY_READER_SCHEME); if (view_active == active) return; address = ephy_web_view_get_address (view); if (view_active) { ephy_web_view_freeze_history (view); webkit_web_view_load_uri (web_view, address); return; } if (!ephy_web_view_is_reader_mode_available (view)) return; reader_uri = g_strconcat (EPHY_READER_SCHEME, ":", address, NULL); webkit_web_view_load_uri (web_view, reader_uri); } gboolean ephy_web_view_is_reader_mode_available (EphyWebView *view) { return view->reader_mode_available; } gboolean ephy_web_view_get_reader_mode_state (EphyWebView *view) { return g_str_has_prefix (view->address, EPHY_READER_SCHEME); } gboolean ephy_web_view_is_in_auth_dialog (EphyWebView *view) { return view->in_auth_dialog; } static void ephy_web_view_dispose (GObject *object) { EphyWebView *view = EPHY_WEB_VIEW (object); WebKitUserContentManager *ucm = webkit_web_view_get_user_content_manager (WEBKIT_WEB_VIEW (view)); ephy_embed_prefs_unregister_ucm (ucm); ephy_embed_shell_unregister_ucm_handler (ephy_embed_shell_get_default (), ucm); untrack_info_bar (&view->geolocation_info_bar); untrack_info_bar (&view->notification_info_bar); untrack_info_bar (&view->microphone_info_bar); untrack_info_bar (&view->webcam_info_bar); untrack_info_bar (&view->webcam_mic_info_bar); untrack_info_bar (&view->password_info_bar); untrack_info_bar (&view->password_form_info_bar); untrack_info_bar (&view->itp_info_bar); g_clear_object (&view->certificate); g_clear_object (&view->file_monitor); g_clear_object (&view->icon); if (view->cancellable) { g_cancellable_cancel (view->cancellable); g_clear_object (&view->cancellable); } g_clear_handle_id (&view->snapshot_timeout_id, g_source_remove); g_clear_handle_id (&view->reader_js_timeout, g_source_remove); G_OBJECT_CLASS (ephy_web_view_parent_class)->dispose (object); } static void ephy_web_view_finalize (GObject *object) { EphyWebView *view = EPHY_WEB_VIEW (object); g_free (view->address); g_free (view->display_address); g_free (view->typed_address); g_free (view->last_committed_address); g_free (view->link_message); g_free (view->loading_message); g_free (view->tls_error_failing_uri); g_free (view->pending_snapshot_uri); G_OBJECT_CLASS (ephy_web_view_parent_class)->finalize (object); } static void ephy_web_view_constructed (GObject *object) { EphyWebView *web_view = EPHY_WEB_VIEW (object); GtkStyleContext *context; GdkRGBA color; G_OBJECT_CLASS (ephy_web_view_parent_class)->constructed (object); g_signal_emit_by_name (ephy_embed_shell_get_default (), "web-view-created", web_view); g_signal_connect (web_view, "web-process-terminated", G_CALLBACK (process_terminated_cb), NULL); g_signal_connect_swapped (webkit_web_view_get_back_forward_list (WEBKIT_WEB_VIEW (web_view)), "changed", G_CALLBACK (update_navigation_flags), web_view); /* Avoid flashing a white background when loading the overview in * dark mode. Note that we have to later reset this to white before * loading any non-Epiphany page. */ context = gtk_widget_get_style_context (GTK_WIDGET (web_view)); if (gtk_style_context_lookup_color (context, "theme_base_color", &color)) webkit_web_view_set_background_color (WEBKIT_WEB_VIEW (web_view), &color); } static void ephy_web_view_init (EphyWebView *web_view) { EphyEmbedShell *shell; shell = ephy_embed_shell_get_default (); web_view->is_blank = TRUE; web_view->ever_committed = FALSE; web_view->document_type = EPHY_WEB_VIEW_DOCUMENT_HTML; web_view->security_level = EPHY_SECURITY_LEVEL_TO_BE_DETERMINED; web_view->file_monitor = ephy_file_monitor_new (web_view); web_view->history_service = ephy_embed_shell_get_global_history_service (shell); web_view->cancellable = g_cancellable_new (); g_signal_connect_object (EPHY_SETTINGS_READER, "changed::" EPHY_PREFS_READER_FONT_STYLE, G_CALLBACK (reader_setting_changed_cb), web_view, 0); g_signal_connect_object (EPHY_SETTINGS_READER, "changed::" EPHY_PREFS_READER_COLOR_SCHEME, G_CALLBACK (reader_setting_changed_cb), web_view, 0); g_signal_connect (web_view, "decide-policy", G_CALLBACK (decide_policy_cb), NULL); g_signal_connect (web_view, "permission-request", G_CALLBACK (permission_request_cb), NULL); g_signal_connect (web_view, "load-changed", G_CALLBACK (load_changed_cb), NULL); g_signal_connect (web_view, "close", G_CALLBACK (close_web_view_cb), NULL); g_signal_connect (web_view, "load-failed", G_CALLBACK (load_failed_cb), NULL); g_signal_connect (web_view, "load-failed-with-tls-errors", G_CALLBACK (load_failed_with_tls_error_cb), NULL); g_signal_connect (web_view, "insecure-content-detected", G_CALLBACK (mixed_content_detected_cb), NULL); g_signal_connect (web_view, "notify::zoom-level", G_CALLBACK (zoom_changed_cb), NULL); g_signal_connect (web_view, "notify::title", G_CALLBACK (title_changed_cb), NULL); g_signal_connect (web_view, "notify::uri", G_CALLBACK (uri_changed_cb), NULL); g_signal_connect (web_view, "mouse-target-changed", G_CALLBACK (mouse_target_changed_cb), NULL); g_signal_connect (web_view, "notify::favicon", G_CALLBACK (icon_changed_cb), NULL); g_signal_connect (web_view, "script-dialog", G_CALLBACK (script_dialog_cb), NULL); g_signal_connect (web_view, "authenticate", G_CALLBACK (authenticate_cb), NULL); g_signal_connect (web_view, "user-message-received", G_CALLBACK (user_message_received_cb), NULL); g_signal_connect_object (shell, "password-form-focused", G_CALLBACK (password_form_focused_cb), web_view, 0); g_signal_connect_object (shell, "allow-tls-certificate", G_CALLBACK (allow_tls_certificate_cb), web_view, 0); g_signal_connect_object (shell, "allow-unsafe-browsing", G_CALLBACK (allow_unsafe_browsing_cb), web_view, 0); } static void ephy_web_view_class_init (EphyWebViewClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); WebKitWebViewClass *webkit_webview_class = WEBKIT_WEB_VIEW_CLASS (klass); gobject_class->dispose = ephy_web_view_dispose; gobject_class->finalize = ephy_web_view_finalize; gobject_class->get_property = ephy_web_view_get_property; gobject_class->set_property = ephy_web_view_set_property; gobject_class->constructed = ephy_web_view_constructed; widget_class->button_press_event = ephy_web_view_button_press_event; widget_class->key_press_event = ephy_web_view_key_press_event; webkit_webview_class->run_file_chooser = ephy_web_view_run_file_chooser; /** * EphyWebView:address: * * View's current address. This is a percent-encoded URI. **/ obj_properties[PROP_ADDRESS] = g_param_spec_string ("address", "Address", "The view's address", "", G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * EphyWebView:typed-address: * * User typed address for the current view. **/ obj_properties[PROP_TYPED_ADDRESS] = g_param_spec_string ("typed-address", "Typed Address", "The typed address", "", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** * EphyWebView:security-level: * * One of #EphySecurityLevel, determining view's current security level. **/ obj_properties[PROP_SECURITY] = g_param_spec_enum ("security-level", "Security Level", "The view's security level", EPHY_TYPE_SECURITY_LEVEL, EPHY_SECURITY_LEVEL_TO_BE_DETERMINED, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * EphyWebView:document-type: * * Document type determined for the view. **/ obj_properties[PROP_DOCUMENT_TYPE] = g_param_spec_enum ("document-type", "Document Type", "The view's document type", EPHY_TYPE_WEB_VIEW_DOCUMENT_TYPE, EPHY_WEB_VIEW_DOCUMENT_HTML, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * EphyWebView:navigation: * * View's navigation flags as #EphyWebViewNavigationFlags. **/ obj_properties[PROP_NAVIGATION] = g_param_spec_flags ("navigation", "Navigation flags", "The view's navigation flags", EPHY_TYPE_WEB_VIEW_NAVIGATION_FLAGS, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * EphyWebView:status-message: * * Statusbar message corresponding to this view. **/ obj_properties[PROP_STATUS_MESSAGE] = g_param_spec_string ("status-message", "Status Message", "The view's statusbar message", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * EphyWebView:link-message: * * ??? **/ obj_properties[PROP_LINK_MESSAGE] = g_param_spec_string ("link-message", "Link Message", "The view's link message", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * EphyWebView:icon: * * View's favicon set by the loaded site. **/ obj_properties[PROP_ICON] = g_param_spec_object ("icon", "Icon", "The view icon's", GDK_TYPE_PIXBUF, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * EphyWebView:is-blank: * * Whether the view is showing the blank address. **/ obj_properties[PROP_IS_BLANK] = g_param_spec_boolean ("is-blank", "Is blank", "If the EphyWebView is blank", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * EphyWebView:reader-mode: * * Whether the view is in reader mode. **/ obj_properties[PROP_READER_MODE] = g_param_spec_boolean ("reader-mode", "Reader mode", "If the EphyWebView is in reader mode", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * EphyWebView:display-address: * * View's current display address. **/ obj_properties[PROP_DISPLAY_ADDRESS] = g_param_spec_string ("display-address", "Display address", "The view's display address", "", G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (gobject_class, LAST_PROP, obj_properties); /** * EphyWebView::new-window: * @view: the #EphyWebView that received the signal * @new_view: the newly opened #EphyWebView * * The ::new-window signal is emitted after a new window has been opened by * the view. For example, when a JavaScript popup window is opened. **/ g_signal_new ("new-window", EPHY_TYPE_WEB_VIEW, G_SIGNAL_RUN_FIRST | G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, GTK_TYPE_WIDGET); /** * EphyWebView::search-key-press: * @view: the #EphyWebView that received the signal * @event: the #GdkEventKey which triggered this signal * * The ::search-key-press signal is emitted for keypresses which * should be used for find implementations. **/ g_signal_new ("search-key-press", EPHY_TYPE_WEB_VIEW, G_SIGNAL_RUN_LAST, 0, g_signal_accumulator_true_handled, NULL, NULL, G_TYPE_BOOLEAN, 1, GDK_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); /** * EphyWebView::download-only-load: * @view: the #EphyWebView that received the signal * * The ::download-only-load signal is emitted when the @view has its main load * replaced by a download, and that is the only reason why the @view has been created. **/ g_signal_new ("download-only-load", EPHY_TYPE_WEB_VIEW, G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } /** * ephy_web_view_new: * * Equivalent to g_object_new() but returns an #GtkWidget so you don't have * to cast it when dealing with most code. * * Return value: the newly created #EphyWebView widget **/ GtkWidget * ephy_web_view_new (void) { EphyEmbedShell *shell = ephy_embed_shell_get_default (); WebKitUserContentManager *ucm = webkit_user_content_manager_new (); ephy_embed_shell_register_ucm_handler (shell, ucm); ephy_embed_prefs_register_ucm (ucm); return g_object_new (EPHY_TYPE_WEB_VIEW, "web-context", ephy_embed_shell_get_web_context (shell), "user-content-manager", ucm, "settings", ephy_embed_prefs_get_settings (), "is-controlled-by-automation", ephy_embed_shell_get_mode (shell) == EPHY_EMBED_SHELL_MODE_AUTOMATION, NULL); } GtkWidget * ephy_web_view_new_with_related_view (WebKitWebView *related_view) { EphyEmbedShell *shell = ephy_embed_shell_get_default (); WebKitUserContentManager *ucm = webkit_user_content_manager_new (); ephy_embed_shell_register_ucm_handler (shell, ucm); ephy_embed_prefs_register_ucm (ucm); return g_object_new (EPHY_TYPE_WEB_VIEW, "related-view", related_view, "user-content-manager", ucm, "settings", ephy_embed_prefs_get_settings (), NULL); }