/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set sw=2 ts=2 sts=2 et: */ /* * Copyright © 2013 Igalia S.L. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include "config.h" #include "ephy-web-dom-utils.h" #include /** * ephy_web_dom_utils_has_modified_forms: * @document: the DOM document to check if there are or not modified forms. * * 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 the @document. **/ gboolean ephy_web_dom_utils_has_modified_forms (WebKitDOMDocument *document) { WebKitDOMHTMLCollection *forms; gulong forms_n; int i; forms = webkit_dom_document_get_forms (document); forms_n = webkit_dom_html_collection_get_length (forms); for (i = 0; i < forms_n; i++) { WebKitDOMHTMLCollection *elements; WebKitDOMNode *form_element = webkit_dom_html_collection_item (forms, i); gulong elements_n; int j; gboolean modified_input_element = FALSE; elements = webkit_dom_html_form_element_get_elements (WEBKIT_DOM_HTML_FORM_ELEMENT (form_element)); elements_n = webkit_dom_html_collection_get_length (elements); for (j = 0; j < elements_n; j++) { WebKitDOMNode *element; element = webkit_dom_html_collection_item (elements, j); if (WEBKIT_DOM_IS_HTML_TEXT_AREA_ELEMENT (element)) if (webkit_dom_html_text_area_element_is_edited (WEBKIT_DOM_HTML_TEXT_AREA_ELEMENT (element))) return TRUE; if (WEBKIT_DOM_IS_HTML_INPUT_ELEMENT (element)) if (webkit_dom_html_input_element_is_edited (WEBKIT_DOM_HTML_INPUT_ELEMENT (element))) { glong length; char *text; /* A small heuristic 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 do * nothing (eg, google search input). */ if (modified_input_element) return TRUE; modified_input_element = TRUE; text = webkit_dom_html_input_element_get_value (WEBKIT_DOM_HTML_INPUT_ELEMENT (element)); length = g_utf8_strlen (text, -1); g_free (text); if (length > 50) return TRUE; } } } return FALSE; } /** * ephy_web_dom_utils_get_application_title: * @document: the DOM document. * * Returns web application title if it is defined in <meta> elements of * @document. **/ char * ephy_web_dom_utils_get_application_title (WebKitDOMDocument *document) { WebKitDOMNodeList *metas; char *title = NULL; gulong length, i; metas = webkit_dom_document_get_elements_by_tag_name (document, "meta"); length = webkit_dom_node_list_get_length (metas); for (i = 0; i < length && title == NULL; i++) { char *name; char *property; WebKitDOMNode *node = webkit_dom_node_list_item (metas, i); name = webkit_dom_html_meta_element_get_name (WEBKIT_DOM_HTML_META_ELEMENT (node)); property = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "property"); if (g_strcmp0 (name, "application-name") == 0 || g_strcmp0 (property, "og:site_name") == 0) { title = webkit_dom_html_meta_element_get_content (WEBKIT_DOM_HTML_META_ELEMENT (node)); } g_free (property); g_free (name); } return title; } static char * resolve_uri (const char *base_uri, const char *uri) { SoupURI *base; SoupURI *new; char *ret; if (uri == NULL) return NULL; if (base_uri == NULL) return NULL; base = soup_uri_new (base_uri); new = soup_uri_new_with_base (base, uri); soup_uri_free (base); ret = soup_uri_to_string (new, FALSE); soup_uri_free (new); return ret; } static gboolean get_icon_from_mstile (WebKitDOMDocument *document, char **uri_out, char **color_out) { gboolean ret; WebKitDOMNodeList *metas; gulong length, i; char *image = NULL; char *color = NULL; metas = webkit_dom_document_get_elements_by_tag_name (document, "meta"); length = webkit_dom_node_list_get_length (metas); for (i = 0; i < length; i++) { WebKitDOMNode *node = webkit_dom_node_list_item (metas, i); char *name; name = webkit_dom_html_meta_element_get_name (WEBKIT_DOM_HTML_META_ELEMENT (node)); if (g_strcmp0 (name, "msapplication-TileImage") == 0) { if (image == NULL) image = webkit_dom_html_meta_element_get_content (WEBKIT_DOM_HTML_META_ELEMENT (node)); } else if (g_strcmp0 (name, "msapplication-TileColor") == 0) { if (color == NULL) color = webkit_dom_html_meta_element_get_content (WEBKIT_DOM_HTML_META_ELEMENT (node)); } } ret = (image != NULL && *image != '\0'); if (uri_out != NULL) *uri_out = g_strdup (image); if (color_out != NULL) *color_out = g_strdup (color); g_free (image); g_free (color); return ret; } static gboolean get_icon_from_ogp (WebKitDOMDocument *document, char **uri_out, char **color_out) { gboolean ret; WebKitDOMNodeList *metas; gulong length, i; char *image = NULL; char *color = NULL; metas = webkit_dom_document_get_elements_by_tag_name (document, "meta"); length = webkit_dom_node_list_get_length (metas); for (i = 0; i < length && image == NULL; i++) { WebKitDOMNode *node = webkit_dom_node_list_item (metas, i); char *property; char *itemprop; property = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "property"); itemprop = webkit_dom_element_get_attribute (WEBKIT_DOM_ELEMENT (node), "itemprop"); if (g_strcmp0 (property, "og:image") == 0 || g_strcmp0 (itemprop, "image") == 0) { image = webkit_dom_html_meta_element_get_content (WEBKIT_DOM_HTML_META_ELEMENT (node)); } g_free (property); g_free (itemprop); } ret = (image != NULL && *image != '\0'); if (uri_out != NULL) *uri_out = g_strdup (image); if (color_out != NULL) *color_out = g_strdup (color); return ret; } static gboolean get_icon_from_touch_icon (WebKitDOMDocument *document, char **uri_out, char **color_out) { gboolean ret; WebKitDOMNodeList *links; gulong length, i; char *image = NULL; char *color = NULL; links = webkit_dom_document_get_elements_by_tag_name (document, "link"); length = webkit_dom_node_list_get_length (links); for (i = 0; i < length && image == NULL; i++) { char *rel; WebKitDOMNode *node = webkit_dom_node_list_item (links, i); rel = webkit_dom_html_link_element_get_rel (WEBKIT_DOM_HTML_LINK_ELEMENT (node)); /* TODO: support more than one possible icon. */ if (g_strcmp0 (rel, "apple-touch-icon") == 0 || g_strcmp0 (rel, "apple-touch-icon-precomposed") == 0) { image = webkit_dom_html_link_element_get_href (WEBKIT_DOM_HTML_LINK_ELEMENT (node)); } g_free (rel); } ret = (image != NULL && *image != '\0'); if (uri_out != NULL) *uri_out = g_strdup (image); if (color_out != NULL) *color_out = g_strdup (color); return ret; } static gboolean get_icon_from_favicon (WebKitDOMDocument *document, char **uri_out, char **color_out) { gboolean ret; WebKitDOMNodeList *links; gulong length, i; char *image = NULL; char *color = NULL; links = webkit_dom_document_get_elements_by_tag_name (document, "link"); length = webkit_dom_node_list_get_length (links); for (i = 0; i < length; i++) { char *rel; WebKitDOMNode *node = webkit_dom_node_list_item (links, i); rel = webkit_dom_html_link_element_get_rel (WEBKIT_DOM_HTML_LINK_ELEMENT (node)); if (g_strcmp0 (rel, "shortcut-icon") == 0 || g_strcmp0 (rel, "shortcut icon") == 0 || g_strcmp0 (rel, "SHORTCUT ICON") == 0 || g_strcmp0 (rel, "Shortcut Icon") == 0 || g_strcmp0 (rel, "icon shortcut") == 0 || g_strcmp0 (rel, "icon") == 0) { image = webkit_dom_html_link_element_get_href (WEBKIT_DOM_HTML_LINK_ELEMENT (node)); } g_free (rel); } ret = (image != NULL && *image != '\0'); if (uri_out != NULL) *uri_out = g_strdup (image); if (color_out != NULL) *color_out = g_strdup (color); return ret; } /** * ephy_web_dom_utils_get_best_icon: * @document: the DOM document. * @base_uri: base URI of the #WebKitWebView. * @uri_out: Icon URI. * @color_out: Icon background color. * * Tries to get the icon (and its background color if any) for a web application * from the @document meta data. First try to get a mstile, then OGP, then touch * icon and finally favicon. * * Returns %TRUE if it finds an icon in the @document. **/ gboolean ephy_web_dom_utils_get_best_icon (WebKitDOMDocument *document, const char *base_uri, char **uri_out, char **color_out) { gboolean ret = FALSE; char *image = NULL; char *color = NULL; ret = get_icon_from_mstile (document, &image, &color); if (! ret) ret = get_icon_from_ogp (document, &image, &color); if (! ret) ret = get_icon_from_touch_icon (document, &image, &color); if (! ret) ret = get_icon_from_favicon (document, &image, &color); if (uri_out != NULL) *uri_out = resolve_uri (base_uri, image); if (color_out != NULL) *color_out = g_strdup (color); g_free (image); g_free (color); return ret; } gboolean ephy_web_dom_utils_find_form_auth_elements (WebKitDOMHTMLFormElement *form, WebKitDOMNode **username, WebKitDOMNode **password) { WebKitDOMHTMLCollection *elements; WebKitDOMNode *username_node = NULL; WebKitDOMNode *password_node = NULL; guint i, n_elements; gboolean found_auth_elements = FALSE; elements = webkit_dom_html_form_element_get_elements (form); n_elements = webkit_dom_html_collection_get_length (elements); for (i = 0; i < n_elements; i++) { WebKitDOMNode *element; char *element_type; element = webkit_dom_html_collection_item (elements, i); if (!WEBKIT_DOM_IS_HTML_INPUT_ELEMENT (element)) continue; g_object_get (element, "type", &element_type, NULL); if (g_str_equal (element_type, "text") || g_str_equal (element_type, "email")) { /* We found more than one inputs of type text; we won't be saving here. */ if (username_node) { g_free (element_type); found_auth_elements = FALSE; break; } username_node = g_object_ref (element); found_auth_elements = TRUE; } else if (g_str_equal (element_type, "password")) { /* We found more than one inputs of type password; we won't be saving here. */ if (password_node) { g_free (element_type); found_auth_elements = FALSE; break; } password_node = g_object_ref (element); found_auth_elements = TRUE; } g_free (element_type); } g_object_unref(elements); if (found_auth_elements && username_node && password_node) { *username = username_node; *password = password_node; return TRUE; } if (username_node) g_object_unref (username_node); if (password_node) g_object_unref (password_node); return FALSE; }