shithub: mycel

Download patch

ref: 52ebaa6c60bf90f3b7b326b94723c1999ea99655
parent: c9c7b73c0740cf8d6bdd6469226b4d4b2001cb9e
author: Philip Silva <[email protected]>
date: Sun Feb 7 16:35:35 EST 2021

take mutation events into account

--- a/browser/browser.go
+++ b/browser/browser.go
@@ -492,6 +492,7 @@
 	if !consumed {
 		return
 	}
+	log.Infof("click processed")
 
 	offset := scroller.Offset
 	browser.Website.html = res
--- a/browser/experimental.go
+++ b/browser/experimental.go
@@ -120,12 +120,12 @@
 	})
 }
 
-func processJS2(d *domino.Domino, scripts []string) (resHtm string, err error) {
+func processJS2(d *domino.Domino, scripts []string) (resHtm string, changed bool, err error) {
 	initialized := false
 	for _, script := range scripts {
 		if _, err := d.Exec/*6*/(script, !initialized); err != nil {
 			if strings.Contains(err.Error(), "halt at") {
-				return "", fmt.Errorf("execution halted: %w", err)
+				return "", false, fmt.Errorf("execution halted: %w", err)
 			}
 			log.Errorf("exec <script>: %v", err)
 		}
@@ -133,12 +133,12 @@
 	}
 
 	if err = d.CloseDoc(); err != nil {
-		return "", fmt.Errorf("close doc: %w", err)
+		return "", false, fmt.Errorf("close doc: %w", err)
 	}
 
-	resHtm, changed, err := d.TrackChanges()
+	resHtm, changed, err = d.TrackChanges()
 	if err != nil {
-		return "", fmt.Errorf("track changes: %w", err)
+		return "", false, fmt.Errorf("track changes: %w", err)
 	}
 	log.Printf("processJS: changed = %v", changed)
 	return
--- a/browser/experimental_test.go
+++ b/browser/experimental_test.go
@@ -53,7 +53,7 @@
 		`$('body').hide()`,
 		`throw 'fail';`,
 	}
-	h, err = processJS2(d, scripts)
+	h, _, err = processJS2(d, scripts)
 	if err != nil { t.Errorf(err.Error()) }
 	t.Logf("h = %+v", h)
 	if !strings.Contains(h, `<body style="display: none;">`) {
--- a/browser/website.go
+++ b/browser/website.go
@@ -122,17 +122,14 @@
 		}
 		w.d = domino.NewDomino(w.html, browser, nt)
 		w.d.Start()
-		jsProcessed, err := processJS2(w.d, codes)
-		if err == nil {
-			if w.html != jsProcessed {
-				log.Infof("html changed")
-			}
+		jsProcessed, changed, err := processJS2(w.d, codes)
+		if changed && err == nil {
 			w.html = jsProcessed
 			if debugPrintHtml {
 				log.Printf("%v\n", jsProcessed)
 			}
 			doc, nodeMap = pass(w.html, csss...)
-		} else {
+		} else if err != nil {
 			log.Errorf("JS error: %v", err)
 		}
 		log.Infof("JS pipeline end")
--- a/domino/domino.go
+++ b/domino/domino.go
@@ -31,6 +31,12 @@
 	log = l
 }
 
+type Mutation struct {
+	time.Time
+	Type int
+	Sel string
+}
+
 type Domino struct {
 	fetcher   opossum.Fetcher
 	loop       *eventloop.EventLoop
@@ -37,7 +43,7 @@
 	html       string
 	nt           *nodes.Node
 	outputHtml string
-	domChanged chan int
+	domChange chan Mutation
 }
 
 func NewDomino(html string, fetcher opossum.Fetcher, nt *nodes.Node) (d *Domino) {
@@ -45,6 +51,7 @@
 		html: html,
 		fetcher: fetcher,
 		nt: nt,
+		domChange: make(chan Mutation, 100),
 	}
 	return
 }
