summaryrefslogtreecommitdiff
path: root/lisp/net/eww.el
diff options
context:
space:
mode:
Diffstat (limited to 'lisp/net/eww.el')
-rw-r--r--lisp/net/eww.el147
1 files changed, 97 insertions, 50 deletions
diff --git a/lisp/net/eww.el b/lisp/net/eww.el
index 568b96f4d58..edb2f729c8b 100644
--- a/lisp/net/eww.el
+++ b/lisp/net/eww.el
@@ -25,14 +25,15 @@
;;; Code:
(require 'cl-lib)
-(require 'format-spec)
+(require 'mm-url)
+(require 'puny)
(require 'shr)
+(require 'text-property-search)
+(require 'thingatpt)
(require 'url)
(require 'url-queue)
-(require 'thingatpt)
-(require 'mm-url)
-(require 'puny)
-(eval-when-compile (require 'subr-x)) ;; for string-trim
+(require 'xdg)
+(eval-when-compile (require 'subr-x))
(defgroup eww nil
"Emacs Web Wowser"
@@ -55,11 +56,24 @@
:group 'eww
:type 'string)
-(defcustom eww-download-directory "~/Downloads/"
- "Directory where files will downloaded."
- :version "24.4"
+(defun erc--download-directory ()
+ "Return the name of the download directory.
+If ~/Downloads/ exists, that will be used, and if not, the
+DOWNLOAD XDG user directory will be returned. If that's
+undefined, ~/Downloads/ is returned anyway."
+ (or (and (file-exists-p "~/Downloads/")
+ "~/Downloads/")
+ (when-let ((dir (xdg-user-dir "DOWNLOAD")))
+ (file-name-as-directory dir))
+ "~/Downloads/"))
+
+(defcustom eww-download-directory 'erc--download-directory
+ "Directory where files will downloaded.
+This should either be a directory name or a function (called with
+no parameters) that returns a directory name."
+ :version "28.1"
:group 'eww
- :type 'directory)
+ :type '(choice directory function))
;;;###autoload
(defcustom eww-suggest-uris
@@ -263,13 +277,17 @@ This list can be customized via `eww-suggest-uris'."
(nreverse uris)))
;;;###autoload
-(defun eww (url &optional arg)
+(defun eww (url &optional arg buffer)
"Fetch URL and render the page.
If the input doesn't look like an URL or a domain name, the
word(s) will be searched for via `eww-search-prefix'.
If called with a prefix ARG, use a new buffer instead of reusing
-the default EWW buffer."
+the default EWW buffer.
+
+If BUFFER, the data to be rendered is in that buffer. In that
+case, this function doesn't actually fetch URL. BUFFER will be
+killed after rendering."
(interactive
(let* ((uris (eww-suggested-uris))
(prompt (concat "Enter URL or keywords"
@@ -307,8 +325,14 @@ the default EWW buffer."
(insert (format "Loading %s..." url))
(goto-char (point-min)))
(let ((url-mime-accept-string eww-accept-content-types))
- (url-retrieve url 'eww-render
- (list url nil (current-buffer)))))
+ (if buffer
+ (let ((eww-buffer (current-buffer)))
+ (with-current-buffer buffer
+ (eww-render nil url nil eww-buffer)))
+ (url-retrieve url #'eww-render
+ (list url nil (current-buffer))))))
+
+(function-put 'eww 'browse-url-browser-kind 'internal)
(defun eww--dwim-expand-url (url)
(setq url (string-trim url))
@@ -359,7 +383,19 @@ the default EWW buffer."
(eww (concat "file://"
(and (memq system-type '(windows-nt ms-dos))
"/")
- (expand-file-name file))))
+ (expand-file-name file))
+ nil
+ ;; The file name may be a non-local Tramp file. The URL
+ ;; library doesn't understand these file names, so use the
+ ;; normal Emacs machinery to load the file.
+ (with-current-buffer (generate-new-buffer " *eww file*")
+ (set-buffer-multibyte nil)
+ (insert "Content-type: " (or (mailcap-extension-to-mime
+ (url-file-extension file))
+ "application/octet-stream")
+ "\n\n")
+ (insert-file-contents file)
+ (current-buffer))))
;;;###autoload
(defun eww-search-words ()
@@ -373,8 +409,8 @@ engine used."
(let ((region-string (buffer-substring (region-beginning) (region-end))))
(if (not (string-match-p "\\`[ \n\t\r\v\f]*\\'" region-string))
(eww region-string)
- (call-interactively 'eww)))
- (call-interactively 'eww)))
+ (call-interactively #'eww)))
+ (call-interactively #'eww)))
(defun eww-open-in-new-buffer ()
"Fetch link at point in a new EWW buffer."
@@ -541,10 +577,10 @@ Currently this means either text/html or application/xhtml+xml."
(goto-char point))
(shr-target-id
(goto-char (point-min))
- (let ((point (next-single-property-change
- (point-min) 'shr-target-id)))
- (when point
- (goto-char point))))
+ (let ((match (text-property-search-forward
+ 'shr-target-id shr-target-id t)))
+ (when match
+ (goto-char (prop-match-beginning match)))))
(t
(goto-char (point-min))
;; Don't leave point inside forms, because the normal eww
@@ -1011,7 +1047,7 @@ just re-display the HTML already fetched."
(eww-display-html 'utf-8 url (plist-get eww-data :dom)
(point) (current-buffer)))
(let ((url-mime-accept-string eww-accept-content-types))
- (url-retrieve url 'eww-render
+ (url-retrieve url #'eww-render
(list url (point) (current-buffer) encode))))))
;; Form support.
@@ -1111,11 +1147,13 @@ just re-display the HTML already fetched."
(defun eww-form-submit (dom)
(let ((start (point))
(value (dom-attr dom 'value)))
- (setq value
- (if (zerop (length value))
- "Submit"
- value))
- (insert value)
+ (if (null value)
+ (shr-generic dom)
+ (insert value))
+ ;; If the contents of the <button>...</button> turns out to be
+ ;; empty, or the value was blank, default to this:
+ (when (= (point) start)
+ (insert "Submit"))
(add-face-text-property start (point) 'eww-form-submit)
(put-text-property start (point) 'eww-form
(list :eww-form eww-form
@@ -1256,7 +1294,7 @@ See URL `https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Input'.")
(defun eww-tag-textarea (dom)
(let ((start (point))
- (value (or (dom-attr dom 'value) ""))
+ (value (or (dom-text dom) ""))
(lines (string-to-number (or (dom-attr dom 'rows) "10")))
(width (string-to-number (or (dom-attr dom 'cols) "10")))
end)
@@ -1572,8 +1610,10 @@ If EXTERNAL is double prefix, browse in new buffer."
(cond
((not url)
(message "No link under point"))
- ((string-match "^mailto:" url)
- (browse-url-mail url))
+ ((string-match-p "\\`mailto:" url)
+ ;; This respects the user options `browse-url-handlers'
+ ;; and `browse-url-mailto-function'.
+ (browse-url url))
((and (consp external) (<= (car external) 4))
(funcall browse-url-secondary-browser-function url)
(shr--blink-link))
@@ -1606,20 +1646,23 @@ Differences in #targets are ignored."
"Download URL to `eww-download-directory'.
Use link at point if there is one, else the current page's URL."
(interactive)
- (access-file eww-download-directory "Download failed")
- (let ((url (or (get-text-property (point) 'shr-url)
- (eww-current-url))))
- (if (not url)
- (message "No URL under point")
- (url-retrieve url 'eww-download-callback (list url)))))
-
-(defun eww-download-callback (status url)
+ (let ((dir (if (stringp eww-download-directory)
+ eww-download-directory
+ (funcall eww-download-directory))))
+ (access-file dir "Download failed")
+ (let ((url (or (get-text-property (point) 'shr-url)
+ (eww-current-url))))
+ (if (not url)
+ (message "No URL under point")
+ (url-retrieve url #'eww-download-callback (list url dir))))))
+
+(defun eww-download-callback (status url dir)
(unless (plist-get status :error)
(let* ((obj (url-generic-parse-url url))
(path (directory-file-name (car (url-path-and-query obj))))
(file (eww-make-unique-file-name
(eww-decode-url-file-name (file-name-nondirectory path))
- eww-download-directory)))
+ dir)))
(goto-char (point-min))
(re-search-forward "\r?\n\r?\n")
(let ((coding-system-for-write 'no-conversion))
@@ -1735,28 +1778,30 @@ If CHARSET is nil then use UTF-8."
(defun eww-write-bookmarks ()
(with-temp-file (expand-file-name "eww-bookmarks" eww-bookmarks-directory)
- (insert ";; Auto-generated file; don't edit\n")
+ (insert ";; Auto-generated file; don't edit -*- mode: lisp-data -*-\n")
(pp eww-bookmarks (current-buffer))))
-(defun eww-read-bookmarks ()
+(defun eww-read-bookmarks (&optional error-out)
+ "Read bookmarks from `eww-bookmarks'.
+If ERROR-OUT, signal user-error if there are no bookmarks."
(let ((file (expand-file-name "eww-bookmarks" eww-bookmarks-directory)))
(setq eww-bookmarks
(unless (zerop (or (file-attribute-size (file-attributes file)) 0))
(with-temp-buffer
(insert-file-contents file)
- (read (current-buffer)))))))
+ (read (current-buffer)))))
+ (when (and error-out (not eww-bookmarks))
+ (user-error "No bookmarks are defined"))))
;;;###autoload
(defun eww-list-bookmarks ()
"Display the bookmarks."
(interactive)
+ (eww-read-bookmarks t)
(pop-to-buffer "*eww bookmarks*")
(eww-bookmark-prepare))
(defun eww-bookmark-prepare ()
- (eww-read-bookmarks)
- (unless eww-bookmarks
- (user-error "No bookmarks are defined"))
(set-buffer (get-buffer-create "*eww bookmarks*"))
(eww-bookmark-mode)
(let* ((width (/ (window-width) 2))
@@ -1824,6 +1869,7 @@ If CHARSET is nil then use UTF-8."
bookmark)
(unless (get-buffer "*eww bookmarks*")
(setq first t)
+ (eww-read-bookmarks t)
(eww-bookmark-prepare))
(with-current-buffer (get-buffer "*eww bookmarks*")
(when (and (not first)
@@ -1842,6 +1888,7 @@ If CHARSET is nil then use UTF-8."
bookmark)
(unless (get-buffer "*eww bookmarks*")
(setq first t)
+ (eww-read-bookmarks t)
(eww-bookmark-prepare))
(with-current-buffer (get-buffer "*eww bookmarks*")
(if first
@@ -2124,12 +2171,12 @@ entries (if any) will be removed from the list.
Only the properties listed in `eww-desktop-data-save' are included.
Generally, the list should not include the (usually overly large)
:dom, :source and :text properties."
- (let ((history (mapcar 'eww-desktop-data-1
- (cons eww-data eww-history))))
- (list :history (if eww-desktop-remove-duplicates
- (cl-remove-duplicates
- history :test 'eww-desktop-history-duplicate)
- history))))
+ (let ((history (mapcar #'eww-desktop-data-1
+ (cons eww-data eww-history))))
+ (list :history (if eww-desktop-remove-duplicates
+ (cl-remove-duplicates
+ history :test #'eww-desktop-history-duplicate)
+ history))))
(defun eww-restore-desktop (file-name buffer-name misc-data)
"Restore an eww buffer from its desktop file record.