Fossil SCM
Add a workaround in the forum post Web Lock for Chrome taking half a minute or more to release the lock when a tab is manually closed, with thanks to an LLM for consulting on this.
Commit
b2d347eaa05f5fc19e6c87ee2a0945bbad70969cd1e733110bb8c3a7a612e77d
Parent
2ad508a11c63127…
1 file changed
+22
-2
+22
-2
| --- src/fossil.page.forumpost.js | ||
| +++ src/fossil.page.forumpost.js | ||
| @@ -1202,27 +1202,47 @@ | ||
| 1202 | 1202 | value here). */ |
| 1203 | 1203 | || fpid |
| 1204 | 1204 | ); |
| 1205 | 1205 | let releaseLock; |
| 1206 | 1206 | if( window.navigator.locks ){ |
| 1207 | + /* This business with forceReleaseLockOnClose and 'pagehide' | |
| 1208 | + event is a workaround for Chrome being overly lazy in | |
| 1209 | + releasing Web Locks when a tab is closed. It sometimes | |
| 1210 | + waits half a minute or more before release. | |
| 1211 | + | |
| 1212 | + Sidebar: we cannot use AbortController because it cannot | |
| 1213 | + be used with the ifAvailable lock check. */ | |
| 1214 | + let forceReleaseLockOnClose = null; | |
| 1215 | + const handlePageHide = ()=>{ | |
| 1216 | + if( forceReleaseLockOnClose ){ | |
| 1217 | + forceReleaseLockOnClose(); | |
| 1218 | + } | |
| 1219 | + }; | |
| 1220 | + window.addEventListener('pagehide', handlePageHide); | |
| 1207 | 1221 | releaseLock = await new Promise((resolve)=>{ |
| 1208 | 1222 | window.navigator.locks.request( |
| 1209 | 1223 | 'fossil-'+draftKey, |
| 1210 | 1224 | {ifAvailable: true}, |
| 1211 | - async (lock) => { | |
| 1225 | + async (lock)=>{ | |
| 1212 | 1226 | if( !lock ){ |
| 1213 | 1227 | /*lock contention*/ |
| 1214 | 1228 | resolve(null); |
| 1215 | 1229 | return; |
| 1216 | 1230 | } |
| 1217 | 1231 | let release; |
| 1218 | 1232 | const lockReleased = new Promise(res=>release=res); |
| 1219 | - resolve(release); | |
| 1233 | + forceReleaseLockOnClose = release; | |
| 1234 | + const wrappedRelease = ()=>{ | |
| 1235 | + window.removeEventListener('pagehide', handlePageHide); | |
| 1236 | + if( release ) release(); | |
| 1237 | + }; | |
| 1238 | + resolve(wrappedRelease); | |
| 1220 | 1239 | await lockReleased/*hold the lock open*/; |
| 1221 | 1240 | }); |
| 1222 | 1241 | }); |
| 1223 | 1242 | if( !releaseLock ){ |
| 1243 | + window.removeEventListener('pagehide', handlePageHide); | |
| 1224 | 1244 | reportFPEError( |
| 1225 | 1245 | ePost, |
| 1226 | 1246 | "This post is actively being replied to ", |
| 1227 | 1247 | "in another tab. To avoid losing edits, ", |
| 1228 | 1248 | "it cannot be opened here until the locking ", |
| 1229 | 1249 |
| --- src/fossil.page.forumpost.js | |
| +++ src/fossil.page.forumpost.js | |
| @@ -1202,27 +1202,47 @@ | |
| 1202 | value here). */ |
| 1203 | || fpid |
| 1204 | ); |
| 1205 | let releaseLock; |
| 1206 | if( window.navigator.locks ){ |
| 1207 | releaseLock = await new Promise((resolve)=>{ |
| 1208 | window.navigator.locks.request( |
| 1209 | 'fossil-'+draftKey, |
| 1210 | {ifAvailable: true}, |
| 1211 | async (lock) => { |
| 1212 | if( !lock ){ |
| 1213 | /*lock contention*/ |
| 1214 | resolve(null); |
| 1215 | return; |
| 1216 | } |
| 1217 | let release; |
| 1218 | const lockReleased = new Promise(res=>release=res); |
| 1219 | resolve(release); |
| 1220 | await lockReleased/*hold the lock open*/; |
| 1221 | }); |
| 1222 | }); |
| 1223 | if( !releaseLock ){ |
| 1224 | reportFPEError( |
| 1225 | ePost, |
| 1226 | "This post is actively being replied to ", |
| 1227 | "in another tab. To avoid losing edits, ", |
| 1228 | "it cannot be opened here until the locking ", |
| 1229 |
| --- src/fossil.page.forumpost.js | |
| +++ src/fossil.page.forumpost.js | |
| @@ -1202,27 +1202,47 @@ | |
| 1202 | value here). */ |
| 1203 | || fpid |
| 1204 | ); |
| 1205 | let releaseLock; |
| 1206 | if( window.navigator.locks ){ |
| 1207 | /* This business with forceReleaseLockOnClose and 'pagehide' |
| 1208 | event is a workaround for Chrome being overly lazy in |
| 1209 | releasing Web Locks when a tab is closed. It sometimes |
| 1210 | waits half a minute or more before release. |
| 1211 | |
| 1212 | Sidebar: we cannot use AbortController because it cannot |
| 1213 | be used with the ifAvailable lock check. */ |
| 1214 | let forceReleaseLockOnClose = null; |
| 1215 | const handlePageHide = ()=>{ |
| 1216 | if( forceReleaseLockOnClose ){ |
| 1217 | forceReleaseLockOnClose(); |
| 1218 | } |
| 1219 | }; |
| 1220 | window.addEventListener('pagehide', handlePageHide); |
| 1221 | releaseLock = await new Promise((resolve)=>{ |
| 1222 | window.navigator.locks.request( |
| 1223 | 'fossil-'+draftKey, |
| 1224 | {ifAvailable: true}, |
| 1225 | async (lock)=>{ |
| 1226 | if( !lock ){ |
| 1227 | /*lock contention*/ |
| 1228 | resolve(null); |
| 1229 | return; |
| 1230 | } |
| 1231 | let release; |
| 1232 | const lockReleased = new Promise(res=>release=res); |
| 1233 | forceReleaseLockOnClose = release; |
| 1234 | const wrappedRelease = ()=>{ |
| 1235 | window.removeEventListener('pagehide', handlePageHide); |
| 1236 | if( release ) release(); |
| 1237 | }; |
| 1238 | resolve(wrappedRelease); |
| 1239 | await lockReleased/*hold the lock open*/; |
| 1240 | }); |
| 1241 | }); |
| 1242 | if( !releaseLock ){ |
| 1243 | window.removeEventListener('pagehide', handlePageHide); |
| 1244 | reportFPEError( |
| 1245 | ePost, |
| 1246 | "This post is actively being replied to ", |
| 1247 | "in another tab. To avoid losing edits, ", |
| 1248 | "it cannot be opened here until the locking ", |
| 1249 |