shithub: opossum

Download patch

ref: c9faa5d093ef6a890e00fb92880ab1a2edf9801c
parent: 45d380a2ac7ab73fb8bf38cf3f261189529dc926
author: Philip Silva <philip.silva@protonmail.com>
date: Wed Dec 16 14:46:24 EST 2020

Correctly handle noscript fallthrough

--- a/browser/browser.go
+++ b/browser/browser.go
@@ -49,6 +49,7 @@
 var numElements int64
 var log *logger.Logger
 var scroller *duit.Scroll
+var display *draw.Display
 
 func SetLogger(l *logger.Logger) {
 	log = l
@@ -122,8 +123,8 @@
 	src string
 }
 
-func NewImage(display *draw.Display, n nodes.Node) duit.UI {
-	img, err := newImage(display, n)
+func NewImage(n nodes.Node) duit.UI {
+	img, err := newImage(n)
 	if err != nil {
 		log.Errorf("could not load image: %v", err)
 		return &duit.Label{}
@@ -131,7 +132,11 @@
 	return img
 }
 
-func newImage(display *draw.Display, n nodes.Node) (ui duit.UI, err error) {
+func newImage(n nodes.Node) (ui duit.UI, err error) {
+	if display == nil {
+		// probably called from a unit test
+		return nil, fmt.Errorf("display nil")
+	}
 	src := attr(*n.DomSubtree, "src")
 	if src == "" {
 		return nil, fmt.Errorf("no src in %+v", n.Attr)
@@ -604,11 +609,11 @@
 	}
 }
 
-func RichInnerContentFrom(r int, b *Browser, display *draw.Display, n *nodes.Node) *Element {
+func RichInnerContentFrom(r int, b *Browser, n *nodes.Node) *Element {
 	childrenAsEls := make([]*Element, 0, 1)
 
 	for _, c := range n.Children {
-		tmp := NodeToBox(r+1, b, display, c)
+		tmp := NodeToBox(r+1, b, c)
 		if tmp != nil {
 			numElements++
 			el := NewElement(tmp, c.Map.ApplyChildStyle(style.TextNode))
@@ -683,7 +688,7 @@
 	return
 }
 
-func (t *Table) Element(r int, b *Browser, display *draw.Display, cs style.Map) *Element {
+func (t *Table) Element(r int, b *Browser, cs style.Map) *Element {
 	numRows := len(t.rows)
 	numCols := t.numColsMax()
 	useOneGrid := t.numColsMin() == t.numColsMax()
@@ -692,7 +697,7 @@
 		uis := make([]duit.UI, 0, numRows*numCols)
 		for _, row := range t.rows {
 			for _, td := range row.columns {
-				uis = append(uis, NodeToBox(r+1, b, display, td))
+				uis = append(uis, NodeToBox(r+1, b, td))
 			}
 		}
 
@@ -722,7 +727,7 @@
 		for _, row := range t.rows {
 			rowEls := make([]*Element, 0, len(row.columns))
 			for _, col := range row.columns {
-				ui := NodeToBox(r+1, b, display, col)
+				ui := NodeToBox(r+1, b, col)
 				if ui != nil {
 					el := NewElement(ui, col.Map)
 					rowEls = append(rowEls, el)
@@ -788,7 +793,7 @@
 	return body
 }
 
-func NodeToBox(r int, b *Browser, display *draw.Display, n *nodes.Node) *Element {
+func NodeToBox(r int, b *Browser, n *nodes.Node) *Element {
 	if attr(*n.DomSubtree, "aria-hidden") == "true" || hasAttr(*n.DomSubtree, "hidden") {
 		return nil
 	}
@@ -800,11 +805,6 @@
 		switch n.Data() {
 		case "style", "script", "svg", "template":
 			return nil
-		case "noscript":
-			if *ExperimentalJsInsecure {
-				return nil
-			}
-			fallthrough
 		case "input":
 			numElements++
 			t := attr(*n.DomSubtree, "type")
@@ -831,7 +831,12 @@
 			}
 		case "table":
 			numElements++
-			return NewTable(n).Element(r+1, b, display, n.Map)
+			return NewTable(n).Element(r+1, b, n.Map)
+		case "noscript":
+			if *ExperimentalJsInsecure {
+				return nil
+			}
+			fallthrough
 		case "body", "p", "h1", "center", "nav", "article", "header", "div", "td":
 			var innerContent duit.UI
 			if nodes.IsPureTextContent(*n) {
@@ -844,7 +849,7 @@
 					Map: n.Map.ApplyChildStyle(style.TextNode),
 				}
 			} else {
-				innerContent = RichInnerContentFrom(r+1, b, display, n)
+				innerContent = RichInnerContentFrom(r+1, b, n)
 			}
 
 			numElements++
@@ -855,7 +860,7 @@
 		case "img":
 			numElements++
 			return NewElement(
-				NewImage(display, *n),
+				NewImage(*n),
 				n.Map,
 			)
 		case "pre":
@@ -879,7 +884,7 @@
 					Map: n.Map,
 				}
 			} else {
-				innerContent = RichInnerContentFrom(r+1, b, display, n)
+				innerContent = RichInnerContentFrom(r+1, b, n)
 			}
 
 			numElements++
@@ -907,7 +912,7 @@
 			} else {
 				// TODO: make blue borders and different
 				//       mouse cursor and actually clickable
-				innerContent = RichInnerContentFrom(r+1, b, display, n)
+				innerContent = RichInnerContentFrom(r+1, b, n)
 			}
 			numElements++
 			if innerContent == nil {
@@ -926,7 +931,7 @@
 			// Internal node object
 			els := make([]*Element, 0, 10)
 			for _, c := range n.Children {
-				el := NodeToBox(r+1, b, display, c)
+				el := NodeToBox(r+1, b, c)
 				if el != nil && !c.IsDisplayNone() {
 					els = append(els, el)
 				}
@@ -1152,6 +1157,7 @@
 	browser = b
 	style.SetFetcher(b)
 	dui = _dui
+	display = dui.Display
 
 	b.layoutWebsite()
 
@@ -1485,7 +1491,7 @@
 	log.Printf("Layout website...")
 	numElements = 0
 	scroller = duit.NewScroll(
-		NodeToBox(0, b, b.dui.Display, nodes.NewNodeTree(body, style.Map{}, nodeMap, nil)),
+		NodeToBox(0, b, nodes.NewNodeTree(body, style.Map{}, nodeMap, nil)),
 	)
 	b.Website.UI = scroller
 	log.Printf("Layouting done (%v elements created)", numElements)
@@ -1492,7 +1498,7 @@
 	if numElements < 10 {
 		log.Errorf("Less than 10 elements layouted, seems css processing failed. Will layout without css")
 		scroller = duit.NewScroll(
-			NodeToBox(0, b, b.dui.Display, nodes.NewNodeTree(body, style.Map{}, make(map[*html.Node]style.Map), nil)),
+			NodeToBox(0, b, nodes.NewNodeTree(body, style.Map{}, make(map[*html.Node]style.Map), nil)),
 		)
 		b.Website.UI = scroller
 	}
--- a/browser/browser_test.go
+++ b/browser/browser_test.go
@@ -1,12 +1,23 @@
 package browser
 
 import (
+	"github.com/mjl-/duit"
+	"golang.org/x/net/html"
+	"net/http"
 	"net/url"
 	"opossum/logger"
+	"opossum/nodes"
+	"opossum/style"
+	"strings"
 	"testing"
 )
 
 func init() {
+	quiet := false
+	logger.Quiet = &quiet
+	js := false
+	ExperimentalJsInsecure = &js
+	logger.Init()
 	SetLogger(&logger.Logger{})
 }
 
@@ -46,3 +57,44 @@
 func TestNilPanic(t *testing.T) {
 	//f, err := os.Open()
 }
+
+func TestNodeToBoxNoscript(t *testing.T) {
+	htm := `
+		<body>
+			<noscript>
+				<a href="https://example.com">Link</a>
+			</noscript>
+			<a>Other</a>
+			<input value=123>
+		</body>
+	`
+	doc, err := html.ParseWithOptions(
+		strings.NewReader(string(htm)),
+		html.ParseOptionEnableScripting(false),
+	)
+	if err != nil {
+		t.Fatalf(err.Error())
+	}
+	nodeMap := make(map[*html.Node]style.Map)
+	body := grepBody(doc)
+	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)
+	nt := nodes.NewNodeTree(body, style.Map{}, nodeMap, nil)
+	boxed := NodeToBox(0, b, nt)
+	numInputs := 0
+	TraverseTree(boxed, func(ui duit.UI) {
+		if _, ok := ui.(*duit.Field); ok {
+			numInputs++
+		}
+	})
+	if numInputs != 1 {
+		t.Fail()
+	}
+}
+