about summary refs log tree commit diff stats
path: root/src/html
diff options
context:
space:
mode:
Diffstat (limited to 'src/html')
-rw-r--r--src/html/chadombuilder.nim41
-rw-r--r--src/html/dom.nim34
2 files changed, 66 insertions, 9 deletions
diff --git a/src/html/chadombuilder.nim b/src/html/chadombuilder.nim
index f070fe8f..129082e2 100644
--- a/src/html/chadombuilder.nim
+++ b/src/html/chadombuilder.nim
@@ -12,6 +12,7 @@ import types/url
 import chakasu/charset
 
 import chame/htmlparser
+import chame/htmltokenizer
 import chame/tags
 
 # DOMBuilder implementation for Chawan.
@@ -166,7 +167,8 @@ proc elementPopped(builder: DOMBuilder[Node], element: Node) =
       #TODO style sheet
       script.execute()
 
-proc newChaDOMBuilder(url: URL, window: Window): ChaDOMBuilder =
+proc newChaDOMBuilder(url: URL, window: Window, isFragment = false):
+    ChaDOMBuilder =
   let document = newDocument()
   document.contentType = "text/html"
   document.url = url
@@ -196,8 +198,44 @@ proc newChaDOMBuilder(url: URL, window: Window): ChaDOMBuilder =
     setScriptAlreadyStarted: setScriptAlreadyStarted,
     associateWithForm: associateWithForm,
     #TODO isSVGIntegrationPoint (SVG support)
+    isFragment: isFragment
   )
 
+# https://html.spec.whatwg.org/multipage/parsing.html#parsing-html-fragments
+proc parseHTMLFragment*(element: Element, s: string): seq[Node] =
+  let url = parseURL("about:blank").get
+  let builder = newChaDOMBuilder(url, nil)
+  builder.isFragment = true
+  let document = Document(builder.document)
+  document.mode = element.document.mode
+  let state = case element.tagType
+  of TAG_TITLE, TAG_TEXTAREA: RCDATA
+  of TAG_STYLE, TAG_XMP, TAG_IFRAME, TAG_NOEMBED, TAG_NOFRAMES: RAWTEXT
+  of TAG_SCRIPT: SCRIPT_DATA
+  of TAG_NOSCRIPT:
+    if element.document != nil and element.document.scriptingEnabled:
+      RAWTEXT
+    else:
+      DATA
+  of TAG_PLAINTEXT:
+    PLAINTEXT
+  else: DATA
+  let root = document.newHTMLElement(TAG_HTML)
+  document.append(root)
+  let opts = HTML5ParserOpts[Node](
+    isIframeSrcdoc: false, #TODO?
+    scripting: false,
+    canReinterpret: false,
+    charsets: @[CHARSET_UTF_8],
+    ctx: some(Node(element)),
+    initialTokenizerState: state,
+    openElementsInit: @[Node(root)],
+    pushInTemplate: element.tagType == TAG_TEMPLATE
+  )
+  let inputStream = newStringStream(s)
+  parseHTML(inputStream, builder, opts)
+  return root.childList
+
 proc parseHTML*(inputStream: Stream, window: Window, url: URL,
     charsets: seq[Charset] = @[], canReinterpret = true): Document =
   let builder = newChaDOMBuilder(url, window)
@@ -207,7 +245,6 @@ proc parseHTML*(inputStream: Stream, window: Window, url: URL,
     canReinterpret: canReinterpret,
     charsets: charsets
   )
-  builder.isFragment = opts.ctx.isSome
   parseHTML(inputStream, builder, opts)
   return Document(builder.document)
 
diff --git a/src/html/dom.nim b/src/html/dom.nim
index 0363c0cd..efae857c 100644
--- a/src/html/dom.nim
+++ b/src/html/dom.nim
@@ -154,7 +154,7 @@ type
     liveCollections: HashSet[int]
     children_cached: HTMLCollection
     childNodes_cached: NodeList
-    document_internal: Document
+    document_internal: Document # not nil
 
   Attr* = ref object of Node
     namespaceURI* {.jsget.}: string
@@ -1423,8 +1423,6 @@ func scriptingEnabled*(document: Document): bool =
   return document.window.settings.scripting
 
 func scriptingEnabled*(element: Element): bool =
-  if element.document == nil:
-    return false
   return element.document.scriptingEnabled
 
 func form*(element: FormAssociatedElement): HTMLFormElement =
@@ -2604,11 +2602,14 @@ proc removeChild(parent, node: Node): DOMResult[Node] {.jsfunc.} =
   return ok(node)
 
 proc replaceAll(parent, node: Node) =
-  for i in countdown(parent.childList.high, 0):
-    parent.childList[i].remove(true)
+  var removedNodes = parent.childList # copy
+  for child in removedNodes:
+    child.remove(true)
+  assert parent != node
   if node != nil:
     if node.nodeType == DOCUMENT_FRAGMENT_NODE:
-      for child in node.childList:
+      var addedNodes = node.childList # copy
+      for child in addedNodes:
         parent.append(child)
     else:
       parent.append(node)
@@ -2648,7 +2649,7 @@ proc renderBlocking*(element: Element): bool =
 
 proc blockRendering*(element: Element) =
   let document = element.document
-  if document != nil and document.contentType == "text/html" and document.body == nil:
+  if document.contentType == "text/html" and document.body == nil:
     element.document.renderBlockingElements.add(element)
 
 proc markAsReady(element: HTMLScriptElement, res: ScriptResult) =
@@ -3044,6 +3045,25 @@ proc toBlob(ctx: JSContext, this: HTMLCanvasElement, callback: JSValue,
   JS_FreeValue(ctx, res)
   return JS_UNDEFINED
 
+import html/chadombuilder
+# https://w3c.github.io/DOM-Parsing/#dfn-fragment-parsing-algorithm
+proc fragmentParsingAlgorithm(element: Element, s: string): DocumentFragment =
+  #TODO xml
+  let newChildren = parseHTMLFragment(element, s)
+  let fragment = element.document.newDocumentFragment()
+  for child in newChildren:
+    fragment.append(child)
+  return fragment
+
+proc innerHTML(element: Element, s: string) {.jsfset.} =
+  #TODO shadow root
+  let fragment = fragmentParsingAlgorithm(element, s)
+  let ctx = if element.tagType == TAG_TEMPLATE:
+    HTMLTemplateElement(element).content
+  else:
+    element
+  ctx.replaceAll(fragment)
+
 proc registerElements(ctx: JSContext, nodeCID: JSClassID) =
   let elementCID = ctx.registerType(Element, parent = nodeCID)
   const extra_getset = getElementReflectFunctions()