unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Qiantan Hong <qthong@stanford.edu>
To: Po Lu <luangruo@yahoo.com>
Cc: Lars Ingebrigtsen <larsi@gnus.org>,
	"emacs-devel@gnu.org" <emacs-devel@gnu.org>
Subject: Re: [PATCH] Add user content APIs for WebKit Xwidgets
Date: Sat, 15 Oct 2022 07:53:21 +0000	[thread overview]
Message-ID: <0F1442C8-45E2-408C-B310-448B4A26496E@stanford.edu> (raw)
In-Reply-To: <D174B265-8DB1-4F30-99E0-0076A44C1290@stanford.edu>


[-- Attachment #1.1: Type: text/plain, Size: 1072 bytes --]

Here it goes, tested on GTK!



Best,
Qiantan



> On Oct 14, 2022, at 6:37 PM, Qiantan Hong <qthong@stanford.edu> wrote:
>
> Ah sorry for the noise, please don’t merge yet. I only tested it on NS before
> and only tried it on GTK just now. It is broken on GTK. Will fix soon.
>
> Best,
> Qiantan
>
>
>
>> On Oct 14, 2022, at 2:13 PM, Qiantan Hong <qthong@stanford.edu> wrote:
>>
>> Hi,
>>
>> Here’s the updated patch.
>> <0001-Implment-some-user-content-APIs-for-WebKit-Xwidgets.txt>
>>
>> Best,
>> Qiantan
>>
>>
>>
>>> On Oct 14, 2022, at 12:35 AM, Po Lu <luangruo@yahoo.com> wrote:
>>>
>>> Qiantan Hong <qthong@stanford.edu> writes:
>>>
>>>> Ah sorry, I’m replying to a really ancient thread in 2020, the patch is at
>>>> https://lists.gnu.org/archive/html/emacs-devel/2020-08/txtNrEQRJNtgd.txt
>>>>
>>>> I hope the patch is still applicable… If it’s not I’ll find sometime this weekend to update it.
>>>
>>> Thanks.  Please do that, and also fix the coding style to comply with
>>> our coding guidelines.
>>
>


[-- Attachment #1.2: Type: text/html, Size: 1987 bytes --]

[-- Attachment #2: 0002-Implment-some-user-content-APIs-for-WebKit-Xwidgets.txt --]
[-- Type: text/plain, Size: 19000 bytes --]

From aac249c4f0a3e0f21aae81e0a26c362b67336af2 Mon Sep 17 00:00:00 2001
From: Qiantan Hong <qhong@alum.mit.edu>
Date: Fri, 14 Oct 2022 14:08:40 -0700
Subject: [PATCH] Implment some user content APIs for WebKit Xwidgets

Implement WebKit user scripts and script message handlers.
* src/xwidget.h (store_xwidget_script_message_event): store script
  message event into event queue
* src/xwidget.c (store_xwidget_script_message_event, make-xwidget,
  webkit_script_message_cb, xwidget-webkit-add-user-script,
  xwidget-webkit-remove-all-user-scripts,
  xwidget-webkit-register-script-message,
  xwidget-webkit-unregister-script-message): Implement user script
  and script message handler primitives.
* src/nsxwidget.c (nsxwidget_webkit_add_user_script,
  nsxwidget_webkit_remove_all_user_scripts,
  nsxwidget_webkit_register_script_message,
  nsxwidget_webkit_unregister_script_message,
  initWithFrame, initialize, userContentController): NS
  implementation. Changed naming of a previous used  script message
  handler to avoid namespace pollution.
* src/nsxwidget.h (nsxwidget_webkit_add_user_script,
  nsxwidget_webkit_remove_all_user_scripts,
  nsxwidget_webkit_register_script_message,
  nsxwidget_webkit_unregister_script_message): NS implementation
* lisp/xwidget.el (xwidget-webkit-callback,
  xwidget-webkit-push-script-message-handler,
  xwidget-webkit-pop-script-message-handler):
  let lisp recognize and dispatch script message events

Acked-by: Qiantan Hong <qhong@mit.edu>
---
 lisp/xwidget.el |  32 +++++++++
 src/nsxwidget.h |   5 ++
 src/nsxwidget.m |  80 +++++++++++++++++++--
 src/xwidget.c   | 181 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/xwidget.h   |   4 ++
 5 files changed, 295 insertions(+), 7 deletions(-)

diff --git a/lisp/xwidget.el b/lisp/xwidget.el
index 41a1190c64..79c9cc2afd 100644
--- a/lisp/xwidget.el
+++ b/lisp/xwidget.el
@@ -485,8 +485,40 @@ xwidget-webkit-callback
            (let ((proc (nth 3 last-input-event))
                  (arg  (nth 4 last-input-event)))
              (funcall proc arg)))
+          ((eq xwidget-event-type 'script-message)
+           (let ((name (nth 3 last-input-event))
+                 (value (nth 4 last-input-event)))
+             (let ((handler-pair (assq name (xwidget-get xwidget 'script-message-handlers))))
+               (if handler-pair
+                   (funcall (cdr handler-pair) xwidget value)
+                 (xwidget-log "unhandled script message:%s" name)))))
           (t (xwidget-log "unhandled event:%s" xwidget-event-type)))))
 
+(defun xwidget-webkit-push-script-message-handler (xwidget name handler)
+  "Associate HANDLER with script messages under symbol NAME for Webkit XWIDGET.
+
+HANDLER will be called with two arguments: the Webkit XWIDGET and
+the javascript object passed from the script message converted to
+a Lisp object."
+  (xwidget-webkit-register-script-message (xwidget-webkit-current-session) name)
+  (xwidget-put xwidget 'script-message-handlers
+               (cons (cons name handler) (xwidget-get xwidget 'script-message-handlers)))
+  name)
+
+(defun xwidget-webkit-pop-script-message-handler (xwidget name)
+  "Remove a handler associated with symbol NAME for Webkit XWIDGET.
+
+Returns the removed handler function, or NIL if such handler is
+not found."
+  (let* ((old-alist (xwidget-get xwidget 'script-message-handlers))
+         (handler-pair (assq name old-alist)))
+    (when handler-pair
+      (let ((new-alist (delq handler-pair old-alist)))
+        (xwidget-put xwidget 'script-message-handlers new-alist)
+        (unless (assq name new-alist)
+          (xwidget-webkit-unregister-script-message (xwidget-webkit-current-session) name)))
+      (cdr handler-pair))))
+
 (defvar bookmark-make-record-function)
 (when (memq window-system '(mac ns))
   (defcustom xwidget-webkit-enable-plugins nil
diff --git a/src/nsxwidget.h b/src/nsxwidget.h
index 666509744a..cd1dc1bc2c 100644
--- a/src/nsxwidget.h
+++ b/src/nsxwidget.h
@@ -40,6 +40,11 @@ #define NSXWIDGET_H_INCLUDED
 void nsxwidget_webkit_execute_script (struct xwidget *xw, const char *script,
                                       Lisp_Object fun);
 
+void nsxwidget_webkit_add_user_script (struct xwidget *xw, const char *script,
+                                       int injection_time_start, int main_frame_only);
+void nsxwidget_webkit_remove_all_user_scripts (struct xwidget *xw);
+Lisp_Object nsxwidget_webkit_register_script_message (struct xwidget *xw, const char *name);
+void nsxwidget_webkit_unregister_script_message (struct xwidget *xw, const char *name);
 /* Functions for xwidget model.  */
 
 #ifdef __OBJC__
diff --git a/src/nsxwidget.m b/src/nsxwidget.m
index be0eba0bcb..e09759f754 100644
--- a/src/nsxwidget.m
+++ b/src/nsxwidget.m
@@ -88,7 +88,7 @@ - (id)initWithFrame:(CGRect)frame
         @"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6)"
         @" AppleWebKit/603.3.8 (KHTML, like Gecko)"
         @" Version/11.0.1 Safari/603.3.8";
-      [scriptor addScriptMessageHandler:self name:@"keyDown"];
+      [scriptor addScriptMessageHandler:self name:@"__xwidget_internal_keyDown"];
       [scriptor addUserScript:[[WKUserScript alloc]
                                 initWithSource:xwScript
                                  injectionTime:
@@ -275,23 +275,34 @@ + (void)initialize
       @"}"
       @"function xwKeyDown(event) {"
       @"  if (event.ctrlKey && event.key == 'g') {"
-      @"    window.webkit.messageHandlers.keyDown.postMessage('C-g');"
+      @"    window.webkit.messageHandlers.__xwidget_internal_keyDown.postMessage('C-g');"
       @"  }"
       @"}"
       @"document.addEventListener('keydown', xwKeyDown);"
       ;
 }
 
+static Lisp_Object js_to_lisp (id value);
+
 /* Confirming to WKScriptMessageHandler, listens concerning keyDown in
    webkit. Currently 'C-g'.  */
 - (void)userContentController:(WKUserContentController *)userContentController
       didReceiveScriptMessage:(WKScriptMessage *)message
 {
-  if ([message.body isEqualToString:@"C-g"])
+  if ([message.name isEqualToString:@"__xwidget_internal_keyDown"])
     {
-      /* Just give up focus, no relay "C-g" to emacs, another "C-g"
-         follows will be handled by emacs.  */
-      [self.window makeFirstResponder:self.xw->xv->emacswindow];
+      if ([message.body isEqualToString:@"C-g"])
+        {
+          /* Just give up focus, no relay "C-g" to emacs, another "C-g"
+             follows will be handled by emacs.  */
+          [self.window makeFirstResponder:self.xw->xv->emacswindow];
+        }
+    }
+  else
+    {
+      store_xwidget_script_message_event (self.xw,
+                                          message.name.UTF8String,
+                                          js_to_lisp (message.body));
     }
 }
 
@@ -437,6 +448,61 @@ - (void)userContentController:(WKUserContentController *)userContentController
     }];
 }
 
+void
+nsxwidget_webkit_add_user_script (struct xwidget *xw, const char *script,
+                                  int injection_time_start, int main_frame_only)
+{
+  XwWebView *xwWebView = (XwWebView *) xw->xwWidget;
+  WKUserContentController *scriptor = xwWebView.configuration.userContentController;
+
+  NSString *javascriptString = [NSString stringWithUTF8String:script];
+  WKUserScriptInjectionTime injectionTime = injection_time_start?
+    WKUserScriptInjectionTimeAtDocumentStart : WKUserScriptInjectionTimeAtDocumentEnd;
+  WKUserScript *userScript = [[WKUserScript alloc]
+                               initWithSource: javascriptString
+                                injectionTime: injectionTime
+                               forMainFrameOnly: main_frame_only];
+  [scriptor addUserScript: userScript];
+}
+
+void
+nsxwidget_webkit_remove_all_user_scripts (struct xwidget *xw)
+{
+  XwWebView *xwWebView = (XwWebView *) xw->xwWidget;
+  WKUserContentController *scriptor = xwWebView.configuration.userContentController;
+
+  [scriptor removeAllUserScripts];
+}
+
+Lisp_Object
+nsxwidget_webkit_register_script_message (struct xwidget *xw, const char *name)
+{
+  XwWebView *xwWebView = (XwWebView *) xw->xwWidget;
+  WKUserContentController *scriptor = xwWebView.configuration.userContentController;
+
+  NSString *messageName = [NSString stringWithUTF8String:name];
+
+  @try
+    {
+      [scriptor addScriptMessageHandler:xwWebView name:messageName];
+    }
+  @catch (NSException *e)
+    {
+      return Qnil;
+    }
+  return Qt;
+}
+
+void
+nsxwidget_webkit_unregister_script_message (struct xwidget *xw, const char *name)
+{
+  XwWebView *xwWebView = (XwWebView *) xw->xwWidget;
+  WKUserContentController *scriptor = xwWebView.configuration.userContentController;
+
+  NSString *messageName = [NSString stringWithUTF8String:name];
+  [scriptor removeScriptMessageHandlerForName:messageName];
+}
+
 /* Window containing an xwidget.  */
 
 @implementation XwWindow
@@ -469,7 +535,7 @@ - (BOOL)isFlipped { return YES; }
       WKUserContentController *scriptor =
         ((XwWebView *) xw->xwWidget).configuration.userContentController;
       [scriptor removeAllUserScripts];
-      [scriptor removeScriptMessageHandlerForName:@"keyDown"];
+      [scriptor removeScriptMessageHandlerForName:@"__xwidget_internal_keyDown"];
       [scriptor release];
       if (xw->xv)
         xw->xv->model = Qnil; /* Make sure related view stale.  */
diff --git a/src/xwidget.c b/src/xwidget.c
index 8bdfab02fd..e4250065b9 100644
--- a/src/xwidget.c
+++ b/src/xwidget.c
@@ -126,6 +126,16 @@ webkit_decide_policy_cb (WebKitWebView *,
 };
 
 static void find_widget (GtkWidget *t, struct widget_search_data *);
+
+struct webkit_script_message_cb_data
+{
+  struct xwidget *xw;
+  char name[0];
+};
+static void webkit_script_message_cb (WebKitUserContentManager *,
+                                          WebKitJavascriptResult *,
+                                          gpointer);
+
 #endif
 
 #ifdef HAVE_PGTK
@@ -380,6 +390,8 @@ DEFUN ("make-xwidget",
 	  settings = webkit_web_view_get_settings (WEBKIT_WEB_VIEW (xw->widget_osr));
 	  g_object_set (G_OBJECT (settings), "enable-developer-extras", TRUE, NULL);
 	}
+      WebKitUserContentManager *scriptor = webkit_user_content_manager_new ();
+      xw->widget_osr = webkit_web_view_new_with_user_content_manager (scriptor);
 
       gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr), xw->width,
                                    xw->height);
@@ -2308,6 +2320,21 @@ store_xwidget_js_callback_event (struct xwidget *xw,
   kbd_buffer_store_event (&event);
 }
 
+void
+store_xwidget_script_message_event (struct xwidget *xw,
+                                         const char *name,
+                                         Lisp_Object body)
+{
+  struct input_event event;
+  Lisp_Object xwl;
+  XSETXWIDGET (xwl, xw);
+  EVENT_INIT (event);
+  event.kind = XWIDGET_EVENT;
+  event.frame_or_window = Qnil;
+  event.arg = list4 (intern ("script-message"), xwl, intern (name), body);
+  kbd_buffer_store_event (&event);
+}
+
 
 #ifdef USE_GTK
 static void
@@ -2622,6 +2649,17 @@ webkit_decide_policy_cb (WebKitWebView *webView,
   }
 }
 
+static void webkit_script_message_cb (WebKitUserContentManager *scriptor,
+                                          WebKitJavascriptResult *js_result,
+                                          gpointer data)
+{
+  JSCValue *value = webkit_javascript_result_get_js_value (js_result);
+  struct webkit_script_message_cb_data *arg = data;
+
+  Lisp_Object lisp_value = webkit_js_to_lisp (value);
+  store_xwidget_script_message_event (arg->xw, arg->name, lisp_value);
+}
+
 static gboolean
 webkit_script_dialog_cb (WebKitWebView *webview,
 			 WebKitScriptDialog *script_dialog,
@@ -2717,6 +2755,7 @@ xwidget_init_view (struct xwidget *xww,
   XSETWINDOW (xv->w, s->w);
   XSETXWIDGET (xv->model, xww);
 
+
 #ifdef HAVE_X_WINDOWS
   xv->dpy = FRAME_X_DISPLAY (s->f);
 
@@ -3198,6 +3237,140 @@ DEFUN ("xwidget-webkit-execute-script",
   return Qnil;
 }
 
+DEFUN ("xwidget-webkit-add-user-script",
+       Fxwidget_webkit_add_user_script, Sxwidget_webkit_add_user_script,
+       4, 4, 0,
+       doc: /* Add user SCRIPT to the Webkit XWIDGET.
+INJECTION-TIME is a symbol which can take one of the following values:
+
+- start: SCRIPT is injected when document starts loading
+- end: SCRIPT is injected when document finishes loading
+
+If MAIN_FRAME_ONLY is nil, SCRIPT is injected to all frames.
+Otherwise, SCRIPT is only injected to top frames.*/)
+  (Lisp_Object xwidget, Lisp_Object script,
+   Lisp_Object injection_time, Lisp_Object main_frame_only)
+{
+  WEBKIT_FN_INIT ();
+  CHECK_STRING (script);
+  CHECK_SYMBOL (injection_time);
+
+  script = ENCODE_SYSTEM(script);
+
+  int injection_time_start, mfo;
+  mfo = !NILP (main_frame_only);
+  if (EQ (injection_time, Qstart))
+    injection_time_start = 1;
+  else if (EQ (injection_time, Qend))
+    injection_time_start = 0;
+  else
+    error ("Unknown Xwidget Webkit user script injection time: %s",
+           SDATA (SYMBOL_NAME (injection_time)));
+
+#ifdef USE_GTK
+  WebKitWebView *wkwv = WEBKIT_WEB_VIEW (xw->widget_osr);
+  WebKitUserContentManager *scriptor = webkit_web_view_get_user_content_manager (wkwv);
+
+  int webkit_injected_frames = mfo?
+    WEBKIT_USER_CONTENT_INJECT_TOP_FRAME : WEBKIT_USER_CONTENT_INJECT_ALL_FRAMES;
+  int webkit_injection_time = injection_time_start?
+    WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_START : WEBKIT_USER_SCRIPT_INJECT_AT_DOCUMENT_END;
+  WebKitUserScript *userScript = webkit_user_script_new (SSDATA (script),
+                                                         webkit_injected_frames,
+                                                         webkit_injection_time,
+                                                         NULL, NULL);
+  webkit_user_content_manager_add_script (scriptor, userScript);
+  webkit_user_script_unref (userScript);
+#elif defined NS_IMPL_COCOA
+  nsxwidget_webkit_add_user_script (xw, SSDATA (script), injection_time_start, mfo);
+#endif
+  return Qnil;
+}
+
+DEFUN ("xwidget-webkit-remove-all-user-scripts",
+       Fxwidget_webkit_remove_all_user_scripts, Sxwidget_webkit_remove_all_user_scripts,
+       1, 1, 0,
+       doc: /* Remove all user scripts from XWIDGET.  */)
+  (Lisp_Object xwidget)
+{
+  WEBKIT_FN_INIT ();
+
+#ifdef USE_GTK
+  WebKitWebView *wkwv = WEBKIT_WEB_VIEW (xw->widget_osr);
+  WebKitUserContentManager *scriptor = webkit_web_view_get_user_content_manager (wkwv);
+
+  webkit_user_content_manager_remove_all_scripts (scriptor);
+#elif defined NS_IMPL_COCOA
+  nsxwidget_webkit_remove_all_user_scripts(xw);
+#endif
+  return Qnil;
+}
+
+DEFUN ("xwidget-webkit-register-script-message",
+       Fxwidget_webkit_register_script_message, Sxwidget_webkit_register_script_message,
+       2, 2, 0,
+       doc: /* Register script message with symbol NAME in Webkit XWIDGET.
+Returns T if the operation is successful, NIL otherwise.
+The cause of failure is usually that NAME has already been registered for XWIDGET.  */)
+  (Lisp_Object xwidget, Lisp_Object name)
+{
+  WEBKIT_FN_INIT ();
+  CHECK_SYMBOL (name);
+  const char *sname = SSDATA( SYMBOL_NAME (name));
+
+#ifdef USE_GTK
+  WebKitWebView *wkwv = WEBKIT_WEB_VIEW (xw->widget_osr);
+  WebKitUserContentManager *scriptor = webkit_web_view_get_user_content_manager (wkwv);
+
+  gchar *signal_name = g_strconcat ("script-message-received::", sname, NULL);
+  size_t name_length = strlen (sname) + 1;
+  struct webkit_script_message_cb_data *arg = malloc (sizeof *arg + name_length);
+  arg->xw = xw;
+  g_strlcpy (arg->name, sname, name_length);
+  g_signal_connect_data(scriptor, signal_name, G_CALLBACK (webkit_script_message_cb),
+                        arg, (GClosureNotify)free, 0);
+  g_free (signal_name);
+  if (webkit_user_content_manager_register_script_message_handler (scriptor, sname))
+    {
+      return Qt;
+    }
+  else
+    {
+      g_signal_handlers_disconnect_matched  (scriptor,
+                                             G_SIGNAL_MATCH_DATA,
+                                             0, 0, 0, 0, arg);
+      return Qnil;
+    }
+#elif defined NS_IMPL_COCOA
+  return nsxwidget_webkit_register_script_message(xw, sname);
+#endif
+}
+
+DEFUN ("xwidget-webkit-unregister-script-message",
+       Fxwidget_webkit_unregister_script_message, Sxwidget_webkit_unregister_script_message,
+       2, 2, 0,
+       doc: /* Unregister script message with symbol NAME in Webkit XWIDGET.  */)
+  (Lisp_Object xwidget, Lisp_Object name)
+{
+  WEBKIT_FN_INIT ();
+  CHECK_SYMBOL (name);
+  const char *sname = SSDATA( SYMBOL_NAME (name));
+
+#ifdef USE_GTK
+  WebKitWebView *wkwv = WEBKIT_WEB_VIEW (xw->widget_osr);
+  WebKitUserContentManager *scriptor = webkit_web_view_get_user_content_manager (wkwv);
+
+  webkit_user_content_manager_unregister_script_message_handler (scriptor, sname);
+  g_signal_handlers_disconnect_matched  (scriptor,
+                                         G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DETAIL,
+                                         0, g_quark_from_string (sname), 0,
+                                         G_CALLBACK (webkit_script_message_cb), 0);
+#elif defined NS_IMPL_COCOA
+  nsxwidget_webkit_unregister_script_message(xw, sname);
+#endif
+  return Qnil;
+}
+
 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)
@@ -3919,6 +4092,14 @@ syms_of_xwidget (void)
   defsubr (&Sxwidget_webkit_execute_script);
   DEFSYM (Qwebkit, "webkit");
 
+  defsubr (&Sxwidget_webkit_add_user_script);
+  DEFSYM (Qstart, "start");
+  DEFSYM (Qend, "end");
+  defsubr (&Sxwidget_webkit_remove_all_user_scripts);
+
+  defsubr (&Sxwidget_webkit_register_script_message);
+  defsubr (&Sxwidget_webkit_unregister_script_message);
+
   defsubr (&Sxwidget_size_request);
   defsubr (&Sdelete_xwidget_view);
 
diff --git a/src/xwidget.h b/src/xwidget.h
index 502beb6765..e835f700bc 100644
--- a/src/xwidget.h
+++ b/src/xwidget.h
@@ -203,6 +203,10 @@ #define XG_XWIDGET_VIEW "emacs_xwidget_view"
                                       Lisp_Object proc,
                                       Lisp_Object argument);
 
+void store_xwidget_script_message_event (struct xwidget *xw,
+                                         const char *name,
+                                         Lisp_Object value);
+
 extern struct xwidget *xwidget_from_id (uint32_t id);
 
 #ifdef HAVE_X_WINDOWS
-- 
2.26.2


  reply	other threads:[~2022-10-15  7:53 UTC|newest]

Thread overview: 41+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-10-14  6:34 [PATCH] Add user content APIs for WebKit Xwidgets Qiantan Hong
2022-10-14  7:01 ` Po Lu
2022-10-14  7:12   ` Qiantan Hong
2022-10-14  7:35     ` Po Lu
2022-10-14 21:13       ` Qiantan Hong
2022-10-15  1:37         ` Qiantan Hong
2022-10-15  7:53           ` Qiantan Hong [this message]
2022-10-15 11:23             ` Po Lu
2022-10-15 18:29               ` Qiantan Hong
2022-10-16  0:26                 ` Po Lu
2022-10-15 23:33               ` Qiantan Hong
2022-10-16  4:32                 ` Po Lu
2022-10-16  6:29                   ` Qiantan Hong
2022-10-16  6:41                     ` Po Lu
2022-10-16  6:45                       ` Po Lu
2022-10-23  9:11                       ` Qiantan Hong
2022-10-23 10:58                         ` Po Lu
2022-10-23 22:16                           ` Qiantan Hong
2022-10-24  0:30                             ` Po Lu
2022-10-24  4:17                               ` Qiantan Hong
2022-10-24  5:38                                 ` Po Lu
2022-10-24  5:44                                   ` Qiantan Hong
2022-10-24  7:20                                     ` Po Lu
2022-10-16 20:51             ` [PATCH] Add user extension " Richard Stallman
2022-10-16 21:13               ` Alan Mackenzie
2022-10-18 11:58                 ` Richard Stallman
2022-10-17  5:31               ` Eli Zaretskii
2022-10-17  8:28                 ` Jean Louis
2022-10-19 17:04                   ` Richard Stallman
2022-10-19 19:06                     ` Eli Zaretskii
2022-10-20 19:46                 ` Richard Stallman
2022-10-21  5:51                   ` Eli Zaretskii
2022-10-21  6:02                     ` Po Lu
2022-10-23 19:14                     ` Richard Stallman
  -- strict thread matches above, loose matches on Subject: below --
2020-08-28  2:25 [PATCH] Add user content " Qiantan Hong
2020-08-28 14:37 ` Lars Ingebrigtsen
2020-08-28 15:41   ` Qiantan Hong
2020-08-30 13:43     ` Lars Ingebrigtsen
2020-08-29  4:07   ` Richard Stallman
2020-08-29  4:10 ` Richard Stallman
2020-08-29  4:45   ` Qiantan Hong

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=0F1442C8-45E2-408C-B310-448B4A26496E@stanford.edu \
    --to=qthong@stanford.edu \
    --cc=emacs-devel@gnu.org \
    --cc=larsi@gnus.org \
    --cc=luangruo@yahoo.com \
    /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).