changeset 1042:a8a825861397

Explicitly abort in-flight RPCs onunload
author Adam Chlipala <adamc@hcoop.net>
date Tue, 24 Nov 2009 09:24:25 -0500
parents 0d767c8d2923
children d73cf02427df
files include/urweb.h lib/js/urweb.js src/c/urweb.c src/monoize.sml
diffstat 4 files changed, 62 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- a/include/urweb.h	Sun Nov 22 17:57:15 2009 -0500
+++ b/include/urweb.h	Tue Nov 24 09:24:25 2009 -0500
@@ -69,6 +69,7 @@
 const char *uw_Basis_get_script(uw_context, uw_unit);
 
 uw_Basis_string uw_Basis_maybe_onload(uw_context, uw_Basis_string);
+uw_Basis_string uw_Basis_maybe_onunload(uw_context, uw_Basis_string);
 
 void uw_set_needs_push(uw_context, int);
 void uw_set_needs_sig(uw_context, int);
--- a/lib/js/urweb.js	Sun Nov 22 17:57:15 2009 -0500
+++ b/lib/js/urweb.js	Tue Nov 24 09:24:25 2009 -0500
@@ -627,7 +627,20 @@
 
 var sig = null;
 
+var unloading = false, inFlight = null;
+
+function unload() {
+  unloading = true;
+
+  for (; inFlight; inFlight = inFlight.next) {
+    inFlight.data.abort();
+  }
+}
+
 function requestUri(xhr, uri, needsSig) {
+  if (unloading)
+    return;
+
   xhr.open("POST", uri, true);
 
   if (client_id != null) {
@@ -642,9 +655,15 @@
     xhr.setRequestHeader("UrWeb-Sig", sig);
   }
 
+  inFlight = cons(xhr, inFlight);
   xhr.send(null);
 }
 
+function xhrFinished(xhr) {
+  xhr.abort();
+  inFlight = remove(xhr, inFlight);
+}
+
 function rc(prefix, uri, parse, k, needsSig) {
   uri = cat(prefix, uri);
   uri = flattenLocal(uri);
@@ -668,6 +687,8 @@
       } else {
         conn();
       }
+
+      xhrFinished(xhr);
     }
   };
 
@@ -772,7 +793,7 @@
           }
         }
 
-        xhr.abort();
+        xhrFinished(xhr);
 
         connect();
       }
@@ -786,7 +807,7 @@
   };
 
   onTimeout = function() {
-    xhr.abort();
+    xhrFinished(xhr);
     connect();
   };
 
--- a/src/c/urweb.c	Sun Nov 22 17:57:15 2009 -0500
+++ b/src/c/urweb.c	Tue Nov 24 09:24:25 2009 -0500
@@ -1173,6 +1173,16 @@
   }
 }
 
+uw_Basis_string uw_Basis_maybe_onunload(uw_context ctx, uw_Basis_string s) {
+  if (ctx->script_header[0] == 0)
+    return "";
+  else {
+    char *r = uw_malloc(ctx, 22 + strlen(s));
+    sprintf(r, " onunload='unload();%s'", s);
+    return r;
+  }
+}
+
 extern uw_Basis_string uw_cookie_sig(uw_context);
 
 const char *uw_Basis_get_settings(uw_context ctx, uw_unit u) {
--- a/src/monoize.sml	Sun Nov 22 17:57:15 2009 -0500
+++ b/src/monoize.sml	Tue Nov 24 09:24:25 2009 -0500
@@ -2483,13 +2483,14 @@
                     else
                         attrs
 
-                fun findOnload (attrs, acc) =
+                fun findOnload (attrs, onload, onunload, acc) =
                     case attrs of
-                        [] => (NONE, acc)
-                      | ("Onload", e, _) :: rest => (SOME e, List.revAppend (acc, rest))
-                      | x :: rest => findOnload (rest, x :: acc)
+                        [] => (onload, onunload, acc)
+                      | ("Onload", e, _) :: rest => findOnload (rest, SOME e, onunload, acc)
+                      | ("Onunload", e, _) :: rest => findOnload (rest, onload, SOME e, acc)
+                      | x :: rest => findOnload (rest, onload, onunload, x :: acc)
                                      
-                val (onload, attrs) = findOnload (attrs, [])
+                val (onload, onunload, attrs) = findOnload (attrs, NONE, NONE, [])
 
                 val (class, fm) = monoExp (env, st, fm) class
 
@@ -2669,26 +2670,33 @@
                                          :: str ";"
                                          :: assgns)
                     end
+
+                fun execify e =
+                    case e of
+                        NONE => (L'.EPrim (Prim.String ""), loc)
+                      | SOME e =>
+                        let
+                            val e = (L'.EApp (e, (L'.ERecord [], loc)), loc)
+                        in
+                            (L'.EStrcat ((L'.EPrim (Prim.String "exec("), loc),
+                                         (L'.EStrcat ((L'.EJavaScript (L'.Attribute, e), loc),
+                                                      (L'.EPrim (Prim.String ")"), loc)), loc)), loc)
+                        end
             in
                 case tag of
                     "body" => let
-                        val onload = case onload of
-                                         NONE => (L'.EPrim (Prim.String ""), loc)
-                                       | SOME e =>
-                                         let
-                                             val e = (L'.EApp (e, (L'.ERecord [], loc)), loc)
-                                         in
-                                             (L'.EStrcat ((L'.EPrim (Prim.String "exec("), loc),
-                                                          (L'.EStrcat ((L'.EJavaScript (L'.Attribute, e), loc),
-                                                                       (L'.EPrim (Prim.String ")"), loc)), loc)), loc)
-                                         end
+                        val onload = execify onload
+                        val onunload = execify onunload
                     in
                         normal ("body",
-                                SOME (L'.EFfiApp ("Basis", "maybe_onload",
-                                                  [(L'.EStrcat ((L'.EFfiApp ("Basis", "get_settings",
-                                                                             [(L'.ERecord [], loc)]), loc),
-                                                                onload), loc)]),
-                                      loc),
+                                SOME (L'.EStrcat ((L'.EFfiApp ("Basis", "maybe_onload",
+                                                               [(L'.EStrcat ((L'.EFfiApp ("Basis", "get_settings",
+                                                                                          [(L'.ERecord [], loc)]), loc),
+                                                                             onload), loc)]),
+                                                   loc),
+                                                  (L'.EFfiApp ("Basis", "maybe_onunload",
+                                                               [onunload]),
+                                                   loc)), loc),
                                 SOME (L'.EFfiApp ("Basis", "get_script", [(L'.ERecord [], loc)]), loc))
                     end