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
}