diff --git a/manipulation.go b/manipulation.go index 202b02d..98a9b8b 100644 --- a/manipulation.go +++ b/manipulation.go @@ -388,7 +388,7 @@ func (s *Selection) WrapHtml(htmlStr string) *Selection { if parent == nil { parent = &html.Node{Type: html.ElementNode} } - nodes := cachedParseHtmlWithContext(cache, htmlStr, parent) + nodes := cachedParseHtmlWithContext(cache, htmlStr, parent, s.parseOpts()) newSingleSelection(n, s.document).wrapAllNodes(cloneNodes(nodes)...) } return s @@ -449,9 +449,9 @@ func (s *Selection) WrapAllHtml(htmlStr string) *Selection { if len(s.Nodes) > 0 { context = s.Nodes[0] if context.Parent != nil { - nodes = parseHtmlWithContext(htmlStr, context) + nodes = parseHtmlWithContext(htmlStr, context, s.parseOpts()) } else { - nodes = parseHtml(htmlStr) + nodes = parseHtml(htmlStr, s.parseOpts()) } } return s.wrapAllNodes(nodes...) @@ -526,7 +526,7 @@ func (s *Selection) WrapInnerSelection(sel *Selection) *Selection { func (s *Selection) WrapInnerHtml(htmlStr string) *Selection { cache := make(map[string][]*html.Node) for _, n := range s.Nodes { - nodes := cachedParseHtmlWithContext(cache, htmlStr, n) + nodes := cachedParseHtmlWithContext(cache, htmlStr, n, s.parseOpts()) newSingleSelection(n, s.document).wrapInnerNodes(cloneNodes(nodes)...) } return s @@ -559,14 +559,23 @@ func (s *Selection) wrapInnerNodes(ns ...*html.Node) *Selection { return s } -func parseHtml(h string) []*html.Node { - return parseHtmlWithContext(h, &html.Node{Type: html.ElementNode}) +// parseOpts returns the parse options of the document this selection belongs to, +// so an HTML string added later is parsed the same way the document was. +func (s *Selection) parseOpts() []html.ParseOption { + if s == nil || s.document == nil { + return nil + } + return s.document.parseOptions } -func parseHtmlWithContext(h string, context *html.Node) []*html.Node { +func parseHtml(h string, opts []html.ParseOption) []*html.Node { + return parseHtmlWithContext(h, &html.Node{Type: html.ElementNode}, opts) +} + +func parseHtmlWithContext(h string, context *html.Node, opts []html.ParseOption) []*html.Node { // Errors are only returned when the io.Reader returns any error besides // EOF, but strings.Reader never will - nodes, err := html.ParseFragment(strings.NewReader(h), context) + nodes, err := html.ParseFragmentWithOptions(strings.NewReader(h), context, opts...) if err != nil { panic("goquery: failed to parse HTML: " + err.Error()) } @@ -660,7 +669,7 @@ func (s *Selection) eachNodeHtml(htmlStr string, isParent bool, mergeFn func(n * if context == nil { continue } - nodes := cachedParseHtmlWithContext(cache, htmlStr, context) + nodes := cachedParseHtmlWithContext(cache, htmlStr, context, s.parseOpts()) mergeFn(n, cloneNodes(nodes)) } return s @@ -669,12 +678,12 @@ func (s *Selection) eachNodeHtml(htmlStr string, isParent bool, mergeFn func(n * // cachedParseHtmlWithContext returns parseHtmlWithContext(htmlStr, context), reusing a prior // result when context's nodeName has already been seen. Callers pass their own // cache map so the cache lifetime matches the caller's loop scope. -func cachedParseHtmlWithContext(cache map[string][]*html.Node, htmlStr string, context *html.Node) []*html.Node { +func cachedParseHtmlWithContext(cache map[string][]*html.Node, htmlStr string, context *html.Node, opts []html.ParseOption) []*html.Node { key := nodeName(context) if nodes, ok := cache[key]; ok { return nodes } - nodes := parseHtmlWithContext(htmlStr, context) + nodes := parseHtmlWithContext(htmlStr, context, opts) cache[key] = nodes return nodes } diff --git a/type.go b/type.go index 7f34170..7548c6f 100644 --- a/type.go +++ b/type.go @@ -19,6 +19,9 @@ type Document struct { *Selection Url *url.URL rootNode *html.Node + // parse options this document was built with, reused whenever an HTML + // string is parsed into it later on + parseOptions []html.ParseOption } // NewDocumentFromNode is a Document constructor that takes a root html Node @@ -71,7 +74,7 @@ func NewDocumentFromReaderWithOptions(r io.Reader, opts ...html.ParseOption) (*D if e != nil { return nil, e } - return newDocument(root, nil), nil + return newDocument(root, nil, opts...), nil } // NewDocumentFromResponse is another Document constructor that takes an http response as argument. @@ -100,13 +103,13 @@ func NewDocumentFromResponse(res *http.Response) (*Document, error) { // CloneDocument creates a deep-clone of a document. func CloneDocument(doc *Document) *Document { - return newDocument(cloneNode(doc.rootNode), doc.Url) + return newDocument(cloneNode(doc.rootNode), doc.Url, doc.parseOptions...) } // Private constructor, make sure all fields are correctly filled. -func newDocument(root *html.Node, url *url.URL) *Document { +func newDocument(root *html.Node, url *url.URL, opts ...html.ParseOption) *Document { // Create and fill the document - d := &Document{nil, url, root} + d := &Document{Url: url, rootNode: root, parseOptions: opts} d.Selection = newSingleSelection(root, d) return d } diff --git a/type_test.go b/type_test.go index 1a85fb3..e720e55 100644 --- a/type_test.go +++ b/type_test.go @@ -290,3 +290,29 @@ func TestNewDocumentFromReaderWithOptionsNoOptions(t *testing.T) { t.Errorf("got %q, want %q", got, "hi") } } + +func TestNewDocumentFromReaderWithOptionsAppliesToAddedHtml(t *testing.T) { + const doc = `
before
` + const frag = `` + + // The options given to the constructor hold for HTML added later, so the + // fragment is parsed the same way the document was. + d, e := NewDocumentFromReaderWithOptions(strings.NewReader(doc), html.ParseOptionEnableScripting(false)) + if e != nil { + t.Fatal(e) + } + d.Find("body").AppendHtml(frag) + if _, ok := d.Find("noscript a").Attr("href"); !ok { + t.Error("expected the added noscript to be parsed with scripting disabled") + } + + // Without the options the added fragment keeps the default behaviour. + d, e = NewDocumentFromReader(strings.NewReader(doc)) + if e != nil { + t.Fatal(e) + } + d.Find("body").AppendHtml(frag) + if _, ok := d.Find("noscript a").Attr("href"); ok { + t.Error("expected no match inside the added noscript with scripting enabled") + } +}