shithub: opossum

Download patch

ref: 39c7730e28dfbd5ee1b8a27ddf90bc7be493c169
parent: b4e4fc28cbe113df49a10bac4333d064aed989eb
author: Philip Silva <philip.silva@protonmail.com>
date: Fri Jan 1 21:14:49 EST 2021

Minor optimizations

--- a/README.md
+++ b/README.md
@@ -86,4 +86,4 @@
 - load images on the fly
 - implement more parts of HTML5 and CSS
 - create a widget for div/span
-- clean up code, support webfs, snarf,
+- clean up code, support webfs, snarf, font sizes vs. hidpi
--- a/browser/browser.go
+++ b/browser/browser.go
@@ -48,7 +48,6 @@
 	opossum.ContentType
 	buf []byte
 })
-var numElements int64
 var log *logger.Logger
 var scroller *duit.Scroll
 var display *draw.Display
@@ -525,16 +524,15 @@
 		} else if len(rows[0]) == 1 {
 			return rows[0][0]
 		}
-		numElements++
+
 		return NewElement(horizontalSeq(true, rows[0]), n)
 	} else {
 		seqs := make([]*Element, 0, len(rows))
 		for _, row := range rows {
 			seq := horizontalSeq(true, row)
-			numElements++
 			seqs = append(seqs, NewElement(seq, n))
 		}
-		numElements++
+
 		return NewElement(verticalSeq(seqs), n)
 	}
 }
@@ -560,12 +558,11 @@
 	}
 
 	if wrap {
-		log.Printf("wrap")
 		finalUis := make([]duit.UI, 0, len(uis))
 		for _, ui := range uis {
-			log.Printf("wrap, tree:")
 			PrintTree(ui)
 			el, ok := ui.(*Element)
+
 			if ok {
 				label, isLabel := el.UI.(*duit.Label)
 				if isLabel {
@@ -583,6 +580,7 @@
 				finalUis = append(finalUis, ui)
 			}
 		}
+
 		return &duit.Box{
 			Padding: duit.SpaceXY(6, 4),
 			Margin:  image.Pt(6, 4),
@@ -696,6 +694,7 @@
 
 	if useOneGrid {
 		uis := make([]duit.UI, 0, numRows*numCols)
+
 		for _, row := range t.rows {
 			for _, td := range row.columns {
 				uis = append(uis, NodeToBox(r+1, b, td))
@@ -702,7 +701,6 @@
 			}
 		}
 
-		log.Printf("use on grid")
 		halign := make([]duit.Halign, 0, len(uis))
 		valign := make([]duit.Valign, 0, len(uis))
 
@@ -722,9 +720,8 @@
 			n,
 		)
 	} else {
-		log.Printf("combine")
-
 		seqs := make([]*Element, 0, len(t.rows))
+
 		for _, row := range t.rows {
 			rowEls := make([]*Element, 0, len(row.columns))
 			for _, col := range row.columns {
@@ -735,14 +732,12 @@
 				}
 			}
 
-			log.Printf("len rowsEls=%v", len(rowEls))
 			if len(rowEls) > 0 {
 				seq := horizontalSeq(false, rowEls)
-				numElements++
 				seqs = append(seqs, NewElement(seq, row.n))
 			}
 		}
-		numElements++
+
 		return NewElement(verticalSeq(seqs), n)
 	}
 }
@@ -800,6 +795,7 @@
 	if attr(*n.DomSubtree, "aria-hidden") == "true" || hasAttr(*n.DomSubtree, "hidden") {
 		return nil
 	}
+
 	if n.IsDisplayNone() {
 		return nil
 	}
@@ -809,7 +805,6 @@
 		case "style", "script", "template":
 			return nil
 		case "input":
-			numElements++
 			t := attr(*n.DomSubtree, "type")
 			if isPw := t == "password"; t == "text" || t == "" || t == "search" || isPw {
 				return NewInputField(n)
@@ -819,7 +814,6 @@
 				return nil
 			}
 		case "button":
-			numElements++
 			if t := attr(*n.DomSubtree, "type"); t == "" || t == "submit" {
 				return NewSubmitButton(b, n)
 			} else {
@@ -833,7 +827,6 @@
 				)
 			}
 		case "table":
