[gnome-builder/wip/gtk4-port: 1382/1774] libide/webkit: add IdeHtmlGenerator abstract class




commit 260526548b844a3464fe3156735c0b95f5055d4b
Author: Christian Hergert <chergert redhat com>
Date:   Mon Jun 6 20:42:44 2022 -0700

    libide/webkit: add IdeHtmlGenerator abstract class
    
    This can simplify how we generate HTML and be more extensible in allowing
    async operations to occur. The other plugins will be ported shortly.

 src/libide/webkit/ide-html-generator.c             | 289 +++++++++++++++++++++
 src/libide/webkit/ide-html-generator.h             |  67 +++++
 src/libide/webkit/ide-text-buffer-html-generator.c | 205 +++++++++++++++
 src/libide/webkit/ide-text-buffer-html-generator.h |  31 +++
 src/libide/webkit/ide-webkit-page.c                | 118 ++++-----
 src/libide/webkit/ide-webkit-page.h                |  10 +-
 src/libide/webkit/meson.build                      |   3 +
 7 files changed, 658 insertions(+), 65 deletions(-)
---
diff --git a/src/libide/webkit/ide-html-generator.c b/src/libide/webkit/ide-html-generator.c
new file mode 100644
index 000000000..2fbca2620
--- /dev/null
+++ b/src/libide/webkit/ide-html-generator.c
@@ -0,0 +1,289 @@
+/* ide-html-generator.c
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-html-generator"
+
+#include "config.h"
+
+#include <libide-threading.h>
+
+#include "ide-html-generator.h"
+#include "ide-text-buffer-html-generator.h"
+
+typedef struct
+{
+  char *base_uri;
+} IdeHtmlGeneratorPrivate;
+
+G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (IdeHtmlGenerator, ide_html_generator, G_TYPE_OBJECT)
+
+enum {
+  PROP_0,
+  PROP_BASE_URI,
+  N_PROPS
+};
+
+enum {
+  INVALIDATE,
+  N_SIGNALS
+};
+
+static guint signals[N_SIGNALS];
+static GParamSpec *properties[N_PROPS];
+
+static void
+ide_html_generator_real_generate_async (IdeHtmlGenerator    *self,
+                                        GCancellable        *cancellable,
+                                        GAsyncReadyCallback  callback,
+                                        gpointer             user_data)
+{
+  g_autoptr(IdeTask) task = NULL;
+
+  task = ide_task_new (self, cancellable, callback, user_data);
+  ide_task_set_source_tag (task, ide_html_generator_real_generate_async);
+  ide_task_return_unsupported_error (task);
+}
+
+static GBytes *
+ide_html_generator_real_generate_finish (IdeHtmlGenerator  *self,
+                                         GAsyncResult      *result,
+                                         GError           **error)
+{
+  return ide_task_propagate_pointer (IDE_TASK (result), error);
+}
+
+static void
+ide_html_generator_dispose (GObject *object)
+{
+  IdeHtmlGenerator *self = (IdeHtmlGenerator *)object;
+  IdeHtmlGeneratorPrivate *priv = ide_html_generator_get_instance_private (self);
+
+  g_clear_pointer (&priv->base_uri, g_free);
+
+  G_OBJECT_CLASS (ide_html_generator_parent_class)->dispose (object);
+}
+
+static void
+ide_html_generator_get_property (GObject    *object,
+                                 guint       prop_id,
+                                 GValue     *value,
+                                 GParamSpec *pspec)
+{
+  IdeHtmlGenerator *self = IDE_HTML_GENERATOR (object);
+
+  switch (prop_id)
+    {
+    case PROP_BASE_URI:
+      g_value_set_string (value, ide_html_generator_get_base_uri (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_html_generator_set_property (GObject      *object,
+                                 guint         prop_id,
+                                 const GValue *value,
+                                 GParamSpec   *pspec)
+{
+  IdeHtmlGenerator *self = IDE_HTML_GENERATOR (object);
+
+  switch (prop_id)
+    {
+    case PROP_BASE_URI:
+      ide_html_generator_set_base_uri (self, g_value_get_string (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_html_generator_class_init (IdeHtmlGeneratorClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->dispose = ide_html_generator_dispose;
+  object_class->get_property = ide_html_generator_get_property;
+  object_class->set_property = ide_html_generator_set_property;
+
+  klass->generate_async = ide_html_generator_real_generate_async;
+  klass->generate_finish = ide_html_generator_real_generate_finish;
+
+  /**
+   * IdeHtmlGenerator::invalidate:
+   *
+   * The "invalidate" signal is emitted when contents have changed.
+   *
+   * This signal is emmitted by subclasses when the contents have changed
+   * and HTML will need to be regenerated.
+   */
+  signals [INVALIDATE] =
+    g_signal_new ("invalidate",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (IdeHtmlGeneratorClass, invalidate),
+                  NULL, NULL,
+                  NULL,
+                  G_TYPE_NONE, 0);
+}
+
+static void
+ide_html_generator_init (IdeHtmlGenerator *self)
+{
+}
+
+/**
+ * ide_html_generator_generate_async:
+ * @self: a #IdeHtmlGenerator
+ * @cancellable: a #GCancellable
+ * @callback: a function to call after completion
+ * @user_data: closure data for @callback
+ *
+ * Asynchronously generate HTML.
+ *
+ * This virtual function should be implemented by subclasses to generate
+ * HTML based on some form of input (which is left to the subclass).
+ *
+ * Upon completion, @callback is called and expected to call
+ * ide_html_generator_generate_finish() to retrieve the result.
+ */
+void
+ide_html_generator_generate_async (IdeHtmlGenerator    *self,
+                                   GCancellable        *cancellable,
+                                   GAsyncReadyCallback  callback,
+                                   gpointer             user_data)
+{
+  g_return_if_fail (IDE_IS_HTML_GENERATOR (self));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  IDE_HTML_GENERATOR_GET_CLASS (self)->generate_async (self, cancellable, callback, user_data);
+}
+
+/**
+ * ide_html_generator_generate_finish:
+ * @self: a #IdeHtmlGenerator
+ * @result: a #GAsyncResult
+ * @error: a location for a #GError
+ *
+ * Completes a request to generate HTML.
+ *
+ * This function is used to complete a request to generate HTML from some
+ * form of input, asynchronously. The content of the HTML is dependent on
+ * the subclass implementation of #IdeHtmlGenerator.
+ *
+ * It is required that the resulting bytes have a NULL terminator at
+ * the end which is not part of the bytes length.
+ *
+ * Returns: (transfer full): a #GBytes if successful; otherwise %NULL
+ *   and @error is set.
+ */
+GBytes *
+ide_html_generator_generate_finish (IdeHtmlGenerator  *self,
+                                    GAsyncResult      *result,
+                                    GError           **error)
+{
+  GBytes *ret;
+
+  g_return_val_if_fail (IDE_IS_HTML_GENERATOR (self), NULL);
+  g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
+
+  ret = IDE_HTML_GENERATOR_GET_CLASS (self)->generate_finish (self, result, error);
+
+#ifdef G_ENABLE_DEBUG
+  if (ret != NULL)
+    {
+      const guint8 *data;
+      gsize len;
+
+      data = g_bytes_get_data (ret, &len);
+      g_assert (data[len] == 0);
+    }
+#endif
+
+  return ret;
+}
+
+/**
+ * ide_html_generator_invalidate:
+ * @self: a #IdeHtmlGenerator
+ *
+ * Notifies that the last generated HTML is now invalid.
+ *
+ * This is used by subclasses to denote that the HTML contents
+ * have changed and will need to be regenerated.
+ */
+void
+ide_html_generator_invalidate (IdeHtmlGenerator *self)
+{
+  g_return_if_fail (IDE_IS_HTML_GENERATOR (self));
+
+  g_signal_emit (self, signals [INVALIDATE], 0);
+}
+
+/**
+ * ide_html_generator_new_for_buffer:
+ * @buffer: a #GtkTextBuffer
+ *
+ * Create a 1:1 HTML generator for a buffer.
+ *
+ * Creates a #IdeHtmlGenerator that passes the content directly from
+ * what is found in a #GtkTextBuffer.
+ *
+ * Returns: (transfer full): an #IdeHtmlGenerator
+ */
+IdeHtmlGenerator *
+ide_html_generator_new_for_buffer (GtkTextBuffer *buffer)
+{
+  g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
+
+  return g_object_new (IDE_TYPE_TEXT_BUFFER_HTML_GENERATOR,
+                       "buffer", buffer,
+                       NULL);
+}
+
+const char *
+ide_html_generator_get_base_uri (IdeHtmlGenerator *self)
+{
+  IdeHtmlGeneratorPrivate *priv = ide_html_generator_get_instance_private (self);
+
+  g_return_val_if_fail (IDE_IS_HTML_GENERATOR (self), NULL);
+
+  return priv->base_uri;
+}
+
+void
+ide_html_generator_set_base_uri (IdeHtmlGenerator *self,
+                                 const char       *base_uri)
+{
+  IdeHtmlGeneratorPrivate *priv = ide_html_generator_get_instance_private (self);
+
+  g_return_if_fail (IDE_IS_HTML_GENERATOR (self));
+
+  if (g_strcmp0 (priv->base_uri, base_uri) != 0)
+    {
+      g_free (priv->base_uri);
+      priv->base_uri = g_strdup (base_uri);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_BASE_URI]);
+    }
+}
diff --git a/src/libide/webkit/ide-html-generator.h b/src/libide/webkit/ide-html-generator.h
new file mode 100644
index 000000000..aef2ec92d
--- /dev/null
+++ b/src/libide/webkit/ide-html-generator.h
@@ -0,0 +1,67 @@
+/* ide-html-generator.h
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <libide-core.h>
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_HTML_GENERATOR (ide_html_generator_get_type())
+
+IDE_AVAILABLE_IN_ALL
+G_DECLARE_DERIVABLE_TYPE (IdeHtmlGenerator, ide_html_generator, IDE, HTML_GENERATOR, GObject)
+
+struct _IdeHtmlGeneratorClass
+{
+  GObjectClass parent_class;
+
+  void    (*invalidate)      (IdeHtmlGenerator     *self);
+  void    (*generate_async)  (IdeHtmlGenerator     *self,
+                              GCancellable         *cancellable,
+                              GAsyncReadyCallback   callback,
+                              gpointer              user_data);
+  GBytes *(*generate_finish) (IdeHtmlGenerator     *self,
+                              GAsyncResult         *result,
+                              GError              **error);
+};
+
+IDE_AVAILABLE_IN_ALL
+const char       *ide_html_generator_get_base_uri    (IdeHtmlGenerator     *self);
+IDE_AVAILABLE_IN_ALL
+void              ide_html_generator_set_base_uri    (IdeHtmlGenerator     *self,
+                                                      const char           *base_uri);
+IDE_AVAILABLE_IN_ALL
+void              ide_html_generator_invalidate      (IdeHtmlGenerator     *self);
+IDE_AVAILABLE_IN_ALL
+void              ide_html_generator_generate_async  (IdeHtmlGenerator     *self,
+                                                      GCancellable         *cancellable,
+                                                      GAsyncReadyCallback   callback,
+                                                      gpointer              user_data);
+IDE_AVAILABLE_IN_ALL
+GBytes           *ide_html_generator_generate_finish (IdeHtmlGenerator     *self,
+                                                      GAsyncResult         *result,
+                                                      GError              **error);
+IDE_AVAILABLE_IN_ALL
+IdeHtmlGenerator *ide_html_generator_new_for_buffer  (GtkTextBuffer        *buffer);
+
+G_END_DECLS
diff --git a/src/libide/webkit/ide-text-buffer-html-generator.c 
b/src/libide/webkit/ide-text-buffer-html-generator.c
new file mode 100644
index 000000000..5764c2148
--- /dev/null
+++ b/src/libide/webkit/ide-text-buffer-html-generator.c
@@ -0,0 +1,205 @@
+/* ide-text-buffer-html-generator.c
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "ide-text-buffer-html-generator"
+
+#include "config.h"
+
+#include <libide-code.h>
+#include <libide-threading.h>
+
+#include "ide-text-buffer-html-generator.h"
+
+struct _IdeTextBufferHtmlGenerator
+{
+  IdeHtmlGenerator parent_instance;
+  GSignalGroup *buffer_signals;
+};
+
+enum {
+  PROP_0,
+  PROP_BUFFER,
+  N_PROPS
+};
+
+G_DEFINE_FINAL_TYPE (IdeTextBufferHtmlGenerator, ide_text_buffer_html_generator, IDE_TYPE_HTML_GENERATOR)
+
+static GParamSpec *properties [N_PROPS];
+
+static inline GBytes *
+get_buffer_bytes (GtkTextBuffer *buffer)
+{
+  GtkTextIter begin, end;
+  char *slice;
+
+  gtk_text_buffer_get_bounds (buffer, &begin, &end);
+  slice = gtk_text_iter_get_slice (&begin, &end);
+
+  return g_bytes_new_take (slice, strlen (slice));
+}
+
+static void
+ide_text_buffer_html_generator_generate_async (IdeHtmlGenerator    *generator,
+                                               GCancellable        *cancellable,
+                                               GAsyncReadyCallback  callback,
+                                               gpointer             user_data)
+{
+  IdeTextBufferHtmlGenerator *self = (IdeTextBufferHtmlGenerator *)generator;
+  g_autoptr(GtkTextBuffer) buffer = NULL;
+  g_autoptr(IdeTask) task = NULL;
+
+  g_assert (IDE_IS_TEXT_BUFFER_HTML_GENERATOR (self));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = ide_task_new (self, cancellable, callback, user_data);
+  ide_task_set_source_tag (task, ide_text_buffer_html_generator_generate_async);
+
+  buffer = g_signal_group_dup_target (self->buffer_signals);
+
+  if (IDE_IS_BUFFER (buffer))
+    ide_task_return_pointer (task,
+                             ide_buffer_dup_content (IDE_BUFFER (buffer)),
+                             g_bytes_unref);
+  else if (GTK_IS_TEXT_BUFFER (buffer))
+    ide_task_return_pointer (task, get_buffer_bytes (buffer), g_bytes_unref);
+  else
+    ide_task_return_pointer (task,
+                             g_bytes_new_take (g_strdup (""), 0),
+                             g_bytes_unref);
+}
+
+static GBytes *
+ide_text_buffer_html_generator_generate_finish (IdeHtmlGenerator  *generator,
+                                                GAsyncResult      *result,
+                                                GError           **error)
+{
+  g_assert (IDE_IS_TEXT_BUFFER_HTML_GENERATOR (generator));
+  g_assert (IDE_IS_TASK (result));
+
+  return ide_task_propagate_pointer (IDE_TASK (result), error);
+}
+
+static gboolean
+file_to_base_uri (GBinding     *binding,
+                  const GValue *from,
+                  GValue       *to,
+                  gpointer      user_data)
+{
+  g_value_set_string (to, g_file_get_uri (g_value_get_object (from)));
+  return TRUE;
+}
+
+static void
+ide_text_buffer_html_generator_set_buffer (IdeTextBufferHtmlGenerator *self,
+                                           GtkTextBuffer              *buffer)
+{
+  g_assert (IDE_IS_TEXT_BUFFER_HTML_GENERATOR (self));
+  g_assert (!buffer || GTK_IS_TEXT_BUFFER (buffer));
+
+  g_signal_group_set_target (self->buffer_signals, buffer);
+
+  if (IDE_IS_BUFFER (buffer))
+    g_object_bind_property_full (buffer, "file",
+                                 self, "base-uri",
+                                 G_BINDING_SYNC_CREATE,
+                                 file_to_base_uri,
+                                 NULL, NULL, NULL);
+}
+
+static void
+ide_text_buffer_html_generator_dispose (GObject *object)
+{
+  IdeTextBufferHtmlGenerator *self = (IdeTextBufferHtmlGenerator *)object;
+
+  g_clear_object (&self->buffer_signals);
+
+  G_OBJECT_CLASS (ide_text_buffer_html_generator_parent_class)->dispose (object);
+}
+
+static void
+ide_text_buffer_html_generator_get_property (GObject    *object,
+                                             guint       prop_id,
+                                             GValue     *value,
+                                             GParamSpec *pspec)
+{
+  IdeTextBufferHtmlGenerator *self = IDE_TEXT_BUFFER_HTML_GENERATOR (object);
+
+  switch (prop_id)
+    {
+    case PROP_BUFFER:
+      g_value_take_object (value, g_signal_group_dup_target (self->buffer_signals));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_text_buffer_html_generator_set_property (GObject      *object,
+                                             guint         prop_id,
+                                             const GValue *value,
+                                             GParamSpec   *pspec)
+{
+  IdeTextBufferHtmlGenerator *self = IDE_TEXT_BUFFER_HTML_GENERATOR (object);
+
+  switch (prop_id)
+    {
+    case PROP_BUFFER:
+      ide_text_buffer_html_generator_set_buffer (self, g_value_get_object (value));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+ide_text_buffer_html_generator_class_init (IdeTextBufferHtmlGeneratorClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  IdeHtmlGeneratorClass *generator_class = IDE_HTML_GENERATOR_CLASS (klass);
+
+  object_class->dispose = ide_text_buffer_html_generator_dispose;
+  object_class->get_property = ide_text_buffer_html_generator_get_property;
+  object_class->set_property = ide_text_buffer_html_generator_set_property;
+
+  generator_class->generate_async = ide_text_buffer_html_generator_generate_async;
+  generator_class->generate_finish = ide_text_buffer_html_generator_generate_finish;
+
+  properties [PROP_BUFFER] =
+    g_param_spec_object ("buffer", NULL, NULL,
+                         GTK_TYPE_TEXT_BUFFER,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+ide_text_buffer_html_generator_init (IdeTextBufferHtmlGenerator *self)
+{
+  self->buffer_signals = g_signal_group_new (GTK_TYPE_TEXT_BUFFER);
+
+  g_signal_group_connect_object (self->buffer_signals,
+                                 "changed",
+                                 G_CALLBACK (ide_html_generator_invalidate),
+                                 self,
+                                 G_CONNECT_SWAPPED);
+}
diff --git a/src/libide/webkit/ide-text-buffer-html-generator.h 
b/src/libide/webkit/ide-text-buffer-html-generator.h
new file mode 100644
index 000000000..843910b33
--- /dev/null
+++ b/src/libide/webkit/ide-text-buffer-html-generator.h
@@ -0,0 +1,31 @@
+/* ide-text-buffer-html-generator.h
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include "ide-html-generator.h"
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_TEXT_BUFFER_HTML_GENERATOR (ide_text_buffer_html_generator_get_type())
+
+G_DECLARE_FINAL_TYPE (IdeTextBufferHtmlGenerator, ide_text_buffer_html_generator, IDE, 
TEXT_BUFFER_HTML_GENERATOR, IdeHtmlGenerator)
+
+G_END_DECLS
diff --git a/src/libide/webkit/ide-webkit-page.c b/src/libide/webkit/ide-webkit-page.c
index ab8cb5ae9..b5bc30680 100644
--- a/src/libide/webkit/ide-webkit-page.c
+++ b/src/libide/webkit/ide-webkit-page.c
@@ -27,8 +27,6 @@
 #include "ide-webkit-page.h"
 #include "ide-url-bar.h"
 
-#define UPDATE_DELAY_MSEC 250
-
 typedef struct
 {
   GtkStack           *reload_stack;
@@ -39,12 +37,11 @@ typedef struct
 
   GSimpleActionGroup *actions;
 
-  /* Used for pages linked to buffers */
-  GtkTextBuffer        *buffer;
-  IdeHtmlTransformFunc  transform_func;
-  gpointer              transform_data;
-  GDestroyNotify        transform_data_destroy;
-  guint                 queued_update_source;
+  IdeHtmlGenerator   *generator;
+
+  guint               dirty : 1;
+  guint               generating : 1;
+  guint               disposed : 1;
 } IdeWebkitPagePrivate;
 
 enum {
@@ -343,17 +340,10 @@ ide_webkit_page_dispose (GObject *object)
   IdeWebkitPage *self = (IdeWebkitPage *)object;
   IdeWebkitPagePrivate *priv = ide_webkit_page_get_instance_private (self);
 
-  if (priv->transform_data_destroy)
-    {
-      GDestroyNotify notify = g_steal_pointer (&priv->transform_data_destroy);
-      gpointer data = g_steal_pointer (&priv->transform_data);
-      priv->transform_func = NULL;
-      notify (data);
-    }
+  priv->disposed = TRUE;
 
+  g_clear_object (&priv->generator);
   g_clear_object (&priv->actions);
-  g_clear_object (&priv->buffer);
-  g_clear_handle_id (&priv->queued_update_source, g_source_remove);
 
   G_OBJECT_CLASS (ide_webkit_page_parent_class)->dispose (object);
 }
