* bug#29565: [PATCH] Support xwidget webkit for macOS X @ 2017-12-04 16:44 Jaesup Kwak 2017-12-04 20:59 ` Alan Third ` (11 more replies) 0 siblings, 12 replies; 41+ messages in thread From: Jaesup Kwak @ 2017-12-04 16:44 UTC (permalink / raw) To: 29565 [-- Attachment #1.1: Type: text/plain, Size: 243 bytes --] I attached a patch to support xwidget webkit for macOS X, rebased onto the latest master. This was proposed in emacs-devel@gnu.org < https://lists.gnu.org/archive/html/emacs-devel/2017-12/msg00091.html> and told to post a patch here. Thanks [-- Attachment #1.2: Type: text/html, Size: 468 bytes --] [-- Attachment #2: 0001-Support-xwidget-webkit-for-macOS-X.patch --] [-- Type: application/octet-stream, Size: 61219 bytes --] From b9ca73a270159755f342ed6296eec932c7dc03a9 Mon Sep 17 00:00:00 2001 From: Jaesup Kwak <veshboo@gmail.com> Date: Mon, 4 Dec 2017 21:23:19 +0900 Subject: [PATCH] Support xwidget webkit for macOS X Add xwidget webkit support for macOS X / NS Cocoa and accompanying changes. * .gitignore: Ignore .DS_Store. * configure.ac: Allow '--with-xwidgets' for 'nextstep'. * lisp/xwidget.el (xwidget-webkit-cx2-cb, xwidget-webkit-cx3-cb, xwidget-webkit-cx2) (xwidget-webkit-cx3, xwidget-webkit-mode-map): New webkit session when window split and key map for 'nextstep'. (xwidget-log): Fix typo in buffer name. (xwidget-event-handler): Remove a 'message'. (xwidget-webkit-callback, xwidget-webkit-url-title-cb): Replace lambda with defun for GC safe, remember URL for bookmark integration, and support vector result from javascript. (xwidget-webkit-mode): No cursor and integration for 'isearch'. (xwidget-webkit-bookmark-jump-new-session): Add new variable. (xwidget-webkit-bookmark-make-record): Changes to save URL in 'filename' attribute of a bookmark record, the URL obtained when the page was loaded, and 'switch-to-buffer' when a bookmark selected. (xwidget-webkit-isearch-last-length, xwidget-webkit-search-js) (xwidget-webkit-search-fun-function): Integration for 'isearch'. (xwidget-webkit-insert-string, xwidget-webkit-insert-string-cb): GC safe, FIELD is now a list, and fix for a javascript exception. (xwidget-window-inside-pixel-width) (xwidget-window-inside-pixel-height) (xwidget-webkit-adjust-size-to-window) (xwidget-webkit-new-session): New functions to get window inside width and height in pixel and insert invisible URL instead of ' '. (xwidget-webkit-current-url, xwidget-webkit-current-url-cb): GC safe. (xwidget-webkit-copy-selection-as-kill): GC safe. * nextstep/templates/Info.plist.in: Add 'NSAppTransportSecurity'. * src/Makefile.in: Add nsxwidget.o for compilation. * src/emacs.c (main): Simplify conditional call to 'syms_of_xwidget'. * src/nsterm.m (ns_draw_glyph_string): Add case for 'XWIDGET_GLYPH'. (note_mouse_movement mouseMoved): Make it easy to resize window by dragging mode-line or vertical separator adjacent to large glyph. * src/nsxwidget.h src/nsxwidget.m: Newly added files, xwidget webkit backend for nextstep. * src/xwidget.c src/xwidget.h (xwidget xwidget_view): Add nextstep specific fields with 'HAVE_NS', guard GTK specific fields with 'USE_GTK' in the structures, and implement accordingly. (x_draw_xwidget_glyph_string): Change for top alignment of xwidgets in a glyph string instead of vertically middle alignment. --- .gitignore | 3 + configure.ac | 34 ++- lisp/xwidget.el | 235 ++++++++++++++---- nextstep/templates/Info.plist.in | 8 + src/Makefile.in | 1 + src/emacs.c | 5 +- src/nsterm.m | 20 +- src/nsxwidget.h | 77 ++++++ src/nsxwidget.m | 510 +++++++++++++++++++++++++++++++++++++++ src/xwidget.c | 183 ++++++++++++-- src/xwidget.h | 35 ++- 11 files changed, 1033 insertions(+), 78 deletions(-) create mode 100644 src/nsxwidget.h create mode 100644 src/nsxwidget.m diff --git a/.gitignore b/.gitignore index 7426082906..2eae338508 100644 --- a/.gitignore +++ b/.gitignore @@ -278,3 +278,6 @@ nt/emacs.rc nt/emacsclient.rc src/gdb.ini /var/ + +# macOS - Folder custom attributes files +.DS_Store diff --git a/configure.ac b/configure.ac index 61455a4b0f..1b7b5c5079 100644 --- a/configure.ac +++ b/configure.ac @@ -399,7 +399,7 @@ AC_DEFUN [with_file_notification=$with_features]) OPTION_DEFAULT_OFF([xwidgets], - [enable use of some gtk widgets in Emacs buffers (requires gtk3)]) + [enable use of some gtk widgets in Emacs buffers (requires gtk3 or NS)]) ## For the times when you want to build Emacs but don't have ## a suitable makeinfo, and can live without the manuals. @@ -2715,20 +2715,34 @@ AC_DEFUN dnl Enable xwidgets if GTK3 and WebKitGTK+ are available. +dnl Enable xwidgets if NS Cocoa and WebKit framework are available. HAVE_XWIDGETS=no XWIDGETS_OBJ= if test "$with_xwidgets" != "no"; then - test "$USE_GTK_TOOLKIT" = "GTK3" && test "$window_system" != "none" || - AC_MSG_ERROR([xwidgets requested but gtk3 not used.]) + if test "$USE_GTK_TOOLKIT" = "GTK3" && test "$window_system" != "none"; then + WEBKIT_REQUIRED=2.12 + WEBKIT_MODULES="webkit2gtk-4.0 >= $WEBKIT_REQUIRED" + EMACS_CHECK_MODULES([WEBKIT], [$WEBKIT_MODULES]) + HAVE_XWIDGETS=$HAVE_WEBKIT + XWIDGETS_OBJ="xwidget.o" + elif test "$window_system" = "nextstep"; then + dnl FIXME: Check framework WebKit2 + dnl WEBKIT_REQUIRED=M.m.p + WEBKIT_LIBS="-Wl,-framework -Wl,WebKit" + WEBKIT_CFLAGS="-D_REENTRANT -I/System/Library/Frameworks/WebKit.framework/Headers" + HAVE_WEBKIT="yes" + HAVE_XWIDGETS=$HAVE_WEBKIT + XWIDGETS_OBJ="xwidget.o" + NS_OBJC_OBJ="$NS_OBJC_OBJ nsxwidget.o" + dnl Update NS_OBJC_OBJ with added nsxwidget.o + AC_SUBST(NS_OBJC_OBJ) + else + AC_MSG_ERROR([xwidgets requested, it requires GTK3 as X window toolkit or Nextstep as window systeml]) + fi - WEBKIT_REQUIRED=2.12 - WEBKIT_MODULES="webkit2gtk-4.0 >= $WEBKIT_REQUIRED" - EMACS_CHECK_MODULES([WEBKIT], [$WEBKIT_MODULES]) - HAVE_XWIDGETS=$HAVE_WEBKIT test $HAVE_XWIDGETS = yes || - AC_MSG_ERROR([xwidgets requested but WebKitGTK+ not found.]) + AC_MSG_ERROR([xwidgets requested but WebKitGTK+ or WebKit framework not found.]) - XWIDGETS_OBJ=xwidget.o AC_DEFINE([HAVE_XWIDGETS], 1, [Define to 1 if you have xwidgets support.]) fi AC_SUBST(XWIDGETS_OBJ) @@ -5419,7 +5433,7 @@ AC_DEFUN Does Emacs directly use zlib? ${HAVE_ZLIB} Does Emacs have dynamic modules support? ${HAVE_MODULES} Does Emacs use toolkit scroll bars? ${USE_TOOLKIT_SCROLL_BARS} - Does Emacs support Xwidgets (requires gtk3)? ${HAVE_XWIDGETS} + Does Emacs support Xwidgets (requires gtk3 or NS)? ${HAVE_XWIDGETS} Does Emacs have threading support in lisp? ${threads_enabled} "]) diff --git a/lisp/xwidget.el b/lisp/xwidget.el index 5e37209cc2..03183c19d5 100644 --- a/lisp/xwidget.el +++ b/lisp/xwidget.el @@ -78,6 +78,7 @@ xwidget-at ;;; webkit support (require 'browse-url) (require 'image-mode);;for some image-mode alike functionality +(require 'seq) ;;;###autoload (defun xwidget-webkit-browse-url (url &optional new-session) @@ -96,6 +97,38 @@ xwidget-webkit-browse-url (xwidget-webkit-new-session url) (xwidget-webkit-goto-url url)))) +;; NOTE: @javascript-callback - prefer defun to lambda. +;; Lambda seems to be more easily garbage collected in flight from +;; `xwidget-webkit-execute-script' to its execution via event. + +;; @javascript-callback +(defun xwidget-webkit-cx2-cb (url) + "New xwidget webkit session and buffer with URL in split window below." + (with-selected-window (split-window-below) + (xwidget-webkit-new-session url))) + +;; @javascript-callback +(defun xwidget-webkit-cx3-cb (url) + "New xwidget webkit session and buffer with URL in split window right." + (with-selected-window (split-window-right) + (xwidget-webkit-new-session url))) + +(defun xwidget-webkit-cx2 () + "Get the URL of current session, then `xwidget-webkit-cx2-cb'." + (interactive) + (xwidget-webkit-execute-script + (xwidget-webkit-current-session) + "document.URL" + 'xwidget-webkit-cx2-cb)) + +(defun xwidget-webkit-cx3 () + "Get the URL of current session, then `xwidget-webkit-cx3-cb'." + (interactive) + (xwidget-webkit-execute-script + (xwidget-webkit-current-session) + "document.URL" + 'xwidget-webkit-cx3-cb)) + ;;todo. ;; - check that the webkit support is compiled in (defvar xwidget-webkit-mode-map @@ -131,6 +164,12 @@ xwidget-webkit-mode-map ;; (define-key map [remap move-end-of-line] 'image-eol) (define-key map [remap beginning-of-buffer] 'xwidget-webkit-scroll-top) (define-key map [remap end-of-buffer] 'xwidget-webkit-scroll-bottom) + + ;; For macOS xwidget webkit, we don't support multiple views for a + ;; model, instead, create a new session and model behind the scene. + (when (memq window-system '(mac ns)) + (define-key map (kbd "C-x 2") 'xwidget-webkit-cx2) + (define-key map (kbd "C-x 3") 'xwidget-webkit-cx3)) map) "Keymap for `xwidget-webkit-mode'.") @@ -192,7 +231,7 @@ xwidget-webkit-scroll-bottom (define-key (current-global-map) [xwidget-event] #'xwidget-event-handler) (defun xwidget-log (&rest msg) "Log MSG to a buffer." - (let ((buf (get-buffer-create " *xwidget-log*"))) + (let ((buf (get-buffer-create "*xwidget-log*"))) (with-current-buffer buf (insert (apply #'format msg)) (insert "\n")))) @@ -208,7 +247,6 @@ xwidget-event-handler ;;TODO stopped working for some reason ) ;;(funcall xwidget-callback xwidget xwidget-event-type) - (message "xw callback %s" xwidget) (funcall 'xwidget-webkit-callback xwidget xwidget-event-type))) (defun xwidget-webkit-callback (xwidget xwidget-event-type) @@ -218,16 +256,25 @@ xwidget-webkit-callback (xwidget-log "error: callback called for xwidget with dead buffer") (with-current-buffer (xwidget-buffer xwidget) + + ;; @javascript-callback + ;; We do not change selected window due to getting to knowing + ;; URL and title. And also do not adjust webkit size to window + ;; here, the window can be the mini-buffer window unwantedly. + (defun xwidget-webkit-url-title-cb (url-title) + "Put URL as text property and change buffer name using TITLE." + (let ((url (car url-title)) + (title (car (cdr url-title)))) + (xwidget-log "webkit finished loading: '%s' from '%s'" title url) + (setq buffer-read-only nil) + (put-text-property 2 3 'URL url) + (setq buffer-read-only t) + (rename-buffer (format "*xwidget webkit: %s *" title)))) + (cond ((eq xwidget-event-type 'load-changed) (xwidget-webkit-execute-script - xwidget "document.title" - (lambda (title) - (xwidget-log "webkit finished loading: '%s'" title) - ;;TODO - check the native/internal scroll - ;;(xwidget-adjust-size-to-content xwidget) - (xwidget-webkit-adjust-size-to-window xwidget) - (rename-buffer (format "*xwidget webkit: %s *" title)))) - (pop-to-buffer (current-buffer))) + xwidget "[document.URL, document.title]" + 'xwidget-webkit-url-title-cb)) ((eq xwidget-event-type 'decide-policy) (let ((strarg (nth 3 last-input-event))) (if (string-match ".*#\\(.*\\)" strarg) @@ -237,25 +284,108 @@ xwidget-webkit-callback ((eq xwidget-event-type 'javascript-callback) (let ((proc (nth 3 last-input-event)) (arg (nth 4 last-input-event))) - (funcall proc arg))) + ;; Some javascript return vector as result + (if (vectorp arg) + (funcall proc (seq-into arg 'list)) + (funcall proc arg)))) (t (xwidget-log "unhandled event:%s" xwidget-event-type)))))) (defvar bookmark-make-record-function) +(defvar isearch-search-fun-function) (define-derived-mode xwidget-webkit-mode special-mode "xwidget-webkit" "Xwidget webkit view mode." (setq buffer-read-only t) + (setq cursor-type nil) (setq-local bookmark-make-record-function #'xwidget-webkit-bookmark-make-record) + (setq-local isearch-search-fun-function + #'xwidget-webkit-search-fun-function) + (setq-local isearch-lazy-highlight nil) ;; Keep track of [vh]scroll when switching buffers (image-mode-setup-winprops)) +;;; Bookmarks integration + +(defvar xwidget-webkit-bookmark-jump-new-session nil + "Control bookmark jump to use new session or not. +If non-nil, it will use a new session. Otherwise, it will use +`xwidget-webkit-last-session'. When you set this variable to +nil, consider further customization with +`xwidget-webkit-last-session-buffer'.") + +;; We avoid using async `xwidget-webkit-current-url', instead use URL +;; kept in xwidget webkit as property (defun xwidget-webkit-bookmark-make-record () "Integrate Emacs bookmarks with the webkit xwidget." (nconc (bookmark-make-record-default t t) - `((page . ,(xwidget-webkit-current-url)) - (handler . (lambda (bmk) (browse-url - (bookmark-prop-get bmk 'page))))))) - + `((filename . ,(get-text-property 2 'URL)) + (handler . (lambda (bmk) + (browse-url + (bookmark-prop-get bmk 'filename) + xwidget-webkit-bookmark-jump-new-session) + (switch-to-buffer + (xwidget-buffer (xwidget-webkit-last-session)))))))) + +;;; Search text in page + +;; Initialize last search text length variable when isearch starts +(defvar xwidget-webkit-isearch-last-length 0) +(add-hook 'isearch-mode-hook + (lambda () + (setq xwidget-webkit-isearch-last-length 0))) + +;; This is minimal. Regex and incremental search will be nice +(defvar xwidget-webkit-search-js " +var xwSearchForward = %s; +var xwSearchRepeat = %s; +var xwSearchString = '%s'; +if (window.getSelection() && !window.getSelection().isCollapsed) { + if (xwSearchRepeat) { + if (xwSearchForward) + window.getSelection().collapseToEnd(); + else + window.getSelection().collapseToStart(); + } else { + if (xwSearchForward) + window.getSelection().collapseToStart(); + else { + var sel = window.getSelection(); + window.getSelection().collapse(sel.focusNode, sel.focusOffset + 1); + } + } +} +window.find(xwSearchString, false, !xwSearchForward, true, false, true); +") + +(defun xwidget-webkit-search-fun-function () + "Return the function which perform the search in xwidget webkit." + (lambda (string &optional bound noerror count) + (ignore bound noerror count) + (let ((current-length (length string)) + search-forward + search-repeat) + ;; Forward or backward + (if (eq isearch-forward nil) + (setq search-forward "false") + (setq search-forward "true")) + ;; Repeat if search string length not changed + (if (eq current-length xwidget-webkit-isearch-last-length) + (setq search-repeat "true") + (setq search-repeat "false")) + (setq xwidget-webkit-isearch-last-length current-length) + (xwidget-webkit-execute-script + (xwidget-webkit-current-session) + (format xwidget-webkit-search-js + search-forward + search-repeat + (regexp-quote string))) + ;; Unconditionally avoid 'Failing I-search ...' + (if (eq isearch-forward nil) + (goto-char (point-max)) + (goto-char (point-min))) + ))) + +;;; xwidget webkit session (defvar xwidget-webkit-last-session-buffer nil) @@ -303,7 +433,7 @@ xwidget-webkit-activeelement-js" " - "javascript that finds the active element." + "Javascript that finds the active element." ;; Yes it's ugly, because: ;; - there is apparently no way to find the active frame other than recursion ;; - the js "for each" construct misbehaved on the "frames" collection @@ -313,29 +443,35 @@ xwidget-webkit-activeelement-js" ) (defun xwidget-webkit-insert-string () - "Prompt for a string and insert it in the active field in the + "Prompt for a string and insert it in the active field in the \ current webkit widget." ;; Read out the string in the field first and provide for edit. (interactive) (let ((xww (xwidget-webkit-current-session))) + + ;; @javascript-callback + (defun xwidget-webkit-insert-string-cb (field) + "Prompt a string for the FIELD and insert in the active input." + (let ((str (pcase field + (`(,val "text") + (read-string "Text: " val)) + (`(,val "password") + (read-passwd "Password: " nil val)) + (`(,val "textarea") + (xwidget-webkit-begin-edit-textarea xww val))))) + (xwidget-webkit-execute-script + xww + (format "findactiveelement(document).value='%s'" str)))) + (xwidget-webkit-execute-script xww (concat xwidget-webkit-activeelement-js " (function () { var res = findactiveelement(document); - return [res.value, res.type]; + if (res) + return [res.value, res.type]; })();") - (lambda (field) - (let ((str (pcase field - (`[,val "text"] - (read-string "Text: " val)) - (`[,val "password"] - (read-passwd "Password: " nil val)) - (`[,val "textarea"] - (xwidget-webkit-begin-edit-textarea xww val))))) - (xwidget-webkit-execute-script - xww - (format "findactiveelement(document).value='%s'" str))))))) + 'xwidget-webkit-insert-string-cb))) (defvar xwidget-xwbl) (defun xwidget-webkit-begin-edit-textarea (xw text) @@ -444,11 +580,23 @@ xwidget-webkit-adjust-size-dispatch (ignore-errors (recenter-top-bottom))) +;; Utility functions, wanted in `window.el' + +(defun xwidget-window-inside-pixel-width (window) + "Return Emacs WINDOW body width in pixel." + (let ((edges (window-inside-pixel-edges window))) + (- (nth 2 edges) (nth 0 edges)))) + +(defun xwidget-window-inside-pixel-height (window) + "Return Emacs WINDOW body height in pixel." + (let ((edges (window-inside-pixel-edges window))) + (- (nth 3 edges) (nth 1 edges)))) + (defun xwidget-webkit-adjust-size-to-window (xwidget &optional window) "Adjust the size of the webkit XWIDGET to fit the WINDOW." (xwidget-resize xwidget - (window-pixel-width window) - (window-pixel-height window))) + (xwidget-window-inside-pixel-width window) + (xwidget-window-inside-pixel-height window))) (defun xwidget-webkit-adjust-size (w h) "Manually set webkit size to width W, height H." @@ -487,10 +635,12 @@ xwidget-webkit-new-session (get-buffer-create bufname))) ;; The xwidget id is stored in a text property, so we need to have ;; at least character in this buffer. - (insert " ") + ;; Insert invisible url, good default for next `g' to browse url. + (insert url) + (put-text-property 1 (+ 1 (length url)) 'invisible t) (setq xw (xwidget-insert 1 'webkit bufname - (window-pixel-width) - (window-pixel-height))) + (xwidget-window-inside-pixel-width (selected-window)) + (xwidget-window-inside-pixel-height (selected-window)))) (xwidget-put xw 'callback 'xwidget-webkit-callback) (xwidget-webkit-mode) (xwidget-webkit-goto-uri (xwidget-webkit-last-session) url))) @@ -515,14 +665,18 @@ xwidget-webkit-reload (xwidget-webkit-execute-script (xwidget-webkit-current-session) "history.go(0);")) +;; @javascript-callback +(defun xwidget-webkit-current-url-cb (result) + "Callback for `xwidget-webkit-current-url', message and kill the RESULT." + (let ((url (kill-new (or result "")))) + (message "url: %s" url))) + (defun xwidget-webkit-current-url () - "Get the webkit url and place it on the kill-ring." + "Get the webkit url and place it on the `kill-ring'." (interactive) (xwidget-webkit-execute-script (xwidget-webkit-current-session) - "document.URL" (lambda (rv) - (let ((url (kill-new (or rv "")))) - (message "url: %s" url))))) + "document.URL" 'xwidget-webkit-current-url-cb)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun xwidget-webkit-get-selection (proc) @@ -533,10 +687,9 @@ xwidget-webkit-get-selection proc)) (defun xwidget-webkit-copy-selection-as-kill () - "Get the webkit selection and put it on the kill-ring." + "Get the webkit selection and put it on the `kill-ring'." (interactive) - (xwidget-webkit-get-selection (lambda (selection) (kill-new selection)))) - + (xwidget-webkit-get-selection #'kill-new)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Xwidget plist management (similar to the process plist functions) diff --git a/nextstep/templates/Info.plist.in b/nextstep/templates/Info.plist.in index 5d2eb7def3..4d375374c8 100644 --- a/nextstep/templates/Info.plist.in +++ b/nextstep/templates/Info.plist.in @@ -675,5 +675,13 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. </array> <key>NSAppleScriptEnabled</key> <string>YES</string> + <!-- FIXME: Uncomment for xwidget webkit to browse remote url, + but this set no restriction at all. Consult apple's documentation + for detail information about `NSApplicationDefinedMask'. --> + <key>NSAppTransportSecurity</key> + <dict> + <key>NSAllowsArbitraryLoads</key> + <true/> + </dict> </dict> </plist> diff --git a/src/Makefile.in b/src/Makefile.in index 9a8c9c85f0..5cadb71c95 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -408,6 +408,7 @@ SOME_MACHINE_OBJECTS = xterm.o xfns.o xmenu.o xselect.o xrdb.o xsmfns.o fringe.o image.o \ fontset.o dbusbind.o cygw32.o \ nsterm.o nsfns.o nsmenu.o nsselect.o nsimage.o nsfont.o macfont.o \ + nsxwidget.o \ w32.o w32console.o w32fns.o w32heap.o w32inevt.o w32notify.o \ w32menu.o w32proc.o w32reg.o w32select.o w32term.o w32xfns.o \ w16select.o widget.o xfont.o ftfont.o xftfont.o ftxfont.o gtkutil.o \ diff --git a/src/emacs.c b/src/emacs.c index 808abcd9aa..8e740381e1 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -1532,7 +1532,6 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem syms_of_xfns (); syms_of_xmenu (); syms_of_fontset (); - syms_of_xwidget (); syms_of_xsettings (); #ifdef HAVE_X_SM syms_of_xsmfns (); @@ -1605,6 +1604,10 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem #endif /* HAVE_W32NOTIFY */ #endif /* WINDOWSNT */ +#ifdef HAVE_XWIDGETS + syms_of_xwidget (); +#endif /* HAVE_XWIDGETS */ + syms_of_threads (); syms_of_profiler (); diff --git a/src/nsterm.m b/src/nsterm.m index 50e06c94d4..5eb4f0c017 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -48,6 +48,7 @@ Updated by Christian Limpach (chris@nice.ch) #include "nsterm.h" #include "systime.h" #include "character.h" +#include "xwidget.h" #include "fontset.h" #include "composite.h" #include "ccl.h" @@ -2429,7 +2430,7 @@ so some key presses (TAB) are swallowed by the system. */ } static int -note_mouse_movement (struct frame *frame, CGFloat x, CGFloat y) +note_mouse_movement (struct frame *frame, CGFloat x, CGFloat y, BOOL dragging) /* ------------------------------------------------------------------------ Called by EmacsView on mouseMovement events. Passes on to emacs mainstream code if we moved off of a rect of interest @@ -2438,17 +2439,24 @@ so some key presses (TAB) are swallowed by the system. */ { struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame); NSRect *r; + BOOL force_update = NO; // NSTRACE ("note_mouse_movement"); dpyinfo->last_mouse_motion_frame = frame; r = &dpyinfo->last_mouse_glyph; + /* If the last rect is too large (ex, xwidget webkit), update at + every move, or resizing by dragging modeline or vertical split is + very hard to make its way. */ + if (dragging && (r->size.width > 32 || r->size.height > 32)) + force_update = YES; + /* Note, this doesn't get called for enter/leave, since we don't have a position. Those are taken care of in the corresponding NSView methods. */ /* has movement gone beyond last rect we were tracking? */ - if (x < r->origin.x || x >= r->origin.x + r->size.width + if (force_update || x < r->origin.x || x >= r->origin.x + r->size.width || y < r->origin.y || y >= r->origin.y + r->size.height) { ns_update_begin (frame); @@ -4066,6 +4074,10 @@ overwriting cursor (usually when cursor on a tab) */ ns_unfocus (s->f); break; + case XWIDGET_GLYPH: + x_draw_xwidget_glyph_string (s); + break; + case STRETCH_GLYPH: ns_dumpglyphs_stretch (s); break; @@ -6724,6 +6736,7 @@ - (void)mouseMoved: (NSEvent *)e struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe); Lisp_Object frame; NSPoint pt; + BOOL dragging; NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "[EmacsView mouseMoved:]"); @@ -6766,7 +6779,8 @@ - (void)mouseMoved: (NSEvent *)e last_mouse_window = window; } - if (!note_mouse_movement (emacsframe, pt.x, pt.y)) + dragging = (e.type == NSEventTypeLeftMouseDragged); + if (!note_mouse_movement (emacsframe, pt.x, pt.y, dragging)) help_echo_string = previous_help_echo_string; XSETFRAME (frame, emacsframe); diff --git a/src/nsxwidget.h b/src/nsxwidget.h new file mode 100644 index 0000000000..339df284c9 --- /dev/null +++ b/src/nsxwidget.h @@ -0,0 +1,77 @@ +/* Header for NS Cocoa part of xwidget and webkit widget. + +Copyright (C) 2011-2017 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs 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. + +GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef NSXWIDGET_H_INCLUDED +#define NSXWIDGET_H_INCLUDED + +/* This file can be included from non-objc files through `xwidget.h' */ +#ifdef __OBJC__ +#import <AppKit/NSView.h> +#endif + +#include "dispextern.h" +#include "lisp.h" +#include "xwidget.h" + +/* webkit */ + +bool nsxwidget_is_web_view (struct xwidget *xw); +void nsxwidget_webkit_goto_uri (struct xwidget *xw, const char *uri); +void nsxwidget_webkit_zoom (struct xwidget *xw, double zoom_change); +void nsxwidget_webkit_execute_script (struct xwidget *xw, const char *script, + Lisp_Object fun); + +/* xw: model */ + +#ifdef __OBJC__ +@interface XwWindow : NSView +@property struct xwidget *xw; +@end +#endif + +void nsxwidget_init (struct xwidget *xw); +void nsxwidget_kill (struct xwidget *xw); +void nsxwidget_resize (struct xwidget *xw); +Lisp_Object nsxwidget_get_size (struct xwidget *xw); + +/* xv: view */ + +#ifdef __OBJC__ +@interface XvWindow : NSView +@property struct xwidget *xw; +@property struct xwidget_view *xv; +@end +#endif + +void nsxwidget_init_view (struct xwidget_view *xv, + struct xwidget *xww, + struct glyph_string *s, + int x, int y); +void nsxwidget_delete_view (struct xwidget_view *xv); + +void nsxwidget_show_view (struct xwidget_view *xv); +void nsxwidget_hide_view (struct xwidget_view *xv); +void nsxwidget_resize_view (struct xwidget_view *xv, + int widget, int height); + +void nsxwidget_move_view (struct xwidget_view *xv, int x, int y); +void nsxwidget_move_widget_in_view (struct xwidget_view *xv, int x, int y); +void nsxwidget_set_needsdisplay (struct xwidget_view *xv); + +#endif /* NSXWIDGET_H_INCLUDED */ diff --git a/src/nsxwidget.m b/src/nsxwidget.m new file mode 100644 index 0000000000..86defdb6c1 --- /dev/null +++ b/src/nsxwidget.m @@ -0,0 +1,510 @@ +/* NS Cocoa part implementation of xwidget and webkit widget. + +Copyright (C) 1989, 1992-1994, 2005-2006, 2008-2017 Free Software +Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs 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. + +GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ + +#include <config.h> + +#include "lisp.h" +#include "blockinput.h" +#include "dispextern.h" +#include "buffer.h" +#include "frame.h" +#include "nsterm.h" +#include "xwidget.h" + +/* in xwidget.c */ +void store_xwidget_event_string (struct xwidget *xw, + const char *eventname, + const char *eventstr); + +void store_xwidget_js_callback_event (struct xwidget *xw, + Lisp_Object proc, + Lisp_Object argument); + +#import <AppKit/AppKit.h> +#import <WebKit/WebKit.h> + +/* Thoughts on NS Cocoa xwidget and webkit2: + + Webkit2 process architecture seems to be very hostile for offscreen + rendering techniques, which is used by GTK xwiget implementation; + Specifically NSView level view sharing / copying is not working. + + *** So only one view can be associcated with a model. *** + + With this decision, implementation is plain and can expect best out + of webkit2's rationale. But process and session structures will + diverge from GTK xwiget. Though, cosmetically similar usages can + be presented and will be preferred, if agreeable. + + For other widget types, OSR seems possible, but will not care for a + while. +*/ + +/* xwidget webkit */ + +@interface XwWebView : WKWebView +<WKNavigationDelegate, WKUIDelegate, WKScriptMessageHandler> +@property struct xwidget *xw; +@end +@implementation XwWebView : WKWebView + +- (id)initWithFrame:(CGRect)frame + configuration:(WKWebViewConfiguration *)configuration + xwidget:(struct xwidget *)xw +{ + /* Script controller to add script message handler and user script */ + WKUserContentController *scriptor = [[WKUserContentController alloc] init]; + configuration.userContentController = scriptor; + + self = [super initWithFrame:frame configuration:configuration]; + if (self) + { + self.xw = xw; + self.navigationDelegate = self; + self.UIDelegate = self; + self.customUserAgent = + @"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 addUserScript:[[WKUserScript alloc] + initWithSource:xwScript + injectionTime: + WKUserScriptInjectionTimeAtDocumentEnd + forMainFrameOnly:NO]]; + } + return self; +} + +#if 0 +/* Non ARC - just to check lifecycle */ +- (void)dealloc +{ + NSLog (@"XwWebView dealloc"); + [super dealloc]; +} +#endif + +- (void)webView:(WKWebView *)webView +didFinishNavigation:(WKNavigation *)navigation +{ + store_xwidget_event_string (self.xw, "load-changed", ""); +} + +- (void)webView:(WKWebView *)webView +decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction +decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler +{ + switch (navigationAction.navigationType) { + case WKNavigationTypeLinkActivated: + decisionHandler (WKNavigationActionPolicyAllow); + break; + default: + // decisionHandler (WKNavigationActionPolicyCancel); + decisionHandler (WKNavigationActionPolicyAllow); + break; + } +} + +- (void)webView:(WKWebView *)webView +decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse +decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler +{ + if (!navigationResponse.canShowMIMEType) + { + /* TODO: download using NSURLxxx? */ + } + decisionHandler (WKNavigationResponsePolicyAllow); +} + +/* No additional new webview or emacs window will be created + for <a ... target="_blank"> */ +- (WKWebView *)webView:(WKWebView *)webView +createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration + forNavigationAction:(WKNavigationAction *)navigationAction + windowFeatures:(WKWindowFeatures *)windowFeatures +{ + if (!navigationAction.targetFrame.isMainFrame) + [webView loadRequest:navigationAction.request]; + return nil; +} + +/* By forwarding mouse events to emacs view (frame) + - mouse click in webview selects the window contains the webview + - correct mouse hand/arrow/I-beam is display (TODO: not perfect yet) +*/ + +- (void)mouseDown:(NSEvent *)event +{ + [self.xw->xv->emacswindow mouseDown:event]; + [super mouseDown:event]; +} + +- (void)mouseUp:(NSEvent *)event +{ + [self.xw->xv->emacswindow mouseUp:event]; + [super mouseUp:event]; +} + +/* Basically we want keyboard events handled by emacs unless an input + element has focus. Especially, while incremental search, we set + emacs as first responder to avoid focus held in an input element + with matching text. */ + +- (void)keyDown:(NSEvent *)event +{ + Lisp_Object var = Fintern (build_string ("isearch-mode"), Qnil); + Lisp_Object val = buffer_local_value (var, Fcurrent_buffer ()); + if (!EQ (val, Qunbound) && !EQ (val, Qnil)) + { + [self.window makeFirstResponder:self.xw->xv->emacswindow]; + [self.xw->xv->emacswindow keyDown:event]; + return; + } + + [self evaluateJavaScript:@"xwHasFocus()" + completionHandler:^(id result, NSError *error) { + if (error) + NSLog (@"xwHasFocus: %@", error.localizedDescription); + else if (result) + { + NSNumber *hasFocus = result; /* __NSCFBoolean */ + if (!hasFocus.boolValue) + [self.xw->xv->emacswindow keyDown:event]; + else + [super keyDown:event]; + } + }]; +} + +- (void)interpretKeyEvents:(NSArray<NSEvent *> *)eventArray +{ + /* We should do nothing and do not forward (default implementation + if we not override here) to let emacs collect key events and ask + interpretKeyEvents to its superclass */ +} + +static NSString *xwScript; ++ (void)initialize +{ + /* Find out if an input element has focus. + Message to script message handler when 'C-g' key down. */ + if (!xwScript) + xwScript = + @"function xwHasFocus() {" + @" var ae = document.activeElement;" + @" if (ae) {" + @" var name = ae.nodeName;" + @" return name == 'INPUT' || name == 'TEXTAREA';" + @" } else {" + @" return false;" + @" }" + @"}" + @"function xwKeyDown(event) {" + @" if (event.ctrlKey && event.key == 'g') {" + @" window.webkit.messageHandlers.keyDown.postMessage('C-g');" + @" }" + @"}" + @"document.addEventListener('keydown', xwKeyDown);" + ; +} + +/* Confirming to WKScriptMessageHandler, listens concerning keyDown in + webkit. Currently 'C-g'. */ +- (void)userContentController:(WKUserContentController *)userContentController + didReceiveScriptMessage:(WKScriptMessage *)message +{ + if ([message.body isEqualToString:@"C-g"]) /* NSTaggedPointerString */ + { + /* 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]; + } +} + +@end + +/* webkit command */ + +bool +nsxwidget_is_web_view (struct xwidget *xw) +{ + return xw->xwWidget != NULL && + [xw->xwWidget isKindOfClass:WKWebView.class]; +} + +/* @Note ATS - application transport security in 'Info.plist' or + remote pages will not loaded */ +void +nsxwidget_webkit_goto_uri (struct xwidget *xw, const char *uri) +{ + XwWebView *xwWebView = (XwWebView *) xw->xwWidget; + NSString *urlString = [NSString stringWithUTF8String:uri]; + NSURL *url = [NSURL URLWithString:urlString]; + NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url]; + [xwWebView loadRequest:urlRequest]; +} + +void +nsxwidget_webkit_zoom (struct xwidget *xw, double zoom_change) +{ + XwWebView *xwWebView = (XwWebView *) xw->xwWidget; + xwWebView.magnification += zoom_change; + /* TODO: setMagnification:centeredAtPoint */ +} + +/* Build lisp string */ +static Lisp_Object +build_string_with_nsstr (NSString *nsstr) +{ + const char *utfstr = [nsstr UTF8String]; + NSUInteger bytes = [nsstr lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + return make_string (utfstr, bytes); +} + +/* Recursively convert an objc native type JavaScript value to a Lisp + value. Mostly copied from GTK xwidget 'webkit_js_to_lisp' */ +static Lisp_Object +js_to_lisp (id value) +{ + if (value == nil || [value isKindOfClass:NSNull.class]) + return Qnil; + else if ([value isKindOfClass:NSString.class]) + return build_string_with_nsstr ((NSString *) value); + else if ([value isKindOfClass:NSNumber.class]) + { + NSNumber *nsnum = (NSNumber *) value; + char type = nsnum.objCType[0]; + if (type == 'c') /* __NSCFBoolean has type character 'c' */ + return nsnum.boolValue? Qt : Qnil; + else + { + if (type == 'i' || type == 'l') + return make_number (nsnum.longValue); + else if (type == 'f' || type == 'd') + return make_float (nsnum.doubleValue); + /* else fall through */ + } + } + else if ([value isKindOfClass:NSArray.class]) + { + NSArray *nsarr = (NSArray *) value; + EMACS_INT n = nsarr.count; + Lisp_Object obj; + struct Lisp_Vector *p = allocate_vector (n); + + for (ptrdiff_t i = 0; i < n; ++i) + p->contents[i] = js_to_lisp ([nsarr objectAtIndex:i]); + XSETVECTOR (obj, p); + return obj; + } + else if ([value isKindOfClass:NSDictionary.class]) + { + NSDictionary *nsdict = (NSDictionary *) value; + NSArray *keys = nsdict.allKeys; + ptrdiff_t n = keys.count; + Lisp_Object obj; + struct Lisp_Vector *p = allocate_vector (n); + + for (ptrdiff_t i = 0; i < n; ++i) + { + NSString *prop_key = (NSString *) [keys objectAtIndex:i]; + id prop_value = [nsdict valueForKey:prop_key]; + p->contents[i] = Fcons (build_string_with_nsstr (prop_key), + js_to_lisp (prop_value)); + } + XSETVECTOR (obj, p); + return obj; + } + NSLog (@"Unhandled type in javascript result"); + return Qnil; +} + +void +nsxwidget_webkit_execute_script (struct xwidget *xw, const char *script, + Lisp_Object fun) +{ + NSString *javascriptString = [NSString stringWithUTF8String:script]; + XwWebView *xwWebView = (XwWebView *) xw->xwWidget; + + [xwWebView evaluateJavaScript:javascriptString + completionHandler:^(id result, NSError *error) { + if (error) + { + NSLog (@"evaluateJavaScript error : %@", error.localizedDescription); + NSLog (@"error script=%@", javascriptString); + } + else if (result && FUNCTIONP (fun)) + { + // NSLog (@"result=%@, type=%@", result, [result class]); + Lisp_Object lisp_value = js_to_lisp (result); + store_xwidget_js_callback_event (xw, fun, lisp_value); + } + }]; +} + +/* window containing an xwidget */ + +@implementation XwWindow +- (BOOL)isFlipped { return YES; } +@end + +/* xw : xwidget model, ns cocoa part */ + +void +nsxwidget_init(struct xwidget *xw) +{ + block_input (); + NSRect rect = NSMakeRect (0, 0, xw->width, xw->height); + xw->xwWidget = [[XwWebView alloc] + initWithFrame:rect + configuration:[[WKWebViewConfiguration alloc] init] + xwidget:xw]; + xw->xwWindow = [[XwWindow alloc] + initWithFrame:rect]; + [xw->xwWindow addSubview:xw->xwWidget]; + xw->xv = NULL; /* for 1 to 1 relationship of webkit2 */ + unblock_input (); +} + +void +nsxwidget_kill (struct xwidget *xw) +{ + if (xw) + { + WKUserContentController *scriptor = + ((XwWebView *) xw->xwWidget).configuration.userContentController; + [scriptor removeAllUserScripts]; + [scriptor removeScriptMessageHandlerForName:@"focusHandler"]; + [scriptor release]; + if (xw->xv) + xw->xv->model = Qnil; /* Make sure related view stale */ + [xw->xwWidget removeFromSuperviewWithoutNeedingDisplay]; + [xw->xwWidget release]; + [xw->xwWindow removeFromSuperviewWithoutNeedingDisplay]; + [xw->xwWindow release]; + xw->xwWidget = nil; + } +} + +void +nsxwidget_resize (struct xwidget *xw) +{ + if (xw->xwWidget) + { + [xw->xwWindow setFrameSize:NSMakeSize(xw->width, xw->height)]; + [xw->xwWidget setFrameSize:NSMakeSize(xw->width, xw->height)]; + } +} + +Lisp_Object +nsxwidget_get_size (struct xwidget *xw) +{ + return list2 (make_number (xw->xwWidget.frame.size.width), + make_number (xw->xwWidget.frame.size.height)); +} + +/* xv : xwidget view, ns cocoa part */ + +@implementation XvWindow : NSView +- (BOOL)isFlipped { return YES; } +@end + +void +nsxwidget_init_view (struct xwidget_view *xv, + struct xwidget *xw, + struct glyph_string *s, + int x, int y) +{ + /* 'x_draw_xwidget_glyph_string' will calculate correct position and + size of clip to draw in emacs buffer window. Thus, just begin at + origin with no crop. */ + xv->x = x; + xv->y = y; + xv->clip_left = 0; + xv->clip_right = xw->width; + xv->clip_top = 0; + xv->clip_bottom = xw->height; + + xv->xvWindow = [[XvWindow alloc] + initWithFrame:NSMakeRect (x, y, xw->width, xw->height)]; + xv->xvWindow.xw = xw; + xv->xvWindow.xv = xv; + + xw->xv = xv; /* For 1 to 1 relationship of webkit2 */ + [xv->xvWindow addSubview:xw->xwWindow]; + + xv->emacswindow = FRAME_NS_VIEW (s->f); + [xv->emacswindow addSubview:xv->xvWindow]; +} + +void +nsxwidget_delete_view (struct xwidget_view *xv) +{ + if (!EQ (xv->model, Qnil)) + { + struct xwidget *xw = XXWIDGET (xv->model); + [xw->xwWindow removeFromSuperviewWithoutNeedingDisplay]; + xw->xv = NULL; /* Now model has no view */ + } + [xv->xvWindow removeFromSuperviewWithoutNeedingDisplay]; + [xv->xvWindow release]; +} + +void +nsxwidget_show_view (struct xwidget_view *xv) +{ + xv->hidden = NO; + [xv->xvWindow setFrameOrigin:NSMakePoint(xv->x + xv->clip_left, + xv->y + xv->clip_top)]; +} + +void +nsxwidget_hide_view (struct xwidget_view *xv) +{ + xv->hidden = YES; + [xv->xvWindow setFrameOrigin:NSMakePoint(10000, 10000)]; +} + +void +nsxwidget_resize_view (struct xwidget_view *xv, int width, int height) +{ + [xv->xvWindow setFrameSize:NSMakeSize(width, height)]; +} + +void +nsxwidget_move_view (struct xwidget_view *xv, int x, int y) +{ + [xv->xvWindow setFrameOrigin:NSMakePoint (x, y)]; +} + +/* Move model window in container (view window) */ +void +nsxwidget_move_widget_in_view (struct xwidget_view *xv, int x, int y) +{ + struct xwidget *xww = xv->xvWindow.xw; + [xww->xwWindow setFrameOrigin:NSMakePoint (x, y)]; +} + +void +nsxwidget_set_needsdisplay (struct xwidget_view *xv) +{ + xv->xvWindow.needsDisplay = YES; +} diff --git a/src/xwidget.c b/src/xwidget.c index a67dc0ecf4..94147ca216 100644 --- a/src/xwidget.c +++ b/src/xwidget.c @@ -18,17 +18,26 @@ You should have received a copy of the GNU General Public License along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ #include <config.h> +#include <stdio.h> /* FIXME: Emacs error? message? instead of printf */ #include "xwidget.h" #include "lisp.h" #include "blockinput.h" +#include "dispextern.h" #include "frame.h" #include "keyboard.h" #include "gtkutil.h" +#include "termhooks.h" +#include "window.h" +/* Include xwidget bottom end headers */ +#if defined (USE_GTK) #include <webkit2/webkit2.h> #include <JavaScriptCore/JavaScript.h> +#elif defined (HAVE_NS) +#include "nsxwidget.h" +#endif static struct xwidget * allocate_xwidget (void) @@ -48,6 +57,7 @@ allocate_xwidget_view (void) static struct xwidget_view *xwidget_view_lookup (struct xwidget *, struct window *); +#if defined (USE_GTK) static void webkit_view_load_changed_cb (WebKitWebView *, WebKitLoadEvent, gpointer); @@ -61,6 +71,7 @@ webkit_decide_policy_cb (WebKitWebView *, WebKitPolicyDecision *, WebKitPolicyDecisionType, gpointer); +#endif DEFUN ("make-xwidget", @@ -92,10 +103,11 @@ Returns the newly constructed xwidget, or nil if construction fails. */) xw->kill_without_query = false; XSETXWIDGET (val, xw); Vxwidget_list = Fcons (val, Vxwidget_list); - xw->widgetwindow_osr = NULL; - xw->widget_osr = NULL; xw->plist = Qnil; +#if defined (USE_GTK) + xw->widgetwindow_osr = NULL; + xw->widget_osr = NULL; if (EQ (xw->type, Qwebkit)) { block_input (); @@ -150,6 +162,9 @@ Returns the newly constructed xwidget, or nil if construction fails. */) unblock_input (); } +#elif defined (HAVE_NS) + nsxwidget_init (xw); +#endif return val; } @@ -185,6 +200,7 @@ xwidget_hidden (struct xwidget_view *xv) return xv->hidden; } +#if defined (USE_GTK) static void xwidget_show_view (struct xwidget_view *xv) { @@ -223,8 +239,9 @@ offscreen_damage_event (GtkWidget *widget, GdkEvent *event, return FALSE; } +#endif /* USE_GTK */ -static void +void store_xwidget_event_string (struct xwidget *xw, const char *eventname, const char *eventstr) { @@ -238,7 +255,7 @@ store_xwidget_event_string (struct xwidget *xw, const char *eventname, kbd_buffer_store_event (&event); } -static void +void store_xwidget_js_callback_event (struct xwidget *xw, Lisp_Object proc, Lisp_Object argument) @@ -254,6 +271,7 @@ store_xwidget_js_callback_event (struct xwidget *xw, } +#if defined (USE_GTK) void webkit_view_load_changed_cb (WebKitWebView *webkitwebview, WebKitLoadEvent load_event, @@ -389,9 +407,8 @@ webkit_javascript_finished_cb (GObject *webview, /* Register an xwidget event here, which then runs the callback. This ensures that the callback runs in sync with the Emacs event loop. */ - /* FIXME: This might lead to disaster if LISP_CALLBACK’s object - was garbage collected before now. See the FIXME in - Fxwidget_webkit_execute_script. */ + /* LISP_CALLBACK must not be garbage collected up to here. See + comments in Fxwidget_webkit_execute_script. */ store_xwidget_js_callback_event (xw, XIL ((intptr_t) lisp_callback), lisp_value); } @@ -500,6 +517,7 @@ xwidget_osr_event_set_embedder (GtkWidget *widget, GdkEvent *event, gtk_widget_get_window (xv->widget)); return FALSE; } +#endif /* USE_GTK */ /* Initializes and does initial placement of an xwidget view on screen. */ @@ -517,6 +535,7 @@ xwidget_init_view (struct xwidget *xww, XSETWINDOW (xv->w, s->w); XSETXWIDGET (xv->model, xww); +#if defined (USE_GTK) if (EQ (xww->type, Qwebkit)) { xv->widget = gtk_drawing_area_new (); @@ -574,6 +593,9 @@ xwidget_init_view (struct xwidget *xww, xv->x = x; xv->y = y; gtk_widget_show_all (xv->widgetwindow); +#elif defined (HAVE_NS) + nsxwidget_init_view (xv, xww, s, x, y); +#endif return xv; } @@ -586,24 +608,59 @@ x_draw_xwidget_glyph_string (struct glyph_string *s) initialization. */ struct xwidget *xww = s->xwidget; struct xwidget_view *xv = xwidget_view_lookup (xww, s->w); + int text_area_x, text_area_y, text_area_width, text_area_height; int clip_right; int clip_bottom; int clip_top; int clip_left; int x = s->x; - int y = s->y + (s->height / 2) - (xww->height / 2); + int y = s->y; /* Do initialization here in the display loop because there is no other time to know things like window placement etc. Do not create a new view if we have found one that is usable. */ +#if defined (USE_GTK) if (!xv) xv = xwidget_init_view (xww, s, x, y); - - int text_area_x, text_area_y, text_area_width, text_area_height; +#elif defined (HAVE_NS) + if (!xv) + { + /* Enforce 1 to 1, model and view for NS Cocoa webkit2 */ + if (xww->xv) + { + if (xwidget_hidden (xww->xv)) + { + Lisp_Object xvl; + XSETXWIDGET_VIEW (xvl, xww->xv); + Fdelete_xwidget_view (xvl); + } + else + { + message ("You can't share an xwidget (webkit2) among windows."); + return; + } + } + xv = xwidget_init_view (xww, s, x, y); + } +#endif window_box (s->w, TEXT_AREA, &text_area_x, &text_area_y, &text_area_width, &text_area_height); + + /* Resize xwidget webkit if its container window size is changed in + some ways, for example, a buffer became hidden in small split + window, then it can appear front in merged whole window. */ + if (EQ (xww->type, Qwebkit) + && (xww->width != text_area_width || xww->height != text_area_height)) + { + Lisp_Object xwl; + XSETXWIDGET (xwl, xww); + Fxwidget_resize (xwl, + make_number (text_area_width), + make_number (text_area_height)); + } + clip_left = max (0, text_area_x - x); clip_right = max (clip_left, min (xww->width, text_area_x + text_area_width - x)); @@ -626,8 +683,14 @@ x_draw_xwidget_glyph_string (struct glyph_string *s) /* Has it moved? */ if (moved) - gtk_fixed_move (GTK_FIXED (FRAME_GTK_WIDGET (s->f)), - xv->widgetwindow, x + clip_left, y + clip_top); + { +#if defined (USE_GTK) + gtk_fixed_move (GTK_FIXED (FRAME_GTK_WIDGET (s->f)), + xv->widgetwindow, x + clip_left, y + clip_top); +#elif defined (HAVE_NS) + nsxwidget_move_view (xv, x + clip_left, y + clip_top); +#endif + } /* Clip the widget window if some parts happen to be outside drawable area. An Emacs window is not a gtk window. A gtk window @@ -638,10 +701,16 @@ x_draw_xwidget_glyph_string (struct glyph_string *s) || xv->clip_bottom != clip_bottom || xv->clip_top != clip_top || xv->clip_left != clip_left) { +#if defined (USE_GTK) gtk_widget_set_size_request (xv->widgetwindow, clip_right - clip_left, clip_bottom - clip_top); gtk_fixed_move (GTK_FIXED (xv->widgetwindow), xv->widget, -clip_left, -clip_top); +#elif defined (HAVE_NS) + nsxwidget_resize_view (xv, clip_right - clip_left, + clip_bottom - clip_top); + nsxwidget_move_widget_in_view (xv, -clip_left, -clip_top); +#endif xv->clip_right = clip_right; xv->clip_bottom = clip_bottom; @@ -655,18 +724,32 @@ x_draw_xwidget_glyph_string (struct glyph_string *s) xwidgets background. It's just a visual glitch though. */ if (!xwidget_hidden (xv)) { +#if defined (USE_GTK) gtk_widget_queue_draw (xv->widgetwindow); gtk_widget_queue_draw (xv->widget); +#elif defined (HAVE_NS) + nsxwidget_set_needsdisplay (xv); +#endif } } -/* Macro that checks WEBKIT_IS_WEB_VIEW (xw->widget_osr) first. */ +static bool +xwidget_is_web_view (struct xwidget *xw) +{ +#if defined (USE_GTK) + return xw->widget_osr != NULL && WEBKIT_IS_WEB_VIEW (xw->widget_osr); +#elif defined (HAVE_NS) + return nsxwidget_is_web_view (xw); +#endif /* defined (HAVE_NS) */ +} + +/* Macro that checks xwidget hold webkit web view first. */ #define WEBKIT_FN_INIT() \ CHECK_XWIDGET (xwidget); \ struct xwidget *xw = XXWIDGET (xwidget); \ - if (!xw->widget_osr || !WEBKIT_IS_WEB_VIEW (xw->widget_osr)) \ + if (!xwidget_is_web_view (xw)) \ { \ - printf ("ERROR xw->widget_osr does not hold a webkit instance\n"); \ + printf ("ERROR xwidget does not hold a webkit instance\n"); \ return Qnil; \ } @@ -678,7 +761,11 @@ DEFUN ("xwidget-webkit-goto-uri", { WEBKIT_FN_INIT (); CHECK_STRING (uri); +#if defined (USE_GTK) webkit_web_view_load_uri (WEBKIT_WEB_VIEW (xw->widget_osr), SSDATA (uri)); +#elif defined (HAVE_NS) + nsxwidget_webkit_goto_uri (xw, SSDATA (uri)); +#endif return Qnil; } @@ -693,10 +780,14 @@ referenced by XWIDGET. */) if (FLOATP (factor)) { double zoom_change = XFLOAT_DATA (factor); +#if defined (USE_GTK) webkit_web_view_set_zoom_level (WEBKIT_WEB_VIEW (xw->widget_osr), webkit_web_view_get_zoom_level (WEBKIT_WEB_VIEW (xw->widget_osr)) + zoom_change); +#elif defined (HAVE_NS) + nsxwidget_webkit_zoom (xw, zoom_change); +#endif } return Qnil; } @@ -712,17 +803,18 @@ argument procedure FUN.*/) { WEBKIT_FN_INIT (); CHECK_STRING (script); - if (!NILP (fun) && !FUNCTIONP (fun)) + /* FUN will not be garbage collected if it is defined with `defun' + instead of `lambda'. If it is garbage collected even though it + is `defun', we can counter by pinning the FUN's symbol. */ + if (!NILP (fun) && !SYMBOLP (fun) && !NILP (Ffboundp (fun))) wrong_type_argument (Qinvalid_function, fun); +#if defined (USE_GTK) GAsyncReadyCallback callback = FUNCTIONP (fun) ? webkit_javascript_finished_cb : NULL; /* FIXME: The following hack assumes USE_LSB_TAG. */ verify (USE_LSB_TAG); - /* FIXME: This hack might lead to disaster if FUN is garbage - collected before store_xwidget_js_callback_event makes it visible - to Lisp again. See the FIXME in webkit_javascript_finished_cb. */ gpointer callback_arg = (gpointer) (intptr_t) XLI (fun); /* JavaScript execution happens asynchronously. If an elisp @@ -732,6 +824,9 @@ argument procedure FUN.*/) SSDATA (script), NULL, /* cancelable */ callback, callback_arg); +#elif defined (HAVE_NS) + nsxwidget_webkit_execute_script (xw, SSDATA (script), fun); +#endif return Qnil; } @@ -750,6 +845,7 @@ DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0, xw->height = h; /* If there is an offscreen widget resize it first. */ +#if defined (USE_GTK) if (xw->widget_osr) { gtk_window_resize (GTK_WINDOW (xw->widgetwindow_osr), xw->width, @@ -758,6 +854,9 @@ DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0, gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr), xw->width, xw->height); } +#elif defined (HAVE_NS) + nsxwidget_resize (xw); +#endif for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail); tail = XCDR (tail)) { @@ -765,8 +864,14 @@ DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0, { struct xwidget_view *xv = XXWIDGET_VIEW (XCAR (tail)); if (XXWIDGET (xv->model) == xw) + { +#if defined (USE_GTK) gtk_widget_set_size_request (GTK_WIDGET (xv->widget), xw->width, xw->height); +#elif defined (HAVE_NS) + nsxwidget_resize_view(xv, xw->width, xw->height); +#endif + } } } @@ -785,10 +890,14 @@ Emacs allocated area accordingly. */) (Lisp_Object xwidget) { CHECK_XWIDGET (xwidget); +#if defined (USE_GTK) GtkRequisition requisition; gtk_widget_size_request (XXWIDGET (xwidget)->widget_osr, &requisition); return list2 (make_number (requisition.width), make_number (requisition.height)); +#elif defined (HAVE_NS) + return nsxwidget_get_size(XXWIDGET (xwidget)); +#endif } DEFUN ("xwidgetp", @@ -865,14 +974,19 @@ DEFUN ("delete-xwidget-view", { CHECK_XWIDGET_VIEW (xwidget_view); struct xwidget_view *xv = XXWIDGET_VIEW (xwidget_view); - gtk_widget_destroy (xv->widgetwindow); Vxwidget_view_list = Fdelq (xwidget_view, Vxwidget_view_list); +#if defined (USE_GTK) + gtk_widget_destroy (xv->widgetwindow); /* xv->model still has signals pointing to the view. There can be several views. Find the matching signals and delete them all. */ g_signal_handlers_disconnect_matched (XXWIDGET (xv->model)->widgetwindow_osr, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, xv->widget); +#elif defined (HAVE_NS) + nsxwidget_delete_view (xv); +#endif + return Qnil; } @@ -1151,11 +1265,19 @@ xwidget_end_redisplay (struct window *w, struct glyph_matrix *matrix) xwidget_end_redisplay (w->current_matrix); */ struct xwidget_view *xv = xwidget_view_lookup (glyph->u.xwidget, w); +#if defined (USE_GTK) /* FIXME: Is it safe to assume xwidget_view_lookup always succeeds here? If so, this comment can be removed. If not, the code probably needs fixing. */ eassume (xv); xwidget_touch (xv); +#elif defined (HAVE_NS) + /* In NS xwidget, xv can be NULL for the second or + later views for a model, the result of 1 to 1 + model view relation enforcement. */ + if (xv) + xwidget_touch (xv); +#endif } } } @@ -1172,9 +1294,21 @@ xwidget_end_redisplay (struct window *w, struct glyph_matrix *matrix) if (XWINDOW (xv->w) == w) { if (xwidget_touched (xv)) - xwidget_show_view (xv); + { +#if defined (USE_GTK) + xwidget_show_view (xv); +#elif defined (HAVE_NS) + nsxwidget_show_view (xv); +#endif + } else - xwidget_hide_view (xv); + { +#if defined (USE_GTK) + xwidget_hide_view (xv); +#elif defined (HAVE_NS) + nsxwidget_hide_view (xv); +#endif + } } } } @@ -1193,11 +1327,16 @@ kill_buffer_xwidgets (Lisp_Object buffer) { CHECK_XWIDGET (xwidget); struct xwidget *xw = XXWIDGET (xwidget); +#if defined (USE_GTK) if (xw->widget_osr && xw->widgetwindow_osr) { gtk_widget_destroy (xw->widget_osr); gtk_widget_destroy (xw->widgetwindow_osr); } +#elif defined (HAVE_NS) + nsxwidget_kill (xw); +#endif + /* TODO: de/unallocate_xwidget */ } } } diff --git a/src/xwidget.h b/src/xwidget.h index 02a0453dab..cdd028e6f8 100644 --- a/src/xwidget.h +++ b/src/xwidget.h @@ -29,7 +29,13 @@ struct xwidget_view; struct window; #ifdef HAVE_XWIDGETS -# include <gtk/gtk.h> + +#if defined (USE_GTK) +#include <gtk/gtk.h> +#elif defined (HAVE_NS) && defined (__OBJC__) +#import <AppKit/NSView.h> +#import "nsxwidget.h" +#endif struct xwidget { @@ -52,9 +58,25 @@ struct xwidget int height; int width; +#if defined (USE_GTK) /* For offscreen widgets, unused if not osr. */ GtkWidget *widget_osr; GtkWidget *widgetwindow_osr; +#elif defined (HAVE_NS) +# ifdef __OBJC__ + /* For offscreen widgets, unused if not osr. */ + NSView *xwWidget; + XwWindow *xwWindow; + + /* Used only for xwidget types (such as webkit2) enforcing 1 to 1 + relationship between model and view. */ + struct xwidget_view *xv; +# else + void *xwWidget; + void *xwWindow; + struct xwidget_view *xv; +# endif +#endif /* Kill silently if Emacs is exited. */ bool_bf kill_without_query : 1; @@ -74,9 +96,20 @@ struct xwidget_view /* The "live" instance isn't drawn. */ bool hidden; +#if defined (USE_GTK) GtkWidget *widget; GtkWidget *widgetwindow; GtkWidget *emacswindow; +#elif defined (HAVE_NS) +# ifdef __OBJC__ + XvWindow *xvWindow; + NSView *emacswindow; +# else + void *xvWindow; + void *emacswindow; +# endif +#endif + int x; int y; int clip_right; -- 2.15.0 ^ permalink raw reply related [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2017-12-04 16:44 bug#29565: [PATCH] Support xwidget webkit for macOS X Jaesup Kwak @ 2017-12-04 20:59 ` Alan Third 2017-12-05 6:01 ` Jaesup Kwak 2017-12-13 11:15 ` Jaesup Kwak ` (10 subsequent siblings) 11 siblings, 1 reply; 41+ messages in thread From: Alan Third @ 2017-12-04 20:59 UTC (permalink / raw) To: Jaesup Kwak; +Cc: 29565 On Tue, Dec 05, 2017 at 01:44:42AM +0900, Jaesup Kwak wrote: > I attached a patch to support xwidget webkit for macOS X, rebased onto the > latest master. Hi, thanks for working on this, it looks really good. I’ve had a quick glance over it and I have a few comments. In configure.ac you’re doing elif test "$window_system" = "nextstep"; then I think instead of that you should be doing elif test "${NS_IMPL_COCOA}" = "yes"; then as nextstep covers GNUstep, but it doesn’t have a webkit implementation (yet). There might be other places where NS_IMPL_COCOA is more appropriate than HAVE_NS, but it builds fine against GNUstep with xwidgets off, so it’s not too important. I notice you’re adding .DS_Store to .gitignore, which is a good idea, but I don’t think it should be done in this patch. It seems a little off‐topic. I’m also a little unsure about this <!-- FIXME: Uncomment for xwidget webkit to browse remote url, but this set no restriction at all. Consult apple's documentation for detail information about `NSApplicationDefinedMask'. --> <key>NSAppTransportSecurity</key> <dict> <key>NSAllowsArbitraryLoads</key> <true/> </dict> The comment says ‘uncomment’, but it *is* uncommented. Am I misunderstanding? (Also I think there should be two spaces at the end of comments.) I think we’ll want to add NSTRACE lines in each function, but perhaps we should create a new category for XWidgets. -- Alan Third ^ permalink raw reply [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2017-12-04 20:59 ` Alan Third @ 2017-12-05 6:01 ` Jaesup Kwak 2017-12-05 7:55 ` Jaesup Kwak 0 siblings, 1 reply; 41+ messages in thread From: Jaesup Kwak @ 2017-12-05 6:01 UTC (permalink / raw) To: Alan Third; +Cc: 29565 [-- Attachment #1: Type: text/plain, Size: 1965 bytes --] On Tue, Dec 5, 2017 at 5:59 AM, Alan Third <alan@idiocy.org> wrote: > > In configure.ac you’re doing > > elif test "$window_system" = "nextstep"; then > > I think instead of that you should be doing > > elif test "${NS_IMPL_COCOA}" = "yes"; then > > as nextstep covers GNUstep, but it doesn’t have a webkit > implementation (yet). > > There might be other places where NS_IMPL_COCOA is more appropriate > than HAVE_NS, but it builds fine against GNUstep with xwidgets off, so > it’s not too important. > Agree. I fixed to use "${NS_IMPL_COCOA}" in configura.ac and #ifdef NS_IMPL_COCOA in implementation source files. I am testing the fixes. It looks good to me also. > I notice you’re adding .DS_Store to .gitignore, which is a good idea, > but I don’t think it should be done in this patch. It seems a little > off‐topic. Okay, I will remove it from .gitignore. I’m also a little unsure about this > > <!-- FIXME: Uncomment for xwidget webkit to browse remote url, > but this set no restriction at all. Consult apple's documentation > for detail information about `NSApplicationDefinedMask'. --> > <key>NSAppTransportSecurity</key> > <dict> > <key>NSAllowsArbitraryLoads</key> > <true/> > </dict> > > The comment says ‘uncomment’, but it *is* uncommented. Am I > misunderstanding? > You are right, the comment is outdated and I will fix it. > (Also I think there should be two spaces at the end of comments.) > I was curious about the two spaces ending comments. It is clear now thanks to you. I will fix this also. > > I think we’ll want to add NSTRACE lines in each function, but perhaps > we should create a new category for XWidgets. I put off this item at this time, there will be chance to do this later. I will soon post a patch accompanying above changes. Thanks for your valuable comments. [-- Attachment #2: Type: text/html, Size: 3589 bytes --] ^ permalink raw reply [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2017-12-05 6:01 ` Jaesup Kwak @ 2017-12-05 7:55 ` Jaesup Kwak 2017-12-05 20:00 ` Alan Third 0 siblings, 1 reply; 41+ messages in thread From: Jaesup Kwak @ 2017-12-05 7:55 UTC (permalink / raw) To: 29565; +Cc: Alan Third [-- Attachment #1.1: Type: text/plain, Size: 324 bytes --] On Tue, Dec 5, 2017 at 3:01 PM, Jaesup Kwak <veshboo@gmail.com> wrote: > > I will soon post a patch accompanying above changes. > > I made and attached a new patch, which includes changes for comments from Alan Third and previous patch. So, please ignore the patch file from previous post and use this patch file. Thanks. [-- Attachment #1.2: Type: text/html, Size: 871 bytes --] [-- Attachment #2: 0001-Support-xwidget-webkit-for-macOS-X.20171205.patch --] [-- Type: application/octet-stream, Size: 61206 bytes --] From 3be38a63735e911b7322591af5e38d1ac90a3125 Mon Sep 17 00:00:00 2001 From: Jaesup Kwak <veshboo@gmail.com> Date: Mon, 4 Dec 2017 21:23:19 +0900 Subject: [PATCH] Support xwidget webkit for macOS X Add xwidget webkit support for macOS X / NS Cocoa and accompanying changes. Squash changes for comments from Alan Third in Bug#29565. * configure.ac: Allow '--with-xwidgets' for "${NS_IMPL_COCOA}". * lisp/xwidget.el (xwidget-webkit-cx2-cb, xwidget-webkit-cx3-cb, xwidget-webkit-cx2) (xwidget-webkit-cx3, xwidget-webkit-mode-map): New webkit session when window split and key map for 'nextstep'. (xwidget-log): Fix typo in buffer name. (xwidget-event-handler): Remove a 'message'. (xwidget-webkit-callback, xwidget-webkit-url-title-cb): Replace lambda with defun for GC safe, remember URL for bookmark integration, and support vector result from javascript. (xwidget-webkit-mode): No cursor and integration for 'isearch'. (xwidget-webkit-bookmark-jump-new-session): Add new variable. (xwidget-webkit-bookmark-make-record): Changes to save URL in 'filename' attribute of a bookmark record, the URL obtained when the page was loaded, and 'switch-to-buffer' when a bookmark selected. (xwidget-webkit-isearch-last-length, xwidget-webkit-search-js) (xwidget-webkit-search-fun-function): Integration for 'isearch'. (xwidget-webkit-insert-string, xwidget-webkit-insert-string-cb): GC safe, FIELD is now a list, and fix for a javascript exception. (xwidget-window-inside-pixel-width) (xwidget-window-inside-pixel-height) (xwidget-webkit-adjust-size-to-window) (xwidget-webkit-new-session): New functions to get window inside width and height in pixel and insert invisible URL instead of ' '. (xwidget-webkit-current-url, xwidget-webkit-current-url-cb): GC safe. (xwidget-webkit-copy-selection-as-kill): GC safe. * nextstep/templates/Info.plist.in: Add 'NSAppTransportSecurity'. * src/Makefile.in: Add nsxwidget.o for compilation. * src/emacs.c (main): Simplify conditional call to 'syms_of_xwidget'. * src/nsterm.m (ns_draw_glyph_string): Add case for 'XWIDGET_GLYPH'. (note_mouse_movement mouseMoved): Make it easy to resize window by dragging mode-line or vertical separator adjacent to large glyph. * src/nsxwidget.h src/nsxwidget.m: Newly added files, xwidget webkit backend for macOS Cocoa. * src/xwidget.c src/xwidget.h (xwidget xwidget_view): Add macOS Cocoa specific fields with 'NS_IMPL_COCOA', guard GTK specific fields with 'USE_GTK' in the structures, and implement accordingly. (x_draw_xwidget_glyph_string): Change for top alignment of xwidgets in a glyph string instead of vertically middle alignment. --- configure.ac | 35 ++- lisp/xwidget.el | 235 ++++++++++++++---- nextstep/templates/Info.plist.in | 8 + src/Makefile.in | 1 + src/emacs.c | 5 +- src/nsterm.m | 20 +- src/nsxwidget.h | 77 ++++++ src/nsxwidget.m | 509 +++++++++++++++++++++++++++++++++++++++ src/xwidget.c | 182 ++++++++++++-- src/xwidget.h | 35 ++- 10 files changed, 1029 insertions(+), 78 deletions(-) create mode 100644 src/nsxwidget.h create mode 100644 src/nsxwidget.m diff --git a/configure.ac b/configure.ac index 61455a4b0f..3d521fcfb1 100644 --- a/configure.ac +++ b/configure.ac @@ -399,7 +399,7 @@ AC_DEFUN [with_file_notification=$with_features]) OPTION_DEFAULT_OFF([xwidgets], - [enable use of some gtk widgets in Emacs buffers (requires gtk3)]) + [enable use of some gtk widgets in Emacs buffers (requires gtk3 or macOS Cocoa)]) ## For the times when you want to build Emacs but don't have ## a suitable makeinfo, and can live without the manuals. @@ -2715,20 +2715,34 @@ AC_DEFUN dnl Enable xwidgets if GTK3 and WebKitGTK+ are available. +dnl Enable xwidgets if macOS Cocoa and WebKit framework are available. HAVE_XWIDGETS=no XWIDGETS_OBJ= if test "$with_xwidgets" != "no"; then - test "$USE_GTK_TOOLKIT" = "GTK3" && test "$window_system" != "none" || - AC_MSG_ERROR([xwidgets requested but gtk3 not used.]) + if test "$USE_GTK_TOOLKIT" = "GTK3" && test "$window_system" != "none"; then + WEBKIT_REQUIRED=2.12 + WEBKIT_MODULES="webkit2gtk-4.0 >= $WEBKIT_REQUIRED" + EMACS_CHECK_MODULES([WEBKIT], [$WEBKIT_MODULES]) + HAVE_XWIDGETS=$HAVE_WEBKIT + XWIDGETS_OBJ="xwidget.o" + elif test "${NS_IMPL_COCOA}" = "yes"; then + dnl FIXME: Check framework WebKit2 + dnl WEBKIT_REQUIRED=M.m.p + WEBKIT_LIBS="-Wl,-framework -Wl,WebKit" + WEBKIT_CFLAGS="-D_REENTRANT -I/System/Library/Frameworks/WebKit.framework/Headers" + HAVE_WEBKIT="yes" + HAVE_XWIDGETS=$HAVE_WEBKIT + XWIDGETS_OBJ="xwidget.o" + NS_OBJC_OBJ="$NS_OBJC_OBJ nsxwidget.o" + dnl Update NS_OBJC_OBJ with added nsxwidget.o + AC_SUBST(NS_OBJC_OBJ) + else + AC_MSG_ERROR([xwidgets requested, it requires GTK3 as X window toolkit or macOS Cocoa as window systeml]) + fi - WEBKIT_REQUIRED=2.12 - WEBKIT_MODULES="webkit2gtk-4.0 >= $WEBKIT_REQUIRED" - EMACS_CHECK_MODULES([WEBKIT], [$WEBKIT_MODULES]) - HAVE_XWIDGETS=$HAVE_WEBKIT test $HAVE_XWIDGETS = yes || - AC_MSG_ERROR([xwidgets requested but WebKitGTK+ not found.]) + AC_MSG_ERROR([xwidgets requested but WebKitGTK+ or WebKit framework not found.]) - XWIDGETS_OBJ=xwidget.o AC_DEFINE([HAVE_XWIDGETS], 1, [Define to 1 if you have xwidgets support.]) fi AC_SUBST(XWIDGETS_OBJ) @@ -5419,7 +5433,8 @@ AC_DEFUN Does Emacs directly use zlib? ${HAVE_ZLIB} Does Emacs have dynamic modules support? ${HAVE_MODULES} Does Emacs use toolkit scroll bars? ${USE_TOOLKIT_SCROLL_BARS} - Does Emacs support Xwidgets (requires gtk3)? ${HAVE_XWIDGETS} + Does Emacs support Xwidgets? ${HAVE_XWIDGETS} + (requires gtk3 or macOS Cocoa) Does Emacs have threading support in lisp? ${threads_enabled} "]) diff --git a/lisp/xwidget.el b/lisp/xwidget.el index 5e37209cc2..03183c19d5 100644 --- a/lisp/xwidget.el +++ b/lisp/xwidget.el @@ -78,6 +78,7 @@ xwidget-at ;;; webkit support (require 'browse-url) (require 'image-mode);;for some image-mode alike functionality +(require 'seq) ;;;###autoload (defun xwidget-webkit-browse-url (url &optional new-session) @@ -96,6 +97,38 @@ xwidget-webkit-browse-url (xwidget-webkit-new-session url) (xwidget-webkit-goto-url url)))) +;; NOTE: @javascript-callback - prefer defun to lambda. +;; Lambda seems to be more easily garbage collected in flight from +;; `xwidget-webkit-execute-script' to its execution via event. + +;; @javascript-callback +(defun xwidget-webkit-cx2-cb (url) + "New xwidget webkit session and buffer with URL in split window below." + (with-selected-window (split-window-below) + (xwidget-webkit-new-session url))) + +;; @javascript-callback +(defun xwidget-webkit-cx3-cb (url) + "New xwidget webkit session and buffer with URL in split window right." + (with-selected-window (split-window-right) + (xwidget-webkit-new-session url))) + +(defun xwidget-webkit-cx2 () + "Get the URL of current session, then `xwidget-webkit-cx2-cb'." + (interactive) + (xwidget-webkit-execute-script + (xwidget-webkit-current-session) + "document.URL" + 'xwidget-webkit-cx2-cb)) + +(defun xwidget-webkit-cx3 () + "Get the URL of current session, then `xwidget-webkit-cx3-cb'." + (interactive) + (xwidget-webkit-execute-script + (xwidget-webkit-current-session) + "document.URL" + 'xwidget-webkit-cx3-cb)) + ;;todo. ;; - check that the webkit support is compiled in (defvar xwidget-webkit-mode-map @@ -131,6 +164,12 @@ xwidget-webkit-mode-map ;; (define-key map [remap move-end-of-line] 'image-eol) (define-key map [remap beginning-of-buffer] 'xwidget-webkit-scroll-top) (define-key map [remap end-of-buffer] 'xwidget-webkit-scroll-bottom) + + ;; For macOS xwidget webkit, we don't support multiple views for a + ;; model, instead, create a new session and model behind the scene. + (when (memq window-system '(mac ns)) + (define-key map (kbd "C-x 2") 'xwidget-webkit-cx2) + (define-key map (kbd "C-x 3") 'xwidget-webkit-cx3)) map) "Keymap for `xwidget-webkit-mode'.") @@ -192,7 +231,7 @@ xwidget-webkit-scroll-bottom (define-key (current-global-map) [xwidget-event] #'xwidget-event-handler) (defun xwidget-log (&rest msg) "Log MSG to a buffer." - (let ((buf (get-buffer-create " *xwidget-log*"))) + (let ((buf (get-buffer-create "*xwidget-log*"))) (with-current-buffer buf (insert (apply #'format msg)) (insert "\n")))) @@ -208,7 +247,6 @@ xwidget-event-handler ;;TODO stopped working for some reason ) ;;(funcall xwidget-callback xwidget xwidget-event-type) - (message "xw callback %s" xwidget) (funcall 'xwidget-webkit-callback xwidget xwidget-event-type))) (defun xwidget-webkit-callback (xwidget xwidget-event-type) @@ -218,16 +256,25 @@ xwidget-webkit-callback (xwidget-log "error: callback called for xwidget with dead buffer") (with-current-buffer (xwidget-buffer xwidget) + + ;; @javascript-callback + ;; We do not change selected window due to getting to knowing + ;; URL and title. And also do not adjust webkit size to window + ;; here, the window can be the mini-buffer window unwantedly. + (defun xwidget-webkit-url-title-cb (url-title) + "Put URL as text property and change buffer name using TITLE." + (let ((url (car url-title)) + (title (car (cdr url-title)))) + (xwidget-log "webkit finished loading: '%s' from '%s'" title url) + (setq buffer-read-only nil) + (put-text-property 2 3 'URL url) + (setq buffer-read-only t) + (rename-buffer (format "*xwidget webkit: %s *" title)))) + (cond ((eq xwidget-event-type 'load-changed) (xwidget-webkit-execute-script - xwidget "document.title" - (lambda (title) - (xwidget-log "webkit finished loading: '%s'" title) - ;;TODO - check the native/internal scroll - ;;(xwidget-adjust-size-to-content xwidget) - (xwidget-webkit-adjust-size-to-window xwidget) - (rename-buffer (format "*xwidget webkit: %s *" title)))) - (pop-to-buffer (current-buffer))) + xwidget "[document.URL, document.title]" + 'xwidget-webkit-url-title-cb)) ((eq xwidget-event-type 'decide-policy) (let ((strarg (nth 3 last-input-event))) (if (string-match ".*#\\(.*\\)" strarg) @@ -237,25 +284,108 @@ xwidget-webkit-callback ((eq xwidget-event-type 'javascript-callback) (let ((proc (nth 3 last-input-event)) (arg (nth 4 last-input-event))) - (funcall proc arg))) + ;; Some javascript return vector as result + (if (vectorp arg) + (funcall proc (seq-into arg 'list)) + (funcall proc arg)))) (t (xwidget-log "unhandled event:%s" xwidget-event-type)))))) (defvar bookmark-make-record-function) +(defvar isearch-search-fun-function) (define-derived-mode xwidget-webkit-mode special-mode "xwidget-webkit" "Xwidget webkit view mode." (setq buffer-read-only t) + (setq cursor-type nil) (setq-local bookmark-make-record-function #'xwidget-webkit-bookmark-make-record) + (setq-local isearch-search-fun-function + #'xwidget-webkit-search-fun-function) + (setq-local isearch-lazy-highlight nil) ;; Keep track of [vh]scroll when switching buffers (image-mode-setup-winprops)) +;;; Bookmarks integration + +(defvar xwidget-webkit-bookmark-jump-new-session nil + "Control bookmark jump to use new session or not. +If non-nil, it will use a new session. Otherwise, it will use +`xwidget-webkit-last-session'. When you set this variable to +nil, consider further customization with +`xwidget-webkit-last-session-buffer'.") + +;; We avoid using async `xwidget-webkit-current-url', instead use URL +;; kept in xwidget webkit as property (defun xwidget-webkit-bookmark-make-record () "Integrate Emacs bookmarks with the webkit xwidget." (nconc (bookmark-make-record-default t t) - `((page . ,(xwidget-webkit-current-url)) - (handler . (lambda (bmk) (browse-url - (bookmark-prop-get bmk 'page))))))) - + `((filename . ,(get-text-property 2 'URL)) + (handler . (lambda (bmk) + (browse-url + (bookmark-prop-get bmk 'filename) + xwidget-webkit-bookmark-jump-new-session) + (switch-to-buffer + (xwidget-buffer (xwidget-webkit-last-session)))))))) + +;;; Search text in page + +;; Initialize last search text length variable when isearch starts +(defvar xwidget-webkit-isearch-last-length 0) +(add-hook 'isearch-mode-hook + (lambda () + (setq xwidget-webkit-isearch-last-length 0))) + +;; This is minimal. Regex and incremental search will be nice +(defvar xwidget-webkit-search-js " +var xwSearchForward = %s; +var xwSearchRepeat = %s; +var xwSearchString = '%s'; +if (window.getSelection() && !window.getSelection().isCollapsed) { + if (xwSearchRepeat) { + if (xwSearchForward) + window.getSelection().collapseToEnd(); + else + window.getSelection().collapseToStart(); + } else { + if (xwSearchForward) + window.getSelection().collapseToStart(); + else { + var sel = window.getSelection(); + window.getSelection().collapse(sel.focusNode, sel.focusOffset + 1); + } + } +} +window.find(xwSearchString, false, !xwSearchForward, true, false, true); +") + +(defun xwidget-webkit-search-fun-function () + "Return the function which perform the search in xwidget webkit." + (lambda (string &optional bound noerror count) + (ignore bound noerror count) + (let ((current-length (length string)) + search-forward + search-repeat) + ;; Forward or backward + (if (eq isearch-forward nil) + (setq search-forward "false") + (setq search-forward "true")) + ;; Repeat if search string length not changed + (if (eq current-length xwidget-webkit-isearch-last-length) + (setq search-repeat "true") + (setq search-repeat "false")) + (setq xwidget-webkit-isearch-last-length current-length) + (xwidget-webkit-execute-script + (xwidget-webkit-current-session) + (format xwidget-webkit-search-js + search-forward + search-repeat + (regexp-quote string))) + ;; Unconditionally avoid 'Failing I-search ...' + (if (eq isearch-forward nil) + (goto-char (point-max)) + (goto-char (point-min))) + ))) + +;;; xwidget webkit session (defvar xwidget-webkit-last-session-buffer nil) @@ -303,7 +433,7 @@ xwidget-webkit-activeelement-js" " - "javascript that finds the active element." + "Javascript that finds the active element." ;; Yes it's ugly, because: ;; - there is apparently no way to find the active frame other than recursion ;; - the js "for each" construct misbehaved on the "frames" collection @@ -313,29 +443,35 @@ xwidget-webkit-activeelement-js" ) (defun xwidget-webkit-insert-string () - "Prompt for a string and insert it in the active field in the + "Prompt for a string and insert it in the active field in the \ current webkit widget." ;; Read out the string in the field first and provide for edit. (interactive) (let ((xww (xwidget-webkit-current-session))) + + ;; @javascript-callback + (defun xwidget-webkit-insert-string-cb (field) + "Prompt a string for the FIELD and insert in the active input." + (let ((str (pcase field + (`(,val "text") + (read-string "Text: " val)) + (`(,val "password") + (read-passwd "Password: " nil val)) + (`(,val "textarea") + (xwidget-webkit-begin-edit-textarea xww val))))) + (xwidget-webkit-execute-script + xww + (format "findactiveelement(document).value='%s'" str)))) + (xwidget-webkit-execute-script xww (concat xwidget-webkit-activeelement-js " (function () { var res = findactiveelement(document); - return [res.value, res.type]; + if (res) + return [res.value, res.type]; })();") - (lambda (field) - (let ((str (pcase field - (`[,val "text"] - (read-string "Text: " val)) - (`[,val "password"] - (read-passwd "Password: " nil val)) - (`[,val "textarea"] - (xwidget-webkit-begin-edit-textarea xww val))))) - (xwidget-webkit-execute-script - xww - (format "findactiveelement(document).value='%s'" str))))))) + 'xwidget-webkit-insert-string-cb))) (defvar xwidget-xwbl) (defun xwidget-webkit-begin-edit-textarea (xw text) @@ -444,11 +580,23 @@ xwidget-webkit-adjust-size-dispatch (ignore-errors (recenter-top-bottom))) +;; Utility functions, wanted in `window.el' + +(defun xwidget-window-inside-pixel-width (window) + "Return Emacs WINDOW body width in pixel." + (let ((edges (window-inside-pixel-edges window))) + (- (nth 2 edges) (nth 0 edges)))) + +(defun xwidget-window-inside-pixel-height (window) + "Return Emacs WINDOW body height in pixel." + (let ((edges (window-inside-pixel-edges window))) + (- (nth 3 edges) (nth 1 edges)))) + (defun xwidget-webkit-adjust-size-to-window (xwidget &optional window) "Adjust the size of the webkit XWIDGET to fit the WINDOW." (xwidget-resize xwidget - (window-pixel-width window) - (window-pixel-height window))) + (xwidget-window-inside-pixel-width window) + (xwidget-window-inside-pixel-height window))) (defun xwidget-webkit-adjust-size (w h) "Manually set webkit size to width W, height H." @@ -487,10 +635,12 @@ xwidget-webkit-new-session (get-buffer-create bufname))) ;; The xwidget id is stored in a text property, so we need to have ;; at least character in this buffer. - (insert " ") + ;; Insert invisible url, good default for next `g' to browse url. + (insert url) + (put-text-property 1 (+ 1 (length url)) 'invisible t) (setq xw (xwidget-insert 1 'webkit bufname - (window-pixel-width) - (window-pixel-height))) + (xwidget-window-inside-pixel-width (selected-window)) + (xwidget-window-inside-pixel-height (selected-window)))) (xwidget-put xw 'callback 'xwidget-webkit-callback) (xwidget-webkit-mode) (xwidget-webkit-goto-uri (xwidget-webkit-last-session) url))) @@ -515,14 +665,18 @@ xwidget-webkit-reload (xwidget-webkit-execute-script (xwidget-webkit-current-session) "history.go(0);")) +;; @javascript-callback +(defun xwidget-webkit-current-url-cb (result) + "Callback for `xwidget-webkit-current-url', message and kill the RESULT." + (let ((url (kill-new (or result "")))) + (message "url: %s" url))) + (defun xwidget-webkit-current-url () - "Get the webkit url and place it on the kill-ring." + "Get the webkit url and place it on the `kill-ring'." (interactive) (xwidget-webkit-execute-script (xwidget-webkit-current-session) - "document.URL" (lambda (rv) - (let ((url (kill-new (or rv "")))) - (message "url: %s" url))))) + "document.URL" 'xwidget-webkit-current-url-cb)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun xwidget-webkit-get-selection (proc) @@ -533,10 +687,9 @@ xwidget-webkit-get-selection proc)) (defun xwidget-webkit-copy-selection-as-kill () - "Get the webkit selection and put it on the kill-ring." + "Get the webkit selection and put it on the `kill-ring'." (interactive) - (xwidget-webkit-get-selection (lambda (selection) (kill-new selection)))) - + (xwidget-webkit-get-selection #'kill-new)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Xwidget plist management (similar to the process plist functions) diff --git a/nextstep/templates/Info.plist.in b/nextstep/templates/Info.plist.in index 5d2eb7def3..54544fb7fa 100644 --- a/nextstep/templates/Info.plist.in +++ b/nextstep/templates/Info.plist.in @@ -675,5 +675,13 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. </array> <key>NSAppleScriptEnabled</key> <string>YES</string> + <!-- For xwidget webkit to browse remote url, + but this set no restriction at all. Consult apple's documentation + for detail information about `NSApplicationDefinedMask'. --> + <key>NSAppTransportSecurity</key> + <dict> + <key>NSAllowsArbitraryLoads</key> + <true/> + </dict> </dict> </plist> diff --git a/src/Makefile.in b/src/Makefile.in index 9a8c9c85f0..5cadb71c95 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -408,6 +408,7 @@ SOME_MACHINE_OBJECTS = xterm.o xfns.o xmenu.o xselect.o xrdb.o xsmfns.o fringe.o image.o \ fontset.o dbusbind.o cygw32.o \ nsterm.o nsfns.o nsmenu.o nsselect.o nsimage.o nsfont.o macfont.o \ + nsxwidget.o \ w32.o w32console.o w32fns.o w32heap.o w32inevt.o w32notify.o \ w32menu.o w32proc.o w32reg.o w32select.o w32term.o w32xfns.o \ w16select.o widget.o xfont.o ftfont.o xftfont.o ftxfont.o gtkutil.o \ diff --git a/src/emacs.c b/src/emacs.c index 808abcd9aa..8e740381e1 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -1532,7 +1532,6 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem syms_of_xfns (); syms_of_xmenu (); syms_of_fontset (); - syms_of_xwidget (); syms_of_xsettings (); #ifdef HAVE_X_SM syms_of_xsmfns (); @@ -1605,6 +1604,10 @@ Using an Emacs configured with --with-x-toolkit=lucid does not have this problem #endif /* HAVE_W32NOTIFY */ #endif /* WINDOWSNT */ +#ifdef HAVE_XWIDGETS + syms_of_xwidget (); +#endif /* HAVE_XWIDGETS */ + syms_of_threads (); syms_of_profiler (); diff --git a/src/nsterm.m b/src/nsterm.m index 50e06c94d4..3d85fdc0d5 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -48,6 +48,7 @@ Updated by Christian Limpach (chris@nice.ch) #include "nsterm.h" #include "systime.h" #include "character.h" +#include "xwidget.h" #include "fontset.h" #include "composite.h" #include "ccl.h" @@ -2429,7 +2430,7 @@ so some key presses (TAB) are swallowed by the system. */ } static int -note_mouse_movement (struct frame *frame, CGFloat x, CGFloat y) +note_mouse_movement (struct frame *frame, CGFloat x, CGFloat y, BOOL dragging) /* ------------------------------------------------------------------------ Called by EmacsView on mouseMovement events. Passes on to emacs mainstream code if we moved off of a rect of interest @@ -2438,17 +2439,24 @@ so some key presses (TAB) are swallowed by the system. */ { struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame); NSRect *r; + BOOL force_update = NO; // NSTRACE ("note_mouse_movement"); dpyinfo->last_mouse_motion_frame = frame; r = &dpyinfo->last_mouse_glyph; + /* If the last rect is too large (ex, xwidget webkit), update at + every move, or resizing by dragging modeline or vertical split is + very hard to make its way. */ + if (dragging && (r->size.width > 32 || r->size.height > 32)) + force_update = YES; + /* Note, this doesn't get called for enter/leave, since we don't have a position. Those are taken care of in the corresponding NSView methods. */ /* has movement gone beyond last rect we were tracking? */ - if (x < r->origin.x || x >= r->origin.x + r->size.width + if (force_update || x < r->origin.x || x >= r->origin.x + r->size.width || y < r->origin.y || y >= r->origin.y + r->size.height) { ns_update_begin (frame); @@ -4066,6 +4074,10 @@ overwriting cursor (usually when cursor on a tab) */ ns_unfocus (s->f); break; + case XWIDGET_GLYPH: + x_draw_xwidget_glyph_string (s); + break; + case STRETCH_GLYPH: ns_dumpglyphs_stretch (s); break; @@ -6724,6 +6736,7 @@ - (void)mouseMoved: (NSEvent *)e struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe); Lisp_Object frame; NSPoint pt; + BOOL dragging; NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "[EmacsView mouseMoved:]"); @@ -6766,7 +6779,8 @@ - (void)mouseMoved: (NSEvent *)e last_mouse_window = window; } - if (!note_mouse_movement (emacsframe, pt.x, pt.y)) + dragging = (e.type == NSEventTypeLeftMouseDragged); + if (!note_mouse_movement (emacsframe, pt.x, pt.y, dragging)) help_echo_string = previous_help_echo_string; XSETFRAME (frame, emacsframe); diff --git a/src/nsxwidget.h b/src/nsxwidget.h new file mode 100644 index 0000000000..a617a0d527 --- /dev/null +++ b/src/nsxwidget.h @@ -0,0 +1,77 @@ +/* Header for NS Cocoa part of xwidget and webkit widget. + +Copyright (C) 2011-2017 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs 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. + +GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef NSXWIDGET_H_INCLUDED +#define NSXWIDGET_H_INCLUDED + +/* This file can be included from non-objc files through 'xwidget.h'. */ +#ifdef __OBJC__ +#import <AppKit/NSView.h> +#endif + +#include "dispextern.h" +#include "lisp.h" +#include "xwidget.h" + +/* Functions for xwidget webkit. */ + +bool nsxwidget_is_web_view (struct xwidget *xw); +void nsxwidget_webkit_goto_uri (struct xwidget *xw, const char *uri); +void nsxwidget_webkit_zoom (struct xwidget *xw, double zoom_change); +void nsxwidget_webkit_execute_script (struct xwidget *xw, const char *script, + Lisp_Object fun); + +/* Functions for xwidget model. */ + +#ifdef __OBJC__ +@interface XwWindow : NSView +@property struct xwidget *xw; +@end +#endif + +void nsxwidget_init (struct xwidget *xw); +void nsxwidget_kill (struct xwidget *xw); +void nsxwidget_resize (struct xwidget *xw); +Lisp_Object nsxwidget_get_size (struct xwidget *xw); + +/* Functions for xwidget view. */ + +#ifdef __OBJC__ +@interface XvWindow : NSView +@property struct xwidget *xw; +@property struct xwidget_view *xv; +@end +#endif + +void nsxwidget_init_view (struct xwidget_view *xv, + struct xwidget *xww, + struct glyph_string *s, + int x, int y); +void nsxwidget_delete_view (struct xwidget_view *xv); + +void nsxwidget_show_view (struct xwidget_view *xv); +void nsxwidget_hide_view (struct xwidget_view *xv); +void nsxwidget_resize_view (struct xwidget_view *xv, + int widget, int height); + +void nsxwidget_move_view (struct xwidget_view *xv, int x, int y); +void nsxwidget_move_widget_in_view (struct xwidget_view *xv, int x, int y); +void nsxwidget_set_needsdisplay (struct xwidget_view *xv); + +#endif /* NSXWIDGET_H_INCLUDED */ diff --git a/src/nsxwidget.m b/src/nsxwidget.m new file mode 100644 index 0000000000..04f1472c96 --- /dev/null +++ b/src/nsxwidget.m @@ -0,0 +1,509 @@ +/* NS Cocoa part implementation of xwidget and webkit widget. + +Copyright (C) 1989, 1992-1994, 2005-2006, 2008-2017 Free Software +Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs 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. + +GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ + +#include <config.h> + +#include "lisp.h" +#include "blockinput.h" +#include "dispextern.h" +#include "buffer.h" +#include "frame.h" +#include "nsterm.h" +#include "xwidget.h" + +/* Defined in 'xwidget.c'. */ +void store_xwidget_event_string (struct xwidget *xw, + const char *eventname, + const char *eventstr); + +void store_xwidget_js_callback_event (struct xwidget *xw, + Lisp_Object proc, + Lisp_Object argument); + +#import <AppKit/AppKit.h> +#import <WebKit/WebKit.h> + +/* Thoughts on NS Cocoa xwidget and webkit2: + + Webkit2 process architecture seems to be very hostile for offscreen + rendering techniques, which is used by GTK xwiget implementation; + Specifically NSView level view sharing / copying is not working. + + *** So only one view can be associcated with a model. *** + + With this decision, implementation is plain and can expect best out + of webkit2's rationale. But process and session structures will + diverge from GTK xwiget. Though, cosmetically similar usages can + be presented and will be preferred, if agreeable. + + For other widget types, OSR seems possible, but will not care for a + while. */ + +/* Xwidget webkit. */ + +@interface XwWebView : WKWebView +<WKNavigationDelegate, WKUIDelegate, WKScriptMessageHandler> +@property struct xwidget *xw; +@end +@implementation XwWebView : WKWebView + +- (id)initWithFrame:(CGRect)frame + configuration:(WKWebViewConfiguration *)configuration + xwidget:(struct xwidget *)xw +{ + /* Script controller to add script message handler and user script. */ + WKUserContentController *scriptor = [[WKUserContentController alloc] init]; + configuration.userContentController = scriptor; + + self = [super initWithFrame:frame configuration:configuration]; + if (self) + { + self.xw = xw; + self.navigationDelegate = self; + self.UIDelegate = self; + self.customUserAgent = + @"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 addUserScript:[[WKUserScript alloc] + initWithSource:xwScript + injectionTime: + WKUserScriptInjectionTimeAtDocumentEnd + forMainFrameOnly:NO]]; + } + return self; +} + +#if 0 +/* Non ARC - just to check lifecycle. */ +- (void)dealloc +{ + NSLog (@"XwWebView dealloc"); + [super dealloc]; +} +#endif + +- (void)webView:(WKWebView *)webView +didFinishNavigation:(WKNavigation *)navigation +{ + store_xwidget_event_string (self.xw, "load-changed", ""); +} + +- (void)webView:(WKWebView *)webView +decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction +decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler +{ + switch (navigationAction.navigationType) { + case WKNavigationTypeLinkActivated: + decisionHandler (WKNavigationActionPolicyAllow); + break; + default: + // decisionHandler (WKNavigationActionPolicyCancel); + decisionHandler (WKNavigationActionPolicyAllow); + break; + } +} + +- (void)webView:(WKWebView *)webView +decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse +decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler +{ + if (!navigationResponse.canShowMIMEType) + { + /* TODO: download using NSURLxxx? */ + } + decisionHandler (WKNavigationResponsePolicyAllow); +} + +/* No additional new webview or emacs window will be created + for <a ... target="_blank">. */ +- (WKWebView *)webView:(WKWebView *)webView +createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration + forNavigationAction:(WKNavigationAction *)navigationAction + windowFeatures:(WKWindowFeatures *)windowFeatures +{ + if (!navigationAction.targetFrame.isMainFrame) + [webView loadRequest:navigationAction.request]; + return nil; +} + +/* By forwarding mouse events to emacs view (frame) + - Mouse click in webview selects the window contains the webview. + - Correct mouse hand/arrow/I-beam is displayed (TODO: not perfect yet). +*/ + +- (void)mouseDown:(NSEvent *)event +{ + [self.xw->xv->emacswindow mouseDown:event]; + [super mouseDown:event]; +} + +- (void)mouseUp:(NSEvent *)event +{ + [self.xw->xv->emacswindow mouseUp:event]; + [super mouseUp:event]; +} + +/* Basically we want keyboard events handled by emacs unless an input + element has focus. Especially, while incremental search, we set + emacs as first responder to avoid focus held in an input element + with matching text. */ + +- (void)keyDown:(NSEvent *)event +{ + Lisp_Object var = Fintern (build_string ("isearch-mode"), Qnil); + Lisp_Object val = buffer_local_value (var, Fcurrent_buffer ()); + if (!EQ (val, Qunbound) && !EQ (val, Qnil)) + { + [self.window makeFirstResponder:self.xw->xv->emacswindow]; + [self.xw->xv->emacswindow keyDown:event]; + return; + } + + [self evaluateJavaScript:@"xwHasFocus()" + completionHandler:^(id result, NSError *error) { + if (error) + NSLog (@"xwHasFocus: %@", error.localizedDescription); + else if (result) + { + NSNumber *hasFocus = result; /* __NSCFBoolean */ + if (!hasFocus.boolValue) + [self.xw->xv->emacswindow keyDown:event]; + else + [super keyDown:event]; + } + }]; +} + +- (void)interpretKeyEvents:(NSArray<NSEvent *> *)eventArray +{ + /* We should do nothing and do not forward (default implementation + if we not override here) to let emacs collect key events and ask + interpretKeyEvents to its superclass. */ +} + +static NSString *xwScript; ++ (void)initialize +{ + /* Find out if an input element has focus. + Message to script message handler when 'C-g' key down. */ + if (!xwScript) + xwScript = + @"function xwHasFocus() {" + @" var ae = document.activeElement;" + @" if (ae) {" + @" var name = ae.nodeName;" + @" return name == 'INPUT' || name == 'TEXTAREA';" + @" } else {" + @" return false;" + @" }" + @"}" + @"function xwKeyDown(event) {" + @" if (event.ctrlKey && event.key == 'g') {" + @" window.webkit.messageHandlers.keyDown.postMessage('C-g');" + @" }" + @"}" + @"document.addEventListener('keydown', xwKeyDown);" + ; +} + +/* Confirming to WKScriptMessageHandler, listens concerning keyDown in + webkit. Currently 'C-g'. */ +- (void)userContentController:(WKUserContentController *)userContentController + didReceiveScriptMessage:(WKScriptMessage *)message +{ + 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]; + } +} + +@end + +/* Xwidget webkit commands. */ + +bool +nsxwidget_is_web_view (struct xwidget *xw) +{ + return xw->xwWidget != NULL && + [xw->xwWidget isKindOfClass:WKWebView.class]; +} + +/* @Note ATS - Need application transport security in 'Info.plist' or + remote pages will not loaded. */ +void +nsxwidget_webkit_goto_uri (struct xwidget *xw, const char *uri) +{ + XwWebView *xwWebView = (XwWebView *) xw->xwWidget; + NSString *urlString = [NSString stringWithUTF8String:uri]; + NSURL *url = [NSURL URLWithString:urlString]; + NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url]; + [xwWebView loadRequest:urlRequest]; +} + +void +nsxwidget_webkit_zoom (struct xwidget *xw, double zoom_change) +{ + XwWebView *xwWebView = (XwWebView *) xw->xwWidget; + xwWebView.magnification += zoom_change; + /* TODO: setMagnification:centeredAtPoint. */ +} + +/* Build lisp string */ +static Lisp_Object +build_string_with_nsstr (NSString *nsstr) +{ + const char *utfstr = [nsstr UTF8String]; + NSUInteger bytes = [nsstr lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + return make_string (utfstr, bytes); +} + +/* Recursively convert an objc native type JavaScript value to a Lisp + value. Mostly copied from GTK xwidget 'webkit_js_to_lisp'. */ +static Lisp_Object +js_to_lisp (id value) +{ + if (value == nil || [value isKindOfClass:NSNull.class]) + return Qnil; + else if ([value isKindOfClass:NSString.class]) + return build_string_with_nsstr ((NSString *) value); + else if ([value isKindOfClass:NSNumber.class]) + { + NSNumber *nsnum = (NSNumber *) value; + char type = nsnum.objCType[0]; + if (type == 'c') /* __NSCFBoolean has type character 'c'. */ + return nsnum.boolValue? Qt : Qnil; + else + { + if (type == 'i' || type == 'l') + return make_number (nsnum.longValue); + else if (type == 'f' || type == 'd') + return make_float (nsnum.doubleValue); + /* else fall through. */ + } + } + else if ([value isKindOfClass:NSArray.class]) + { + NSArray *nsarr = (NSArray *) value; + EMACS_INT n = nsarr.count; + Lisp_Object obj; + struct Lisp_Vector *p = allocate_vector (n); + + for (ptrdiff_t i = 0; i < n; ++i) + p->contents[i] = js_to_lisp ([nsarr objectAtIndex:i]); + XSETVECTOR (obj, p); + return obj; + } + else if ([value isKindOfClass:NSDictionary.class]) + { + NSDictionary *nsdict = (NSDictionary *) value; + NSArray *keys = nsdict.allKeys; + ptrdiff_t n = keys.count; + Lisp_Object obj; + struct Lisp_Vector *p = allocate_vector (n); + + for (ptrdiff_t i = 0; i < n; ++i) + { + NSString *prop_key = (NSString *) [keys objectAtIndex:i]; + id prop_value = [nsdict valueForKey:prop_key]; + p->contents[i] = Fcons (build_string_with_nsstr (prop_key), + js_to_lisp (prop_value)); + } + XSETVECTOR (obj, p); + return obj; + } + NSLog (@"Unhandled type in javascript result"); + return Qnil; +} + +void +nsxwidget_webkit_execute_script (struct xwidget *xw, const char *script, + Lisp_Object fun) +{ + NSString *javascriptString = [NSString stringWithUTF8String:script]; + XwWebView *xwWebView = (XwWebView *) xw->xwWidget; + + [xwWebView evaluateJavaScript:javascriptString + completionHandler:^(id result, NSError *error) { + if (error) + { + NSLog (@"evaluateJavaScript error : %@", error.localizedDescription); + NSLog (@"error script=%@", javascriptString); + } + else if (result && FUNCTIONP (fun)) + { + // NSLog (@"result=%@, type=%@", result, [result class]); + Lisp_Object lisp_value = js_to_lisp (result); + store_xwidget_js_callback_event (xw, fun, lisp_value); + } + }]; +} + +/* Window containing an xwidget. */ + +@implementation XwWindow +- (BOOL)isFlipped { return YES; } +@end + +/* Xwidget model, macOS Cocoa part. */ + +void +nsxwidget_init(struct xwidget *xw) +{ + block_input (); + NSRect rect = NSMakeRect (0, 0, xw->width, xw->height); + xw->xwWidget = [[XwWebView alloc] + initWithFrame:rect + configuration:[[WKWebViewConfiguration alloc] init] + xwidget:xw]; + xw->xwWindow = [[XwWindow alloc] + initWithFrame:rect]; + [xw->xwWindow addSubview:xw->xwWidget]; + xw->xv = NULL; /* for 1 to 1 relationship of webkit2. */ + unblock_input (); +} + +void +nsxwidget_kill (struct xwidget *xw) +{ + if (xw) + { + WKUserContentController *scriptor = + ((XwWebView *) xw->xwWidget).configuration.userContentController; + [scriptor removeAllUserScripts]; + [scriptor removeScriptMessageHandlerForName:@"keyDown"]; + [scriptor release]; + if (xw->xv) + xw->xv->model = Qnil; /* Make sure related view stale. */ + [xw->xwWidget removeFromSuperviewWithoutNeedingDisplay]; + [xw->xwWidget release]; + [xw->xwWindow removeFromSuperviewWithoutNeedingDisplay]; + [xw->xwWindow release]; + xw->xwWidget = nil; + } +} + +void +nsxwidget_resize (struct xwidget *xw) +{ + if (xw->xwWidget) + { + [xw->xwWindow setFrameSize:NSMakeSize(xw->width, xw->height)]; + [xw->xwWidget setFrameSize:NSMakeSize(xw->width, xw->height)]; + } +} + +Lisp_Object +nsxwidget_get_size (struct xwidget *xw) +{ + return list2 (make_number (xw->xwWidget.frame.size.width), + make_number (xw->xwWidget.frame.size.height)); +} + +/* Xwidget view, macOS Cocoa part. */ + +@implementation XvWindow : NSView +- (BOOL)isFlipped { return YES; } +@end + +void +nsxwidget_init_view (struct xwidget_view *xv, + struct xwidget *xw, + struct glyph_string *s, + int x, int y) +{ + /* 'x_draw_xwidget_glyph_string' will calculate correct position and + size of clip to draw in emacs buffer window. Thus, just begin at + origin with no crop. */ + xv->x = x; + xv->y = y; + xv->clip_left = 0; + xv->clip_right = xw->width; + xv->clip_top = 0; + xv->clip_bottom = xw->height; + + xv->xvWindow = [[XvWindow alloc] + initWithFrame:NSMakeRect (x, y, xw->width, xw->height)]; + xv->xvWindow.xw = xw; + xv->xvWindow.xv = xv; + + xw->xv = xv; /* For 1 to 1 relationship of webkit2. */ + [xv->xvWindow addSubview:xw->xwWindow]; + + xv->emacswindow = FRAME_NS_VIEW (s->f); + [xv->emacswindow addSubview:xv->xvWindow]; +} + +void +nsxwidget_delete_view (struct xwidget_view *xv) +{ + if (!EQ (xv->model, Qnil)) + { + struct xwidget *xw = XXWIDGET (xv->model); + [xw->xwWindow removeFromSuperviewWithoutNeedingDisplay]; + xw->xv = NULL; /* Now model has no view. */ + } + [xv->xvWindow removeFromSuperviewWithoutNeedingDisplay]; + [xv->xvWindow release]; +} + +void +nsxwidget_show_view (struct xwidget_view *xv) +{ + xv->hidden = NO; + [xv->xvWindow setFrameOrigin:NSMakePoint(xv->x + xv->clip_left, + xv->y + xv->clip_top)]; +} + +void +nsxwidget_hide_view (struct xwidget_view *xv) +{ + xv->hidden = YES; + [xv->xvWindow setFrameOrigin:NSMakePoint(10000, 10000)]; +} + +void +nsxwidget_resize_view (struct xwidget_view *xv, int width, int height) +{ + [xv->xvWindow setFrameSize:NSMakeSize(width, height)]; +} + +void +nsxwidget_move_view (struct xwidget_view *xv, int x, int y) +{ + [xv->xvWindow setFrameOrigin:NSMakePoint (x, y)]; +} + +/* Move model window in container (view window). */ +void +nsxwidget_move_widget_in_view (struct xwidget_view *xv, int x, int y) +{ + struct xwidget *xww = xv->xvWindow.xw; + [xww->xwWindow setFrameOrigin:NSMakePoint (x, y)]; +} + +void +nsxwidget_set_needsdisplay (struct xwidget_view *xv) +{ + xv->xvWindow.needsDisplay = YES; +} diff --git a/src/xwidget.c b/src/xwidget.c index a67dc0ecf4..17e1e6428a 100644 --- a/src/xwidget.c +++ b/src/xwidget.c @@ -18,17 +18,26 @@ You should have received a copy of the GNU General Public License along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ #include <config.h> +#include <stdio.h> /* FIXME: Emacs error? message? instead of printf. */ #include "xwidget.h" #include "lisp.h" #include "blockinput.h" +#include "dispextern.h" #include "frame.h" #include "keyboard.h" #include "gtkutil.h" +#include "termhooks.h" +#include "window.h" +/* Include xwidget bottom end headers. */ +#if defined (USE_GTK) #include <webkit2/webkit2.h> #include <JavaScriptCore/JavaScript.h> +#elif defined (NS_IMPL_COCOA) +#include "nsxwidget.h" +#endif static struct xwidget * allocate_xwidget (void) @@ -48,6 +57,7 @@ allocate_xwidget_view (void) static struct xwidget_view *xwidget_view_lookup (struct xwidget *, struct window *); +#if defined (USE_GTK) static void webkit_view_load_changed_cb (WebKitWebView *, WebKitLoadEvent, gpointer); @@ -61,6 +71,7 @@ webkit_decide_policy_cb (WebKitWebView *, WebKitPolicyDecision *, WebKitPolicyDecisionType, gpointer); +#endif DEFUN ("make-xwidget", @@ -92,10 +103,11 @@ Returns the newly constructed xwidget, or nil if construction fails. */) xw->kill_without_query = false; XSETXWIDGET (val, xw); Vxwidget_list = Fcons (val, Vxwidget_list); - xw->widgetwindow_osr = NULL; - xw->widget_osr = NULL; xw->plist = Qnil; +#if defined (USE_GTK) + xw->widgetwindow_osr = NULL; + xw->widget_osr = NULL; if (EQ (xw->type, Qwebkit)) { block_input (); @@ -150,6 +162,9 @@ Returns the newly constructed xwidget, or nil if construction fails. */) unblock_input (); } +#elif defined (NS_IMPL_COCOA) + nsxwidget_init (xw); +#endif return val; } @@ -185,6 +200,7 @@ xwidget_hidden (struct xwidget_view *xv) return xv->hidden; } +#if defined (USE_GTK) static void xwidget_show_view (struct xwidget_view *xv) { @@ -223,8 +239,9 @@ offscreen_damage_event (GtkWidget *widget, GdkEvent *event, return FALSE; } +#endif /* USE_GTK */ -static void +void store_xwidget_event_string (struct xwidget *xw, const char *eventname, const char *eventstr) { @@ -238,7 +255,7 @@ store_xwidget_event_string (struct xwidget *xw, const char *eventname, kbd_buffer_store_event (&event); } -static void +void store_xwidget_js_callback_event (struct xwidget *xw, Lisp_Object proc, Lisp_Object argument) @@ -254,6 +271,7 @@ store_xwidget_js_callback_event (struct xwidget *xw, } +#if defined (USE_GTK) void webkit_view_load_changed_cb (WebKitWebView *webkitwebview, WebKitLoadEvent load_event, @@ -389,9 +407,8 @@ webkit_javascript_finished_cb (GObject *webview, /* Register an xwidget event here, which then runs the callback. This ensures that the callback runs in sync with the Emacs event loop. */ - /* FIXME: This might lead to disaster if LISP_CALLBACK’s object - was garbage collected before now. See the FIXME in - Fxwidget_webkit_execute_script. */ + /* LISP_CALLBACK must not be garbage collected up to here. See + comments in Fxwidget_webkit_execute_script. */ store_xwidget_js_callback_event (xw, XIL ((intptr_t) lisp_callback), lisp_value); } @@ -500,6 +517,7 @@ xwidget_osr_event_set_embedder (GtkWidget *widget, GdkEvent *event, gtk_widget_get_window (xv->widget)); return FALSE; } +#endif /* USE_GTK */ /* Initializes and does initial placement of an xwidget view on screen. */ @@ -517,6 +535,7 @@ xwidget_init_view (struct xwidget *xww, XSETWINDOW (xv->w, s->w); XSETXWIDGET (xv->model, xww); +#if defined (USE_GTK) if (EQ (xww->type, Qwebkit)) { xv->widget = gtk_drawing_area_new (); @@ -574,6 +593,9 @@ xwidget_init_view (struct xwidget *xww, xv->x = x; xv->y = y; gtk_widget_show_all (xv->widgetwindow); +#elif defined (NS_IMPL_COCOA) + nsxwidget_init_view (xv, xww, s, x, y); +#endif return xv; } @@ -586,24 +608,59 @@ x_draw_xwidget_glyph_string (struct glyph_string *s) initialization. */ struct xwidget *xww = s->xwidget; struct xwidget_view *xv = xwidget_view_lookup (xww, s->w); + int text_area_x, text_area_y, text_area_width, text_area_height; int clip_right; int clip_bottom; int clip_top; int clip_left; int x = s->x; - int y = s->y + (s->height / 2) - (xww->height / 2); + int y = s->y; /* Do initialization here in the display loop because there is no other time to know things like window placement etc. Do not create a new view if we have found one that is usable. */ +#if defined (USE_GTK) if (!xv) xv = xwidget_init_view (xww, s, x, y); - - int text_area_x, text_area_y, text_area_width, text_area_height; +#elif defined (NS_IMPL_COCOA) + if (!xv) + { + /* Enforce 1 to 1, model and view for macOS Cocoa webkit2. */ + if (xww->xv) + { + if (xwidget_hidden (xww->xv)) + { + Lisp_Object xvl; + XSETXWIDGET_VIEW (xvl, xww->xv); + Fdelete_xwidget_view (xvl); + } + else + { + message ("You can't share an xwidget (webkit2) among windows."); + return; + } + } + xv = xwidget_init_view (xww, s, x, y); + } +#endif window_box (s->w, TEXT_AREA, &text_area_x, &text_area_y, &text_area_width, &text_area_height); + + /* Resize xwidget webkit if its container window size is changed in + some ways, for example, a buffer became hidden in small split + window, then it can appear front in merged whole window. */ + if (EQ (xww->type, Qwebkit) + && (xww->width != text_area_width || xww->height != text_area_height)) + { + Lisp_Object xwl; + XSETXWIDGET (xwl, xww); + Fxwidget_resize (xwl, + make_number (text_area_width), + make_number (text_area_height)); + } + clip_left = max (0, text_area_x - x); clip_right = max (clip_left, min (xww->width, text_area_x + text_area_width - x)); @@ -626,8 +683,14 @@ x_draw_xwidget_glyph_string (struct glyph_string *s) /* Has it moved? */ if (moved) - gtk_fixed_move (GTK_FIXED (FRAME_GTK_WIDGET (s->f)), - xv->widgetwindow, x + clip_left, y + clip_top); + { +#if defined (USE_GTK) + gtk_fixed_move (GTK_FIXED (FRAME_GTK_WIDGET (s->f)), + xv->widgetwindow, x + clip_left, y + clip_top); +#elif defined (NS_IMPL_COCOA) + nsxwidget_move_view (xv, x + clip_left, y + clip_top); +#endif + } /* Clip the widget window if some parts happen to be outside drawable area. An Emacs window is not a gtk window. A gtk window @@ -638,10 +701,16 @@ x_draw_xwidget_glyph_string (struct glyph_string *s) || xv->clip_bottom != clip_bottom || xv->clip_top != clip_top || xv->clip_left != clip_left) { +#if defined (USE_GTK) gtk_widget_set_size_request (xv->widgetwindow, clip_right - clip_left, clip_bottom - clip_top); gtk_fixed_move (GTK_FIXED (xv->widgetwindow), xv->widget, -clip_left, -clip_top); +#elif defined (NS_IMPL_COCOA) + nsxwidget_resize_view (xv, clip_right - clip_left, + clip_bottom - clip_top); + nsxwidget_move_widget_in_view (xv, -clip_left, -clip_top); +#endif xv->clip_right = clip_right; xv->clip_bottom = clip_bottom; @@ -655,18 +724,32 @@ x_draw_xwidget_glyph_string (struct glyph_string *s) xwidgets background. It's just a visual glitch though. */ if (!xwidget_hidden (xv)) { +#if defined (USE_GTK) gtk_widget_queue_draw (xv->widgetwindow); gtk_widget_queue_draw (xv->widget); +#elif defined (NS_IMPL_COCOA) + nsxwidget_set_needsdisplay (xv); +#endif } } -/* Macro that checks WEBKIT_IS_WEB_VIEW (xw->widget_osr) first. */ +static bool +xwidget_is_web_view (struct xwidget *xw) +{ +#if defined (USE_GTK) + return xw->widget_osr != NULL && WEBKIT_IS_WEB_VIEW (xw->widget_osr); +#elif defined (NS_IMPL_COCOA) + return nsxwidget_is_web_view (xw); +#endif +} + +/* Macro that checks xwidget hold webkit web view first. */ #define WEBKIT_FN_INIT() \ CHECK_XWIDGET (xwidget); \ struct xwidget *xw = XXWIDGET (xwidget); \ - if (!xw->widget_osr || !WEBKIT_IS_WEB_VIEW (xw->widget_osr)) \ + if (!xwidget_is_web_view (xw)) \ { \ - printf ("ERROR xw->widget_osr does not hold a webkit instance\n"); \ + printf ("ERROR xwidget does not hold a webkit instance\n"); \ return Qnil; \ } @@ -678,7 +761,11 @@ DEFUN ("xwidget-webkit-goto-uri", { WEBKIT_FN_INIT (); CHECK_STRING (uri); +#if defined (USE_GTK) webkit_web_view_load_uri (WEBKIT_WEB_VIEW (xw->widget_osr), SSDATA (uri)); +#elif defined (NS_IMPL_COCOA) + nsxwidget_webkit_goto_uri (xw, SSDATA (uri)); +#endif return Qnil; } @@ -693,10 +780,14 @@ referenced by XWIDGET. */) if (FLOATP (factor)) { double zoom_change = XFLOAT_DATA (factor); +#if defined (USE_GTK) webkit_web_view_set_zoom_level (WEBKIT_WEB_VIEW (xw->widget_osr), webkit_web_view_get_zoom_level (WEBKIT_WEB_VIEW (xw->widget_osr)) + zoom_change); +#elif defined (NS_IMPL_COCOA) + nsxwidget_webkit_zoom (xw, zoom_change); +#endif } return Qnil; } @@ -712,17 +803,18 @@ argument procedure FUN.*/) { WEBKIT_FN_INIT (); CHECK_STRING (script); - if (!NILP (fun) && !FUNCTIONP (fun)) + /* FUN will not be garbage collected if it is defined with `defun' + instead of `lambda'. If it is garbage collected even though it + is `defun', we can counter by pinning the FUN's symbol. */ + if (!NILP (fun) && !SYMBOLP (fun) && !NILP (Ffboundp (fun))) wrong_type_argument (Qinvalid_function, fun); +#if defined (USE_GTK) GAsyncReadyCallback callback = FUNCTIONP (fun) ? webkit_javascript_finished_cb : NULL; /* FIXME: The following hack assumes USE_LSB_TAG. */ verify (USE_LSB_TAG); - /* FIXME: This hack might lead to disaster if FUN is garbage - collected before store_xwidget_js_callback_event makes it visible - to Lisp again. See the FIXME in webkit_javascript_finished_cb. */ gpointer callback_arg = (gpointer) (intptr_t) XLI (fun); /* JavaScript execution happens asynchronously. If an elisp @@ -732,6 +824,9 @@ argument procedure FUN.*/) SSDATA (script), NULL, /* cancelable */ callback, callback_arg); +#elif defined (NS_IMPL_COCOA) + nsxwidget_webkit_execute_script (xw, SSDATA (script), fun); +#endif return Qnil; } @@ -750,6 +845,7 @@ DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0, xw->height = h; /* If there is an offscreen widget resize it first. */ +#if defined (USE_GTK) if (xw->widget_osr) { gtk_window_resize (GTK_WINDOW (xw->widgetwindow_osr), xw->width, @@ -758,6 +854,9 @@ DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0, gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr), xw->width, xw->height); } +#elif defined (NS_IMPL_COCOA) + nsxwidget_resize (xw); +#endif for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail); tail = XCDR (tail)) { @@ -765,8 +864,14 @@ DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0, { struct xwidget_view *xv = XXWIDGET_VIEW (XCAR (tail)); if (XXWIDGET (xv->model) == xw) + { +#if defined (USE_GTK) gtk_widget_set_size_request (GTK_WIDGET (xv->widget), xw->width, xw->height); +#elif defined (NS_IMPL_COCOA) + nsxwidget_resize_view(xv, xw->width, xw->height); +#endif + } } } @@ -785,10 +890,14 @@ Emacs allocated area accordingly. */) (Lisp_Object xwidget) { CHECK_XWIDGET (xwidget); +#if defined (USE_GTK) GtkRequisition requisition; gtk_widget_size_request (XXWIDGET (xwidget)->widget_osr, &requisition); return list2 (make_number (requisition.width), make_number (requisition.height)); +#elif defined (NS_IMPL_COCOA) + return nsxwidget_get_size(XXWIDGET (xwidget)); +#endif } DEFUN ("xwidgetp", @@ -865,14 +974,19 @@ DEFUN ("delete-xwidget-view", { CHECK_XWIDGET_VIEW (xwidget_view); struct xwidget_view *xv = XXWIDGET_VIEW (xwidget_view); - gtk_widget_destroy (xv->widgetwindow); Vxwidget_view_list = Fdelq (xwidget_view, Vxwidget_view_list); +#if defined (USE_GTK) + gtk_widget_destroy (xv->widgetwindow); /* xv->model still has signals pointing to the view. There can be several views. Find the matching signals and delete them all. */ g_signal_handlers_disconnect_matched (XXWIDGET (xv->model)->widgetwindow_osr, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, xv->widget); +#elif defined (NS_IMPL_COCOA) + nsxwidget_delete_view (xv); +#endif + return Qnil; } @@ -1151,11 +1265,19 @@ xwidget_end_redisplay (struct window *w, struct glyph_matrix *matrix) xwidget_end_redisplay (w->current_matrix); */ struct xwidget_view *xv = xwidget_view_lookup (glyph->u.xwidget, w); +#if defined (USE_GTK) /* FIXME: Is it safe to assume xwidget_view_lookup always succeeds here? If so, this comment can be removed. If not, the code probably needs fixing. */ eassume (xv); xwidget_touch (xv); +#elif defined (NS_IMPL_COCOA) + /* In NS xwidget, xv can be NULL for the second or + later views for a model, the result of 1 to 1 + model view relation enforcement. */ + if (xv) + xwidget_touch (xv); +#endif } } } @@ -1172,9 +1294,21 @@ xwidget_end_redisplay (struct window *w, struct glyph_matrix *matrix) if (XWINDOW (xv->w) == w) { if (xwidget_touched (xv)) - xwidget_show_view (xv); + { +#if defined (USE_GTK) + xwidget_show_view (xv); +#elif defined (NS_IMPL_COCOA) + nsxwidget_show_view (xv); +#endif + } else - xwidget_hide_view (xv); + { +#if defined (USE_GTK) + xwidget_hide_view (xv); +#elif defined (NS_IMPL_COCOA) + nsxwidget_hide_view (xv); +#endif + } } } } @@ -1193,11 +1327,15 @@ kill_buffer_xwidgets (Lisp_Object buffer) { CHECK_XWIDGET (xwidget); struct xwidget *xw = XXWIDGET (xwidget); +#if defined (USE_GTK) if (xw->widget_osr && xw->widgetwindow_osr) { gtk_widget_destroy (xw->widget_osr); gtk_widget_destroy (xw->widgetwindow_osr); } +#elif defined (NS_IMPL_COCOA) + nsxwidget_kill (xw); +#endif } } } diff --git a/src/xwidget.h b/src/xwidget.h index 02a0453dab..eee9654a6f 100644 --- a/src/xwidget.h +++ b/src/xwidget.h @@ -29,7 +29,13 @@ struct xwidget_view; struct window; #ifdef HAVE_XWIDGETS -# include <gtk/gtk.h> + +#if defined (USE_GTK) +#include <gtk/gtk.h> +#elif defined (NS_IMPL_COCOA) && defined (__OBJC__) +#import <AppKit/NSView.h> +#import "nsxwidget.h" +#endif struct xwidget { @@ -52,9 +58,25 @@ struct xwidget int height; int width; +#if defined (USE_GTK) /* For offscreen widgets, unused if not osr. */ GtkWidget *widget_osr; GtkWidget *widgetwindow_osr; +#elif defined (NS_IMPL_COCOA) +# ifdef __OBJC__ + /* For offscreen widgets, unused if not osr. */ + NSView *xwWidget; + XwWindow *xwWindow; + + /* Used only for xwidget types (such as webkit2) enforcing 1 to 1 + relationship between model and view. */ + struct xwidget_view *xv; +# else + void *xwWidget; + void *xwWindow; + struct xwidget_view *xv; +# endif +#endif /* Kill silently if Emacs is exited. */ bool_bf kill_without_query : 1; @@ -74,9 +96,20 @@ struct xwidget_view /* The "live" instance isn't drawn. */ bool hidden; +#if defined (USE_GTK) GtkWidget *widget; GtkWidget *widgetwindow; GtkWidget *emacswindow; +#elif defined (NS_IMPL_COCOA) +# ifdef __OBJC__ + XvWindow *xvWindow; + NSView *emacswindow; +# else + void *xvWindow; + void *emacswindow; +# endif +#endif + int x; int y; int clip_right; -- 2.15.0 ^ permalink raw reply related [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2017-12-05 7:55 ` Jaesup Kwak @ 2017-12-05 20:00 ` Alan Third 2017-12-06 5:59 ` Jaesup Kwak 0 siblings, 1 reply; 41+ messages in thread From: Alan Third @ 2017-12-05 20:00 UTC (permalink / raw) To: Jaesup Kwak; +Cc: 29565 On Tue, Dec 05, 2017 at 04:55:56PM +0900, Jaesup Kwak wrote: > On Tue, Dec 5, 2017 at 3:01 PM, Jaesup Kwak <veshboo@gmail.com> wrote: > > > > > I will soon post a patch accompanying above changes. > > > > > I made and attached a new patch, which includes changes for comments > from Alan Third and previous patch. So, please ignore the patch file > from previous post and use this patch file. > > Thanks. Looks good to me. One small thing I noticed is AC_MSG_ERROR([xwidgets requested, it requires GTK3 as X window toolkit or macOS Cocoa as window systeml]) ^ I’ve also had the experience a couple of times where I’ve tried to visit a website (for example http://www.bbc.co.uk/news) but all I get is a blank xwidget buffer. Then if I try to do things in it I get this message: 2017-12-05 19:34:38.116 Emacs[56317:26076674] xwHasFocus: A JavaScript exception occurred I don’t know what was going wrong. -- Alan Third ^ permalink raw reply [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2017-12-05 20:00 ` Alan Third @ 2017-12-06 5:59 ` Jaesup Kwak 2017-12-06 6:20 ` Jaesup Kwak 0 siblings, 1 reply; 41+ messages in thread From: Jaesup Kwak @ 2017-12-06 5:59 UTC (permalink / raw) To: 29565; +Cc: Alan Third [-- Attachment #1.1: Type: text/plain, Size: 787 bytes --] On Wed, Dec 6, 2017 at 5:00 AM, Alan Third <alan@idiocy.org> wrote: > > One small thing I noticed is > > AC_MSG_ERROR([xwidgets requested, it requires GTK3 as X window > toolkit or macOS Cocoa as window systeml]) > ^ > Sharp eyes! Fixed the typo. > I’ve also had the experience a couple of times where I’ve tried to > visit a website (for example http://www.bbc.co.uk/news) but all I get > is a blank xwidget buffer. Then if I try to do things in it I get this > message: > > 2017-12-05 19:34:38.116 Emacs[56317:26076674] xwHasFocus: A JavaScript > exception occurred > > I don’t know what was going wrong. > I failed to re-create the problem stated. I will investigate it further. Jaesup [-- Attachment #1.2: Type: text/html, Size: 1486 bytes --] [-- Attachment #2: 0001-Fix-typo-Bug-29565.patch --] [-- Type: application/octet-stream, Size: 778 bytes --] From 212ff86e1f5ddc0b03c988f22a25366be3c11620 Mon Sep 17 00:00:00 2001 From: Jaesup Kwak <veshboo@gmail.com> Date: Wed, 6 Dec 2017 14:04:23 +0900 Subject: [PATCH] Fix typo (Bug#29565) * configure.ac --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 3d521fcfb1..4528073e7a 100644 --- a/configure.ac +++ b/configure.ac @@ -2737,7 +2737,7 @@ AC_DEFUN dnl Update NS_OBJC_OBJ with added nsxwidget.o AC_SUBST(NS_OBJC_OBJ) else - AC_MSG_ERROR([xwidgets requested, it requires GTK3 as X window toolkit or macOS Cocoa as window systeml]) + AC_MSG_ERROR([xwidgets requested, it requires GTK3 as X window toolkit or macOS Cocoa as window system]) fi test $HAVE_XWIDGETS = yes || -- 2.15.0 ^ permalink raw reply related [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2017-12-06 5:59 ` Jaesup Kwak @ 2017-12-06 6:20 ` Jaesup Kwak 0 siblings, 0 replies; 41+ messages in thread From: Jaesup Kwak @ 2017-12-06 6:20 UTC (permalink / raw) To: 29565; +Cc: Alan Third [-- Attachment #1.1: Type: text/plain, Size: 101 bytes --] I have missed a period (".") in the previous typo fix patch. Please ignore it and use this. Thanks. [-- Attachment #1.2: Type: text/html, Size: 177 bytes --] [-- Attachment #2: 0001-Fix-typo-Bug-29565.patch --] [-- Type: application/octet-stream, Size: 779 bytes --] From 4f5eb53cc78624f79d8335e6e657decc4d8f74e6 Mon Sep 17 00:00:00 2001 From: Jaesup Kwak <veshboo@gmail.com> Date: Wed, 6 Dec 2017 14:04:23 +0900 Subject: [PATCH] Fix typo (Bug#29565) * configure.ac --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 3d521fcfb1..b1ce90ce2e 100644 --- a/configure.ac +++ b/configure.ac @@ -2737,7 +2737,7 @@ AC_DEFUN dnl Update NS_OBJC_OBJ with added nsxwidget.o AC_SUBST(NS_OBJC_OBJ) else - AC_MSG_ERROR([xwidgets requested, it requires GTK3 as X window toolkit or macOS Cocoa as window systeml]) + AC_MSG_ERROR([xwidgets requested, it requires GTK3 as X window toolkit or macOS Cocoa as window system.]) fi test $HAVE_XWIDGETS = yes || -- 2.15.0 ^ permalink raw reply related [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2017-12-04 16:44 bug#29565: [PATCH] Support xwidget webkit for macOS X Jaesup Kwak 2017-12-04 20:59 ` Alan Third @ 2017-12-13 11:15 ` Jaesup Kwak 2017-12-13 11:27 ` Jaesup Kwak ` (9 subsequent siblings) 11 siblings, 0 replies; 41+ messages in thread From: Jaesup Kwak @ 2017-12-13 11:15 UTC (permalink / raw) To: 29565 [-- Attachment #1: Type: text/plain, Size: 187 bytes --] These are patch files for further development from the last post. * 0001-Stop-audio-when-xwidget-webkit-killed-Bug-29565.patch 0002-Functions-when-javascript-not-allowed-Bug-29565.patch [-- Attachment #2: Type: text/html, Size: 259 bytes --] ^ permalink raw reply [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2017-12-04 16:44 bug#29565: [PATCH] Support xwidget webkit for macOS X Jaesup Kwak 2017-12-04 20:59 ` Alan Third 2017-12-13 11:15 ` Jaesup Kwak @ 2017-12-13 11:27 ` Jaesup Kwak 2017-12-13 16:13 ` Jaesup Kwak ` (8 subsequent siblings) 11 siblings, 0 replies; 41+ messages in thread From: Jaesup Kwak @ 2017-12-13 11:27 UTC (permalink / raw) To: 29565 [-- Attachment #1.1: Type: text/plain, Size: 519 bytes --] These are path files for further development from the last post. * 0001-Stop-audio-when-xwidget-webkit-killed-Bug-29565.patch For example, this stop playing audio when you kill the xwidget webkit buffer while watching a youtube. * 0002-Functions-when-javascript-not-allowed-Bug-29565.patch This patch support basic xwidget webkit functions even when javascript is not allowed due to 'Content-Security-Policy' HTTP Response header. And enable web inspector, which is very valuable for javascript related debugging. [-- Attachment #1.2: Type: text/html, Size: 672 bytes --] [-- Attachment #2: 0001-Stop-audio-when-xwidget-webkit-killed-Bug-29565.patch --] [-- Type: application/octet-stream, Size: 950 bytes --] From 8c114974e3e9b56694ce3e17070d1e281c5a1310 Mon Sep 17 00:00:00 2001 From: Jaesup Kwak <veshboo@gmail.com> Date: Sun, 10 Dec 2017 16:24:29 +0900 Subject: [PATCH 1/2] Stop audio when xwidget-webkit killed (Bug#29565) * src/nsxwidget.m --- src/nsxwidget.m | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/nsxwidget.m b/src/nsxwidget.m index 04f1472c96..9a0d939c3d 100644 --- a/src/nsxwidget.m +++ b/src/nsxwidget.m @@ -395,6 +395,11 @@ - (BOOL)isFlipped { return YES; } [scriptor release]; if (xw->xv) xw->xv->model = Qnil; /* Make sure related view stale. */ + + /* This stops playing audio when a xwidget-webkit buffer is + killed. I could not find other solution. */ + nsxwidget_webkit_goto_uri (xw, "about:blank"); + [xw->xwWidget removeFromSuperviewWithoutNeedingDisplay]; [xw->xwWidget release]; [xw->xwWindow removeFromSuperviewWithoutNeedingDisplay]; -- 2.15.0 [-- Attachment #3: 0002-Functions-when-javascript-not-allowed-Bug-29565.patch --] [-- Type: application/octet-stream, Size: 19474 bytes --] From 8d7750f464c7c5097668875daed1376a1945094e Mon Sep 17 00:00:00 2001 From: Jaesup Kwak <veshboo@gmail.com> Date: Wed, 13 Dec 2017 15:03:00 +0900 Subject: [PATCH 2/2] Functions when javascript not allowed (Bug#29565) Support some basic xwidget webkit functions when javascript blocked by 'Content-Security-Policy'. Replace some javascript calls with webkit native functions and remove callbacks for the javascripts. Refactor 'xwidget-webkit-current-url', no side effect of message or kill-ring. No more async, directly used in bookmarking, removing URL text property. Add new function 'xwidget-webkit-url-message-kill' for old behavior. Add new key map 'f' to new function 'xwidget-webkit-forward'. Uniquely 'rename-buffer' with page title when the page is loaded. Enable web inspector for ns xwidget webkit. Change xwScript (ns xwidget webkit keyboard focus related script) injection time to document start from document end. * lisp/xwidget.el (xwidget-webkit-uri, xwidget-webkit-title) (xwidget-webkit-goto-history): New webkit native functions. (xwidget-webkit-cx2-cb, xwidget-webkit-cx3-cb) (xwidget-webkit-url-title-cb, xwidget-webkit-current-url-cb): Remove javascript callbacks. (xwidget-webkit-callback): Replace javascript with webkit native. No URL text property. Rename buffer unique. (xwidget-webkit-back, xwidget-webkit-reload): Replace javascript with webkit native. (xwidget-webkit-mode-map, xwidget-webkit-forward): New function. (xwidget-webkit-current-url, xwidget-webkit-current-url-message-kill) (xwidget-webkit-cx2, xwidget-webkit-cx3) (xwidget-webkit-bookmark-make-record): Replace javascript with webkit native. Refactor 'xwidget-webkit-current-url'. * src/nsxwidget.h * src/nsxwidget.m (nsxwidget_webkit_uri, nsxwidget_webkit_title) (nsxwidget_webkit_goto_history): New webkit native functions. (XwWebView::urlScriptBlocked, XwWebView::initWithFrame) (XwWebView::decidePolicyForNavigationResponse, nsxwidget_kill): Add property mapping URL to whether javascript allowed or not. Enable web inspector. Change user script injection time. (XwWebView::keyDown): Send keyDown event to emacs when javascript not allowed or javascript execution error. (nsxwidget_webkit_execute_script): Do not evaluate javascript when not allowed. * src/xwidget.c (Fxwidget_webkit_uri, Fxwiget_webkit_title) (Fxwidget_webkit_goto_history, syms_of_xwidget): DEFUNs for new webkit native functions and implementations for USE_GTK and NS_IMPLE_COCOA. --- lisp/xwidget.el | 102 ++++++++++++++++++++++---------------------------------- src/nsxwidget.h | 3 ++ src/nsxwidget.m | 79 +++++++++++++++++++++++++++++++++++++++++-- src/xwidget.c | 54 ++++++++++++++++++++++++++++++ 4 files changed, 172 insertions(+), 66 deletions(-) diff --git a/lisp/xwidget.el b/lisp/xwidget.el index 03183c19d5..e5b3fb2ad9 100644 --- a/lisp/xwidget.el +++ b/lisp/xwidget.el @@ -39,9 +39,14 @@ (declare-function xwidget-buffer "xwidget.c" (xwidget)) (declare-function xwidget-size-request "xwidget.c" (xwidget)) (declare-function xwidget-resize "xwidget.c" (xwidget new-width new-height)) +;; @callback - Prefer defun to lambda, not to be garbage collected +;; before its execution in `xwidget-webkit-callback'. (declare-function xwidget-webkit-execute-script "xwidget.c" (xwidget script &optional callback)) +(declare-function xwidget-webkit-uri "xwidget.c" (xwidget)) +(declare-function xwidget-webkit-title "xwidget.c" (xwidget)) (declare-function xwidget-webkit-goto-uri "xwidget.c" (xwidget uri)) +(declare-function xwidget-webkit-goto-history "xwidget.c" (xwidget rel-pos)) (declare-function xwidget-webkit-zoom "xwidget.c" (xwidget factor)) (declare-function xwidget-plist "xwidget.c" (xwidget)) (declare-function set-xwidget-plist "xwidget.c" (xwidget plist)) @@ -97,37 +102,21 @@ xwidget-webkit-browse-url (xwidget-webkit-new-session url) (xwidget-webkit-goto-url url)))) -;; NOTE: @javascript-callback - prefer defun to lambda. -;; Lambda seems to be more easily garbage collected in flight from -;; `xwidget-webkit-execute-script' to its execution via event. - -;; @javascript-callback -(defun xwidget-webkit-cx2-cb (url) - "New xwidget webkit session and buffer with URL in split window below." - (with-selected-window (split-window-below) - (xwidget-webkit-new-session url))) - -;; @javascript-callback -(defun xwidget-webkit-cx3-cb (url) - "New xwidget webkit session and buffer with URL in split window right." - (with-selected-window (split-window-right) - (xwidget-webkit-new-session url))) - (defun xwidget-webkit-cx2 () - "Get the URL of current session, then `xwidget-webkit-cx2-cb'." + "Get the URL of current session, then browse to the URL \ +in `split-window-below' with a new xwidget webkit session." (interactive) - (xwidget-webkit-execute-script - (xwidget-webkit-current-session) - "document.URL" - 'xwidget-webkit-cx2-cb)) + (let ((url (xwidget-webkit-current-url))) + (with-selected-window (split-window-below) + (xwidget-webkit-new-session url)))) (defun xwidget-webkit-cx3 () - "Get the URL of current session, then `xwidget-webkit-cx3-cb'." + "Get the URL of current session, then browse to the URL \ +in `split-window-right' with a new xwidget webkit session." (interactive) - (xwidget-webkit-execute-script - (xwidget-webkit-current-session) - "document.URL" - 'xwidget-webkit-cx3-cb)) + (let ((url (xwidget-webkit-current-url))) + (with-selected-window (split-window-right) + (xwidget-webkit-new-session url)))) ;;todo. ;; - check that the webkit support is compiled in @@ -136,10 +125,11 @@ xwidget-webkit-mode-map (define-key map "g" 'xwidget-webkit-browse-url) (define-key map "a" 'xwidget-webkit-adjust-size-dispatch) (define-key map "b" 'xwidget-webkit-back) + (define-key map "f" 'xwidget-webkit-forward) (define-key map "r" 'xwidget-webkit-reload) (define-key map "t" (lambda () (interactive) (message "o"))) ;FIXME: ?!? (define-key map "\C-m" 'xwidget-webkit-insert-string) - (define-key map "w" 'xwidget-webkit-current-url) + (define-key map "w" 'xwidget-webkit-current-url-message-kill) (define-key map "+" 'xwidget-webkit-zoom-in) (define-key map "-" 'xwidget-webkit-zoom-out) @@ -256,25 +246,13 @@ xwidget-webkit-callback (xwidget-log "error: callback called for xwidget with dead buffer") (with-current-buffer (xwidget-buffer xwidget) - - ;; @javascript-callback - ;; We do not change selected window due to getting to knowing - ;; URL and title. And also do not adjust webkit size to window - ;; here, the window can be the mini-buffer window unwantedly. - (defun xwidget-webkit-url-title-cb (url-title) - "Put URL as text property and change buffer name using TITLE." - (let ((url (car url-title)) - (title (car (cdr url-title)))) - (xwidget-log "webkit finished loading: '%s' from '%s'" title url) - (setq buffer-read-only nil) - (put-text-property 2 3 'URL url) - (setq buffer-read-only t) - (rename-buffer (format "*xwidget webkit: %s *" title)))) - (cond ((eq xwidget-event-type 'load-changed) - (xwidget-webkit-execute-script - xwidget "[document.URL, document.title]" - 'xwidget-webkit-url-title-cb)) +;;; We do not change selected window for the finish of loading a page. +;;; And do not adjust webkit size to window here, the selected window +;;; can be the mini-buffer window unwantedly. + (let ((title (xwidget-webkit-title xwidget))) + (xwidget-log "webkit finished loading: %s" title) + (rename-buffer (format "*xwidget webkit: %s *" title) t))) ((eq xwidget-event-type 'decide-policy) (let ((strarg (nth 3 last-input-event))) (if (string-match ".*#\\(.*\\)" strarg) @@ -313,12 +291,10 @@ xwidget-webkit-bookmark-jump-new-session nil, consider further customization with `xwidget-webkit-last-session-buffer'.") -;; We avoid using async `xwidget-webkit-current-url', instead use URL -;; kept in xwidget webkit as property (defun xwidget-webkit-bookmark-make-record () "Integrate Emacs bookmarks with the webkit xwidget." (nconc (bookmark-make-record-default t t) - `((filename . ,(get-text-property 2 'URL)) + `((filename . ,(xwidget-webkit-current-url)) (handler . (lambda (bmk) (browse-url (bookmark-prop-get bmk 'filename) @@ -656,27 +632,27 @@ xwidget-webkit-goto-url (defun xwidget-webkit-back () "Go back in history." (interactive) - (xwidget-webkit-execute-script (xwidget-webkit-current-session) - "history.go(-1);")) + (xwidget-webkit-goto-history (xwidget-webkit-current-session) -1)) -(defun xwidget-webkit-reload () - "Reload current url." +(defun xwidget-webkit-forward () + "Go forward in history." (interactive) - (xwidget-webkit-execute-script (xwidget-webkit-current-session) - "history.go(0);")) + (xwidget-webkit-goto-history (xwidget-webkit-current-session) 1)) -;; @javascript-callback -(defun xwidget-webkit-current-url-cb (result) - "Callback for `xwidget-webkit-current-url', message and kill the RESULT." - (let ((url (kill-new (or result "")))) - (message "url: %s" url))) +(defun xwidget-webkit-reload () + "Reload current URL." + (interactive) + (xwidget-webkit-goto-history (xwidget-webkit-current-session) 0)) (defun xwidget-webkit-current-url () - "Get the webkit url and place it on the `kill-ring'." + "Get the current xwidget webkit URL." (interactive) - (xwidget-webkit-execute-script - (xwidget-webkit-current-session) - "document.URL" 'xwidget-webkit-current-url-cb)) + (xwidget-webkit-uri (xwidget-webkit-current-session))) + +(defun xwidget-webkit-current-url-message-kill () + "Message the current xwidget webkit URL and place it on the `kill-ring'." + (interactive) + (message "url: %s" (kill-new (or (xwidget-webkit-current-url) "")))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun xwidget-webkit-get-selection (proc) diff --git a/src/nsxwidget.h b/src/nsxwidget.h index a617a0d527..6af5fe5a4d 100644 --- a/src/nsxwidget.h +++ b/src/nsxwidget.h @@ -32,7 +32,10 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ /* Functions for xwidget webkit. */ bool nsxwidget_is_web_view (struct xwidget *xw); +Lisp_Object nsxwidget_webkit_uri (struct xwidget *xw); +Lisp_Object nsxwidget_webkit_title (struct xwidget *xw); void nsxwidget_webkit_goto_uri (struct xwidget *xw, const char *uri); +void nsxwidget_webkit_goto_history (struct xwidget *xw, int rel_pos); void nsxwidget_webkit_zoom (struct xwidget *xw, double zoom_change); void nsxwidget_webkit_execute_script (struct xwidget *xw, const char *script, Lisp_Object fun); diff --git a/src/nsxwidget.m b/src/nsxwidget.m index 9a0d939c3d..65ca83da73 100644 --- a/src/nsxwidget.m +++ b/src/nsxwidget.m @@ -61,6 +61,9 @@ void store_xwidget_js_callback_event (struct xwidget *xw, @interface XwWebView : WKWebView <WKNavigationDelegate, WKUIDelegate, WKScriptMessageHandler> @property struct xwidget *xw; +/* Map url to whether javascript is blocked by + 'Content-Security-Policy' sandbox without allow-scripts. */ +@property(retain) NSMutableDictionary *urlScriptBlocked; @end @implementation XwWebView : WKWebView @@ -72,10 +75,15 @@ - (id)initWithFrame:(CGRect)frame WKUserContentController *scriptor = [[WKUserContentController alloc] init]; configuration.userContentController = scriptor; + /* Enable inspect element context menu item for debugging. */ + [configuration.preferences setValue:@YES + forKey:@"developerExtrasEnabled"]; + self = [super initWithFrame:frame configuration:configuration]; if (self) { self.xw = xw; + self.urlScriptBlocked = [[NSMutableDictionary alloc] init]; self.navigationDelegate = self; self.UIDelegate = self; self.customUserAgent = @@ -86,7 +94,7 @@ - (id)initWithFrame:(CGRect)frame [scriptor addUserScript:[[WKUserScript alloc] initWithSource:xwScript injectionTime: - WKUserScriptInjectionTimeAtDocumentEnd + WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:NO]]; } return self; @@ -131,6 +139,28 @@ - (void)webView:(WKWebView *)webView /* TODO: download using NSURLxxx? */ } decisionHandler (WKNavigationResponsePolicyAllow); + + self.urlScriptBlocked[navigationResponse.response.URL] = + [NSNumber numberWithBool:NO]; + if ([navigationResponse.response isKindOfClass:[NSHTTPURLResponse class]]) + { + NSDictionary *headers = + ((NSHTTPURLResponse *) navigationResponse.response).allHeaderFields; + NSString *value = headers[@"Content-Security-Policy"]; + if (value) + { + /* TODO: Sloppy parsing of 'Content-Security-Policy' value. */ + NSRange sandbox = [value rangeOfString:@"sandbox"]; + if (sandbox.location != NSNotFound) + { + NSRange allowScripts = [value rangeOfString:@"allow-scripts"]; + if (allowScripts.location == NSNotFound + || allowScripts.location < sandbox.location) + self.urlScriptBlocked[navigationResponse.response.URL] = + [NSNumber numberWithBool:YES]; + } + } + } } /* No additional new webview or emacs window will be created @@ -178,10 +208,20 @@ - (void)keyDown:(NSEvent *)event return; } + /* Emacs handles keyboard events when javascript is blocked. */ + if ([self.urlScriptBlocked[self.URL] boolValue]) + { + [self.xw->xv->emacswindow keyDown:event]; + return; + } + [self evaluateJavaScript:@"xwHasFocus()" completionHandler:^(id result, NSError *error) { if (error) - NSLog (@"xwHasFocus: %@", error.localizedDescription); + { + NSLog (@"xwHasFocus: %@", error); + [self.xw->xv->emacswindow keyDown:event]; + } else if (result) { NSNumber *hasFocus = result; /* __NSCFBoolean */ @@ -242,6 +282,8 @@ - (void)userContentController:(WKUserContentController *)userContentController /* Xwidget webkit commands. */ +static Lisp_Object build_string_with_nsstr (NSString *nsstr); + bool nsxwidget_is_web_view (struct xwidget *xw) { @@ -249,6 +291,20 @@ - (void)userContentController:(WKUserContentController *)userContentController [xw->xwWidget isKindOfClass:WKWebView.class]; } +Lisp_Object +nsxwidget_webkit_uri (struct xwidget *xw) +{ + XwWebView *xwWebView = (XwWebView *) xw->xwWidget; + return build_string_with_nsstr (xwWebView.URL.absoluteString); +} + +Lisp_Object +nsxwidget_webkit_title (struct xwidget *xw) +{ + XwWebView *xwWebView = (XwWebView *) xw->xwWidget; + return build_string_with_nsstr (xwWebView.title); +} + /* @Note ATS - Need application transport security in 'Info.plist' or remote pages will not loaded. */ void @@ -261,6 +317,17 @@ - (void)userContentController:(WKUserContentController *)userContentController [xwWebView loadRequest:urlRequest]; } +void +nsxwidget_webkit_goto_history (struct xwidget *xw, int rel_pos) +{ + XwWebView *xwWebView = (XwWebView *) xw->xwWidget; + switch (rel_pos) { + case -1: [xwWebView goBack]; break; + case 0: [xwWebView reload]; break; + case 1: [xwWebView goForward]; break; + } +} + void nsxwidget_webkit_zoom (struct xwidget *xw, double zoom_change) { @@ -340,9 +407,14 @@ - (void)userContentController:(WKUserContentController *)userContentController nsxwidget_webkit_execute_script (struct xwidget *xw, const char *script, Lisp_Object fun) { - NSString *javascriptString = [NSString stringWithUTF8String:script]; XwWebView *xwWebView = (XwWebView *) xw->xwWidget; + if ([xwWebView.urlScriptBlocked[xwWebView.URL] boolValue]) + { + message ("Javascript is blocked by 'CSP: sandbox'."); + return; + } + NSString *javascriptString = [NSString stringWithUTF8String:script]; [xwWebView evaluateJavaScript:javascriptString completionHandler:^(id result, NSError *error) { if (error) @@ -400,6 +472,7 @@ - (BOOL)isFlipped { return YES; } killed. I could not find other solution. */ nsxwidget_webkit_goto_uri (xw, "about:blank"); + [((XwWebView *) xw->xwWidget).urlScriptBlocked release]; [xw->xwWidget removeFromSuperviewWithoutNeedingDisplay]; [xw->xwWidget release]; [xw->xwWindow removeFromSuperviewWithoutNeedingDisplay]; diff --git a/src/xwidget.c b/src/xwidget.c index 17e1e6428a..f333b2287c 100644 --- a/src/xwidget.c +++ b/src/xwidget.c @@ -753,6 +753,36 @@ xwidget_is_web_view (struct xwidget *xw) return Qnil; \ } +DEFUN ("xwidget-webkit-uri", + Fxwidget_webkit_uri, Sxwidget_webkit_uri, + 1, 1, 0, + doc: /* Get the current URL of XWIDGET webkit. */) + (Lisp_Object xwidget) +{ + WEBKIT_FN_INIT (); +#if defined (USE_GTK) + WebKitWebView *wkwv = WEBKIT_WEB_VIEW (xw->widget_osr); + return build_string (webkit_web_view_get_uri (wkwv)); +#elif defined (NS_IMPL_COCOA) + return nsxwidget_webkit_uri (xw); +#endif +} + +DEFUN ("xwidget-webkit-title", + Fxwidget_webkit_title, Sxwidget_webkit_title, + 1, 1, 0, + doc: /* Get the current title of XWIDGET webkit. */) + (Lisp_Object xwidget) +{ + WEBKIT_FN_INIT (); +#if defined (USE_GTK) + WebKitWebView *wkwv = WEBKIT_WEB_VIEW (xw->widget_osr); + return build_string (webkit_web_view_get_title (wkwv)); +#elif defined (NS_IMPL_COCOA) + return nsxwidget_webkit_title (xw); +#endif +} + DEFUN ("xwidget-webkit-goto-uri", Fxwidget_webkit_goto_uri, Sxwidget_webkit_goto_uri, 2, 2, 0, @@ -769,6 +799,27 @@ DEFUN ("xwidget-webkit-goto-uri", return Qnil; } +DEFUN ("xwidget-webkit-goto-history", + Fxwidget_webkit_goto_history, Sxwidget_webkit_goto_history, + 2, 2, 0, + doc: /* Make the XWIDGET webkit load REL-POS (-1, 0, 1) page in browse history. */) + (Lisp_Object xwidget, Lisp_Object rel_pos) +{ + WEBKIT_FN_INIT (); + CHECK_RANGED_INTEGER (rel_pos, -1, 1); /* -1, 0, 1 */ +#if defined (USE_GTK) + WebKitWebView *wkwv = WEBKIT_WEB_VIEW (xw->widget_osr); + switch (XFASTINT (rel_pos)) { + case -1: webkit_web_view_go_back (wkwv); break; + case 0: webkit_web_view_go_reload (wkwv); break; + case 1: webkit_web_view_forward (wkwv); break; + } +#elif defined (NS_IMPL_COCOA) + nsxwidget_webkit_goto_history (xw, XFASTINT (rel_pos)); +#endif + return Qnil; +} + DEFUN ("xwidget-webkit-zoom", Fxwidget_webkit_zoom, Sxwidget_webkit_zoom, 2, 2, 0, @@ -1092,7 +1143,10 @@ syms_of_xwidget (void) defsubr (&Sxwidget_query_on_exit_flag); defsubr (&Sset_xwidget_query_on_exit_flag); + defsubr (&Sxwidget_webkit_uri); + defsubr (&Sxwidget_webkit_title); defsubr (&Sxwidget_webkit_goto_uri); + defsubr (&Sxwidget_webkit_goto_history); defsubr (&Sxwidget_webkit_zoom); defsubr (&Sxwidget_webkit_execute_script); DEFSYM (Qwebkit, "webkit"); -- 2.15.0 ^ permalink raw reply related [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2017-12-04 16:44 bug#29565: [PATCH] Support xwidget webkit for macOS X Jaesup Kwak ` (2 preceding siblings ...) 2017-12-13 11:27 ` Jaesup Kwak @ 2017-12-13 16:13 ` Jaesup Kwak 2017-12-15 2:01 ` Jaesup Kwak ` (7 subsequent siblings) 11 siblings, 0 replies; 41+ messages in thread From: Jaesup Kwak @ 2017-12-13 16:13 UTC (permalink / raw) To: 29565 [-- Attachment #1.1: Type: text/plain, Size: 191 bytes --] Fix "callback called for xwidget with dead buffer" symptom (observed in *xwidget-log*), the bug was introduced in previous patch '0001-Stop-audio-when-xwidget-webkit-killed-Bug-29565.patch'. [-- Attachment #1.2: Type: text/html, Size: 245 bytes --] [-- Attachment #2: 0001-Fix-event-for-dead-xwidget-buffer-Bug-29565.patch --] [-- Type: application/octet-stream, Size: 889 bytes --] From 5cb99297e9d5a566b888067ba108db5581120471 Mon Sep 17 00:00:00 2001 From: Jaesup Kwak <veshboo@gmail.com> Date: Thu, 14 Dec 2017 00:13:17 +0900 Subject: [PATCH] Fix event for dead xwidget buffer (Bug#29565) Fix the bug introduced in 'Stop audio when xwidget-webkit killed'. * src/nsxwidget.m (XwWebView::didFinishNavigation) --- src/nsxwidget.m | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/nsxwidget.m b/src/nsxwidget.m index 65ca83da73..7895c8bed6 100644 --- a/src/nsxwidget.m +++ b/src/nsxwidget.m @@ -112,7 +112,8 @@ - (void)dealloc - (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation { - store_xwidget_event_string (self.xw, "load-changed", ""); + if (EQ (Fbuffer_live_p (self.xw->buffer), Qt)) + store_xwidget_event_string (self.xw, "load-changed", ""); } - (void)webView:(WKWebView *)webView -- 2.15.0 ^ permalink raw reply related [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2017-12-04 16:44 bug#29565: [PATCH] Support xwidget webkit for macOS X Jaesup Kwak ` (3 preceding siblings ...) 2017-12-13 16:13 ` Jaesup Kwak @ 2017-12-15 2:01 ` Jaesup Kwak 2017-12-15 2:27 ` Jaesup Kwak ` (6 subsequent siblings) 11 siblings, 0 replies; 41+ messages in thread From: Jaesup Kwak @ 2017-12-15 2:01 UTC (permalink / raw) To: 29565 [-- Attachment #1.1: Type: text/plain, Size: 229 bytes --] Enhance xwidget webkit scroll (Bug#29565) Add key map 'S-SPC' for scroll down page. Correct remappings for scroll by page and by line. Add 'xwidget-webkit-scroll-line-height' variable. Fix scroll to bottom function. Thank you. [-- Attachment #1.2: Type: text/html, Size: 356 bytes --] [-- Attachment #2: 0001-Enhance-xwidget-webkit-scroll-Bug-29565.patch --] [-- Type: application/octet-stream, Size: 5211 bytes --] From 36cb782cb203c0f9bddfa5a4e138bbfe1aabf834 Mon Sep 17 00:00:00 2001 From: Jaesup Kwak <veshboo@gmail.com> Date: Fri, 15 Dec 2017 10:05:27 +0900 Subject: [PATCH] Enhance xwidget webkit scroll (Bug#29565) Add key map 'S-SPC' for scroll down page. Correct remappings for scroll by page and by line. Add 'xwidget-webkit-scroll-line-height' variable. Fix scroll to bottom function. * lisp/xwidget.el (xwidget-webkit-mode-map): Well suited scroll key mappings. (xwidget-webkit-scroll-up, xwidget-webkit-scroll-down): Add optional argument, scroll by a screen page by default. (xwidget-webkit-scroll-line-height): Add variable. (xwidget-webkit-scroll-up-line, xwidget-webkit-scroll-down-line): New functions to scroll by lines. (xwidget-webkit-scroll-bottom): Fix to scroll to bottom of the document. --- lisp/xwidget.el | 55 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/lisp/xwidget.el b/lisp/xwidget.el index e5b3fb2ad9..386b2d84ee 100644 --- a/lisp/xwidget.el +++ b/lisp/xwidget.el @@ -135,20 +135,21 @@ xwidget-webkit-mode-map ;;similar to image mode bindings (define-key map (kbd "SPC") 'xwidget-webkit-scroll-up) + (define-key map (kbd "S-SPC") 'xwidget-webkit-scroll-down) (define-key map (kbd "DEL") 'xwidget-webkit-scroll-down) - (define-key map [remap scroll-up] 'xwidget-webkit-scroll-up) + (define-key map [remap scroll-up] 'xwidget-webkit-scroll-up-line) (define-key map [remap scroll-up-command] 'xwidget-webkit-scroll-up) - (define-key map [remap scroll-down] 'xwidget-webkit-scroll-down) + (define-key map [remap scroll-down] 'xwidget-webkit-scroll-down-line) (define-key map [remap scroll-down-command] 'xwidget-webkit-scroll-down) (define-key map [remap forward-char] 'xwidget-webkit-scroll-forward) (define-key map [remap backward-char] 'xwidget-webkit-scroll-backward) (define-key map [remap right-char] 'xwidget-webkit-scroll-forward) (define-key map [remap left-char] 'xwidget-webkit-scroll-backward) - (define-key map [remap previous-line] 'xwidget-webkit-scroll-down) - (define-key map [remap next-line] 'xwidget-webkit-scroll-up) + (define-key map [remap previous-line] 'xwidget-webkit-scroll-down-line) + (define-key map [remap next-line] 'xwidget-webkit-scroll-up-line) ;; (define-key map [remap move-beginning-of-line] 'image-bol) ;; (define-key map [remap move-end-of-line] 'image-eol) @@ -173,19 +174,45 @@ xwidget-webkit-zoom-out (interactive) (xwidget-webkit-zoom (xwidget-webkit-current-session) -0.1)) -(defun xwidget-webkit-scroll-up () - "Scroll webkit up." - (interactive) +(defun xwidget-webkit-scroll-up (&optional n) + "Scroll webkit up by N pixels or window height pixels. +Stop if the bottom edge of the page is reached. +If N is omitted or nil, scroll up by window height pixels." + (interactive "P") + (message "scroll-up n=%S" n) (xwidget-webkit-execute-script (xwidget-webkit-current-session) - "window.scrollBy(0, 50);")) - -(defun xwidget-webkit-scroll-down () - "Scroll webkit down." - (interactive) + (cond ((null n) "window.scrollBy(0, window.document.body.clientHeight);") + (t (format "window.scrollBy(0, %d);" n))))) + +(defun xwidget-webkit-scroll-down (&optional n) + "Scroll webkit down by N pixels or window height pixels. +Stop if the top edge of the page is reached. +If N is omitted or nil, scroll down by window height pixels." + (interactive "P") (xwidget-webkit-execute-script (xwidget-webkit-current-session) - "window.scrollBy(0, -50);")) + (cond ((null n) "window.scrollBy(0, -window.document.body.clientHeight);") + (t (format "window.scrollBy(0, %d);" (- n)))))) + +(defvar xwidget-webkit-scroll-line-height 50 + "Default line height in pixels for scroll xwidget webkit.") + +(defun xwidget-webkit-scroll-up-line (&optional n) + "Scroll webkit up by N lines. +The height of line is `xwidget-webkit-scroll-line-height' pixels. +Stop if the bottom edge of the page is reached. +If N is omitted or nil, scroll up by one line." + (interactive "p") + (xwidget-webkit-scroll-up (* n xwidget-webkit-scroll-line-height))) + +(defun xwidget-webkit-scroll-down-line (&optional n) + "Scroll webkit down by N lines. +The height of line is `xwidget-webkit-scroll-line-height' pixels. +Stop if the top edge of the page is reached. +If N is omitted or nil, scroll down by one line." + (interactive "p") + (xwidget-webkit-scroll-down (* n xwidget-webkit-scroll-line-height))) (defun xwidget-webkit-scroll-forward () "Scroll webkit forwards." @@ -213,7 +240,7 @@ xwidget-webkit-scroll-bottom (interactive) (xwidget-webkit-execute-script (xwidget-webkit-current-session) - "window.scrollTo(pageXOffset, window.document.body.clientHeight);")) + "window.scrollTo(pageXOffset, window.document.body.scrollHeight);")) ;; The xwidget event needs to go into a higher level handler ;; since the xwidget can generate an event even if it's offscreen. -- 2.15.0 ^ permalink raw reply related [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2017-12-04 16:44 bug#29565: [PATCH] Support xwidget webkit for macOS X Jaesup Kwak ` (4 preceding siblings ...) 2017-12-15 2:01 ` Jaesup Kwak @ 2017-12-15 2:27 ` Jaesup Kwak 2017-12-15 16:06 ` Jaesup Kwak ` (5 subsequent siblings) 11 siblings, 0 replies; 41+ messages in thread From: Jaesup Kwak @ 2017-12-15 2:27 UTC (permalink / raw) To: 29565 [-- Attachment #1.1: Type: text/plain, Size: 55 bytes --] Remove a debug message in the previous patch. Thanks. [-- Attachment #1.2: Type: text/html, Size: 100 bytes --] [-- Attachment #2: 0001-Remove-a-debug-message.patch --] [-- Type: application/octet-stream, Size: 788 bytes --] From 8d6e1f8c462c70a9d9a24a0e30f8367b8dd05486 Mon Sep 17 00:00:00 2001 From: Jaesup Kwak <veshboo@gmail.com> Date: Fri, 15 Dec 2017 11:18:18 +0900 Subject: [PATCH] Remove a debug message * lisp/xwidget.el (xwidget-webkit-scroll-up) --- lisp/xwidget.el | 1 - 1 file changed, 1 deletion(-) diff --git a/lisp/xwidget.el b/lisp/xwidget.el index 386b2d84ee..acd3ea2a5b 100644 --- a/lisp/xwidget.el +++ b/lisp/xwidget.el @@ -179,7 +179,6 @@ xwidget-webkit-scroll-up Stop if the bottom edge of the page is reached. If N is omitted or nil, scroll up by window height pixels." (interactive "P") - (message "scroll-up n=%S" n) (xwidget-webkit-execute-script (xwidget-webkit-current-session) (cond ((null n) "window.scrollBy(0, window.document.body.clientHeight);") -- 2.15.0 ^ permalink raw reply related [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2017-12-04 16:44 bug#29565: [PATCH] Support xwidget webkit for macOS X Jaesup Kwak ` (5 preceding siblings ...) 2017-12-15 2:27 ` Jaesup Kwak @ 2017-12-15 16:06 ` Jaesup Kwak 2017-12-20 2:39 ` bug#29565: [PATCH] Fix compile failure for GTK xwidget (Bug#29565) Jaesup Kwak ` (4 subsequent siblings) 11 siblings, 0 replies; 41+ messages in thread From: Jaesup Kwak @ 2017-12-15 16:06 UTC (permalink / raw) To: 29565 [-- Attachment #1.1: Type: text/plain, Size: 192 bytes --] Page up/down by emacs window height (Bug#29565) 'xwidget-window-inside-pixel-height' is more reliable than javascript 'window.document.body.clientHeight' for scroll up/down by page. Thanks. [-- Attachment #1.2: Type: text/html, Size: 310 bytes --] [-- Attachment #2: 0001-Page-up-down-by-emacs-window-height-Bug-29565.patch --] [-- Type: application/octet-stream, Size: 1575 bytes --] From 59fd6c81cc3f97c2ca48bddffd346f77b7a9d58b Mon Sep 17 00:00:00 2001 From: Jaesup Kwak <veshboo@gmail.com> Date: Sat, 16 Dec 2017 00:50:47 +0900 Subject: [PATCH] Page up/down by emacs window height (Bug#29565) 'xwidget-window-inside-pixel-height' is more reliable than javascript 'window.document.body.clientHeight' for scroll up/down by page. * lisp/xwidget.el (xwidget-webkit-scroll-up) (xwidget-webkit-scroll-down) --- lisp/xwidget.el | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lisp/xwidget.el b/lisp/xwidget.el index acd3ea2a5b..a218868965 100644 --- a/lisp/xwidget.el +++ b/lisp/xwidget.el @@ -181,7 +181,9 @@ xwidget-webkit-scroll-up (interactive "P") (xwidget-webkit-execute-script (xwidget-webkit-current-session) - (cond ((null n) "window.scrollBy(0, window.document.body.clientHeight);") + (cond ((null n) + (format "window.scrollBy(0, %d);" + (xwidget-window-inside-pixel-height (selected-window)))) (t (format "window.scrollBy(0, %d);" n))))) (defun xwidget-webkit-scroll-down (&optional n) @@ -191,7 +193,9 @@ xwidget-webkit-scroll-down (interactive "P") (xwidget-webkit-execute-script (xwidget-webkit-current-session) - (cond ((null n) "window.scrollBy(0, -window.document.body.clientHeight);") + (cond ((null n) + (format "window.scrollBy(0, %d);" + (- (xwidget-window-inside-pixel-height (selected-window))))) (t (format "window.scrollBy(0, %d);" (- n)))))) (defvar xwidget-webkit-scroll-line-height 50 -- 2.15.0 ^ permalink raw reply related [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Fix compile failure for GTK xwidget (Bug#29565) 2017-12-04 16:44 bug#29565: [PATCH] Support xwidget webkit for macOS X Jaesup Kwak ` (6 preceding siblings ...) 2017-12-15 16:06 ` Jaesup Kwak @ 2017-12-20 2:39 ` Jaesup Kwak 2017-12-20 8:25 ` bug#29565: [PATCH] Enable plugins for ns xwidget webkit (Bug#29565) Jaesup Kwak ` (3 subsequent siblings) 11 siblings, 0 replies; 41+ messages in thread From: Jaesup Kwak @ 2017-12-20 2:39 UTC (permalink / raw) To: 29565 [-- Attachment #1.1: Type: text/plain, Size: 180 bytes --] Fix incorrect GTK webkit function names, those were introduced with "8d7750f464 Functions when javascript not allowed (Bug#29565)". * src/xwidget.c (Fxwidget_webkit_goto_history) [-- Attachment #1.2: Type: text/html, Size: 272 bytes --] [-- Attachment #2: 0001-Fix-compile-failure-for-GTK-xwidget-Bug-29565.patch --] [-- Type: application/octet-stream, Size: 1102 bytes --] From 7ec3d92363472706a734ceb9b1bccfb82f2bbcee Mon Sep 17 00:00:00 2001 From: Jaesup Kwak <veshboo@gmail.com> Date: Wed, 20 Dec 2017 11:19:13 +0900 Subject: [PATCH] Fix compile failure for GTK xwidget (Bug#29565) Fix incorrect GTK webkit function names, those were introduced with "8d7750f464 Functions when javascript not allowed (Bug#29565)". * src/xwidget.c (Fxwidget_webkit_goto_history) --- src/xwidget.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/xwidget.c b/src/xwidget.c index f333b2287c..60e8260377 100644 --- a/src/xwidget.c +++ b/src/xwidget.c @@ -811,8 +811,8 @@ DEFUN ("xwidget-webkit-goto-history", WebKitWebView *wkwv = WEBKIT_WEB_VIEW (xw->widget_osr); switch (XFASTINT (rel_pos)) { case -1: webkit_web_view_go_back (wkwv); break; - case 0: webkit_web_view_go_reload (wkwv); break; - case 1: webkit_web_view_forward (wkwv); break; + case 0: webkit_web_view_reload (wkwv); break; + case 1: webkit_web_view_go_forward (wkwv); break; } #elif defined (NS_IMPL_COCOA) nsxwidget_webkit_goto_history (xw, XFASTINT (rel_pos)); -- 2.15.0 ^ permalink raw reply related [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Enable plugins for ns xwidget webkit (Bug#29565) 2017-12-04 16:44 bug#29565: [PATCH] Support xwidget webkit for macOS X Jaesup Kwak ` (7 preceding siblings ...) 2017-12-20 2:39 ` bug#29565: [PATCH] Fix compile failure for GTK xwidget (Bug#29565) Jaesup Kwak @ 2017-12-20 8:25 ` Jaesup Kwak 2017-12-21 4:12 ` bug#29565: [PATCH] Support file download and upload (Bug#29565) Jaesup Kwak ` (2 subsequent siblings) 11 siblings, 0 replies; 41+ messages in thread From: Jaesup Kwak @ 2017-12-20 8:25 UTC (permalink / raw) To: 29565 [-- Attachment #1.1: Type: text/plain, Size: 180 bytes --] Enable plugins for ns xwidget webkit if 'xwidget-webkit-enable-plugins' is non-nil. * lisp/xwidget.el (xwidget-webkit-enable-plugins) * src/nsxwidget.m (XwWebView::initWithFrame) [-- Attachment #1.2: Type: text/html, Size: 280 bytes --] [-- Attachment #2: 0001-Enable-plugins-for-ns-xwidget-webkit-Bug-29565.patch --] [-- Type: application/octet-stream, Size: 1658 bytes --] From 8156627829bcc26b38bd6d2c7921289f50ad1ad0 Mon Sep 17 00:00:00 2001 From: Jaesup Kwak <veshboo@gmail.com> Date: Wed, 20 Dec 2017 17:11:06 +0900 Subject: [PATCH] Enable plugins for ns xwidget webkit (Bug#29565) Enable plugins for ns xwidget webkit if 'xwidget-webkit-enable-plugins' is non-nil. * lisp/xwidget.el (xwidget-webkit-enable-plugins) * src/nsxwidget.m (XwWebView::initWithFrame) --- lisp/xwidget.el | 5 +++++ src/nsxwidget.m | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/lisp/xwidget.el b/lisp/xwidget.el index a218868965..901a6be9a2 100644 --- a/lisp/xwidget.el +++ b/lisp/xwidget.el @@ -300,6 +300,11 @@ xwidget-webkit-callback (defvar bookmark-make-record-function) (defvar isearch-search-fun-function) +(when (memq window-system '(mac ns)) + (defvar xwidget-webkit-enable-plugins nil + "Enable plugins for xwidget webkit. +If non-nil, plugins are enabled. Otherwise, disabled.")) + (define-derived-mode xwidget-webkit-mode special-mode "xwidget-webkit" "Xwidget webkit view mode." (setq buffer-read-only t) diff --git a/src/nsxwidget.m b/src/nsxwidget.m index 7895c8bed6..ee295518b8 100644 --- a/src/nsxwidget.m +++ b/src/nsxwidget.m @@ -79,6 +79,11 @@ - (id)initWithFrame:(CGRect)frame [configuration.preferences setValue:@YES forKey:@"developerExtrasEnabled"]; + Lisp_Object enablePlugins = + Fintern (build_string ("xwidget-webkit-enable-plugins"), Qnil); + if (!EQ (Fsymbol_value (enablePlugins), Qnil)) + configuration.preferences.plugInsEnabled = YES; + self = [super initWithFrame:frame configuration:configuration]; if (self) { -- 2.15.0 ^ permalink raw reply related [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support file download and upload (Bug#29565) 2017-12-04 16:44 bug#29565: [PATCH] Support xwidget webkit for macOS X Jaesup Kwak ` (8 preceding siblings ...) 2017-12-20 8:25 ` bug#29565: [PATCH] Enable plugins for ns xwidget webkit (Bug#29565) Jaesup Kwak @ 2017-12-21 4:12 ` Jaesup Kwak 2018-03-30 11:48 ` bug#29565: [PATCH] Support xwidget webkit for macOS X Alan Third 2019-09-28 23:52 ` Stefan Kangas 11 siblings, 0 replies; 41+ messages in thread From: Jaesup Kwak @ 2017-12-21 4:12 UTC (permalink / raw) To: 29565 [-- Attachment #1.1: Type: text/plain, Size: 690 bytes --] For the MIME types, which ns xwidget webkit cannot show in a view, 'xwidget-webkit-save-as-file' is called via 'response-callback' event. Ns xwidget webkit presents file open panel to select upload files. Tighter check for javascript availability. * lisp/xwidget.el (xwidget-webkit-callback): Add case for 'response-callback' event. (xwidget-webkit-download-dir): New variable. (xwidget-webkit-save-as-file): New function. * src/nsxwidget.m (XwWebView::decidePolicyForNavigationResponse): Store the event. And tighter check for javascript availability. (XwWebView::runOpenPanelWithParameters): Select upload files. * src/xwidget.c (store_xwidget_response_callback_event): New function. [-- Attachment #1.2: Type: text/html, Size: 935 bytes --] [-- Attachment #2: 0001-Support-file-download-and-upload-Bug-29565.patch --] [-- Type: application/octet-stream, Size: 7189 bytes --] From b5e3db090f43734c365f86d92cae1107f8df5398 Mon Sep 17 00:00:00 2001 From: Jaesup Kwak <veshboo@gmail.com> Date: Thu, 21 Dec 2017 12:36:51 +0900 Subject: [PATCH] Support file download and upload (Bug#29565) For the MIME types, which ns xwidget webkit cannot show in a view, 'xwidget-webkit-save-as-file' is called via 'response-callback' event. Ns xwidget webkit presents file open panel to select upload files. Tighter check for javascript availability. * lisp/xwidget.el (xwidget-webkit-callback): Add case for 'response-callback' event. (xwidget-webkit-download-dir): New variable. (xwidget-webkit-save-as-file): New function. * src/nsxwidget.m (XwWebView::decidePolicyForNavigationResponse): Store the event. And tighter check for javascript availability. (XwWebView::runOpenPanelWithParameters): Select upload files. * src/xwidget.c (store_xwidget_response_callback_event): New function. --- lisp/xwidget.el | 23 +++++++++++++++++++++++ src/nsxwidget.m | 36 ++++++++++++++++++++++++++++++++++-- src/xwidget.c | 20 ++++++++++++++++++++ 3 files changed, 77 insertions(+), 2 deletions(-) diff --git a/lisp/xwidget.el b/lisp/xwidget.el index 901a6be9a2..6c88262e43 100644 --- a/lisp/xwidget.el +++ b/lisp/xwidget.el @@ -84,6 +84,7 @@ xwidget-at (require 'browse-url) (require 'image-mode);;for some image-mode alike functionality (require 'seq) +(require 'url-handlers) ;;;###autoload (defun xwidget-webkit-browse-url (url &optional new-session) @@ -289,6 +290,12 @@ xwidget-webkit-callback (xwidget-webkit-show-id-or-named-element xwidget (match-string 1 strarg))))) +;;; TODO: Response handling other than download. + ((eq xwidget-event-type 'response-callback) + (let ((url (nth 3 last-input-event)) + (mime-type (nth 4 last-input-event)) + (file-name (nth 5 last-input-event))) + (xwidget-webkit-save-as-file xwidget url mime-type file-name))) ((eq xwidget-event-type 'javascript-callback) (let ((proc (nth 3 last-input-event)) (arg (nth 4 last-input-event))) @@ -317,6 +324,22 @@ xwidget-webkit-mode ;; Keep track of [vh]scroll when switching buffers (image-mode-setup-winprops)) +;;; Download, save as file. + +(defvar xwidget-webkit-download-dir "~/Downloads/" + "Directory where download file saved.") + +(defun xwidget-webkit-save-as-file (xwidget url mime-type &optional file-name) + "For XWIDGET webkit, save URL resource of MIME-TYPE as FILE-NAME." + (ignore xwidget) ;; Not used currently + (let ((save-name (read-file-name + (format "Save '%s' file as: " mime-type) + xwidget-webkit-download-dir file-name nil file-name))) + (if (file-directory-p save-name) + (setq save-name (concat (file-name-as-directory save-name) file-name))) + (setq xwidget-webkit-download-dir (file-name-directory save-name)) + (url-copy-file url save-name t))) + ;;; Bookmarks integration (defvar xwidget-webkit-bookmark-jump-new-session nil diff --git a/src/nsxwidget.m b/src/nsxwidget.m index ee295518b8..22baaecedf 100644 --- a/src/nsxwidget.m +++ b/src/nsxwidget.m @@ -33,6 +33,11 @@ void store_xwidget_event_string (struct xwidget *xw, const char *eventname, const char *eventstr); +void store_xwidget_response_callback_event (struct xwidget *xw, + const char *url, + const char *mimetype, + const char *filename); + void store_xwidget_js_callback_event (struct xwidget *xw, Lisp_Object proc, Lisp_Object argument); @@ -142,7 +147,15 @@ - (void)webView:(WKWebView *)webView { if (!navigationResponse.canShowMIMEType) { - /* TODO: download using NSURLxxx? */ + NSString *url = navigationResponse.response.URL.absoluteString; + NSString *mimetype = navigationResponse.response.MIMEType; + NSString *filename = navigationResponse.response.suggestedFilename; + decisionHandler (WKNavigationResponsePolicyCancel); + store_xwidget_response_callback_event (self.xw, + url.UTF8String, + mimetype.UTF8String, + filename.UTF8String); + return; } decisionHandler (WKNavigationResponsePolicyAllow); @@ -157,7 +170,10 @@ - (void)webView:(WKWebView *)webView { /* TODO: Sloppy parsing of 'Content-Security-Policy' value. */ NSRange sandbox = [value rangeOfString:@"sandbox"]; - if (sandbox.location != NSNotFound) + if (sandbox.location != NSNotFound + && (sandbox.location == 0 + || [value characterAtIndex:(sandbox.location - 1)] == ' ' + || [value characterAtIndex:(sandbox.location - 1)] == ';')) { NSRange allowScripts = [value rangeOfString:@"allow-scripts"]; if (allowScripts.location == NSNotFound @@ -181,6 +197,22 @@ - (WKWebView *)webView:(WKWebView *)webView return nil; } +/* Open panel for file upload. */ +- (void)webView:(WKWebView *)webView +runOpenPanelWithParameters:(WKOpenPanelParameters *)parameters +initiatedByFrame:(WKFrameInfo *)frame +completionHandler:(void (^)(NSArray<NSURL *> *URLs))completionHandler +{ + NSOpenPanel *openPanel = [NSOpenPanel openPanel]; + openPanel.canChooseFiles = YES; + openPanel.canChooseDirectories = NO; + openPanel.allowsMultipleSelection = parameters.allowsMultipleSelection; + if ([openPanel runModal] == NSModalResponseOK) + completionHandler (openPanel.URLs); + else + completionHandler (nil); +} + /* By forwarding mouse events to emacs view (frame) - Mouse click in webview selects the window contains the webview. - Correct mouse hand/arrow/I-beam is displayed (TODO: not perfect yet). diff --git a/src/xwidget.c b/src/xwidget.c index 60e8260377..c6a8218c56 100644 --- a/src/xwidget.c +++ b/src/xwidget.c @@ -255,6 +255,26 @@ store_xwidget_event_string (struct xwidget *xw, const char *eventname, kbd_buffer_store_event (&event); } +void +store_xwidget_response_callback_event (struct xwidget *xw, + const char *url, + const char *mimetype, + const char *filename) +{ + struct input_event event; + Lisp_Object xwl; + XSETXWIDGET (xwl, xw); + EVENT_INIT (event); + event.kind = XWIDGET_EVENT; + event.frame_or_window = Qnil; + event.arg = list5 (intern ("response-callback"), + xwl, + build_string (url), + build_string (mimetype), + build_string (filename)); + kbd_buffer_store_event (&event); +} + void store_xwidget_js_callback_event (struct xwidget *xw, Lisp_Object proc, -- 2.15.0 ^ permalink raw reply related [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2017-12-04 16:44 bug#29565: [PATCH] Support xwidget webkit for macOS X Jaesup Kwak ` (9 preceding siblings ...) 2017-12-21 4:12 ` bug#29565: [PATCH] Support file download and upload (Bug#29565) Jaesup Kwak @ 2018-03-30 11:48 ` Alan Third 2018-03-30 12:19 ` Jaesup Kwak 2019-09-28 23:52 ` Stefan Kangas 11 siblings, 1 reply; 41+ messages in thread From: Alan Third @ 2018-03-30 11:48 UTC (permalink / raw) To: Jaesup Kwak; +Cc: 29565 Jaesup Kwak <veshboo@gmail.com> writes: > I attached a patch to support xwidget webkit for macOS X, rebased onto > the latest master. Hi, I was wondering if there was any progress on this? Did you get your copyright assignment sorted? -- Alan Third ^ permalink raw reply [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2018-03-30 11:48 ` bug#29565: [PATCH] Support xwidget webkit for macOS X Alan Third @ 2018-03-30 12:19 ` Jaesup Kwak 0 siblings, 0 replies; 41+ messages in thread From: Jaesup Kwak @ 2018-03-30 12:19 UTC (permalink / raw) To: Alan Third; +Cc: 29565 [-- Attachment #1: Type: text/plain, Size: 432 bytes --] Yes, I did. And sorry That I think I will be busy in some months. 2018년 3월 30일 (금) 오후 8:48, Alan Third <alan@idiocy.org>님이 작성: > Jaesup Kwak <veshboo@gmail.com> writes: > > > I attached a patch to support xwidget webkit for macOS X, rebased onto > > the latest master. > > Hi, I was wondering if there was any progress on this? Did you get your > copyright assignment sorted? > -- > Alan Third > [-- Attachment #2: Type: text/html, Size: 767 bytes --] ^ permalink raw reply [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2017-12-04 16:44 bug#29565: [PATCH] Support xwidget webkit for macOS X Jaesup Kwak ` (10 preceding siblings ...) 2018-03-30 11:48 ` bug#29565: [PATCH] Support xwidget webkit for macOS X Alan Third @ 2019-09-28 23:52 ` Stefan Kangas 2020-08-10 13:55 ` Lars Ingebrigtsen 11 siblings, 1 reply; 41+ messages in thread From: Stefan Kangas @ 2019-09-28 23:52 UTC (permalink / raw) To: Alan Third; +Cc: 29565, Jaesup Kwak Alan Third <alan@idiocy.org> writes: > Jaesup Kwak <veshboo@gmail.com> writes: > >> I attached a patch to support xwidget webkit for macOS X, rebased onto >> the latest master. > > Hi, I was wondering if there was any progress on this? Did you get your > copyright assignment sorted? (In case someone stumbles across this in the bug tracker.) This work was continued by Sungbin Jo. I believe that the latest code is here: https://lists.gnu.org/archive/html/emacs-devel/2019-07/msg00435.html There were some roadblocks to merging this. See the following thread for a discussion: https://lists.gnu.org/archive/html/emacs-devel/2019-08/msg00033.html Best regards, Stefan Kangas ^ permalink raw reply [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2019-09-28 23:52 ` Stefan Kangas @ 2020-08-10 13:55 ` Lars Ingebrigtsen 2020-08-10 19:06 ` Alan Third 0 siblings, 1 reply; 41+ messages in thread From: Lars Ingebrigtsen @ 2020-08-10 13:55 UTC (permalink / raw) To: Stefan Kangas; +Cc: 29565, Alan Third, Jaesup Kwak, Sungbin Jo Stefan Kangas <stefan@marxist.se> writes: > This work was continued by Sungbin Jo. I believe that the latest code > is here: > > https://lists.gnu.org/archive/html/emacs-devel/2019-07/msg00435.html > > There were some roadblocks to merging this. See the following thread > for a discussion: > > https://lists.gnu.org/archive/html/emacs-devel/2019-08/msg00033.html If I understand this correctly, we're not merging Sungbin's patch because gcc doesn't understand the "block" thing in Objective C (yet)... but gcc can't compile the Emacs trunk currently either, so this isn't really a regression? I may be misunderstanding -- I've only skimmed the two threads. But if that's the case, this shouldn't really be something that hinders inclusion of Sungbin's patch anyway. Especially since the xwidget stuff is an optional feature. -- (domestic pets only, the antidote for overdose, milk.) bloggy blog: http://lars.ingebrigtsen.no ^ permalink raw reply [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2020-08-10 13:55 ` Lars Ingebrigtsen @ 2020-08-10 19:06 ` Alan Third 2020-08-11 11:04 ` Lars Ingebrigtsen 2020-08-12 2:26 ` Richard Stallman 0 siblings, 2 replies; 41+ messages in thread From: Alan Third @ 2020-08-10 19:06 UTC (permalink / raw) To: Lars Ingebrigtsen; +Cc: 29565, Stefan Kangas, Jaesup Kwak, Sungbin Jo On Mon, Aug 10, 2020 at 03:55:02PM +0200, Lars Ingebrigtsen wrote: > Stefan Kangas <stefan@marxist.se> writes: > > > This work was continued by Sungbin Jo. I believe that the latest code > > is here: > > > > https://lists.gnu.org/archive/html/emacs-devel/2019-07/msg00435.html > > > > There were some roadblocks to merging this. See the following thread > > for a discussion: > > > > https://lists.gnu.org/archive/html/emacs-devel/2019-08/msg00033.html > > If I understand this correctly, we're not merging Sungbin's patch > because gcc doesn't understand the "block" thing in Objective C > (yet)... but gcc can't compile the Emacs trunk currently either, so > this isn't really a regression? > > I may be misunderstanding -- I've only skimmed the two threads. > > But if that's the case, this shouldn't really be something that hinders > inclusion of Sungbin's patch anyway. Especially since the xwidget stuff > is an optional feature. I felt it was pretty clear that RMS did not want this code in Emacs. We've already had to remove code that used blocks, even though as you point out, GCC can't build Emacs on macOS anyway. I don't really understand the argument. If you wish to argue the point with RMS, go ahead. :) -- Alan Third ^ permalink raw reply [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2020-08-10 19:06 ` Alan Third @ 2020-08-11 11:04 ` Lars Ingebrigtsen 2020-08-11 15:22 ` Eli Zaretskii 2020-08-12 2:26 ` Richard Stallman 1 sibling, 1 reply; 41+ messages in thread From: Lars Ingebrigtsen @ 2020-08-11 11:04 UTC (permalink / raw) To: Alan Third; +Cc: 29565, Stefan Kangas, Jaesup Kwak, Sungbin Jo Alan Third <alan@idiocy.org> writes: > I felt it was pretty clear that RMS did not want this code in Emacs. > We've already had to remove code that used blocks, even though as you > point out, GCC can't build Emacs on macOS anyway. > > I don't really understand the argument. > > If you wish to argue the point with RMS, go ahead. :) rms isn't the Emacs maintainer, though, so it's up to Eli. Eli, what do you think? -- (domestic pets only, the antidote for overdose, milk.) bloggy blog: http://lars.ingebrigtsen.no ^ permalink raw reply [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2020-08-11 11:04 ` Lars Ingebrigtsen @ 2020-08-11 15:22 ` Eli Zaretskii 2020-08-11 16:26 ` Lars Ingebrigtsen 0 siblings, 1 reply; 41+ messages in thread From: Eli Zaretskii @ 2020-08-11 15:22 UTC (permalink / raw) To: Lars Ingebrigtsen, Richard Stallman Cc: 29565, stefan, alan, veshboo, pcr910303 > From: Lars Ingebrigtsen <larsi@gnus.org> > Cc: Stefan Kangas <stefan@marxist.se>, 29565@debbugs.gnu.org, Jaesup Kwak > <veshboo@gmail.com>, Sungbin Jo <pcr910303@icloud.com>, Eli Zaretskii > <eliz@gnu.org> > Date: Tue, 11 Aug 2020 13:04:17 +0200 > > Alan Third <alan@idiocy.org> writes: > > > I felt it was pretty clear that RMS did not want this code in Emacs. > > We've already had to remove code that used blocks, even though as you > > point out, GCC can't build Emacs on macOS anyway. > > > > I don't really understand the argument. > > > > If you wish to argue the point with RMS, go ahead. :) > > rms isn't the Emacs maintainer, though, so it's up to Eli. > > Eli, what do you think? I actually don't see where Richard said he didn't want this code in Emacs. The code doesn't implement any feature we don't have on free systems, so why does it matter what compiler is required for that? At worst, macOS users will not have xwidget webkit support, so what? ^ permalink raw reply [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2020-08-11 15:22 ` Eli Zaretskii @ 2020-08-11 16:26 ` Lars Ingebrigtsen 2020-08-11 18:28 ` Eli Zaretskii 2020-08-11 19:56 ` Alan Third 0 siblings, 2 replies; 41+ messages in thread From: Lars Ingebrigtsen @ 2020-08-11 16:26 UTC (permalink / raw) To: Eli Zaretskii; +Cc: alan, veshboo, Richard Stallman, stefan, 29565, pcr910303 Eli Zaretskii <eliz@gnu.org> writes: > I actually don't see where Richard said he didn't want this code in > Emacs. The code doesn't implement any feature we don't have on free > systems, so why does it matter what compiler is required for that? At > worst, macOS users will not have xwidget webkit support, so what? Indeed. The patch no longer applies cleanly to Emacs 28, so I've respun it, and there's no adverse effects on Debian, at least. I've included it below for reference (since I think all proposed patches should be in the bug tracker, even if this is very long). There's a couple of compilation warnings, but if we decide to apply this, I'll fix those before pushing. There's one thing that doesn't work, though (so it's commented out below): + CHECK_RANGED_INTEGER (rel_pos, -1, 1); /* -1, 0, 1 */ Is that a macro that Emacs used to have? I can't find it now. And... I'm unable to test this on Macos, because Homebrew doesn't offer webkitgtk, apparently. diff --git a/configure.ac b/configure.ac index c9aa076eb3..7ce64f79ca 100644 --- a/configure.ac +++ b/configure.ac @@ -489,7 +489,7 @@ AC_DEFUN [with_file_notification=$with_features]) OPTION_DEFAULT_OFF([xwidgets], - [enable use of some gtk widgets in Emacs buffers (requires gtk3)]) + [enable use of xwidgets in Emacs buffers (requires gtk3 or macOS Cocoa)]) ## For the times when you want to build Emacs but don't have ## a suitable makeinfo, and can live without the manuals. @@ -2754,20 +2754,34 @@ AC_DEFUN dnl Enable xwidgets if GTK3 and WebKitGTK+ are available. +dnl Enable xwidgets if macOS Cocoa and WebKit framework are available. HAVE_XWIDGETS=no XWIDGETS_OBJ= if test "$with_xwidgets" != "no"; then - test "$USE_GTK_TOOLKIT" = "GTK3" && test "$window_system" != "none" || - AC_MSG_ERROR([xwidgets requested but gtk3 not used.]) + if test "$USE_GTK_TOOLKIT" = "GTK3" && test "$window_system" != "none"; then + WEBKIT_REQUIRED=2.12 + WEBKIT_MODULES="webkit2gtk-4.0 >= $WEBKIT_REQUIRED" + EMACS_CHECK_MODULES([WEBKIT], [$WEBKIT_MODULES]) + HAVE_XWIDGETS=$HAVE_WEBKIT + XWIDGETS_OBJ="xwidget.o" + elif test "${NS_IMPL_COCOA}" = "yes"; then + dnl FIXME: Check framework WebKit2 + dnl WEBKIT_REQUIRED=M.m.p + WEBKIT_LIBS="-Wl,-framework -Wl,WebKit" + WEBKIT_CFLAGS="-I/System/Library/Frameworks/WebKit.framework/Headers" + HAVE_WEBKIT="yes" + HAVE_XWIDGETS=$HAVE_WEBKIT + XWIDGETS_OBJ="xwidget.o" + NS_OBJC_OBJ="$NS_OBJC_OBJ nsxwidget.o" + dnl Update NS_OBJC_OBJ with added nsxwidget.o + AC_SUBST(NS_OBJC_OBJ) + else + AC_MSG_ERROR([xwidgets requested, it requires GTK3 as X window toolkit or macOS Cocoa as window system.]) + fi - WEBKIT_REQUIRED=2.12 - WEBKIT_MODULES="webkit2gtk-4.0 >= $WEBKIT_REQUIRED" - EMACS_CHECK_MODULES([WEBKIT], [$WEBKIT_MODULES]) - HAVE_XWIDGETS=$HAVE_WEBKIT test $HAVE_XWIDGETS = yes || - AC_MSG_ERROR([xwidgets requested but WebKitGTK+ not found.]) + AC_MSG_ERROR([xwidgets requested but WebKitGTK+ or WebKit framework not found.]) - XWIDGETS_OBJ=xwidget.o AC_DEFINE([HAVE_XWIDGETS], 1, [Define to 1 if you have xwidgets support.]) fi AC_SUBST(XWIDGETS_OBJ) @@ -5688,7 +5702,7 @@ AC_DEFUN Does Emacs directly use zlib? ${HAVE_ZLIB} Does Emacs have dynamic modules support? ${HAVE_MODULES} Does Emacs use toolkit scroll bars? ${USE_TOOLKIT_SCROLL_BARS} - Does Emacs support Xwidgets (requires gtk3)? ${HAVE_XWIDGETS} + Does Emacs support Xwidgets? ${HAVE_XWIDGETS} Does Emacs have threading support in lisp? ${threads_enabled} Does Emacs support the portable dumper? ${with_pdumper} Does Emacs support legacy unexec dumping? ${with_unexec} diff --git a/lisp/xwidget.el b/lisp/xwidget.el index a4c15a1e26..6bc16f744a 100644 --- a/lisp/xwidget.el +++ b/lisp/xwidget.el @@ -39,9 +39,10 @@ (declare-function xwidget-buffer "xwidget.c" (xwidget)) (declare-function xwidget-size-request "xwidget.c" (xwidget)) (declare-function xwidget-resize "xwidget.c" (xwidget new-width new-height)) -(declare-function xwidget-webkit-execute-script "xwidget.c" - (xwidget script &optional callback)) +(declare-function xwidget-webkit-uri "xwidget.c" (xwidget)) +(declare-function xwidget-webkit-title "xwidget.c" (xwidget)) (declare-function xwidget-webkit-goto-uri "xwidget.c" (xwidget uri)) +(declare-function xwidget-webkit-goto-history "xwidget.c" (xwidget rel-pos)) (declare-function xwidget-webkit-zoom "xwidget.c" (xwidget factor)) (declare-function xwidget-plist "xwidget.c" (xwidget)) (declare-function set-xwidget-plist "xwidget.c" (xwidget plist)) @@ -51,6 +52,10 @@ (declare-function get-buffer-xwidgets "xwidget.c" (buffer)) (declare-function xwidget-query-on-exit-flag "xwidget.c" (xwidget)) +(defgroup xwidget nil + "Displaying native widgets in Emacs buffers." + :group 'widgets) + (defun xwidget-insert (pos type title width height &optional args) "Insert an xwidget at position POS. Supply the xwidget's TYPE, TITLE, WIDTH, and HEIGHT. @@ -78,6 +83,8 @@ xwidget-at ;;; webkit support (require 'browse-url) (require 'image-mode);;for some image-mode alike functionality +(require 'seq) +(require 'url-handlers) ;;;###autoload (defun xwidget-webkit-browse-url (url &optional new-session) @@ -99,6 +106,24 @@ xwidget-webkit-browse-url (xwidget-webkit-new-session url) (xwidget-webkit-goto-url url)))) +(defun xwidget-webkit-split-below () + "Clone current URL into a new widget place in new window below. +Get the URL of current session, then browse to the URL +in `split-window-below' with a new xwidget webkit session." + (interactive) + (let ((url (xwidget-webkit-current-url))) + (with-selected-window (split-window-below) + (xwidget-webkit-new-session url)))) + +(defun xwidget-webkit-split-right () + "Clone current URL into a new widget place in new window right. +Get the URL of current session, then browse to the URL +in `split-window-right' with a new xwidget webkit session." + (interactive) + (let ((url (xwidget-webkit-current-url))) + (with-selected-window (split-window-right) + (xwidget-webkit-new-session url)))) + ;;todo. ;; - check that the webkit support is compiled in (defvar xwidget-webkit-mode-map @@ -106,34 +131,42 @@ xwidget-webkit-mode-map (define-key map "g" 'xwidget-webkit-browse-url) (define-key map "a" 'xwidget-webkit-adjust-size-dispatch) (define-key map "b" 'xwidget-webkit-back) + (define-key map "f" 'xwidget-webkit-forward) (define-key map "r" 'xwidget-webkit-reload) (define-key map "t" (lambda () (interactive) (message "o"))) ;FIXME: ?!? (define-key map "\C-m" 'xwidget-webkit-insert-string) - (define-key map "w" 'xwidget-webkit-current-url) + (define-key map "w" 'xwidget-webkit-current-url-message-kill) (define-key map "+" 'xwidget-webkit-zoom-in) (define-key map "-" 'xwidget-webkit-zoom-out) ;;similar to image mode bindings (define-key map (kbd "SPC") 'xwidget-webkit-scroll-up) + (define-key map (kbd "S-SPC") 'xwidget-webkit-scroll-down) (define-key map (kbd "DEL") 'xwidget-webkit-scroll-down) - (define-key map [remap scroll-up] 'xwidget-webkit-scroll-up) + (define-key map [remap scroll-up] 'xwidget-webkit-scroll-up-line) (define-key map [remap scroll-up-command] 'xwidget-webkit-scroll-up) - (define-key map [remap scroll-down] 'xwidget-webkit-scroll-down) + (define-key map [remap scroll-down] 'xwidget-webkit-scroll-down-line) (define-key map [remap scroll-down-command] 'xwidget-webkit-scroll-down) (define-key map [remap forward-char] 'xwidget-webkit-scroll-forward) (define-key map [remap backward-char] 'xwidget-webkit-scroll-backward) (define-key map [remap right-char] 'xwidget-webkit-scroll-forward) (define-key map [remap left-char] 'xwidget-webkit-scroll-backward) - (define-key map [remap previous-line] 'xwidget-webkit-scroll-down) - (define-key map [remap next-line] 'xwidget-webkit-scroll-up) + (define-key map [remap previous-line] 'xwidget-webkit-scroll-down-line) + (define-key map [remap next-line] 'xwidget-webkit-scroll-up-line) ;; (define-key map [remap move-beginning-of-line] 'image-bol) ;; (define-key map [remap move-end-of-line] 'image-eol) (define-key map [remap beginning-of-buffer] 'xwidget-webkit-scroll-top) (define-key map [remap end-of-buffer] 'xwidget-webkit-scroll-bottom) + + ;; For macOS xwidget webkit, we don't support multiple views for a + ;; model, instead, create a new session and model behind the scene. + (when (memq window-system '(mac ns)) + (define-key map [remap split-window-below] 'xwidget-webkit-split-below) + (define-key map [remap split-window-right] 'xwidget-webkit-split-right)) map) "Keymap for `xwidget-webkit-mode'.") @@ -147,33 +180,69 @@ xwidget-webkit-zoom-out (interactive) (xwidget-webkit-zoom (xwidget-webkit-current-session) -0.1)) -(defun xwidget-webkit-scroll-up () - "Scroll webkit up." - (interactive) +(defun xwidget-webkit-scroll-up (&optional n) + "Scroll webkit up by N pixels or window height pixels. +Stop if the bottom edge of the page is reached. +If N is omitted or nil, scroll up by window height pixels." + (interactive "P") (xwidget-webkit-execute-script (xwidget-webkit-current-session) - "window.scrollBy(0, 50);")) - -(defun xwidget-webkit-scroll-down () - "Scroll webkit down." - (interactive) + (format "window.scrollBy(0, %d);" + (or n (xwidget-window-inside-pixel-height (selected-window)))))) + +(defun xwidget-webkit-scroll-down (&optional n) + "Scroll webkit down by N pixels or window height pixels. +Stop if the top edge of the page is reached. +If N is omitted or nil, scroll down by window height pixels." + (interactive "P") (xwidget-webkit-execute-script (xwidget-webkit-current-session) - "window.scrollBy(0, -50);")) - -(defun xwidget-webkit-scroll-forward () - "Scroll webkit forwards." - (interactive) + (format "window.scrollBy(0, -%d);" + (or n (xwidget-window-inside-pixel-height (selected-window)))))) + +(defcustom xwidget-webkit-scroll-line-height 50 + "Default line height in pixels to scroll xwidget webkit." + :type 'integer) + +(defcustom xwidget-webkit-scroll-char-width 10 + "Default char height in pixels to scroll xwidget webkit." + :type 'integer) + +(defun xwidget-webkit-scroll-up-line (&optional n) + "Scroll webkit up by N lines. +The height of line is `xwidget-webkit-scroll-line-height' pixels. +Stop if the bottom edge of the page is reached. +If N is omitted or nil, scroll up by one line." + (interactive "p") + (xwidget-webkit-scroll-up (* n xwidget-webkit-scroll-line-height))) + +(defun xwidget-webkit-scroll-down-line (&optional n) + "Scroll webkit down by N lines. +The height of line is `xwidget-webkit-scroll-line-height' pixels. +Stop if the top edge of the page is reached. +If N is omitted or nil, scroll down by one line." + (interactive "p") + (xwidget-webkit-scroll-down (* n xwidget-webkit-scroll-line-height))) + +(defun xwidget-webkit-scroll-forward (&optional n) + "Scroll webkit forwards by N chars. +The width of char is `xwidget-webkit-scroll-char-width' pixels. +If N is ommited or nil, scroll forwards by one char." + (interactive "p") (xwidget-webkit-execute-script (xwidget-webkit-current-session) - "window.scrollBy(50, 0);")) - -(defun xwidget-webkit-scroll-backward () - "Scroll webkit backwards." - (interactive) + (format "window.scrollBy(%d, 0);" + (* n xwidget-webkit-scroll-char-width)))) + +(defun xwidget-webkit-scroll-backward (&optional n) + "Scroll webkit backwards by N chars. +The width of char is `xwidget-webkit-scroll-char-width' pixels. +If N is ommited or nil, scroll backwards by one char." + (interactive "p") (xwidget-webkit-execute-script (xwidget-webkit-current-session) - "window.scrollBy(-50, 0);")) + (format "window.scrollBy(-%d, 0);" + (* n xwidget-webkit-scroll-char-width)))) (defun xwidget-webkit-scroll-top () "Scroll webkit to the very top." @@ -187,7 +256,7 @@ xwidget-webkit-scroll-bottom (interactive) (xwidget-webkit-execute-script (xwidget-webkit-current-session) - "window.scrollTo(pageXOffset, window.document.body.clientHeight);")) + "window.scrollTo(pageXOffset, window.document.body.scrollHeight);")) ;; The xwidget event needs to go into a higher level handler ;; since the xwidget can generate an event even if it's offscreen. @@ -218,43 +287,141 @@ xwidget-webkit-callback "error: callback called for xwidget with dead buffer") (with-current-buffer (xwidget-buffer xwidget) (cond ((eq xwidget-event-type 'load-changed) - (xwidget-webkit-execute-script - xwidget "document.title" - (lambda (title) - (xwidget-log "webkit finished loading: '%s'" title) - ;;TODO - check the native/internal scroll - ;;(xwidget-adjust-size-to-content xwidget) - (xwidget-webkit-adjust-size-to-window xwidget) - (rename-buffer (format "*xwidget webkit: %s *" title)))) - (pop-to-buffer (current-buffer))) + ;; We do not change selected window for the finish of loading a page. + ;; And do not adjust webkit size to window here, the selected window + ;; can be the mini-buffer window unwantedly. + (let ((title (xwidget-webkit-title xwidget))) + (xwidget-log "webkit finished loading: %s" title) + (rename-buffer (format "*xwidget webkit: %s *" title) t))) ((eq xwidget-event-type 'decide-policy) (let ((strarg (nth 3 last-input-event))) (if (string-match ".*#\\(.*\\)" strarg) (xwidget-webkit-show-id-or-named-element xwidget (match-string 1 strarg))))) + ;; TODO: Response handling other than download. + ((eq xwidget-event-type 'download-callback) + (let ((url (nth 3 last-input-event)) + (mime-type (nth 4 last-input-event)) + (file-name (nth 5 last-input-event))) + (xwidget-webkit-save-as-file url mime-type file-name))) ((eq xwidget-event-type 'javascript-callback) (let ((proc (nth 3 last-input-event)) (arg (nth 4 last-input-event))) - (funcall proc arg))) + ;; Some javascript return vector as result + (funcall proc (if (vectorp arg) (seq-into arg 'list) arg)))) (t (xwidget-log "unhandled event:%s" xwidget-event-type)))))) (defvar bookmark-make-record-function) +(defvar isearch-search-fun-function) +(when (memq window-system '(mac ns)) + (defcustom xwidget-webkit-enable-plugins nil + "Enable plugins for xwidget webkit. +If non-nil, plugins are enabled. Otherwise, disabled." + :type 'boolean)) + (define-derived-mode xwidget-webkit-mode special-mode "xwidget-webkit" "Xwidget webkit view mode." (setq buffer-read-only t) + (setq cursor-type nil) (setq-local bookmark-make-record-function #'xwidget-webkit-bookmark-make-record) + (setq-local isearch-search-fun-function + #'xwidget-webkit-search-fun-function) + (setq-local isearch-lazy-highlight nil) ;; Keep track of [vh]scroll when switching buffers (image-mode-setup-winprops)) +;;; Download, save as file. + +(defcustom xwidget-webkit-download-dir "~/Downloads/" + "Directory where download file saved." + :type 'string) + +(defun xwidget-webkit-save-as-file (url mime-type &optional file-name) + "For XWIDGET webkit, save URL resource of MIME-TYPE as FILE-NAME." + (let ((save-name (read-file-name + (format "Save '%s' file as: " mime-type) + xwidget-webkit-download-dir + (expand-file-name + file-name + xwidget-webkit-download-dir)))) + (if (file-directory-p save-name) + (setq save-name + (expand-file-name (file-name-nondirectory file-name) save-name))) + (setq xwidget-webkit-download-dir (file-name-directory save-name)) + (url-copy-file url save-name t))) + +;;; Bookmarks integration + +(defcustom xwidget-webkit-bookmark-jump-new-session nil + "Control bookmark jump to use new session or not. +If non-nil, it will use a new session. Otherwise, it will use +`xwidget-webkit-last-session'. When you set this variable to +nil, consider further customization with +`xwidget-webkit-last-session-buffer'." + :type 'boolean) + (defun xwidget-webkit-bookmark-make-record () "Integrate Emacs bookmarks with the webkit xwidget." (nconc (bookmark-make-record-default t t) - `((page . ,(xwidget-webkit-current-url)) - (handler . (lambda (bmk) (browse-url - (bookmark-prop-get bmk 'page))))))) - + `((page . ,(xwidget-webkit-current-url)) + (handler . (lambda (bmk) + (xwidget-webkit-browse-url + (bookmark-prop-get bmk 'page) + xwidget-webkit-bookmark-jump-new-session)))))) + +;;; Search text in page + +;; Initialize last search text length variable when isearch starts +(defvar xwidget-webkit-isearch-last-length 0) +(add-hook 'isearch-mode-hook + (lambda () + (setq xwidget-webkit-isearch-last-length 0))) + +;; This is minimal. Regex and incremental search will be nice +(defvar xwidget-webkit-search-js " +var xwSearchForward = %s; +var xwSearchRepeat = %s; +var xwSearchString = '%s'; +if (window.getSelection() && !window.getSelection().isCollapsed) { + if (xwSearchRepeat) { + if (xwSearchForward) + window.getSelection().collapseToEnd(); + else + window.getSelection().collapseToStart(); + } else { + if (xwSearchForward) + window.getSelection().collapseToStart(); + else { + var sel = window.getSelection(); + window.getSelection().collapse(sel.focusNode, sel.focusOffset + 1); + } + } +} +window.find(xwSearchString, false, !xwSearchForward, true, false, true); +") + +(defun xwidget-webkit-search-fun-function () + "Return the function which perform the search in xwidget webkit." + (lambda (string &optional _bound _noerror _count) + (let* ((current-length (length string)) + (search-forward (if isearch-forward "true" "false")) + (search-repeat + (if (eq current-length xwidget-webkit-isearch-last-length) + "true" + "false"))) + (setq xwidget-webkit-isearch-last-length current-length) + (xwidget-webkit-execute-script + (xwidget-webkit-current-session) + (format xwidget-webkit-search-js + search-forward + search-repeat + (regexp-quote string))) + ;; Unconditionally avoid 'Failing I-search ...' + (goto-char (if isearch-forward (point-min) (point-max)))))) + +;;; xwidget webkit session (defvar xwidget-webkit-last-session-buffer nil) @@ -302,7 +469,7 @@ xwidget-webkit-activeelement-js" " - "javascript that finds the active element." + "Javascript that finds the active element." ;; Yes it's ugly, because: ;; - there is apparently no way to find the active frame other than recursion ;; - the js "for each" construct misbehaved on the "frames" collection @@ -312,25 +479,29 @@ xwidget-webkit-activeelement-js" ) (defun xwidget-webkit-insert-string () - "Prompt for a string and insert it in the active field in the -current webkit widget." + "Insert string into the active field in the current webkit widget." ;; Read out the string in the field first and provide for edit. (interactive) + ;; As the prompt needs to change based on the asynchronous execution results, + ;; the function must handle the string itself. (let ((xww (xwidget-webkit-current-session))) + (xwidget-webkit-execute-script xww (concat xwidget-webkit-activeelement-js " (function () { var res = findactiveelement(document); - return [res.value, res.type]; + if (res) + return [res.value, res.type]; })();") (lambda (field) + "Prompt a string for the FIELD and insert in the active input." (let ((str (pcase field - (`[,val "text"] + (`(,val "text") (read-string "Text: " val)) - (`[,val "password"] + (`(,val "password") (read-passwd "Password: " nil val)) - (`[,val "textarea"] + (`(,val "textarea") (xwidget-webkit-begin-edit-textarea xww val))))) (xwidget-webkit-execute-script xww @@ -443,11 +614,23 @@ xwidget-webkit-adjust-size-dispatch (ignore-errors (recenter-top-bottom))) +;; Utility functions, wanted in `window.el' + +(defun xwidget-window-inside-pixel-width (window) + "Return Emacs WINDOW body width in pixel." + (let ((edges (window-inside-pixel-edges window))) + (- (nth 2 edges) (nth 0 edges)))) + +(defun xwidget-window-inside-pixel-height (window) + "Return Emacs WINDOW body height in pixel." + (let ((edges (window-inside-pixel-edges window))) + (- (nth 3 edges) (nth 1 edges)))) + (defun xwidget-webkit-adjust-size-to-window (xwidget &optional window) "Adjust the size of the webkit XWIDGET to fit the WINDOW." (xwidget-resize xwidget - (window-pixel-width window) - (window-pixel-height window))) + (xwidget-window-inside-pixel-width window) + (xwidget-window-inside-pixel-height window))) (defun xwidget-webkit-adjust-size (w h) "Manually set webkit size to width W, height H." @@ -487,10 +670,14 @@ xwidget-webkit-new-session (get-buffer-create bufname))) ;; The xwidget id is stored in a text property, so we need to have ;; at least character in this buffer. - (insert " ") - (setq xw (xwidget-insert 1 'webkit bufname - (window-pixel-width) - (window-pixel-height))) + ;; Insert invisible url, good default for next `g' to browse url. + (let ((start (point))) + (insert url) + (put-text-property start (+ start (length url)) 'invisible t) + (setq xw (xwidget-insert + start 'webkit bufname + (xwidget-window-inside-pixel-width (selected-window)) + (xwidget-window-inside-pixel-height (selected-window))))) (xwidget-put xw 'callback callback) (xwidget-webkit-mode) (xwidget-webkit-goto-uri (xwidget-webkit-last-session) url))) @@ -506,23 +693,27 @@ xwidget-webkit-goto-url (defun xwidget-webkit-back () "Go back in history." (interactive) - (xwidget-webkit-execute-script (xwidget-webkit-current-session) - "history.go(-1);")) + (xwidget-webkit-goto-history (xwidget-webkit-current-session) -1)) + +(defun xwidget-webkit-forward () + "Go forward in history." + (interactive) + (xwidget-webkit-goto-history (xwidget-webkit-current-session) 1)) (defun xwidget-webkit-reload () - "Reload current url." + "Reload current URL." (interactive) - (xwidget-webkit-execute-script (xwidget-webkit-current-session) - "history.go(0);")) + (xwidget-webkit-goto-history (xwidget-webkit-current-session) 0)) (defun xwidget-webkit-current-url () - "Get the webkit url and place it on the kill-ring." + "Get the current xwidget webkit URL." (interactive) - (xwidget-webkit-execute-script - (xwidget-webkit-current-session) - "document.URL" (lambda (rv) - (let ((url (kill-new (or rv "")))) - (message "url: %s" url))))) + (xwidget-webkit-uri (xwidget-webkit-current-session))) + +(defun xwidget-webkit-current-url-message-kill () + "Display the current xwidget webkit URL and place it on the `kill-ring'." + (interactive) + (message "URL: %s" (kill-new (or (xwidget-webkit-current-url) "")))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun xwidget-webkit-get-selection (proc) @@ -533,10 +724,9 @@ xwidget-webkit-get-selection proc)) (defun xwidget-webkit-copy-selection-as-kill () - "Get the webkit selection and put it on the kill-ring." + "Get the webkit selection and put it on the `kill-ring'." (interactive) - (xwidget-webkit-get-selection (lambda (selection) (kill-new selection)))) - + (xwidget-webkit-get-selection #'kill-new)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Xwidget plist management (similar to the process plist functions) diff --git a/nextstep/templates/Info.plist.in b/nextstep/templates/Info.plist.in index f791ade7b9..1f074b0457 100644 --- a/nextstep/templates/Info.plist.in +++ b/nextstep/templates/Info.plist.in @@ -675,8 +675,16 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. </array> <key>NSAppleScriptEnabled</key> <string>YES</string> - <key>NSAppleEventsUsageDescription</key> - <string>Emacs requires permission to send AppleEvents to other applications.</string> + <key>NSAppleEventsUsageDescription</key> + <string>Emacs requires permission to send AppleEvents to other applications.</string> + <!-- For xwidget webkit to browse remote url, + but this set no restriction at all. Consult apple's documentation + for detail information about `NSApplicationDefinedMask'. --> + <key>NSAppTransportSecurity</key> + <dict> + <key>NSAllowsArbitraryLoads</key> + <true/> + </dict> <key>NSDesktopFolderUsageDescription</key> <string>Emacs requires permission to access the Desktop folder.</string> <key>NSDocumentsFolderUsageDescription</key> diff --git a/src/Makefile.in b/src/Makefile.in index 7141f16ec2..c5fb2ea3ab 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -433,6 +433,7 @@ SOME_MACHINE_OBJECTS = xterm.o xfns.o xmenu.o xselect.o xrdb.o xsmfns.o fringe.o image.o \ fontset.o dbusbind.o cygw32.o \ nsterm.o nsfns.o nsmenu.o nsselect.o nsimage.o nsfont.o macfont.o \ + nsxwidget.o \ w32.o w32console.o w32cygwinx.o w32fns.o w32heap.o w32inevt.o w32notify.o \ w32menu.o w32proc.o w32reg.o w32select.o w32term.o w32xfns.o \ w16select.o widget.o xfont.o ftfont.o xftfont.o gtkutil.o \ diff --git a/src/emacs.c b/src/emacs.c index d31fa2cb28..cb04de4aab 100644 --- a/src/emacs.c +++ b/src/emacs.c @@ -1860,7 +1860,6 @@ main (int argc, char **argv) syms_of_xfns (); syms_of_xmenu (); syms_of_fontset (); - syms_of_xwidget (); syms_of_xsettings (); #ifdef HAVE_X_SM syms_of_xsmfns (); @@ -1937,6 +1936,7 @@ main (int argc, char **argv) #endif /* HAVE_W32NOTIFY */ #endif /* WINDOWSNT */ + syms_of_xwidget (); syms_of_threads (); syms_of_profiler (); syms_of_pdumper (); diff --git a/src/nsterm.m b/src/nsterm.m index 572b859a98..03dfa157a5 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -49,6 +49,7 @@ Updated by Christian Limpach (chris@nice.ch) #include "nsterm.h" #include "systime.h" #include "character.h" +#include "xwidget.h" #include "fontset.h" #include "composite.h" #include "ccl.h" @@ -2600,7 +2601,7 @@ so some key presses (TAB) are swallowed by the system. */ } static int -ns_note_mouse_movement (struct frame *frame, CGFloat x, CGFloat y) +ns_note_mouse_movement (struct frame *frame, CGFloat x, CGFloat y, BOOL dragging) /* ------------------------------------------------------------------------ Called by EmacsView on mouseMovement events. Passes on to emacs mainstream code if we moved off of a rect of interest @@ -2609,17 +2610,24 @@ so some key presses (TAB) are swallowed by the system. */ { struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame); NSRect *r; + BOOL force_update = NO; // NSTRACE ("note_mouse_movement"); dpyinfo->last_mouse_motion_frame = frame; r = &dpyinfo->last_mouse_glyph; + /* If the last rect is too large (ex, xwidget webkit), update at + every move, or resizing by dragging modeline or vertical split is + very hard to make its way. */ + if (dragging && (r->size.width > 32 || r->size.height > 32)) + force_update = YES; + /* Note, this doesn't get called for enter/leave, since we don't have a position. Those are taken care of in the corresponding NSView methods. */ - /* Has movement gone beyond last rect we were tracking? */ - if (x < r->origin.x || x >= r->origin.x + r->size.width + /* Has movement gone beyond last rect we were tracking? */ + if (force_update || x < r->origin.x || x >= r->origin.x + r->size.width || y < r->origin.y || y >= r->origin.y + r->size.height) { ns_update_begin (frame); @@ -4368,6 +4376,10 @@ overwriting cursor (usually when cursor on a tab) */ ns_unfocus (s->f); break; + case XWIDGET_GLYPH: + x_draw_xwidget_glyph_string (s); + break; + case STRETCH_GLYPH: ns_dumpglyphs_stretch (s); break; @@ -7065,6 +7077,7 @@ - (void)mouseMoved: (NSEvent *)e struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe); Lisp_Object frame; NSPoint pt; + BOOL dragging; NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "[EmacsView mouseMoved:]"); @@ -7107,7 +7120,8 @@ - (void)mouseMoved: (NSEvent *)e last_mouse_window = window; } - if (!ns_note_mouse_movement (emacsframe, pt.x, pt.y)) + dragging = (e.type == NSEventTypeLeftMouseDragged); + if (!ns_note_mouse_movement (emacsframe, pt.x, pt.y, dragging)) help_echo_string = previous_help_echo_string; XSETFRAME (frame, emacsframe); diff --git a/src/nsxwidget.h b/src/nsxwidget.h new file mode 100644 index 0000000000..6af5fe5a4d --- /dev/null +++ b/src/nsxwidget.h @@ -0,0 +1,80 @@ +/* Header for NS Cocoa part of xwidget and webkit widget. + +Copyright (C) 2011-2017 Free Software Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs 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. + +GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ + +#ifndef NSXWIDGET_H_INCLUDED +#define NSXWIDGET_H_INCLUDED + +/* This file can be included from non-objc files through 'xwidget.h'. */ +#ifdef __OBJC__ +#import <AppKit/NSView.h> +#endif + +#include "dispextern.h" +#include "lisp.h" +#include "xwidget.h" + +/* Functions for xwidget webkit. */ + +bool nsxwidget_is_web_view (struct xwidget *xw); +Lisp_Object nsxwidget_webkit_uri (struct xwidget *xw); +Lisp_Object nsxwidget_webkit_title (struct xwidget *xw); +void nsxwidget_webkit_goto_uri (struct xwidget *xw, const char *uri); +void nsxwidget_webkit_goto_history (struct xwidget *xw, int rel_pos); +void nsxwidget_webkit_zoom (struct xwidget *xw, double zoom_change); +void nsxwidget_webkit_execute_script (struct xwidget *xw, const char *script, + Lisp_Object fun); + +/* Functions for xwidget model. */ + +#ifdef __OBJC__ +@interface XwWindow : NSView +@property struct xwidget *xw; +@end +#endif + +void nsxwidget_init (struct xwidget *xw); +void nsxwidget_kill (struct xwidget *xw); +void nsxwidget_resize (struct xwidget *xw); +Lisp_Object nsxwidget_get_size (struct xwidget *xw); + +/* Functions for xwidget view. */ + +#ifdef __OBJC__ +@interface XvWindow : NSView +@property struct xwidget *xw; +@property struct xwidget_view *xv; +@end +#endif + +void nsxwidget_init_view (struct xwidget_view *xv, + struct xwidget *xww, + struct glyph_string *s, + int x, int y); +void nsxwidget_delete_view (struct xwidget_view *xv); + +void nsxwidget_show_view (struct xwidget_view *xv); +void nsxwidget_hide_view (struct xwidget_view *xv); +void nsxwidget_resize_view (struct xwidget_view *xv, + int widget, int height); + +void nsxwidget_move_view (struct xwidget_view *xv, int x, int y); +void nsxwidget_move_widget_in_view (struct xwidget_view *xv, int x, int y); +void nsxwidget_set_needsdisplay (struct xwidget_view *xv); + +#endif /* NSXWIDGET_H_INCLUDED */ diff --git a/src/nsxwidget.m b/src/nsxwidget.m new file mode 100644 index 0000000000..8d8a92d09c --- /dev/null +++ b/src/nsxwidget.m @@ -0,0 +1,611 @@ +/* NS Cocoa part implementation of xwidget and webkit widget. + +Copyright (C) 1989, 1992-1994, 2005-2006, 2008-2017 Free Software +Foundation, Inc. + +This file is part of GNU Emacs. + +GNU Emacs 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. + +GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ + +#include <config.h> + +#include "lisp.h" +#include "blockinput.h" +#include "dispextern.h" +#include "buffer.h" +#include "frame.h" +#include "nsterm.h" +#include "xwidget.h" + +#import <AppKit/AppKit.h> +#import <WebKit/WebKit.h> + +/* Thoughts on NS Cocoa xwidget and webkit2: + + Webkit2 process architecture seems to be very hostile for offscreen + rendering techniques, which is used by GTK xwiget implementation; + Specifically NSView level view sharing / copying is not working. + + *** So only one view can be associcated with a model. *** + + With this decision, implementation is plain and can expect best out + of webkit2's rationale. But process and session structures will + diverge from GTK xwiget. Though, cosmetically similar usages can + be presented and will be preferred, if agreeable. + + For other widget types, OSR seems possible, but will not care for a + while. */ + +/* Xwidget webkit. */ + +@interface XwWebView : WKWebView +<WKNavigationDelegate, WKUIDelegate, WKScriptMessageHandler> +@property struct xwidget *xw; +/* Map url to whether javascript is blocked by + 'Content-Security-Policy' sandbox without allow-scripts. */ +@property(retain) NSMutableDictionary *urlScriptBlocked; +@end +@implementation XwWebView : WKWebView + +- (id)initWithFrame:(CGRect)frame + configuration:(WKWebViewConfiguration *)configuration + xwidget:(struct xwidget *)xw +{ + /* Script controller to add script message handler and user script. */ + WKUserContentController *scriptor = [[WKUserContentController alloc] init]; + configuration.userContentController = scriptor; + + /* Enable inspect element context menu item for debugging. */ + [configuration.preferences setValue:@YES + forKey:@"developerExtrasEnabled"]; + + Lisp_Object enablePlugins = + Fintern (build_string ("xwidget-webkit-enable-plugins"), Qnil); + if (!EQ (Fsymbol_value (enablePlugins), Qnil)) + configuration.preferences.plugInsEnabled = YES; + + self = [super initWithFrame:frame configuration:configuration]; + if (self) + { + self.xw = xw; + self.urlScriptBlocked = [[NSMutableDictionary alloc] init]; + self.navigationDelegate = self; + self.UIDelegate = self; + self.customUserAgent = + @"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 addUserScript:[[WKUserScript alloc] + initWithSource:xwScript + injectionTime: + WKUserScriptInjectionTimeAtDocumentStart + forMainFrameOnly:NO]]; + } + return self; +} + +#if 0 +/* Non ARC - just to check lifecycle. */ +- (void)dealloc +{ + NSLog (@"XwWebView dealloc"); + [super dealloc]; +} +#endif + +- (void)webView:(WKWebView *)webView +didFinishNavigation:(WKNavigation *)navigation +{ + if (EQ (Fbuffer_live_p (self.xw->buffer), Qt)) + store_xwidget_event_string (self.xw, "load-changed", ""); +} + +- (void)webView:(WKWebView *)webView +decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction +decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler +{ + switch (navigationAction.navigationType) { + case WKNavigationTypeLinkActivated: + decisionHandler (WKNavigationActionPolicyAllow); + break; + default: + // decisionHandler (WKNavigationActionPolicyCancel); + decisionHandler (WKNavigationActionPolicyAllow); + break; + } +} + +- (void)webView:(WKWebView *)webView +decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse +decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler +{ + if (!navigationResponse.canShowMIMEType) + { + NSString *url = navigationResponse.response.URL.absoluteString; + NSString *mimetype = navigationResponse.response.MIMEType; + NSString *filename = navigationResponse.response.suggestedFilename; + decisionHandler (WKNavigationResponsePolicyCancel); + store_xwidget_download_callback_event (self.xw, + url.UTF8String, + mimetype.UTF8String, + filename.UTF8String); + return; + } + decisionHandler (WKNavigationResponsePolicyAllow); + + self.urlScriptBlocked[navigationResponse.response.URL] = + [NSNumber numberWithBool:NO]; + if ([navigationResponse.response isKindOfClass:[NSHTTPURLResponse class]]) + { + NSDictionary *headers = + ((NSHTTPURLResponse *) navigationResponse.response).allHeaderFields; + NSString *value = headers[@"Content-Security-Policy"]; + if (value) + { + /* TODO: Sloppy parsing of 'Content-Security-Policy' value. */ + NSRange sandbox = [value rangeOfString:@"sandbox"]; + if (sandbox.location != NSNotFound + && (sandbox.location == 0 + || [value characterAtIndex:(sandbox.location - 1)] == ' ' + || [value characterAtIndex:(sandbox.location - 1)] == ';')) + { + NSRange allowScripts = [value rangeOfString:@"allow-scripts"]; + if (allowScripts.location == NSNotFound + || allowScripts.location < sandbox.location) + self.urlScriptBlocked[navigationResponse.response.URL] = + [NSNumber numberWithBool:YES]; + } + } + } +} + +/* No additional new webview or emacs window will be created + for <a ... target="_blank">. */ +- (WKWebView *)webView:(WKWebView *)webView +createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration + forNavigationAction:(WKNavigationAction *)navigationAction + windowFeatures:(WKWindowFeatures *)windowFeatures +{ + if (!navigationAction.targetFrame.isMainFrame) + [webView loadRequest:navigationAction.request]; + return nil; +} + +/* Open panel for file upload. */ +- (void)webView:(WKWebView *)webView +runOpenPanelWithParameters:(WKOpenPanelParameters *)parameters +initiatedByFrame:(WKFrameInfo *)frame +completionHandler:(void (^)(NSArray<NSURL *> *URLs))completionHandler +{ + NSOpenPanel *openPanel = [NSOpenPanel openPanel]; + openPanel.canChooseFiles = YES; + openPanel.canChooseDirectories = NO; + openPanel.allowsMultipleSelection = parameters.allowsMultipleSelection; + if ([openPanel runModal] == NSModalResponseOK) + completionHandler (openPanel.URLs); + else + completionHandler (nil); +} + +/* By forwarding mouse events to emacs view (frame) + - Mouse click in webview selects the window contains the webview. + - Correct mouse hand/arrow/I-beam is displayed (TODO: not perfect yet). +*/ + +- (void)mouseDown:(NSEvent *)event +{ + [self.xw->xv->emacswindow mouseDown:event]; + [super mouseDown:event]; +} + +- (void)mouseUp:(NSEvent *)event +{ + [self.xw->xv->emacswindow mouseUp:event]; + [super mouseUp:event]; +} + +/* Basically we want keyboard events handled by emacs unless an input + element has focus. Especially, while incremental search, we set + emacs as first responder to avoid focus held in an input element + with matching text. */ + +- (void)keyDown:(NSEvent *)event +{ + Lisp_Object var = Fintern (build_string ("isearch-mode"), Qnil); + Lisp_Object val = buffer_local_value (var, Fcurrent_buffer ()); + if (!EQ (val, Qunbound) && !EQ (val, Qnil)) + { + [self.window makeFirstResponder:self.xw->xv->emacswindow]; + [self.xw->xv->emacswindow keyDown:event]; + return; + } + + /* Emacs handles keyboard events when javascript is blocked. */ + if ([self.urlScriptBlocked[self.URL] boolValue]) + { + [self.xw->xv->emacswindow keyDown:event]; + return; + } + + [self evaluateJavaScript:@"xwHasFocus()" + completionHandler:^(id result, NSError *error) { + if (error) + { + NSLog (@"xwHasFocus: %@", error); + [self.xw->xv->emacswindow keyDown:event]; + } + else if (result) + { + NSNumber *hasFocus = result; /* __NSCFBoolean */ + if (!hasFocus.boolValue) + [self.xw->xv->emacswindow keyDown:event]; + else + [super keyDown:event]; + } + }]; +} + +- (void)interpretKeyEvents:(NSArray<NSEvent *> *)eventArray +{ + /* We should do nothing and do not forward (default implementation + if we not override here) to let emacs collect key events and ask + interpretKeyEvents to its superclass. */ +} + +static NSString *xwScript; ++ (void)initialize +{ + /* Find out if an input element has focus. + Message to script message handler when 'C-g' key down. */ + if (!xwScript) + xwScript = + @"function xwHasFocus() {" + @" var ae = document.activeElement;" + @" if (ae) {" + @" var name = ae.nodeName;" + @" return name == 'INPUT' || name == 'TEXTAREA';" + @" } else {" + @" return false;" + @" }" + @"}" + @"function xwKeyDown(event) {" + @" if (event.ctrlKey && event.key == 'g') {" + @" window.webkit.messageHandlers.keyDown.postMessage('C-g');" + @" }" + @"}" + @"document.addEventListener('keydown', xwKeyDown);" + ; +} + +/* Confirming to WKScriptMessageHandler, listens concerning keyDown in + webkit. Currently 'C-g'. */ +- (void)userContentController:(WKUserContentController *)userContentController + didReceiveScriptMessage:(WKScriptMessage *)message +{ + 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]; + } +} + +@end + +/* Xwidget webkit commands. */ + +static Lisp_Object build_string_with_nsstr (NSString *nsstr); + +bool +nsxwidget_is_web_view (struct xwidget *xw) +{ + return xw->xwWidget != NULL && + [xw->xwWidget isKindOfClass:WKWebView.class]; +} + +Lisp_Object +nsxwidget_webkit_uri (struct xwidget *xw) +{ + XwWebView *xwWebView = (XwWebView *) xw->xwWidget; + return build_string_with_nsstr (xwWebView.URL.absoluteString); +} + +Lisp_Object +nsxwidget_webkit_title (struct xwidget *xw) +{ + XwWebView *xwWebView = (XwWebView *) xw->xwWidget; + return build_string_with_nsstr (xwWebView.title); +} + +/* @Note ATS - Need application transport security in 'Info.plist' or + remote pages will not loaded. */ +void +nsxwidget_webkit_goto_uri (struct xwidget *xw, const char *uri) +{ + XwWebView *xwWebView = (XwWebView *) xw->xwWidget; + NSString *urlString = [NSString stringWithUTF8String:uri]; + NSURL *url = [NSURL URLWithString:urlString]; + NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url]; + [xwWebView loadRequest:urlRequest]; +} + +void +nsxwidget_webkit_goto_history (struct xwidget *xw, int rel_pos) +{ + XwWebView *xwWebView = (XwWebView *) xw->xwWidget; + switch (rel_pos) { + case -1: [xwWebView goBack]; break; + case 0: [xwWebView reload]; break; + case 1: [xwWebView goForward]; break; + } +} + +void +nsxwidget_webkit_zoom (struct xwidget *xw, double zoom_change) +{ + XwWebView *xwWebView = (XwWebView *) xw->xwWidget; + xwWebView.magnification += zoom_change; + /* TODO: setMagnification:centeredAtPoint. */ +} + +/* Build lisp string */ +static Lisp_Object +build_string_with_nsstr (NSString *nsstr) +{ + const char *utfstr = [nsstr UTF8String]; + NSUInteger bytes = [nsstr lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + return make_string (utfstr, bytes); +} + +/* Recursively convert an objc native type JavaScript value to a Lisp + value. Mostly copied from GTK xwidget 'webkit_js_to_lisp'. */ +static Lisp_Object +js_to_lisp (id value) +{ + if (value == nil || [value isKindOfClass:NSNull.class]) + return Qnil; + else if ([value isKindOfClass:NSString.class]) + return build_string_with_nsstr ((NSString *) value); + else if ([value isKindOfClass:NSNumber.class]) + { + NSNumber *nsnum = (NSNumber *) value; + char type = nsnum.objCType[0]; + if (type == 'c') /* __NSCFBoolean has type character 'c'. */ + return nsnum.boolValue? Qt : Qnil; + else + { + if (type == 'i' || type == 'l') + return make_int (nsnum.longValue); + else if (type == 'f' || type == 'd') + return make_float (nsnum.doubleValue); + /* else fall through. */ + } + } + else if ([value isKindOfClass:NSArray.class]) + { + NSArray *nsarr = (NSArray *) value; + EMACS_INT n = nsarr.count; + Lisp_Object obj; + struct Lisp_Vector *p = allocate_vector (n); + + for (ptrdiff_t i = 0; i < n; ++i) + p->contents[i] = js_to_lisp ([nsarr objectAtIndex:i]); + XSETVECTOR (obj, p); + return obj; + } + else if ([value isKindOfClass:NSDictionary.class]) + { + NSDictionary *nsdict = (NSDictionary *) value; + NSArray *keys = nsdict.allKeys; + ptrdiff_t n = keys.count; + Lisp_Object obj; + struct Lisp_Vector *p = allocate_vector (n); + + for (ptrdiff_t i = 0; i < n; ++i) + { + NSString *prop_key = (NSString *) [keys objectAtIndex:i]; + id prop_value = [nsdict valueForKey:prop_key]; + p->contents[i] = Fcons (build_string_with_nsstr (prop_key), + js_to_lisp (prop_value)); + } + XSETVECTOR (obj, p); + return obj; + } + NSLog (@"Unhandled type in javascript result"); + return Qnil; +} + +void +nsxwidget_webkit_execute_script (struct xwidget *xw, const char *script, + Lisp_Object fun) +{ + XwWebView *xwWebView = (XwWebView *) xw->xwWidget; + if ([xwWebView.urlScriptBlocked[xwWebView.URL] boolValue]) + { + message ("Javascript is blocked by 'CSP: sandbox'."); + return; + } + + NSString *javascriptString = [NSString stringWithUTF8String:script]; + [xwWebView evaluateJavaScript:javascriptString + completionHandler:^(id result, NSError *error) { + if (error) + { + NSLog (@"evaluateJavaScript error : %@", error.localizedDescription); + NSLog (@"error script=%@", javascriptString); + } + else if (result && FUNCTIONP (fun)) + { + // NSLog (@"result=%@, type=%@", result, [result class]); + Lisp_Object lisp_value = js_to_lisp (result); + store_xwidget_js_callback_event (xw, fun, lisp_value); + } + }]; +} + +/* Window containing an xwidget. */ + +@implementation XwWindow +- (BOOL)isFlipped { return YES; } +@end + +/* Xwidget model, macOS Cocoa part. */ + +void +nsxwidget_init(struct xwidget *xw) +{ + block_input (); + NSRect rect = NSMakeRect (0, 0, xw->width, xw->height); + xw->xwWidget = [[XwWebView alloc] + initWithFrame:rect + configuration:[[WKWebViewConfiguration alloc] init] + xwidget:xw]; + xw->xwWindow = [[XwWindow alloc] + initWithFrame:rect]; + [xw->xwWindow addSubview:xw->xwWidget]; + xw->xv = NULL; /* for 1 to 1 relationship of webkit2. */ + unblock_input (); +} + +void +nsxwidget_kill (struct xwidget *xw) +{ + if (xw) + { + WKUserContentController *scriptor = + ((XwWebView *) xw->xwWidget).configuration.userContentController; + [scriptor removeAllUserScripts]; + [scriptor removeScriptMessageHandlerForName:@"keyDown"]; + [scriptor release]; + if (xw->xv) + xw->xv->model = Qnil; /* Make sure related view stale. */ + + /* This stops playing audio when a xwidget-webkit buffer is + killed. I could not find other solution. */ + nsxwidget_webkit_goto_uri (xw, "about:blank"); + + [((XwWebView *) xw->xwWidget).urlScriptBlocked release]; + [xw->xwWidget removeFromSuperviewWithoutNeedingDisplay]; + [xw->xwWidget release]; + [xw->xwWindow removeFromSuperviewWithoutNeedingDisplay]; + [xw->xwWindow release]; + xw->xwWidget = nil; + } +} + +void +nsxwidget_resize (struct xwidget *xw) +{ + if (xw->xwWidget) + { + [xw->xwWindow setFrameSize:NSMakeSize(xw->width, xw->height)]; + [xw->xwWidget setFrameSize:NSMakeSize(xw->width, xw->height)]; + } +} + +Lisp_Object +nsxwidget_get_size (struct xwidget *xw) +{ + return list2i (xw->xwWidget.frame.size.width, + xw->xwWidget.frame.size.height); +} + +/* Xwidget view, macOS Cocoa part. */ + +@implementation XvWindow : NSView +- (BOOL)isFlipped { return YES; } +@end + +void +nsxwidget_init_view (struct xwidget_view *xv, + struct xwidget *xw, + struct glyph_string *s, + int x, int y) +{ + /* 'x_draw_xwidget_glyph_string' will calculate correct position and + size of clip to draw in emacs buffer window. Thus, just begin at + origin with no crop. */ + xv->x = x; + xv->y = y; + xv->clip_left = 0; + xv->clip_right = xw->width; + xv->clip_top = 0; + xv->clip_bottom = xw->height; + + xv->xvWindow = [[XvWindow alloc] + initWithFrame:NSMakeRect (x, y, xw->width, xw->height)]; + xv->xvWindow.xw = xw; + xv->xvWindow.xv = xv; + + xw->xv = xv; /* For 1 to 1 relationship of webkit2. */ + [xv->xvWindow addSubview:xw->xwWindow]; + + xv->emacswindow = FRAME_NS_VIEW (s->f); + [xv->emacswindow addSubview:xv->xvWindow]; +} + +void +nsxwidget_delete_view (struct xwidget_view *xv) +{ + if (!EQ (xv->model, Qnil)) + { + struct xwidget *xw = XXWIDGET (xv->model); + [xw->xwWindow removeFromSuperviewWithoutNeedingDisplay]; + xw->xv = NULL; /* Now model has no view. */ + } + [xv->xvWindow removeFromSuperviewWithoutNeedingDisplay]; + [xv->xvWindow release]; +} + +void +nsxwidget_show_view (struct xwidget_view *xv) +{ + xv->hidden = NO; + [xv->xvWindow setFrameOrigin:NSMakePoint(xv->x + xv->clip_left, + xv->y + xv->clip_top)]; +} + +void +nsxwidget_hide_view (struct xwidget_view *xv) +{ + xv->hidden = YES; + [xv->xvWindow setFrameOrigin:NSMakePoint(10000, 10000)]; +} + +void +nsxwidget_resize_view (struct xwidget_view *xv, int width, int height) +{ + [xv->xvWindow setFrameSize:NSMakeSize(width, height)]; +} + +void +nsxwidget_move_view (struct xwidget_view *xv, int x, int y) +{ + [xv->xvWindow setFrameOrigin:NSMakePoint (x, y)]; +} + +/* Move model window in container (view window). */ +void +nsxwidget_move_widget_in_view (struct xwidget_view *xv, int x, int y) +{ + struct xwidget *xww = xv->xvWindow.xw; + [xww->xwWindow setFrameOrigin:NSMakePoint (x, y)]; +} + +void +nsxwidget_set_needsdisplay (struct xwidget_view *xv) +{ + xv->xvWindow.needsDisplay = YES; +} diff --git a/src/xwidget.c b/src/xwidget.c index 0347f1e648..44e6c45df1 100644 --- a/src/xwidget.c +++ b/src/xwidget.c @@ -23,13 +23,21 @@ Copyright (C) 2011-2020 Free Software Foundation, Inc. #include "lisp.h" #include "blockinput.h" +#include "dispextern.h" #include "frame.h" #include "keyboard.h" #include "gtkutil.h" #include "sysstdio.h" +#include "termhooks.h" +#include "window.h" +/* Include xwidget bottom end headers. */ +#ifdef USE_GTK #include <webkit2/webkit2.h> #include <JavaScriptCore/JavaScript.h> +#elif defined NS_IMPL_COCOA +#include "nsxwidget.h" +#endif static struct xwidget * allocate_xwidget (void) @@ -48,6 +56,7 @@ #define XSETXWIDGET_VIEW(a, b) XSETPSEUDOVECTOR (a, b, PVEC_XWIDGET_VIEW) static struct xwidget_view *xwidget_view_lookup (struct xwidget *, struct window *); +#ifdef USE_GTK static void webkit_view_load_changed_cb (WebKitWebView *, WebKitLoadEvent, gpointer); @@ -61,6 +70,7 @@ webkit_decide_policy_cb (WebKitWebView *, WebKitPolicyDecision *, WebKitPolicyDecisionType, gpointer); +#endif DEFUN ("make-xwidget", @@ -78,8 +88,10 @@ DEFUN ("make-xwidget", Lisp_Object title, Lisp_Object width, Lisp_Object height, Lisp_Object arguments, Lisp_Object buffer) { +#ifdef USE_GTK if (!xg_gtk_initialized) error ("make-xwidget: GTK has not been initialized"); +#endif CHECK_SYMBOL (type); CHECK_FIXNAT (width); CHECK_FIXNAT (height); @@ -94,10 +106,11 @@ DEFUN ("make-xwidget", xw->kill_without_query = false; XSETXWIDGET (val, xw); Vxwidget_list = Fcons (val, Vxwidget_list); - xw->widgetwindow_osr = NULL; - xw->widget_osr = NULL; xw->plist = Qnil; +#ifdef USE_GTK + xw->widgetwindow_osr = NULL; + xw->widget_osr = NULL; if (EQ (xw->type, Qwebkit)) { block_input (); @@ -152,6 +165,9 @@ DEFUN ("make-xwidget", unblock_input (); } +#elif defined NS_IMPL_COCOA + nsxwidget_init (xw); +#endif return val; } @@ -187,6 +203,7 @@ xwidget_hidden (struct xwidget_view *xv) return xv->hidden; } +#ifdef USE_GTK static void xwidget_show_view (struct xwidget_view *xv) { @@ -220,13 +237,14 @@ offscreen_damage_event (GtkWidget *widget, GdkEvent *event, if (GTK_IS_WIDGET (xv_widget)) gtk_widget_queue_draw (GTK_WIDGET (xv_widget)); else - printf ("Warning, offscreen_damage_event received invalid xv pointer:%p\n", - xv_widget); + message ("Warning, offscreen_damage_event received invalid xv pointer:%p\n", + xv_widget); return FALSE; } +#endif /* USE_GTK */ -static void +void store_xwidget_event_string (struct xwidget *xw, const char *eventname, const char *eventstr) { @@ -240,7 +258,27 @@ store_xwidget_event_string (struct xwidget *xw, const char *eventname, kbd_buffer_store_event (&event); } -static void +void +store_xwidget_download_callback_event (struct xwidget *xw, + const char *url, + const char *mimetype, + const char *filename) +{ + struct input_event event; + Lisp_Object xwl; + XSETXWIDGET (xwl, xw); + EVENT_INIT (event); + event.kind = XWIDGET_EVENT; + event.frame_or_window = Qnil; + event.arg = list5 (intern ("download-callback"), + xwl, + build_string (url), + build_string (mimetype), + build_string (filename)); + kbd_buffer_store_event (&event); +} + +void store_xwidget_js_callback_event (struct xwidget *xw, Lisp_Object proc, Lisp_Object argument) @@ -256,6 +294,7 @@ store_xwidget_js_callback_event (struct xwidget *xw, } +#ifdef USE_GTK void webkit_view_load_changed_cb (WebKitWebView *webkitwebview, WebKitLoadEvent load_event, @@ -486,6 +525,7 @@ xwidget_osr_event_set_embedder (GtkWidget *widget, GdkEvent *event, gtk_widget_get_window (xv->widget)); return FALSE; } +#endif /* USE_GTK */ /* Initializes and does initial placement of an xwidget view on screen. */ @@ -495,8 +535,10 @@ xwidget_init_view (struct xwidget *xww, int x, int y) { +#ifdef USE_GTK if (!xg_gtk_initialized) error ("xwidget_init_view: GTK has not been initialized"); +#endif struct xwidget_view *xv = allocate_xwidget_view (); Lisp_Object val; @@ -507,6 +549,7 @@ xwidget_init_view (struct xwidget *xww, XSETWINDOW (xv->w, s->w); XSETXWIDGET (xv->model, xww); +#ifdef USE_GTK if (EQ (xww->type, Qwebkit)) { xv->widget = gtk_drawing_area_new (); @@ -564,6 +607,10 @@ xwidget_init_view (struct xwidget *xww, xv->x = x; xv->y = y; gtk_widget_show_all (xv->widgetwindow); +#elif defined NS_IMPL_COCOA + nsxwidget_init_view (xv, xww, s, x, y); + nsxwidget_resize_view(xv, xww->width, xww->height); +#endif return xv; } @@ -576,24 +623,59 @@ x_draw_xwidget_glyph_string (struct glyph_string *s) initialization. */ struct xwidget *xww = s->xwidget; struct xwidget_view *xv = xwidget_view_lookup (xww, s->w); + int text_area_x, text_area_y, text_area_width, text_area_height; int clip_right; int clip_bottom; int clip_top; int clip_left; int x = s->x; - int y = s->y + (s->height / 2) - (xww->height / 2); + int y = s->y; /* Do initialization here in the display loop because there is no other time to know things like window placement etc. Do not create a new view if we have found one that is usable. */ +#ifdef USE_GTK if (!xv) xv = xwidget_init_view (xww, s, x, y); - - int text_area_x, text_area_y, text_area_width, text_area_height; +#elif defined NS_IMPL_COCOA + if (!xv) + { + /* Enforce 1 to 1, model and view for macOS Cocoa webkit2. */ + if (xww->xv) + { + if (xwidget_hidden (xww->xv)) + { + Lisp_Object xvl; + XSETXWIDGET_VIEW (xvl, xww->xv); + Fdelete_xwidget_view (xvl); + } + else + { + message ("You can't share an xwidget (webkit2) among windows."); + return; + } + } + xv = xwidget_init_view (xww, s, x, y); + } +#endif window_box (s->w, TEXT_AREA, &text_area_x, &text_area_y, &text_area_width, &text_area_height); + + /* Resize xwidget webkit if its container window size is changed in + some ways, for example, a buffer became hidden in small split + window, then it can appear front in merged whole window. */ + if (EQ (xww->type, Qwebkit) + && (xww->width != text_area_width || xww->height != text_area_height)) + { + Lisp_Object xwl; + XSETXWIDGET (xwl, xww); + Fxwidget_resize (xwl, + make_int (text_area_width), + make_int (text_area_height)); + } + clip_left = max (0, text_area_x - x); clip_right = max (clip_left, min (xww->width, text_area_x + text_area_width - x)); @@ -616,8 +698,14 @@ x_draw_xwidget_glyph_string (struct glyph_string *s) /* Has it moved? */ if (moved) - gtk_fixed_move (GTK_FIXED (FRAME_GTK_WIDGET (s->f)), - xv->widgetwindow, x + clip_left, y + clip_top); + { +#ifdef USE_GTK + gtk_fixed_move (GTK_FIXED (FRAME_GTK_WIDGET (s->f)), + xv->widgetwindow, x + clip_left, y + clip_top); +#elif defined NS_IMPL_COCOA + nsxwidget_move_view (xv, x + clip_left, y + clip_top); +#endif + } /* Clip the widget window if some parts happen to be outside drawable area. An Emacs window is not a gtk window. A gtk window @@ -628,10 +716,16 @@ x_draw_xwidget_glyph_string (struct glyph_string *s) || xv->clip_bottom != clip_bottom || xv->clip_top != clip_top || xv->clip_left != clip_left) { +#ifdef USE_GTK gtk_widget_set_size_request (xv->widgetwindow, clip_right - clip_left, clip_bottom - clip_top); gtk_fixed_move (GTK_FIXED (xv->widgetwindow), xv->widget, -clip_left, -clip_top); +#elif defined NS_IMPL_COCOA + nsxwidget_resize_view (xv, clip_right - clip_left, + clip_bottom - clip_top); + nsxwidget_move_widget_in_view (xv, -clip_left, -clip_top); +#endif xv->clip_right = clip_right; xv->clip_bottom = clip_bottom; @@ -645,22 +739,66 @@ x_draw_xwidget_glyph_string (struct glyph_string *s) xwidgets background. It's just a visual glitch though. */ if (!xwidget_hidden (xv)) { +#ifdef USE_GTK gtk_widget_queue_draw (xv->widgetwindow); gtk_widget_queue_draw (xv->widget); +#elif defined NS_IMPL_COCOA + nsxwidget_set_needsdisplay (xv); +#endif } } -/* Macro that checks WEBKIT_IS_WEB_VIEW (xw->widget_osr) first. */ +static bool +xwidget_is_web_view (struct xwidget *xw) +{ +#ifdef USE_GTK + return xw->widget_osr != NULL && WEBKIT_IS_WEB_VIEW (xw->widget_osr); +#elif defined NS_IMPL_COCOA + return nsxwidget_is_web_view (xw); +#endif +} + +/* Macro that checks xwidget hold webkit web view first. */ #define WEBKIT_FN_INIT() \ CHECK_XWIDGET (xwidget); \ struct xwidget *xw = XXWIDGET (xwidget); \ - if (!xw->widget_osr || !WEBKIT_IS_WEB_VIEW (xw->widget_osr)) \ + if (!xwidget_is_web_view (xw)) \ { \ fputs ("ERROR xw->widget_osr does not hold a webkit instance\n", \ stdout); \ return Qnil; \ } +DEFUN ("xwidget-webkit-uri", + Fxwidget_webkit_uri, Sxwidget_webkit_uri, + 1, 1, 0, + doc: /* Get the current URL of XWIDGET webkit. */) + (Lisp_Object xwidget) +{ + WEBKIT_FN_INIT (); +#ifdef USE_GTK + WebKitWebView *wkwv = WEBKIT_WEB_VIEW (xw->widget_osr); + return build_string (webkit_web_view_get_uri (wkwv)); +#elif defined NS_IMPL_COCOA + return nsxwidget_webkit_uri (xw); +#endif +} + +DEFUN ("xwidget-webkit-title", + Fxwidget_webkit_title, Sxwidget_webkit_title, + 1, 1, 0, + doc: /* Get the current title of XWIDGET webkit. */) + (Lisp_Object xwidget) +{ + WEBKIT_FN_INIT (); +#ifdef USE_GTK + WebKitWebView *wkwv = WEBKIT_WEB_VIEW (xw->widget_osr); + return build_string (webkit_web_view_get_title (wkwv)); +#elif defined NS_IMPL_COCOA + return nsxwidget_webkit_title (xw); +#endif +} + DEFUN ("xwidget-webkit-goto-uri", Fxwidget_webkit_goto_uri, Sxwidget_webkit_goto_uri, 2, 2, 0, @@ -670,7 +808,32 @@ DEFUN ("xwidget-webkit-goto-uri", WEBKIT_FN_INIT (); CHECK_STRING (uri); uri = ENCODE_FILE (uri); +#ifdef USE_GTK webkit_web_view_load_uri (WEBKIT_WEB_VIEW (xw->widget_osr), SSDATA (uri)); +#elif defined NS_IMPL_COCOA + nsxwidget_webkit_goto_uri (xw, SSDATA (uri)); +#endif + return Qnil; +} + +DEFUN ("xwidget-webkit-goto-history", + Fxwidget_webkit_goto_history, Sxwidget_webkit_goto_history, + 2, 2, 0, + doc: /* Make the XWIDGET webkit load REL-POS (-1, 0, 1) page in browse history. */) + (Lisp_Object xwidget, Lisp_Object rel_pos) +{ + WEBKIT_FN_INIT (); + /* CHECK_RANGED_INTEGER (rel_pos, -1, 1); */ /* -1, 0, 1 */ +#ifdef USE_GTK + WebKitWebView *wkwv = WEBKIT_WEB_VIEW (xw->widget_osr); + switch (XFIXNAT (rel_pos)) { + case -1: webkit_web_view_go_back (wkwv); break; + case 0: webkit_web_view_reload (wkwv); break; + case 1: webkit_web_view_go_forward (wkwv); break; + } +#elif defined NS_IMPL_COCOA + nsxwidget_webkit_goto_history (xw, XFIXNAT (rel_pos)); +#endif return Qnil; } @@ -684,14 +847,19 @@ DEFUN ("xwidget-webkit-zoom", if (FLOATP (factor)) { double zoom_change = XFLOAT_DATA (factor); +#ifdef USE_GTK webkit_web_view_set_zoom_level (WEBKIT_WEB_VIEW (xw->widget_osr), webkit_web_view_get_zoom_level (WEBKIT_WEB_VIEW (xw->widget_osr)) + zoom_change); +#elif defined NS_IMPL_COCOA + nsxwidget_webkit_zoom (xw, zoom_change); +#endif } return Qnil; } +#ifdef USE_GTK /* Save script and fun in the script/callback save vector and return its index. */ static ptrdiff_t @@ -713,6 +881,7 @@ save_script_callback (struct xwidget *xw, Lisp_Object script, Lisp_Object fun) ASET (cbs, idx, Fcons (make_mint_ptr (xlispstrdup (script)), fun)); return idx; } +#endif DEFUN ("xwidget-webkit-execute-script", Fxwidget_webkit_execute_script, Sxwidget_webkit_execute_script, @@ -724,11 +893,12 @@ DEFUN ("xwidget-webkit-execute-script", { WEBKIT_FN_INIT (); CHECK_STRING (script); - if (!NILP (fun) && !FUNCTIONP (fun)) + if (!FUNCTIONP (fun)) wrong_type_argument (Qinvalid_function, fun); script = ENCODE_SYSTEM (script); +#ifdef USE_GTK /* Protect script and fun during GC. */ intptr_t idx = save_script_callback (xw, script, fun); @@ -742,6 +912,9 @@ DEFUN ("xwidget-webkit-execute-script", NULL, /* cancelable */ webkit_javascript_finished_cb, (gpointer) idx); +#elif defined NS_IMPL_COCOA + nsxwidget_webkit_execute_script (xw, SSDATA (script), fun); +#endif return Qnil; } @@ -758,6 +931,7 @@ DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0, xw->height = h; /* If there is an offscreen widget resize it first. */ +#ifdef USE_GTK if (xw->widget_osr) { gtk_window_resize (GTK_WINDOW (xw->widgetwindow_osr), xw->width, @@ -766,6 +940,9 @@ DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0, gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr), xw->width, xw->height); } +#elif defined NS_IMPL_COCOA + nsxwidget_resize (xw); +#endif for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail); tail = XCDR (tail)) { @@ -773,8 +950,14 @@ DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize, 3, 3, 0, { struct xwidget_view *xv = XXWIDGET_VIEW (XCAR (tail)); if (XXWIDGET (xv->model) == xw) + { +#ifdef USE_GTK gtk_widget_set_size_request (GTK_WIDGET (xv->widget), xw->width, xw->height); +#elif defined NS_IMPL_COCOA + nsxwidget_resize_view(xv, xw->width, xw->height); +#endif + } } } @@ -793,9 +976,13 @@ DEFUN ("xwidget-size-request", (Lisp_Object xwidget) { CHECK_XWIDGET (xwidget); +#ifdef USE_GTK GtkRequisition requisition; gtk_widget_size_request (XXWIDGET (xwidget)->widget_osr, &requisition); return list2i (requisition.width, requisition.height); +#elif defined NS_IMPL_COCOA + return nsxwidget_get_size(XXWIDGET (xwidget)); +#endif } DEFUN ("xwidgetp", @@ -872,14 +1059,19 @@ DEFUN ("delete-xwidget-view", { CHECK_XWIDGET_VIEW (xwidget_view); struct xwidget_view *xv = XXWIDGET_VIEW (xwidget_view); - gtk_widget_destroy (xv->widgetwindow); Vxwidget_view_list = Fdelq (xwidget_view, Vxwidget_view_list); +#ifdef USE_GTK + gtk_widget_destroy (xv->widgetwindow); /* xv->model still has signals pointing to the view. There can be several views. Find the matching signals and delete them all. */ g_signal_handlers_disconnect_matched (XXWIDGET (xv->model)->widgetwindow_osr, G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, xv->widget); +#elif defined NS_IMPL_COCOA + nsxwidget_delete_view (xv); +#endif + return Qnil; } @@ -985,7 +1177,10 @@ syms_of_xwidget (void) defsubr (&Sxwidget_query_on_exit_flag); defsubr (&Sset_xwidget_query_on_exit_flag); + defsubr (&Sxwidget_webkit_uri); + defsubr (&Sxwidget_webkit_title); defsubr (&Sxwidget_webkit_goto_uri); + defsubr (&Sxwidget_webkit_goto_history); defsubr (&Sxwidget_webkit_zoom); defsubr (&Sxwidget_webkit_execute_script); DEFSYM (Qwebkit, "webkit"); @@ -1156,11 +1351,19 @@ xwidget_end_redisplay (struct window *w, struct glyph_matrix *matrix) xwidget_end_redisplay (w->current_matrix); */ struct xwidget_view *xv = xwidget_view_lookup (glyph->u.xwidget, w); +#ifdef USE_GTK /* FIXME: Is it safe to assume xwidget_view_lookup always succeeds here? If so, this comment can be removed. If not, the code probably needs fixing. */ eassume (xv); xwidget_touch (xv); +#elif defined NS_IMPL_COCOA + /* In NS xwidget, xv can be NULL for the second or + later views for a model, the result of 1 to 1 + model view relation enforcement. */ + if (xv) + xwidget_touch (xv); +#endif } } } @@ -1177,9 +1380,21 @@ xwidget_end_redisplay (struct window *w, struct glyph_matrix *matrix) if (XWINDOW (xv->w) == w) { if (xwidget_touched (xv)) - xwidget_show_view (xv); + { +#ifdef USE_GTK + xwidget_show_view (xv); +#elif defined NS_IMPL_COCOA + nsxwidget_show_view (xv); +#endif + } else - xwidget_hide_view (xv); + { +#ifdef USE_GTK + xwidget_hide_view (xv); +#elif defined NS_IMPL_COCOA + nsxwidget_hide_view (xv); +#endif + } } } } @@ -1198,6 +1413,7 @@ kill_buffer_xwidgets (Lisp_Object buffer) { CHECK_XWIDGET (xwidget); struct xwidget *xw = XXWIDGET (xwidget); +#ifdef USE_GTK if (xw->widget_osr && xw->widgetwindow_osr) { gtk_widget_destroy (xw->widget_osr); @@ -1211,6 +1427,9 @@ kill_buffer_xwidgets (Lisp_Object buffer) xfree (xmint_pointer (XCAR (cb))); ASET (xw->script_callbacks, idx, Qnil); } +#elif defined NS_IMPL_COCOA + nsxwidget_kill (xw); +#endif } } } diff --git a/src/xwidget.h b/src/xwidget.h index 99fa8bbd61..c1b2035a8a 100644 --- a/src/xwidget.h +++ b/src/xwidget.h @@ -29,7 +29,13 @@ #define XWIDGET_H_INCLUDED struct window; #ifdef HAVE_XWIDGETS -# include <gtk/gtk.h> + +#if defined (USE_GTK) +#include <gtk/gtk.h> +#elif defined (NS_IMPL_COCOA) && defined (__OBJC__) +#import <AppKit/NSView.h> +#import "nsxwidget.h" +#endif struct xwidget { @@ -54,9 +60,25 @@ #define XWIDGET_H_INCLUDED int height; int width; +#if defined (USE_GTK) /* For offscreen widgets, unused if not osr. */ GtkWidget *widget_osr; GtkWidget *widgetwindow_osr; +#elif defined (NS_IMPL_COCOA) +# ifdef __OBJC__ + /* For offscreen widgets, unused if not osr. */ + NSView *xwWidget; + XwWindow *xwWindow; + + /* Used only for xwidget types (such as webkit2) enforcing 1 to 1 + relationship between model and view. */ + struct xwidget_view *xv; +# else + void *xwWidget; + void *xwWindow; + struct xwidget_view *xv; +# endif +#endif /* Kill silently if Emacs is exited. */ bool_bf kill_without_query : 1; @@ -75,9 +97,20 @@ #define XWIDGET_H_INCLUDED /* The "live" instance isn't drawn. */ bool hidden; +#if defined (USE_GTK) GtkWidget *widget; GtkWidget *widgetwindow; GtkWidget *emacswindow; +#elif defined (NS_IMPL_COCOA) +# ifdef __OBJC__ + XvWindow *xvWindow; + NSView *emacswindow; +# else + void *xvWindow; + void *emacswindow; +# endif +#endif + int x; int y; int clip_right; @@ -116,6 +149,21 @@ #define XG_XWIDGET_VIEW "emacs_xwidget_view" struct xwidget *lookup_xwidget (Lisp_Object spec); void xwidget_end_redisplay (struct window *, struct glyph_matrix *); void kill_buffer_xwidgets (Lisp_Object); +#ifdef NS_IMPL_COCOA +/* Defined in 'xwidget.c'. */ +void store_xwidget_event_string (struct xwidget *xw, + const char *eventname, + const char *eventstr); + +void store_xwidget_download_callback_event (struct xwidget *xw, + const char *url, + const char *mimetype, + const char *filename); + +void store_xwidget_js_callback_event (struct xwidget *xw, + Lisp_Object proc, + Lisp_Object argument); +#endif #else INLINE_HEADER_BEGIN INLINE void syms_of_xwidget (void) {} -- (domestic pets only, the antidote for overdose, milk.) bloggy blog: http://lars.ingebrigtsen.no ^ permalink raw reply related [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2020-08-11 16:26 ` Lars Ingebrigtsen @ 2020-08-11 18:28 ` Eli Zaretskii 2020-08-11 19:26 ` Lars Ingebrigtsen 2020-08-11 19:56 ` Alan Third 1 sibling, 1 reply; 41+ messages in thread From: Eli Zaretskii @ 2020-08-11 18:28 UTC (permalink / raw) To: Lars Ingebrigtsen; +Cc: alan, veshboo, rms, stefan, 29565, pcr910303 > From: Lars Ingebrigtsen <larsi@gnus.org> > Cc: Richard Stallman <rms@gnu.org>, alan@idiocy.org, stefan@marxist.se, > 29565@debbugs.gnu.org, veshboo@gmail.com, pcr910303@icloud.com > Date: Tue, 11 Aug 2020 18:26:45 +0200 > > There's one thing that doesn't work, though (so it's commented out below): > > + CHECK_RANGED_INTEGER (rel_pos, -1, 1); /* -1, 0, 1 */ > > Is that a macro that Emacs used to have? I can't find it now. Perhaps clip_to_bounds? ^ permalink raw reply [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2020-08-11 18:28 ` Eli Zaretskii @ 2020-08-11 19:26 ` Lars Ingebrigtsen 2020-08-11 19:33 ` Eli Zaretskii 0 siblings, 1 reply; 41+ messages in thread From: Lars Ingebrigtsen @ 2020-08-11 19:26 UTC (permalink / raw) To: Eli Zaretskii; +Cc: alan, veshboo, rms, stefan, 29565, pcr910303 Eli Zaretskii <eliz@gnu.org> writes: >> There's one thing that doesn't work, though (so it's commented out below): >> >> + CHECK_RANGED_INTEGER (rel_pos, -1, 1); /* -1, 0, 1 */ >> >> Is that a macro that Emacs used to have? I can't find it now. > > Perhaps clip_to_bounds? I think the intention is to signal an error if rel_pos is anything other than -1, 0 or 1... -- (domestic pets only, the antidote for overdose, milk.) bloggy blog: http://lars.ingebrigtsen.no ^ permalink raw reply [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2020-08-11 19:26 ` Lars Ingebrigtsen @ 2020-08-11 19:33 ` Eli Zaretskii 2020-08-12 10:44 ` Lars Ingebrigtsen 0 siblings, 1 reply; 41+ messages in thread From: Eli Zaretskii @ 2020-08-11 19:33 UTC (permalink / raw) To: Lars Ingebrigtsen; +Cc: alan, veshboo, rms, stefan, 29565, pcr910303 > From: Lars Ingebrigtsen <larsi@gnus.org> > Cc: rms@gnu.org, alan@idiocy.org, stefan@marxist.se, > 29565@debbugs.gnu.org, veshboo@gmail.com, pcr910303@icloud.com > Date: Tue, 11 Aug 2020 21:26:54 +0200 > > I think the intention is to signal an error if rel_pos is anything other > than -1, 0 or 1... In that case, just call args_out_of_range_3 by hand, after testing that the value is within bounds. You can see examples of that in several source files. ^ permalink raw reply [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2020-08-11 19:33 ` Eli Zaretskii @ 2020-08-12 10:44 ` Lars Ingebrigtsen 0 siblings, 0 replies; 41+ messages in thread From: Lars Ingebrigtsen @ 2020-08-12 10:44 UTC (permalink / raw) To: Eli Zaretskii; +Cc: alan, veshboo, rms, stefan, 29565, pcr910303 Eli Zaretskii <eliz@gnu.org> writes: >> I think the intention is to signal an error if rel_pos is anything other >> than -1, 0 or 1... > > In that case, just call args_out_of_range_3 by hand, after testing > that the value is within bounds. You can see examples of that in > several source files. Yup. I just wondered where that macro came from... Anyway, I've now applied all of Sungbin's patches to Emacs 28, and things seem to work for me. I've tested compiling on Debian and the latest Macos, and xwidget seems to work well on Macos now. Thanks for implementing this, Sungbin. There's a couple added compilation warnings (on Macos) that should be fixed, but before looking at that, I noticed that an unrelated patch made the build spew out all these warnings: In file included from print.c:25: ./lisp.h:1830:12: warning: the argument to '__builtin_assume' has side effects that will be discarded [-Wassume] eassume (0 <= i && i < bool_vector_size (a)); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ./lisp.h:182:32: note: expanded from macro 'eassume' # define eassume(cond) assume (cond) ^~~~ etc etc. Was that from one of Paul's patches yesterday? Anyway, I'm closing this bug report now, and we can open new ones for any problems that may crop up... -- (domestic pets only, the antidote for overdose, milk.) bloggy blog: http://lars.ingebrigtsen.no ^ permalink raw reply [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2020-08-11 16:26 ` Lars Ingebrigtsen 2020-08-11 18:28 ` Eli Zaretskii @ 2020-08-11 19:56 ` Alan Third 2020-08-11 19:58 ` Lars Ingebrigtsen 1 sibling, 1 reply; 41+ messages in thread From: Alan Third @ 2020-08-11 19:56 UTC (permalink / raw) To: Lars Ingebrigtsen; +Cc: veshboo, Richard Stallman, stefan, 29565, pcr910303 On Tue, Aug 11, 2020 at 06:26:45PM +0200, Lars Ingebrigtsen wrote: > > And... I'm unable to test this on Macos, because Homebrew doesn't offer > webkitgtk, apparently. It shouldn't need webkitgtk, the webkit it uses comes with the macOS dev tools. -- Alan Third ^ permalink raw reply [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2020-08-11 19:56 ` Alan Third @ 2020-08-11 19:58 ` Lars Ingebrigtsen 2020-08-11 20:18 ` Alan Third 0 siblings, 1 reply; 41+ messages in thread From: Lars Ingebrigtsen @ 2020-08-11 19:58 UTC (permalink / raw) To: Alan Third; +Cc: veshboo, Richard Stallman, stefan, 29565, pcr910303 Alan Third <alan@idiocy.org> writes: > On Tue, Aug 11, 2020 at 06:26:45PM +0200, Lars Ingebrigtsen wrote: >> >> And... I'm unable to test this on Macos, because Homebrew doesn't offer >> webkitgtk, apparently. > > It shouldn't need webkitgtk, the webkit it uses comes with the macOS > dev tools. Hm... ./configure says checking for webkit2gtk-4.0 >= 2.12... no configure: error: xwidgets requested but WebKitGTK+ or WebKit framework not found. -- (domestic pets only, the antidote for overdose, milk.) bloggy blog: http://lars.ingebrigtsen.no ^ permalink raw reply [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2020-08-11 19:58 ` Lars Ingebrigtsen @ 2020-08-11 20:18 ` Alan Third 2020-08-11 20:29 ` Lars Ingebrigtsen 0 siblings, 1 reply; 41+ messages in thread From: Alan Third @ 2020-08-11 20:18 UTC (permalink / raw) To: Lars Ingebrigtsen; +Cc: 29565, veshboo, stefan, Richard Stallman, pcr910303 On Tue, Aug 11, 2020 at 09:58:58PM +0200, Lars Ingebrigtsen wrote: > Alan Third <alan@idiocy.org> writes: > > > On Tue, Aug 11, 2020 at 06:26:45PM +0200, Lars Ingebrigtsen wrote: > >> > >> And... I'm unable to test this on Macos, because Homebrew doesn't offer > >> webkitgtk, apparently. > > > > It shouldn't need webkitgtk, the webkit it uses comes with the macOS > > dev tools. > > Hm... ./configure says > > checking for webkit2gtk-4.0 >= 2.12... no > configure: error: xwidgets requested but WebKitGTK+ or WebKit framework not found. Is it building against Cocoa or GTK? I don't know if you maybe need to be explicit? ./configure --with-ns --with-xwidgets BTW, there's a branch on savannah, scratch/nsxwidgets, that definitely built at the time, but I suspect it's been superseded by that last patch from Jo Sungbin. -- Alan Third ^ permalink raw reply [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2020-08-11 20:18 ` Alan Third @ 2020-08-11 20:29 ` Lars Ingebrigtsen 2020-08-11 20:35 ` Alan Third 2020-08-11 20:50 ` Lars Ingebrigtsen 0 siblings, 2 replies; 41+ messages in thread From: Lars Ingebrigtsen @ 2020-08-11 20:29 UTC (permalink / raw) To: Alan Third; +Cc: 29565, veshboo, stefan, Richard Stallman, pcr910303 Alan Third <alan@idiocy.org> writes: > Is it building against Cocoa or GTK? > > I don't know if you maybe need to be explicit? > > ./configure --with-ns --with-xwidgets D'oh! I tried first with just --with-xwidgets, and then it said I needed gtk3. But that was just because I had forgotten to rebuild the configure script, apparently. I've now done so, and ./configure --with-xwidgets works fine, and it compiles without any problems, but with some extra warnings that I don't think were there before, like: Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/AppKit.framework/Headers/NSPasteboard.h:312:32: note: 'NSFilenamesPboardType' has been explicitly marked deprecated here APPKIT_EXTERN NSPasteboardType NSFilenamesPboardType API_DEPRECATED("Cre... ^ nsterm.m:8624:35: warning: 'NSURLPboardType' is deprecated: first deprecated in macOS 10.14 [-Wdeprecated-declarations] else if ([type isEqualToString: NSURLPboardType]) ^~~~~~~~~~~~~~~ NSPasteboardTypeURL Or were these there before, too? I did have some warnings before applying the patch... Anyway, it builds, and it seems to work well -- M-x xwidget-webkit-browse-url seems to work fine. So, tomorrow I'll look into the warnings and get this thing checked in and pushed. Thanks for the pointer to --with-ns. :-) -- (domestic pets only, the antidote for overdose, milk.) bloggy blog: http://lars.ingebrigtsen.no ^ permalink raw reply [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2020-08-11 20:29 ` Lars Ingebrigtsen @ 2020-08-11 20:35 ` Alan Third 2020-08-11 20:50 ` Lars Ingebrigtsen 1 sibling, 0 replies; 41+ messages in thread From: Alan Third @ 2020-08-11 20:35 UTC (permalink / raw) To: Lars Ingebrigtsen; +Cc: 29565, veshboo, stefan, Richard Stallman, pcr910303 On Tue, Aug 11, 2020 at 10:29:13PM +0200, Lars Ingebrigtsen wrote: > > I've now done so, and ./configure --with-xwidgets works fine, and it > compiles without any problems, but with some extra warnings that I don't > think were there before, like: > > Library/Developer/CommandLineTools/SDKs/MacOSX10.15.sdk/System/Library/Frameworks/AppKit.framework/Headers/NSPasteboard.h:312:32: note: > 'NSFilenamesPboardType' has been explicitly marked deprecated here > APPKIT_EXTERN NSPasteboardType NSFilenamesPboardType API_DEPRECATED("Cre... > ^ > nsterm.m:8624:35: warning: 'NSURLPboardType' is deprecated: first deprecated in > macOS 10.14 [-Wdeprecated-declarations] > else if ([type isEqualToString: NSURLPboardType]) > ^~~~~~~~~~~~~~~ > NSPasteboardTypeURL > > Or were these there before, too? I did have some warnings before > applying the patch... They should have been, yes. I've been very lazy with fixing those PasteBoard warnings. > Thanks for the pointer to --with-ns. :-) NP, I'm glad it's working! -- Alan Third ^ permalink raw reply [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2020-08-11 20:29 ` Lars Ingebrigtsen 2020-08-11 20:35 ` Alan Third @ 2020-08-11 20:50 ` Lars Ingebrigtsen 2020-08-12 3:46 ` Unknown 1 sibling, 1 reply; 41+ messages in thread From: Lars Ingebrigtsen @ 2020-08-11 20:50 UTC (permalink / raw) To: Alan Third; +Cc: 29565, veshboo, stefan, Richard Stallman, pcr910303 Lars Ingebrigtsen <larsi@gnus.org> writes: > So, tomorrow I'll look into the warnings and get this thing checked in > and pushed. That is, unless Sungbin chimes in... and has a new patch set that he's worked on in the year since this was discussed last or something. :-) Sungbin? -- (domestic pets only, the antidote for overdose, milk.) bloggy blog: http://lars.ingebrigtsen.no ^ permalink raw reply [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2020-08-11 20:50 ` Lars Ingebrigtsen @ 2020-08-12 3:46 ` Unknown 2020-08-12 10:05 ` Lars Ingebrigtsen 0 siblings, 1 reply; 41+ messages in thread From: Unknown @ 2020-08-12 3:46 UTC (permalink / raw) To: Lars Ingebrigtsen; +Cc: 29565, stefan, Alan Third, veshboo, Richard Stallman On Aug 12, 2020, at 5:50 AM, Lars Ingebrigtsen <larsi@gnus.org> wrote: > > Lars Ingebrigtsen <larsi@gnus.org> writes: > >> So, tomorrow I'll look into the warnings and get this thing checked in >> and pushed. > > That is, unless Sungbin chimes in... and has a new patch set that he's > worked on in the year since this was discussed last or something. :-) Unfortunately I don’t have a new patchset that I’ve been working on... (I lost interest on this after the long thread on Obj-C and gcc) but I’ll be more than happy to incorporate the patch on master (but only if I have the confidence that my time will be spent well and not being blocked by the gcc issue, I guess). IIRC, the biggest problem was that the patch currently just messages (a lot) when two xwidget views gets duplicated and Emacs can’t display one. The sensible approach would be to show another view (I guess a NSView with a NSTextField label in it?) but I’m not familiar enough with the display engine (as I didn’t write the code from scratch). Maybe someone might help on that part...? > Sungbin? > > -- > (domestic pets only, the antidote for overdose, milk.) > bloggy blog: http://lars.ingebrigtsen.no ^ permalink raw reply [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2020-08-12 3:46 ` Unknown @ 2020-08-12 10:05 ` Lars Ingebrigtsen 2020-08-12 16:34 ` Alan Third 0 siblings, 1 reply; 41+ messages in thread From: Lars Ingebrigtsen @ 2020-08-12 10:05 UTC (permalink / raw) To: 조성빈 Cc: 29565, veshboo, Alan Third, stefan, Richard Stallman 조성빈 <pcr910303@icloud.com> writes: > Unfortunately I don’t have a new patchset that I’ve been working > on... (I lost interest on this after the long thread on Obj-C and gcc) > but I’ll be more than happy to incorporate the patch on master (but > only if I have the confidence that my time will be spent well and not > being blocked by the gcc issue, I guess). Yeah, that was a discouraging thread on emacs-devel. I've taken your final version of the patch set and applied it to Emacs 28 -- there were a couple things I had to merge by hand, but everything seems to work fine, so I'll just go ahead and commit and push the change now. I'll also be applying the other two follow-up patches that added new functionality after doing a bit more testing. > IIRC, the biggest problem was that the patch currently just messages > (a lot) when two xwidget views gets duplicated and Emacs can’t display > one. The sensible approach would be to show another view (I guess a > NSView with a NSTextField label in it?) but I’m not familiar enough > with the display engine (as I didn’t write the code from > scratch). Maybe someone might help on that part...? As this is new functionality on Macos, I don't think that should block the inclusion of the patch -- further display glitches here (this is an optional feature, after all) can be worked on afterwards. -- (domestic pets only, the antidote for overdose, milk.) bloggy blog: http://lars.ingebrigtsen.no ^ permalink raw reply [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2020-08-12 10:05 ` Lars Ingebrigtsen @ 2020-08-12 16:34 ` Alan Third 0 siblings, 0 replies; 41+ messages in thread From: Alan Third @ 2020-08-12 16:34 UTC (permalink / raw) To: Lars Ingebrigtsen Cc: 29565, veshboo, stefan, Richard Stallman, 조성빈 On Wed, Aug 12, 2020 at 12:05:46PM +0200, Lars Ingebrigtsen wrote: > I've taken your final version of the patch set and applied it to Emacs > 28 -- there were a couple things I had to merge by hand, but everything > seems to work fine, so I'll just go ahead and commit and push the change > now. I'll also be applying the other two follow-up patches that added > new functionality after doing a bit more testing. Thanks Lars, and also Jo Sungbin and Kwak Jaesup for writing it in the first place! -- Alan Third ^ permalink raw reply [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2020-08-10 19:06 ` Alan Third 2020-08-11 11:04 ` Lars Ingebrigtsen @ 2020-08-12 2:26 ` Richard Stallman 2020-08-12 4:13 ` Eli Zaretskii 1 sibling, 1 reply; 41+ messages in thread From: Richard Stallman @ 2020-08-12 2:26 UTC (permalink / raw) To: Alan Third; +Cc: alan, veshboo, stefan, 29565, pcr910303, larsi [[[ To any NSA and FBI agents reading my email: please consider ]]] [[[ whether defending the US Constitution against all enemies, ]]] [[[ foreign or domestic, requires you to follow Snowden's example. ]]] Since GCC supports Objective C, we would like people to implement in GCC any new Objective C features. Until someone does that, let's not use those features in our own Objective C code. -- Dr Richard Stallman Chief GNUisance of the GNU Project (https://gnu.org) Founder, Free Software Foundation (https://fsf.org) Internet Hall-of-Famer (https://internethalloffame.org) ^ permalink raw reply [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2020-08-12 2:26 ` Richard Stallman @ 2020-08-12 4:13 ` Eli Zaretskii 2020-08-12 4:27 ` Eli Zaretskii 2020-08-13 3:43 ` Richard Stallman 0 siblings, 2 replies; 41+ messages in thread From: Eli Zaretskii @ 2020-08-12 4:13 UTC (permalink / raw) To: rms, Richard Stallman, Alan Third Cc: alan, veshboo, stefan, 29565, pcr910303, larsi On August 12, 2020 5:26:11 AM GMT+03:00, Richard Stallman <rms@gnu.org> wrote: > >Since GCC supports Objective C, we would like people to implement >in GCC any new Objective C features. Until someone does that, >let's not use those features in our own Objective C code. We do not use these features in our code. Those features are used in the Apple system libraries. The result is that NS Emacs cannot be built using GCC at all, and that is not new. The patch being discussed adds xwidget support to an Emacs configuration that cannot be built with GCC, so I think it's okay to rely on clang for building it, as we did in the past. Especially since extending GCC's ObjC implementation is nig a priority for the GNU project. ^ permalink raw reply [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2020-08-12 4:13 ` Eli Zaretskii @ 2020-08-12 4:27 ` Eli Zaretskii 2020-08-13 3:43 ` Richard Stallman 1 sibling, 0 replies; 41+ messages in thread From: Eli Zaretskii @ 2020-08-12 4:27 UTC (permalink / raw) To: 29565, rms, rms, alan; +Cc: stefan, alan, veshboo, larsi, pcr910303 On August 12, 2020 7:13:14 AM GMT+03:00, Eli Zaretskii <eliz@gnu.org> wrote: > >We do not use these features in our code. Those features are used in >the Apple system libraries. Sorry I meant "system header files", not "system libraries". ^ permalink raw reply [flat|nested] 41+ messages in thread
* bug#29565: [PATCH] Support xwidget webkit for macOS X 2020-08-12 4:13 ` Eli Zaretskii 2020-08-12 4:27 ` Eli Zaretskii @ 2020-08-13 3:43 ` Richard Stallman 1 sibling, 0 replies; 41+ messages in thread From: Richard Stallman @ 2020-08-13 3:43 UTC (permalink / raw) To: Eli Zaretskii; +Cc: alan, veshboo, stefan, 29565, pcr910303, larsi [[[ To any NSA and FBI agents reading my email: please consider ]]] [[[ whether defending the US Constitution against all enemies, ]]] [[[ foreign or domestic, requires you to follow Snowden's example. ]]] > The patch being discussed adds xwidget support to an Emacs > configuration that cannot be built with GCC, so I think it's okay > to rely on clang for building it, as we did in the past. Yes. It is unfortunate, but not horribly so. -- Dr Richard Stallman Chief GNUisance of the GNU Project (https://gnu.org) Founder, Free Software Foundation (https://fsf.org) Internet Hall-of-Famer (https://internethalloffame.org) ^ permalink raw reply [flat|nested] 41+ messages in thread
end of thread, other threads:[~2020-08-13 3:43 UTC | newest] Thread overview: 41+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2017-12-04 16:44 bug#29565: [PATCH] Support xwidget webkit for macOS X Jaesup Kwak 2017-12-04 20:59 ` Alan Third 2017-12-05 6:01 ` Jaesup Kwak 2017-12-05 7:55 ` Jaesup Kwak 2017-12-05 20:00 ` Alan Third 2017-12-06 5:59 ` Jaesup Kwak 2017-12-06 6:20 ` Jaesup Kwak 2017-12-13 11:15 ` Jaesup Kwak 2017-12-13 11:27 ` Jaesup Kwak 2017-12-13 16:13 ` Jaesup Kwak 2017-12-15 2:01 ` Jaesup Kwak 2017-12-15 2:27 ` Jaesup Kwak 2017-12-15 16:06 ` Jaesup Kwak 2017-12-20 2:39 ` bug#29565: [PATCH] Fix compile failure for GTK xwidget (Bug#29565) Jaesup Kwak 2017-12-20 8:25 ` bug#29565: [PATCH] Enable plugins for ns xwidget webkit (Bug#29565) Jaesup Kwak 2017-12-21 4:12 ` bug#29565: [PATCH] Support file download and upload (Bug#29565) Jaesup Kwak 2018-03-30 11:48 ` bug#29565: [PATCH] Support xwidget webkit for macOS X Alan Third 2018-03-30 12:19 ` Jaesup Kwak 2019-09-28 23:52 ` Stefan Kangas 2020-08-10 13:55 ` Lars Ingebrigtsen 2020-08-10 19:06 ` Alan Third 2020-08-11 11:04 ` Lars Ingebrigtsen 2020-08-11 15:22 ` Eli Zaretskii 2020-08-11 16:26 ` Lars Ingebrigtsen 2020-08-11 18:28 ` Eli Zaretskii 2020-08-11 19:26 ` Lars Ingebrigtsen 2020-08-11 19:33 ` Eli Zaretskii 2020-08-12 10:44 ` Lars Ingebrigtsen 2020-08-11 19:56 ` Alan Third 2020-08-11 19:58 ` Lars Ingebrigtsen 2020-08-11 20:18 ` Alan Third 2020-08-11 20:29 ` Lars Ingebrigtsen 2020-08-11 20:35 ` Alan Third 2020-08-11 20:50 ` Lars Ingebrigtsen 2020-08-12 3:46 ` Unknown 2020-08-12 10:05 ` Lars Ingebrigtsen 2020-08-12 16:34 ` Alan Third 2020-08-12 2:26 ` Richard Stallman 2020-08-12 4:13 ` Eli Zaretskii 2020-08-12 4:27 ` Eli Zaretskii 2020-08-13 3:43 ` Richard Stallman
Code repositories for project(s) associated with this external index https://git.savannah.gnu.org/cgit/emacs.git https://git.savannah.gnu.org/cgit/emacs/org-mode.git This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.