shithub: opossum

Download patch

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