@@ -593,79 +583,91 @@ ide_webkit_page_reload_ignoring_cache (IdeWebkitPage *self)
   webkit_web_view_reload_bypass_cache (priv->web_view);
 }
 
-static gboolean
-ide_webkit_page_do_update_cb (gpointer user_data)
+static void
+ide_webkit_page_generate_cb (GObject      *object,
+                             GAsyncResult *result,
+                             gpointer      user_data)
 {
-  IdeWebkitPage *self = user_data;
+  IdeHtmlGenerator *generator = (IdeHtmlGenerator *)object;
+  g_autoptr(IdeWebkitPage) self = user_data;
   IdeWebkitPagePrivate *priv = ide_webkit_page_get_instance_private (self);
-  g_autofree char *base_uri = NULL;
-  g_autofree char *text = NULL;
-  GtkTextIter begin, end;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GBytes) bytes = NULL;
 
-  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (IDE_IS_HTML_GENERATOR (generator));
+  g_assert (G_IS_ASYNC_RESULT (result));
   g_assert (IDE_IS_WEBKIT_PAGE (self));
-  g_assert (GTK_IS_TEXT_BUFFER (priv->buffer));
-
-  priv->queued_update_source = 0;
-
-  gtk_text_buffer_get_bounds (priv->buffer, &begin, &end);
-  text = gtk_text_iter_get_slice (&begin, &end);
 
