审计修复:前端(模板 / CSS / JS 与无障碍)
- 地图脚本:localStorage 读写走 lsGet/lsSet 兜底(隐私模式下裸访问会打断整段脚本); preview() 在 /api/map-config 还没回来时不丢点(previewQueue 在 if (live) 之前排队); 异步回包一律发序号作废,取消后回来的包不再画 - photo-exif.js:EXIF 时间全 0 不再变成 1899-11-30 写进到访时间;读 count 前先按版本卡宽度 - 数据编辑页:三个筛选表单都把其余条件用隐藏域原样带上,提交不再把别的列表的筛选清掉 - 补全页:sessionStorage 的读写一律包 try(脏数据/隐私模式都会抛) - 窄屏:删掉被后面同特异性规则盖掉的死内边距;宽表堆叠类名成对、判据扩到所有 tb 表 - 无障碍:图片删除按钮补 aria-label,访客画廊图片补 alt - report.html 补上漏掉的 </tr>,fog-mask 的宽表带 tb-stack 本批不碰数据库与导出路径:往返包 823/323/8/30/791/557/2246 全等、 坐标偏差 0.000000000、时间偏差 0s、迷雾 8701 块一致。
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// funcBody 从 marker 起按大括号配平取出一段函数体(含 marker)。
|
||||
// 这几段代码里的字符串字面量都不含大括号,直接数括号是安全的。
|
||||
func funcBody(src, marker string) string {
|
||||
i := strings.Index(src, marker)
|
||||
if i < 0 {
|
||||
return ""
|
||||
}
|
||||
j := strings.Index(src[i:], "{")
|
||||
if j < 0 {
|
||||
return ""
|
||||
}
|
||||
depth := 0
|
||||
for k := i + j; k < len(src); k++ {
|
||||
switch src[k] {
|
||||
case '{':
|
||||
depth++
|
||||
case '}':
|
||||
depth--
|
||||
if depth == 0 {
|
||||
return src[i : k+1]
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// TestMapLayersDropStaleResponses 守住「开关关着图层还在画」这个竞态。
|
||||
//
|
||||
// 勾选 → 请求发出 → 用户立刻取消 → 请求回包。回包若直接 addTo(map),图层就画上去了。
|
||||
// 修法是给每次调用发一个序号,回包时对不上就作废;关键是**序号必须在 `if (!on) return;`
|
||||
// 之前就递增**,否则取消时压根不会作废在途的那次请求。
|
||||
//
|
||||
// 判据放在源码文本上:这是「两个异步分支的相对顺序」,用 CDP 造这个时序既慢又脆。
|
||||
func TestMapLayersDropStaleResponses(t *testing.T) {
|
||||
js, err := staticFS.ReadFile("static/js/rond-map.js")
|
||||
if err != nil {
|
||||
t.Fatalf("读取 rond-map.js 失败: %v", err)
|
||||
}
|
||||
src := string(js)
|
||||
|
||||
for _, c := range []struct{ setter, seq, draw string }{
|
||||
{"window.rondMap.setGpx = function (on)", "gpxSeq", "addTo(map)"},
|
||||
{"window.rondMap.setHeat = function (on)", "heatSeq", "addTo(map)"},
|
||||
{"window.rondMap.setCityAgg = function (on)", "citySeq", "addTo(map)"},
|
||||
} {
|
||||
body := funcBody(src, c.setter)
|
||||
if body == "" {
|
||||
t.Fatalf("在 rond-map.js 里找不到 %s 的函数体(写法变了,这条守卫就失效了)", c.setter)
|
||||
}
|
||||
inc := strings.Index(body, "++"+c.seq)
|
||||
off := strings.Index(body, "if (!on) return;")
|
||||
guard := strings.Index(body, "seq !== "+c.seq)
|
||||
draw := strings.Index(body, c.draw)
|
||||
if inc < 0 || off < 0 || inc > off {
|
||||
t.Errorf("%s:必须在 `if (!on) return;` 之前先递增序号(否则取消时作废不了在途回包)", c.setter)
|
||||
}
|
||||
if guard < 0 || draw < 0 || guard > draw {
|
||||
t.Errorf("%s:回包在 %s 之前没有做 `seq !== %s` 作废检查", c.setter, c.draw, c.seq)
|
||||
}
|
||||
}
|
||||
|
||||
// 行程轨迹开关在模板里(map.html),症状一样:取消勾选后旧回包把轨迹画回来。
|
||||
html, err := fs.ReadFile(tmplFS, "templates/map.html")
|
||||
if err != nil {
|
||||
t.Fatalf("读取 map.html 失败: %v", err)
|
||||
}
|
||||
body := funcBody(string(html), "function refreshTrack()")
|
||||
if body == "" {
|
||||
t.Fatal("在 map.html 里找不到 refreshTrack(写法变了,这条守卫就失效了)")
|
||||
}
|
||||
inc := strings.Index(body, "++trackSeq")
|
||||
off := strings.Index(body, "if (!trackOn)")
|
||||
guard := strings.Index(body, "seq !== trackSeq")
|
||||
draw := strings.Index(body, "setTrack(d.segs")
|
||||
if inc < 0 || off < 0 || inc > off {
|
||||
t.Error("refreshTrack:必须在 `if (!trackOn)` 之前先递增序号")
|
||||
}
|
||||
if guard < 0 || draw < 0 || guard > draw {
|
||||
t.Error("refreshTrack:回包在 setTrack(d.segs…) 之前没有做 `seq !== trackSeq` 作废检查")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user