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
}
--
⑨