From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!.POSTED.blaine.gmane.org!not-for-mail From: =?utf-8?B?7KGw7ISx67mI?= Newsgroups: gmane.emacs.devel Subject: Re: [PATCH v2] Add xwidget webkit support for macOS Cocoa Date: Sun, 2 Jun 2019 15:14:14 +0900 Message-ID: References: <20190525132727.94982-1-pcr910303@icloud.com> <679B30F5-4AB8-4F70-AEA1-A428538E928B@icloud.com> <768c3ba1-d494-154f-b752-ff32cfb3085a@cs.ucla.edu> <374E0E1B-6642-4338-A1B1-F373AC587FD2@icloud.com> Mime-Version: 1.0 (1.0) Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: quoted-printable Injection-Info: blaine.gmane.org; posting-host="blaine.gmane.org:195.159.176.226"; logging-data="227302"; mail-complaints-to="usenet@blaine.gmane.org" Cc: emacs-devel@gnu.org To: Paul Eggert Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Sun Jun 02 08:14:49 2019 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([209.51.188.17]) by blaine.gmane.org with esmtps (TLS1.0:RSA_AES_256_CBC_SHA1:256) (Exim 4.89) (envelope-from ) id 1hXJlY-000wv2-LS for ged-emacs-devel@m.gmane.org; Sun, 02 Jun 2019 08:14:49 +0200 Original-Received: from localhost ([127.0.0.1]:45459 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hXJlW-0003mR-Vb for ged-emacs-devel@m.gmane.org; Sun, 02 Jun 2019 02:14:47 -0400 Original-Received: from eggs.gnu.org ([209.51.188.92]:32784) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hXJlI-0003mI-Dr for emacs-devel@gnu.org; Sun, 02 Jun 2019 02:14:39 -0400 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hXJlB-0004nr-Mi for emacs-devel@gnu.org; Sun, 02 Jun 2019 02:14:32 -0400 Original-Received: from pv50p00im-ztdg10012001.me.com ([17.58.6.51]:48717) by eggs.gnu.org with esmtps (TLS1.0:DHE_RSA_AES_256_CBC_SHA1:32) (Exim 4.71) (envelope-from ) id 1hXJlB-0004h5-9k for emacs-devel@gnu.org; Sun, 02 Jun 2019 02:14:25 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=icloud.com; s=04042017; t=1559456058; bh=OrWoHM/DAvmY37298YsYnZBd8T9EIOwmfnWdG9ZWtT0=; h=Content-Type:Mime-Version:Subject:From:Date:Message-Id:To; b=F1YFtCWvUQ1ZxExi6She8ooJqkkt2QFFjm08UezdVRfje5vNyRhE9/euZZufzCi+G wzFYgghZhCjhDVHLVKdmWQAN1fq1cGTQw4yNg24XVQ6dZICxrf6+GVQqgPL7J6/pvJ L2R1zeK3lkgzkHcn7gZkRf1r8zihU+JxUnMmApTL6sRVrx8tBvY2PVTomk79kpxk3Z 3zRIZ3E6PFAhTOPiRcPA05N11w4TYeup4tLKbL2+2k/v/roYFKLWbD/sz7DJy4+BbI jroarkccgdP5MTit9fodz94vzDnBbF9/douXg7ZnFsbDqMXoiDdAqjB2gmG4rOwxHn PKTsmPmdtP1dA== Original-Received: from [10.237.223.72] (unknown [223.62.173.161]) by pv50p00im-ztdg10012001.me.com (Postfix) with ESMTPSA id 6E355280FF5; Sun, 2 Jun 2019 06:14:17 +0000 (UTC) X-Mailer: iPhone Mail (16E227) In-Reply-To: <374E0E1B-6642-4338-A1B1-F373AC587FD2@icloud.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:, , definitions=2019-06-02_01:, , signatures=0 X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 suspectscore=2 malwarescore=0 phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 mlxscore=0 mlxlogscore=999 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1812120000 definitions=main-1906020047 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 17.58.6.51 X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: "Emacs-devel" Xref: news.gmane.org gmane.emacs.devel:237190 Archived-At: It feels like I=E2=80=99m spamming the mailing list, (Sorry!) but can anybod= y review my patch? I would really like to use the xwidget functionality in m= acOS :-( 2019. 5. 29. =EC=98=A4=EC=A0=84 12:07, =EC=A1=B0=EC=84=B1=EB=B9=88 =EC=9E=91=EC=84=B1: > --- > 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 >=20 > 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=3D$with_features]) >=20 > 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 macO= S Cocoa)]) >=20 > ## 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 >=20 >=20 > dnl Enable xwidgets if GTK3 and WebKitGTK+ are available. > +dnl Enable xwidgets if macOS Cocoa and WebKit framework are available. > HAVE_XWIDGETS=3Dno > XWIDGETS_OBJ=3D > if test "$with_xwidgets" !=3D "no"; then > - test "$USE_GTK_TOOLKIT" =3D "GTK3" && test "$window_system" !=3D "none"= || > - AC_MSG_ERROR([xwidgets requested but gtk3 not used.]) > + if test "$USE_GTK_TOOLKIT" =3D "GTK3" && test "$window_system" !=3D "no= ne"; then > + WEBKIT_REQUIRED=3D2.12 > + WEBKIT_MODULES=3D"webkit2gtk-4.0 >=3D $WEBKIT_REQUIRED" > + EMACS_CHECK_MODULES([WEBKIT], [$WEBKIT_MODULES]) > + HAVE_XWIDGETS=3D$HAVE_WEBKIT > + XWIDGETS_OBJ=3D"xwidget.o" > + elif test "${NS_IMPL_COCOA}" =3D "yes"; then > + dnl FIXME: Check framework WebKit2 > + dnl WEBKIT_REQUIRED=3DM.m.p > + WEBKIT_LIBS=3D"-Wl,-framework -Wl,WebKit" > + WEBKIT_CFLAGS=3D"-I/System/Library/Frameworks/WebKit.framework/Header= s" > + HAVE_WEBKIT=3D"yes" > + HAVE_XWIDGETS=3D$HAVE_WEBKIT > + XWIDGETS_OBJ=3D"xwidget.o" > + NS_OBJC_OBJ=3D"$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 toolki= t or macOS Cocoa as window system.]) > + fi >=20 > - WEBKIT_REQUIRED=3D2.12 > - WEBKIT_MODULES=3D"webkit2gtk-4.0 >=3D $WEBKIT_REQUIRED" > - EMACS_CHECK_MODULES([WEBKIT], [$WEBKIT_MODULES]) > - HAVE_XWIDGETS=3D$HAVE_WEBKIT > test $HAVE_XWIDGETS =3D yes || > - AC_MSG_ERROR([xwidgets requested but WebKitGTK+ not found.]) > + AC_MSG_ERROR([xwidgets requested but WebKitGTK+ or WebKit framework n= ot found.]) >=20 > - XWIDGETS_OBJ=3Dxwidget.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_SCR= OLL_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-po= s)) > (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) >=20 > ;;;###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)))) >=20 > +(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) >=20 > ;;similar to image mode bindings > (define-key map (kbd "SPC") 'xwidget-webkit-scroll-up) > + (define-key map (kbd "S-SPC") 'xwidget-webkit-scroll-do= wn) > (define-key map (kbd "DEL") 'xwidget-webkit-scroll-down= ) >=20 > - (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) >=20 > - (define-key map [remap scroll-down] 'xwidget-webkit-scroll-do= wn) > + (define-key map [remap scroll-down] 'xwidget-webkit-scroll-do= wn-line) > (define-key map [remap scroll-down-command] 'xwidget-webkit-scroll-down= ) >=20 > (define-key map [remap forward-char] 'xwidget-webkit-scroll-forw= ard) > (define-key map [remap backward-char] 'xwidget-webkit-scroll-back= ward) > (define-key map [remap right-char] 'xwidget-webkit-scroll-forw= ard) > (define-key map [remap left-char] 'xwidget-webkit-scroll-back= ward) > - (define-key map [remap previous-line] 'xwidget-webkit-scroll-do= wn) > - (define-key map [remap next-line] 'xwidget-webkit-scroll-up= ) > + (define-key map [remap previous-line] 'xwidget-webkit-scroll-do= wn-line) > + (define-key map [remap next-line] 'xwidget-webkit-scroll-up= -line) >=20 > ;; (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-bott= om) > + > + ;; 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'.") >=20 > @@ -144,19 +175,48 @@ xwidget-webkit-zoom-out > (interactive) > (xwidget-webkit-zoom (xwidget-webkit-current-session) -0.1)) >=20 > -(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))) >=20 > (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);")) >=20 > ;; 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))) >=20 > (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-na= me))) > ((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)))))) >=20 > (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)) >=20 > +;;; 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-n= ame))) > + (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 =3D %s; > +var xwSearchRepeat =3D %s; > +var xwSearchString =3D '%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 =3D 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 >=20 > (defvar xwidget-webkit-last-session-buffer nil) >=20 > @@ -303,7 +467,7 @@ xwidget-webkit-activeelement-js" >=20 > " >=20 > - "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 recur= sion > ;; - the js "for each" construct misbehaved on the "frames" collection > @@ -313,29 +477,35 @@ xwidget-webkit-activeelement-js" > ) >=20 > (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=3D'%s'" str)))) > + > (xwidget-webkit-execute-script > xww > (concat xwidget-webkit-activeelement-js " > (function () { > var res =3D 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=3D'%s'" str))))))) > + 'xwidget-webkit-insert-string-cb))) >=20 > (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))) >=20 > +;; 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))) >=20 > (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 (selecte= d-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)) >=20 > (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)) >=20 > (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) "")))) >=20 > ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; > (defun xwidget-webkit-get-selection (proc) > @@ -533,10 +721,9 @@ xwidget-webkit-get-selection > proc)) >=20 > (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)) >=20 > ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; > ;; Xwidget plist management (similar to the process plist functions) > diff --git a/nextstep/templates/Info.plist.inb/nextstep/templates/Info.pli= st.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 . > > NSAppleScriptEnabled > YES > - NSAppleEventsUsageDescription > - Emacs requires permission to send AppleEvents to other ap= plications. > + NSAppleEventsUsageDescription > + Emacs requires permission to send AppleEvents to other applic= ations. > + > + NSAppTransportSecurity > + > + NSAllowsArbitraryLoads > + > + > > > 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 =3D > 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 */ >=20 > + 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 (chris@nice.ch) > #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 (callab= le from C) > enum glyph_row_area area) > /* Get the row as an NSRect. */ > { > - struct frame *f =3D XFRAME (WINDOW_FRAME (w)); > NSRect rect; > int window_x, window_y, window_width; >=20 > @@ -2412,7 +2412,7 @@ so some key presses (TAB) are swallowed by the syste= m. */ > } >=20 > 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 d= ragging) > /* ---------------------------------------------------------------------= --- > 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 sys= tem. */ > { > struct ns_display_info *dpyinfo =3D FRAME_DISPLAY_INFO (frame); > NSRect *r; > + BOOL force_update =3D NO; >=20 > // NSTRACE ("note_mouse_movement"); >=20 > dpyinfo->last_mouse_motion_frame =3D frame; > r =3D &dpyinfo->last_mouse_glyph; >=20 > + /* 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 =3D 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= . */ >=20 > - /* Has movement gone beyond last rect we were tracking? */ > - if (x < r->origin.x || x >=3D r->origin.x + r->size.width > + /* Has movement gone beyond last rect we were tracking? */ > + if (force_update || x < r->origin.x || x >=3D r->origin.x + r->size.wid= th > || y < r->origin.y || y >=3D r->origin.y + r->size.height) > { > ns_update_begin (frame); > @@ -4170,6 +4177,10 @@ overwriting cursor (usually when cursor on a tab). = */ > } > break; >=20 > + 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 =3D FRAME_DISPLAY_INFO (emacsframe); > Lisp_Object frame; > NSPoint pt; > + BOOL dragging; >=20 > NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "[EmacsView mouseMoved:]"); >=20 > @@ -6865,7 +6877,8 @@ - (void)mouseMoved: (NSEvent *)e > last_mouse_window =3D window; > } >=20 > - if (!ns_note_mouse_movement (emacsframe, pt.x, pt.y)) > + dragging =3D (e.type =3D=3D NSEventTypeLeftMouseDragged); > + if (!ns_note_mouse_movement (emacsframe, pt.x, pt.y, dragging)) > help_echo_string =3D previous_help_echo_string; >=20 > 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 . */ > + > +#ifndef NSXWIDGET_H_INCLUDED > +#define NSXWIDGET_H_INCLUDED > + > +/* This file can be included from non-objc files through 'xwidget.h'. */= > +#ifdef __OBJC__ > +#import > +#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 *scr= ipt, > + 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..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 . */ > + > +#include > + > +#include "lisp.h" > +#include "blockinput.h" > +#include "dispextern.h" > +#include "buffer.h" > +#include "frame.h" > +#include "nsterm.h" > +#include "xwidget.h" > + > +#import > +#import > + > +/* 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 > + > +@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 =3D [[WKUserContentController alloc] i= nit]; > + configuration.userContentController =3D scriptor; > + > + /* Enable inspect element context menu item for debugging. */ > + [configuration.preferences setValue:@YES > + forKey:@"developerExtrasEnabled"]; > + > + Lisp_Object enablePlugins =3D > + Fintern (build_string ("xwidget-webkit-enable-plugins"), Qnil); > + if (!EQ (Fsymbol_value (enablePlugins), Qnil)) > + configuration.preferences.plugInsEnabled =3D YES; > + > + self =3D [super initWithFrame:frame configuration:configuration]; > + if (self) > + { > + self.xw =3D xw; > + self.urlScriptBlocked =3D [[NSMutableDictionary alloc] init]; > + self.navigationDelegate =3D self; > + self.UIDelegate =3D self; > + self.customUserAgent =3D > + @"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: > + WKUserScriptInjectionTimeAtDocumentStar= t > + 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 *)navigationRespo= nse > +decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler > +{ > + if (!navigationResponse.canShowMIMEType) > + { > + NSString *url =3D navigationResponse.response.URL.absoluteString; > + NSString *mimetype =3D navigationResponse.response.MIMEType; > + NSString *filename =3D navigationResponse.response.suggestedFilenam= e; > + decisionHandler (WKNavigationResponsePolicyCancel); > + store_xwidget_response_callback_event (self.xw, > + url.UTF8String, > + mimetype.UTF8String, > + filename.UTF8String); > + return; > + } > + decisionHandler (WKNavigationResponsePolicyAllow); > + > + self.urlScriptBlocked[navigationResponse.response.URL] =3D > + [NSNumber numberWithBool:NO]; > + if ([navigationResponse.response isKindOfClass:[NSHTTPURLResponse class= ]]) > + { > + NSDictionary *headers =3D > + ((NSHTTPURLResponse *) navigationResponse.response).allHeaderFiel= ds; > + NSString *value =3D headers[@"Content-Security-Policy"]; > + if (value) > + { > + /* TODO: Sloppy parsing of 'Content-Security-Policy' value. */= > + NSRange sandbox =3D [value rangeOfString:@"sandbox"]; > + if (sandbox.location !=3D NSNotFound > + && (sandbox.location =3D=3D 0 > + || [value characterAtIndex:(sandbox.location - 1)] =3D=3D= ' ' > + || [value characterAtIndex:(sandbox.location - 1)] =3D=3D= ';')) > + { > + NSRange allowScripts =3D [value rangeOfString:@"allow-scrip= ts"]; > + if (allowScripts.location =3D=3D NSNotFound > + || allowScripts.location < sandbox.location) > + self.urlScriptBlocked[navigationResponse.response.URL] =3D= > + [NSNumber numberWithBool:YES]; > + } > + } > + } > +} > + > +/* No additional new webview or emacs window will be created > + for . */ > +- (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 *URLs))completionHandler > +{ > + NSOpenPanel *openPanel =3D [NSOpenPanel openPanel]; > + openPanel.canChooseFiles =3D YES; > + openPanel.canChooseDirectories =3D NO; > + openPanel.allowsMultipleSelection =3D parameters.allowsMultipleSelectio= n; > + if ([openPanel runModal] =3D=3D 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 =3D Fintern (build_string ("isearch-mode"), Qnil); > + Lisp_Object val =3D 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 =3D result; /* __NSCFBoolean */ > + if (!hasFocus.boolValue) > + [self.xw->xv->emacswindow keyDown:event]; > + else > + [super keyDown:event]; > + } > + }]; > +} > + > +- (void)interpretKeyEvents:(NSArray *)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 =3D > + @"function xwHasFocus() {" > + @" var ae =3D document.activeElement;" > + @" if (ae) {" > + @" var name =3D ae.nodeName;" > + @" return name =3D=3D 'INPUT' || name =3D=3D 'TEXTAREA';" > + @" } else {" > + @" return false;" > + @" }" > + @"}" > + @"function xwKeyDown(event) {" > + @" if (event.ctrlKey && event.key =3D=3D '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 *)userContentContr= oller > + 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 !=3D NULL && > + [xw->xwWidget isKindOfClass:WKWebView.class]; > +} > + > +Lisp_Object > +nsxwidget_webkit_uri (struct xwidget *xw) > +{ > + XwWebView *xwWebView =3D (XwWebView *) xw->xwWidget; > + return build_string_with_nsstr (xwWebView.URL.absoluteString); > +} > + > +Lisp_Object > +nsxwidget_webkit_title (struct xwidget *xw) > +{ > + XwWebView *xwWebView =3D (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 =3D (XwWebView *) xw->xwWidget; > + NSString *urlString =3D [NSString stringWithUTF8String:uri]; > + NSURL *url =3D [NSURL URLWithString:urlString]; > + NSURLRequest *urlRequest =3D [NSURLRequest requestWithURL:url]; > + [xwWebView loadRequest:urlRequest]; > +} > + > +void > +nsxwidget_webkit_goto_history (struct xwidget *xw, int rel_pos) > +{ > + XwWebView *xwWebView =3D (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 =3D (XwWebView *) xw->xwWidget; > + xwWebView.magnification +=3D zoom_change; > + /* TODO: setMagnification:centeredAtPoint. */ > +} > + > +/* Build lisp string */ > +static Lisp_Object > +build_string_with_nsstr (NSString *nsstr) > +{ > + const char *utfstr =3D [nsstr UTF8String]; > + NSUInteger bytes =3D [nsstr lengthOfBytesUsingEncoding:NSUTF8StringEnco= ding]; > + 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 =3D=3D 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 =3D (NSNumber *) value; > + char type =3D nsnum.objCType[0]; > + if (type =3D=3D 'c') /* __NSCFBoolean has type character 'c'. */ > + return nsnum.boolValue? Qt : Qnil; > + else > + { > + if (type =3D=3D 'i' || type =3D=3D 'l') > + return make_int (nsnum.longValue); > + else if (type =3D=3D 'f' || type =3D=3D 'd') > + return make_float (nsnum.doubleValue); > + /* else fall through. */ > + } > + } > + else if ([value isKindOfClass:NSArray.class]) > + { > + NSArray *nsarr =3D (NSArray *) value; > + EMACS_INT n =3D nsarr.count; > + Lisp_Object obj; > + struct Lisp_Vector *p =3D allocate_vector (n); > + > + for (ptrdiff_t i =3D 0; i < n; ++i) > + p->contents[i] =3D js_to_lisp ([nsarr objectAtIndex:i]); > + XSETVECTOR (obj, p); > + return obj; > + } > + else if ([value isKindOfClass:NSDictionary.class]) > + { > + NSDictionary *nsdict =3D (NSDictionary *) value; > + NSArray *keys =3D nsdict.allKeys; > + ptrdiff_t n =3D keys.count; > + Lisp_Object obj; > + struct Lisp_Vector *p =3D allocate_vector (n); > + > + for (ptrdiff_t i =3D 0; i < n; ++i) > + { > + NSString *prop_key =3D (NSString *) [keys objectAtIndex:i]; > + id prop_value =3D [nsdict valueForKey:prop_key]; > + p->contents[i] =3D 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 =3D (XwWebView *) xw->xwWidget; > + if ([xwWebView.urlScriptBlocked[xwWebView.URL] boolValue]) > + { > + message ("Javascript is blocked by 'CSP: sandbox'."); > + return; > + } > + > + NSString *javascriptString =3D [NSString stringWithUTF8String:script]; > + [xwWebView evaluateJavaScript:javascriptString > + completionHandler:^(id result, NSError *error) { > + if (error) > + { > + NSLog (@"evaluateJavaScript error : %@", error.localizedDescrip= tion); > + NSLog (@"error script=3D%@", javascriptString); > + } > + else if (result && FUNCTIONP (fun)) > + { > + // NSLog (@"result=3D%@, type=3D%@", result, [result class]); > + Lisp_Object lisp_value =3D 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 =3D NSMakeRect (0, 0, xw->width, xw->height); > + xw->xwWidget =3D [[XwWebView alloc] > + initWithFrame:rect > + configuration:[[WKWebViewConfiguration alloc] init] > + xwidget:xw]; > + xw->xwWindow =3D [[XwWindow alloc] > + initWithFrame:rect]; > + [xw->xwWindow addSubview:xw->xwWidget]; > + xw->xv =3D NULL; /* for 1 to 1 relationship of webkit2. */ > + unblock_input (); > +} > + > +void > +nsxwidget_kill (struct xwidget *xw) > +{ > + if (xw) > + { > + WKUserContentController *scriptor =3D > + ((XwWebView *) xw->xwWidget).configuration.userContentController;= > + [scriptor removeAllUserScripts]; > + [scriptor removeScriptMessageHandlerForName:@"keyDown"]; > + [scriptor release]; > + if (xw->xv) > + xw->xv->model =3D 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 =3D 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 =3D x; > + xv->y =3D y; > + xv->clip_left =3D 0; > + xv->clip_right =3D xw->width; > + xv->clip_top =3D 0; > + xv->clip_bottom =3D xw->height; > + > + xv->xvWindow =3D [[XvWindow alloc] > + initWithFrame:NSMakeRect (x, y, xw->width, xw->height)= ]; > + xv->xvWindow.xw =3D xw; > + xv->xvWindow.xv =3D xv; > + > + xw->xv =3D xv; /* For 1 to 1 relationship of webkit2. */ > + [xv->xvWindow addSubview:xw->xwWindow]; > + > + xv->emacswindow =3D 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 =3D XXWIDGET (xv->model); > + [xw->xwWindow removeFromSuperviewWithoutNeedingDisplay]; > + xw->xv =3D NULL; /* Now model has no view. */ > + } > + [xv->xvWindow removeFromSuperviewWithoutNeedingDisplay]; > + [xv->xvWindow release]; > +} > + > +void > +nsxwidget_show_view (struct xwidget_view *xv) > +{ > + xv->hidden =3D 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 =3D 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 =3D xv->xvWindow.xw; > + [xww->xwWindow setFrameOrigin:NSMakePoint (x, y)]; > +} > + > +void > +nsxwidget_set_needsdisplay (struct xwidget_view *xv) > +{ > + xv->xvWindow.needsDisplay =3D 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 . */ >=20 > #include > +#include /* FIXME: Emacs error? message? instead of printf. */= >=20 > #include "xwidget.h" >=20 > #include "lisp.h" > #include "blockinput.h" > +#include "dispextern.h" > #include "frame.h" > #include "keyboard.h" > #include "gtkutil.h" > +#include "termhooks.h" > +#include "window.h" >=20 > +/* Include xwidget bottom end headers. */ > +#if defined (USE_GTK) > #include > #include > +#elif defined (NS_IMPL_COCOA) > +#include "nsxwidget.h" > +#endif >=20 > /* 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 >=20 > static struct xwidget * > allocate_xwidget (void) > @@ -55,6 +66,7 @@ #define XSETXWIDGET_VIEW(a, b) XSETPSEUDOVECTOR (a, b, P= VEC_XWIDGET_VIEW) >=20 > 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 >=20 >=20 > 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 =3D false; > XSETXWIDGET (val, xw); > Vxwidget_list =3D Fcons (val, Vxwidget_list); > - xw->widgetwindow_osr =3D NULL; > - xw->widget_osr =3D NULL; > xw->plist =3D Qnil; >=20 > +#if defined (USE_GTK) > + xw->widgetwindow_osr =3D NULL; > + xw->widget_osr =3D NULL; > if (EQ (xw->type, Qwebkit)) > { > block_input (); > @@ -159,6 +175,9 @@ DEFUN ("make-xwidget", >=20 > unblock_input (); > } > +#elif defined (NS_IMPL_COCOA) > + nsxwidget_init (xw); > +#endif >=20 > return val; > } > @@ -194,6 +213,7 @@ xwidget_hidden (struct xwidget_view *xv) > return xv->hidden; > } >=20 > +#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); >=20 > return FALSE; > } > +#endif /* USE_GTK */ >=20 > -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); > } >=20 > -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 =3D XWIDGET_EVENT; > + event.frame_or_window =3D Qnil; > + event.arg =3D 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, > } >=20 >=20 > +#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, Gdk= Event *event, > gtk_widget_get_window (xv->widget)); > return FALSE; > } > +#endif /* USE_GTK */ >=20 >=20 > /* 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) > { >=20 > +#if defined (USE_GTK) > if (!xg_gtk_initialized) > error ("xwidget_init_view: GTK has not been initialized"); > +#endif >=20 > struct xwidget_view *xv =3D 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); >=20 > +#if defined (USE_GTK) > if (EQ (xww->type, Qwebkit)) > { > xv->widget =3D gtk_drawing_area_new (); > @@ -598,6 +644,10 @@ xwidget_init_view (struct xwidget *xww, > xv->x =3D x; > xv->y =3D 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 >=20 > return xv; > } > @@ -610,24 +660,59 @@ x_draw_xwidget_glyph_string (struct glyph_string *s)= > initialization. */ > struct xwidget *xww =3D s->xwidget; > struct xwidget_view *xv =3D 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; >=20 > int x =3D s->x; > - int y =3D s->y + (s->height / 2) - (xww->height / 2); > + int y =3D s->y; >=20 > /* 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 =3D 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 window= s."); > + return; > + } > + } > + xv =3D xwidget_init_view (xww, s, x, y); > + } > +#endif >=20 > 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 !=3D text_area_width || xww->height !=3D text_area_h= eight)) > + { > + Lisp_Object xwl; > + XSETXWIDGET (xwl, xww); > + Fxwidget_resize (xwl, > + make_int (text_area_width), > + make_int (text_area_height)); > + } > + > clip_left =3D max (0, text_area_x - x); > clip_right =3D 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) >=20 > /* 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 > + } >=20 > /* 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 !=3D clip_bottom > || xv->clip_top !=3D clip_top || xv->clip_left !=3D 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 >=20 > xv->clip_right =3D clip_right; > xv->clip_bottom =3D 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 > } > } >=20 > -/* 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 !=3D 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 =3D 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; \ > } >=20 > +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 =3D 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 =3D 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 =3D 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 br= owse 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 =3D 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; > } >=20 > @@ -717,14 +883,19 @@ DEFUN ("xwidget-webkit-zoom", > if (FLOATP (factor)) > { > double zoom_change =3D 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; > } >=20 > +#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 s= cript, Lisp_Object fun) > ASET (cbs, idx, Fcons (make_mint_ptr (xlispstrdup (script)), fun)); > return idx; > } > +#endif >=20 > 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); >=20 > script =3D ENCODE_SYSTEM (script); >=20 > +#if defined (USE_GTK) > /* Protect script and fun during GC. */ > intptr_t idx =3D save_script_callback (xw, script, fun); >=20 > @@ -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; > } >=20 > @@ -793,6 +972,7 @@ DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_res= ize, 3, 3, 0, > xw->height =3D h; >=20 > /* 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_res= ize, 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 >=20 > for (Lisp_Object tail =3D Vxwidget_view_list; CONSP (tail); tail =3D XCDR= (tail)) > { > @@ -808,8 +991,14 @@ DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_re= size, 3, 3, 0, > { > struct xwidget_view *xv =3D XXWIDGET_VIEW (XCAR (tail)); > if (XXWIDGET (xv->model) =3D=3D xw) > + { > +#if defined (USE_GTK) > gtk_widget_set_size_request (GTK_WIDGET (xv->widget), xw->wid= th, > xw->height); > +#elif defined (NS_IMPL_COCOA) > + nsxwidget_resize_view(xv, xw->width, xw->height); > +#endif > + } > } > } >=20 > @@ -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 > } >=20 > DEFUN ("xwidgetp", > @@ -907,14 +1100,19 @@ DEFUN ("delete-xwidget-view", > { > CHECK_XWIDGET_VIEW (xwidget_view); > struct xwidget_view *xv =3D XXWIDGET_VIEW (xwidget_view); > - gtk_widget_destroy (xv->widgetwindow); > Vxwidget_view_list =3D 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; > } >=20 > @@ -1020,7 +1218,10 @@ syms_of_xwidget (void) > defsubr (&Sxwidget_query_on_exit_flag); > defsubr (&Sset_xwidget_query_on_exit_flag); >=20 > + 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 gl= yph_matrix *matrix) > xwidget_end_redisplay (w->current_matrix); */ > struct xwidget_view *xv > =3D 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 gly= ph_matrix *matrix) > if (XWINDOW (xv->w) =3D=3D 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 =3D 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; >=20 > #ifdef HAVE_XWIDGETS > -# include > + > +#if defined (USE_GTK) > +#include > +#elif defined (NS_IMPL_COCOA) && defined (__OBJC__) > +#import > +#import "nsxwidget.h" > +#endif >=20 > struct xwidget > { > @@ -54,9 +60,25 @@ #define XWIDGET_H_INCLUDED > int height; > int width; >=20 > +#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 >=20 > /* 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; >=20 > +#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) {} > --=20 > 2.17.2 (Apple Git-113)