unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Po Lu <luangruo@yahoo.com>
To: Lars Ingebrigtsen <larsi@gnus.org>
Cc: emacs-devel@gnu.org
Subject: Introduce "killed" state for xwidgets (Re: shr using `make-xwidget' incorrectly)
Date: Thu, 11 Nov 2021 14:37:49 +0800	[thread overview]
Message-ID: <87tugjxkn6.fsf_-_@yahoo.com> (raw)
In-Reply-To: <87fss3e5kc.fsf@gnus.org> (Lars Ingebrigtsen's message of "Thu, 11 Nov 2021 04:26:11 +0100")

[-- Attachment #1: Type: text/plain, Size: 369 bytes --]

Lars Ingebrigtsen <larsi@gnus.org> writes:

>> I think the best thing to do in this case would be to introduce a
>> `dead' state for xwidgets, not unlike killed buffers and dead frames,
>> which an xwidget is set to after being killed.
>>
>> Then, performing any sort of operation on a killed xwidget can either
>> throw an error, or do nothing.

> Sounds good to me.


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: 0001-Prevent-crashes-in-xwidgets-whose-buffers-have-been-.patch --]
[-- Type: text/x-patch, Size: 12941 bytes --]

From 7db8a74c8eb463db9b4db20fb5c3964b28f1c521 Mon Sep 17 00:00:00 2001
From: Po Lu <luangruo@yahoo.com>
Date: Thu, 11 Nov 2021 14:29:12 +0800
Subject: [PATCH] Prevent crashes in xwidgets whose buffers have been killed

* doc/lispref/display.texi (Xwidgets): Explain meaning of killed
xwidgets.
* src/xwidget.c (Fxwidget_live_p): New function.
(Fxwidget_perform_lispy_event, WEBKIT_FN_INIT)
(Fxwidget_resize, Fxwidget_size_request)
(Fxwidget_info, Fxwidget_plist)
(Fset_xwidget_buffer, Fset_xwidget_plist)
(Fset_xwidget_query_on_exit_flag)
(Fxwidget_query_on_exit_flag)
(Fxwidget_webkit_search)
(Fxwidget_webkit_next_result)
(Fxwidget_webkit_previous_result)
(Fxwidget_webkit_finish_search)
(Fxwidget_webkit_load_html): Check for live xwidgets instead of
just xwidgets.

(xwidget_button, xwidget_motion_or_crossing)
(xv_do_draw, x_draw_xwidget_glyph_string)
(Fdelete_xwidget_view): Ignore killed xwidgets.

(syms_of_xwidget): Define new symbols and subrs and define
appropriate weakness of id_to_xwidget map.
(kill_buffer_xwidgets): Check live xwidgets instead of
killed xwidgets, set xwidget buffer to nil, and rely on GC to
free the hash table for us instead.

* src/xwidget.h (XWIDGET_LIVE_P, CHECK_LIVE_XWIDGET): New
macros.
---
 doc/lispref/display.texi |  14 ++++-
 src/xwidget.c            | 116 +++++++++++++++++++++++++++------------
 src/xwidget.h            |   7 +++
 3 files changed, 101 insertions(+), 36 deletions(-)

diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi
index 094bb5a5f3..63d2838eaa 100644
--- a/doc/lispref/display.texi
+++ b/doc/lispref/display.texi
@@ -6804,6 +6804,12 @@ Xwidgets
 is used internally by the WebKit widget, and specifies another WebKit
 widget that the newly created widget should share settings and
 subprocesses with.
+
+The xwidget that is returned will be killed alongside its buffer
+(@pxref{Killing Buffers}).  Once it is killed, the xwidget may
+continue to exist as a Lisp object and act as a @code{display}
+property until all references to it are gone, but most actions that
+can be performed on live xwidgets will no longer be available.
 @end defun
 
 @defun xwidgetp object
@@ -6811,6 +6817,11 @@ Xwidgets
 @code{nil} otherwise.
 @end defun
 
+@defun xwidget-live-p object
+This function returns @code{t} if @var{object} is an xwidget that
+hasn't been killed, and @code{nil} otherwise.
+@end defun
+
 @defun xwidget-plist xwidget
 This function returns the property list of @var{xwidget}.
 @end defun
