diff options
Diffstat (limited to 'lisp/net/browse-url.el')
-rw-r--r-- | lisp/net/browse-url.el | 176 |
1 files changed, 157 insertions, 19 deletions
diff --git a/lisp/net/browse-url.el b/lisp/net/browse-url.el index b34665358ca..f88de98fca7 100644 --- a/lisp/net/browse-url.el +++ b/lisp/net/browse-url.el @@ -119,6 +119,17 @@ ;; could be done by setting `browse-url-browser-function' to an alist ;; but this usage is deprecated now. +;; All browser functions provided by here have a +;; `browse-url-browser-kind' symbol property set to either `internal' +;; or `external' which determines if they browse the given URL inside +;; Emacs or spawn an external application with it. Some parts of +;; Emacs make use of that, e.g., when an URL is dragged into Emacs, it +;; is not sensible to invoke an external browser with it, so here only +;; internal browsers are considered. Therefore, it is advised to put +;; that property also on custom browser functions. +;; (put 'my-browse-url-in-emacs 'browse-url-browser-kind 'internal) +;; (put 'my-browse-url-externally 'browse-url-browser-kind 'external) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Code: @@ -593,26 +604,54 @@ down (this *won't* always work)." "Wrapper command prepended to the Elinks command-line." :type '(repeat (string :tag "Wrapper"))) +(defun browse-url--browser-kind (function url) + "Return the browser kind of a browser FUNCTION for URL. +The browser kind is either `internal' (the browser runs inside +Emacs), `external' (the browser is spawned in an external +process), or nil (we don't know)." + (let ((kind (if (symbolp function) + (get function 'browse-url-browser-kind)))) + (if (functionp kind) + (funcall kind url) + kind))) + (defun browse-url--mailto (url &rest args) "Calls `browse-url-mailto-function' with URL and ARGS." (funcall browse-url-mailto-function url args)) +(defun browse-url--browser-kind-mailto (url) + (browse-url--browser-kind browse-url-mailto-function url)) +(put 'browse-url--mailto 'browse-url-browser-kind + #'browse-url--browser-kind-mailto) + (defun browse-url--man (url &rest args) "Calls `browse-url-man-function' with URL and ARGS." (funcall browse-url-man-function url args)) +(defun browse-url--browser-kind-man (url) + (browse-url--browser-kind browse-url-man-function url)) +(put 'browse-url--man 'browse-url-browser-kind + #'browse-url--browser-kind-man) + (defun browse-url--browser (url &rest args) "Calls `browse-url-browser-function' with URL and ARGS." (funcall browse-url-browser-function url args)) +(defun browse-url--browser-kind-browser (url) + (browse-url--browser-kind browse-url-browser-function url)) +(put 'browse-url--browser 'browse-url-browser-kind + #'browse-url--browser-kind-browser) + +(defun browse-url--non-html-file-url-p (url) + "Return non-nil if URL is a file:// URL of a non-HTML file." + (and (string-match-p "\\`file://" url) + (not (string-match-p "\\`file://.*\\.html?\\b" url)))) + ;;;###autoload (defvar browse-url-default-handlers '(("\\`mailto:" . browse-url--mailto) ("\\`man:" . browse-url--man) - ;; Render file:// URLs if they are HTML pages, otherwise just find - ;; the file. - ("\\`file://.*\\.html?\\b" . browse-url--browser) - ("\\`file://" . browse-url-emacs)) + (browse-url--non-html-file-url-p . browse-url-emacs)) "Like `browse-url-handlers' but populated by Emacs and packages. Emacs and external packages capable of browsing certain URLs @@ -620,46 +659,60 @@ should place their entries in this alist rather than `browse-url-handlers' which is reserved for the user.") (defcustom browse-url-handlers nil - "An alist with elements of the form (REGEXP HANDLER). -Each REGEXP is matched against the URL to be opened in turn and -the first match's HANDLER is invoked with the URL. + "An alist with elements of the form (REGEXP-OR-PREDICATE . HANDLER). +Each REGEXP-OR-PREDICATE is matched against the URL to be opened +in turn and the first match's HANDLER is invoked with the URL. A HANDLER must be a function with the same arguments as `browse-url'. -If no REGEXP matches, the same procedure is performed with the -value of `browse-url-default-handlers'. If there is also no -match, the URL is opened using the value of +If no REGEXP-OR-PREDICATE matches, the same procedure is +performed with the value of `browse-url-default-handlers'. If +there is also no match, the URL is opened using the value of `browse-url-browser-function'." - :type '(alist :key-type (regexp :tag "Regexp") + :type '(alist :key-type (choice + (regexp :tag "Regexp") + (function :tag "Predicate")) :value-type (function :tag "Handler")) :version "28.1") ;;;###autoload -(defun browse-url-select-handler (url) - "Return a handler suitable for browsing URL. +(defun browse-url-select-handler (url &optional kind) + "Return a handler of suitable for browsing URL. This searches `browse-url-handlers', and `browse-url-default-handlers' for a matching handler. Return nil if no handler is found. +If KIND is given, the search is restricted to handlers whose +function symbol has the symbol-property `browse-url-browser-kind' +set to KIND. + Currently, it also consults `browse-url-browser-function' first if it is set to an alist, although this usage is deprecated since Emacs 28.1 and will be removed in a future release." (catch 'custom-url-handler - (dolist (regex-handler + (dolist (rxpred-handler (append ;; The alist choice of browse-url-browser-function ;; is deprecated since 28.1, so the (unless ...) ;; can be removed at some point in time. (when (and (consp browse-url-browser-function) (not (functionp browse-url-browser-function))) - (warn "Having `browse-url-browser-function' set to an + (lwarn 'browse-url :warning + "Having `browse-url-browser-function' set to an alist is deprecated. Use `browse-url-handlers' instead.") browse-url-browser-function) browse-url-handlers browse-url-default-handlers)) - (when (string-match-p (car regex-handler) url) - (throw 'custom-url-handler (cdr regex-handler)))))) + (let ((rx-or-pred (car rxpred-handler)) + (handler (cdr rxpred-handler))) + (when (and (or (null kind) + (eq kind (browse-url--browser-kind + handler url))) + (if (functionp rx-or-pred) + (funcall rx-or-pred url) + (string-match-p rx-or-pred url))) + (throw 'custom-url-handler handler)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; URL encoding @@ -864,7 +917,7 @@ If ARGS are omitted, the default is to pass ;; which may not even exist any more. (if (stringp (frame-parameter nil 'display)) (setenv "DISPLAY" (frame-parameter nil 'display))) - (if (functionp nil) + (if (functionp function) (apply function url args) (error "No suitable browser for URL %s" url)))) @@ -883,6 +936,34 @@ Optional prefix argument ARG non-nil inverts the value of the option (error "No URL found")))) ;;;###autoload +(defun browse-url-with-browser-kind (kind url &optional arg) + "Browse URL with a browser of the given browser KIND. +KIND is either `internal' or `external'. + +When called interactively, the default browser kind is the +opposite of the browser kind of `browse-url-browser-function'." + (interactive + (let* ((url-arg (browse-url-interactive-arg "URL: ")) + ;; Default to the inverse kind of the default browser. + (default (if (eq (browse-url--browser-kind + browse-url-browser-function (car url-arg)) + 'internal) + 'external + 'internal)) + (k (intern (completing-read + (format "Browser kind (default %s): " default) + '(internal external) + nil t nil nil + default)))) + (cons k url-arg))) + (let ((function (browse-url-select-handler url kind))) + (unless function + (setq function (if (eq kind 'external) + #'browse-url-default-browser + #'eww))) + (funcall function url arg))) + +;;;###autoload (defun browse-url-at-mouse (event) "Ask a WWW browser to load a URL clicked with the mouse. The URL is the one around or before the position of the mouse click @@ -929,12 +1010,18 @@ The optional NEW-WINDOW argument is not used." (url-unhex-string url) url))))) +(put 'browse-url-default-windows-browser 'browse-url-browser-kind + 'external) + (defun browse-url-default-macosx-browser (url &optional _new-window) "Invoke the macOS system's default Web browser. The optional NEW-WINDOW argument is not used." (interactive (browse-url-interactive-arg "URL: ")) (start-process (concat "open " url) nil "open" url)) +(put 'browse-url-default-macosx-browser 'browse-url-browser-kind + 'external) + ;; --- Netscape --- (defun browse-url-process-environment () @@ -991,6 +1078,10 @@ instead of `browse-url-new-window-flag'." (lambda (&rest _ignore) (error "No usable browser found")))) url args)) +(put 'browse-url-default-browser 'browse-url-browser-kind + ;; Well, most probably external if we ignore w3. + 'external) + (defun browse-url-can-use-xdg-open () "Return non-nil if the \"xdg-open\" program can be used. xdg-open is a desktop utility that calls your preferred web browser." @@ -1010,6 +1101,8 @@ The optional argument IGNORED is not used." (interactive (browse-url-interactive-arg "URL: ")) (call-process "xdg-open" nil 0 nil url)) +(put 'browse-url-xdg-open 'browse-url-browser-kind 'external) + ;;;###autoload (defun browse-url-netscape (url &optional new-window) "Ask the Netscape WWW browser to load URL. @@ -1053,6 +1146,8 @@ used instead of `browse-url-new-window-flag'." `(lambda (process change) (browse-url-netscape-sentinel process ,url))))) +(put 'browse-url-netscape 'browse-url-browser-kind 'external) + (defun browse-url-netscape-sentinel (process url) "Handle a change to the process communicating with Netscape." (declare (obsolete nil "25.1")) @@ -1123,6 +1218,8 @@ used instead of `browse-url-new-window-flag'." `(lambda (process change) (browse-url-mozilla-sentinel process ,url))))) +(put 'browse-url-mozilla 'browse-url-browser-kind 'external) + (defun browse-url-mozilla-sentinel (process url) "Handle a change to the process communicating with Mozilla." (or (eq (process-exit-status process) 0) @@ -1163,6 +1260,8 @@ instead of `browse-url-new-window-flag'." '("-new-window"))) (list url))))) +(put 'browse-url-firefox 'browse-url-browser-kind 'external) + ;;;###autoload (defun browse-url-chromium (url &optional _new-window) "Ask the Chromium WWW browser to load URL. @@ -1180,6 +1279,8 @@ The optional argument NEW-WINDOW is not used." browse-url-chromium-arguments (list url))))) +(put 'browse-url-chromium 'browse-url-browser-kind 'external) + (defun browse-url-chrome (url &optional _new-window) "Ask the Google Chrome WWW browser to load URL. Default to the URL around or before point. The strings in @@ -1196,6 +1297,8 @@ The optional argument NEW-WINDOW is not used." browse-url-chrome-arguments (list url))))) +(put 'browse-url-chrome 'browse-url-browser-kind 'external) + ;;;###autoload (defun browse-url-galeon (url &optional new-window) "Ask the Galeon WWW browser to load URL. @@ -1233,6 +1336,8 @@ used instead of `browse-url-new-window-flag'." `(lambda (process change) (browse-url-galeon-sentinel process ,url))))) +(put 'browse-url-galeon 'browse-url-browser-kind 'external) + (defun browse-url-galeon-sentinel (process url) "Handle a change to the process communicating with Galeon." (declare (obsolete nil "25.1")) @@ -1279,6 +1384,8 @@ used instead of `browse-url-new-window-flag'." `(lambda (process change) (browse-url-epiphany-sentinel process ,url))))) +(put 'browse-url-epiphany 'browse-url-browser-kind 'external) + (defun browse-url-epiphany-sentinel (process url) "Handle a change to the process communicating with Epiphany." (or (eq (process-exit-status process) 0) @@ -1303,6 +1410,8 @@ currently selected window instead." file-name-handler-alist))) (if same-window (find-file url) (find-file-other-window url)))) +(put 'browse-url-emacs 'browse-url-browser-kind 'internal) + ;;;###autoload (defun browse-url-gnome-moz (url &optional new-window) "Ask Mozilla/Netscape to load URL via the GNOME program `gnome-moz-remote'. @@ -1327,6 +1436,8 @@ used instead of `browse-url-new-window-flag'." '("--newwin")) (list "--raise" url)))) +(put 'browse-url-gnome-moz 'browse-url-browser-kind 'external) + ;; --- Mosaic --- ;;;###autoload @@ -1378,6 +1489,8 @@ used instead of `browse-url-new-window-flag'." (append browse-url-mosaic-arguments (list url))) (message "Starting %s...done" browse-url-mosaic-program)))) +(put 'browse-url-mosaic 'browse-url-browser-kind 'external) + ;; --- Mosaic using CCI --- ;;;###autoload @@ -1410,6 +1523,8 @@ used instead of `browse-url-new-window-flag'." (process-send-string "browse-url" "disconnect\r\n") (delete-process "browse-url")) +(put 'browse-url-cci 'browse-url-browser-kind 'external) + ;; --- Conkeror --- ;;;###autoload (defun browse-url-conkeror (url &optional new-window) @@ -1446,6 +1561,9 @@ NEW-WINDOW instead of `browse-url-new-window-flag'." "window") "buffer") url)))))) + +(put 'browse-url-conkeror 'browse-url-browser-kind 'external) + ;; --- W3 --- ;; External. @@ -1469,6 +1587,8 @@ used instead of `browse-url-new-window-flag'." (w3-fetch-other-window url) (w3-fetch url))) +(put 'browse-url-w3 'browse-url-browser-kind 'internal) + ;;;###autoload (defun browse-url-w3-gnudoit (url &optional _new-window) ;; new-window ignored @@ -1483,6 +1603,8 @@ The `browse-url-gnudoit-program' program is used with options given by (list (concat "(w3-fetch \"" url "\")") "(raise-frame)")))) +(put 'browse-url-w3-gnudoit 'browse-url-browser-kind 'internal) + ;; --- Lynx in an xterm --- ;;;###autoload @@ -1500,6 +1622,8 @@ The optional argument NEW-WINDOW is not used." ,@browse-url-xterm-args "-e" ,browse-url-text-browser ,url))) +(put 'browse-url-text-xterm 'browse-url-browser-kind 'external) + ;; --- Lynx in an Emacs "term" window --- (declare-function term-char-mode "term" ()) @@ -1574,6 +1698,8 @@ used instead of `browse-url-new-window-flag'." url "\r"))))) +(put 'browse-url-text-emacs 'browse-url-browser-kind 'internal) + ;; --- mailto --- (autoload 'rfc2368-parse-mailto-url "rfc2368") @@ -1621,6 +1747,8 @@ used instead of `browse-url-new-window-flag'." (unless (bolp) (insert "\n")))))))) +(put 'browse-url-mail 'browse-url-browser-kind 'internal) + ;; --- man --- (defvar manual-program) @@ -1632,7 +1760,9 @@ used instead of `browse-url-new-window-flag'." (setq url (replace-regexp-in-string "\\`man:" "" url)) (cond ((executable-find manual-program) (man url)) - (t (woman (replace-regexp-in-string "([[:alnum:]]+)" "" url))))) + (t (woman (replace-regexp-in-string "([[:alnum:]]+)" "" url))))) + +(put 'browse-url-man 'browse-url-browser-kind 'internal) ;; --- Random browser --- @@ -1651,6 +1781,8 @@ don't offer a form of remote control." 0 nil (append browse-url-generic-args (list url)))) +(put 'browse-url-generic 'browse-url-browser-kind 'external) + ;;;###autoload (defun browse-url-kde (url &optional _new-window) "Ask the KDE WWW browser to load URL. @@ -1661,6 +1793,8 @@ The optional argument NEW-WINDOW is not used." (apply #'start-process (concat "KDE " url) nil browse-url-kde-program (append browse-url-kde-args (list url)))) +(put 'browse-url-kde 'browse-url-browser-kind 'external) + (defun browse-url-elinks-new-window (url) "Ask the Elinks WWW browser to load URL in a new window." (let ((process-environment (browse-url-process-environment))) @@ -1670,6 +1804,8 @@ The optional argument NEW-WINDOW is not used." browse-url-elinks-wrapper (list "elinks" url))))) +(put 'browse-url-elinks-new-window 'browse-url-browser-kind 'external) + ;;;###autoload (defun browse-url-elinks (url &optional new-window) "Ask the Elinks WWW browser to load URL. @@ -1691,6 +1827,8 @@ from `browse-url-elinks-wrapper'." `(lambda (process change) (browse-url-elinks-sentinel process ,url)))))) +(put 'browse-url-elinks 'browse-url-browser-kind 'external) + (defun browse-url-elinks-sentinel (process url) "Determines if Elinks is running or a new one has to be started." ;; Try to determine if an instance is running or if we have to |