1 ;;; eww.el --- Emacs Web Wowser
3 ;; Copyright (C) 2013 Free Software Foundation, Inc.
5 ;; Author: Lars Magne Ingebrigtsen <larsi@gnus.org>
8 ;; This file is part of GNU Emacs.
10 ;; GNU Emacs is free software: you can redistribute it and/or modify
11 ;; it under the terms of the GNU General Public License as published by
12 ;; the Free Software Foundation, either version 3 of the License, or
13 ;; (at your option) any later version.
15 ;; GNU Emacs is distributed in the hope that it will be useful,
16 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
17 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 ;; GNU General Public License for more details.
20 ;; You should have received a copy of the GNU General Public License
21 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
27 (eval-when-compile (require 'cl))
32 (defvar eww-current-url nil)
33 (defvar eww-history nil)
36 "Fetch URL and render the page."
37 (interactive "sUrl: ")
38 (url-retrieve url 'eww-render (list url)))
40 (defun eww-render (status url &optional point)
41 (let* ((headers (eww-parse-headers))
43 (mail-header-parse-content-type
44 (or (cdr (assoc "content-type" headers))
48 (or (cdr (assq 'charset (cdr content-type)))
50 (data-buffer (current-buffer)))
54 ((equal (car content-type) "text/html")
55 (eww-display-html charset url))
56 ((string-match "^image/" (car content-type))
59 (eww-display-raw charset)))
62 (kill-buffer data-buffer))))
64 (defun eww-parse-headers ()
66 (while (and (not (eobp))
68 (when (looking-at "\\([^:]+\\): *\\(.*\\)")
69 (push (cons (downcase (match-string 1))
77 (defun eww-display-html (charset url)
78 (unless (eq charset 'utf8)
79 (decode-coding-region (point) (point-max) charset))
82 'base (list (cons 'href url))
83 (libxml-parse-html-region (point) (point-max)))))
85 (setq eww-current-url url)
86 (let ((inhibit-read-only t)
87 (shr-external-rendering-functions
88 '((form . eww-tag-form)
89 (input . eww-tag-input)
90 (submit . eww-tag-submit))))
91 (shr-insert-document document)
92 (eww-convert-widgets))
93 (goto-char (point-min))))
95 (defun eww-display-raw (charset)
96 (let ((data (buffer-substring (point) (point-max))))
98 (let ((inhibit-read-only t))
100 (goto-char (point-min))))
102 (defun eww-display-image ()
103 (let ((data (buffer-substring (point) (point-max))))
105 (let ((inhibit-read-only t))
106 (shr-put-image data nil))
107 (goto-char (point-min))))
109 (defun eww-setup-buffer ()
110 (pop-to-buffer (get-buffer-create "*eww*"))
112 (setq widget-field-list nil)
113 (let ((inhibit-read-only t))
118 (let ((map (make-sparse-keymap)))
119 (suppress-keymap map)
120 (define-key map "q" 'eww-quit)
121 (define-key map [tab] 'widget-forward)
122 (define-key map [backtab] 'widget-backward)
123 (define-key map [delete] 'scroll-down-command)
124 (define-key map "\177" 'scroll-down-command)
125 (define-key map " " 'scroll-up-command)
126 (define-key map "p" 'eww-previous-url)
127 ;;(define-key map "n" 'eww-next-url)
131 "Mode for browsing the web.
135 (setq major-mode 'eww-mode
137 (set (make-local-variable 'eww-current-url) 'author)
138 (set (make-local-variable 'browse-url-browser-function) 'eww-browse-url)
139 ;;(setq buffer-read-only t)
140 (use-local-map eww-mode-map))
142 (defun eww-browse-url (url &optional new-window)
143 (push (list eww-current-url (point))
148 "Exit the Emacs Web Wowser."
150 (setq eww-history nil)
151 (kill-buffer (current-buffer)))
153 (defun eww-previous-url ()
154 "Go to the previously displayed page."
156 (when (zerop (length eww-history))
157 (error "No previous page"))
158 (let ((prev (pop eww-history)))
159 (url-retrieve (car prev) 'eww-render (list (car prev) (cadr prev)))))
163 (defvar eww-form nil)
165 (defun eww-tag-form (cont)
167 (list (assq :method cont)
168 (assq :action cont)))
170 (shr-ensure-paragraph)
172 (shr-ensure-paragraph)
173 (put-text-property start (1+ start)
174 'eww-form eww-form)))
176 (defun eww-tag-input (cont)
177 (let ((start (point))
180 :size (string-to-number
181 (or (cdr (assq :size cont))
183 :value (or (cdr (assq :value cont)) "")
185 :name (cdr (assq :name cont))
186 :eww-form eww-form)))
187 (apply 'widget-create widget)
189 (put-text-property start (point) 'eww-widget widget)))
191 (defun eww-submit (widget dummy)
192 (let ((form (getf (cdr widget) :eww-form))
194 (dolist (overlay (overlays-in (point-min) (point-max)))
195 (let ((field (getf (overlay-properties overlay) 'field)))
196 (when (eq (getf (cdr field) :eww-form) form)
197 (let ((name (getf (cdr field) :name)))
199 (push (cons name (widget-value field))
201 (let ((shr-base eww-current-url))
207 (mm-url-encode-www-form-urlencoded values)))))))
209 (defun eww-convert-widgets ()
210 (let ((start (point-min))
212 (while (setq start (next-single-property-change start 'eww-widget))
213 (setq widget (get-text-property start 'eww-widget))
215 (delete-region start (next-single-property-change start 'eww-widget))
216 (apply 'widget-create widget)
217 (put-text-property start (point) 'not-read-only t))