aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJulien Danjou2010-09-21 11:48:40 +0000
committerKatsumi Yamaoka2010-09-21 11:48:40 +0000
commit2c8b2fc8d5fb6fbf765acd324c5d12f3b2b9f4db (patch)
treef512f6fa7487c5b9ba456d8018818854e99cd1ea
parentd19e096ef54a8764186a72b1a0d6cf551ca8cc6e (diff)
downloademacs-2c8b2fc8d5fb6fbf765acd324c5d12f3b2b9f4db.tar.gz
emacs-2c8b2fc8d5fb6fbf765acd324c5d12f3b2b9f4db.zip
gnus-html.el: Make gnus-html use url-cache system.
gnus-html.el (gnus-html-image-map): Add v to browse-url on undisplayed image.
-rw-r--r--lisp/gnus/ChangeLog24
-rw-r--r--lisp/gnus/gnus-html.el341
2 files changed, 191 insertions, 174 deletions
diff --git a/lisp/gnus/ChangeLog b/lisp/gnus/ChangeLog
index b85618ae705..6ca3a0198c6 100644
--- a/lisp/gnus/ChangeLog
+++ b/lisp/gnus/ChangeLog
@@ -1,3 +1,27 @@
12010-09-21 Julien Danjou <julien@danjou.info>
2
3 * gnus-html.el (gnus-html-image-cache-ttl): Add new variable.
4 (gnus-html-cache-expired): Add new function.
5 (gnus-html-wash-images): Use `gnus-html-cache-expired' to check
6 wethever we should display image for fetch it.
7 Compute alt-text earlier to pass it to the fetching function too.
8 (gnus-html-schedule-image-fetching): Change function argument to only
9 get one image at a time, not a list.
10 (gnus-html-image-fetched): Use `url-store-in-cache' to store image in
11 cache.
12 (gnus-html-get-image-data): New function to retrieve image data from
13 cache.
14 (gnus-html-put-image): Change buffer argument to use image data rather
15 than file, and place image above region rather than inserting a new
16 one. Do not take alt-text as argument, since it's useless now: we place
17 the image above alt-text.
18 (gnus-html-prune-cache): Remove.
19 (gnus-html-show-images): Start to fetch image when we find one, do not
20 push into a temporary list.
21 (gnus-html-prefetch-images): Only fetch image if they have expired.
22 (gnus-html-browse-image): Fix, use 'gnus-image-url.
23 (gnus-html-image-map): Add "v" to browse-url on undisplayed image.
24
12010-09-20 Katsumi Yamaoka <yamaoka@jpl.org> 252010-09-20 Katsumi Yamaoka <yamaoka@jpl.org>
2 26
3 * rfc2047.el (rfc2047-encode-parameter): Doc fix. 27 * rfc2047.el (rfc2047-encode-parameter): Doc fix.
diff --git a/lisp/gnus/gnus-html.el b/lisp/gnus/gnus-html.el
index 78bb7ca18b5..34dbb4dd878 100644
--- a/lisp/gnus/gnus-html.el
+++ b/lisp/gnus/gnus-html.el
@@ -34,15 +34,10 @@
34(require 'gnus-art) 34(require 'gnus-art)
35(require 'mm-url) 35(require 'mm-url)
36(require 'url) 36(require 'url)
37(require 'url-cache)
37 38
38(defcustom gnus-html-cache-directory (nnheader-concat gnus-directory "html-cache/") 39(defcustom gnus-html-image-cache-ttl (days-to-time 7)
39 "Where Gnus will cache images it downloads from the web." 40 "Time in seconds used to cache the image on disk."
40 :version "24.1"
41 :group 'gnus-art
42 :type 'directory)
43
44(defcustom gnus-html-cache-size 500000000
45 "The size of the Gnus image cache."
46 :version "24.1" 41 :version "24.1"
47 :group 'gnus-art 42 :group 'gnus-art
48 :type 'integer) 43 :type 'integer)
@@ -73,6 +68,7 @@ fit these criteria."
73 (let ((map (make-sparse-keymap))) 68 (let ((map (make-sparse-keymap)))
74 (define-key map "u" 'gnus-article-copy-string) 69 (define-key map "u" 'gnus-article-copy-string)
75 (define-key map "i" 'gnus-html-insert-image) 70 (define-key map "i" 'gnus-html-insert-image)
71 (define-key map "v" 'gnus-html-browse-url)
76 map)) 72 map))
77 73
78(defvar gnus-html-displayed-image-map 74(defvar gnus-html-displayed-image-map
@@ -84,6 +80,19 @@ fit these criteria."
84 (define-key map [tab] 'widget-forward) 80 (define-key map [tab] 'widget-forward)
85 map)) 81 map))
86 82
83(defun gnus-html-cache-expired (url ttl)
84 "Check if URL is cached for more than TTL."
85 (cond (url-standalone-mode
86 (not (file-exists-p (url-cache-create-filename url))))
87 (t (let ((cache-time (url-is-cached url)))
88 (if cache-time
89 (time-less-p
90 (time-add
91 cache-time
92 ttl)
93 (current-time))
94 t)))))
95
87;;;###autoload 96;;;###autoload
88(defun gnus-article-html (&optional handle) 97(defun gnus-article-html (&optional handle)
89 (let ((article-buffer (current-buffer))) 98 (let ((article-buffer (current-buffer)))
@@ -133,6 +142,7 @@ fit these criteria."
133 (replace-match "" t t))) 142 (replace-match "" t t)))
134 143
135(defun gnus-html-wash-images () 144(defun gnus-html-wash-images ()
145 "Run through current buffer and replace img tags by images."
136 (let (tag parameters string start end images url) 146 (let (tag parameters string start end images url)
137 (goto-char (point-min)) 147 (goto-char (point-min))
138 ;; Search for all the images first. 148 ;; Search for all the images first.
@@ -158,62 +168,68 @@ fit these criteria."
158 (setq image (gnus-create-image (buffer-string) 168 (setq image (gnus-create-image (buffer-string)
159 nil t)))) 169 nil t))))
160 (when image 170 (when image
161 (let ((string (buffer-substring start end))) 171 (let ((string (buffer-substring start end)))
162 (delete-region start end) 172 (delete-region start end)
163 (gnus-put-image image (gnus-string-or string "*") 'cid) 173 (gnus-put-image image (gnus-string-or string "*") 'cid)
164 (gnus-add-image 'cid image)))) 174 (gnus-add-image 'cid image))))
165 ;; Normal, external URL. 175 ;; Normal, external URL.
166 (if (gnus-html-image-url-blocked-p 176 (let ((alt-text (when (string-match "\\(alt\\|title\\)=\"\\([^\"]+\\)"
167 url 177 parameters)
168 (if (buffer-live-p gnus-summary-buffer) 178 (match-string 2 parameters))))
169 (with-current-buffer gnus-summary-buffer 179 (if (gnus-html-image-url-blocked-p
170 gnus-blocked-images) 180 url
171 gnus-blocked-images)) 181 (if (buffer-live-p gnus-summary-buffer)
172 (progn 182 (with-current-buffer gnus-summary-buffer
173 (widget-convert-button 183 gnus-blocked-images)
174 'link start end 184 gnus-blocked-images))
175 :action 'gnus-html-insert-image 185 (progn
176 :help-echo url 186 (widget-convert-button
177 :keymap gnus-html-image-map 187 'link start end
178 :button-keymap gnus-html-image-map) 188 :action 'gnus-html-insert-image
179 (let ((overlay (gnus-make-overlay start end)) 189 :help-echo url
180 (spec (list url 190 :keymap gnus-html-image-map
181 (set-marker (make-marker) start) 191 :button-keymap gnus-html-image-map)
182 (set-marker (make-marker) end)))) 192 (let ((overlay (gnus-make-overlay start end))
183 (gnus-overlay-put overlay 'local-map gnus-html-image-map) 193 (spec (list url
184 (gnus-overlay-put overlay 'gnus-image spec) 194 (set-marker (make-marker) start)
185 (gnus-put-text-property 195 (set-marker (make-marker) end)
186 start end 196 alt-text)))
187 'gnus-image spec))) 197 (gnus-overlay-put overlay 'local-map gnus-html-image-map)
188 (let ((file (gnus-html-image-id url)) 198 (gnus-overlay-put overlay 'gnus-image spec)
189 width height alt-text) 199 (gnus-put-text-property start end 'gnus-image-url url)
190 (when (string-match "height=\"?\\([0-9]+\\)" parameters) 200 (gnus-put-text-property
191 (setq height (string-to-number (match-string 1 parameters)))) 201 start end
192 (when (string-match "width=\"?\\([0-9]+\\)" parameters) 202 'gnus-image spec)))
193 (setq width (string-to-number (match-string 1 parameters)))) 203 ;; Non-blocked url
194 (when (string-match "\\(alt\\|title\\)=\"\\([^\"]+\\)" 204 (let ((width
195 parameters) 205 (when (string-match "width=\"?\\([0-9]+\\)" parameters)
196 (setq alt-text (match-string 2 parameters))) 206 (string-to-number (match-string 1 parameters))))
197 ;; Don't fetch images that are really small. They're 207 (height
198 ;; probably tracking pictures. 208 (when (string-match "height=\"?\\([0-9]+\\)" parameters)
199 (when (and (or (null height) 209 (string-to-number (match-string 1 parameters)))))
200 (> height 4)) 210 ;; Don't fetch images that are really small. They're
201 (or (null width) 211 ;; probably tracking pictures.
202 (> width 4))) 212 (when (and (or (null height)
203 (if (file-exists-p file) 213 (> height 4))
204 ;; It's already cached, so just insert it. 214 (or (null width)
205 (let ((string (buffer-substring start end))) 215 (> width 4)))
206 ;; Delete the IMG text. 216 (gnus-html-display-image url start end alt-text))))))))))
207 (delete-region start end) 217
208 (gnus-html-put-image file (point) string url alt-text)) 218(defun gnus-html-display-image (url start end alt-text)
209 ;; We don't have it, so schedule it for fetching 219 "Display image at URL on text from START to END.
210 ;; asynchronously. 220Use ALT-TEXT for the image string."
211 (push (list url 221 (if (gnus-html-cache-expired url gnus-html-image-cache-ttl)
212 (set-marker (make-marker) start) 222 ;; We don't have it, so schedule it for fetching
213 (point-marker)) 223 ;; asynchronously.
214 images)))))))) 224 (gnus-html-schedule-image-fetching
215 (when images 225 (current-buffer)
216 (gnus-html-schedule-image-fetching (current-buffer) (nreverse images))))) 226 (list url
227 (set-marker (make-marker) start)
228 (set-marker (make-marker) end)
229 alt-text))
230 ;; It's already cached, so just insert it.
231 (gnus-html-put-image (gnus-html-get-image-data url)
232 start end url alt-text)))
217 233
218(defun gnus-html-wash-tags () 234(defun gnus-html-wash-tags ()
219 (let (tag parameters string start end images url) 235 (let (tag parameters string start end images url)
@@ -300,8 +316,7 @@ fit these criteria."
300(defun gnus-html-insert-image () 316(defun gnus-html-insert-image ()
301 "Fetch and insert the image under point." 317 "Fetch and insert the image under point."
302 (interactive) 318 (interactive)
303 (gnus-html-schedule-image-fetching 319 (apply 'gnus-html-display-image (get-text-property (point) 'gnus-image)))
304 (current-buffer) (list (get-text-property (point) 'gnus-image))))
305 320
306(defun gnus-html-show-alt-text () 321(defun gnus-html-show-alt-text ()
307 "Show the ALT text of the image under point." 322 "Show the ALT text of the image under point."
@@ -311,7 +326,7 @@ fit these criteria."
311(defun gnus-html-browse-image () 326(defun gnus-html-browse-image ()
312 "Browse the image under point." 327 "Browse the image under point."
313 (interactive) 328 (interactive)
314 (browse-url (get-text-property (point) 'gnus-image))) 329 (browse-url (get-text-property (point) 'gnus-image-url)))
315 330
316(defun gnus-html-browse-url () 331(defun gnus-html-browse-url ()
317 "Browse the image under point." 332 "Browse the image under point."
@@ -321,87 +336,89 @@ fit these criteria."
321 (message "No URL at point") 336 (message "No URL at point")
322 (browse-url url)))) 337 (browse-url url))))
323 338
324(defun gnus-html-schedule-image-fetching (buffer images) 339(defun gnus-html-schedule-image-fetching (buffer image)
325 (gnus-message 8 "gnus-html-schedule-image-fetching: buffer %s, images %s" 340 "Retrieve IMAGE, and place it into BUFFER on arrival."
326 buffer images) 341 (gnus-message 8 "gnus-html-schedule-image-fetching: buffer %s, image %s"
327 (dolist (image images) 342 buffer image)
328 (ignore-errors 343 (ignore-errors
329 (url-retrieve (car image) 344 (url-retrieve (car image)
330 'gnus-html-image-fetched 345 'gnus-html-image-fetched
331 (list buffer image))))) 346 (list buffer image))))
332
333(defun gnus-html-image-id (url)
334 (expand-file-name (sha1 url) gnus-html-cache-directory))
335 347
336(defun gnus-html-image-fetched (status buffer image) 348(defun gnus-html-image-fetched (status buffer image)
337 (let ((file (gnus-html-image-id (car image)))) 349 (url-store-in-cache (current-buffer))
338 ;; Search the start of the image data 350 (when (and (search-forward "\n\n" nil t)
351 (buffer-live-p buffer)
352 ;; If the `image' has no marker, do not replace anything
353 (cadr image)
354 ;; If the position of the marker is 1, then that
355 ;; means that the text it was in has been deleted;
356 ;; i.e., that the user has selected a different
357 ;; article before the image arrived.
358 (not (= (marker-position (cadr image))
359 (with-current-buffer buffer
360 (point-min)))))
361 (let ((data (buffer-substring (point) (point-max))))
362 (with-current-buffer buffer
363 (let ((inhibit-read-only t))
364 (gnus-html-put-image data (cadr image) (caddr image) (car image) (cadddr image))))))
365 (kill-buffer (current-buffer)))
366
367(defun gnus-html-get-image-data (url)
368 "Get image data for URL.
369Return a string with image data."
370 (with-temp-buffer
371 (mm-disable-multibyte)
372 (url-cache-extract (url-cache-create-filename url))
339 (when (search-forward "\n\n" nil t) 373 (when (search-forward "\n\n" nil t)
340 ;; Write region (image data) silently 374 (buffer-substring (point) (point-max)))))
341 (write-region (point) (point-max) file nil 1) 375
342 (kill-buffer (current-buffer)) 376(defun gnus-html-put-image (data start end &optional url alt-text)
343 (when (and (buffer-live-p buffer)
344 ;; If the `image' has no marker, do not replace anything
345 (cadr image)
346 ;; If the position of the marker is 1, then that
347 ;; means that the text it was in has been deleted;
348 ;; i.e., that the user has selected a different
349 ;; article before the image arrived.
350 (not (= (marker-position (cadr image)) (point-min))))
351 (with-current-buffer buffer
352 (let ((inhibit-read-only t)
353 (string (buffer-substring (cadr image) (caddr image))))
354 (delete-region (cadr image) (caddr image))
355 (gnus-html-put-image file (cadr image) (car image) string)))))))
356
357(defun gnus-html-put-image (file point string &optional url alt-text)
358 (when (gnus-graphic-display-p) 377 (when (gnus-graphic-display-p)
359 (let* ((image (ignore-errors 378 (let* ((image (ignore-errors
360 (gnus-create-image file))) 379 (gnus-create-image data nil t)))
361 (size (and image 380 (size (and image
362 (if (featurep 'xemacs) 381 (if (featurep 'xemacs)
363 (cons (glyph-width image) (glyph-height image)) 382 (cons (glyph-width image) (glyph-height image))
364 (image-size image t))))) 383 (image-size image t)))))
365 (save-excursion 384 (save-excursion
366 (goto-char point) 385 (goto-char start)
367 (if (and image 386 (let ((alt-text (or alt-text (buffer-substring-no-properties start end))))
368 ;; Kludge to avoid displaying 30x30 gif images, which 387 (if (and image
369 ;; seems to be a signal of a broken image. 388 ;; Kludge to avoid displaying 30x30 gif images, which
370 (not (and (if (featurep 'xemacs) 389 ;; seems to be a signal of a broken image.
371 (glyphp image) 390 (not (and (if (featurep 'xemacs)
372 (listp image)) 391 (glyphp image)
373 (eq (if (featurep 'xemacs) 392 (listp image))
374 (let ((data (cdadar (specifier-spec-list 393 (eq (if (featurep 'xemacs)
375 (glyph-image image))))) 394 (let ((d (cdadar (specifier-spec-list
376 (and (vectorp data) 395 (glyph-image image)))))
377 (aref data 0))) 396 (and (vectorp d)
378 (plist-get (cdr image) :type)) 397 (aref d 0)))
379 'gif) 398 (plist-get (cdr image) :type))
380 (= (car size) 30) 399 'gif)
381 (= (cdr size) 30)))) 400 (= (car size) 30)
382 (let ((start (point))) 401 (= (cdr size) 30))))
383 (setq image (gnus-html-rescale-image image file size)) 402 ;; Good image, add it!
384 (gnus-put-image image 403 (let ((image (gnus-html-rescale-image image data size)))
385 (gnus-string-or string "*") 404 (delete-region start end)
386 'external) 405 (gnus-put-image image alt-text 'external)
387 (let ((overlay (gnus-make-overlay start (point)))) 406 (gnus-overlay-put (gnus-make-overlay start (point)) 'local-map
388 (gnus-overlay-put overlay 'local-map 407 gnus-html-displayed-image-map)
389 gnus-html-displayed-image-map) 408 (gnus-put-text-property start (point) 'gnus-alt-text alt-text)
390 (gnus-put-text-property start (point) 'gnus-alt-text alt-text) 409 (when url
391 (when url 410 (gnus-put-text-property start (point) 'gnus-image-url url))
392 (gnus-put-text-property start (point) 'gnus-image url))) 411 (gnus-add-image 'external image)
393 (gnus-add-image 'external image) 412 t)
394 t) 413 ;; Bad image, try to show something else
395 (insert string) 414 (delete-region start end)
396 (when (fboundp 'find-image) 415 (when (fboundp 'find-image)
397 (setq image (find-image '((:type xpm :file "lock-broken.xpm")))) 416 (setq image (find-image '((:type xpm :file "lock-broken.xpm"))))
398 (gnus-put-image image 417 (gnus-put-image image alt-text 'internal)
399 (gnus-string-or string "*") 418 (gnus-add-image 'internal image))
400 'internal) 419 nil))))))
401 (gnus-add-image 'internal image)) 420
402 nil))))) 421(defun gnus-html-rescale-image (image data size)
403
404(defun gnus-html-rescale-image (image file size)
405 (if (or (not (fboundp 'imagemagick-types)) 422 (if (or (not (fboundp 'imagemagick-types))
406 (not (get-buffer-window (current-buffer)))) 423 (not (get-buffer-window (current-buffer))))
407 image 424 image
@@ -414,35 +431,17 @@ fit these criteria."
414 (- (nth 3 edges) (nth 1 edges))))) 431 (- (nth 3 edges) (nth 1 edges)))))
415 scaled-image) 432 scaled-image)
416 (when (> height window-height) 433 (when (> height window-height)
417 (setq image (or (create-image file 'imagemagick nil 434 (setq image (or (create-image data 'imagemagick t
418 :height window-height) 435 :height window-height)
419 image)) 436 image))
420 (setq size (image-size image t))) 437 (setq size (image-size image t)))
421 (when (> (car size) window-width) 438 (when (> (car size) window-width)
422 (setq image (or 439 (setq image (or
423 (create-image file 'imagemagick nil 440 (create-image data 'imagemagick t
424 :width window-width) 441 :width window-width)
425 image))) 442 image)))
426 image))) 443 image)))
427 444
428(defun gnus-html-prune-cache ()
429 (let ((total-size 0)
430 files)
431 (dolist (file (directory-files gnus-html-cache-directory t nil t))
432 (let ((attributes (file-attributes file)))
433 (unless (nth 0 attributes)
434 (incf total-size (nth 7 attributes))
435 (push (list (time-to-seconds (nth 5 attributes))
436 (nth 7 attributes) file)
437 files))))
438 (when (> total-size gnus-html-cache-size)
439 (setq files (sort files (lambda (f1 f2)
440 (< (car f1) (car f2)))))
441 (dolist (file files)
442 (when (> total-size gnus-html-cache-size)
443 (decf total-size (cadr file))
444 (delete-file (nth 2 file)))))))
445
446(defun gnus-html-image-url-blocked-p (url blocked-images) 445(defun gnus-html-image-url-blocked-p (url blocked-images)
447 "Find out if URL is blocked by BLOCKED-IMAGES." 446 "Find out if URL is blocked by BLOCKED-IMAGES."
448 (let ((ret (and blocked-images 447 (let ((ret (and blocked-images
@@ -459,14 +458,10 @@ fit these criteria."
459This only works if the article in question is HTML." 458This only works if the article in question is HTML."
460 (interactive) 459 (interactive)
461 (gnus-with-article-buffer 460 (gnus-with-article-buffer
462 (let ((overlays (overlays-in (point-min) (point-max))) 461 (dolist (overlay (overlays-in (point-min) (point-max)))
463 overlay images) 462 (let ((o (overlay-get overlay 'gnus-image)))
464 (while (setq overlay (pop overlays)) 463 (when o
465 (when (overlay-get overlay 'gnus-image) 464 (apply 'gnus-html-display-image o))))))
466 (push (overlay-get overlay 'gnus-image) images)))
467 (if (not images)
468 (message "No images to show")
469 (gnus-html-schedule-image-fetching (current-buffer) images)))))
470 465
471;;;###autoload 466;;;###autoload
472(defun gnus-html-prefetch-images (summary) 467(defun gnus-html-prefetch-images (summary)
@@ -477,11 +472,9 @@ This only works if the article in question is HTML."
477 (while (re-search-forward "<img.*src=[\"']\\([^\"']+\\)" nil t) 472 (while (re-search-forward "<img.*src=[\"']\\([^\"']+\\)" nil t)
478 (let ((url (match-string 1))) 473 (let ((url (match-string 1)))
479 (unless (gnus-html-image-url-blocked-p url blocked-images) 474 (unless (gnus-html-image-url-blocked-p url blocked-images)
480 (unless (file-exists-p (gnus-html-image-id url)) 475 (when (gnus-html-cache-expired url gnus-html-image-cache-ttl)
481 (ignore-errors 476 (gnus-html-schedule-image-fetching nil
482 (url-retrieve (mm-url-decode-entities-string url) 477 (list url))))))))))
483 'gnus-html-image-fetched
484 (list nil (list url))))))))))))
485 478
486(provide 'gnus-html) 479(provide 'gnus-html)
487 480