ref: 8bafb50f34edce70de96005a21dfb73a400852bd
parent: 70388cdceb9f92b6a3578cca2cacb30beb036640
author: Philip Silva <philip.silva@protonmail.com>
date: Fri Dec 25 19:07:20 EST 2020
Rudimentary click handling
--- a/browser/browser.go
+++ b/browser/browser.go
@@ -137,7 +137,7 @@
}
src := attr(*n.DomSubtree, "src")
if src == "" {- return nil, fmt.Errorf("no src in %+v", n.Attr)+ return nil, fmt.Errorf("no src in %+v", n.Attrs)}
var i *draw.Image
@@ -285,12 +285,15 @@
Text: t,
Font: n.Font(),
Click: func() (r duit.Event) {- b.submit(n.Ancestor("form").DomSubtree)- return duit.Event{- Consumed: true,
- NeedLayout: true,
- NeedDraw: true,
+ if f := n.Ancestor("form"); f != nil {+ b.submit(f.DomSubtree, n.DomSubtree)
+ return duit.Event{+ Consumed: true,
+ NeedLayout: true,
+ NeedDraw: true,
+ }
}
+ return
},
}
return NewElement(btn, n)
@@ -312,7 +315,7 @@
},
Keys: func(k rune, m draw.Mouse) (e duit.Event) { if k == 10 {- browser.submit(n.Ancestor("form").DomSubtree)+ browser.submit(n.Ancestor("form").DomSubtree, nil) return duit.Event{Consumed: true,
NeedLayout: true,
@@ -335,6 +338,21 @@
if m.Buttons == 1 { if el.Click != nil {el.Click()
+ } else {+ if *ExperimentalJsInsecure {+ res, changed, err := browser.Website.d.TriggerClick(el.n.QueryRef())
+ if changed && err == nil {+ browser.Website.html = res
+ browser.Website.layout(browser)
+ dui.MarkLayout(dui.Top.UI)
+ dui.MarkDraw(dui.Top.UI)
+ dui.Render()
+
+ return duit.Result{+ Consumed: true,
+ }
+ }
+ }
}
}
x := m.Point.X
@@ -365,6 +383,10 @@
// makeLink of el and its children
func (el *Element) makeLink(href string) {+ if href == "" || strings.HasPrefix(href, "#") || strings.Contains(href, "javascript:void(0)") {+ return
+ }
+
f := browser.SetAndLoadUrl(href)
TraverseTree(el, func(ui duit.UI) {el, ok := ui.(*Element)
@@ -818,8 +840,11 @@
var innerContent duit.UI
if nodes.IsPureTextContent(*n) {t := nodes.ContentFrom(*n)
- if s, ok := n.Map.Declarations["list-style"]; !ok || s.Value != "none" {- t = "• " + t
+
+ if ul := n.Ancestor("ul"); ul != nil {+ if s, ok := ul.Map.Declarations["list-style"]; !ok || s.Value != "none" {+ t = "• " + t
+ }
}
innerContent = &ColoredLabel{ Label: &duit.Label{@@ -839,7 +864,7 @@
)
case "a":
var href string
- for _, a := range n.Attr {+ for _, a := range n.Attrs { if a.Key == "href" {href = a.Val
}
@@ -850,7 +875,6 @@
Label: &duit.Label{Text: nodes.ContentFrom(*n),
Font: n.Font(),
- Click: browser.SetAndLoadUrl(href),
},
n: n,
}
--- a/browser/website.go
+++ b/browser/website.go
@@ -159,15 +159,18 @@
log.Flush()
}
-func formData(n html.Node) (data url.Values) {+func formData(n, submitBtn *html.Node) (data url.Values) {data = make(url.Values)
if n.Data == "input" {- if k := attr(n, "name"); k != "" {- data.Set(k, attr(n, "value"))
+ if attr(*n, "type") == "submit" && n != submitBtn {+ return
}
+ if k := attr(*n, "name"); k != "" {+ data.Set(k, attr(*n, "value"))
+ }
}
for c := n.FirstChild; c != nil; c = c.NextSibling {- for k, vs := range formData(*c) {+ for k, vs := range formData(c, submitBtn) {data.Set(k, vs[0]) // TODO: what aboot the rest?
}
}
@@ -174,7 +177,7 @@
return
}
-func (b *Browser) submit(form *html.Node) {+func (b *Browser) submit(form *html.Node, submitBtn *html.Node) {var err error
method := "GET" // TODO
if m := attr(*form, "method"); m != "" {@@ -193,7 +196,7 @@
var contentType opossum.ContentType
if method == "GET" {q := uri.Query()
- for k, vs := range formData(*form) {+ for k, vs := range formData(form, submitBtn) { log.Printf("add query info %v => %v", k, vs[0])q.Set(k, vs[0]) // TODO: what is with the rest?
}
@@ -202,7 +205,7 @@
buf, contentType, err = b.get(uri, true)
log.Printf("uri=%v", uri.String()) } else {- buf, contentType, err = b.PostForm(uri, formData(*form))
+ buf, contentType, err = b.PostForm(uri, formData(form, submitBtn))
}
if err == nil { if contentType.IsHTML() {--- a/domino-lib/Document.js
+++ b/domino-lib/Document.js
@@ -160,7 +160,7 @@
},
// XXX: DOMCore may remove documentURI, so it is NYI for now
- documentURI: { get: function() { return this._address; }, set: utils.nyi },+ documentURI: { get: function() { return this._address; }, set: utils.nyi.bind(this, 'set documentURI') }, compatMode: { get: function() {// The _quirks property is set by the HTML parser
return this._quirks ? 'BackCompat' : 'CSS1Compat';
@@ -366,15 +366,15 @@
characterSet: { get: function characterSet() { return "UTF-8"; } }, contentType: { get: function contentType() { return this._contentType; } }, URL: { get: function URL() { return this._address; } },- domain: { get: utils.nyi, set: utils.nyi },- referrer: { get: utils.nyi },- cookie: { get: utils.nyi, set: utils.nyi },- lastModified: { get: utils.nyi },+ domain: { get: utils.nyi.bind(this, 'domain'), set: utils.nyi.bind(this, 'domain') },+ referrer: { get: utils.nyi.bind(this, 'referrer') },+ cookie: { get: utils.nyi.bind(this, 'cookie get'), set: utils.nyi.bind(this, 'cookie set') },+ lastModified: { get: utils.nyi.bind(this, 'lastModified get') }, location: { get: function() {return this.defaultView ? this.defaultView.location : null; // gh #75
},
- set: utils.nyi
+ set: utils.nyi.bind(this, 'location set')
},
_titleElement: { get: function() {--- a/domino-lib/utils.js
+++ b/domino-lib/utils.js
@@ -37,8 +37,8 @@
exports.InvalidNodeTypeError = function() { throw new DOMException(ERR.INVALID_NODE_TYPE_ERR); }; exports.DataCloneError = function() { throw new DOMException(ERR.DATA_CLONE_ERR); };-exports.nyi = function() {- throw new Error("NotYetImplemented");+exports.nyi = function(what) {+ throw new Error("NotYetImplemented " + what);};
exports.shouldOverride = function() {--- a/domino/domino.go
+++ b/domino/domino.go
@@ -109,11 +109,7 @@
if *DebugDumpJS { ioutil.WriteFile("main.js", []byte(SCRIPT), 0644)}
- prg, err := goja.Compile("main.js", SCRIPT, false)- if err != nil {- IntrospectError(err, SCRIPT)
- return fmt.Errorf("compile: %w", err)- }
+
ready := make(chan int)
go func() { d.loop.RunOnLoop(func(vm *goja.Runtime) {@@ -136,7 +132,7 @@
HTML: d.html,
Buf: "yolo",
})
- _, err := vm.RunProgram(prg)
+ _, err := vm.RunString(SCRIPT)
if err != nil { log.Printf("run program: %v", err)IntrospectError(err, script)
@@ -172,7 +168,16 @@
}
func (d *Domino) Export(expr string) (res string, err error) {- v, err := d.vm.RunString(expr)
+ var v goja.Value
+ ch := make(chan int, 1)
+
+ d.loop.RunOnLoop(func(vm *goja.Runtime) {+ v, err = vm.RunString(expr)
+ ch <- 1
+ })
+
+ <-ch
+
if err != nil { return "", fmt.Errorf("export: %w", err)}
@@ -179,6 +184,7 @@
if v != nil { res = fmt.Sprintf("%v", v.Export())}
+
return
}
@@ -186,22 +192,36 @@
// ...then HTML5 parse it, diff the node tree
// (probably faster and cleaner than anything else)
func (d *Domino) TriggerClick(selector string) (newHTML string, ok bool, err error) {- res, err := d.vm.RunString(`
- var sel = '` + selector + `';
- var sell = document.querySelector(sel);
- if (sell._listeners && sell._listeners.click) {- var selfn = sell.click.bind(sell);
- if (selfn) {- selfn();
+ var res goja.Value
+ ch := make(chan int, 1)
+
+ d.loop.RunOnLoop(func(vm *goja.Runtime) {+ res, err = vm.RunString(`
+ var sel = '` + selector + `';
+ console.log('sel=');+ console.log(sel);
+ var sell = document.querySelector(sel);
+ if (sell._listeners && sell._listeners.click) {+ var selfn = sell.click.bind(sell);
+ if (selfn) {+ selfn();
+ }
+ !!selfn;
+ } else {+ false;
}
- !!selfn;
- } else {- false;
- }
- `)
+ `)
+ ch <- 1
+ })
+ <- ch
+
ok = fmt.Sprintf("%v", res) == "true"+ if ok {+ newHTML, ok, err = d.TrackChanges()
+ }
+
return
}
@@ -209,9 +229,7 @@
func (d *Domino) PutAttr(selector, attr, val string) (ok bool, err error) {res, err := d.vm.RunString(`
var sel = '` + selector + `';
- console.log('sel=' + sel);var sell = document.querySelector(sel);
- console.log('sell=' + sell); sell.attr('` + attr + `', '` + val + `');!!sell;
`)
@@ -297,7 +315,7 @@
isJS := true
src := ""
- for _, a := range n.Attr {+ for _, a := range n.Attrs { switch strings.ToLower(a.Key) {case "type":
t, err := opossum.NewContentType(a.Val, nil)
--- a/domino/domino_test.go
+++ b/domino/domino_test.go
@@ -161,11 +161,11 @@
if res != "Hello" {t.Fatalf(res)
}
- _, ok, err := d.TriggerClick("h1")+ _, changed, err := d.TriggerClick("h1") if err != nil {t.Fatalf(err.Error())
}
- if !ok {+ if changed {t.Fatal()
}
res, err = d.Export("clicked")--- a/nodes/nodes.go
+++ b/nodes/nodes.go
@@ -18,7 +18,7 @@
DomSubtree *html.Node
Text string
Wrappable bool
- Attr []html.Attribute
+ Attrs []html.Attribute
style.Map
Children []*Node
Parent *Node
@@ -41,7 +41,7 @@
//Data: data,
//Type: doc.Type,
DomSubtree: doc,
- Attr: doc.Attr,
+ Attrs: doc.Attr,
Map: ncs,
Children: make([]*Node, 0, 2),
Parent: parent,
@@ -80,6 +80,9 @@
// Ancestor of tag
func (n *Node) Ancestor(tag string) *Node {+ if n.DomSubtree == nil {+ return nil
+ }
log.Printf("<%v>.ParentForm()", n.DomSubtree.Data) if n.DomSubtree.Data == tag { log.Printf(" I'm a %v :-)", tag)@@ -92,13 +95,28 @@
return nil
}
+func (n *Node) Attr(k string) string {+ for _, a := range n.Attrs {+ if a.Key == k {+ return a.Val
+ }
+ }
+ return ""
+}
+
func (n *Node) QueryRef() string {+ if id := n.Attr("id"); id != "" {+ return "#" + id
+ }
+
path := make([]string, 0, 5)
- path = append(path, n.Data())
+ if n.Type() != html.TextNode {+ path = append(path, n.Data())
+ }
for p := n.Parent; p != nil; p = p.Parent { if p.Data() != "html" && p.Data() != "body" { path = append([]string{p.Data()}, path...)- }
+ }
}
return strings.TrimSpace(strings.Join(path, " "))
}
--
⑨