-  if (IDE_IS_BUFFER (priv->buffer))
-    base_uri = g_file_get_uri (ide_buffer_get_file (IDE_BUFFER (priv->buffer)));
+  priv->generating = FALSE;
 
-  if (priv->transform_func != NULL)
+  if (!(bytes = ide_html_generator_generate_finish (generator, result, &error)))
     {
-      g_autofree char *input = g_steal_pointer (&text);
-      text = priv->transform_func (input, priv->transform_data);
+      /* Don't try to spin again in this case by checking dirty */
+      g_warning ("Failed to generate HTML: %s", error->message);
+      return;
     }
 
-  webkit_web_view_load_html (priv->web_view, text, base_uri);
+  if (priv->disposed)
+    return;
+
+  webkit_web_view_load_html (priv->web_view,
+                             (const char *)g_bytes_get_data (bytes, NULL),
+                             ide_html_generator_get_base_uri (generator));
 
-  return G_SOURCE_REMOVE;
+  /* See if we need to run again, and check for re-entrantcy */
+  if (priv->dirty && !priv->generating)
+    {
+      priv->dirty = FALSE;
+      priv->generating = TRUE;
+      ide_html_generator_generate_async (generator,
+                                         NULL,
+                                         ide_webkit_page_generate_cb,
+                                         g_steal_pointer (&self));
+    }
 }
 
 static void