-			numElements++
 			return NewTable(n).Element(r+1, b, n)
 		case "noscript":
 			if *ExperimentalJsInsecure || !*EnableNoScriptTag {
@@ -855,19 +848,16 @@
 				innerContent = InnerNodesToBox(r+1, b, n)
 			}
 
-			numElements++
 			return NewBoxElement(
 				innerContent,
 				n,
 			)
 		case "img", "svg":
-			numElements++
 			return NewElement(
 				NewImage(n),
 				n,
 			)
 		case "pre":
-			numElements++
 			return NewElement(
 				NewCodeView(nodes.ContentFrom(*n), n.Map),
 				n,
@@ -878,7 +868,7 @@
 				t := nodes.ContentFrom(*n)
 
 				if ul := n.Ancestor("ul"); ul != nil {
-					if s, ok := ul.Map.Declarations["list-style"]; !ok || s.Value != "none" {
+					if ul.Css("list-style") != "none" && n.Css("list-style-type") != "none" {
 						t = "• " + t
 					}
 				}
@@ -893,7 +883,6 @@
 				innerContent = InnerNodesToBox(r+1, b, n)
 			}
 
-			numElements++
 			return NewElement(
 				innerContent,
 				n,
@@ -913,16 +902,15 @@
 			} else {
 				innerContent = InnerNodesToBox(r+1, b, n)
 			}
-			numElements++
+
 			if innerContent == nil {
 				return nil
 			}
+
 			el := NewElement(
 				innerContent,
 				n,
 			)
-			//      also a way to bubble up
-			// will be needed eventually
 			el.makeLink(href)
 			return el
 		default:
@@ -937,17 +925,19 @@
 			text = strings.ReplaceAll(text, "\t", "")
 			l := strings.Split(text, " ")
 			nn := make([]string, 0, len(l))
+
 			for _, w := range l {
 				if w != "" {
 					nn = append(nn, w)
 				}
 			}
+
 			text = strings.Join(nn, " ")
 			ui := &duit.Label{
 				Text: text,
 				Font: n.Font(),
 			}
-			numElements++
+
 			return NewElement(
 				ui,
 				n,
@@ -961,23 +951,21 @@
 }
 
 func InnerNodesToBox(r int, b *Browser, n *nodes.Node) *Element {
-	childrenAsEls := make([]*Element, 0, len(n.Children))
+	els := make([]*Element, 0, len(n.Children))
 
 	for _, c := range n.Children {
-		el := NodeToBox(r+1, b, c)
-		if el != nil && !c.IsDisplayNone() {
-			numElements++
-			childrenAsEls = append(childrenAsEls, el)
+		if el := NodeToBox(r+1, b, c); el != nil && !c.IsDisplayNone() {
+			els = append(els, el)
 		}
 	}
 
-	if len(childrenAsEls) == 0 {
+	if len(els) == 0 {
 		return nil
-	} else if len(childrenAsEls) == 1 {
-		return childrenAsEls[0]
+	} else if len(els) == 1 {
+		return els[0]
 	}
 
-	return Arrange(n, childrenAsEls...)
+	return Arrange(n, els...)
 }
 
 func TraverseTree(ui duit.UI, f func(ui duit.UI)) {
@@ -1077,7 +1065,7 @@
 		}
 		fmt.Printf("ColoredLabel %v\n", t)
 	default:
-		fmt.Printf("default :-) %+v\n", v)
+		fmt.Printf("%+v\n", v)
 	}
 }
 
@@ -1300,7 +1288,9 @@
 	dui.MarkLayout(dui.Top.UI)
 	dui.MarkDraw(dui.Top.UI)
 	TraverseTree(b.Website.UI, func(ui duit.UI) {
-		// just checking
+		// just checking for nil elements. That would be a bug anyway and it's better
+		// to notice it before it gets rendered
+
 		if ui == nil {
 			panic("nil")
 		}
@@ -1321,6 +1311,7 @@
 			cache[uri.String()] = c
 		}
 	}
+
 	return c.buf, c.ContentType, err
 }
 
--- a/browser/website.go
+++ b/browser/website.go
@@ -150,10 +150,13 @@
 	body := grepBody(doc)
 
 	log.Printf("Layout website...")
