You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

146 lines
3.7 KiB

  1. // Copyright 2010 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package html
  5. import (
  6. "fmt"
  7. )
  8. // checkTreeConsistency checks that a node and its descendants are all
  9. // consistent in their parent/child/sibling relationships.
  10. func checkTreeConsistency(n *Node) error {
  11. return checkTreeConsistency1(n, 0)
  12. }
  13. func checkTreeConsistency1(n *Node, depth int) error {
  14. if depth == 1e4 {
  15. return fmt.Errorf("html: tree looks like it contains a cycle")
  16. }
  17. if err := checkNodeConsistency(n); err != nil {
  18. return err
  19. }
  20. for c := n.FirstChild; c != nil; c = c.NextSibling {
  21. if err := checkTreeConsistency1(c, depth+1); err != nil {
  22. return err
  23. }
  24. }
  25. return nil
  26. }
  27. // checkNodeConsistency checks that a node's parent/child/sibling relationships
  28. // are consistent.
  29. func checkNodeConsistency(n *Node) error {
  30. if n == nil {
  31. return nil
  32. }
  33. nParent := 0
  34. for p := n.Parent; p != nil; p = p.Parent {
  35. nParent++
  36. if nParent == 1e4 {
  37. return fmt.Errorf("html: parent list looks like an infinite loop")
  38. }
  39. }
  40. nForward := 0
  41. for c := n.FirstChild; c != nil; c = c.NextSibling {
  42. nForward++
  43. if nForward == 1e6 {
  44. return fmt.Errorf("html: forward list of children looks like an infinite loop")
  45. }
  46. if c.Parent != n {
  47. return fmt.Errorf("html: inconsistent child/parent relationship")
  48. }
  49. }
  50. nBackward := 0
  51. for c := n.LastChild; c != nil; c = c.PrevSibling {
  52. nBackward++
  53. if nBackward == 1e6 {
  54. return fmt.Errorf("html: backward list of children looks like an infinite loop")
  55. }
  56. if c.Parent != n {
  57. return fmt.Errorf("html: inconsistent child/parent relationship")
  58. }
  59. }
  60. if n.Parent != nil {
  61. if n.Parent == n {
  62. return fmt.Errorf("html: inconsistent parent relationship")
  63. }
  64. if n.Parent == n.FirstChild {
  65. return fmt.Errorf("html: inconsistent parent/first relationship")
  66. }
  67. if n.Parent == n.LastChild {
  68. return fmt.Errorf("html: inconsistent parent/last relationship")
  69. }
  70. if n.Parent == n.PrevSibling {
  71. return fmt.Errorf("html: inconsistent parent/prev relationship")
  72. }
  73. if n.Parent == n.NextSibling {
  74. return fmt.Errorf("html: inconsistent parent/next relationship")
  75. }
  76. parentHasNAsAChild := false
  77. for c := n.Parent.FirstChild; c != nil; c = c.NextSibling {
  78. if c == n {
  79. parentHasNAsAChild = true
  80. break
  81. }
  82. }
  83. if !parentHasNAsAChild {
  84. return fmt.Errorf("html: inconsistent parent/child relationship")
  85. }
  86. }
  87. if n.PrevSibling != nil && n.PrevSibling.NextSibling != n {
  88. return fmt.Errorf("html: inconsistent prev/next relationship")
  89. }
  90. if n.NextSibling != nil && n.NextSibling.PrevSibling != n {
  91. return fmt.Errorf("html: inconsistent next/prev relationship")
  92. }
  93. if (n.FirstChild == nil) != (n.LastChild == nil) {
  94. return fmt.Errorf("html: inconsistent first/last relationship")
  95. }
  96. if n.FirstChild != nil && n.FirstChild == n.LastChild {
  97. // We have a sole child.
  98. if n.FirstChild.PrevSibling != nil || n.FirstChild.NextSibling != nil {
  99. return fmt.Errorf("html: inconsistent sole child's sibling relationship")
  100. }
  101. }
  102. seen := map[*Node]bool{}
  103. var last *Node
  104. for c := n.FirstChild; c != nil; c = c.NextSibling {
  105. if seen[c] {
  106. return fmt.Errorf("html: inconsistent repeated child")
  107. }
  108. seen[c] = true
  109. last = c
  110. }
  111. if last != n.LastChild {
  112. return fmt.Errorf("html: inconsistent last relationship")
  113. }
  114. var first *Node
  115. for c := n.LastChild; c != nil; c = c.PrevSibling {
  116. if !seen[c] {
  117. return fmt.Errorf("html: inconsistent missing child")
  118. }
  119. delete(seen, c)
  120. first = c
  121. }
  122. if first != n.FirstChild {
  123. return fmt.Errorf("html: inconsistent first relationship")
  124. }
  125. if len(seen) != 0 {
  126. return fmt.Errorf("html: inconsistent forwards/backwards child list")
  127. }
  128. return nil
  129. }