@@ -144,7 +151,12 @@
 		window.location.href = 'http://example.com';
 		var ___fq;
 		___fq = function(pre, el) {
-			var i, p = el.parentElement;
+			var i, p;
+			
+			if (!el) {
+				return undefined;
+			}
+			p = el.parentElement;
 
 			if (p) {
 				for (i = 0; i < p.children.length; i++) {
@@ -156,6 +168,11 @@
 				return el.tagName;
 			}
 		};
+		document._setMutationHandler(function(a) {
+			// a provides attributes type, target and node or attr
+			// (cf Object.keys(a))
+			opossum.mutated(a.type, ___fq('yolo', a.target));
+		});
 		window.getComputedStyle = function(el, pseudo) {
 			this.el = el;
 			this.getPropertyValue = function(prop) {
@@ -254,6 +271,7 @@
 					Referrer func() string `json:"referrer"`
 					Style func(string, string, string, string) string `json:"style"`
 					XHR func(string, string, map[string]string, string, func(string, string)) `json:"xhr"`
+					Mutated func(int, string) `json:"mutated"`
 				}
 
 				vm.SetFieldNameMapper(goja.TagFieldNameMapper("json", true))
@@ -274,6 +292,7 @@
 						return res[0].Css(prop)
 					},
 					XHR: d.xhr,
+					Mutated: d.mutated,
 				})
 			}
 
@@ -341,7 +360,7 @@
 
 // CloseDoc fires DOMContentLoaded to trigger $(document).ready(..)
 func (d *Domino) CloseDoc() (err error) {
-	_, err = d.Exec("document.close();", false)
+	_, err = d.Exec("if (this.document) document.close();", false)
 	return
 }
 
@@ -394,11 +413,23 @@
 }
 
 func (d *Domino) TrackChanges() (html string, changed bool, err error) {
-	html, err = d.Exec("document.querySelector('html').innerHTML;", false)
-	if err != nil {
-		return
+	outer:
+	for {
+		select {
+		case m := <-d.domChange:
+			log.Infof("mutation received @ %v for %v", m.Time, m.Sel)
+			changed = true
+		default:
+			break outer
+		}
 	}
-	changed = d.outputHtml != html
+	
+	if changed {
+		html, err = d.Exec("document.querySelector('html').innerHTML;", false)
+		if err != nil {
+			return
+		}
+	}
 	d.outputHtml = html
 	return
 }
@@ -526,6 +557,21 @@
 		return
 	}
 	cb(string(bs), "")
+}
+
+func (d *Domino) mutated(t int, q string) {
+	m := Mutation{
+		Time: time.Now(),
+		Type: t,
+		Sel: q,
+	}
+	log.Infof("mutation received: %+v", m)
+	select {
+	case d.domChange <- m:
+	default:
+		// TODO: that's not supposed to happen
+		log.Errorf("dom changes backlog full")
+	}
 }
 
 // AJAX:
--- a/domino/domino_test.go
+++ b/domino/domino_test.go
@@ -395,9 +395,6 @@
 	if err != nil {
 		t.Fatalf(err.Error())
 	}
-	if html == "" {
-		t.Fatalf(err.Error())
-	}
 	if changed == true {
 		t.Fatal()
 	}
@@ -406,9 +403,6 @@
 	if err != nil {
 		t.Fatalf(err.Error())
 	}
-	if html == "" {
-		t.Fatalf(err.Error())
-	}
 	if changed == true {
 		t.Fatal()
 	}
@@ -652,5 +646,31 @@
 		t.Fatalf("%v", err)
 	}
 	t.Logf("res=%v", res)
+	d.Stop()
+}
+
+func TestMutationEvents(t *testing.T) {
+	buf, err := ioutil.ReadFile("jquery-3.5.1.js")
+	if err != nil {
+		t.Fatalf("%v", err)
+	}
+	d := NewDomino(simpleHTML, nil, nil)
+	d.Start()
+	script := `
+	$('h1').hide();
+	$('h1').show();
+	`
+	_, err = d.Exec(string(buf) + ";" + script, true)
+	if err != nil {
+		t.Fatalf("%v", err)
+	}
+	if err = d.CloseDoc(); err != nil {
+		t.Fatalf("%v", err)
+	}
+	res, err := d.Exec("$('h1').attr('style')", false)
+	t.Logf("res=%v", res)
+	if err != nil {
+		t.Fatalf("%v", err)
+	}
 	d.Stop()
 }