* lisp/xwidget.el (xwidget-webkit-scroll-behavior):
[emacs.git] / lisp / xwidget.el
blob9f257ba463af67e350dc6ca73e5cecc84ba9d700
1 ;;; xwidget.el --- api functions for xwidgets -*- lexical-binding: t -*-
2 ;;
3 ;; Copyright (C) 2011-2016 Free Software Foundation, Inc.
4 ;;
5 ;; Author: Joakim Verona (joakim@verona.se)
6 ;;
7 ;; This file is part of GNU Emacs.
8 ;;
9 ;; GNU Emacs is free software: you can redistribute it and/or modify
10 ;; it under the terms of the GNU General Public License as published by
11 ;; the Free Software Foundation, either version 3 of the License, or
12 ;; (at your option) any later version.
14 ;; GNU Emacs is distributed in the hope that it will be useful,
15 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 ;; GNU General Public License for more details.
19 ;; You should have received a copy of the GNU General Public License
20 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
22 ;; --------------------------------------------------------------------
24 ;;; Commentary:
26 ;; See xwidget.c for more api functions.
28 ;; This breaks compilation when we don't have xwidgets.
29 ;; And is pointless when we do, since it's in C and so preloaded.
30 ;;(require 'xwidget-internal)
32 ;;; Code:
34 (require 'cl-lib)
35 (require 'bookmark)
37 (defcustom xwidget-webkit-scroll-behavior 'native
38 "Scrolling behavior of the webkit instance.
39 The possible values are: `native' or `image'."
40 :version "25.1"
41 :group 'frames ; TODO add xwidgets group if more options are added
42 :type '(choice (const native) (const image)))
44 (declare-function make-xwidget "xwidget.c"
45 (beg end type title width height arguments &optional buffer))
46 (declare-function xwidget-set-adjustment "xwidget.c"
47 (xwidget axis relative value))
48 (declare-function xwidget-buffer "xwidget.c" (xwidget))
49 (declare-function xwidget-webkit-get-title "xwidget.c" (xwidget))
50 (declare-function xwidget-size-request "xwidget.c" (xwidget))
51 (declare-function xwidget-resize "xwidget.c" (xwidget new-width new-height))
52 (declare-function xwidget-webkit-execute-script "xwidget.c" (xwidget script))
53 (declare-function xwidget-webkit-goto-uri "xwidget.c" (xwidget uri))
54 (declare-function xwidget-plist "xwidget.c" (xwidget))
55 (declare-function set-xwidget-plist "xwidget.c" (xwidget plist))
56 (declare-function xwidget-view-window "xwidget.c" (xwidget-view))
57 (declare-function xwidget-view-model "xwidget.c" (xwidget-view))
58 (declare-function delete-xwidget-view "xwidget.c" (xwidget-view))
59 (declare-function get-buffer-xwidgets "xwidget.c" (buffer))
61 (defun xwidget-insert (pos type title width height &optional args)
62 "Insert an xwidget at position POS.
63 Supply the xwidget's TYPE, TITLE, WIDTH, and HEIGHT.
64 See `make-xwidget' for the possible TYPE values.
65 The usage of optional argument ARGS depends on the xwidget.
66 This returns the result of `make-xwidget'."
67 (goto-char pos)
68 (let ((id (make-xwidget (point) (point)
69 type title width height args)))
70 (put-text-property (point) (+ 1 (point))
71 'display (list 'xwidget ':xwidget id))
72 id))
74 (defun xwidget-at (pos)
75 "Return xwidget at POS."
76 ;; TODO this function is a bit tedious because the C layer isn't well
77 ;; protected yet and xwidgetp apparently doesn't work yet.
78 (let* ((disp (get-text-property pos 'display))
79 (xw (car (cdr (cdr disp)))))
80 ;;(if (xwidgetp xw) xw nil)
81 (if (equal 'xwidget (car disp)) xw)))
85 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
86 ;;; webkit support
87 (require 'browse-url)
88 (require 'image-mode);;for some image-mode alike functionality
90 ;;;###autoload
91 (defun xwidget-webkit-browse-url (url &optional new-session)
92 "Ask xwidget-webkit to browse URL.
93 NEW-SESSION specifies whether to create a new xwidget-webkit session.
94 Interactively, URL defaults to the string looking like a url around point."
95 (interactive (progn
96 (require 'browse-url)
97 (browse-url-interactive-arg "xwidget-webkit URL: "
98 ;;(xwidget-webkit-current-url)
99 )))
100 (when (stringp url)
101 (if new-session
102 (xwidget-webkit-new-session url)
103 (xwidget-webkit-goto-url url))))
105 ;;todo.
106 ;; - check that the webkit support is compiled in
107 (defvar xwidget-webkit-mode-map
108 (let ((map (make-sparse-keymap)))
109 (define-key map "g" 'xwidget-webkit-browse-url)
110 (define-key map "a" 'xwidget-webkit-adjust-size-dispatch)
111 (define-key map "b" 'xwidget-webkit-back)
112 (define-key map "r" 'xwidget-webkit-reload)
113 (define-key map "t" (lambda () (interactive) (message "o"))) ;FIXME: ?!?
114 (define-key map "\C-m" 'xwidget-webkit-insert-string)
115 (define-key map "w" 'xwidget-webkit-current-url)
117 ;;similar to image mode bindings
118 (define-key map (kbd "SPC") 'xwidget-webkit-scroll-up)
119 (define-key map (kbd "DEL") 'xwidget-webkit-scroll-down)
121 (define-key map [remap scroll-up] 'xwidget-webkit-scroll-up)
122 (define-key map [remap scroll-up-command] 'xwidget-webkit-scroll-up)
124 (define-key map [remap scroll-down] 'xwidget-webkit-scroll-down)
125 (define-key map [remap scroll-down-command] 'xwidget-webkit-scroll-down)
127 (define-key map [remap forward-char] 'xwidget-webkit-scroll-forward)
128 (define-key map [remap backward-char] 'xwidget-webkit-scroll-backward)
129 (define-key map [remap right-char] 'xwidget-webkit-scroll-forward)
130 (define-key map [remap left-char] 'xwidget-webkit-scroll-backward)
131 ;; (define-key map [remap previous-line] 'image-previous-line)
132 ;; (define-key map [remap next-line] 'image-next-line)
134 ;; (define-key map [remap move-beginning-of-line] 'image-bol)
135 ;; (define-key map [remap move-end-of-line] 'image-eol)
136 ;; (define-key map [remap beginning-of-buffer] 'image-bob)
137 ;; (define-key map [remap end-of-buffer] 'image-eob)
138 map)
139 "Keymap for `xwidget-webkit-mode'.")
141 (defun xwidget-webkit-scroll-up ()
142 "Scroll webkit up.
143 Depending on the value of `xwidget-webkit-scroll-behavior',
144 this scrolls in 'native' fashion, or like `image-mode' would."
145 (interactive)
146 (if (eq xwidget-webkit-scroll-behavior 'native)
147 (xwidget-set-adjustment (xwidget-webkit-last-session) 'vertical t 50)
148 (image-scroll-up)))
150 (defun xwidget-webkit-scroll-down ()
151 "Scroll webkit down.
152 Depending on the value of `xwidget-webkit-scroll-behavior',
153 this scrolls in 'native' fashion, or like `image-mode' would."
154 (interactive)
155 (if (eq xwidget-webkit-scroll-behavior 'native)
156 (xwidget-set-adjustment (xwidget-webkit-last-session) 'vertical t -50)
157 (image-scroll-down)))
159 (defun xwidget-webkit-scroll-forward ()
160 "Scroll webkit forwards.
161 Depending on the value of `xwidget-webkit-scroll-behavior',
162 this scrolls in 'native' fashion, or like `image-mode' would."
163 (interactive)
164 (if (eq xwidget-webkit-scroll-behavior 'native)
165 (xwidget-set-adjustment (xwidget-webkit-last-session) 'horizontal t 50)
166 (xwidget-webkit-scroll-forward))) ; FIXME infloop!
168 (defun xwidget-webkit-scroll-backward ()
169 "Scroll webkit backwards.
170 Depending on the value of `xwidget-webkit-scroll-behavior',
171 this scrolls in 'native' fashion, or like `image-mode' would."
172 (interactive)
173 (if (eq xwidget-webkit-scroll-behavior 'native)
174 (xwidget-set-adjustment (xwidget-webkit-last-session) 'horizontal t -50)
175 (xwidget-webkit-scroll-backward))) ; FIXME infloop!
178 ;; The xwidget event needs to go into a higher level handler
179 ;; since the xwidget can generate an event even if it's offscreen.
180 ;; TODO this needs to use callbacks and consider different xwidget event types.
181 (define-key (current-global-map) [xwidget-event] #'xwidget-event-handler)
182 (defun xwidget-log (&rest msg)
183 "Log MSG to a buffer."
184 (let ((buf (get-buffer-create " *xwidget-log*")))
185 (with-current-buffer buf
186 (insert (apply #'format msg))
187 (insert "\n"))))
189 (defun xwidget-event-handler ()
190 "Receive xwidget event."
191 (interactive)
192 (xwidget-log "stuff happened to xwidget %S" last-input-event)
193 (let*
194 ((xwidget-event-type (nth 1 last-input-event))
195 (xwidget (nth 2 last-input-event))
196 ;;(xwidget-callback (xwidget-get xwidget 'callback))
197 ;;TODO stopped working for some reason
199 ;;(funcall xwidget-callback xwidget xwidget-event-type)
200 (message "xw callback %s" xwidget)
201 (funcall 'xwidget-webkit-callback xwidget xwidget-event-type)))
203 (defun xwidget-webkit-callback (xwidget xwidget-event-type)
204 "Callback for xwidgets.
205 XWIDGET instance, XWIDGET-EVENT-TYPE depends on the originating xwidget."
206 (if (not (buffer-live-p (xwidget-buffer xwidget)))
207 (xwidget-log
208 "error: callback called for xwidget with dead buffer")
209 (with-current-buffer (xwidget-buffer xwidget)
210 (let* ((strarg (nth 3 last-input-event)))
211 (cond ((eq xwidget-event-type 'document-load-finished)
212 (xwidget-log "webkit finished loading: '%s'"
213 (xwidget-webkit-get-title xwidget))
214 ;;TODO - check the native/internal scroll
215 ;;(xwidget-adjust-size-to-content xwidget)
216 (xwidget-webkit-adjust-size-dispatch) ;;TODO xwidget arg
217 (rename-buffer (format "*xwidget webkit: %s *"
218 (xwidget-webkit-get-title xwidget)))
219 (pop-to-buffer (current-buffer)))
220 ((eq xwidget-event-type
221 'navigation-policy-decision-requested)
222 (if (string-match ".*#\\(.*\\)" strarg)
223 (xwidget-webkit-show-id-or-named-element
224 xwidget
225 (match-string 1 strarg))))
226 (t (xwidget-log "unhandled event:%s" xwidget-event-type)))))))
228 (defvar bookmark-make-record-function)
229 (define-derived-mode xwidget-webkit-mode
230 special-mode "xwidget-webkit" "Xwidget webkit view mode."
231 (setq buffer-read-only t)
232 (setq-local bookmark-make-record-function
233 #'xwidget-webkit-bookmark-make-record)
234 ;; Keep track of [vh]scroll when switching buffers
235 (image-mode-setup-winprops))
237 (defun xwidget-webkit-bookmark-make-record ()
238 "Integrate Emacs bookmarks with the webkit xwidget."
239 (nconc (bookmark-make-record-default t t)
240 `((page . ,(xwidget-webkit-current-url))
241 (handler . (lambda (bmk) (browse-url
242 (bookmark-prop-get bmk 'page)))))))
245 (defvar xwidget-webkit-last-session-buffer nil)
247 (defun xwidget-webkit-last-session ()
248 "Last active webkit, or nil."
249 (if (buffer-live-p xwidget-webkit-last-session-buffer)
250 (with-current-buffer xwidget-webkit-last-session-buffer
251 (xwidget-at (point-min)))
252 nil))
254 (defun xwidget-webkit-current-session ()
255 "Either the webkit in the current buffer, or the last one used.
256 The latter might be nil."
257 (or (xwidget-at (point-min)) (xwidget-webkit-last-session)))
259 (defun xwidget-adjust-size-to-content (xw)
260 "Resize XW to content."
261 ;; xwidgets doesn't support widgets that have their own opinions about
262 ;; size well, yet this reads the desired size and resizes the Emacs
263 ;; allocated area accordingly.
264 (let ((size (xwidget-size-request xw)))
265 (xwidget-resize xw (car size) (cadr size))))
268 (defvar xwidget-webkit-activeelement-js"
269 function findactiveelement(doc){
270 //alert(doc.activeElement.value);
271 if(doc.activeElement.value != undefined){
272 return doc.activeElement;
273 }else{
274 // recurse over the child documents:
275 var frames = doc.getElementsByTagName('frame');
276 for (var i = 0; i < frames.length; i++)
278 var d = frames[i].contentDocument;
279 var rv = findactiveelement(d);
280 if(rv != undefined){
281 return rv;
285 return undefined;
291 "javascript that finds the active element."
292 ;; Yes it's ugly, because:
293 ;; - there is apparently no way to find the active frame other than recursion
294 ;; - the js "for each" construct misbehaved on the "frames" collection
295 ;; - a window with no frameset still has frames.length == 1, but
296 ;; frames[0].document.activeElement != document.activeElement
297 ;;TODO the activeelement type needs to be examined, for iframe, etc.
300 (defun xwidget-webkit-insert-string (xw str)
301 "Insert string STR in the active field in the webkit XW."
302 ;; Read out the string in the field first and provide for edit.
303 (interactive
304 (let* ((xww (xwidget-webkit-current-session))
306 (field-value
307 (progn
308 (xwidget-webkit-execute-script xww xwidget-webkit-activeelement-js)
309 (xwidget-webkit-execute-script-rv
311 "findactiveelement(document).value;")))
312 (field-type (xwidget-webkit-execute-script-rv
314 "findactiveelement(document).type;")))
315 (list xww
316 (cond ((equal "text" field-type)
317 (read-string "Text: " field-value))
318 ((equal "password" field-type)
319 (read-passwd "Password: " nil field-value))
320 ((equal "textarea" field-type)
321 (xwidget-webkit-begin-edit-textarea xww field-value))))))
322 (xwidget-webkit-execute-script
324 (format "findactiveelement(document).value='%s'" str)))
326 (defvar xwidget-xwbl)
327 (defun xwidget-webkit-begin-edit-textarea (xw text)
328 "Start editing of a webkit text area.
329 XW is the xwidget identifier, TEXT is retrieved from the webkit."
330 (switch-to-buffer
331 (generate-new-buffer "textarea"))
332 (set (make-local-variable 'xwidget-xwbl) xw)
333 (insert text))
335 (defun xwidget-webkit-end-edit-textarea ()
336 "End editing of a webkit text area."
337 (interactive)
338 (goto-char (point-min))
339 (while (search-forward "\n" nil t)
340 (replace-match "\\n" nil t))
341 (xwidget-webkit-execute-script
342 xwidget-xwbl
343 (format "findactiveelement(document).value='%s'"
344 (buffer-substring (point-min) (point-max))))
345 ;;TODO convert linefeed to \n
348 (defun xwidget-webkit-show-named-element (xw element-name)
349 "Make webkit xwidget XW show a named element ELEMENT-NAME.
350 For example, use this to display an anchor."
351 (interactive (list (xwidget-webkit-current-session)
352 (read-string "Element name: ")))
353 ;;TODO since an xwidget is an Emacs object, it is not trivial to do
354 ;; some things that are taken for granted in a normal browser.
355 ;; scrolling an anchor/named-element into view is one such thing.
356 ;; This function implements a proof-of-concept for this. Problems
357 ;; remaining: - The selected window is scrolled but this is not
358 ;; always correct - This needs to be interfaced into browse-url
359 ;; somehow. The tricky part is that we need to do this in two steps:
360 ;; A: load the base url, wait for load signal to arrive B: navigate
361 ;; to the anchor when the base url is finished rendering
363 ;; This part figures out the Y coordinate of the element
364 (let ((y (string-to-number
365 (xwidget-webkit-execute-script-rv
367 (format
368 "document.getElementsByName('%s')[0].getBoundingClientRect().top"
369 element-name)
370 0))))
371 ;; Now we need to tell Emacs to scroll the element into view.
372 (xwidget-log "scroll: %d" y)
373 (set-window-vscroll (selected-window) y t)))
375 (defun xwidget-webkit-show-id-element (xw element-id)
376 "Make webkit xwidget XW show an id-element ELEMENT-ID.
377 For example, use this to display an anchor."
378 (interactive (list (xwidget-webkit-current-session)
379 (read-string "Element id: ")))
380 (let ((y (string-to-number
381 (xwidget-webkit-execute-script-rv
383 (format "document.getElementById('%s').getBoundingClientRect().top"
384 element-id)
385 0))))
386 ;; Now we need to tell Emacs to scroll the element into view.
387 (xwidget-log "scroll: %d" y)
388 (set-window-vscroll (selected-window) y t)))
390 (defun xwidget-webkit-show-id-or-named-element (xw element-id)
391 "Make webkit xwidget XW show a name or element id ELEMENT-ID.
392 For example, use this to display an anchor."
393 (interactive (list (xwidget-webkit-current-session)
394 (read-string "Name or element id: ")))
395 (let* ((y1 (string-to-number
396 (xwidget-webkit-execute-script-rv
398 (format "document.getElementsByName('%s')[0].getBoundingClientRect().top" element-id)
399 "0")))
400 (y2 (string-to-number
401 (xwidget-webkit-execute-script-rv
403 (format "document.getElementById('%s').getBoundingClientRect().top" element-id)
404 "0")))
405 (y3 (max y1 y2)))
406 ;; Now we need to tell Emacs to scroll the element into view.
407 (xwidget-log "scroll: %d" y3)
408 (set-window-vscroll (selected-window) y3 t)))
410 (defun xwidget-webkit-adjust-size-to-content ()
411 "Adjust webkit to content size."
412 (interactive)
413 (xwidget-adjust-size-to-content (xwidget-webkit-current-session)))
415 (defun xwidget-webkit-adjust-size-dispatch ()
416 "Adjust size according to mode."
417 (interactive)
418 (if (eq xwidget-webkit-scroll-behavior 'native)
419 (xwidget-webkit-adjust-size-to-window)
420 (xwidget-webkit-adjust-size-to-content))
421 ;; The recenter is intended to correct a visual glitch.
422 ;; It errors out if the buffer isn't visible, but then we don't get
423 ;; the glitch, so silence errors.
424 (ignore-errors
425 (recenter-top-bottom)))
427 (defun xwidget-webkit-adjust-size-to-window ()
428 "Adjust webkit to window."
429 (interactive)
430 (xwidget-resize (xwidget-webkit-current-session) (window-pixel-width)
431 (window-pixel-height)))
433 (defun xwidget-webkit-adjust-size (w h)
434 "Manually set webkit size to width W, height H."
435 ;; TODO shouldn't be tied to the webkit xwidget
436 (interactive "nWidth:\nnHeight:\n")
437 (xwidget-resize (xwidget-webkit-current-session) w h))
439 (defun xwidget-webkit-fit-width ()
440 "Adjust width of webkit to window width."
441 (interactive)
442 (xwidget-webkit-adjust-size (- (nth 2 (window-inside-pixel-edges))
443 (car (window-inside-pixel-edges)))
444 1000))
446 (defun xwidget-webkit-new-session (url)
447 "Create a new webkit session buffer with URL."
448 (let*
449 ((bufname (generate-new-buffer-name "*xwidget-webkit*"))
451 (setq xwidget-webkit-last-session-buffer (switch-to-buffer
452 (get-buffer-create bufname)))
453 (insert " 'a' adjusts the xwidget size.")
454 (setq xw (xwidget-insert 1 'webkit-osr bufname 1000 1000))
455 (xwidget-put xw 'callback 'xwidget-webkit-callback)
456 (xwidget-webkit-mode)
457 (xwidget-webkit-goto-uri (xwidget-webkit-last-session) url)))
460 (defun xwidget-webkit-goto-url (url)
461 "Goto URL."
462 (if (xwidget-webkit-current-session)
463 (progn
464 (xwidget-webkit-goto-uri (xwidget-webkit-current-session) url))
465 (xwidget-webkit-new-session url)))
467 (defun xwidget-webkit-back ()
468 "Go back in history."
469 (interactive)
470 (xwidget-webkit-execute-script (xwidget-webkit-current-session)
471 "history.go(-1);"))
473 (defun xwidget-webkit-reload ()
474 "Reload current url."
475 (interactive)
476 (xwidget-webkit-execute-script (xwidget-webkit-current-session)
477 "history.go(0);"))
479 (defun xwidget-webkit-current-url ()
480 "Get the webkit url and place it on the kill-ring."
481 (interactive)
482 (let* ((rv (xwidget-webkit-execute-script-rv (xwidget-webkit-current-session)
483 "document.URL"))
484 (url (kill-new (or rv ""))))
485 (message "url: %s" url)
486 url))
488 (defun xwidget-webkit-execute-script-rv (xw script &optional default)
489 "Same as `xwidget-webkit-execute-script' but with return value.
490 XW is the webkit instance. SCRIPT is the script to execute.
491 DEFAULT is the default return value."
492 ;; Notice the ugly "title" hack. It is needed because the Webkit
493 ;; API at the time of writing didn't support returning values. This
494 ;; is a wrapper for the title hack so it's easy to remove should
495 ;; Webkit someday support JS return values or we find some other way
496 ;; to access the DOM.
498 ;; Reset webkit title. Not very nice.
499 (let* ((emptytag "titlecantbewhitespaceohthehorror")
500 title)
501 (xwidget-webkit-execute-script xw (format "document.title=\"%s\";"
502 (or default emptytag)))
503 (xwidget-webkit-execute-script xw (format "document.title=%s;" script))
504 (setq title (xwidget-webkit-get-title xw))
505 (if (equal emptytag title)
506 (setq title ""))
507 (unless title
508 (setq title default))
509 title))
511 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
512 (defun xwidget-webkit-get-selection ()
513 "Get the webkit selection."
514 (xwidget-webkit-execute-script-rv (xwidget-webkit-current-session)
515 "window.getSelection().toString();"))
517 (defun xwidget-webkit-copy-selection-as-kill ()
518 "Get the webkit selection and put it on the kill-ring."
519 (interactive)
520 (kill-new (xwidget-webkit-get-selection)))
523 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
524 ;; Xwidget plist management (similar to the process plist functions)
526 (defun xwidget-get (xwidget propname)
527 "Get an xwidget's property value.
528 XWIDGET is an xwidget, PROPNAME a property.
529 Returns the last value stored with `xwidget-put'."
530 (plist-get (xwidget-plist xwidget) propname))
532 (defun xwidget-put (xwidget propname value)
533 "Set an xwidget's property value.
534 XWIDGET is an xwidget, PROPNAME a property to be set to specified VALUE.
535 You can retrieve the value with `xwidget-get'."
536 (set-xwidget-plist xwidget
537 (plist-put (xwidget-plist xwidget) propname value)))
540 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
542 (defvar xwidget-view-list) ; xwidget.c
543 (defvar xwidget-list) ; xwidget.c
545 (defun xwidget-delete-zombies ()
546 "Helper for `xwidget-cleanup'."
547 (dolist (xwidget-view xwidget-view-list)
548 (when (or (not (window-live-p (xwidget-view-window xwidget-view)))
549 (not (memq (xwidget-view-model xwidget-view)
550 xwidget-list)))
551 (delete-xwidget-view xwidget-view))))
553 (defun xwidget-cleanup ()
554 "Delete zombie xwidgets."
555 ;; During development it was sometimes easy to wind up with zombie
556 ;; xwidget instances.
557 ;; This function tries to implement a workaround should it occur again.
558 (interactive)
559 ;; Kill xviews that should have been deleted but still linger.
560 (xwidget-delete-zombies)
561 ;; Redraw display otherwise ghost of zombies will remain to haunt the screen
562 (redraw-display))
564 (defun xwidget-kill-buffer-query-function ()
565 "Ask before killing a buffer that has xwidgets."
566 (let ((xwidgets (get-buffer-xwidgets (current-buffer))))
567 (or (not xwidgets)
568 (not (memq t (mapcar #'xwidget-query-on-exit-flag xwidgets)))
569 (yes-or-no-p
570 (format "Buffer %S has xwidgets; kill it? " (buffer-name))))))
572 (when (featurep 'xwidget-internal)
573 (add-hook 'kill-buffer-query-functions #'xwidget-kill-buffer-query-function)
574 ;; This would have felt better in C, but this seems to work well in
575 ;; practice though.
576 (add-hook 'window-configuration-change-hook #'xwidget-delete-zombies))
578 (provide 'xwidget)
579 ;;; xwidget.el ends here