-	numElements = 0
 	scroller = duit.NewScroll(
 		NodeToBox(0, browser, nodes.NewNodeTree(body, style.Map{}, nodeMap, &nodes.Node{})),
 	)
+	numElements := 0
+	TraverseTree(scroller, func(ui duit.UI) {
+		numElements++
+	})
 	w.UI = scroller
 	log.Printf("Layouting done (%v elements created)", numElements)
 	if numElements < 10 {
--- a/cmd/browse/main.go
+++ b/cmd/browse/main.go
@@ -127,15 +127,12 @@
 	}
 
 	style.Init(dui, log)
-
-	w := dui.Display.Windows.Bounds().Dx()
-	log.Printf("w=%v", w)
-	log.Printf("w'=%v", dui.Scale(w))
-	log.Printf("kid=%v", dui.Top.R)
 	browser.SetLogger(log)
+	domino.SetLogger(log)
 	img.SetLogger(log)
 	opossum.SetLogger(log)
 	nodes.SetLogger(log)
+
 	b := browser.NewBrowser(dui, *startPage)
 	b.Download = func(done chan int) chan string {
 		go func() {
@@ -144,7 +141,6 @@
 		}()
 		return confirm(b, fmt.Sprintf("Download %v", b.URL()), "/download.file")
 	}
-
 	render(b)
 
 	for {
@@ -151,7 +147,6 @@
 		select {
 		case e := <-dui.Inputs:
 			dui.Input(e)
-			//log.Printf("e=%+v", e)
 
 		case err, ok := <-dui.Error:
 			if !ok {
@@ -178,11 +173,12 @@
 			os.Exit(2)
 		}()
 	}
-	os.Chdir("../..")
+
 	log = logger.Log
 	log.Debug = *dbg
 	style.CssFonts = *cssFonts
 	style.ExperimentalUseBoxBackgrounds = *experimentalUseBoxBackgrounds
+
 	if err := Main(); err != nil {
 		log.Fatalf("Main: %v", err)
 	}
--- a/domino-lib/Document.js
+++ b/domino-lib/Document.js
@@ -368,7 +368,7 @@
   URL: { get: function URL() { return this._address; } },
   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') },
+  //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() {
@@ -428,18 +428,18 @@
     get: function() {
       return namedHTMLChild(this.documentElement, 'body');
     },
-    set: utils.nyi
+    set: utils.nyi.bind(this, 'body set')
   },
   // Return the first <head> child of the document element.
   head: { get: function() {
     return namedHTMLChild(this.documentElement, 'head');
   }},
-  images: { get: utils.nyi },
-  embeds: { get: utils.nyi },
-  plugins: { get: utils.nyi },
-  links: { get: utils.nyi },
-  forms: { get: utils.nyi },
-  scripts: { get: utils.nyi },
+  images: { get: utils.nyi.bind(this, 'images get') },
+  embeds: { get: utils.nyi.bind(this, 'embeds get') },
+  plugins: { get: utils.nyi.bind(this, 'plugins get') },
+  links: { get: utils.nyi.bind(this, 'links get') },
+  forms: { get: utils.nyi.bind(this, 'forms get') },
+  scripts: { get: utils.nyi.bind(this, 'scripts get') },
   applets: { get: function() { return []; } },
   activeElement: { get: function() { return null; } },
   innerHTML: {
--- a/domino/domino.go
+++ b/domino/domino.go
@@ -9,8 +9,8 @@
 	"github.com/jvatic/goja-babel"
 	"golang.org/x/net/html"
 	"io/ioutil"
-	"log"
 	"opossum"
+	"opossum/logger"
 	"opossum/nodes"
 	"strconv"
 	"strings"
@@ -18,7 +18,13 @@
 )
 
 var DebugDumpJS *bool
+var log *logger.Logger
+var timeout = 10*time.Second
 
