ref: c4be6e0dd0ca49ab1e1535c37888435e6411bb16
parent: 09b964472704c60ee0aa9a34d00e82316b94f952
author: Philip Silva <philip.silva@protonmail.com>
date: Sun Apr 6 04:04:08 EDT 2025
js object
--- a/browser/browser.go
+++ b/browser/browser.go
@@ -820,7 +820,7 @@
q := el.n.QueryRef()
var res string
var err error
- res, consumed, err = js.TriggerClick(q)
+ res, consumed, err = el.b.js.TriggerClick(q)
if err != nil { log.Errorf("trigger click %v: %v", q, err) } else if consumed {@@ -1519,6 +1519,7 @@
history.History
dui *duit.DUI
+ js *js.JS
Website *Website
loading bool
client *http.Client
--- a/browser/experimental.go
+++ b/browser/experimental.go
@@ -2,14 +2,15 @@
import (
"fmt"
+ "github.com/psilva261/mycel"
"github.com/psilva261/mycel/js"
"github.com/psilva261/mycel/logger"
)
-func processJS2() (resHtm string, changed bool, err error) {- resHtm, changed, err = js.Start()
+func processJS2(f mycel.Fetcher) (s *js.JS, resHtm string, changed bool, err error) {+ s, resHtm, changed, err = js.Start(f)
if err != nil {- return "", false, fmt.Errorf("start: %w", err)+ return nil, "", false, fmt.Errorf("start: %w", err)}
log.Printf("processJS: changed = %v", changed)return
--- a/browser/experimental_test.go
+++ b/browser/experimental_test.go
@@ -18,7 +18,6 @@
func init() {log.Debug = true
- js.SetFetcher(&TestFetcher{})style.Init(nil)
go fs.Srv9p()
}
@@ -46,6 +45,7 @@
if testing.Short() { t.Skip("skipping test in short mode.")}
+ f := &TestFetcher{}h := `
<html>
<body>
@@ -72,8 +72,8 @@
}
fs.SetDOM(nt)
fs.Update("", h, nil, scripts)- js.Start()
- h, _, err = processJS2()
+ js.Start(f)
+ s, h, _, err := processJS2(f)
if err != nil {t.Errorf(err.Error())
}
@@ -81,5 +81,5 @@
if !strings.Contains(h, `<body style="display: none; ">`) {t.Fail()
}
- js.Stop()
+ s.Stop()
}
--- a/browser/website.go
+++ b/browser/website.go
@@ -82,6 +82,12 @@
// state. During subsequent calls from click handlers that state is kept.
var scripts []string
if ExperimentalJsInsecure && layouting != ClickRelayout {+ var (
+ jsProcessed string
+ changed bool
+ err error
+ )
+
log.Printf("3rd pass") nt := nodes.NewNodeTree(doc, style.Map{}, nodeMap, nil)jsSrcs := js.Srcs(nt)
@@ -104,9 +110,8 @@
fs.Update(f.Origin().String(), htm, csss, scripts)
fs.SetDOM(nt)
log.Infof("JS pipeline start")- js.Stop()
- js.SetFetcher(f)
- jsProcessed, changed, err := processJS2()
+ w.b.js.Stop()
+ w.b.js, jsProcessed, changed, err = processJS2(f)
if changed && err == nil {htm = jsProcessed
if debugPrintHtml {--- a/cmd/mycel/main.go
+++ b/cmd/mycel/main.go
@@ -341,7 +341,7 @@
}
func finalize() {- js.Stop()
+ js.StopAll()
os.Exit(1)
}
--- a/js/js.go
+++ b/js/js.go
@@ -13,6 +13,7 @@
"os"
"os/exec"
"strings"
+ "sync"
"time"
)
@@ -19,21 +20,19 @@
var timeout = 60 * time.Second
var (
- fetcher mycel.Fetcher
+ instances sync.Map
+)
+type JS struct {service string
cmd *exec.Cmd
cancel context.CancelFunc
-)
-
-func SetFetcher(f mycel.Fetcher) {- fetcher = f
}
-func call(fn, cmd string, args ...string) (resp string, err error) {+func (js *JS) call(fn, cmd string, args ...string) (resp string, err error) {var rwc io.ReadWriteCloser
for t := 100*time.Millisecond; t < 5*time.Second; t *= 2 {- rwc, err = callSparkleCtl()
+ rwc, err = js.callSparkleCtl()
if err == nil {break
}
@@ -57,28 +56,32 @@
}
// Start with pre-defined scripts
-func Start(scripts ...string) (resHtm string, changed bool, err error) {- service = fmt.Sprintf("sparkle.%d", os.Getpid())+func Start(f mycel.Fetcher, scripts ...string) (js *JS, resHtm string, changed bool, err error) {+ js = &JS{+ service: fmt.Sprintf("sparkle.%d", os.Getpid()),+ }
args := make([]string, 0, len(scripts)+2)
if log.Debug {args = append(args, "-v")
}
- args = append(args, "-s", service)
+ args = append(args, "-s", js.service)
log.Infof("Start sparklefs")var ctx context.Context
- ctx, cancel = context.WithCancel(fetcher.Ctx())
- cmd = exec.CommandContext(ctx, "sparklefs", args...)
- cmd.Stderr = os.Stderr
+ ctx, js.cancel = context.WithCancel(f.Ctx())
+ js.cmd = exec.CommandContext(ctx, "sparklefs", args...)
+ js.cmd.Stderr = os.Stderr
log.Infof("cmd.Start...")- if err = cmd.Start(); err != nil {- return "", false, fmt.Errorf("cmd start: %w", err)+ if err = js.cmd.Start(); err != nil {+ return nil, "", false, fmt.Errorf("cmd start: %w", err)}
- resp, err := call("ctl", "start")+ instances.Store(js, js)
+
+ resp, err := js.call("ctl", "start") if err != nil {- return "", false, fmt.Errorf("call start: %v", err)+ return nil, "", false, fmt.Errorf("call start: %v", err)}
if resp != "" {@@ -89,26 +92,36 @@
return
}
-func Stop() {+func StopAll() {+ instances.Range(func(k, _ any) bool {+ js := k.(*JS)
+ js.Stop()
+ instances.Delete(js)
+ return true
+ })
+}
+
+func (js *JS) Stop() { log.Infof("Stop sparklefs")- hangup()
- if cancel != nil {+ js.hangup()
+ if js.cancel != nil { log.Infof("cancel()")- cancel()
- cancel = nil
- if cmd != nil {+ js.cancel()
+ js.cancel = nil
+ if js.cmd != nil {// Prevent Zombie processes after stopping
- cmd.Wait()
- cmd = nil
+ js.cmd.Wait()
+ js.cmd = nil
}
}
+ instances.Delete(js)
}
// TriggerClick, and return the result html
// ...then HTML5 parse it, diff the node tree
// (probably faster and cleaner than anything else)
-func TriggerClick(selector string) (newHTML string, ok bool, err error) {- newHTML, err = call("ctl", "click", selector)+func (js *JS) TriggerClick(selector string) (newHTML string, ok bool, err error) {+ newHTML, err = js.call("ctl", "click", selector)ok = newHTML != "" && err == nil
return
}
--- a/js/js_plan9.go
+++ b/js/js_plan9.go
@@ -5,8 +5,8 @@
"os"
)
-func hangup() {}+func (js *JS) hangup() {}-func callSparkleCtl() (rwc io.ReadWriteCloser, err error) {+func (js *JS) callSparkleCtl() (rwc io.ReadWriteCloser, err error) { return os.OpenFile("/mnt/sparkle/ctl", os.O_RDWR, 0600)}
--- a/js/js_test.go
+++ b/js/js_test.go
@@ -26,7 +26,6 @@
func init() {log.Debug = true
- SetFetcher(&TestFetcher{})go fs.Srv9p()
<-time.After(2*time.Second)
}
@@ -51,6 +50,7 @@
}
func TestJQueryHide(t *testing.T) {+ f := &TestFetcher{} if testing.Short() { t.Skip("skipping test in short mode.")}
@@ -73,7 +73,7 @@
fs.SetDOM(nt)
fs.Update("", simpleHTML, nil, []string{string(buf), script})- resHtm, changed, err := Start(string(buf), script)
+ svc, resHtm, changed, err := Start(f, string(buf), script)
if err != nil { t.Fatalf("%v", err)}
@@ -91,5 +91,5 @@
if v := nt.Find("h1").Css("display"); v != "none" { t.Fatalf("%v", v)}
- Stop()
+ svc.Stop()
}
--- a/js/js_unix.go
+++ b/js/js_unix.go
@@ -16,9 +16,9 @@
fsys *client.Fsys
)
-func dial() (err error) {+func (js *JS) dial() (err error) { log.Infof("Init...")- conn, err := client.DialService(service)
+ conn, err := client.DialService(js.service)
if err != nil { return fmt.Errorf("dial: %v", err)}
@@ -34,7 +34,7 @@
return
}
-func hangup() {+func (js *JS) hangup() { if fsys != nil {fsys = nil
}
@@ -44,9 +44,9 @@
}
}
-func callSparkleCtl() (rwc io.ReadWriteCloser, err error) {+func (js *JS) callSparkleCtl() (rwc io.ReadWriteCloser, err error) { if fsys == nil {- if err := dial(); err != nil {+ if err := js.dial(); err != nil { return nil, fmt.Errorf("dial: %v", err)}
}
--
⑨