ref: 52ebaa6c60bf90f3b7b326b94723c1999ea99655
parent: c9c7b73c0740cf8d6bdd6469226b4d4b2001cb9e
author: Philip Silva <philip.silva@protonmail.com>
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()
}