diff options
author | bptato <nincsnevem662@gmail.com> | 2023-06-19 20:16:39 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2023-06-19 20:17:06 +0200 |
commit | 82fb1f70ab275884c42dd769b2af8f9df5389e88 (patch) | |
tree | b7a83ca2e2d22e926959f2525169f2a2e4530e38 /src/html | |
parent | 070cfca46f60c3a00fe6dd66457f454a1a217897 (diff) | |
download | chawan-82fb1f70ab275884c42dd769b2af8f9df5389e88.tar.gz |
Get rid of the .jserr pragma
Diffstat (limited to 'src/html')
-rw-r--r-- | src/html/dom.nim | 314 | ||||
-rw-r--r-- | src/html/htmlparser.nim | 12 |
2 files changed, 174 insertions, 152 deletions
diff --git a/src/html/dom.nim b/src/html/dom.nim index 20b10674..1fa3b043 100644 --- a/src/html/dom.nim +++ b/src/html/dom.nim @@ -21,6 +21,7 @@ import io/loader import io/promise import io/request import io/window +import js/exception import js/javascript import js/timeout import types/blob @@ -619,25 +620,21 @@ proc quadraticCurveTo(ctx: CanvasRenderingContext2D, cpx, cpy, x, y: float64) {.jsfunc.} = ctx.state.path.quadraticCurveTo(cpx, cpy, x, y) -proc arcTo(ctx: CanvasRenderingContext2D, x1, y1, x2, y2, radius: float64) - {.jsfunc.} = - if not ctx.state.path.arcTo(x1, y1, x2, y2, radius): - #TODO should be DOMException - JS_ERR JS_TypeError, "IndexSizeError" +proc arcTo(ctx: CanvasRenderingContext2D, x1, y1, x2, y2, radius: float64): + Err[DOMException] {.jsfunc.} = + return ctx.state.path.arcTo(x1, y1, x2, y2, radius) proc arc(ctx: CanvasRenderingContext2D, x, y, radius, startAngle, - endAngle: float64, counterclockwise = false) {.jsfunc.} = - if not ctx.state.path.arc(x, y, radius, startAngle, endAngle, - counterclockwise): - #TODO should be DOMException - JS_ERR JS_TypeError, "IndexSizeError" + endAngle: float64, counterclockwise = false): Err[DOMException] + {.jsfunc.} = + return ctx.state.path.arc(x, y, radius, startAngle, endAngle, + counterclockwise) proc ellipse(ctx: CanvasRenderingContext2D, x, y, radiusX, radiusY, rotation, - startAngle, endAngle: float64, counterclockwise = false) {.jsfunc.} = - if not ctx.state.path.ellipse(x, y, radiusX, radiusY, rotation, startAngle, - endAngle, counterclockwise): - #TODO should be DOMException - JS_ERR JS_TypeError, "IndexSizeError" + startAngle, endAngle: float64, counterclockwise = false): Err[DOMException] + {.jsfunc.} = + return ctx.state.path.ellipse(x, y, radiusX, radiusY, rotation, startAngle, + endAngle, counterclockwise) proc rect(ctx: CanvasRenderingContext2D, x, y, w, h: float64) {.jsfunc.} = ctx.state.path.rect(x, y, w, h) @@ -981,76 +978,71 @@ proc update(tokenList: DOMTokenList) = return tokenList.element.attr(tokenList.localName, tokenList.toks.join(' ')) -proc add(tokenList: DOMTokenList, tokens: varargs[string]) {.jserr, jsfunc.} = +func validateDOMToken(tok: string): Err[DOMException] = + if tok == "": + return err(newDOMException("Got an empty string", "SyntaxError")) + if AsciiWhitespace in tok: + return err(newDOMException("Got a string containing whitespace", + "InvalidCharacterError")) + +proc add(tokenList: DOMTokenList, tokens: varargs[string]): Err[DOMException] + {.jsfunc.} = for tok in tokens: - if tok == "": - #TODO should be DOMException - JS_ERR JS_TypeError, "SyntaxError" - if AsciiWhitespace in tok: - #TODO should be DOMException - JS_ERR JS_TypeError, "InvalidCharacterError" + ?validateDOMToken(tok) for tok in tokens: tokenList.toks.add(tok) tokenList.update() + return ok() -proc remove(tokenList: DOMTokenList, tokens: varargs[string]) {.jserr, jsfunc.} = +proc remove(tokenList: DOMTokenList, tokens: varargs[string]): + Err[DOMException] {.jsfunc.} = for tok in tokens: - if tok == "": - #TODO should be DOMException - JS_ERR JS_TypeError, "SyntaxError" - if AsciiWhitespace in tok: - #TODO should be DOMException - JS_ERR JS_TypeError, "InvalidCharacterError" + ?validateDOMToken(tok) for tok in tokens: let i = tokenList.toks.find(tok) if i != -1: tokenList.toks.delete(i) tokenList.update() + return ok() -proc toggle(tokenList: DOMTokenList, token: string, force = none(bool)): bool {.jserr, jsfunc.} = - if token == "": - #TODO should be DOMException - JS_ERR JS_TypeError, "SyntaxError" - if AsciiWhitespace in token: - #TODO should be DOMException - JS_ERR JS_TypeError, "InvalidCharacterError" +proc toggle(tokenList: DOMTokenList, token: string, force = none(bool)): + Result[bool, DOMException] {.jsfunc.} = + ?validateDOMToken(token) let i = tokenList.toks.find(token) if i != -1: if not force.get(false): tokenList.toks.delete(i) tokenList.update() - return false - return true + return ok(false) + return ok(true) if force.get(true): tokenList.toks.add(token) tokenList.update() - return true - return false + return ok(true) + return ok(false) -proc replace(tokenList: DOMTokenList, token, newToken: string): bool {.jserr, jsfunc.} = - if token == "" or newToken == "": - #TODO should be DOMException - JS_ERR JS_TypeError, "SyntaxError" - if AsciiWhitespace in token or AsciiWhitespace in newToken: - #TODO should be DOMException - JS_ERR JS_TypeError, "InvalidCharacterError" +proc replace(tokenList: DOMTokenList, token, newToken: string): + Result[bool, DOMException] {.jsfunc.} = + ?validateDOMToken(token) + ?validateDOMToken(newToken) let i = tokenList.toks.find(token) if i == -1: - return false + return ok(false) tokenList.toks[i] = newToken tokenList.update() - return true + return ok(true) const SupportedTokensMap = { "abcd": @["adsf"] #TODO }.toTable() -func supports(tokenList: DOMTokenList, token: string): bool {.jserr, jsfunc.} = +func supports(tokenList: DOMTokenList, token: string): + Result[bool, JSError] {.jsfunc.} = if tokenList.localName in SupportedTokensMap: let lowercase = token.toLowerAscii() - return lowercase in SupportedTokensMap[tokenList.localName] - else: - JS_ERR JS_TypeError, "No supported tokens defined for attribute " & tokenList.localName + return ok(lowercase in SupportedTokensMap[tokenList.localName]) + return err(newTypeError("No supported tokens defined for attribute " & + tokenList.localName)) func `$`(tokenList: DOMTokenList): string {.jsfunc.} = return tokenList.toks.join(' ') @@ -1796,7 +1788,7 @@ func newHTMLElement*(document: Document, localName: string, func newDocument*(): Document {.jsctor.} = result = Document( nodeType: DOCUMENT_NODE, - url: newURL("about:blank"), + url: newURL("about:blank").get, index: -1 ) result.document = result @@ -1842,7 +1834,7 @@ func baseURL*(document: Document): Url = if href == "": return document.url if document.url == nil: - return newURL("about:blank") #TODO ??? + return newURL("about:blank").get #TODO ??? let url = parseURL(href, some(document.url)) if url.isNone: return document.url @@ -1960,20 +1952,30 @@ proc attrulgz(element: Element, name: string, value: uint32) = if value > 0: element.attrul(name, value) -proc setAttribute(element: Element, qualifiedName, value: string) {.jserr, jsfunc.} = - if not qualifiedName.matchNameProduction(): - #TODO should be DOMException - JS_ERR JS_TypeError, "InvalidCharacterError" +func validateAttributeName(name: string, isq: static bool = false): + Err[DOMException] = + when isq: + if name.matchNameProduction(): + return ok() + else: + if name.matchQNameProduction(): + return ok() + return err(newDOMException("Invalid character in attribute name", + "InvalidCharacterError")) + +proc setAttribute(element: Element, qualifiedName, value: string): + Err[DOMException] {.jsfunc.} = + ?validateAttributeName(qualifiedName) let qualifiedName = if element.namespace == Namespace.HTML and not element.document.isxml: qualifiedName.toLowerAscii2() else: qualifiedName element.attr(qualifiedName, value) + return ok() -proc setAttributeNS(element: Element, namespace, qualifiedName, value: string) {.jserr, jsfunc.} = - if not qualifiedName.matchQNameProduction(): - #TODO this should be a DOMException - JS_ERR JS_TypeError, "InvalidCharacterError" +proc setAttributeNS(element: Element, namespace, qualifiedName, + value: string): Err[DOMException] {.jsfunc.} = + ?validateAttributeName(qualifiedName, isq = true) let ps = qualifiedName.until(':') let prefix = if ps.len < qualifiedName.len: ps else: "" let localName = qualifiedName.substr(prefix.len) @@ -1981,14 +1983,14 @@ proc setAttributeNS(element: Element, namespace, qualifiedName, value: string) { prefix == "xml" and namespace != $Namespace.XML or (qualifiedName == "xmlns" or prefix == "xmlns") and namespace != $Namespace.XMLNS or namespace == $Namespace.XMLNS and qualifiedName != "xmlns" and prefix != "xmlns": - #TODO this should be a DOMException - JS_ERR JS_TypeError, "NamespaceError" + return err(newDOMException("Unexpected namespace", "NamespaceError")) element.attr0(qualifiedName, value) let i = element.attributes.findAttrNS(namespace, localName) if i != -1: element.attributes.attrlist[i].value = value else: element.attributes.attrlist.add(element.newAttr(localName, value, prefix, namespace)) + return ok() proc removeAttribute(element: Element, qualifiedName: string) {.jsfunc.} = let qualifiedName = if element.namespace == Namespace.HTML and not element.document.isxml: @@ -2002,10 +2004,9 @@ proc removeAttributeNS(element: Element, namespace, localName: string) {.jsfunc. if i != -1: element.delAttr(i) -proc toggleAttribute(element: Element, qualifiedName: string, force = none(bool)): bool {.jserr, jsfunc.} = - if not qualifiedName.matchNameProduction(): - #TODO should be DOMException - JS_ERR JS_TypeError, "InvalidCharacterError" +proc toggleAttribute(element: Element, qualifiedName: string, + force = none(bool)): Result[bool, DOMException] {.jsfunc.} = + ?validateAttributeName(qualifiedName) let qualifiedName = if element.namespace == Namespace.HTML and not element.document.isxml: qualifiedName.toLowerAscii2() else: @@ -2013,51 +2014,55 @@ proc toggleAttribute(element: Element, qualifiedName: string, force = none(bool) if not element.attrb(qualifiedName): if force.get(true): element.attr(qualifiedName, "") - return true - return false + return ok(true) + return ok(false) if not force.get(false): element.delAttr(qualifiedName) - return false - return true + return ok(false) + return ok(true) proc value(attr: Attr, s: string) {.jsfset.} = attr.value = s if attr.ownerElement != nil: attr.ownerElement.attr0(attr.name, s) -proc setNamedItem(map: NamedNodeMap, attr: Attr): Option[Attr] {.jserr, jsfunc.} = +proc setNamedItem(map: NamedNodeMap, attr: Attr): Result[Attr, DOMException] + {.jsfunc.} = if attr.ownerElement != nil and attr.ownerElement != map.element: - #TODO should be DOMException - JS_ERR JS_TypeError, "InUseAttributeError" + return err(newDOMException("Attribute is currently in use", + "InUseAttributeError")) if attr.name in map.element.attrs: - return some(attr) + return ok(attr) let i = map.findAttr(attr.name) if i != -1: - result = some(map.attrlist[i]) + result = ok(map.attrlist[i]) map.attrlist.delete(i) + else: + result = ok(nil) map.element.attrs[attr.name] = attr.value map.attrlist.add(attr) -proc setNamedItemNS(map: NamedNodeMap, attr: Attr): Option[Attr] {.jsfunc.} = - map.setNamedItem(attr) +proc setNamedItemNS(map: NamedNodeMap, attr: Attr): Result[Attr, DOMException] + {.jsfunc.} = + return map.setNamedItem(attr) -proc removeNamedItem(map: NamedNodeMap, qualifiedName: string): Attr {.jserr, jsfunc.} = +proc removeNamedItem(map: NamedNodeMap, qualifiedName: string): + Result[Attr, DOMException] {.jsfunc.} = let i = map.findAttr(qualifiedName) if i != -1: let attr = map.attrlist[i] map.element.delAttr(i) - return attr - #TODO should be DOMException - JS_ERR JS_TypeError, "Not found" + return ok(attr) + return err(newDOMException("Item not found", "NotFoundError")) -proc removeNamedItemNS(map: NamedNodeMap, namespace, localName: string): Attr {.jserr, jsfunc.} = +proc removeNamedItemNS(map: NamedNodeMap, namespace, localName: string): + Result[Attr, DOMException] {.jsfunc.} = let i = map.findAttrNS(namespace, localName) if i != -1: let attr = map.attrlist[i] map.element.delAttr(i) - return attr - #TODO should be DOMException - JS_ERR JS_TypeError, "Not found" + return ok(attr) + return err(newDOMException("Item not found", "NotFoundError")) proc id(element: Element, id: string) {.jsfset.} = element.id = id @@ -2202,43 +2207,53 @@ proc insertionSteps(insertedNode: Node) = element.resetFormOwner() # WARNING the ordering of the arguments in the standard is whack so this doesn't match that -func preInsertionValidity*(parent, node, before: Node): bool = +func preInsertionValidity*(parent, node, before: Node): Err[DOMException] = if parent.nodeType notin {DOCUMENT_NODE, DOCUMENT_FRAGMENT_NODE, ELEMENT_NODE}: - # HierarchyRequestError - return false + return err(newDOMException("Parent must be a document, document fragment, " & + "or element", "HierarchyRequestError")) if node.isHostIncludingInclusiveAncestor(parent): - # HierarchyRequestError - return false + return err(newDOMException("Parent must be an ancestor", "HierarchyRequestError")) if before != nil and before.parentNode != parent: - # NotFoundError - return false - if node.nodeType notin {DOCUMENT_FRAGMENT_NODE, DOCUMENT_TYPE_NODE, ELEMENT_NODE} + CharacterDataNodes: - # HierarchyRequestError - return false - if (node.nodeType == TEXT_NODE and parent.nodeType == DOCUMENT_NODE) or - (node.nodeType == DOCUMENT_TYPE_NODE and parent.nodeType != DOCUMENT_NODE): - # HierarchyRequestError - return false + return err(newDOMException("Reference node is not a child of parent", + "NotFoundError")) + if node.nodeType notin {DOCUMENT_FRAGMENT_NODE, DOCUMENT_TYPE_NODE, + ELEMENT_NODE} + CharacterDataNodes: + return err(newDOMException("Cannot insert node type", + "HierarchyRequestError")) + if node.nodeType == TEXT_NODE and parent.nodeType == DOCUMENT_NODE: + return err(newDOMException("Cannot insert text into document", + "HierarchyRequestError")) + if node.nodeType == DOCUMENT_TYPE_NODE and parent.nodeType != DOCUMENT_NODE: + return err(newDOMException("Document type can only be inserted into " & + "document", "HierarchyRequestError")) if parent.nodeType == DOCUMENT_NODE: case node.nodeType of DOCUMENT_FRAGMENT_NODE: let elems = node.countChildren(ELEMENT_NODE) if elems > 1 or node.hasChild(TEXT_NODE): - # HierarchyRequestError - return false - elif elems == 1 and (parent.hasChild(ELEMENT_NODE) or before != nil and (before.nodeType == DOCUMENT_TYPE_NODE or before.hasNextSibling(DOCUMENT_TYPE_NODE))): - # HierarchyRequestError - return false + return err(newDOMException("Document fragment has invalid children", + "HierarchyRequestError")) + elif elems == 1 and (parent.hasChild(ELEMENT_NODE) or + before != nil and (before.nodeType == DOCUMENT_TYPE_NODE or + before.hasNextSibling(DOCUMENT_TYPE_NODE))): + return err(newDOMException("Document fragment has invalid children", + "HierarchyRequestError")) of ELEMENT_NODE: - if parent.hasChild(ELEMENT_NODE) or before != nil and (before.nodeType == DOCUMENT_TYPE_NODE or before.hasNextSibling(DOCUMENT_TYPE_NODE)): - # HierarchyRequestError - return false + if parent.hasChild(ELEMENT_NODE): + return err(newDOMException("Document already has an element child", + "HierarchyRequestError")) + elif before != nil and (before.nodeType == DOCUMENT_TYPE_NODE or + before.hasNextSibling(DOCUMENT_TYPE_NODE)): + return err(newDOMException("Cannot insert element before document " & + "type", "HierarchyRequestError")) of DOCUMENT_TYPE_NODE: - if parent.hasChild(DOCUMENT_TYPE_NODE) or before != nil and before.hasPreviousSibling(ELEMENT_NODE) or before == nil and parent.hasChild(ELEMENT_NODE): - # HierarchyRequestError - return false + if parent.hasChild(DOCUMENT_TYPE_NODE) or + before != nil and before.hasPreviousSibling(ELEMENT_NODE) or + before == nil and parent.hasChild(ELEMENT_NODE): + return err(newDOMException("Cannot insert document type before " & + "an element node", "HierarchyRequestError")) else: discard - return true # no exception reached + return ok() # no exception reached proc insertNode(parent, node, before: Node) = parent.document.adopt(node) @@ -2282,18 +2297,17 @@ proc insert*(parent, node, before: Node) = for node in nodes: insertNode(parent, node, before) -proc insertBefore(parent, node, before: Node): Node {.jserr, jsfunc.} = - if parent.preInsertionValidity(node, before): - let referenceChild = if before == node: - node.nextSibling - else: - before - parent.insert(node, referenceChild) - return node - #TODO use preInsertionValidity result - JS_ERR JS_TypeError, "Pre-insertion validity violated" +proc insertBefore(parent, node, before: Node): Result[Node, DOMException] + {.jsfunc.} = + ?parent.preInsertionValidity(node, before) + let referenceChild = if before == node: + node.nextSibling + else: + before + parent.insert(node, referenceChild) + return ok(node) -proc appendChild(parent, node: Node): Node {.jsfunc.} = +proc appendChild(parent, node: Node): Result[Node, DOMException] {.jsfunc.} = return parent.insertBefore(node, nil) proc append*(parent, node: Node) = @@ -2301,11 +2315,12 @@ proc append*(parent, node: Node) = #TODO replaceChild -proc removeChild(parent, node: Node): Node {.jsfunc.} = - #TODO should be DOMException +proc removeChild(parent, node: Node): Result[Node, DOMException] {.jsfunc.} = if node.parentNode != parent: - JS_ERR JS_TypeError, "NotFoundError" + return err(newDOMException("Node is not a child of parent", + "NotFoundError")) node.remove() + return ok(node) proc replaceAll(parent, node: Node) = for i in countdown(parent.childList.high, 0): @@ -2576,10 +2591,11 @@ proc prepare*(element: HTMLScriptElement) = element.execute() #TODO options/custom elements -proc createElement(document: Document, localName: string): Element {.jserr, jsfunc.} = +proc createElement(document: Document, localName: string): + Result[Element, DOMException] {.jsfunc.} = if not localName.matchNameProduction(): - #TODO DOMException - JS_ERR JS_TypeError, "InvalidCharacterError" + return err(newDOMException("Invalid character in element name", + "InvalidCharacterError")) let localName = if not document.isxml: localName.toLowerAscii2() else: @@ -2588,20 +2604,23 @@ proc createElement(document: Document, localName: string): Element {.jserr, jsfu Namespace.HTML else: NO_NAMESPACE - return document.newHTMLElement(localName, namespace) + return ok(document.newHTMLElement(localName, namespace)) #TODO createElementNS proc createDocumentFragment(document: Document): DocumentFragment {.jsfunc.} = return newDocumentFragment(document) -proc createDocumentType(implementation: DOMImplementation, qualifiedName, publicId, systemId: string): DocumentType {.jserr, jsfunc.} = +proc createDocumentType(implementation: DOMImplementation, qualifiedName, + publicId, systemId: string): Result[DocumentType, DOMException] {.jsfunc.} = if not qualifiedName.matchQNameProduction(): - #TODO should be DOMException - JS_ERR JS_TypeError, "InvalidCharacterError" - return implementation.document.newDocumentType(qualifiedName, publicId, systemId) + return err(newDOMException("Invalid character in document type name", + "InvalidCharacterError")) + return ok(implementation.document.newDocumentType(qualifiedName, publicId, + systemId)) -proc createHTMLDocument(implementation: DOMImplementation, title = none(string)): Document {.jsfunc.} = +proc createHTMLDocument(implementation: DOMImplementation, title = + none(string)): Document {.jsfunc.} = let doc = newDocument() doc.contentType = "text/html" doc.append(doc.newDocumentType("html")) @@ -2617,23 +2636,24 @@ proc createHTMLDocument(implementation: DOMImplementation, title = none(string)) #TODO set origin return doc -proc createCDATASection(document: Document, data: string): CDATASection {.jserr, jsfunc.} = +proc createCDATASection(document: Document, data: string): Result[CDATASection, DOMException] {.jsfunc.} = if not document.isxml: - #TODO should be DOMException - JS_ERR JS_TypeError, "NotSupportedError" + return err(newDOMException("CDATA sections are not supported in HTML", + "NotSupportedError")) if "]]>" in data: - #TODO should be DOMException - JS_ERR JS_TypeError, "InvalidCharacterError" - return newCDATASection(document, data) + return err(newDOMException("CDATA sections may not contain the string ]]>", + "InvalidCharacterError")) + return ok(newCDATASection(document, data)) proc createComment*(document: Document, data: string): Comment {.jsfunc.} = return newComment(document, data) -proc createProcessingInstruction(document: Document, target, data: string): ProcessingInstruction {.jsfunc.} = +proc createProcessingInstruction(document: Document, target, data: string): + Result[ProcessingInstruction, DOMException] {.jsfunc.} = if not target.matchNameProduction() or "?>" in data: - #TODO should be DOMException - JS_ERR JS_TypeError, "InvalidCharacterError" - return newProcessingInstruction(document, target, data) + return err(newDOMException("Invalid data for processing instruction", + "InvalidCharacterError")) + return ok(newProcessingInstruction(document, target, data)) # Forward definition hack (these are set in selectors.nim) var doqsa*: proc (node: Node, q: string): seq[Element] diff --git a/src/html/htmlparser.nim b/src/html/htmlparser.nim index f6af8709..c3df8e3d 100644 --- a/src/html/htmlparser.nim +++ b/src/html/htmlparser.nim @@ -14,6 +14,7 @@ import encoding/decoderstream import html/dom import html/tags import html/htmltokenizer +import js/exception import js/javascript import types/url import utils/twtstr @@ -239,7 +240,7 @@ proc insert(location: AdjustedInsertionLocation, node: Node) = proc insertForeignElement(parser: var HTML5Parser, token: Token, namespace: Namespace): Element = let location = parser.appropriatePlaceForInsert() let element = parser.createElement(token, namespace, location.inside) - if location.inside.preInsertionValidity(element, location.before): + if location.inside.preInsertionValidity(element, location.before).isOk: #TODO custom elements location.insert(element) parser.pushElement(element) @@ -2239,15 +2240,16 @@ proc parseHTML*(inputStream: Stream, charsets: seq[Charset] = @[], proc newDOMParser*(): DOMParser {.jsctor.} = new(result) -proc parseFromString(parser: DOMParser, str: string, t: string): Document {.jserr, jsfunc.} = +proc parseFromString(parser: DOMParser, str: string, t: string): + Result[Document, JSError] {.jsfunc.} = case t of "text/html": let res = parseHTML(newStringStream(str)) - return res + return ok(res) of "text/xml", "application/xml", "application/xhtml+xml", "image/svg+xml": - JS_ERR JS_InternalError, "XML parsing is not supported yet" + return err(newInternalError("XML parsing is not supported yet")) else: - JS_ERR JS_TypeError, "Invalid mime type" + return err(newTypeError("Invalid mime type")) proc addHTMLModule*(ctx: JSContext) = ctx.registerType(DOMParser) |