-ide_webkit_page_buffer_changed_cb (IdeWebkitPage *self,
-                                   GtkTextBuffer *buffer)
+ide_webkit_page_generator_invalidate_cb (IdeWebkitPage    *self,
+                                         IdeHtmlGenerator *generator)
 {
   IdeWebkitPagePrivate *priv = ide_webkit_page_get_instance_private (self);
 
   g_assert (IDE_IS_MAIN_THREAD ());
   g_assert (IDE_IS_WEBKIT_PAGE (self));
-  g_assert (GTK_IS_TEXT_BUFFER (buffer));
+  g_assert (IDE_IS_HTML_GENERATOR (generator));
+
+  priv->dirty = TRUE;
 
-  if (priv->queued_update_source == 0)
-    priv->queued_update_source = g_timeout_add (UPDATE_DELAY_MSEC,
-                                                ide_webkit_page_do_update_cb,
-                                                self);
+  if (priv->generating)
+    return;
+
+  priv->generating = TRUE;
+  priv->dirty = FALSE;
+
+  ide_html_generator_generate_async (generator,
+                                     NULL,
+                                     ide_webkit_page_generate_cb,
+                                     g_object_ref (self));
 }
 
 IdeWebkitPage *
-ide_webkit_page_new_for_buffer (GtkTextBuffer        *buffer,
-                                IdeHtmlTransformFunc  transform_func,
-                                gpointer              transform_data,
-                                GDestroyNotify        transform_data_destroy)
+ide_webkit_page_new_for_generator (IdeHtmlGenerator *generator)
 {
   IdeWebkitPage *self;
   IdeWebkitPagePrivate *priv;
 
-  g_return_val_if_fail (GTK_IS_TEXT_BUFFER (buffer), NULL);
+  g_return_val_if_fail (IDE_IS_HTML_GENERATOR (generator), NULL);
 
   self = ide_webkit_page_new ();
   priv = ide_webkit_page_get_instance_private (self);
 
-  priv->transform_func = transform_func;
-  priv->transform_data = transform_data;
-  priv->transform_data_destroy = transform_data_destroy;
-
-  priv->buffer = g_object_ref (buffer);
-  g_signal_connect_object (buffer,
-                           "changed",
-                           G_CALLBACK (ide_webkit_page_buffer_changed_cb),
+  priv->generator = g_object_ref (generator);
+  g_signal_connect_object (priv->generator,
+                           "invalidate",
+                           G_CALLBACK (ide_webkit_page_generator_invalidate_cb),
                            self,
                            G_CONNECT_SWAPPED);
-  ide_webkit_page_buffer_changed_cb (self, buffer);
+  ide_webkit_page_generator_invalidate_cb (self, generator);
 
   return self;
 }
diff --git a/src/libide/webkit/ide-webkit-page.h b/src/libide/webkit/ide-webkit-page.h
index 26e47335c..091511fbe 100644
--- a/src/libide/webkit/ide-webkit-page.h
+++ b/src/libide/webkit/ide-webkit-page.h
@@ -22,13 +22,12 @@
 
 #include <libide-gui.h>
 
+#include "ide-html-generator.h"
+
 G_BEGIN_DECLS
 
 #define IDE_TYPE_WEBKIT_PAGE (ide_webkit_page_get_type())
 
-typedef char *(*IdeHtmlTransformFunc) (const char *input,
-                                       gpointer    user_data);
-
 IDE_AVAILABLE_IN_ALL
 G_DECLARE_DERIVABLE_TYPE (IdeWebkitPage, ide_webkit_page, IDE, WEBKIT_PAGE, IdePage)
 
@@ -40,10 +39,7 @@ struct _IdeWebkitPageClass
 IDE_AVAILABLE_IN_ALL
 IdeWebkitPage *ide_webkit_page_new                   (void);
 IDE_AVAILABLE_IN_ALL
-IdeWebkitPage *ide_webkit_page_new_for_buffer        (GtkTextBuffer        *buffer,
-                                                      IdeHtmlTransformFunc  transform_func,
-                                                      gpointer              transform_data,
-                                                      GDestroyNotify        transform_data_destroy);
+IdeWebkitPage *ide_webkit_page_new_for_generator     (IdeHtmlGenerator     *generator);
 IDE_AVAILABLE_IN_ALL
 void           ide_webkit_page_load_uri              (IdeWebkitPage        *self,
                                                       const char           *uri);
diff --git a/src/libide/webkit/meson.build b/src/libide/webkit/meson.build
index c06cc4246..43011c64a 100644
--- a/src/libide/webkit/meson.build
+++ b/src/libide/webkit/meson.build
@@ -13,6 +13,7 @@ libide_include_directories += include_directories('.')
 libide_webkit_private_sources = [
   'ide-webkit-plugin.c',
   'ide-webkit-util.c',
+  'ide-text-buffer-html-generator.c',
   'ide-url-bar.c',
 ]
 
@@ -22,10 +23,12 @@ libide_webkit_private_sources = [
 
 libide_webkit_public_headers = [
   'libide-webkit.h',
+  'ide-html-generator.h',
   'ide-webkit-page.h',
 ]
 
 libide_webkit_public_sources = [
+  'ide-html-generator.c',
   'ide-webkit-page.c',
 ]
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]