summaryrefslogtreecommitdiff
path: root/libyelp
diff options
context:
space:
mode:
authorMarcos Chavarría Teijeiro <chavarria1991@gmail.com>2014-10-21 12:45:42 +0200
committerDavid King <amigadave@amigadave.com>2015-06-22 13:21:31 +0100
commit3598be880e631b1cd43164f3dd2e48bb8fa53976 (patch)
treee3041baa89eab8c3e2ad5a14722061d11b21edd9 /libyelp
parent6f2d5aa706204d3d443e015d3e0ae7b93f59513a (diff)
downloadyelp-3598be880e631b1cd43164f3dd2e48bb8fa53976.tar.gz
yelp-view: Implement web extension to deal with DOM tree
Diffstat (limited to 'libyelp')
-rw-r--r--libyelp/Makefile.am5
-rw-r--r--libyelp/web-extension/Makefile.am8
-rw-r--r--libyelp/web-extension/yelp-web-extension.c144
-rw-r--r--libyelp/yelp-view.c140
4 files changed, 209 insertions, 88 deletions
diff --git a/libyelp/Makefile.am b/libyelp/Makefile.am
index 0ae9d607..e721fd5a 100644
--- a/libyelp/Makefile.am
+++ b/libyelp/Makefile.am
@@ -1,3 +1,5 @@
+SUBDIRS = web-extension
+
lib_LTLIBRARIES = libyelp.la
libyelp_la_SOURCES = \
@@ -53,6 +55,9 @@ libyelp_la_CFLAGS = \
-DDATADIR=\""$(datadir)"\" \
-DYELP_ICON_PATH=\"$(YELP_ICON_PATH)\"
+libyelp_la_CPPFLAGS = \
+ -DYELP_WEB_EXTENSIONS_DIR=\""$(pkglibdir)/"web-extensions\"
+
libyelp_la_LIBADD = $(YELP_LIBS)
libyelp_headers = \
diff --git a/libyelp/web-extension/Makefile.am b/libyelp/web-extension/Makefile.am
new file mode 100644
index 00000000..0cafe7af
--- /dev/null
+++ b/libyelp/web-extension/Makefile.am
@@ -0,0 +1,8 @@
+
+webextension_LTLIBRARIES = libyelpwebextension.la
+webextensiondir = $(pkglibdir)/web-extensions
+
+libyelpwebextension_la_SOURCES = yelp-web-extension.c
+libyelpwebextension_la_CFLAGS = $(YELP_EXTENSION_CFLAGS)
+libyelpwebextension_la_LIBADD = $(YELP_EXTENSION_LIBS)
+libyelpwebextension_la_LDFLAGS = -module -avoid-version -no-undefined
diff --git a/libyelp/web-extension/yelp-web-extension.c b/libyelp/web-extension/yelp-web-extension.c
new file mode 100644
index 00000000..76ef1158
--- /dev/null
+++ b/libyelp/web-extension/yelp-web-extension.c
@@ -0,0 +1,144 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2014 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 of the
+ * License, 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., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <webkit2/webkit-web-extension.h>
+#include <string.h>
+
+#define WEBKIT_DOM_USE_UNSTABLE_API
+#include <webkitdom/WebKitDOMElementUnstable.h>
+
+static gboolean
+web_page_context_menu (WebKitWebPage *web_page,
+ WebKitContextMenu *context_menu,
+ WebKitWebHitTestResult *hit_test_result)
+{
+ WebKitDOMNode *node, *cur, *link_node = NULL, *code_node = NULL, *code_title_node = NULL;
+ gchar *popup_link_text = NULL;
+ GVariantDict user_data_dict;
+
+ node = webkit_web_hit_test_result_get_node (hit_test_result);
+
+ for (cur = node; cur != NULL; cur = webkit_dom_node_get_parent_node (cur)) {
+ if (WEBKIT_DOM_IS_ELEMENT (cur) &&
+ webkit_dom_element_webkit_matches_selector (WEBKIT_DOM_ELEMENT (cur),
+ "a", NULL))
+ link_node = cur;
+
+ if (WEBKIT_DOM_IS_ELEMENT (cur) &&
+ webkit_dom_element_webkit_matches_selector (WEBKIT_DOM_ELEMENT (cur),
+ "div.code", NULL)) {
+ WebKitDOMNode *title;
+ code_node = WEBKIT_DOM_NODE (
+ webkit_dom_element_query_selector (WEBKIT_DOM_ELEMENT (cur),
+ "pre.contents", NULL));
+ title = webkit_dom_node_get_parent_node (cur);
+ if (WEBKIT_DOM_IS_ELEMENT (title) &&
+ webkit_dom_element_webkit_matches_selector (WEBKIT_DOM_ELEMENT (title),
+ "div.contents", NULL)) {
+ title = webkit_dom_node_get_previous_sibling (title);
+ if (WEBKIT_DOM_IS_ELEMENT (title) &&
+ webkit_dom_element_webkit_matches_selector (WEBKIT_DOM_ELEMENT (title),
+ "div.title", NULL)) {
+ code_title_node = title;
+ }
+ }
+ }
+ }
+
+ if (webkit_hit_test_result_context_is_link (WEBKIT_HIT_TEST_RESULT (hit_test_result)) && link_node) {
+ WebKitDOMNode *child;
+ gchar *tmp;
+ gint i, tmpi;
+ gboolean ws;
+
+ child = WEBKIT_DOM_NODE (
+ webkit_dom_element_query_selector (WEBKIT_DOM_ELEMENT (link_node),
+ "span.title", NULL));
+ if (child)
+ popup_link_text = webkit_dom_node_get_text_content (child);
+
+ if (!popup_link_text)
+ popup_link_text = webkit_dom_node_get_text_content (link_node);
+
+ tmp = g_new0 (gchar, strlen (popup_link_text) + 1);
+ ws = FALSE;
+ for (i = 0, tmpi = 0; popup_link_text[i] != '\0'; i++) {
+ if (popup_link_text[i] == ' ' || popup_link_text[i] == '\n') {
+ if (!ws) {
+ tmp[tmpi] = ' ';
+ tmpi++;
+ ws = TRUE;
+ }
+ }
+ else {
+ tmp[tmpi] = popup_link_text[i];
+ tmpi++;
+ ws = FALSE;
+ }
+ }
+ tmp[tmpi] = '\0';
+ g_free (popup_link_text);
+ popup_link_text = tmp;
+ }
+
+ if (!(popup_link_text || code_node || code_title_node))
+ return FALSE;
+
+ g_variant_dict_init (&user_data_dict, NULL);
+
+ if (popup_link_text) {
+ g_variant_dict_insert_value (&user_data_dict, "link-title",
+ g_variant_new_take_string (popup_link_text));
+ }
+
+ if (code_node) {
+ gchar *code_code = webkit_dom_node_get_text_content (code_node);
+ g_variant_dict_insert_value (&user_data_dict, "code-text",
+ g_variant_new_take_string (code_code));
+ }
+
+ if (code_title_node) {
+ gchar *code_title = webkit_dom_node_get_text_content (code_title_node);
+ g_variant_dict_insert_value (&user_data_dict, "code-title",
+ g_variant_new_take_string (code_title));
+ }
+
+ webkit_context_menu_set_user_data (context_menu, g_variant_dict_end (&user_data_dict));
+
+ return FALSE;
+}
+
+static void
+web_page_created_callback (WebKitWebExtension *extension,
+ WebKitWebPage *web_page,
+ gpointer user_data)
+{
+ g_signal_connect (web_page, "context-menu",
+ G_CALLBACK (web_page_context_menu),
+ NULL);
+}
+
+G_MODULE_EXPORT void
+webkit_web_extension_initialize (WebKitWebExtension *extension)
+{
+ g_signal_connect (extension, "page-created",
+ G_CALLBACK (web_page_created_callback),
+ NULL);
+}
diff --git a/libyelp/yelp-view.c b/libyelp/yelp-view.c
index 46130957..cca02b9f 100644
--- a/libyelp/yelp-view.c
+++ b/libyelp/yelp-view.c
@@ -130,6 +130,7 @@ static void document_callback (YelpDocument *document,
static void gtk_xft_dpi_changed (GtkSettings *gtk_settings,
GParamSpec *pspec,
gpointer user_data);
+static void yelp_view_register_extensions (void);
static gchar *nautilus_sendto = NULL;
@@ -211,9 +212,8 @@ struct _YelpViewPrivate {
gchar *popup_link_uri;
gchar *popup_link_text;
gchar *popup_image_uri;
- WebKitDOMNode *popup_code_node;
- WebKitDOMNode *popup_code_title;
gchar *popup_code_text;
+ gchar *popup_code_title;
YelpViewState state;
YelpViewState prevstate;
@@ -449,6 +449,7 @@ yelp_view_finalize (GObject *object)
g_free (priv->popup_link_text);
g_free (priv->popup_image_uri);
g_free (priv->popup_code_text);
+ g_free (priv->popup_code_title);
g_free (priv->page_id);
g_free (priv->root_title);
@@ -486,6 +487,8 @@ yelp_view_class_init (YelpViewClass *klass)
NULL);
settings_show_text_cursor (settings);
+ yelp_view_register_extensions ();
+
klass->external_uri = view_external_uri;
object_class->dispose = yelp_view_dispose;
@@ -786,6 +789,16 @@ yelp_view_get_active_link_text (YelpView *view)
/******************************************************************************/
+static void
+yelp_view_register_extensions (void)
+{
+ WebKitWebContext *context = webkit_web_context_get_default ();
+
+ webkit_web_context_set_web_extensions_directory (context, YELP_WEB_EXTENSIONS_DIR);
+}
+
+/******************************************************************************/
+
static gboolean
view_external_uri (YelpView *view,
YelpUri *uri)
@@ -1207,10 +1220,13 @@ popup_copy_code (GtkAction *action,
YelpView *view)
{
YelpViewPrivate *priv = GET_PRIV (view);
- GtkClipboard *clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
- gchar *content = webkit_dom_node_get_text_content (priv->popup_code_node);
- gtk_clipboard_set_text (clipboard, content, -1);
- g_free (content);
+ GtkClipboard *clipboard;
+
+ if (!priv->popup_code_text)
+ return;
+
+ clipboard = gtk_clipboard_get (GDK_SELECTION_CLIPBOARD);
+ gtk_clipboard_set_text (clipboard, priv->popup_code_text, -1);
}
static void
@@ -1221,8 +1237,9 @@ popup_save_code (GtkAction *action,
GtkWidget *dialog, *window;
gint res;
- g_free (priv->popup_code_text);
- priv->popup_code_text = webkit_dom_node_get_text_content (priv->popup_code_node);
+ if (!priv->popup_code_text)
+ return;
+
if (!g_str_has_suffix (priv->popup_code_text, "\n")) {
gchar *tmp = g_strconcat (priv->popup_code_text, "\n", NULL);
g_free (priv->popup_code_text);
@@ -1240,11 +1257,10 @@ popup_save_code (GtkAction *action,
GTK_STOCK_SAVE, GTK_RESPONSE_OK,
NULL);
gtk_file_chooser_set_do_overwrite_confirmation (GTK_FILE_CHOOSER (dialog), TRUE);
- if (priv->popup_code_title) {
- gchar *filename = webkit_dom_node_get_text_content (priv->popup_code_title);
- gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), filename);
- g_free (filename);
- }
+
+ if (priv->popup_code_title)
+ gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), priv->popup_code_title);
+
gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER (dialog),
g_get_user_special_dir (G_USER_DIRECTORY_DOCUMENTS));
@@ -1285,11 +1301,6 @@ popup_save_code (GtkAction *action,
g_object_unref (file);
}
- priv->popup_code_node = NULL;
- priv->popup_code_title = NULL;
- g_free (priv->popup_code_text);
- priv->popup_code_text = NULL;
-
gtk_widget_destroy (dialog);
}
@@ -1310,36 +1321,15 @@ view_populate_context_menu (YelpView *view,
YelpViewPrivate *priv = GET_PRIV (view);
WebKitContextMenuItem *item;
GtkAction *action;
- WebKitDOMNode *node, *cur, *link_node = NULL, *code_node = NULL, *code_title_node = NULL;
+ GVariant *dom_info_variant;
+ GVariantDict dom_info_dict;
webkit_context_menu_remove_all (context_menu);
- for (cur = node; cur != NULL; cur = webkit_dom_node_get_parent_node (cur)) {
- if (WEBKIT_DOM_IS_ELEMENT (cur) &&
- webkit_dom_element_webkit_matches_selector ((WebKitDOMElement *) cur,
- "a", NULL))
- link_node = cur;
-
- if (WEBKIT_DOM_IS_ELEMENT (cur) &&
- webkit_dom_element_webkit_matches_selector ((WebKitDOMElement *) cur,
- "div.code", NULL)) {
- WebKitDOMNode *title;
- code_node = (WebKitDOMNode *)
- webkit_dom_element_query_selector ((WebKitDOMElement *) cur,
- "pre.contents", NULL);
- title = webkit_dom_node_get_parent_node (cur);
- if (title != NULL && WEBKIT_DOM_IS_ELEMENT (title) &&
- webkit_dom_element_webkit_matches_selector ((WebKitDOMElement *) title,
- "div.contents", NULL)) {
- title = webkit_dom_node_get_previous_sibling (title);
- if (title != NULL && WEBKIT_DOM_IS_ELEMENT (title) &&
- webkit_dom_element_webkit_matches_selector ((WebKitDOMElement *) title,
- "div.title", NULL)) {
- code_title_node = title;
- }
- }
- }
- }
+ /* We extract the info about the dom tree that we build in the web-extension.*/
+ dom_info_variant = webkit_context_menu_get_user_data (context_menu);
+ if (dom_info_variant)
+ g_variant_dict_init (&dom_info_dict, dom_info_variant);
if (webkit_hit_test_result_context_is_link (hit_test_result)) {
gchar *uri;
@@ -1347,46 +1337,13 @@ view_populate_context_menu (YelpView *view,
g_free (priv->popup_link_uri);
priv->popup_link_uri = uri;
- g_free (priv->popup_link_text);
- priv->popup_link_text = NULL;
- if (link_node != NULL) {
- WebKitDOMNode *child;
- gchar *tmp;
- gint i, tmpi;
- gboolean ws;
-
- child = (WebKitDOMNode *)
- webkit_dom_element_query_selector (WEBKIT_DOM_ELEMENT (link_node),
- "span.title", NULL);
- if (child != NULL)
- priv->popup_link_text = webkit_dom_node_get_text_content (child);
-
- if (priv->popup_link_text == NULL)
- priv->popup_link_text = webkit_dom_node_get_text_content (link_node);
-
- tmp = g_new0 (gchar, strlen(priv->popup_link_text) + 1);
- ws = FALSE;
- for (i = 0, tmpi = 0; priv->popup_link_text[i] != '\0'; i++) {
- if (priv->popup_link_text[i] == ' ' || priv->popup_link_text[i] == '\n') {
- if (!ws) {
- tmp[tmpi] = ' ';
- tmpi++;
- ws = TRUE;
- }
- }
- else {
- tmp[tmpi] = priv->popup_link_text[i];
- tmpi++;
- ws = FALSE;
- }
- }
- tmp[tmpi] = '\0';
- g_free (priv->popup_link_text);
- priv->popup_link_text = tmp;
- }
- else {
+ g_clear_pointer (&priv->popup_link_text, g_free);
+ if (dom_info_variant)
+ g_variant_dict_lookup (&dom_info_dict, "link-title", "s",
+ &(priv->popup_link_text));
+
+ if (!priv->popup_link_text)
priv->popup_link_text = g_strdup (uri);
- }
if (g_str_has_prefix (priv->popup_link_uri, "mailto:")) {
gchar *label = g_strdup_printf (_("Send email to %s"),
@@ -1503,13 +1460,20 @@ view_populate_context_menu (YelpView *view,
webkit_context_menu_append (context_menu, item);
}
- if (code_node != NULL) {
+ g_clear_pointer (&priv->popup_code_title, g_free);
+ if (dom_info_variant)
+ g_variant_dict_lookup (&dom_info_dict, "code-title",
+ "s", &(priv->popup_code_title));
+
+ g_clear_pointer (&priv->popup_code_text, g_free);
+ if (dom_info_variant)
+ g_variant_dict_lookup (&dom_info_dict, "code-text",
+ "s", &(priv->popup_code_text));
+
+ if (priv->popup_code_text) {
item = webkit_context_menu_item_new_separator ();
webkit_context_menu_append (context_menu, item);
- priv->popup_code_node = code_node;
- priv->popup_code_title = code_title_node;
-
action = gtk_action_group_get_action (priv->popup_actions,
"CopyCode");
item = webkit_context_menu_item_new (action);