changeset 1833:be0c4e2e488a

Allow any FFI module to declare new HTML tags
author Adam Chlipala <adam@chlipala.net>
date Wed, 28 Nov 2012 16:56:45 -0500
parents 373e2c3f03b2
children 690638bd9fef
files demo/more/grid.ur demo/more/grid.urs demo/more/out/grid.css demo/subforms.ur doc/manual.tex src/monoize.sml src/urweb.grm tests/ffitag.ur tests/ffitag.urp tests/tagffi.urs
diffstat 10 files changed, 41 insertions(+), 27 deletions(-) [+]
line wrap: on
line diff
--- a/demo/more/grid.ur	Wed Nov 28 11:45:46 2012 -0500
+++ b/demo/more/grid.ur	Wed Nov 28 16:56:45 2012 -0500
@@ -40,10 +40,10 @@
 
                  val pageLength : option int
              end) = struct
-    style tabl
-    style tr
-    style th
-    style td
+    style tab
+    style row
+    style header
+    style data
     style agg
 
     fun make (row : M.row) [input] [filter] (m : colMeta' M.row input filter) : transaction input = m.Project row
@@ -122,13 +122,13 @@
          M.folder M.cols grid.Cols grid.Filters row
 
     fun render (grid : grid) = <xml>
-      <table class={tabl}>
-        <tr class={tr}>
+      <table class={tab}>
+        <tr class={row}>
           <th/> <th/> <th><button value="No sort" onclick={fn _ => set grid.Sort None}/></th>
           {@mapX2 [fst3] [colMeta M.row] [tr]
             (fn [nm :: Name] [p :: (Type * Type * Type)] [rest :: {(Type * Type * Type)}] [[nm] ~ rest]
                              data (meta : colMeta M.row p) =>
-                <xml><th class={th}>
+                <xml><th class={header}>
                   {case (meta.Handlers data).Sort of
                        None => txt (meta.Handlers data).Header
                      | sort => <xml><button value={(meta.Handlers data).Header}
@@ -185,7 +185,7 @@
                                       cols <- makeAll grid.Cols row';
                                       set colsS cols
                           in
-                              <xml><tr class={tr}>
+                              <xml><tr class={row}>
                                 <td>
                                   <dyn signal={b <- signal grid.Selection;
                                                return (if b then
@@ -213,18 +213,18 @@
                                              return (@mapX3 [fst3] [colMeta M.row] [snd3] [_]
                                                       (fn [nm :: Name] [t :: (Type * Type * Type)]
                                                                        [rest :: {(Type * Type * Type)}]
-                                                                       [[nm] ~ rest] data meta v =>
-                                                          <xml><td class={td}>
+                                                                       [[nm] ~ rest] dat meta v =>
+                                                          <xml><td class={data}>
                                                             <dyn signal={b <- signal ud;
                                                                          return (if b then
-                                                                                     (meta.Handlers data).Edit v
+                                                                                     (meta.Handlers dat).Edit v
                                                                                  else
-                                                                                     (meta.Handlers data).Display
+                                                                                     (meta.Handlers dat).Display
                                                                                                          v)}/>
                                                             <dyn signal={b <- signal ud;
                                                                          if b then
                                                                              valid <-
-                                                                             (meta.Handlers data).Validate v;
+                                                                             (meta.Handlers dat).Validate v;
                                                                              return (if valid then
                                                                                          <xml/>
                                                                                      else
--- a/demo/more/grid.urs	Wed Nov 28 11:45:46 2012 -0500
+++ b/demo/more/grid.urs	Wed Nov 28 16:56:45 2012 -0500
@@ -49,9 +49,9 @@
     val showSelection : grid -> source bool
     val selection : grid -> signal (list M.row)
 
-    style tabl
-    style tr
-    style th
-    style td
+    style tab
+    style row
+    style header
+    style data
     style agg
 end
--- a/demo/more/out/grid.css	Wed Nov 28 11:45:46 2012 -0500
+++ b/demo/more/out/grid.css	Wed Nov 28 16:56:45 2012 -0500
@@ -1,16 +1,16 @@
-.Grid1_tabl {
+.Grid1_tab {
         border-style: solid
 }
 
-.Grid1_th {
+.Grid1_header {
         border-style: solid
 }
 
-.Grid1_tr {
+.Grid1_row {
         border-style: solid
 }
 
-.Grid1_td {
+.Grid1_data {
         border-style: solid
 }
 
--- a/demo/subforms.ur	Wed Nov 28 11:45:46 2012 -0500
+++ b/demo/subforms.ur	Wed Nov 28 16:56:45 2012 -0500
@@ -13,7 +13,7 @@
         </body></xml>
     end
 
-fun subforms n =
+fun subfrms n =
     if n <= 0 then
         <xml/>
     else
@@ -22,13 +22,13 @@
             <hidden{#Num} value={show n}/>
             <li>{[n]}: <textbox{#Text}/></li>
           </entry>
-          {subforms (n - 1)}
+          {subfrms (n - 1)}
         </xml>
 
 fun form n = return <xml><body>
   <form>
     <subforms{#Lines}>
-      {subforms n}
+      {subfrms n}
     </subforms>
     <submit action={sub}/>
   </form>
--- a/doc/manual.tex	Wed Nov 28 11:45:46 2012 -0500
+++ b/doc/manual.tex	Wed Nov 28 16:56:45 2012 -0500
@@ -2482,6 +2482,12 @@
 \item It is possible to use the more standard ``IDs and mutation'' style of JavaScript coding, though that style is unidiomatic for Ur/Web and should be avoided wherever possible.  Recall the abstract type $\mt{id}$ and its constructor $\mt{fresh}$, which can be used to generate new unique IDs in Ur/Web code.  Values of this type are represented as strings in JavaScript, and a function \cd{fresh()} is available to generate new unique IDs.  Application-specific ID generation schemes may cause bad interactions with Ur/Web code that also generates IDs, so the recommended approach is to produce IDs only via calls to \cd{fresh()}.  FFI code shouldn't depend on the ID generation scheme (on either server side or client side), but it is safe to include these IDs in tag attributes (in either server-side or client-side code) and manipulate the associated DOM nodes in the standard way (in client-side code).  Be forewarned that this kind of imperative DOM manipulation may confuse the Ur/Web runtime system and interfere with proper behavior of tags like \cd{<dyn>}!
 \end{itemize}
 
+\subsection{Introducing New HTML Tags}
+
+FFI modules may introduce new tags as values with $\mt{Basis.tag}$ types.  See \texttt{basis.urs} for examples of how tags are declared.  The identifier of a tag value is used as its rendering in HTML.  The Ur/Web syntax sugar for XML literals desugars each use of a tag into a reference to an identifier with the same name.  There is no need to provide implementations (i.e., in C or JavaScript code) for such identifiers.
+
+The onus is on the coder of a new tag's interface to think about consequences for code injection attacks, messing with the DOM in ways that may break Ur/Web reactive programming, etc.
+
 
 \section{Compiler Phases}
 
--- a/src/monoize.sml	Wed Nov 28 11:45:46 2012 -0500
+++ b/src/monoize.sml	Wed Nov 28 16:56:45 2012 -0500
@@ -3203,7 +3203,7 @@
             let
                 fun getTag' (e, _) =
                     case e of
-                        L.EFfi ("Basis", tag) => (tag, [])
+                        L.EFfi (_, tag) => (tag, [])
                       | L.ECApp (e, t) => let
                             val (tag, ts) = getTag' e
                         in
@@ -3215,7 +3215,7 @@
 
                 fun getTag (e, _) =
                     case e of
-                        L.EFfiApp ("Basis", tag, [((L.ERecord [], _), _)]) => (tag, [])
+                        L.EFfiApp (_, tag, [((L.ERecord [], _), _)]) => (tag, [])
                       | L.EApp (e, (L.ERecord [], _)) => getTag' e
                       | _ => (E.errorAt loc "Non-constant XML tag";
                               Print.eprefaces' [("Expression", CorePrint.p_exp env tag)];
--- a/src/urweb.grm	Wed Nov 28 11:45:46 2012 -0500
+++ b/src/urweb.grm	Wed Nov 28 16:56:45 2012 -0500
@@ -1608,7 +1608,7 @@
                                              val pos = s (BEGIN_TAGleft, BEGIN_TAGright)
                                          in
                                              (bt,
-                                              (EVar (["Basis"], bt, Infer), pos))
+                                              (EVar ([], bt, Infer), pos))
                                          end)
        | tagHead LBRACE cexp RBRACE     (#1 tagHead, (ECApp (#2 tagHead, cexp), s (tagHeadleft, RBRACEright)))
                                           
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/ffitag.ur	Wed Nov 28 16:56:45 2012 -0500
@@ -0,0 +1,3 @@
+open Tagffi
+
+fun main () : transaction page = return <xml><body><funky>test</funky></body></xml>
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/ffitag.urp	Wed Nov 28 16:56:45 2012 -0500
@@ -0,0 +1,4 @@
+ffi tagffi
+rewrite all Ffitag/*
+
+ffitag
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/tagffi.urs	Wed Nov 28 16:56:45 2012 -0500
@@ -0,0 +1,1 @@
+val funky : bodyTag boxAttrs