ref: cdae93dae8b27ebf26cca981be01088e01e9f865
parent: 3ce4cfc9371c16b2ac4ac167668122fca13cc850
author: Philip Silva <philip.silva@protonmail.com>
date: Mon Dec 28 08:53:33 EST 2020
merge exec and export
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@
~*
.DS_Store
/cmd/browse/browse
+main.js
--- a/browser/experimental.go
+++ b/browser/experimental.go
@@ -3,7 +3,6 @@
import (
"fmt"
"image"
- "strings"
"opossum/domino"
"opossum/nodes"
"time"
@@ -137,42 +136,16 @@
})
}
-func processJS(htm string) (resHtm string, err error) {
- _ = strings.Replace(htm, "window.", "", -1)
- d := domino.NewDomino(htm)
- d.Start()
- if err = d.ExecInlinedScripts(); err != nil {
- return "", fmt.Errorf("exec <script>s: %w", err)
- }
- time.Sleep(time.Second)
- resHtm, changed, err := d.TrackChanges()
- log.Infof("processJS: changes = %v", changed)
- d.Stop()
- return
-}
-
func processJS2(d *domino.Domino, doc *nodes.Node, scripts []string) (resHtm string, err error) {
- code := ""
+ initialized := false
for _, script := range scripts {
- code += `
- try {
- ` + script + `;
- ` + fmt.Sprintf(`
- console.log('==============');
- console.log('Success!');
- console.log('==============');
- `) + `
- } catch(e) {
- console.log('==============');
- console.log('Catch:');
- console.log(e);
- console.log('==============');
- }
- `
+ if _, err := d.Exec/*6*/(script, !initialized); err == nil {
+ initialized = true
+ } else {
+ log.Errorf("exec <script>: %v", err)
+ }
}
- if err = d.Exec/*6*/(code); err != nil {
- return "", fmt.Errorf("exec <script>s: %w", err)
- }
+
time.Sleep(time.Second)
resHtm, changed, err := d.TrackChanges()
if err != nil {
--- a/domino/domino.go
+++ b/domino/domino.go
@@ -20,8 +20,8 @@
var DebugDumpJS *bool
type Domino struct {
+ initialized bool
loop *eventloop.EventLoop
- vm *goja.Runtime
html string
outputHtml string
domChanged chan int
@@ -56,7 +56,6 @@
yx := strings.Split(yxStart, ":")
y, _ := strconv.Atoi(yx[0])
x, _ := strconv.Atoi(yx[1])
- log.Printf("line %v, column %v", y, x)
lines := strings.Split(script, "\n")
if wholeLine := lines[y-1]; len(wholeLine) > 100 {
@@ -81,18 +80,19 @@
}
}
-func (d *Domino) Exec(script string) (err error) {
+func (d *Domino) Exec(script string, initial bool) (res string, err error) {
+ if !initial && !d.initialized {
+ initial = true
+ }
script = strings.Replace(script, "const ", "var ", -1)
script = strings.Replace(script, "let ", "var ", -1)
script = strings.Replace(script, "<!--", "", -1)
SCRIPT := `
- global = {};
- //global.__domino_frozen__ = true; // Must precede any require('domino')
- var domino = require('domino-lib/index');
- var Element = domino.impl.Element; // etc
+ global = {};
+ //global.__domino_frozen__ = true; // Must precede any require('domino')
+ var domino = require('domino-lib/index');
+ var Element = domino.impl.Element; // etc
- // JSDOM also knows the style tag
- // https://github.com/jsdom/jsdom/issues/2485
Object.assign(this, domino.createWindow(s.html, 'http://example.com'));
window = this;
window.parent = window;
@@ -102,54 +102,57 @@
window.location.href = 'http://example.com';
navigator = {};
HTMLElement = domino.impl.HTMLElement;
- // Fire DOMContentLoaded
- // to trigger $(document)readfy!!!!!!!
- document.close();
+ // Fire DOMContentLoaded to trigger $(document).ready(..)
+ document.close();
` + script
+ if !initial {
+ SCRIPT = script
+ }
if *DebugDumpJS {
ioutil.WriteFile("main.js", []byte(SCRIPT), 0644)
}
- ready := make(chan int)
+ ready := make(chan goja.Value)
go func() {
d.loop.RunOnLoop(func(vm *goja.Runtime) {
log.Printf("RunOnLoop")
- registry := require.NewRegistry(
- require.WithGlobalFolders(".", ".."),
- )
- console.Enable(vm)
- req := registry.Enable(vm)
- _ = req
+ if initial {
+ registry := require.NewRegistry(
+ require.WithGlobalFolders(".", ".."),
+ )
+ console.Enable(vm)
+ req := registry.Enable(vm)
+ _ = req
- vm.SetFieldNameMapper(goja.TagFieldNameMapper("json", true))
- type S struct {
- Buf string `json:"buf"`
- HTML string `json:"html"`
- }
- d.vm = vm
+ vm.SetFieldNameMapper(goja.TagFieldNameMapper("json", true))
+ type S struct {
+ Buf string `json:"buf"`
+ HTML string `json:"html"`
+ }
- vm.Set("s", S{
- HTML: d.html,
- Buf: "yolo",
- })
- _, err := vm.RunString(SCRIPT)
+ vm.Set("s", S{
+ HTML: d.html,
+ Buf: "yolo",
+ })
+ }
+ vv, err := vm.RunString(SCRIPT)
if err != nil {
log.Printf("run program: %v", err)
IntrospectError(err, script)
}
- ready <- 1
+ ready <- vv
})
}()
- <-ready
+ v := <-ready
<-time.After(10 * time.Millisecond)
- //res = fmt.Sprintf("%v", v.Export())
- if _, _, err = d.TrackChanges(); err != nil {
- return fmt.Errorf("track changes: %w", err)
+ if v != nil {
+ res = v.String()
}
+ if err == nil { d.initialized=true }
return
}
-func (d *Domino) Exec6(script string) (err error) {
+func (d *Domino) Exec6(script string) (res string, err error) {
babel.Init(4) // Setup 4 transformers (can be any number > 0)
r, err := babel.Transform(strings.NewReader(script), map[string]interface{}{
"plugins": []string{
@@ -158,67 +161,40 @@
},
})
if err != nil {
- return fmt.Errorf("babel: %v", err)
+ return "", fmt.Errorf("babel: %v", err)
}
buf, err := ioutil.ReadAll(r)
if err != nil {
- return fmt.Errorf("read all: %v", err)
+ return "", fmt.Errorf("read all: %v", err)
}
- return d.Exec(string(buf))
+ return d.Exec(string(buf), true)
}
-func (d *Domino) Export(expr string) (res string, err error) {
- 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)
- }
- if v != nil {
- res = fmt.Sprintf("%v", v.Export())
- }
-
- return
-}
-
// TriggerClick, and return the result html
// ...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) {
- var res goja.Value
- ch := make(chan int, 1)
+ res, err := d.Exec(`
+ var sel = '` + selector + `';
+ var el = document.querySelector(sel);
- 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;
- }
- `)
- ch <- 1
- })
- <- ch
+ console.log('query ' + sel);
+ if (el._listeners && el._listeners.click) {
+ var fn = el.click.bind(el);
- ok = fmt.Sprintf("%v", res) == "true"
+ if (fn) {
+ console.log(' call click handler...');
+ fn();
+ }
- if ok {
+ !!fn;
+ } else {
+ false;
+ }
+ `, false)
+
+ if ok = res == "true"; ok {
newHTML, ok, err = d.TrackChanges()
}
@@ -227,20 +203,20 @@
// Put change into html (e.g. from input field mutation)
func (d *Domino) PutAttr(selector, attr, val string) (ok bool, err error) {
- res, err := d.vm.RunString(`
+ res, err := d.Exec(`
var sel = '` + selector + `';
- var sell = document.querySelector(sel);
- sell.attr('` + attr + `', '` + val + `');
- !!sell;
- `)
+ var el = document.querySelector(sel);
+ el.attr('` + attr + `', '` + val + `');
+ !!el;
+ `, false)
- ok = fmt.Sprintf("%v", res) == "true"
+ ok = res == "true"
return
}
func (d *Domino) TrackChanges() (html string, changed bool, err error) {
- html, err = d.Export("document.querySelector('html').innerHTML;")
+ html, err = d.Exec("document.querySelector('html').innerHTML;", false)
if err != nil {
return
}
@@ -247,30 +223,6 @@
changed = d.outputHtml != html
d.outputHtml = html
return
-}
-
-// https://stackoverflow.com/a/26716182
-// TODO: eval is evil
-func (d *Domino) ExecInlinedScripts() (err error) {
- return d.Exec(`
- navigator = {};
-
- var scripts = Array.prototype.slice.call(document.getElementsByTagName("script"));
- for (var i = 0; i < scripts.length; i++) {
- if (scripts[i].src != "") {
- var tag = document.createElement("script");
- tag.src = scripts[i].src;
- document.getElementsByTagName("head")[0].appendChild(tag);
- }
- else {
- try {
- eval.call(window, scripts[i].innerHTML);
- } catch(e) {
- console.log(e);
- }
- }
- }
- `)
}
func Srcs(doc *nodes.Node) (srcs []string) {
--- a/domino/domino_test.go
+++ b/domino/domino_test.go
@@ -23,25 +23,36 @@
func TestSimple(t *testing.T) {
d := NewDomino(simpleHTML)
d.Start()
- script := `
- console.log('Hello!!');
- var numberOne = 1;
+ s := `
+ var state = 'empty';
+ var a = 1;
+ b = 2;
`
- err := d.Exec(script)
+ _, err := d.Exec(s, true)
if err != nil {
t.Fatalf("%v", err)
}
- res, err := d.Export("numberOne+1")
- t.Logf("res=%v", res)
+ s2 := `
+ (function() {
+ if (state !== 'empty') throw new Exception(state);
+
+ state = a + b;
+ })()
+ var a = 1;
+ b = 2;
+ `
+ _, err = d.Exec(s2, false)
if err != nil {
t.Fatalf("%v", err)
}
- if res != "2" {
- t.Fatal()
- }
d.Stop()
}
+func TestGlobals(t *testing.T) {
+ d := NewDomino(simpleHTML)
+ d.Start()
+}
+
func TestJQuery(t *testing.T) {
buf, err := ioutil.ReadFile("jquery-3.5.1.js")
if err != nil {
@@ -66,11 +77,11 @@
var numberOne = 1;
`
_=buf
- err = d.Exec(string(buf) + ";" + script)
+ _, err = d.Exec(string(buf) + ";" + script, true)
if err != nil {
t.Fatalf("%v", err)
}
- res, err := d.Export("numberOne+1")
+ res, err := d.Exec("numberOne+1", false)
t.Logf("res=%v", res)
if err != nil {
t.Fatalf("%v", err)
@@ -111,13 +122,13 @@
`
d := NewDomino(simpleHTML)
d.Start()
- err = d.Exec(SCRIPT)
+ _, err = d.Exec(SCRIPT, true)
if err != nil {
t.Fatalf(err.Error())
}
time.Sleep(2 * time.Second)
- res, err := d.Export("$('h1').html()")
+ res, err := d.Exec("$('h1').html()", false)
if err != nil {
t.Fatalf(err.Error())
}
@@ -148,13 +159,13 @@
`
d := NewDomino(simpleHTML)
d.Start()
- err = d.Exec(SCRIPT)
+ _, err = d.Exec(SCRIPT, true)
if err != nil {
t.Fatalf(err.Error())
}
//time.Sleep(2 * time.Second)
- res, err := d.Export("$('h1').html()")
+ res, err := d.Exec("$('h1').html()", false)
if err != nil {
t.Fatalf(err.Error())
}
@@ -161,6 +172,10 @@
if res != "Hello" {
t.Fatalf(res)
}
+
+ if _, _, err = d.TrackChanges(); err != nil {
+ t.Fatalf(err.Error())
+ }
_, changed, err := d.TriggerClick("h1")
if err != nil {
t.Fatalf(err.Error())
@@ -168,7 +183,7 @@
if changed {
t.Fatal()
}
- res, err = d.Export("clicked")
+ res, err = d.Exec("clicked", false)
if err != nil {
t.Fatalf(err.Error())
}
@@ -207,13 +222,13 @@
`
d := NewDomino(simpleHTML)
d.Start()
- err = d.Exec(SCRIPT)
+ _, err = d.Exec(SCRIPT, true)
if err != nil {
t.Fatalf(err.Error())
}
time.Sleep(2 * time.Second)
- res, err := d.Export("$('h1').html()")
+ res, err := d.Exec("$('h1').html()", false)
if err != nil {
t.Fatalf(err.Error())
}
@@ -221,7 +236,7 @@
t.Fatalf(res)
}*/
_=res
- res, err = d.Export("$('h1').html('minor updates :-)'); $('h1').html();")
+ res, err = d.Exec("$('h1').html('minor updates :-)'); $('h1').html();", false)
if err != nil {
t.Fatalf(err.Error())
}
@@ -233,10 +248,14 @@
func TestTrackChanges(t *testing.T) {
d := NewDomino(simpleHTML)
d.Start()
- err := d.Exec(``)
+ _, err := d.Exec(``, true)
if err != nil {
t.Fatalf(err.Error())
}
+ // 0th time: init
+ if _, _, err = d.TrackChanges(); err != nil {
+ t.Fatalf(err.Error())
+ }
// 1st time: no change
html, changed, err := d.TrackChanges()
if err != nil {
@@ -259,7 +278,7 @@
if changed == true {
t.Fatal()
}
- _, err = d.Export("document.getElementById('title').innerHTML='new title'; true;")
+ _, err = d.Exec("document.getElementById('title').innerHTML='new title'; true;", false)
if err != nil {
t.Fatalf(err.Error())
}
@@ -280,7 +299,7 @@
d.Stop()
}
-func TestExecInlinedScripts(t *testing.T) {
+/*func TestExecInlinedScripts(t *testing.T) {
const h = `
<html>
<body>
@@ -305,9 +324,9 @@
t.Fatalf(res)
}
d.Stop()
-}
+}*/
-func TestWindowEqualsGlobal(t *testing.T) {
+/*func TestWindowEqualsGlobal(t *testing.T) {
const h = `
<html>
<body>
@@ -347,7 +366,7 @@
t.Fatalf(res)
}
d.Stop()
-}
+}*/
func TestES6(t *testing.T) {
d := NewDomino(simpleHTML)
@@ -356,11 +375,11 @@
console.log('Hello!!');
const numberOne = 1;
`
- err := d.Exec6(script)
+ _, err := d.Exec6(script)
if err != nil {
t.Fatalf("%v", err)
}
- res, err := d.Export("numberOne+1")
+ res, err := d.Exec("numberOne+1", false)
t.Logf("res=%v", res)
if err != nil {
t.Fatalf("%v", err)
@@ -377,11 +396,11 @@
script := `
console.log('Hello!!')
`
- err := d.Exec(script)
+ _, err := d.Exec(script, true)
if err != nil {
t.Fatalf("%v", err)
}
- res, err := d.Export("window === window.parent")
+ res, err := d.Exec("window === window.parent", false)
t.Logf("res=%v", res)
if err != nil {
t.Fatalf("%v", err)
--- a/domino/main.js
+++ /dev/null
@@ -1,23 +1,0 @@
-
- global = {};
- //global.__domino_frozen__ = true; // Must precede any require('domino')
- var domino = require('domino-lib/index');
- var Element = domino.impl.Element; // etc
-
- // JSDOM also knows the style tag
- // https://github.com/jsdom/jsdom/issues/2485
- Object.assign(this, domino.createWindow(s.html, 'http://example.com'));
- window = this;
- window.parent = window;
- window.top = window;
- window.self = window;
- addEventListener = function() {};
- window.location.href = 'http://example.com';
- navigator = {};
- HTMLElement = domino.impl.HTMLElement;
- // Fire DOMContentLoaded
- // to trigger $(document)readfy!!!!!!!
- document.close();
-
- console.log('Hello!!')
-
\ No newline at end of file