· 6 years ago · Sep 11, 2019, 01:22 PM
1---
2 configure.ac | 34 +-
3 lisp/xwidget.el | 326 +++++++++++++----
4 nextstep/templates/Info.plist.in | 12 +-
5 src/Makefile.in | 1 +
6 src/emacs.c | 2 +-
7 src/nsterm.m | 22 +-
8 src/nsxwidget.h | 80 ++++
9 src/nsxwidget.m | 611 +++++++++++++++++++++++++++++++
10 src/xwidget.c | 255 ++++++++++++-
11 src/xwidget.h | 50 ++-
12 10 files changed, 1289 insertions(+), 104 deletions(-)
13 create mode 100644 src/nsxwidget.h
14 create mode 100644 src/nsxwidget.m
15
16diff --git a/configure.ac b/configure.ac
17index c093d8650d..9685b65862 100644
18--- a/configure.ac
19+++ b/configure.ac
20@@ -484,7 +484,7 @@ AC_DEFUN
21 [with_file_notification=$with_features])
22
23 OPTION_DEFAULT_OFF([xwidgets],
24- [enable use of some gtk widgets in Emacs buffers (requires gtk3)])
25+ [enable use of xwidgets in Emacs buffers (requires gtk3 or macOS Cocoa)])
26
27 ## For the times when you want to build Emacs but don't have
28 ## a suitable makeinfo, and can live without the manuals.
29@@ -2808,20 +2808,34 @@ AC_DEFUN
30
31
32 dnl Enable xwidgets if GTK3 and WebKitGTK+ are available.
33+dnl Enable xwidgets if macOS Cocoa and WebKit framework are available.
34 HAVE_XWIDGETS=no
35 XWIDGETS_OBJ=
36 if test "$with_xwidgets" != "no"; then
37- test "$USE_GTK_TOOLKIT" = "GTK3" && test "$window_system" != "none" ||
38- AC_MSG_ERROR([xwidgets requested but gtk3 not used.])
39+ if test "$USE_GTK_TOOLKIT" = "GTK3" && test "$window_system" != "none"; then
40+ WEBKIT_REQUIRED=2.12
41+ WEBKIT_MODULES="webkit2gtk-4.0 >= $WEBKIT_REQUIRED"
42+ EMACS_CHECK_MODULES([WEBKIT], [$WEBKIT_MODULES])
43+ HAVE_XWIDGETS=$HAVE_WEBKIT
44+ XWIDGETS_OBJ="xwidget.o"
45+ elif test "${NS_IMPL_COCOA}" = "yes"; then
46+ dnl FIXME: Check framework WebKit2
47+ dnl WEBKIT_REQUIRED=M.m.p
48+ WEBKIT_LIBS="-Wl,-framework -Wl,WebKit"
49+ WEBKIT_CFLAGS="-I/System/Library/Frameworks/WebKit.framework/Headers"
50+ HAVE_WEBKIT="yes"
51+ HAVE_XWIDGETS=$HAVE_WEBKIT
52+ XWIDGETS_OBJ="xwidget.o"
53+ NS_OBJC_OBJ="$NS_OBJC_OBJ nsxwidget.o"
54+ dnl Update NS_OBJC_OBJ with added nsxwidget.o
55+ AC_SUBST(NS_OBJC_OBJ)
56+ else
57+ AC_MSG_ERROR([xwidgets requested, it requires GTK3 as X window toolkit or
58macOS Cocoa as window system.])
59+ fi
60
61- WEBKIT_REQUIRED=2.12
62- WEBKIT_MODULES="webkit2gtk-4.0 >= $WEBKIT_REQUIRED"
63- EMACS_CHECK_MODULES([WEBKIT], [$WEBKIT_MODULES])
64- HAVE_XWIDGETS=$HAVE_WEBKIT
65 test $HAVE_XWIDGETS = yes ||
66- AC_MSG_ERROR([xwidgets requested but WebKitGTK+ not found.])
67+ AC_MSG_ERROR([xwidgets requested but WebKitGTK+ or WebKit framework not
68found.])
69
70- XWIDGETS_OBJ=xwidget.o
71 AC_DEFINE([HAVE_XWIDGETS], 1, [Define to 1 if you have xwidgets support.])
72 fi
73 AC_SUBST(XWIDGETS_OBJ)
74@@ -5697,7 +5711,7 @@ AC_DEFUN
75 Does Emacs directly use zlib? ${HAVE_ZLIB}
76 Does Emacs have dynamic modules support? ${HAVE_MODULES}
77 Does Emacs use toolkit scroll bars?
78${USE_TOOLKIT_SCROLL_BARS}
79- Does Emacs support Xwidgets (requires gtk3)? ${HAVE_XWIDGETS}
80+ Does Emacs support Xwidgets? ${HAVE_XWIDGETS}
81 Does Emacs have threading support in lisp? ${threads_enabled}
82 Does Emacs support the portable dumper? ${with_pdumper}
83 Does Emacs support legacy unexec dumping? ${with_unexec}
84diff --git a/lisp/xwidget.el b/lisp/xwidget.el
85index 662a854ac3..9b146b0202 100644
86--- a/lisp/xwidget.el
87+++ b/lisp/xwidget.el
88@@ -39,9 +39,10 @@
89 (declare-function xwidget-buffer "xwidget.c" (xwidget))
90 (declare-function xwidget-size-request "xwidget.c" (xwidget))
91 (declare-function xwidget-resize "xwidget.c" (xwidget new-width new-height))
92-(declare-function xwidget-webkit-execute-script "xwidget.c"
93- (xwidget script &optional callback))
94+(declare-function xwidget-webkit-uri "xwidget.c" (xwidget))
95+(declare-function xwidget-webkit-title "xwidget.c" (xwidget))
96 (declare-function xwidget-webkit-goto-uri "xwidget.c" (xwidget uri))
97+(declare-function xwidget-webkit-goto-history "xwidget.c" (xwidget rel-pos))
98 (declare-function xwidget-webkit-zoom "xwidget.c" (xwidget factor))
99 (declare-function xwidget-plist "xwidget.c" (xwidget))
100 (declare-function set-xwidget-plist "xwidget.c" (xwidget plist))
101@@ -51,6 +52,10 @@
102 (declare-function get-buffer-xwidgets "xwidget.c" (buffer))
103 (declare-function xwidget-query-on-exit-flag "xwidget.c" (xwidget))
104
105+(defgroup xwidget nil
106+ "Displaying native widgets in Emacs buffers."
107+ :group 'widgets)
108+
109 (defun xwidget-insert (pos type title width height &optional args)
110 "Insert an xwidget at position POS.
111 Supply the xwidget's TYPE, TITLE, WIDTH, and HEIGHT.
112@@ -78,6 +83,8 @@ xwidget-at
113 ;;; webkit support
114 (require 'browse-url)
115 (require 'image-mode);;for some image-mode alike functionality
116+(require 'seq)
117+(require 'url-handlers)
118
119 ;;;###autoload
120 (defun xwidget-webkit-browse-url (url &optional new-session)
121@@ -96,6 +103,24 @@ xwidget-webkit-browse-url
122 (xwidget-webkit-new-session url)
123 (xwidget-webkit-goto-url url))))
124
125+(defun xwidget-webkit-split-below ()
126+ "Clone current URL into a new widget place in new window below.
127+Get the URL of current session, then browse to the URL
128+in `split-window-below' with a new xwidget webkit session."
129+ (interactive)
130+ (let ((url (xwidget-webkit-current-url)))
131+ (with-selected-window (split-window-below)
132+ (xwidget-webkit-new-session url))))
133+
134+(defun xwidget-webkit-split-right ()
135+ "Clone current URL into a new widget place in new window right.
136+Get the URL of current session, then browse to the URL
137+in `split-window-right' with a new xwidget webkit session."
138+ (interactive)
139+ (let ((url (xwidget-webkit-current-url)))
140+ (with-selected-window (split-window-right)
141+ (xwidget-webkit-new-session url))))
142+
143 ;;todo.
144 ;; - check that the webkit support is compiled in
145 (defvar xwidget-webkit-mode-map
146@@ -103,34 +128,42 @@ xwidget-webkit-mode-map
147 (define-key map "g" 'xwidget-webkit-browse-url)
148 (define-key map "a" 'xwidget-webkit-adjust-size-dispatch)
149 (define-key map "b" 'xwidget-webkit-back)
150+ (define-key map "f" 'xwidget-webkit-forward)
151 (define-key map "r" 'xwidget-webkit-reload)
152 (define-key map "t" (lambda () (interactive) (message "o"))) ;FIXME: ?!?
153 (define-key map "\C-m" 'xwidget-webkit-insert-string)
154- (define-key map "w" 'xwidget-webkit-current-url)
155+ (define-key map "w" 'xwidget-webkit-current-url-message-kill)
156 (define-key map "+" 'xwidget-webkit-zoom-in)
157 (define-key map "-" 'xwidget-webkit-zoom-out)
158
159 ;;similar to image mode bindings
160 (define-key map (kbd "SPC") 'xwidget-webkit-scroll-up)
161+ (define-key map (kbd "S-SPC") 'xwidget-webkit-scroll-down)
162 (define-key map (kbd "DEL") 'xwidget-webkit-scroll-down)
163
164- (define-key map [remap scroll-up] 'xwidget-webkit-scroll-up)
165+ (define-key map [remap scroll-up] 'xwidget-webkit-scroll-up-line)
166 (define-key map [remap scroll-up-command] 'xwidget-webkit-scroll-up)
167
168- (define-key map [remap scroll-down] 'xwidget-webkit-scroll-down)
169+ (define-key map [remap scroll-down]
170'xwidget-webkit-scroll-down-line)
171 (define-key map [remap scroll-down-command] 'xwidget-webkit-scroll-down)
172
173 (define-key map [remap forward-char] 'xwidget-webkit-scroll-forward)
174 (define-key map [remap backward-char]
175'xwidget-webkit-scroll-backward)
176 (define-key map [remap right-char] 'xwidget-webkit-scroll-forward)
177 (define-key map [remap left-char]
178'xwidget-webkit-scroll-backward)
179- (define-key map [remap previous-line] 'xwidget-webkit-scroll-down)
180- (define-key map [remap next-line] 'xwidget-webkit-scroll-up)
181+ (define-key map [remap previous-line]
182'xwidget-webkit-scroll-down-line)
183+ (define-key map [remap next-line] 'xwidget-webkit-scroll-up-line)
184
185 ;; (define-key map [remap move-beginning-of-line] 'image-bol)
186 ;; (define-key map [remap move-end-of-line] 'image-eol)
187 (define-key map [remap beginning-of-buffer] 'xwidget-webkit-scroll-top)
188 (define-key map [remap end-of-buffer] 'xwidget-webkit-scroll-bottom)
189+
190+ ;; For macOS xwidget webkit, we don't support multiple views for a
191+ ;; model, instead, create a new session and model behind the scene.
192+ (when (memq window-system '(mac ns))
193+ (define-key map [remap split-window-below] 'xwidget-webkit-split-below)
194+ (define-key map [remap split-window-right] 'xwidget-webkit-split-right))
195 map)
196 "Keymap for `xwidget-webkit-mode'.")
197
198@@ -144,33 +177,69 @@ xwidget-webkit-zoom-out
199 (interactive)
200 (xwidget-webkit-zoom (xwidget-webkit-current-session) -0.1))
201
202-(defun xwidget-webkit-scroll-up ()
203- "Scroll webkit up."
204- (interactive)
205+(defun xwidget-webkit-scroll-up (&optional n)
206+ "Scroll webkit up by N pixels or window height pixels.
207+Stop if the bottom edge of the page is reached.
208+If N is omitted or nil, scroll up by window height pixels."
209+ (interactive "P")
210 (xwidget-webkit-execute-script
211 (xwidget-webkit-current-session)
212- "window.scrollBy(0, 50);"))
213-
214-(defun xwidget-webkit-scroll-down ()
215- "Scroll webkit down."
216- (interactive)
217+ (format "window.scrollBy(0, %d);"
218+ (or n (xwidget-window-inside-pixel-height (selected-window))))))
219+
220+(defun xwidget-webkit-scroll-down (&optional n)
221+ "Scroll webkit down by N pixels or window height pixels.
222+Stop if the top edge of the page is reached.
223+If N is omitted or nil, scroll down by window height pixels."
224+ (interactive "P")
225 (xwidget-webkit-execute-script
226 (xwidget-webkit-current-session)
227- "window.scrollBy(0, -50);"))
228-
229-(defun xwidget-webkit-scroll-forward ()
230- "Scroll webkit forwards."
231- (interactive)
232+ (format "window.scrollBy(0, -%d);"
233+ (or n (xwidget-window-inside-pixel-height (selected-window))))))
234+
235+(defcustom xwidget-webkit-scroll-line-height 50
236+ "Default line height in pixels to scroll xwidget webkit."
237+ :type 'integer)
238+
239+(defcustom xwidget-webkit-scroll-char-width 10
240+ "Default char height in pixels to scroll xwidget webkit."
241+ :type 'integer)
242+
243+(defun xwidget-webkit-scroll-up-line (&optional n)
244+ "Scroll webkit up by N lines.
245+The height of line is `xwidget-webkit-scroll-line-height' pixels.
246+Stop if the bottom edge of the page is reached.
247+If N is omitted or nil, scroll up by one line."
248+ (interactive "p")
249+ (xwidget-webkit-scroll-up (* n xwidget-webkit-scroll-line-height)))
250+
251+(defun xwidget-webkit-scroll-down-line (&optional n)
252+ "Scroll webkit down by N lines.
253+The height of line is `xwidget-webkit-scroll-line-height' pixels.
254+Stop if the top edge of the page is reached.
255+If N is omitted or nil, scroll down by one line."
256+ (interactive "p")
257+ (xwidget-webkit-scroll-down (* n xwidget-webkit-scroll-line-height)))
258+
259+(defun xwidget-webkit-scroll-forward (&optional n)
260+ "Scroll webkit forwards by N chars.
261+The width of char is `xwidget-webkit-scroll-char-width' pixels.
262+If N is ommited or nil, scroll forwards by one char."
263+ (interactive "p")
264 (xwidget-webkit-execute-script
265 (xwidget-webkit-current-session)
266- "window.scrollBy(50, 0);"))
267-
268-(defun xwidget-webkit-scroll-backward ()
269- "Scroll webkit backwards."
270- (interactive)
271+ (format "window.scrollBy(%d, 0);"
272+ (* n xwidget-webkit-scroll-char-width))))
273+
274+(defun xwidget-webkit-scroll-backward (&optional n)
275+ "Scroll webkit backwards by N chars.
276+The width of char is `xwidget-webkit-scroll-char-width' pixels.
277+If N is ommited or nil, scroll backwards by one char."
278+ (interactive "p")
279 (xwidget-webkit-execute-script
280 (xwidget-webkit-current-session)
281- "window.scrollBy(-50, 0);"))
282+ (format "window.scrollBy(-%d, 0);"
283+ (* n xwidget-webkit-scroll-char-width))))
284
285 (defun xwidget-webkit-scroll-top ()
286 "Scroll webkit to the very top."
287@@ -184,7 +253,7 @@ xwidget-webkit-scroll-bottom
288 (interactive)
289 (xwidget-webkit-execute-script
290 (xwidget-webkit-current-session)
291- "window.scrollTo(pageXOffset, window.document.body.clientHeight);"))
292+ "window.scrollTo(pageXOffset, window.document.body.scrollHeight);"))
293
294 ;; The xwidget event needs to go into a higher level handler
295 ;; since the xwidget can generate an event even if it's offscreen.
296@@ -208,7 +277,6 @@ xwidget-event-handler
297 ;;TODO stopped working for some reason
298 )
299 ;;(funcall xwidget-callback xwidget xwidget-event-type)
300- (message "xw callback %s" xwidget)
301 (funcall 'xwidget-webkit-callback xwidget xwidget-event-type)))
302
303 (defun xwidget-webkit-callback (xwidget xwidget-event-type)
304@@ -219,43 +287,141 @@ xwidget-webkit-callback
305 "error: callback called for xwidget with dead buffer")
306 (with-current-buffer (xwidget-buffer xwidget)
307 (cond ((eq xwidget-event-type 'load-changed)
308- (xwidget-webkit-execute-script
309- xwidget "document.title"
310- (lambda (title)
311- (xwidget-log "webkit finished loading: '%s'" title)
312- ;;TODO - check the native/internal scroll
313- ;;(xwidget-adjust-size-to-content xwidget)
314- (xwidget-webkit-adjust-size-to-window xwidget)
315- (rename-buffer (format "*xwidget webkit: %s *" title))))
316- (pop-to-buffer (current-buffer)))
317+ ;; We do not change selected window for the finish of loading a
318page.
319+ ;; And do not adjust webkit size to window here, the selected
320window
321+ ;; can be the mini-buffer window unwantedly.
322+ (let ((title (xwidget-webkit-title xwidget)))
323+ (xwidget-log "webkit finished loading: %s" title)
324+ (rename-buffer (format "*xwidget webkit: %s *" title) t)))
325 ((eq xwidget-event-type 'decide-policy)
326 (let ((strarg (nth 3 last-input-event)))
327 (if (string-match ".*#\\(.*\\)" strarg)
328 (xwidget-webkit-show-id-or-named-element
329 xwidget
330 (match-string 1 strarg)))))
331+ ;; TODO: Response handling other than download.
332+ ((eq xwidget-event-type 'download-callback)
333+ (let ((url (nth 3 last-input-event))
334+ (mime-type (nth 4 last-input-event))
335+ (file-name (nth 5 last-input-event)))
336+ (xwidget-webkit-save-as-file url mime-type file-name)))
337 ((eq xwidget-event-type 'javascript-callback)
338 (let ((proc (nth 3 last-input-event))
339 (arg (nth 4 last-input-event)))
340- (funcall proc arg)))
341+ ;; Some javascript return vector as result
342+ (funcall proc (if (vectorp arg) (seq-into arg 'list) arg))))
343 (t (xwidget-log "unhandled event:%s" xwidget-event-type))))))
344
345 (defvar bookmark-make-record-function)
346+(defvar isearch-search-fun-function)
347+(when (memq window-system '(mac ns))
348+ (defcustom xwidget-webkit-enable-plugins nil
349+ "Enable plugins for xwidget webkit.
350+If non-nil, plugins are enabled. Otherwise, disabled."
351+ :type 'boolean))
352+
353 (define-derived-mode xwidget-webkit-mode
354 special-mode "xwidget-webkit" "Xwidget webkit view mode."
355 (setq buffer-read-only t)
356+ (setq cursor-type nil)
357 (setq-local bookmark-make-record-function
358 #'xwidget-webkit-bookmark-make-record)
359+ (setq-local isearch-search-fun-function
360+ #'xwidget-webkit-search-fun-function)
361+ (setq-local isearch-lazy-highlight nil)
362 ;; Keep track of [vh]scroll when switching buffers
363 (image-mode-setup-winprops))
364
365+;;; Download, save as file.
366+
367+(defcustom xwidget-webkit-download-dir "~/Downloads/"
368+ "Directory where download file saved."
369+ :type 'string)
370+
371+(defun xwidget-webkit-save-as-file (url mime-type &optional file-name)
372+ "For XWIDGET webkit, save URL resource of MIME-TYPE as FILE-NAME."
373+ (let ((save-name (read-file-name
374+ (format "Save '%s' file as: " mime-type)
375+ xwidget-webkit-download-dir
376+ (expand-file-name
377+ file-name
378+ xwidget-webkit-download-dir))))
379+ (if (file-directory-p save-name)
380+ (setq save-name
381+ (expand-file-name (file-name-nondirectory file-name) save-name)))
382+ (setq xwidget-webkit-download-dir (file-name-directory save-name))
383+ (url-copy-file url save-name t)))
384+
385+;;; Bookmarks integration
386+
387+(defcustom xwidget-webkit-bookmark-jump-new-session nil
388+ "Control bookmark jump to use new session or not.
389+If non-nil, it will use a new session. Otherwise, it will use
390+`xwidget-webkit-last-session'. When you set this variable to
391+nil, consider further customization with
392+`xwidget-webkit-last-session-buffer'."
393+ :type 'boolean)
394+
395 (defun xwidget-webkit-bookmark-make-record ()
396 "Integrate Emacs bookmarks with the webkit xwidget."
397 (nconc (bookmark-make-record-default t t)
398- `((page . ,(xwidget-webkit-current-url))
399- (handler . (lambda (bmk) (browse-url
400- (bookmark-prop-get bmk 'page)))))))
401-
402+ `((page . ,(xwidget-webkit-current-url))
403+ (handler . (lambda (bmk)
404+ (xwidget-webkit-browse-url
405+ (bookmark-prop-get bmk 'page)
406+ xwidget-webkit-bookmark-jump-new-session))))))
407+
408+;;; Search text in page
409+
410+;; Initialize last search text length variable when isearch starts
411+(defvar xwidget-webkit-isearch-last-length 0)
412+(add-hook 'isearch-mode-hook
413+ (lambda ()
414+ (setq xwidget-webkit-isearch-last-length 0)))
415+
416+;; This is minimal. Regex and incremental search will be nice
417+(defvar xwidget-webkit-search-js "
418+var xwSearchForward = %s;
419+var xwSearchRepeat = %s;
420+var xwSearchString = '%s';
421+if (window.getSelection() && !window.getSelection().isCollapsed) {
422+ if (xwSearchRepeat) {
423+ if (xwSearchForward)
424+ window.getSelection().collapseToEnd();
425+ else
426+ window.getSelection().collapseToStart();
427+ } else {
428+ if (xwSearchForward)
429+ window.getSelection().collapseToStart();
430+ else {
431+ var sel = window.getSelection();
432+ window.getSelection().collapse(sel.focusNode, sel.focusOffset + 1);
433+ }
434+ }
435+}
436+window.find(xwSearchString, false, !xwSearchForward, true, false, true);
437+")
438+
439+(defun xwidget-webkit-search-fun-function ()
440+ "Return the function which perform the search in xwidget webkit."
441+ (lambda (string &optional _bound _noerror _count)
442+ (let* ((current-length (length string))
443+ (search-forward (if isearch-forward "true" "false"))
444+ (search-repeat
445+ (if (eq current-length xwidget-webkit-isearch-last-length)
446+ "true"
447+ "false")))
448+ (setq xwidget-webkit-isearch-last-length current-length)
449+ (xwidget-webkit-execute-script
450+ (xwidget-webkit-current-session)
451+ (format xwidget-webkit-search-js
452+ search-forward
453+ search-repeat
454+ (regexp-quote string)))
455+ ;; Unconditionally avoid 'Failing I-search ...'
456+ (goto-char (if isearch-forward (point-min) (point-max))))))
457+
458+;;; xwidget webkit session
459
460 (defvar xwidget-webkit-last-session-buffer nil)
461
462@@ -303,7 +469,7 @@ xwidget-webkit-activeelement-js"
463
464 "
465
466- "javascript that finds the active element."
467+ "Javascript that finds the active element."
468 ;; Yes it's ugly, because:
469 ;; - there is apparently no way to find the active frame other than recursion
470 ;; - the js "for each" construct misbehaved on the "frames" collection
471@@ -313,25 +479,29 @@ xwidget-webkit-activeelement-js"
472 )
473
474 (defun xwidget-webkit-insert-string ()
475- "Prompt for a string and insert it in the active field in the
476-current webkit widget."
477+ "Insert string into the active field in the current webkit widget."
478 ;; Read out the string in the field first and provide for edit.
479 (interactive)
480+ ;; As the prompt needs to change based on the asynchronous execution results,
481+ ;; the function must handle the string itself.
482 (let ((xww (xwidget-webkit-current-session)))
483+
484 (xwidget-webkit-execute-script
485 xww
486 (concat xwidget-webkit-activeelement-js "
487 (function () {
488 var res = findactiveelement(document);
489- return [res.value, res.type];
490+ if (res)
491+ return [res.value, res.type];
492 })();")
493 (lambda (field)
494+ "Prompt a string for the FIELD and insert in the active input."
495 (let ((str (pcase field
496- (`[,val "text"]
497+ (`(,val "text")
498 (read-string "Text: " val))
499- (`[,val "password"]
500+ (`(,val "password")
501 (read-passwd "Password: " nil val))
502- (`[,val "textarea"]
503+ (`(,val "textarea")
504 (xwidget-webkit-begin-edit-textarea xww val)))))
505 (xwidget-webkit-execute-script
506 xww
507@@ -444,11 +614,23 @@ xwidget-webkit-adjust-size-dispatch
508 (ignore-errors
509 (recenter-top-bottom)))
510
511+;; Utility functions, wanted in `window.el'
512+
513+(defun xwidget-window-inside-pixel-width (window)
514+ "Return Emacs WINDOW body width in pixel."
515+ (let ((edges (window-inside-pixel-edges window)))
516+ (- (nth 2 edges) (nth 0 edges))))
517+
518+(defun xwidget-window-inside-pixel-height (window)
519+ "Return Emacs WINDOW body height in pixel."
520+ (let ((edges (window-inside-pixel-edges window)))
521+ (- (nth 3 edges) (nth 1 edges))))
522+
523 (defun xwidget-webkit-adjust-size-to-window (xwidget &optional window)
524 "Adjust the size of the webkit XWIDGET to fit the WINDOW."
525 (xwidget-resize xwidget
526- (window-pixel-width window)
527- (window-pixel-height window)))
528+ (xwidget-window-inside-pixel-width window)
529+ (xwidget-window-inside-pixel-height window)))
530
531 (defun xwidget-webkit-adjust-size (w h)
532 "Manually set webkit size to width W, height H."
533@@ -487,10 +669,13 @@ xwidget-webkit-new-session
534 (get-buffer-create bufname)))
535 ;; The xwidget id is stored in a text property, so we need to have
536 ;; at least character in this buffer.
537- (insert " ")
538- (setq xw (xwidget-insert 1 'webkit bufname
539- (window-pixel-width)
540- (window-pixel-height)))
541+ ;; Insert invisible url, good default for next `g' to browse url.
542+ (let ((start (point)))
543+ (insert url)
544+ (put-text-property start (+ start (length url)) 'invisible t)
545+ (setq xw (xwidget-insert start 'webkit bufname
546+ (xwidget-window-inside-pixel-width
547(selected-window))
548+ (xwidget-window-inside-pixel-height
549(selected-window)))))
550 (xwidget-put xw 'callback 'xwidget-webkit-callback)
551 (xwidget-webkit-mode)
552 (xwidget-webkit-goto-uri (xwidget-webkit-last-session) url)))
553@@ -506,23 +691,27 @@ xwidget-webkit-goto-url
554 (defun xwidget-webkit-back ()
555 "Go back in history."
556 (interactive)
557- (xwidget-webkit-execute-script (xwidget-webkit-current-session)
558- "history.go(-1);"))
559+ (xwidget-webkit-goto-history (xwidget-webkit-current-session) -1))
560+
561+(defun xwidget-webkit-forward ()
562+ "Go forward in history."
563+ (interactive)
564+ (xwidget-webkit-goto-history (xwidget-webkit-current-session) 1))
565
566 (defun xwidget-webkit-reload ()
567- "Reload current url."
568+ "Reload current URL."
569 (interactive)
570- (xwidget-webkit-execute-script (xwidget-webkit-current-session)
571- "history.go(0);"))
572+ (xwidget-webkit-goto-history (xwidget-webkit-current-session) 0))
573
574 (defun xwidget-webkit-current-url ()
575- "Get the webkit url and place it on the kill-ring."
576+ "Get the current xwidget webkit URL."
577 (interactive)
578- (xwidget-webkit-execute-script
579- (xwidget-webkit-current-session)
580- "document.URL" (lambda (rv)
581- (let ((url (kill-new (or rv ""))))
582- (message "url: %s" url)))))
583+ (xwidget-webkit-uri (xwidget-webkit-current-session)))
584+
585+(defun xwidget-webkit-current-url-message-kill ()
586+ "Display the current xwidget webkit URL and place it on the `kill-ring'."
587+ (interactive)
588+ (message "URL: %s" (kill-new (or (xwidget-webkit-current-url) ""))))
589
590 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
591 (defun xwidget-webkit-get-selection (proc)
592@@ -533,10 +722,9 @@ xwidget-webkit-get-selection
593 proc))
594
595 (defun xwidget-webkit-copy-selection-as-kill ()
596- "Get the webkit selection and put it on the kill-ring."
597+ "Get the webkit selection and put it on the `kill-ring'."
598 (interactive)
599- (xwidget-webkit-get-selection (lambda (selection) (kill-new selection))))
600-
601+ (xwidget-webkit-get-selection #'kill-new))
602
603 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
604 ;; Xwidget plist management (similar to the process plist functions)
605diff --git a/nextstep/templates/Info.plist.in b/nextstep/templates/Info.plist.in
606index c1e50a8409..76efe95673 100644
607--- a/nextstep/templates/Info.plist.in
608+++ b/nextstep/templates/Info.plist.in
609@@ -675,7 +675,15 @@ along with GNU Emacs. If not, see
610<https://www.gnu.org/licenses/>.
611 </array>
612 <key>NSAppleScriptEnabled</key>
613 <string>YES</string>
614- <key>NSAppleEventsUsageDescription</key>
615- <string>Emacs requires permission to send AppleEvents to other
616applications.</string>
617+ <key>NSAppleEventsUsageDescription</key>
618+ <string>Emacs requires permission to send AppleEvents to other
619applications.</string>
620+ <!-- For xwidget webkit to browse remote url,
621+ but this set no restriction at all. Consult apple's documentation
622+ for detail information about `NSApplicationDefinedMask'. -->
623+ <key>NSAppTransportSecurity</key>
624+ <dict>
625+ <key>NSAllowsArbitraryLoads</key>
626+ <true/>
627+ </dict>
628 </dict>
629 </plist>
630diff --git a/src/Makefile.in b/src/Makefile.in
631index fd05a45df5..0af650244d 100644
632--- a/src/Makefile.in
633+++ b/src/Makefile.in
634@@ -434,6 +434,7 @@ SOME_MACHINE_OBJECTS =
635 xterm.o xfns.o xmenu.o xselect.o xrdb.o xsmfns.o fringe.o image.o \
636 fontset.o dbusbind.o cygw32.o \
637 nsterm.o nsfns.o nsmenu.o nsselect.o nsimage.o nsfont.o macfont.o \
638+ nsxwidget.o \
639 w32.o w32console.o w32cygwinx.o w32fns.o w32heap.o w32inevt.o w32notify.o \
640 w32menu.o w32proc.o w32reg.o w32select.o w32term.o w32xfns.o \
641 w16select.o widget.o xfont.o ftfont.o xftfont.o ftxfont.o gtkutil.o \
642diff --git a/src/emacs.c b/src/emacs.c
643index ad661a081b..7c6871f1e7 100644
644--- a/src/emacs.c
645+++ b/src/emacs.c
646@@ -1902,7 +1902,6 @@ main (int argc, char **argv)
647 syms_of_xfns ();
648 syms_of_xmenu ();
649 syms_of_fontset ();
650- syms_of_xwidget ();
651 syms_of_xsettings ();
652 #ifdef HAVE_X_SM
653 syms_of_xsmfns ();
654@@ -1979,6 +1978,7 @@ main (int argc, char **argv)
655 #endif /* HAVE_W32NOTIFY */
656 #endif /* WINDOWSNT */
657
658+ syms_of_xwidget ();
659 syms_of_threads ();
660 syms_of_profiler ();
661 syms_of_pdumper ();
662diff --git a/src/nsterm.m b/src/nsterm.m
663index 02331826d9..9479c40006 100644
664--- a/src/nsterm.m
665+++ b/src/nsterm.m
666@@ -49,6 +49,7 @@ Updated by Christian Limpach (address@hidden)
667 #include "nsterm.h"
668 #include "systime.h"
669 #include "character.h"
670+#include "xwidget.h"
671 #include "fontset.h"
672 #include "composite.h"
673 #include "ccl.h"
674@@ -2411,7 +2412,7 @@ so some key presses (TAB) are swallowed by the system. */
675 }
676
677 static int
678-ns_note_mouse_movement (struct frame *frame, CGFloat x, CGFloat y)
679+ns_note_mouse_movement (struct frame *frame, CGFloat x, CGFloat y, BOOL
680dragging)
681 /* ------------------------------------------------------------------------
682 Called by EmacsView on mouseMovement events. Passes on
683 to emacs mainstream code if we moved off of a rect of interest
684@@ -2420,17 +2421,24 @@ so some key presses (TAB) are swallowed by the system.
685*/
686 {
687 struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (frame);
688 NSRect *r;
689+ BOOL force_update = NO;
690
691 // NSTRACE ("note_mouse_movement");
692
693 dpyinfo->last_mouse_motion_frame = frame;
694 r = &dpyinfo->last_mouse_glyph;
695
696+ /* If the last rect is too large (ex, xwidget webkit), update at
697+ every move, or resizing by dragging modeline or vertical split is
698+ very hard to make its way. */
699+ if (dragging && (r->size.width > 32 || r->size.height > 32))
700+ force_update = YES;
701+
702 /* Note, this doesn't get called for enter/leave, since we don't have a
703 position. Those are taken care of in the corresponding NSView methods.
704*/
705
706- /* Has movement gone beyond last rect we were tracking? */
707- if (x < r->origin.x || x >= r->origin.x + r->size.width
708+ /* Has movement gone beyond last rect we were tracking? */
709+ if (force_update || x < r->origin.x || x >= r->origin.x + r->size.width
710 || y < r->origin.y || y >= r->origin.y + r->size.height)
711 {
712 ns_update_begin (frame);
713@@ -4182,6 +4190,10 @@ overwriting cursor (usually when cursor on a tab). */
714 }
715 break;
716
717+ case XWIDGET_GLYPH:
718+ x_draw_xwidget_glyph_string (s);
719+ break;
720+
721 case STRETCH_GLYPH:
722 ns_dumpglyphs_stretch (s);
723 break;
724@@ -6835,6 +6847,7 @@ - (void)mouseMoved: (NSEvent *)e
725 struct ns_display_info *dpyinfo = FRAME_DISPLAY_INFO (emacsframe);
726 Lisp_Object frame;
727 NSPoint pt;
728+ BOOL dragging;
729
730 NSTRACE_WHEN (NSTRACE_GROUP_EVENTS, "[EmacsView mouseMoved:]");
731
732@@ -6877,7 +6890,8 @@ - (void)mouseMoved: (NSEvent *)e
733 last_mouse_window = window;
734 }
735
736- if (!ns_note_mouse_movement (emacsframe, pt.x, pt.y))
737+ dragging = (e.type == NSEventTypeLeftMouseDragged);
738+ if (!ns_note_mouse_movement (emacsframe, pt.x, pt.y, dragging))
739 help_echo_string = previous_help_echo_string;
740
741 XSETFRAME (frame, emacsframe);
742diff --git a/src/nsxwidget.h b/src/nsxwidget.h
743new file mode 100644
744index 0000000000..6af5fe5a4d
745--- /dev/null
746+++ b/src/nsxwidget.h
747@@ -0,0 +1,80 @@
748+/* Header for NS Cocoa part of xwidget and webkit widget.
749+
750+Copyright (C) 2011-2017 Free Software Foundation, Inc.
751+
752+This file is part of GNU Emacs.
753+
754+GNU Emacs is free software: you can redistribute it and/or modify
755+it under the terms of the GNU General Public License as published by
756+the Free Software Foundation, either version 3 of the License, or (at
757+your option) any later version.
758+
759+GNU Emacs is distributed in the hope that it will be useful,
760+but WITHOUT ANY WARRANTY; without even the implied warranty of
761+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
762+GNU General Public License for more details.
763+
764+You should have received a copy of the GNU General Public License
765+along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
766+
767+#ifndef NSXWIDGET_H_INCLUDED
768+#define NSXWIDGET_H_INCLUDED
769+
770+/* This file can be included from non-objc files through 'xwidget.h'. */
771+#ifdef __OBJC__
772+#import <AppKit/NSView.h>
773+#endif
774+
775+#include "dispextern.h"
776+#include "lisp.h"
777+#include "xwidget.h"
778+
779+/* Functions for xwidget webkit. */
780+
781+bool nsxwidget_is_web_view (struct xwidget *xw);
782+Lisp_Object nsxwidget_webkit_uri (struct xwidget *xw);
783+Lisp_Object nsxwidget_webkit_title (struct xwidget *xw);
784+void nsxwidget_webkit_goto_uri (struct xwidget *xw, const char *uri);
785+void nsxwidget_webkit_goto_history (struct xwidget *xw, int rel_pos);
786+void nsxwidget_webkit_zoom (struct xwidget *xw, double zoom_change);
787+void nsxwidget_webkit_execute_script (struct xwidget *xw, const char *script,
788+ Lisp_Object fun);
789+
790+/* Functions for xwidget model. */
791+
792+#ifdef __OBJC__
793+@interface XwWindow : NSView
794+@property struct xwidget *xw;
795+@end
796+#endif
797+
798+void nsxwidget_init (struct xwidget *xw);
799+void nsxwidget_kill (struct xwidget *xw);
800+void nsxwidget_resize (struct xwidget *xw);
801+Lisp_Object nsxwidget_get_size (struct xwidget *xw);
802+
803+/* Functions for xwidget view. */
804+
805+#ifdef __OBJC__
806+@interface XvWindow : NSView
807+@property struct xwidget *xw;
808+@property struct xwidget_view *xv;
809+@end
810+#endif
811+
812+void nsxwidget_init_view (struct xwidget_view *xv,
813+ struct xwidget *xww,
814+ struct glyph_string *s,
815+ int x, int y);
816+void nsxwidget_delete_view (struct xwidget_view *xv);
817+
818+void nsxwidget_show_view (struct xwidget_view *xv);
819+void nsxwidget_hide_view (struct xwidget_view *xv);
820+void nsxwidget_resize_view (struct xwidget_view *xv,
821+ int widget, int height);
822+
823+void nsxwidget_move_view (struct xwidget_view *xv, int x, int y);
824+void nsxwidget_move_widget_in_view (struct xwidget_view *xv, int x, int y);
825+void nsxwidget_set_needsdisplay (struct xwidget_view *xv);
826+
827+#endif /* NSXWIDGET_H_INCLUDED */
828diff --git a/src/nsxwidget.m b/src/nsxwidget.m
829new file mode 100644
830index 0000000000..8d8a92d09c
831--- /dev/null
832+++ b/src/nsxwidget.m
833@@ -0,0 +1,611 @@
834+/* NS Cocoa part implementation of xwidget and webkit widget.
835+
836+Copyright (C) 1989, 1992-1994, 2005-2006, 2008-2017 Free Software
837+Foundation, Inc.
838+
839+This file is part of GNU Emacs.
840+
841+GNU Emacs is free software: you can redistribute it and/or modify
842+it under the terms of the GNU General Public License as published by
843+the Free Software Foundation, either version 3 of the License, or (at
844+your option) any later version.
845+
846+GNU Emacs is distributed in the hope that it will be useful,
847+but WITHOUT ANY WARRANTY; without even the implied warranty of
848+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
849+GNU General Public License for more details.
850+
851+You should have received a copy of the GNU General Public License
852+along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
853+
854+#include <config.h>
855+
856+#include "lisp.h"
857+#include "blockinput.h"
858+#include "dispextern.h"
859+#include "buffer.h"
860+#include "frame.h"
861+#include "nsterm.h"
862+#include "xwidget.h"
863+
864+#import <AppKit/AppKit.h>
865+#import <WebKit/WebKit.h>
866+
867+/* Thoughts on NS Cocoa xwidget and webkit2:
868+
869+ Webkit2 process architecture seems to be very hostile for offscreen
870+ rendering techniques, which is used by GTK xwiget implementation;
871+ Specifically NSView level view sharing / copying is not working.
872+
873+ *** So only one view can be associcated with a model. ***
874+
875+ With this decision, implementation is plain and can expect best out
876+ of webkit2's rationale. But process and session structures will
877+ diverge from GTK xwiget. Though, cosmetically similar usages can
878+ be presented and will be preferred, if agreeable.
879+
880+ For other widget types, OSR seems possible, but will not care for a
881+ while. */
882+
883+/* Xwidget webkit. */
884+
885+@interface XwWebView : WKWebView
886+<WKNavigationDelegate, WKUIDelegate, WKScriptMessageHandler>
887+@property struct xwidget *xw;
888+/* Map url to whether javascript is blocked by
889+ 'Content-Security-Policy' sandbox without allow-scripts. */
890+@property(retain) NSMutableDictionary *urlScriptBlocked;
891+@end
892+@implementation XwWebView : WKWebView
893+
894+- (id)initWithFrame:(CGRect)frame
895+ configuration:(WKWebViewConfiguration *)configuration
896+ xwidget:(struct xwidget *)xw
897+{
898+ /* Script controller to add script message handler and user script. */
899+ WKUserContentController *scriptor = [[WKUserContentController alloc] init];
900+ configuration.userContentController = scriptor;
901+
902+ /* Enable inspect element context menu item for debugging. */
903+ [configuration.preferences setValue:@YES
904+ forKey:@"developerExtrasEnabled"];
905+
906+ Lisp_Object enablePlugins =
907+ Fintern (build_string ("xwidget-webkit-enable-plugins"), Qnil);
908+ if (!EQ (Fsymbol_value (enablePlugins), Qnil))
909+ configuration.preferences.plugInsEnabled = YES;
910+
911+ self = [super initWithFrame:frame configuration:configuration];
912+ if (self)
913+ {
914+ self.xw = xw;
915+ self.urlScriptBlocked = [[NSMutableDictionary alloc] init];
916+ self.navigationDelegate = self;
917+ self.UIDelegate = self;
918+ self.customUserAgent =
919+ @"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6)"
920+ @" AppleWebKit/603.3.8 (KHTML, like Gecko)"
921+ @" Version/11.0.1 Safari/603.3.8";
922+ [scriptor addScriptMessageHandler:self name:@"keyDown"];
923+ [scriptor addUserScript:[[WKUserScript alloc]
924+ initWithSource:xwScript
925+ injectionTime:
926+ WKUserScriptInjectionTimeAtDocumentStart
927+ forMainFrameOnly:NO]];
928+ }
929+ return self;
930+}
931+
932+#if 0
933+/* Non ARC - just to check lifecycle. */
934+- (void)dealloc
935+{
936+ NSLog (@"XwWebView dealloc");
937+ [super dealloc];
938+}
939+#endif
940+
941+- (void)webView:(WKWebView *)webView
942+didFinishNavigation:(WKNavigation *)navigation
943+{
944+ if (EQ (Fbuffer_live_p (self.xw->buffer), Qt))
945+ store_xwidget_event_string (self.xw, "load-changed", "");
946+}
947+
948+- (void)webView:(WKWebView *)webView
949+decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
950+decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler
951+{
952+ switch (navigationAction.navigationType) {
953+ case WKNavigationTypeLinkActivated:
954+ decisionHandler (WKNavigationActionPolicyAllow);
955+ break;
956+ default:
957+ // decisionHandler (WKNavigationActionPolicyCancel);
958+ decisionHandler (WKNavigationActionPolicyAllow);
959+ break;
960+ }
961+}
962+
963+- (void)webView:(WKWebView *)webView
964+decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse
965+decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler
966+{
967+ if (!navigationResponse.canShowMIMEType)
968+ {
969+ NSString *url = navigationResponse.response.URL.absoluteString;
970+ NSString *mimetype = navigationResponse.response.MIMEType;
971+ NSString *filename = navigationResponse.response.suggestedFilename;
972+ decisionHandler (WKNavigationResponsePolicyCancel);
973+ store_xwidget_download_callback_event (self.xw,
974+ url.UTF8String,
975+ mimetype.UTF8String,
976+ filename.UTF8String);
977+ return;
978+ }
979+ decisionHandler (WKNavigationResponsePolicyAllow);
980+
981+ self.urlScriptBlocked[navigationResponse.response.URL] =
982+ [NSNumber numberWithBool:NO];
983+ if ([navigationResponse.response isKindOfClass:[NSHTTPURLResponse class]])
984+ {
985+ NSDictionary *headers =
986+ ((NSHTTPURLResponse *) navigationResponse.response).allHeaderFields;
987+ NSString *value = headers[@"Content-Security-Policy"];
988+ if (value)
989+ {
990+ /* TODO: Sloppy parsing of 'Content-Security-Policy' value. */
991+ NSRange sandbox = [value rangeOfString:@"sandbox"];
992+ if (sandbox.location != NSNotFound
993+ && (sandbox.location == 0
994+ || [value characterAtIndex:(sandbox.location - 1)] == ' '
995+ || [value characterAtIndex:(sandbox.location - 1)] == ';'))
996+ {
997+ NSRange allowScripts = [value rangeOfString:@"allow-scripts"];
998+ if (allowScripts.location == NSNotFound
999+ || allowScripts.location < sandbox.location)
1000+ self.urlScriptBlocked[navigationResponse.response.URL] =
1001+ [NSNumber numberWithBool:YES];
1002+ }
1003+ }
1004+ }
1005+}
1006+
1007+/* No additional new webview or emacs window will be created
1008+ for <a ... target="_blank">. */
1009+- (WKWebView *)webView:(WKWebView *)webView
1010+createWebViewWithConfiguration:(WKWebViewConfiguration *)configuration
1011+ forNavigationAction:(WKNavigationAction *)navigationAction
1012+ windowFeatures:(WKWindowFeatures *)windowFeatures
1013+{
1014+ if (!navigationAction.targetFrame.isMainFrame)
1015+ [webView loadRequest:navigationAction.request];
1016+ return nil;
1017+}
1018+
1019+/* Open panel for file upload. */
1020+- (void)webView:(WKWebView *)webView
1021+runOpenPanelWithParameters:(WKOpenPanelParameters *)parameters
1022+initiatedByFrame:(WKFrameInfo *)frame
1023+completionHandler:(void (^)(NSArray<NSURL *> *URLs))completionHandler
1024+{
1025+ NSOpenPanel *openPanel = [NSOpenPanel openPanel];
1026+ openPanel.canChooseFiles = YES;
1027+ openPanel.canChooseDirectories = NO;
1028+ openPanel.allowsMultipleSelection = parameters.allowsMultipleSelection;
1029+ if ([openPanel runModal] == NSModalResponseOK)
1030+ completionHandler (openPanel.URLs);
1031+ else
1032+ completionHandler (nil);
1033+}
1034+
1035+/* By forwarding mouse events to emacs view (frame)
1036+ - Mouse click in webview selects the window contains the webview.
1037+ - Correct mouse hand/arrow/I-beam is displayed (TODO: not perfect yet).
1038+*/
1039+
1040+- (void)mouseDown:(NSEvent *)event
1041+{
1042+ [self.xw->xv->emacswindow mouseDown:event];
1043+ [super mouseDown:event];
1044+}
1045+
1046+- (void)mouseUp:(NSEvent *)event
1047+{
1048+ [self.xw->xv->emacswindow mouseUp:event];
1049+ [super mouseUp:event];
1050+}
1051+
1052+/* Basically we want keyboard events handled by emacs unless an input
1053+ element has focus. Especially, while incremental search, we set
1054+ emacs as first responder to avoid focus held in an input element
1055+ with matching text. */
1056+
1057+- (void)keyDown:(NSEvent *)event
1058+{
1059+ Lisp_Object var = Fintern (build_string ("isearch-mode"), Qnil);
1060+ Lisp_Object val = buffer_local_value (var, Fcurrent_buffer ());
1061+ if (!EQ (val, Qunbound) && !EQ (val, Qnil))
1062+ {
1063+ [self.window makeFirstResponder:self.xw->xv->emacswindow];
1064+ [self.xw->xv->emacswindow keyDown:event];
1065+ return;
1066+ }
1067+
1068+ /* Emacs handles keyboard events when javascript is blocked. */
1069+ if ([self.urlScriptBlocked[self.URL] boolValue])
1070+ {
1071+ [self.xw->xv->emacswindow keyDown:event];
1072+ return;
1073+ }
1074+
1075+ [self evaluateJavaScript:@"xwHasFocus()"
1076+ completionHandler:^(id result, NSError *error) {
1077+ if (error)
1078+ {
1079+ NSLog (@"xwHasFocus: %@", error);
1080+ [self.xw->xv->emacswindow keyDown:event];
1081+ }
1082+ else if (result)
1083+ {
1084+ NSNumber *hasFocus = result; /* __NSCFBoolean */
1085+ if (!hasFocus.boolValue)
1086+ [self.xw->xv->emacswindow keyDown:event];
1087+ else
1088+ [super keyDown:event];
1089+ }
1090+ }];
1091+}
1092+
1093+- (void)interpretKeyEvents:(NSArray<NSEvent *> *)eventArray
1094+{
1095+ /* We should do nothing and do not forward (default implementation
1096+ if we not override here) to let emacs collect key events and ask
1097+ interpretKeyEvents to its superclass. */
1098+}
1099+
1100+static NSString *xwScript;
1101++ (void)initialize
1102+{
1103+ /* Find out if an input element has focus.
1104+ Message to script message handler when 'C-g' key down. */
1105+ if (!xwScript)
1106+ xwScript =
1107+ @"function xwHasFocus() {"
1108+ @" var ae = document.activeElement;"
1109+ @" if (ae) {"
1110+ @" var name = ae.nodeName;"
1111+ @" return name == 'INPUT' || name == 'TEXTAREA';"
1112+ @" } else {"
1113+ @" return false;"
1114+ @" }"
1115+ @"}"
1116+ @"function xwKeyDown(event) {"
1117+ @" if (event.ctrlKey && event.key == 'g') {"
1118+ @" window.webkit.messageHandlers.keyDown.postMessage('C-g');"
1119+ @" }"
1120+ @"}"
1121+ @"document.addEventListener('keydown', xwKeyDown);"
1122+ ;
1123+}
1124+
1125+/* Confirming to WKScriptMessageHandler, listens concerning keyDown in
1126+ webkit. Currently 'C-g'. */
1127+- (void)userContentController:(WKUserContentController *)userContentController
1128+ didReceiveScriptMessage:(WKScriptMessage *)message
1129+{
1130+ if ([message.body isEqualToString:@"C-g"])
1131+ {
1132+ /* Just give up focus, no relay "C-g" to emacs, another "C-g"
1133+ follows will be handled by emacs. */
1134+ [self.window makeFirstResponder:self.xw->xv->emacswindow];
1135+ }
1136+}
1137+
1138+@end
1139+
1140+/* Xwidget webkit commands. */
1141+
1142+static Lisp_Object build_string_with_nsstr (NSString *nsstr);
1143+
1144+bool
1145+nsxwidget_is_web_view (struct xwidget *xw)
1146+{
1147+ return xw->xwWidget != NULL &&
1148+ [xw->xwWidget isKindOfClass:WKWebView.class];
1149+}
1150+
1151+Lisp_Object
1152+nsxwidget_webkit_uri (struct xwidget *xw)
1153+{
1154+ XwWebView *xwWebView = (XwWebView *) xw->xwWidget;
1155+ return build_string_with_nsstr (xwWebView.URL.absoluteString);
1156+}
1157+
1158+Lisp_Object
1159+nsxwidget_webkit_title (struct xwidget *xw)
1160+{
1161+ XwWebView *xwWebView = (XwWebView *) xw->xwWidget;
1162+ return build_string_with_nsstr (xwWebView.title);
1163+}
1164+
1165+/* @Note ATS - Need application transport security in 'Info.plist' or
1166+ remote pages will not loaded. */
1167+void
1168+nsxwidget_webkit_goto_uri (struct xwidget *xw, const char *uri)
1169+{
1170+ XwWebView *xwWebView = (XwWebView *) xw->xwWidget;
1171+ NSString *urlString = [NSString stringWithUTF8String:uri];
1172+ NSURL *url = [NSURL URLWithString:urlString];
1173+ NSURLRequest *urlRequest = [NSURLRequest requestWithURL:url];
1174+ [xwWebView loadRequest:urlRequest];
1175+}
1176+
1177+void
1178+nsxwidget_webkit_goto_history (struct xwidget *xw, int rel_pos)
1179+{
1180+ XwWebView *xwWebView = (XwWebView *) xw->xwWidget;
1181+ switch (rel_pos) {
1182+ case -1: [xwWebView goBack]; break;
1183+ case 0: [xwWebView reload]; break;
1184+ case 1: [xwWebView goForward]; break;
1185+ }
1186+}
1187+
1188+void
1189+nsxwidget_webkit_zoom (struct xwidget *xw, double zoom_change)
1190+{
1191+ XwWebView *xwWebView = (XwWebView *) xw->xwWidget;
1192+ xwWebView.magnification += zoom_change;
1193+ /* TODO: setMagnification:centeredAtPoint. */
1194+}
1195+
1196+/* Build lisp string */
1197+static Lisp_Object
1198+build_string_with_nsstr (NSString *nsstr)
1199+{
1200+ const char *utfstr = [nsstr UTF8String];
1201+ NSUInteger bytes = [nsstr lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
1202+ return make_string (utfstr, bytes);
1203+}
1204+
1205+/* Recursively convert an objc native type JavaScript value to a Lisp
1206+ value. Mostly copied from GTK xwidget 'webkit_js_to_lisp'. */
1207+static Lisp_Object
1208+js_to_lisp (id value)
1209+{
1210+ if (value == nil || [value isKindOfClass:NSNull.class])
1211+ return Qnil;
1212+ else if ([value isKindOfClass:NSString.class])
1213+ return build_string_with_nsstr ((NSString *) value);
1214+ else if ([value isKindOfClass:NSNumber.class])
1215+ {
1216+ NSNumber *nsnum = (NSNumber *) value;
1217+ char type = nsnum.objCType[0];
1218+ if (type == 'c') /* __NSCFBoolean has type character 'c'. */
1219+ return nsnum.boolValue? Qt : Qnil;
1220+ else
1221+ {
1222+ if (type == 'i' || type == 'l')
1223+ return make_int (nsnum.longValue);
1224+ else if (type == 'f' || type == 'd')
1225+ return make_float (nsnum.doubleValue);
1226+ /* else fall through. */
1227+ }
1228+ }
1229+ else if ([value isKindOfClass:NSArray.class])
1230+ {
1231+ NSArray *nsarr = (NSArray *) value;
1232+ EMACS_INT n = nsarr.count;
1233+ Lisp_Object obj;
1234+ struct Lisp_Vector *p = allocate_vector (n);
1235+
1236+ for (ptrdiff_t i = 0; i < n; ++i)
1237+ p->contents[i] = js_to_lisp ([nsarr objectAtIndex:i]);
1238+ XSETVECTOR (obj, p);
1239+ return obj;
1240+ }
1241+ else if ([value isKindOfClass:NSDictionary.class])
1242+ {
1243+ NSDictionary *nsdict = (NSDictionary *) value;
1244+ NSArray *keys = nsdict.allKeys;
1245+ ptrdiff_t n = keys.count;
1246+ Lisp_Object obj;
1247+ struct Lisp_Vector *p = allocate_vector (n);
1248+
1249+ for (ptrdiff_t i = 0; i < n; ++i)
1250+ {
1251+ NSString *prop_key = (NSString *) [keys objectAtIndex:i];
1252+ id prop_value = [nsdict valueForKey:prop_key];
1253+ p->contents[i] = Fcons (build_string_with_nsstr (prop_key),
1254+ js_to_lisp (prop_value));
1255+ }
1256+ XSETVECTOR (obj, p);
1257+ return obj;
1258+ }
1259+ NSLog (@"Unhandled type in javascript result");
1260+ return Qnil;
1261+}
1262+
1263+void
1264+nsxwidget_webkit_execute_script (struct xwidget *xw, const char *script,
1265+ Lisp_Object fun)
1266+{
1267+ XwWebView *xwWebView = (XwWebView *) xw->xwWidget;
1268+ if ([xwWebView.urlScriptBlocked[xwWebView.URL] boolValue])
1269+ {
1270+ message ("Javascript is blocked by 'CSP: sandbox'.");
1271+ return;
1272+ }
1273+
1274+ NSString *javascriptString = [NSString stringWithUTF8String:script];
1275+ [xwWebView evaluateJavaScript:javascriptString
1276+ completionHandler:^(id result, NSError *error) {
1277+ if (error)
1278+ {
1279+ NSLog (@"evaluateJavaScript error : %@", error.localizedDescription);
1280+ NSLog (@"error script=%@", javascriptString);
1281+ }
1282+ else if (result && FUNCTIONP (fun))
1283+ {
1284+ // NSLog (@"result=%@, type=%@", result, [result class]);
1285+ Lisp_Object lisp_value = js_to_lisp (result);
1286+ store_xwidget_js_callback_event (xw, fun, lisp_value);
1287+ }
1288+ }];
1289+}
1290+
1291+/* Window containing an xwidget. */
1292+
1293+@implementation XwWindow
1294+- (BOOL)isFlipped { return YES; }
1295+@end
1296+
1297+/* Xwidget model, macOS Cocoa part. */
1298+
1299+void
1300+nsxwidget_init(struct xwidget *xw)
1301+{
1302+ block_input ();
1303+ NSRect rect = NSMakeRect (0, 0, xw->width, xw->height);
1304+ xw->xwWidget = [[XwWebView alloc]
1305+ initWithFrame:rect
1306+ configuration:[[WKWebViewConfiguration alloc] init]
1307+ xwidget:xw];
1308+ xw->xwWindow = [[XwWindow alloc]
1309+ initWithFrame:rect];
1310+ [xw->xwWindow addSubview:xw->xwWidget];
1311+ xw->xv = NULL; /* for 1 to 1 relationship of webkit2. */
1312+ unblock_input ();
1313+}
1314+
1315+void
1316+nsxwidget_kill (struct xwidget *xw)
1317+{
1318+ if (xw)
1319+ {
1320+ WKUserContentController *scriptor =
1321+ ((XwWebView *) xw->xwWidget).configuration.userContentController;
1322+ [scriptor removeAllUserScripts];
1323+ [scriptor removeScriptMessageHandlerForName:@"keyDown"];
1324+ [scriptor release];
1325+ if (xw->xv)
1326+ xw->xv->model = Qnil; /* Make sure related view stale. */
1327+
1328+ /* This stops playing audio when a xwidget-webkit buffer is
1329+ killed. I could not find other solution. */
1330+ nsxwidget_webkit_goto_uri (xw, "about:blank");
1331+
1332+ [((XwWebView *) xw->xwWidget).urlScriptBlocked release];
1333+ [xw->xwWidget removeFromSuperviewWithoutNeedingDisplay];
1334+ [xw->xwWidget release];
1335+ [xw->xwWindow removeFromSuperviewWithoutNeedingDisplay];
1336+ [xw->xwWindow release];
1337+ xw->xwWidget = nil;
1338+ }
1339+}
1340+
1341+void
1342+nsxwidget_resize (struct xwidget *xw)
1343+{
1344+ if (xw->xwWidget)
1345+ {
1346+ [xw->xwWindow setFrameSize:NSMakeSize(xw->width, xw->height)];
1347+ [xw->xwWidget setFrameSize:NSMakeSize(xw->width, xw->height)];
1348+ }
1349+}
1350+
1351+Lisp_Object
1352+nsxwidget_get_size (struct xwidget *xw)
1353+{
1354+ return list2i (xw->xwWidget.frame.size.width,
1355+ xw->xwWidget.frame.size.height);
1356+}
1357+
1358+/* Xwidget view, macOS Cocoa part. */
1359+
1360+@implementation XvWindow : NSView
1361+- (BOOL)isFlipped { return YES; }
1362+@end
1363+
1364+void
1365+nsxwidget_init_view (struct xwidget_view *xv,
1366+ struct xwidget *xw,
1367+ struct glyph_string *s,
1368+ int x, int y)
1369+{
1370+ /* 'x_draw_xwidget_glyph_string' will calculate correct position and
1371+ size of clip to draw in emacs buffer window. Thus, just begin at
1372+ origin with no crop. */
1373+ xv->x = x;
1374+ xv->y = y;
1375+ xv->clip_left = 0;
1376+ xv->clip_right = xw->width;
1377+ xv->clip_top = 0;
1378+ xv->clip_bottom = xw->height;
1379+
1380+ xv->xvWindow = [[XvWindow alloc]
1381+ initWithFrame:NSMakeRect (x, y, xw->width, xw->height)];
1382+ xv->xvWindow.xw = xw;
1383+ xv->xvWindow.xv = xv;
1384+
1385+ xw->xv = xv; /* For 1 to 1 relationship of webkit2. */
1386+ [xv->xvWindow addSubview:xw->xwWindow];
1387+
1388+ xv->emacswindow = FRAME_NS_VIEW (s->f);
1389+ [xv->emacswindow addSubview:xv->xvWindow];
1390+}
1391+
1392+void
1393+nsxwidget_delete_view (struct xwidget_view *xv)
1394+{
1395+ if (!EQ (xv->model, Qnil))
1396+ {
1397+ struct xwidget *xw = XXWIDGET (xv->model);
1398+ [xw->xwWindow removeFromSuperviewWithoutNeedingDisplay];
1399+ xw->xv = NULL; /* Now model has no view. */
1400+ }
1401+ [xv->xvWindow removeFromSuperviewWithoutNeedingDisplay];
1402+ [xv->xvWindow release];
1403+}
1404+
1405+void
1406+nsxwidget_show_view (struct xwidget_view *xv)
1407+{
1408+ xv->hidden = NO;
1409+ [xv->xvWindow setFrameOrigin:NSMakePoint(xv->x + xv->clip_left,
1410+ xv->y + xv->clip_top)];
1411+}
1412+
1413+void
1414+nsxwidget_hide_view (struct xwidget_view *xv)
1415+{
1416+ xv->hidden = YES;
1417+ [xv->xvWindow setFrameOrigin:NSMakePoint(10000, 10000)];
1418+}
1419+
1420+void
1421+nsxwidget_resize_view (struct xwidget_view *xv, int width, int height)
1422+{
1423+ [xv->xvWindow setFrameSize:NSMakeSize(width, height)];
1424+}
1425+
1426+void
1427+nsxwidget_move_view (struct xwidget_view *xv, int x, int y)
1428+{
1429+ [xv->xvWindow setFrameOrigin:NSMakePoint (x, y)];
1430+}
1431+
1432+/* Move model window in container (view window). */
1433+void
1434+nsxwidget_move_widget_in_view (struct xwidget_view *xv, int x, int y)
1435+{
1436+ struct xwidget *xww = xv->xvWindow.xw;
1437+ [xww->xwWindow setFrameOrigin:NSMakePoint (x, y)];
1438+}
1439+
1440+void
1441+nsxwidget_set_needsdisplay (struct xwidget_view *xv)
1442+{
1443+ xv->xvWindow.needsDisplay = YES;
1444+}
1445diff --git a/src/xwidget.c b/src/xwidget.c
1446index 121510ebac..8e21311e03 100644
1447--- a/src/xwidget.c
1448+++ b/src/xwidget.c
1449@@ -23,21 +23,31 @@ Copyright (C) 2011-2019 Free Software Foundation, Inc.
1450
1451 #include "lisp.h"
1452 #include "blockinput.h"
1453+#include "dispextern.h"
1454 #include "frame.h"
1455 #include "keyboard.h"
1456 #include "gtkutil.h"
1457 #include "sysstdio.h"
1458+#include "termhooks.h"
1459+#include "window.h"
1460
1461+/* Include xwidget bottom end headers. */
1462+#ifdef USE_GTK
1463 #include <webkit2/webkit2.h>
1464 #include <JavaScriptCore/JavaScript.h>
1465+#elif defined NS_IMPL_COCOA
1466+#include "nsxwidget.h"
1467+#endif
1468
1469 /* Suppress GCC deprecation warnings starting in WebKitGTK+ 2.21.1 for
1470 webkit_javascript_result_get_global_context and
1471 webkit_javascript_result_get_value (Bug#33679).
1472 FIXME: Use the JavaScriptCore GLib API instead, and remove this hack. */
1473+#ifdef USE_GTK
1474 #if WEBKIT_CHECK_VERSION (2, 21, 1) && GNUC_PREREQ (4, 2, 0)
1475 # pragma GCC diagnostic ignored "-Wdeprecated-declarations"
1476 #endif
1477+#endif
1478
1479 static struct xwidget *
1480 allocate_xwidget (void)
1481@@ -56,6 +66,7 @@ #define XSETXWIDGET_VIEW(a, b) XSETPSEUDOVECTOR (a, b,
1482PVEC_XWIDGET_VIEW)
1483
1484 static struct xwidget_view *xwidget_view_lookup (struct xwidget *,
1485 struct window *);
1486+#ifdef USE_GTK
1487 static void webkit_view_load_changed_cb (WebKitWebView *,
1488 WebKitLoadEvent,
1489 gpointer);
1490@@ -69,6 +80,7 @@ webkit_decide_policy_cb (WebKitWebView *,
1491 WebKitPolicyDecision *,
1492 WebKitPolicyDecisionType,
1493 gpointer);
1494+#endif
1495
1496
1497 DEFUN ("make-xwidget",
1498@@ -86,8 +98,10 @@ DEFUN ("make-xwidget",
1499 Lisp_Object title, Lisp_Object width, Lisp_Object height,
1500 Lisp_Object arguments, Lisp_Object buffer)
1501 {
1502+#ifdef USE_GTK
1503 if (!xg_gtk_initialized)
1504 error ("make-xwidget: GTK has not been initialized");
1505+#endif
1506 CHECK_SYMBOL (type);
1507 CHECK_FIXNAT (width);
1508 CHECK_FIXNAT (height);
1509@@ -102,10 +116,11 @@ DEFUN ("make-xwidget",
1510 xw->kill_without_query = false;
1511 XSETXWIDGET (val, xw);
1512 Vxwidget_list = Fcons (val, Vxwidget_list);
1513- xw->widgetwindow_osr = NULL;
1514- xw->widget_osr = NULL;
1515 xw->plist = Qnil;
1516
1517+#ifdef USE_GTK
1518+ xw->widgetwindow_osr = NULL;
1519+ xw->widget_osr = NULL;
1520 if (EQ (xw->type, Qwebkit))
1521 {
1522 block_input ();
1523@@ -160,6 +175,9 @@ DEFUN ("make-xwidget",
1524
1525 unblock_input ();
1526 }
1527+#elif defined NS_IMPL_COCOA
1528+ nsxwidget_init (xw);
1529+#endif
1530
1531 return val;
1532 }
1533@@ -195,6 +213,7 @@ xwidget_hidden (struct xwidget_view *xv)
1534 return xv->hidden;
1535 }
1536
1537+#ifdef USE_GTK
1538 static void
1539 xwidget_show_view (struct xwidget_view *xv)
1540 {
1541@@ -228,13 +247,14 @@ offscreen_damage_event (GtkWidget *widget, GdkEvent
1542*event,
1543 if (GTK_IS_WIDGET (xv_widget))
1544 gtk_widget_queue_draw (GTK_WIDGET (xv_widget));
1545 else
1546- printf ("Warning, offscreen_damage_event received invalid xv pointer:%p\n",
1547- xv_widget);
1548+ message ("Warning, offscreen_damage_event received invalid xv
1549pointer:%p\n",
1550+ xv_widget);
1551
1552 return FALSE;
1553 }
1554+#endif /* USE_GTK */
1555
1556-static void
1557+void
1558 store_xwidget_event_string (struct xwidget *xw, const char *eventname,
1559 const char *eventstr)
1560 {
1561@@ -248,7 +268,27 @@ store_xwidget_event_string (struct xwidget *xw, const char
1562*eventname,
1563 kbd_buffer_store_event (&event);
1564 }
1565
1566-static void
1567+void
1568+store_xwidget_download_callback_event (struct xwidget *xw,
1569+ const char *url,
1570+ const char *mimetype,
1571+ const char *filename)
1572+{
1573+ struct input_event event;
1574+ Lisp_Object xwl;
1575+ XSETXWIDGET (xwl, xw);
1576+ EVENT_INIT (event);
1577+ event.kind = XWIDGET_EVENT;
1578+ event.frame_or_window = Qnil;
1579+ event.arg = list5 (intern ("download-callback"),
1580+ xwl,
1581+ build_string (url),
1582+ build_string (mimetype),
1583+ build_string (filename));
1584+ kbd_buffer_store_event (&event);
1585+}
1586+
1587+void
1588 store_xwidget_js_callback_event (struct xwidget *xw,
1589 Lisp_Object proc,
1590 Lisp_Object argument)
1591@@ -264,6 +304,7 @@ store_xwidget_js_callback_event (struct xwidget *xw,
1592 }
1593
1594
1595+#ifdef USE_GTK
1596 void
1597 webkit_view_load_changed_cb (WebKitWebView *webkitwebview,
1598 WebKitLoadEvent load_event,
1599@@ -521,6 +562,7 @@ xwidget_osr_event_set_embedder (GtkWidget *widget, GdkEvent
1600*event,
1601 gtk_widget_get_window (xv->widget));
1602 return FALSE;
1603 }
1604+#endif /* USE_GTK */
1605
1606
1607 /* Initializes and does initial placement of an xwidget view on screen. */
1608@@ -530,8 +572,10 @@ xwidget_init_view (struct xwidget *xww,
1609 int x, int y)
1610 {
1611
1612+#ifdef USE_GTK
1613 if (!xg_gtk_initialized)
1614 error ("xwidget_init_view: GTK has not been initialized");
1615+#endif
1616
1617 struct xwidget_view *xv = allocate_xwidget_view ();
1618 Lisp_Object val;
1619@@ -542,6 +586,7 @@ xwidget_init_view (struct xwidget *xww,
1620 XSETWINDOW (xv->w, s->w);
1621 XSETXWIDGET (xv->model, xww);
1622
1623+#ifdef USE_GTK
1624 if (EQ (xww->type, Qwebkit))
1625 {
1626 xv->widget = gtk_drawing_area_new ();
1627@@ -599,6 +644,10 @@ xwidget_init_view (struct xwidget *xww,
1628 xv->x = x;
1629 xv->y = y;
1630 gtk_widget_show_all (xv->widgetwindow);
1631+#elif defined NS_IMPL_COCOA
1632+ nsxwidget_init_view (xv, xww, s, x, y);
1633+ nsxwidget_resize_view(xv, xww->width, xww->height);
1634+#endif
1635
1636 return xv;
1637 }
1638@@ -611,24 +660,59 @@ x_draw_xwidget_glyph_string (struct glyph_string *s)
1639 initialization. */
1640 struct xwidget *xww = s->xwidget;
1641 struct xwidget_view *xv = xwidget_view_lookup (xww, s->w);
1642+ int text_area_x, text_area_y, text_area_width, text_area_height;
1643 int clip_right;
1644 int clip_bottom;
1645 int clip_top;
1646 int clip_left;
1647
1648 int x = s->x;
1649- int y = s->y + (s->height / 2) - (xww->height / 2);
1650+ int y = s->y;
1651
1652 /* Do initialization here in the display loop because there is no
1653 other time to know things like window placement etc. Do not
1654 create a new view if we have found one that is usable. */
1655+#ifdef USE_GTK
1656 if (!xv)
1657 xv = xwidget_init_view (xww, s, x, y);
1658-
1659- int text_area_x, text_area_y, text_area_width, text_area_height;
1660+#elif defined NS_IMPL_COCOA
1661+ if (!xv)
1662+ {
1663+ /* Enforce 1 to 1, model and view for macOS Cocoa webkit2. */
1664+ if (xww->xv)
1665+ {
1666+ if (xwidget_hidden (xww->xv))
1667+ {
1668+ Lisp_Object xvl;
1669+ XSETXWIDGET_VIEW (xvl, xww->xv);
1670+ Fdelete_xwidget_view (xvl);
1671+ }
1672+ else
1673+ {
1674+ message ("You can't share an xwidget (webkit2) among windows.");
1675+ return;
1676+ }
1677+ }
1678+ xv = xwidget_init_view (xww, s, x, y);
1679+ }
1680+#endif
1681
1682 window_box (s->w, TEXT_AREA, &text_area_x, &text_area_y,
1683 &text_area_width, &text_area_height);
1684+
1685+ /* Resize xwidget webkit if its container window size is changed in
1686+ some ways, for example, a buffer became hidden in small split
1687+ window, then it can appear front in merged whole window. */
1688+ if (EQ (xww->type, Qwebkit)
1689+ && (xww->width != text_area_width || xww->height != text_area_height))
1690+ {
1691+ Lisp_Object xwl;
1692+ XSETXWIDGET (xwl, xww);
1693+ Fxwidget_resize (xwl,
1694+ make_int (text_area_width),
1695+ make_int (text_area_height));
1696+ }
1697+
1698 clip_left = max (0, text_area_x - x);
1699 clip_right = max (clip_left,
1700 min (xww->width, text_area_x + text_area_width - x));
1701@@ -651,8 +735,14 @@ x_draw_xwidget_glyph_string (struct glyph_string *s)
1702
1703 /* Has it moved? */
1704 if (moved)
1705- gtk_fixed_move (GTK_FIXED (FRAME_GTK_WIDGET (s->f)),
1706- xv->widgetwindow, x + clip_left, y + clip_top);
1707+ {
1708+#ifdef USE_GTK
1709+ gtk_fixed_move (GTK_FIXED (FRAME_GTK_WIDGET (s->f)),
1710+ xv->widgetwindow, x + clip_left, y + clip_top);
1711+#elif defined NS_IMPL_COCOA
1712+ nsxwidget_move_view (xv, x + clip_left, y + clip_top);
1713+#endif
1714+ }
1715
1716 /* Clip the widget window if some parts happen to be outside
1717 drawable area. An Emacs window is not a gtk window. A gtk window
1718@@ -663,10 +753,16 @@ x_draw_xwidget_glyph_string (struct glyph_string *s)
1719 || xv->clip_bottom != clip_bottom
1720 || xv->clip_top != clip_top || xv->clip_left != clip_left)
1721 {
1722+#ifdef USE_GTK
1723 gtk_widget_set_size_request (xv->widgetwindow, clip_right - clip_left,
1724 clip_bottom - clip_top);
1725 gtk_fixed_move (GTK_FIXED (xv->widgetwindow), xv->widget, -clip_left,
1726 -clip_top);
1727+#elif defined NS_IMPL_COCOA
1728+ nsxwidget_resize_view (xv, clip_right - clip_left,
1729+ clip_bottom - clip_top);
1730+ nsxwidget_move_widget_in_view (xv, -clip_left, -clip_top);
1731+#endif
1732
1733 xv->clip_right = clip_right;
1734 xv->clip_bottom = clip_bottom;
1735@@ -680,22 +776,66 @@ x_draw_xwidget_glyph_string (struct glyph_string *s)
1736 xwidgets background. It's just a visual glitch though. */
1737 if (!xwidget_hidden (xv))
1738 {
1739+#ifdef USE_GTK
1740 gtk_widget_queue_draw (xv->widgetwindow);
1741 gtk_widget_queue_draw (xv->widget);
1742+#elif defined NS_IMPL_COCOA
1743+ nsxwidget_set_needsdisplay (xv);
1744+#endif
1745 }
1746 }
1747
1748-/* Macro that checks WEBKIT_IS_WEB_VIEW (xw->widget_osr) first. */
1749+static bool
1750+xwidget_is_web_view (struct xwidget *xw)
1751+{
1752+#ifdef USE_GTK
1753+ return xw->widget_osr != NULL && WEBKIT_IS_WEB_VIEW (xw->widget_osr);
1754+#elif defined NS_IMPL_COCOA
1755+ return nsxwidget_is_web_view (xw);
1756+#endif
1757+}
1758+
1759+/* Macro that checks xwidget hold webkit web view first. */
1760 #define WEBKIT_FN_INIT() \
1761 CHECK_XWIDGET (xwidget); \
1762 struct xwidget *xw = XXWIDGET (xwidget); \
1763- if (!xw->widget_osr || !WEBKIT_IS_WEB_VIEW (xw->widget_osr)) \
1764+ if (!xwidget_is_web_view (xw)) \
1765 { \
1766 fputs ("ERROR xw->widget_osr does not hold a webkit instance\n", \
1767 stdout); \
1768 return Qnil; \
1769 }
1770
1771+DEFUN ("xwidget-webkit-uri",
1772+ Fxwidget_webkit_uri, Sxwidget_webkit_uri,
1773+ 1, 1, 0,
1774+ doc: /* Get the current URL of XWIDGET webkit. */)
1775+ (Lisp_Object xwidget)
1776+{
1777+ WEBKIT_FN_INIT ();
1778+#ifdef USE_GTK
1779+ WebKitWebView *wkwv = WEBKIT_WEB_VIEW (xw->widget_osr);
1780+ return build_string (webkit_web_view_get_uri (wkwv));
1781+#elif defined NS_IMPL_COCOA
1782+ return nsxwidget_webkit_uri (xw);
1783+#endif
1784+}
1785+
1786+DEFUN ("xwidget-webkit-title",
1787+ Fxwidget_webkit_title, Sxwidget_webkit_title,
1788+ 1, 1, 0,
1789+ doc: /* Get the current title of XWIDGET webkit. */)
1790+ (Lisp_Object xwidget)
1791+{
1792+ WEBKIT_FN_INIT ();
1793+#ifdef USE_GTK
1794+ WebKitWebView *wkwv = WEBKIT_WEB_VIEW (xw->widget_osr);
1795+ return build_string (webkit_web_view_get_title (wkwv));
1796+#elif defined NS_IMPL_COCOA
1797+ return nsxwidget_webkit_title (xw);
1798+#endif
1799+}
1800+
1801 DEFUN ("xwidget-webkit-goto-uri",
1802 Fxwidget_webkit_goto_uri, Sxwidget_webkit_goto_uri,
1803 2, 2, 0,
1804@@ -705,7 +845,32 @@ DEFUN ("xwidget-webkit-goto-uri",
1805 WEBKIT_FN_INIT ();
1806 CHECK_STRING (uri);
1807 uri = ENCODE_FILE (uri);
1808+#ifdef USE_GTK
1809 webkit_web_view_load_uri (WEBKIT_WEB_VIEW (xw->widget_osr), SSDATA (uri));
1810+#elif defined NS_IMPL_COCOA
1811+ nsxwidget_webkit_goto_uri (xw, SSDATA (uri));
1812+#endif
1813+ return Qnil;
1814+}
1815+
1816+DEFUN ("xwidget-webkit-goto-history",
1817+ Fxwidget_webkit_goto_history, Sxwidget_webkit_goto_history,
1818+ 2, 2, 0,
1819+ doc: /* Make the XWIDGET webkit load REL-POS (-1, 0, 1) page in browse
1820history. */)
1821+ (Lisp_Object xwidget, Lisp_Object rel_pos)
1822+{
1823+ WEBKIT_FN_INIT ();
1824+ CHECK_RANGED_INTEGER (rel_pos, -1, 1); /* -1, 0, 1 */
1825+#ifdef USE_GTK
1826+ WebKitWebView *wkwv = WEBKIT_WEB_VIEW (xw->widget_osr);
1827+ switch (XFIXNAT (rel_pos)) {
1828+ case -1: webkit_web_view_go_back (wkwv); break;
1829+ case 0: webkit_web_view_reload (wkwv); break;
1830+ case 1: webkit_web_view_go_forward (wkwv); break;
1831+ }
1832+#elif defined NS_IMPL_COCOA
1833+ nsxwidget_webkit_goto_history (xw, XFIXNAT (rel_pos));
1834+#endif
1835 return Qnil;
1836 }
1837
1838@@ -719,14 +884,19 @@ DEFUN ("xwidget-webkit-zoom",
1839 if (FLOATP (factor))
1840 {
1841 double zoom_change = XFLOAT_DATA (factor);
1842+#ifdef USE_GTK
1843 webkit_web_view_set_zoom_level
1844 (WEBKIT_WEB_VIEW (xw->widget_osr),
1845 webkit_web_view_get_zoom_level
1846 (WEBKIT_WEB_VIEW (xw->widget_osr)) + zoom_change);
1847+#elif defined NS_IMPL_COCOA
1848+ nsxwidget_webkit_zoom (xw, zoom_change);
1849+#endif
1850 }
1851 return Qnil;
1852 }
1853
1854+#ifdef USE_GTK
1855 /* Save script and fun in the script/callback save vector and return
1856 its index. */
1857 static ptrdiff_t
1858@@ -748,6 +918,7 @@ save_script_callback (struct xwidget *xw, Lisp_Object
1859script, Lisp_Object fun)
1860 ASET (cbs, idx, Fcons (make_mint_ptr (xlispstrdup (script)), fun));
1861 return idx;
1862 }
1863+#endif
1864
1865 DEFUN ("xwidget-webkit-execute-script",
1866 Fxwidget_webkit_execute_script, Sxwidget_webkit_execute_script,
1867@@ -759,11 +930,12 @@ DEFUN ("xwidget-webkit-execute-script",
1868 {
1869 WEBKIT_FN_INIT ();
1870 CHECK_STRING (script);
1871- if (!NILP (fun) && !FUNCTIONP (fun))
1872+ if (!FUNCTIONP (fun))
1873 wrong_type_argument (Qinvalid_function, fun);
1874
1875 script = ENCODE_SYSTEM (script);
1876
1877+#ifdef USE_GTK
1878 /* Protect script and fun during GC. */
1879 intptr_t idx = save_script_callback (xw, script, fun);
1880
1881@@ -777,6 +949,9 @@ DEFUN ("xwidget-webkit-execute-script",
1882 NULL, /* cancelable */
1883 webkit_javascript_finished_cb,
1884 (gpointer) idx);
1885+#elif defined NS_IMPL_COCOA
1886+ nsxwidget_webkit_execute_script (xw, SSDATA (script), fun);
1887+#endif
1888 return Qnil;
1889 }
1890
1891@@ -795,6 +970,7 @@ DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize,
18923, 3, 0,
1893 xw->height = h;
1894
1895 /* If there is an offscreen widget resize it first. */
1896+#ifdef USE_GTK
1897 if (xw->widget_osr)
1898 {
1899 gtk_window_resize (GTK_WINDOW (xw->widgetwindow_osr), xw->width,
1900@@ -803,6 +979,9 @@ DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize,
19013, 3, 0,
1902 gtk_widget_set_size_request (GTK_WIDGET (xw->widget_osr), xw->width,
1903 xw->height);
1904 }
1905+#elif defined NS_IMPL_COCOA
1906+ nsxwidget_resize (xw);
1907+#endif
1908
1909 for (Lisp_Object tail = Vxwidget_view_list; CONSP (tail); tail = XCDR (tail))
1910 {
1911@@ -810,8 +989,14 @@ DEFUN ("xwidget-resize", Fxwidget_resize, Sxwidget_resize,
19123, 3, 0,
1913 {
1914 struct xwidget_view *xv = XXWIDGET_VIEW (XCAR (tail));
1915 if (XXWIDGET (xv->model) == xw)
1916+ {
1917+#ifdef USE_GTK
1918 gtk_widget_set_size_request (GTK_WIDGET (xv->widget), xw->width,
1919 xw->height);
1920+#elif defined NS_IMPL_COCOA
1921+ nsxwidget_resize_view(xv, xw->width, xw->height);
1922+#endif
1923+ }
1924 }
1925 }
1926
1927@@ -830,9 +1015,13 @@ DEFUN ("xwidget-size-request",
1928 (Lisp_Object xwidget)
1929 {
1930 CHECK_XWIDGET (xwidget);
1931+#ifdef USE_GTK
1932 GtkRequisition requisition;
1933 gtk_widget_size_request (XXWIDGET (xwidget)->widget_osr, &requisition);
1934 return list2i (requisition.width, requisition.height);
1935+#elif defined NS_IMPL_COCOA
1936+ return nsxwidget_get_size(XXWIDGET (xwidget));
1937+#endif
1938 }
1939
1940 DEFUN ("xwidgetp",
1941@@ -909,14 +1098,19 @@ DEFUN ("delete-xwidget-view",
1942 {
1943 CHECK_XWIDGET_VIEW (xwidget_view);
1944 struct xwidget_view *xv = XXWIDGET_VIEW (xwidget_view);
1945- gtk_widget_destroy (xv->widgetwindow);
1946 Vxwidget_view_list = Fdelq (xwidget_view, Vxwidget_view_list);
1947+#ifdef USE_GTK
1948+ gtk_widget_destroy (xv->widgetwindow);
1949 /* xv->model still has signals pointing to the view. There can be
1950 several views. Find the matching signals and delete them all. */
1951 g_signal_handlers_disconnect_matched (XXWIDGET
1952(xv->model)->widgetwindow_osr,
1953 G_SIGNAL_MATCH_DATA,
1954 0, 0, 0, 0,
1955 xv->widget);
1956+#elif defined NS_IMPL_COCOA
1957+ nsxwidget_delete_view (xv);
1958+#endif
1959+
1960 return Qnil;
1961 }
1962
1963@@ -1022,7 +1216,10 @@ syms_of_xwidget (void)
1964 defsubr (&Sxwidget_query_on_exit_flag);
1965 defsubr (&Sset_xwidget_query_on_exit_flag);
1966
1967+ defsubr (&Sxwidget_webkit_uri);
1968+ defsubr (&Sxwidget_webkit_title);
1969 defsubr (&Sxwidget_webkit_goto_uri);
1970+ defsubr (&Sxwidget_webkit_goto_history);
1971 defsubr (&Sxwidget_webkit_zoom);
1972 defsubr (&Sxwidget_webkit_execute_script);
1973 DEFSYM (Qwebkit, "webkit");
1974@@ -1193,11 +1390,19 @@ xwidget_end_redisplay (struct window *w, struct
1975glyph_matrix *matrix)
1976 xwidget_end_redisplay (w->current_matrix); */
1977 struct xwidget_view *xv
1978 = xwidget_view_lookup (glyph->u.xwidget, w);
1979+#ifdef USE_GTK
1980 /* FIXME: Is it safe to assume xwidget_view_lookup
1981 always succeeds here? If so, this comment can be removed.
1982 If not, the code probably needs fixing. */
1983 eassume (xv);
1984 xwidget_touch (xv);
1985+#elif defined NS_IMPL_COCOA
1986+ /* In NS xwidget, xv can be NULL for the second or
1987+ later views for a model, the result of 1 to 1
1988+ model view relation enforcement. */
1989+ if (xv)
1990+ xwidget_touch (xv);
1991+#endif
1992 }
1993 }
1994 }
1995@@ -1214,9 +1419,21 @@ xwidget_end_redisplay (struct window *w, struct
1996glyph_matrix *matrix)
1997 if (XWINDOW (xv->w) == w)
1998 {
1999 if (xwidget_touched (xv))
2000- xwidget_show_view (xv);
2001+ {
2002+#ifdef USE_GTK
2003+ xwidget_show_view (xv);
2004+#elif defined NS_IMPL_COCOA
2005+ nsxwidget_show_view (xv);
2006+#endif
2007+ }
2008 else
2009- xwidget_hide_view (xv);
2010+ {
2011+#ifdef USE_GTK
2012+ xwidget_hide_view (xv);
2013+#elif defined NS_IMPL_COCOA
2014+ nsxwidget_hide_view (xv);
2015+#endif
2016+ }
2017 }
2018 }
2019 }
2020@@ -1235,6 +1452,7 @@ kill_buffer_xwidgets (Lisp_Object buffer)
2021 {
2022 CHECK_XWIDGET (xwidget);
2023 struct xwidget *xw = XXWIDGET (xwidget);
2024+#ifdef USE_GTK
2025 if (xw->widget_osr && xw->widgetwindow_osr)
2026 {
2027 gtk_widget_destroy (xw->widget_osr);
2028@@ -1248,6 +1466,9 @@ kill_buffer_xwidgets (Lisp_Object buffer)
2029 xfree (xmint_pointer (XCAR (cb)));
2030 ASET (xw->script_callbacks, idx, Qnil);
2031 }
2032+#elif defined NS_IMPL_COCOA
2033+ nsxwidget_kill (xw);
2034+#endif
2035 }
2036 }
2037 }
2038diff --git a/src/xwidget.h b/src/xwidget.h
2039index 1b6368daab..11260f98b1 100644
2040--- a/src/xwidget.h
2041+++ b/src/xwidget.h
2042@@ -29,7 +29,13 @@ #define XWIDGET_H_INCLUDED
2043 struct window;
2044
2045 #ifdef HAVE_XWIDGETS
2046-# include <gtk/gtk.h>
2047+
2048+#if defined (USE_GTK)
2049+#include <gtk/gtk.h>
2050+#elif defined (NS_IMPL_COCOA) && defined (__OBJC__)
2051+#import <AppKit/NSView.h>
2052+#import "nsxwidget.h"
2053+#endif
2054
2055 struct xwidget
2056 {
2057@@ -54,9 +60,25 @@ #define XWIDGET_H_INCLUDED
2058 int height;
2059 int width;
2060
2061+#if defined (USE_GTK)
2062 /* For offscreen widgets, unused if not osr. */
2063 GtkWidget *widget_osr;
2064 GtkWidget *widgetwindow_osr;
2065+#elif defined (NS_IMPL_COCOA)
2066+# ifdef __OBJC__
2067+ /* For offscreen widgets, unused if not osr. */
2068+ NSView *xwWidget;
2069+ XwWindow *xwWindow;
2070+
2071+ /* Used only for xwidget types (such as webkit2) enforcing 1 to 1
2072+ relationship between model and view. */
2073+ struct xwidget_view *xv;
2074+# else
2075+ void *xwWidget;
2076+ void *xwWindow;
2077+ struct xwidget_view *xv;
2078+# endif
2079+#endif
2080
2081 /* Kill silently if Emacs is exited. */
2082 bool_bf kill_without_query : 1;
2083@@ -75,9 +97,20 @@ #define XWIDGET_H_INCLUDED
2084 /* The "live" instance isn't drawn. */
2085 bool hidden;
2086
2087+#if defined (USE_GTK)
2088 GtkWidget *widget;
2089 GtkWidget *widgetwindow;
2090 GtkWidget *emacswindow;
2091+#elif defined (NS_IMPL_COCOA)
2092+# ifdef __OBJC__
2093+ XvWindow *xvWindow;
2094+ NSView *emacswindow;
2095+# else
2096+ void *xvWindow;
2097+ void *emacswindow;
2098+# endif
2099+#endif
2100+
2101 int x;
2102 int y;
2103 int clip_right;
2104@@ -116,6 +149,21 @@ #define XG_XWIDGET_VIEW "emacs_xwidget_view"
2105 struct xwidget *lookup_xwidget (Lisp_Object spec);
2106 void xwidget_end_redisplay (struct window *, struct glyph_matrix *);
2107 void kill_buffer_xwidgets (Lisp_Object);
2108+#ifdef NS_IMPL_COCOA
2109+/* Defined in 'xwidget.c'. */
2110+void store_xwidget_event_string (struct xwidget *xw,
2111+ const char *eventname,
2112+ const char *eventstr);
2113+
2114+void store_xwidget_download_callback_event (struct xwidget *xw,
2115+ const char *url,
2116+ const char *mimetype,
2117+ const char *filename);
2118+
2119+void store_xwidget_js_callback_event (struct xwidget *xw,
2120+ Lisp_Object proc,
2121+ Lisp_Object argument);
2122+#endif
2123 #else
2124 INLINE_HEADER_BEGIN
2125 INLINE void syms_of_xwidget (void) {}
2126--
21272.17.2 (Apple Git-113)