@@ -6821,7 +6832,8 @@ Xwidgets
 @end defun
 
 @defun xwidget-buffer xwidget
-This function returns the buffer of @var{xwidget}.
+This function returns the buffer of @var{xwidget}.  If @var{xwidget}
+has been killed, it returns @code{nil}.
 @end defun
 
 @defun set-xwidget-buffer xwidget buffer
diff --git a/src/xwidget.c b/src/xwidget.c
index ace63e3742..4c18c531b9 100644
--- a/src/xwidget.c
+++ b/src/xwidget.c
@@ -273,6 +273,16 @@ DEFUN ("make-xwidget",
   return val;
 }
 
+DEFUN ("xwidget-live-p", Fxwidget_live_p, Sxwidget_live_p,
+       1, 1, 0, doc: /* Return t if OBJECT is an xwidget that has not been killed.
+Value is nil if OBJECT is not an xwidget or if it has been killed.  */)
+  (Lisp_Object object)
+{
+  return ((XWIDGETP (object)
+	   && !NILP (XXWIDGET (object)->buffer))
+	  ? Qt : Qnil);
+}
+
 #ifdef USE_GTK
 static void
 set_widget_if_text_view (GtkWidget *widget, void *data)
@@ -305,7 +315,7 @@ DEFUN ("xwidget-perform-lispy-event",
   GtkWidget *temp = NULL;
 #endif
 
-  CHECK_XWIDGET (xwidget);
+  CHECK_LIVE_XWIDGET (xwidget);
   xw = XXWIDGET (xwidget);
 
   if (!NILP (frame))
@@ -807,6 +817,9 @@ xwidget_button (struct xwidget_view *view,
 		bool down_p, int x, int y, int button,
 		int modifier_state, Time time)
 {
+  if (NILP (XXWIDGET (view->model)->buffer))
+    return;
+
   record_osr_embedder (view);
 
   if (button < 4 || button > 8)
@@ -857,22 +870,29 @@ xwidget_button (struct xwidget_view *view,
 void
 xwidget_motion_or_crossing (struct xwidget_view *view, const XEvent *event)
 {
-  GdkEvent *xg_event = gdk_event_new (event->type == MotionNotify
-				      ? GDK_MOTION_NOTIFY
-				      : (event->type == LeaveNotify
-					 ? GDK_LEAVE_NOTIFY
-					 : GDK_ENTER_NOTIFY));
+  GdkEvent *xg_event;
   struct xwidget *model = XXWIDGET (view->model);
   int x;
   int y;
-  GtkWidget *target = find_widget_at_pos (model->widgetwindow_osr,
-					  (event->type == MotionNotify
-					   ? event->xmotion.x + view->clip_left
-					   : event->xcrossing.x + view->clip_left),
-					  (event->type == MotionNotify
-					   ? event->xmotion.y + view->clip_top
-					   : event->xcrossing.y + view->clip_top),
-					  &x, &y);
+  GtkWidget *target;
+
+  if (NILP (model->buffer))
+    return;
+
+  xg_event = gdk_event_new (event->type == MotionNotify
+			    ? GDK_MOTION_NOTIFY
+			    : (event->type == LeaveNotify
+			       ? GDK_LEAVE_NOTIFY
+			       : GDK_ENTER_NOTIFY));
+
+  target = find_widget_at_pos (model->widgetwindow_osr,
+			       (event->type == MotionNotify
+				? event->xmotion.x + view->clip_left
+				: event->xcrossing.x + view->clip_left),
+			       (event->type == MotionNotify
+				? event->xmotion.y + view->clip_top
+				: event->xcrossing.y + view->clip_top),
+			       &x, &y);
 
   if (!target)
     target = model->widget_osr;
@@ -969,6 +989,13 @@ xv_do_draw (struct xwidget_view *xw, struct xwidget *w)
 {
   GtkOffscreenWindow *wnd;
   cairo_surface_t *surface;
+
+  if (NILP (w->buffer))
+    {
+      XClearWindow (xw->dpy, xw->wdesc);
+      return;
+    }
+
   block_input ();
   wnd = GTK_OFFSCREEN_WINDOW (w->widgetwindow_osr);
   surface = gtk_offscreen_window_get_surface (wnd);
@@ -1651,14 +1678,25 @@ x_draw_xwidget_glyph_string (struct glyph_string *s)
      a redraw.  It seems its possible to get out of sync with emacs
      redraws so emacs background sometimes shows up instead of the
      xwidgets background.  It's just a visual glitch though.  */
-  if (!xwidget_hidden (xv))
+  /* When xww->buffer is nil, that means the xwidget has been killed.  */
+  if (!NILP (xww->buffer))
     {
+      if (!xwidget_hidden (xv))
+	{
 #ifdef USE_GTK
-      gtk_widget_queue_draw (xww->widget_osr);
+	  gtk_widget_queue_draw (xww->widget_osr);
 #elif defined NS_IMPL_COCOA
-      nsxwidget_set_needsdisplay (xv);
+	  nsxwidget_set_needsdisplay (xv);
 #endif
+	}
+    }
+#ifdef USE_GTK
+  else
+    {
+      XSetWindowBackground (xv->dpy, xv->wdesc,
+			    FRAME_BACKGROUND_PIXEL (s->f));
     }
+#endif
 
 #ifdef USE_GTK
   unblock_input ();
@@ -1677,7 +1715,7 @@ xwidget_is_web_view (struct xwidget *xw)
 
 /* Macro that checks xwidget hold webkit web view first.  */
 #define WEBKIT_FN_INIT()						\
-  CHECK_XWIDGET (xwidget);						\
+  CHECK_LIVE_XWIDGET (xwidget);						\
   struct xwidget *xw = XXWIDGET (xwidget);				\
   if (!xwidget_is_web_view (xw))					\
     {									\
@@ -1856,7 +1894,7 @@ DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0,
        doc: /* Resize XWIDGET to NEW_WIDTH, NEW_HEIGHT.  */ )
   (Lisp_Object xwidget, Lisp_Object new_width, Lisp_Object new_height)
 {
-  CHECK_XWIDGET (xwidget);
+  CHECK_LIVE_XWIDGET (xwidget);
   int w = check_integer_range (new_width, 0, INT_MAX);
   int h = check_integer_range (new_height, 0, INT_MAX);
   struct xwidget *xw = XXWIDGET (xwidget);
@@ -1907,7 +1945,7 @@ DEFUN ("xwidget-size-request",
 Emacs allocated area accordingly.  */)
   (Lisp_Object xwidget)
 {
-  CHECK_XWIDGET (xwidget);
+  CHECK_LIVE_XWIDGET (xwidget);
 #ifdef USE_GTK
   GtkRequisition requisition;
   gtk_widget_size_request (XXWIDGET (xwidget)->widget_osr, &requisition);
@@ -1942,7 +1980,7 @@ DEFUN ("xwidget-info",
 Currently [TYPE TITLE WIDTH HEIGHT].  */)
   (Lisp_Object xwidget)
 {
-  CHECK_XWIDGET (xwidget);
+  CHECK_LIVE_XWIDGET (xwidget);
   struct xwidget *xw = XXWIDGET (xwidget);
   return CALLN (Fvector, xw->type, xw->title,
 		make_fixed_natnum (xw->width), make_fixed_natnum (xw->height));
@@ -2005,7 +2043,7 @@ DEFUN ("delete-xwidget-view",
       unblock_input ();
     }
 
-  if (xw->embedder_view == xv)
+  if (xw->embedder_view == xv && !NILP (xw->buffer))
     {
       w = gtk_widget_get_window (xw->widgetwindow_osr);
 
@@ -2054,7 +2092,7 @@ DEFUN ("xwidget-plist",
        doc: /* Return the plist of XWIDGET.  */)
   (Lisp_Object xwidget)
 {
-  CHECK_XWIDGET (xwidget);
+  CHECK_LIVE_XWIDGET (xwidget);
   return XXWIDGET (xwidget)->plist;
 }
 
@@ -2074,7 +2112,7 @@ DEFUN ("set-xwidget-buffer",
        doc: /* Set XWIDGET's buffer to BUFFER.  */)
   (Lisp_Object xwidget, Lisp_Object buffer)
 {
-  CHECK_XWIDGET (xwidget);
+  CHECK_LIVE_XWIDGET (xwidget);
   CHECK_BUFFER (buffer);
 
   XXWIDGET (xwidget)->buffer = buffer;
@@ -2088,7 +2126,7 @@ DEFUN ("set-xwidget-plist",
 Returns PLIST.  */)
   (Lisp_Object xwidget, Lisp_Object plist)
 {
-  CHECK_XWIDGET (xwidget);
+  CHECK_LIVE_XWIDGET (xwidget);
   CHECK_LIST (plist);
 
   XXWIDGET (xwidget)->plist = plist;
@@ -2104,7 +2142,7 @@ DEFUN ("set-xwidget-query-on-exit-flag",
 This function returns FLAG.  */)
   (Lisp_Object xwidget, Lisp_Object flag)
 {
-  CHECK_XWIDGET (xwidget);
+  CHECK_LIVE_XWIDGET (xwidget);
   XXWIDGET (xwidget)->kill_without_query = NILP (flag);
   return flag;
 }
@@ -2115,7 +2153,7 @@ DEFUN ("xwidget-query-on-exit-flag",
        doc: /* Return the current value of the query-on-exit flag for XWIDGET.  */)
   (Lisp_Object xwidget)
 {
-  CHECK_XWIDGET (xwidget);
+  CHECK_LIVE_XWIDGET (xwidget);
   return (XXWIDGET (xwidget)->kill_without_query ? Qnil : Qt);
 }
 
@@ -2148,7 +2186,7 @@ DEFUN ("xwidget-webkit-search", Fxwidget_webkit_search, Sxwidget_webkit_search,
 #endif
 
   CHECK_STRING (query);
-  CHECK_XWIDGET (xwidget);
+  CHECK_LIVE_XWIDGET (xwidget);
 
 #ifdef USE_GTK
   xw = XXWIDGET (xwidget);
@@ -2192,7 +2230,7 @@ DEFUN ("xwidget-webkit-next-result", Fxwidget_webkit_next_result,
   WebKitFindController *controller;
 #endif
 
-  CHECK_XWIDGET (xwidget);
+  CHECK_LIVE_XWIDGET (xwidget);
   xw = XXWIDGET (xwidget);
 
   if (!xw->find_text)
@@ -2224,7 +2262,7 @@ DEFUN ("xwidget-webkit-previous-result", Fxwidget_webkit_previous_result,
   WebKitFindController *controller;
 #endif
 
-  CHECK_XWIDGET (xwidget);
+  CHECK_LIVE_XWIDGET (xwidget);
   xw = XXWIDGET (xwidget);
 
   if (!xw->find_text)
@@ -2256,7 +2294,7 @@ DEFUN ("xwidget-webkit-finish-search", Fxwidget_webkit_finish_search,
   WebKitFindController *controller;
 #endif
 
-  CHECK_XWIDGET (xwidget);
+  CHECK_LIVE_XWIDGET (xwidget);
   xw = XXWIDGET (xwidget);
 
   if (!xw->find_text)
@@ -2294,7 +2332,7 @@ DEFUN ("xwidget-webkit-load-html", Fxwidget_webkit_load_html,
   WebKitWebView *webview;
   char *data, *uri;
 
-  CHECK_XWIDGET (xwidget);
+  CHECK_LIVE_XWIDGET (xwidget);
   CHECK_STRING (text);
   if (NILP (base_uri))
     base_uri = build_string ("about:blank");
@@ -2415,7 +2453,9 @@ syms_of_xwidget (void)
 {
   defsubr (&Smake_xwidget);
   defsubr (&Sxwidgetp);
+  defsubr (&Sxwidget_live_p);
   DEFSYM (Qxwidgetp, "xwidgetp");
+  DEFSYM (Qxwidget_live_p, "xwidget-live-p");
   defsubr (&Sxwidget_view_p);
   DEFSYM (Qxwidget_view_p, "xwidget-view-p");
   defsubr (&Sxwidget_info);
@@ -2474,7 +2514,8 @@ syms_of_xwidget (void)
 
   Fprovide (intern ("xwidget-internal"), Qnil);
 
-  id_to_xwidget_map = CALLN (Fmake_hash_table, QCtest, Qeq);
+  id_to_xwidget_map = CALLN (Fmake_hash_table, QCtest, Qeq,
+			     QCweakness, Qvalue);
   staticpro (&id_to_xwidget_map);
 
 #ifdef USE_GTK
@@ -2700,9 +2741,10 @@ kill_buffer_xwidgets (Lisp_Object buffer)
       Vxwidget_list = Fdelq (xwidget, Vxwidget_list);
       /* TODO free the GTK things in xw.  */
       {
-        CHECK_XWIDGET (xwidget);
+        CHECK_LIVE_XWIDGET (xwidget);
         struct xwidget *xw = XXWIDGET (xwidget);
-	Fremhash (make_fixnum (xw->xwidget_id), id_to_xwidget_map);
+	xw->buffer = Qnil;
+
 #ifdef USE_GTK
         if (xw->widget_osr && xw->widgetwindow_osr)
           {
@@ -2719,6 +2761,10 @@ kill_buffer_xwidgets (Lisp_Object buffer)
 		xfree (xmint_pointer (XCAR (cb)));
 	      ASET (xw->script_callbacks, idx, Qnil);
 	    }
+
+	xw->widget_osr = NULL;
+	xw->widgetwindow_osr = NULL;
+	xw->find_text = NULL;
 #elif defined NS_IMPL_COCOA
         nsxwidget_kill (xw);
 #endif
diff --git a/src/xwidget.h b/src/xwidget.h
index 6e6b39c8b4..4377b50e84 100644
--- a/src/xwidget.h
+++ b/src/xwidget.h
@@ -138,9 +138,16 @@ #define XWIDGETP(x) PSEUDOVECTORP (x, PVEC_XWIDGET)
 #define XXWIDGET(a) (eassert (XWIDGETP (a)), \
 		     XUNTAG (a, Lisp_Vectorlike, struct xwidget))
 
+#define XWIDGET_LIVE_P(w) (!NILP ((w)->buffer))
+
 #define CHECK_XWIDGET(x) \
   CHECK_TYPE (XWIDGETP (x), Qxwidgetp, x)
 
+#define CHECK_LIVE_XWIDGET(x)				\
+  CHECK_TYPE ((XWIDGETP (x)				\
+	       && XWIDGET_LIVE_P (XXWIDGET (x))),	\
+	      Qxwidget_live_p, x)
+
 /* Test for xwidget_view pseudovector.  */
 #define XWIDGET_VIEW_P(x) PSEUDOVECTORP (x, PVEC_XWIDGET_VIEW)
 #define XXWIDGET_VIEW(a) (eassert (XWIDGET_VIEW_P (a)), \
-- 
2.31.1


[-- Attachment #3: Type: text/plain, Size: 112 bytes --]


Thanks, how does this look to you?  And perhaps Eli has something to
say about it as well.

Thanks in advance.

  parent reply	other threads:[~2021-11-11  6:37 UTC|newest]

Thread overview: 23+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <87sfw31mok.fsf.ref@yahoo.com>
2021-11-11  1:54 ` shr using `make-xwidget' incorrectly Po Lu
2021-11-11  3:26   ` Lars Ingebrigtsen
2021-11-11  3:38     ` Po Lu
2021-11-11  3:41       ` Lars Ingebrigtsen
2021-11-11  3:43         ` Lars Ingebrigtsen
2021-11-11  4:46         ` Po Lu
2021-11-11  4:56           ` Lars Ingebrigtsen
2021-11-11  5:10             ` Po Lu
2021-11-11 12:33               ` Lars Ingebrigtsen
2021-11-11 12:37                 ` Po Lu
2021-11-11 12:43                   ` Lars Ingebrigtsen
2021-11-11 12:47                     ` Po Lu
2021-11-11  6:37     ` Po Lu [this message]
2021-11-11  7:59       ` Introduce "killed" state for xwidgets (Re: shr using `make-xwidget' incorrectly) Eli Zaretskii
2021-11-11  9:28         ` Po Lu
2021-11-11 12:23       ` Lars Ingebrigtsen
2021-11-11 12:25         ` Po Lu
2021-11-11 15:10           ` Eli Zaretskii
2021-11-12  0:18             ` Po Lu
2021-11-11  6:58   ` shr using `make-xwidget' incorrectly Eli Zaretskii
2021-11-11  7:05     ` Po Lu
2021-11-11  8:01       ` Eli Zaretskii
2021-11-11  9:27         ` Po Lu

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87tugjxkn6.fsf_-_@yahoo.com \
    --to=luangruo@yahoo.com \
    --cc=emacs-devel@gnu.org \
    --cc=larsi@gnus.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).