+func SetLogger(l *logger.Logger) {
+	log = l
+}
+
 type Domino struct {
 	initialized bool
 	loop       *eventloop.EventLoop
@@ -90,7 +96,7 @@
 	if maxWidth > len(code) {
 		maxWidth = len(code)
 	}
-	fmt.Printf("js code: %v\n", code[:maxWidth])
+	log.Infof("js code: %v", code[:maxWidth])
 }
 
 func (d *Domino) Exec(script string, initial bool) (res string, err error) {
@@ -116,6 +122,12 @@
 		window.getComputedStyle = function() {
 			// stub
 		}
+		window.screen = {
+			width: 1280,
+			height: 1024
+		};
+		window.screenX = 0;
+		window.screenY = 25;
 		location = window.location;
 		navigator = {
 			platform: 'plan9(port)',
@@ -134,24 +146,31 @@
 	}
 
 	ready := make(chan goja.Value)
-	errChan := make(chan error)
+	errCh := make(chan error)
+	intCh := make(chan int)
 	go func() {
 		d.loop.RunOnLoop(func(vm *goja.Runtime) {
 			log.Printf("RunOnLoop")
+
 			if initial {
+				// find domino-lib folder
 				registry := require.NewRegistry(
-					require.WithGlobalFolders(".", ".."),
+					require.WithGlobalFolders(
+						".",     // standalone
+						"..",    // tests
+						"../..", // go run
+					),
 				)
+
 				console.Enable(vm)
-				req := registry.Enable(vm)
-				_ = req
+				registry.Enable(vm)
 
-				vm.SetFieldNameMapper(goja.TagFieldNameMapper("json", true))
 				type S struct {
 					Buf  string `json:"buf"`
 					HTML string `json:"html"`
 				}
 
+				vm.SetFieldNameMapper(goja.TagFieldNameMapper("json", true))
 				vm.Set("s", S{
 					HTML: d.html,
 					Buf:  "yolo",
@@ -158,28 +177,46 @@
 				})
 			}
 
+			go func() {
+				for _ = range intCh {
+					vm.Interrupt("halt")
+				}
+			}()
+
 			vv, err := vm.RunString(SCRIPT)
 			if err != nil {
 				IntrospectError(err, script)
-				errChan <- fmt.Errorf("run program: %w", err)
+				errCh <- fmt.Errorf("run program: %w", err)
 			} else {
 				ready <- vv
 			}
 		})
 	}()
-	select {
-		case v := <-ready:
-			<-time.After(10 * time.Millisecond)
-			if v != nil {
-				res = v.String()
-			}
-			if err == nil { d.initialized=true }
-		case er := <- errChan:
-			err = fmt.Errorf("event loop: %w", er)
+
+	for {
+		select {
+			case v := <-ready:
+				log.Infof("ready")
+				<-time.After(10 * time.Millisecond)
+				if v != nil {
+					res = v.String()
+				}
+				if err == nil { d.initialized=true }
+				goto cleanup
+			case er := <- errCh:
+				log.Infof("err")
+				err = fmt.Errorf("event loop: %w", er)
+				goto cleanup
+			case <-time.After(timeout):
+				log.Errorf("Interrupt JS after %v", timeout)
+				intCh <- 1
+		}
 	}
 
+cleanup:
 	close(ready)
-	close(errChan)
+	close(errCh)
+	close(intCh)
 
 	return
 }
@@ -283,11 +320,11 @@
 
 	iterateJsElements(doc, func(src, inlineCode string) {
 		if strings.TrimSpace(inlineCode) != "" {
-			fmt.Printf("domino.Scripts: inline code:\n")
+			log.Infof("domino.Scripts: inline code:")
 			printCode(inlineCode, 20)
 			codes = append(codes, inlineCode)
 		} else if c, ok := downloads[src]; ok {
-			fmt.Printf("domino.Scripts: referenced code (%v)\n", src)
+			log.Infof("domino.Scripts: referenced code (%v)", src)
 			codes = append(codes, c)
 		}
 	})
--- a/domino/domino_test.go
+++ b/domino/domino_test.go
@@ -2,6 +2,7 @@
 
 import (
 	"io/ioutil"
+	"opossum/logger"
 	"strings"
 	"testing"
 	"time"
@@ -18,6 +19,9 @@
 func init() {
 	t := true
 	DebugDumpJS = &t
+	logger.Quiet = &t
+	logger.Init()
+	log = &logger.Logger{Debug: true}
 }
 
 func TestSimple(t *testing.T) {
--- a/style/experimental.go
+++ b/style/experimental.go
@@ -94,7 +94,7 @@
 	if !ok {
 		decl, ok = cs.Declarations["background"]
 	}
-	log.Printf("decl=%+v\n", decl)
+
 	if ok {
 		imgUrl, ok := backgroundImageUrl(decl)
 		if !ok {
@@ -101,18 +101,19 @@
 			log.Printf("bg img not ok")
 			return
 		}
+
 		w := cs.Width()
 		h := cs.Height()
-		log.Printf("bg img ok")
+
 		r, err := img.Load(fetcher, imgUrl, w, h)
 		if err != nil {
 			log.Errorf("bg img load %v: %v", imgUrl, err)
 			return nil
 		}
-		log.Printf("Read %v...", imgUrl)
+
 		i, err = duit.ReadImage(dui.Display, r)
 		if err != nil {
-			log.Errorf("bg read image: %v", err)
+			log.Errorf("bg read image %v: %v", imgUrl, err)
 			return
 		}
 		return i
--- a/style/stylesheets.go
+++ b/style/stylesheets.go
@@ -31,6 +31,8 @@
 var rMinWidth = regexp.MustCompile(`min-width: (\d+)px`)
 var rMaxWidth = regexp.MustCompile(`max-width: (\d+)px`)
 
+const FontBaseSize = 14.0
+
 const AddOnCSS = `
 a, span, i, tt, b {
   display: inline;
@@ -225,17 +227,25 @@
 	for _, a := range n.Attr {
 		if a.Key == "style" {
 			decls, err := parser.ParseDeclarations(a.Val)
+
 			if err != nil {
 				log.Printf("could not parse '%v'", a.Val)
 				break
 			}
+
 			for _, d := range decls {
 				s.Declarations[d.Property] = *d
 			}
 		} else if a.Key == "height" || a.Key == "width" {
+			v := a.Val
+
+			if !strings.HasSuffix(v, "%") {
+				v += "px"
+			}
+
 			s.Declarations[a.Key] = css.Declaration{
 				Property: a.Key,
-				Value: a.Val+"px",
+				Value: v,
 			}
 		} else if a.Key == "bgcolor" {
 			s.Declarations["background-color"] = css.Declaration{
@@ -332,21 +342,21 @@
 func (cs Map) FontSize() float64 {
 	fs, ok := cs.Declarations["font-size"]
 	if !ok || fs.Value == "" {
-		return 14
+		return FontBaseSize
 	}
 
 	if len(fs.Value) <= 2 {
 		log.Printf("error parsing font size %v", fs.Value)
-		return 14.0
+		return FontBaseSize
 	}
 	numStr := fs.Value[0 : len(fs.Value)-2]
 	f, err := strconv.ParseFloat(numStr, 64)
 	if err != nil {
 		log.Printf("error parsing font size %v", fs.Value)
-		return 14.0
+		return FontBaseSize
 	}
 	if strings.HasSuffix(fs.Value, "em") {
-		f *= 14.0
+		f *= FontBaseSize
 	}
 	return f
 }
@@ -512,9 +522,11 @@
 
 func length(l string) (f float64, unit string, err error) {
 	var s string
-	if s == "auto" {
+
+	if l == "auto" {
 		return 0, "px", nil
 	}
+
 	for _, suffix := range []string{"px", "%", "rem", "em"} {
 		if strings.HasSuffix(l, suffix) {
 			s = strings.TrimSuffix(l, suffix)
@@ -522,12 +534,18 @@
 			break
 		}
 	}
-	if unit == "" {
+
+	switch unit {
+	case "":
 		return f, unit, fmt.Errorf("unknown suffix: %v", l)
-	}
-	if unit == "px" {
+	case "px", "em":
 		f, err = strconv.ParseFloat(s, 64)
 	}
+
+	if unit == "em" {
+		f *= FontBaseSize
+	}
+
 	return
 }
 
@@ -553,4 +571,12 @@
 		return int(f)
 	}
 	return 0
+}
+
+func (cs Map) Css(propName string) string {
+	d, ok := cs.Declarations[propName]
+	if !ok {
+		return ""
+	}
+	return d.Value
 }