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)
- }
-}*/
+}