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, " "))
}