ref: b0f057869b6c4fe492224a134c83403469e24f14
parent: 833decef052e263ed371d55774d4598e1bd17016
author: Philip Silva <philip.silva@protonmail.com>
date: Fri Jan 22 17:11:34 EST 2021
css property inheritance
--- a/browser/browser.go
+++ b/browser/browser.go
@@ -160,7 +160,7 @@
if err != nil { return nil, fmt.Errorf("read image %v: %v", xml, err)}
-
+
goto img_elem
} else { return nil, fmt.Errorf("img svg %v: %v", xml, err)@@ -219,7 +219,7 @@
if stashElements {existingEl, ok := ui.(*Element)
- if ok && existingEl != nil {+ if ok && existingEl != nil && n == existingEl.n { return &Element{UI: existingEl.UI,
n: existingEl.n,
@@ -424,6 +424,14 @@
}
func (el *Element) Mouse(dui *duit.DUI, self *duit.Kid, m draw.Mouse, origM draw.Mouse, orig image.Point) (r duit.Result) {+ if m.Buttons == 2 {+ if el == nil {+ log.Infof("inspect nil element")+ } else {+ log.Infof("inspect el %+v %+v %+v", el, el.n, el.UI)+ }
+ }
+
if el == nil {return
}
@@ -572,7 +580,6 @@
}
}
flushCurrentRow()
-
if len(rows) == 0 {return nil
} else if len(rows) == 1 {--- a/browser/browser_test.go
+++ b/browser/browser_test.go
@@ -20,6 +20,7 @@
ExperimentalJsInsecure = &js
logger.Init()
SetLogger(&logger.Logger{})+ style.Init(nil, &logger.Logger{})}
type item struct {@@ -160,5 +161,63 @@
})
if numInputs != 1 {t.Fail()
+ }
+}
+
+func TestInlining(t *testing.T) {+ htm := `
+ <body>
+ <span id="outer">(<a href="http://example.com"><span>example.com</span></a></span>
+ </body>
+ `
+ doc, err := html.ParseWithOptions(
+ strings.NewReader(string(htm)),
+ html.ParseOptionEnableScripting(false),
+ )
+ if err != nil {+ t.Fatalf(err.Error())
+ }
+ body := grep(doc, "body")
+ b := &Browser{}+ b.client = &http.Client{}+ browser = b
+ u, err := url.Parse("https://example.com")+ if err != nil {+ log.Fatalf("parse: %v", err)+ }
+ b.History.Push(u)
+ nm, err := style.FetchNodeMap(doc, style.AddOnCSS, 1280)
+ if err != nil {+ log.Fatalf("FetchNodeMap: %v", err)+ }
+ nt := nodes.NewNodeTree(body, style.Map{}, nm, nil)+ boxed := NodeToBox(0, b, nt)
+
+ // 1. nodes are row-like
+ outerSpan := nt.Find("span")+ if outerSpan.Attr("id") != "outer" || len(outerSpan.Children) != 2 || outerSpan.IsFlex() {+ t.Fatalf(" node")+ }
+ bracket := outerSpan.Children[0]
+ if bracket.Data() != "(" || !bracket.IsInline() {+ t.Errorf("bracket, is inline: %v", bracket.IsInline())+ }
+ a := outerSpan.Children[1]
+ if a.Data() != "a" || !a.IsInline() {+ t.Errorf("a, is inline: %v, %+v %+v", a.IsInline(), a, nil)+ }
+
+ // 2. Elements are row-like
+ box := boxed.UI.(*Element).UI.(*duit.Box)
+ if len(box.Kids) != 2 {+ t.Errorf("box: %+v", box)+ }
+ bel := box.Kids[0].UI.(*Element)
+ ael := box.Kids[1].UI.(*Element)
+ if bel.n.Data() != "(" {+ t.Errorf("bel: %+v", bel)+ }
+ if ael.n.Data() != "a" {+ t.Errorf("ael: %+v %+v", ael, ael.n)}
}
--- a/nodes/nodes.go
+++ b/nodes/nodes.go
@@ -3,6 +3,7 @@
import (
"bytes"
"golang.org/x/net/html"
+ "github.com/chris-ramon/douceur/css"
"github.com/psilva261/opossum/logger"
"github.com/psilva261/opossum/style"
"strings"
@@ -27,13 +28,23 @@
// NewNodeTree propagates the cascading styles to the leaves
//
-// First applies the global style and at the end the local style attribute's style is attached.
-func NewNodeTree(doc *html.Node, cs style.Map, nodeMap map[*html.Node]style.Map, parent *Node) (n *Node) {- ncs := cs
+// First applies the parent style and at the end the local style attribute's style is attached.
+func NewNodeTree(doc *html.Node, ps style.Map, nodeMap map[*html.Node]style.Map, parent *Node) (n *Node) {+ ncs := style.Map{+ Declarations: make(map[string]css.Declaration),
+ }
+ ncs = ps.ApplyChildStyle(ncs, false)
+ // add from matching selectors
+ // (keep only inheriting properties from parent node)
if m, ok := nodeMap[doc]; ok {- ncs = ncs.ApplyChildStyle(m)
+ ncs = ncs.ApplyChildStyle(m, false)
}
- ncs = ncs.ApplyChildStyle(style.NewMap(doc))
+
+ // add style attribute
+ // (keep all properties that already match)
+ styleAttr := style.NewMap(doc)
+ ncs = ncs.ApplyChildStyle(styleAttr, true)
+
data := doc.Data
if doc.Type == html.ElementNode {data = strings.ToLower(data)
@@ -51,6 +62,13 @@
if doc.Type == html.TextNode {n.Text = filterText(doc.Data)
+ n.Map = style.Map{+ Declarations: make(map[string]css.Declaration),
+ }
+ n.Map.Declarations["display"] = css.Declaration{+ Property: "display",
+ Value: "inline",
+ }
}
i := 0
for c := doc.FirstChild; c != nil; c = c.NextSibling {@@ -233,9 +251,9 @@
},
}
}
-
+
n.Children[0].Text = t
n.Children[0].DomSubtree.Data = t
-
+
return
}
--- a/nodes/nodes_test.go
+++ b/nodes/nodes_test.go
@@ -47,3 +47,31 @@
t.Fatalf(s)
}
}
+
+func TestNewNodeTree(t *testing.T) {+ buf := strings.NewReader(`
+ <html>
+ <body style="width: 900px; height: 700px; font-size: 12px">
+ <p>
+ <b style="height: 100px;">bold stuff</b>
+ </p>
+ </body>
+ </html>`)
+ doc, err := html.Parse(buf)
+ if err != nil { t.Fatalf(err.Error()) }+ n := NewNodeTree(doc, style.Map{}, make(map[*html.Node]style.Map), nil)+ body := n.Find("body")+ bodyW := body.Map.Css("width")+ bodyH := body.Map.Css("height")+ bodyF := body.Map.Css("font-size")+ if bodyW != "900px" || bodyH != "700px"/* || bodyF != "12px"*/ {+ t.Fatalf("<%v> w=%v h=%v f=%v", body.Data(), bodyW, bodyH, bodyF)+ }
+ b := n.Find("b")+ bW := b.Map.Css("width")+ bH := b.Map.Css("height")+ bF := b.Map.Css("font-size")+ if bW != "" || bH != "100px"/* || bF != "12px"*/ {+ t.Fatalf("<%v> w=%v h=%v f=%v", b.Data(), bW, bH, bF)+ }
+}
--- a/style/stylesheets.go
+++ b/style/stylesheets.go
@@ -42,7 +42,7 @@
display: inline-block;
}
-h1, h2, h3, h4. h5, h6, div, center, frame, frameset, p, ul, menu, pre, dir, dl, dd, dt {+h1, h2, h3, h4, h5, h6, div, center, frame, frameset, p, ul, menu, pre, dir, dl, dd, dt {display: block;
}
@@ -109,7 +109,7 @@
// "zero" valued Map if it doesn't exist yet
initial := m[n]
- m[n] = initial.ApplyChildStyle(mp)
+ m[n] = initial.ApplyChildStyle(mp, true)
}
}
@@ -241,40 +241,24 @@
return s
}
-func (cs Map) ApplyChildStyle(ccs Map) (res Map) {+func (cs Map) ApplyChildStyle(ccs Map, copyAll bool) (res Map) {res.Declarations = make(map[string]css.Declaration)
for k, v := range cs.Declarations {- res.Declarations[k] = v
- }
- // overwrite with higher prio child props
- for k, v := range ccs.Declarations { switch k {- /*case "height", "width":
- parentL, ok := res.Declarations[k]
- if ok && strings.HasSuffix(v.Value, "%") && strings.HasSuffix(parentL.Value, "px") {- parentLNum, err := strconv.Atoi(strings.TrimSuffix(parentL.Value, "px"))
- if err != nil {- log.Errorf("atoi: %v", err)- continue
- }
- percentNum, err := strconv.ParseFloat(strings.TrimSuffix(v.Value, "%"), 64)
- if err != nil {- log.Errorf("atoi: %v", err)- continue
- }
- prod := int(percentNum * float64(parentLNum) / 100.0)
- res.Declarations[k] = css.Declaration{- Property: k,
- Value: fmt.Sprintf("%vpx", prod),- }
+ // https://www.w3.org/TR/CSS21/propidx.html
+ case "azimuth", "border-collapse", "border-spacing", "caption-side", "color", "cursor", "direction", "elevation", "empty-cells", "font-family", "font-size", "font-style", "font-variant", "font-weight", "font", "letter-spacing", "line-height", "list-style-image", "list-style-position", "list-style-type", "list-style", "orphans", "pitch-range", "pitch", "quotes", "richness", "speak-header", "speak-numeral", "speak-punctuation", "speak", "speech-rate", "stress", "text-align", "text-indent", "text-transform", "visibility", "voice-family", "volume", "white-space", "widows", "word-spacing":
+ default:
+ if !copyAll {continue
}
- fallthrough*/
- default:
- res.Declarations[k] = v
}
+ res.Declarations[k] = v
}
+ // overwrite with higher prio child props
+ for k, v := range ccs.Declarations {+ res.Declarations[k] = v
+ }
return
}
@@ -528,7 +512,9 @@
return f, unit, fmt.Errorf("unknown suffix: %v", l)}
- f = float64(dui.Scale(int(f)))
+ if dui != nil {+ f = float64(dui.Scale(int(f)))
+ }
return
}
--- a/style/stylesheets_test.go
+++ b/style/stylesheets_test.go
@@ -103,7 +103,7 @@
}
if w == 400 {- _ =m[body][0]
+ _ =m[body][0]
if m[body][0].Declarations[0].Value != "lightblue" {t.Fail()
}
@@ -133,6 +133,18 @@
t.Logf("m=%+v", m)}
+func TestSmaller(t *testing.T) {+ d := css.Declaration{+ Important: false,
+ }
+ dd := css.Declaration{+ Important: true,
+ }
+ if !smaller(d, dd) {+ t.Fail()
+ }
+}
+
func TestApplyChildStyleInherit(t *testing.T) { parent := Map{Declarations: make(map[string]css.Declaration),
@@ -145,30 +157,29 @@
Declarations: make(map[string]css.Declaration),
}
- res := parent.ApplyChildStyle(child)
+ res := parent.ApplyChildStyle(child, true)
if v := res.Declarations["height"].Value; v != "80px" {t.Fatalf(v)
}
}
-/*func TestApplyChildStyleMultiply(t *testing.T) {- parent := Map{- Declarations: make(map[string]css.Declaration),
+func TestLength(t *testing.T) {+ lpx := map[string]float64{+ "auto": 0.0,
+ "inherit": 0.0,
+ "17px": 17.0,
+ "10em": 110.0,
+ "10vw": 128.0,
+ "10vh": 108.0,
+ "10%": 0,
}
- parent.Declarations["height"] = css.Declaration{- Property: "height",
- Value: "80px",
+ for l, px := range lpx {+ f, _, err := length(l)
+ if err != nil {+ t.Fatalf("%v: %v", l, err)+ }
+ if f != px {+ t.Fatalf("expected %v but got %v", px, f)+ }
}
- child := Map{- Declarations: make(map[string]css.Declaration),
- }
- child.Declarations["height"] = css.Declaration{- Property: "height",
- Value: "50%",
- }
-
- res := parent.ApplyChildStyle(child)
- if v := res.Declarations["height"].Value; v != "40px" {- t.Fatalf(v)
- }
-}*/
+}
--
⑨