Hi Mac,
----- Original message -----
Date: Mon, 21 May 2007 21:50:12 -0700
Subject: Re: [cl-who-devel] escaping attributes question
Post by Simon CusackI have read the syntax and semantics chapter and was just wondering why
values in the attribute position aren't escaped by default? Or is there
something I am missing?
I guess it's the same reason why body text ain't escaped by default,
because the lib can't assume too much about that data that you are
feeding.
Values in the attribute position get treated differently to body text
already. They are included in the html stream by default and the
result of lisp forms are emited when used in this position.
It just feels natural to me that it goes the extra step and also
escapes the value so that it is 'safe'.
In my experience 95% of the time the attribute values are constant
html attributes like :width "100%" or :colspan 2 etc that don't
require escaping.
But your use case might be different.
-- Mac
Well I'm in the unfortunate position where we regularly included the
results of calculations as attributes for a javascript library to hook
into. Something like the following is pretty common.
(defun emit-ajaxy-button (url)
(with-html-output (*html-stream*)
((:input :type :button :onclick (format nil
"javascript:doMyAjaxyThing('~A')" url)) "whatever...")))
It gets pretty messy having to remember which ones to escape all the
time.
I patched my convert-attributes (added calls to escape-string code in
all caps)
to do the following;
(defun convert-attributes (attr-list)
"Helper function for CONVERT-TAG-TO-STRING-LIST which converts the
alist ATTR-LIST of attributes into a list of strings and/or Lisp
forms."
(declare (optimize speed space))
(loop with =var= = (gensym)
with attribute-quote = (string *attribute-quote-char*)
for (attr . val) in attr-list
unless (null val) ;; no attribute at all if VAL is NIL
if (constantp val)
if (and (eq *html-mode* :sgml) (eq val t)) ; special case
for SGML
nconc (list " " (string-downcase attr))
else
nconc (list " "
;; name of attribute
(string-downcase attr)
(format nil "=~C" *attribute-quote-char*)
;; value of attribute
(cond ((stringp val)
;; a string, just use it - this case is
;; actually not necessary because of
;; the last case
(ESCAPE-STRING val))
((eq val t)
;; VAL is T, use attribute's name
(string-downcase attr))
(t
;; constant form, PRINC it -
;; EVAL is OK here because of CONSTANTP
(ESCAPE-STRING (FORMAT NIL "~A" (eval
val)))))
attribute-quote)
end
else
;; do the same things as above but at runtime
nconc (list `(let ((,=var= ,val))
(cond ((null ,=var=))
((eq ,=var= t)
,(case *html-mode*
(:sgml
`(htm ,(format nil " ~A"
(string-downcase
attr))))
;; otherwise default to :xml
mode
(t
`(htm ,(format nil " ~A=~C~A~C"
(string-downcase
attr)
*attribute-quote-char*
(string-downcase
attr)
*attribute-quote-char*)))))
(t
(htm ,(format nil " ~A=~C"
(string-downcase attr)
*attribute-quote-char*)
(STR (ESCAPE-STRING (FORMAT NIL
"~A" ,=var=)))
,attribute-quote)))))))
It seems like a sane thing to do to me but was wondering if I had
missed the CL-WHO way of doing the same thing.
Any comments would be most appreciated.
- sim.