emacs-devel
[Top][All Lists]
Advanced

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

Re: [PATCH v2] Add xwidget webkit support for macOS Cocoa


From: 조성빈
Subject: Re: [PATCH v2] Add xwidget webkit support for macOS Cocoa
Date: Sun, 2 Jun 2019 15:14:14 +0900

It feels like I’m spamming the mailing list, (Sorry!) but can anybody review my 
patch? I would really like to use the xwidget functionality in macOS :-(

2019. 5. 29. 오전 12:07, 조성빈 <address@hidden> 작성:

> ---
> configure.ac                     |  34 +-
> lisp/xwidget.el                  | 315 ++++++++++++----
> nextstep/templates/Info.plist.in |  12 +-
> src/Makefile.in                  |   1 +
> src/emacs.c                      |   2 +-
> src/nsterm.m                     |  23 +-
> src/nsxwidget.h                  |  80 ++++
> src/nsxwidget.m                  | 611 +++++++++++++++++++++++++++++++
> src/xwidget.c                    | 261 ++++++++++++-
> src/xwidget.h                    |  50 ++-
> 10 files changed, 1288 insertions(+), 101 deletions(-)
> create mode 100644 src/nsxwidget.h
> create mode 100644 src/nsxwidget.m
> 
> diff --git a/configure.ac b/configure.ac
> index 0f1fd5d26e..443ff4e6c2 100644
> --- a/configure.ac
> +++ b/configure.ac
> @@ -483,7 +483,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.
> @@ -2804,20 +2804,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)
> @@ -5656,7 +5670,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 662a854ac3..f93b4bedba 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))
> @@ -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)
> @@ -96,6 +103,22 @@ xwidget-webkit-browse-url
>        (xwidget-webkit-new-session url)
>      (xwidget-webkit-goto-url url))))
> 
> +(defun xwidget-webkit-cx2 ()
> +  "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-cx3 ()
> +  "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
> @@ -103,34 +126,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 (kbd "C-x 2") 'xwidget-webkit-cx2)
> +      (define-key map (kbd "C-x 3") 'xwidget-webkit-cx3))
>    map)
>  "Keymap for `xwidget-webkit-mode'.")
> 
> @@ -144,19 +175,48 @@ 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)
> +   (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)
> +  "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)
> +          (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
> +  "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."
> @@ -184,7 +244,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.
> @@ -192,7 +252,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 +268,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)
> @@ -219,43 +278,148 @@ 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 '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)))
> -               (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)
> +(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)
> +    (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.
> +
> +(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
> +  "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'.")
> +
> (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 . ,(xwidget-webkit-current-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 +467,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 +477,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 +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 +669,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)))
> @@ -506,23 +690,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 ()
> +  "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)
> @@ -533,10 +721,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.inb/nextstep/templates/Info.plist.in
> index c1e50a8409..76efe95673 100644
> --- a/nextstep/templates/Info.plist.in
> +++ b/nextstep/templates/Info.plist.in
> @@ -675,7 +675,15 @@ 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>
> </dict>
> </plist>
> diff --git a/src/Makefile.in b/src/Makefile.in
> index 3aab5270a4..77200440af 100644
> --- a/src/Makefile.in
> +++ b/src/Makefile.in
> @@ -426,6 +426,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 ftxfont.o gtkutil.o \
> diff --git a/src/emacs.c b/src/emacs.c
> index fd46540ce2..e568724ac2 100644
> --- a/src/emacs.c
> +++ b/src/emacs.c
> @@ -1778,7 +1778,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 ();
> @@ -1855,6 +1854,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 0cae5e9d44..14e11580c1 100644
> --- a/src/nsterm.m
> +++ b/src/nsterm.m
> @@ -49,6 +49,7 @@ Updated by Christian Limpach (address@hidden)
> #include "nsterm.h"
> #include "systime.h"
> #include "character.h"
> +#include "xwidget.h"
> #include "fontset.h"
> #include "composite.h"
> #include "ccl.h"
> @@ -827,7 +828,6 @@ Free a pool and temporary objects it refers to (callable 
> from C)
>               enum glyph_row_area area)
> /* Get the row as an NSRect.  */
> {
> -  struct frame *f = XFRAME (WINDOW_FRAME (w));
>  NSRect rect;
>  int window_x, window_y, window_width;
> 
> @@ -2412,7 +2412,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
> @@ -2421,17 +2421,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);
> @@ -4170,6 +4177,10 @@ overwriting cursor (usually when cursor on a tab).  */
>        }
>      break;
> 
> +    case XWIDGET_GLYPH:
> +      x_draw_xwidget_glyph_string (s);
> +      break;
> +
>    case STRETCH_GLYPH:
>      ns_dumpglyphs_stretch (s);
>      break;
> @@ -6823,6 +6834,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:]");
> 
> @@ -6865,7 +6877,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__
> address@hidden XwWindow : NSView
> address@hidden struct xwidget *xw;
> address@hidden
> +#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__
> address@hidden XvWindow : NSView
> address@hidden struct xwidget *xw;
> address@hidden struct xwidget_view *xv;
> address@hidden
> +#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..0119087f47
> --- /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.  */
> +
> address@hidden XwWebView : WKWebView
> +<WKNavigationDelegate, WKUIDelegate, WKScriptMessageHandler>
> address@hidden struct xwidget *xw;
> +/* Map url to whether javascript is blocked by
> +   'Content-Security-Policy' sandbox without allow-scripts.  */
> address@hidden(retain) NSMutableDictionary *urlScriptBlocked;
> address@hidden
> address@hidden 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_response_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];
> +    }
> +}
> +
> address@hidden
> +
> +/* 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.  */
> +
> address@hidden XwWindow
> +- (BOOL)isFlipped { return YES; }
> address@hidden
> +
> +/* 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.  */
> +
> address@hidden XvWindow : NSView
> +- (BOOL)isFlipped { return YES; }
> address@hidden
> +
> +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 2486a2d4da..a6bf13821d 100644
> --- a/src/xwidget.c
> +++ b/src/xwidget.c
> @@ -18,25 +18,36 @@ Copyright (C) 2011-2019 Free Software Foundation, Inc.
> 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
> 
> /* Suppress GCC deprecation warnings starting in WebKitGTK+ 2.21.1 for
>   webkit_javascript_result_get_global_context and
>   webkit_javascript_result_get_value (Bug#33679).
>   FIXME: Use the JavaScriptCore GLib API instead, and remove this hack.  */
> +#if defined (USE_GTK)
> #if WEBKIT_CHECK_VERSION (2, 21, 1) && GNUC_PREREQ (4, 2, 0)
> # pragma GCC diagnostic ignored "-Wdeprecated-declarations"
> #endif
> +#endif
> 
> static struct xwidget *
> allocate_xwidget (void)
> @@ -55,6 +66,7 @@ #define XSETXWIDGET_VIEW(a, b) XSETPSEUDOVECTOR (a, b, 
> PVEC_XWIDGET_VIEW)
> 
> 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);
> @@ -68,6 +80,7 @@ webkit_decide_policy_cb (WebKitWebView *,
>                         WebKitPolicyDecision *,
>                         WebKitPolicyDecisionType,
>                         gpointer);
> +#endif
> 
> 
> DEFUN ("make-xwidget",
> @@ -85,8 +98,10 @@ DEFUN ("make-xwidget",
>   Lisp_Object title, Lisp_Object width, Lisp_Object height,
>   Lisp_Object arguments, Lisp_Object buffer)
> {
> +#if defined (USE_GTK)
>  if (!xg_gtk_initialized)
>    error ("make-xwidget: GTK has not been initialized");
> +#endif
>  CHECK_SYMBOL (type);
>  CHECK_FIXNAT (width);
>  CHECK_FIXNAT (height);
> @@ -101,10 +116,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;
> 
> +#if defined (USE_GTK)
> +  xw->widgetwindow_osr = NULL;
> +  xw->widget_osr = NULL;
>  if (EQ (xw->type, Qwebkit))
>    {
>      block_input ();
> @@ -159,6 +175,9 @@ DEFUN ("make-xwidget",
> 
>      unblock_input ();
>    }
> +#elif defined (NS_IMPL_COCOA)
> +  nsxwidget_init (xw);
> +#endif
> 
>  return val;
> }
> @@ -194,6 +213,7 @@ xwidget_hidden (struct xwidget_view *xv)
>  return xv->hidden;
> }
> 
> +#if defined (USE_GTK)
> static void
> xwidget_show_view (struct xwidget_view *xv)
> {
> @@ -227,13 +247,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)
> {
> @@ -247,7 +268,27 @@ store_xwidget_event_string (struct xwidget *xw, const 
> char *eventname,
>  kbd_buffer_store_event (&event);
> }
> 
> -static void
> +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,
>                                 Lisp_Object argument)
> @@ -263,6 +304,7 @@ store_xwidget_js_callback_event (struct xwidget *xw,
> }
> 
> 
> +#if defined (USE_GTK)
> void
> webkit_view_load_changed_cb (WebKitWebView *webkitwebview,
>                             WebKitLoadEvent load_event,
> @@ -520,6 +562,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.  */
> @@ -529,8 +572,10 @@ xwidget_init_view (struct xwidget *xww,
>                   int x, int y)
> {
> 
> +#if defined (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;
> @@ -541,6 +586,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 ();
> @@ -598,6 +644,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;
> }
> @@ -610,24 +660,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_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));
> @@ -650,8 +735,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
> @@ -662,10 +753,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;
> @@ -679,21 +776,65 @@ 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"); \
> +      message ("ERROR xwidget does not hold a webkit instance\n");    \
>      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,
> @@ -703,7 +844,32 @@ DEFUN ("xwidget-webkit-goto-uri",
>  WEBKIT_FN_INIT ();
>  CHECK_STRING (uri);
>  uri = ENCODE_FILE (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;
> +}
> +
> +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 (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;
> }
> 
> @@ -717,14 +883,19 @@ DEFUN ("xwidget-webkit-zoom",
>  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;
> }
> 
> +#if defined(USE_GTK)
> /* Save script and fun in the script/callback save vector and return
>   its index.  */
> static ptrdiff_t
> @@ -746,6 +917,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,
> @@ -757,11 +929,15 @@ DEFUN ("xwidget-webkit-execute-script",
> {
>  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);
> 
>  script = ENCODE_SYSTEM (script);
> 
> +#if defined (USE_GTK)
>  /* Protect script and fun during GC.  */
>  intptr_t idx = save_script_callback (xw, script, fun);
> 
> @@ -775,6 +951,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;
> }
> 
> @@ -793,6 +972,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,
> @@ -801,6 +981,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))
>    {
> @@ -808,8 +991,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
> +            }
>        }
>    }
> 
> @@ -828,9 +1017,13 @@ DEFUN ("xwidget-size-request",
>  (Lisp_Object xwidget)
> {
>  CHECK_XWIDGET (xwidget);
> +#if defined (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",
> @@ -907,14 +1100,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;
> }
> 
> @@ -1020,7 +1218,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");
> @@ -1191,11 +1392,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
>        }
>      }
>    }
> @@ -1212,9 +1421,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
> +                }
>            }
>        }
>    }
> @@ -1233,6 +1454,7 @@ 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);
> @@ -1246,6 +1468,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 1b6368daab..0042912fc6 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_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);
> +#endif
> #else
> INLINE_HEADER_BEGIN
> INLINE void syms_of_xwidget (void) {}
> -- 
> 2.17.2 (Apple Git-113)




reply via email to

[Prev in Thread] Current Thread [Next in Thread]