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/catom.nim103
-rw-r--r--src/html/chadombuilder.nim24
-rw-r--r--src/html/dom.nim299
-rw-r--r--src/html/env.nim9
-rw-r--r--src/html/event.nim15
-rw-r--r--src/html/xmlhttprequest.nim4
6 files changed, 185 insertions, 269 deletions
diff --git a/src/html/catom.nim b/src/html/catom.nim
index e4893125..c888333e 100644
--- a/src/html/catom.nim
+++ b/src/html/catom.nim
@@ -229,48 +229,56 @@ const factoryInit = (func(): CAtomFactoryInit =
   return init
 )()
 
-proc newCAtomFactory*(): CAtomFactory =
+proc newCAtomFactory(): CAtomFactory =
   let factory = new(CAtomFactory)
   factory[] = factoryInit.obj
   return factory
 
-func toLowerAscii*(factory: CAtomFactory; a: CAtom): CAtom =
-  return factory.lowerMap[int32(a)]
+var factory {.global.}: CAtomFactory = nil
 
-func equalsIgnoreCase*(factory: CAtomFactory; a, b: CAtom): bool =
-  return factory.lowerMap[int32(a)] == factory.lowerMap[int32(b)]
+func getFactory(): CAtomFactory =
+  {.cast(noSideEffect).}:
+    return factory
 
-func containsIgnoreCase*(factory: CAtomFactory; aa: openArray[CAtom];
-    a: CAtom): bool =
-  let a = factory.toLowerAscii(a)
+proc initCAtomFactory*() =
+  assert factory == nil
+  factory = newCAtomFactory()
+
+func toLowerAscii*(a: CAtom): CAtom =
+  return getFactory().lowerMap[int32(a)]
+
+func equalsIgnoreCase*(a, b: CAtom): bool =
+  return getFactory().lowerMap[int32(a)] == getFactory().lowerMap[int32(b)]
+
+func containsIgnoreCase*(aa: openArray[CAtom]; a: CAtom): bool =
+  let a = a.toLowerAscii()
   for it in aa:
-    if a == factory.toLowerAscii(it):
+    if a == it.toLowerAscii():
       return true
   return false
 
-func toAtom*(factory: CAtomFactory; s: sink string): CAtom =
-  return factory[].toAtom(s)
+proc toAtom*(s: sink string): CAtom {.sideEffect.} =
+  return getFactory()[].toAtom(s)
 
-func toAtom*(factory: CAtomFactory; tagType: TagType): CAtom =
+func toAtom*(tagType: TagType): CAtom =
   assert tagType != TAG_UNKNOWN
   return CAtom(tagType)
 
-func toAtom*(factory: CAtomFactory; attrType: StaticAtom): CAtom =
+func toAtom*(attrType: StaticAtom): CAtom =
   assert attrType != atUnknown
   return CAtom(attrType)
 
-func toAtomLower*(factory: CAtomFactory; s: sink string): CAtom =
-  return factory.lowerMap[int32(factory.toAtom(s))]
+proc toAtomLower*(s: sink string): CAtom {.sideEffect.} =
+  return getFactory().lowerMap[int32(s.toAtom())]
 
-func containsIgnoreCase*(factory: CAtomFactory; aa: openArray[CAtom];
-    a: StaticAtom): bool =
-  return factory.containsIgnoreCase(aa, factory.toAtom(a))
+func containsIgnoreCase*(aa: openArray[CAtom]; a: StaticAtom): bool =
+  return aa.containsIgnoreCase(a.toAtom())
 
-func toStr*(factory: CAtomFactory; atom: CAtom): lent string =
-  return factory.atomMap[int(atom)]
+func toStr*(atom: CAtom): lent string =
+  return getFactory().atomMap[int(atom)]
 
-func toStr*(factory: CAtomFactory; sa: StaticAtom): lent string =
-  return factory.toStr(factory.toAtom(sa))
+func toStr*(sa: StaticAtom): lent string =
+  return sa.toAtom().toStr()
 
 func toTagType*(atom: CAtom): TagType =
   let i = int(atom)
@@ -278,17 +286,17 @@ func toTagType*(atom: CAtom): TagType =
     return TagType(i)
   return TAG_UNKNOWN
 
-func toStaticAtom*(factory: CAtomFactory; atom: CAtom): StaticAtom =
+func toStaticAtom*(atom: CAtom): StaticAtom =
   let i = int(atom)
   if i <= int(StaticAtom.high):
     return StaticAtom(i)
   return atUnknown
 
-func toStaticAtom*(factory: CAtomFactory; s: string): StaticAtom =
-  return factory.toStaticAtom(factory.toAtom(s))
+proc toStaticAtom*(s: string): StaticAtom =
+  return s.toAtom().toStaticAtom()
 
-func toNamespace*(factory: CAtomFactory; atom: CAtom): Namespace =
-  case factory.toStaticAtom(atom)
+func toNamespace*(atom: CAtom): Namespace =
+  case atom.toStaticAtom()
   of satUempty: return NO_NAMESPACE
   of satNamespaceHTML: return Namespace.HTML
   of satNamespaceMathML: return Namespace.MATHML
@@ -298,36 +306,11 @@ func toNamespace*(factory: CAtomFactory; atom: CAtom): Namespace =
   of satNamespaceXMLNS: return Namespace.XMLNS
   else: return NAMESPACE_UNKNOWN
 
-func toAtom*(factory: CAtomFactory; namespace: Namespace): CAtom =
-  return factory.namespaceMap[namespace]
-
-func toAtom*(factory: CAtomFactory; prefix: NamespacePrefix): CAtom =
-  return factory.prefixMap[prefix]
-
-# Forward declaration hack
-var getFactoryImpl*: proc(ctx: JSContext): CAtomFactory {.nimcall, noSideEffect,
-  raises: [].}
-
-proc toAtom*(ctx: JSContext; atom: StaticAtom): CAtom =
-  return ctx.getFactoryImpl().toAtom(atom)
-
-proc toAtom*(ctx: JSContext; s: string): CAtom =
-  return ctx.getFactoryImpl().toAtom(s)
-
-proc toStaticAtom*(ctx: JSContext; atom: CAtom): StaticAtom =
-  return ctx.getFactoryImpl().toStaticAtom(atom)
-
-proc toStaticAtom*(ctx: JSContext; s: string): StaticAtom =
-  return ctx.getFactoryImpl().toStaticAtom(s)
-
-proc toStr*(ctx: JSContext; atom: CAtom): lent string =
-  return ctx.getFactoryImpl().toStr(atom)
-
-proc toLowerAscii*(ctx: JSContext; atom: CAtom): CAtom =
-  return ctx.getFactoryImpl().toLowerAscii(atom)
+func toAtom*(namespace: Namespace): CAtom =
+  return getFactory().namespaceMap[namespace]
 
-proc toStr*(ctx: JSContext; sa: StaticAtom): lent string =
-  return ctx.getFactoryImpl().toStr(sa)
+func toAtom*(prefix: NamespacePrefix): CAtom =
+  return getFactory().prefixMap[prefix]
 
 proc fromJS*(ctx: JSContext; val: JSValue; res: var CAtom): Opt[void] =
   if JS_IsNull(val):
@@ -335,7 +318,7 @@ proc fromJS*(ctx: JSContext; val: JSValue; res: var CAtom): Opt[void] =
   else:
     var s: string
     ?ctx.fromJS(val, s)
-    res = ctx.getFactoryImpl().toAtom(s)
+    res = s.toAtom()
   return ok()
 
 proc fromJS*(ctx: JSContext; val: JSAtom; res: var CAtom): Opt[void] =
@@ -344,19 +327,19 @@ proc fromJS*(ctx: JSContext; val: JSAtom; res: var CAtom): Opt[void] =
   else:
     var s: string
     ?ctx.fromJS(val, s)
-    res = ctx.getFactoryImpl().toAtom(s)
+    res = s.toAtom()
   return ok()
 
 proc fromJS*(ctx: JSContext; val: JSAtom; res: var StaticAtom): Opt[void] =
   var ca: CAtom
   ?ctx.fromJS(val, ca)
-  res = ctx.getFactoryImpl().toStaticAtom(ca)
+  res = ca.toStaticAtom()
   return ok()
 
 proc toJS*(ctx: JSContext; atom: CAtom): JSValue =
   if atom == CAtomNull:
     return JS_NULL
-  return ctx.toJS(ctx.getFactoryImpl().toStr(atom))
+  return ctx.toJS(atom.toStr())
 
 proc toJS*(ctx: JSContext; atom: StaticAtom): JSValue =
   return ctx.toJS($atom)
diff --git a/src/html/chadombuilder.nim b/src/html/chadombuilder.nim
index add5b55d..b6eaab9b 100644
--- a/src/html/chadombuilder.nim
+++ b/src/html/chadombuilder.nim
@@ -33,7 +33,6 @@ type
     charset*: Charset
     confidence*: CharsetConfidence
     document*: Document
-    factory: CAtomFactory
     poppedScript: HTMLScriptElement
 
   DOMBuilderImpl = ChaDOMBuilder
@@ -56,10 +55,10 @@ proc atomToTagTypeImpl(builder: ChaDOMBuilder; atom: CAtom): TagType =
   return atom.toTagType()
 
 proc tagTypeToAtomImpl(builder: ChaDOMBuilder; tagType: TagType): CAtom =
-  return builder.factory.toAtom(tagType)
+  return tagType.toAtom()
 
 proc strToAtomImpl(builder: ChaDOMBuilder; s: string): CAtom =
-  return builder.factory.toAtom(s)
+  return s.toAtom()
 
 proc finish(builder: ChaDOMBuilder) =
   while builder.document.scriptsToExecOnLoad.len > 0:
@@ -70,7 +69,7 @@ proc finish(builder: ChaDOMBuilder) =
 
 proc restart*(wrapper: HTML5ParserWrapper; charset: Charset) =
   let builder = wrapper.builder
-  let document = newDocument(builder.factory)
+  let document = newDocument()
   document.charset = charset
   document.setActiveParser(wrapper)
   document.contentType = "text/html"
@@ -82,7 +81,6 @@ proc restart*(wrapper: HTML5ParserWrapper; charset: Charset) =
     window.document = document
   builder.document = document
   builder.charset = charset
-  assert document.factory != nil
   wrapper.parser = initHTML5Parser(builder, wrapper.opts)
 
 proc setQuirksModeImpl(builder: ChaDOMBuilder; quirksMode: QuirksMode) =
@@ -219,9 +217,9 @@ proc elementPoppedImpl(builder: ChaDOMBuilder; element: Node) =
   elif element of HTMLStyleElement:
     HTMLStyleElement(element).updateSheet()
 
-proc newChaDOMBuilder(url: URL; window: Window; factory: CAtomFactory;
-    confidence: CharsetConfidence; charset = DefaultCharset): ChaDOMBuilder =
-  let document = newDocument(factory)
+proc newChaDOMBuilder(url: URL; window: Window; confidence: CharsetConfidence;
+    charset = DefaultCharset): ChaDOMBuilder =
+  let document = newDocument()
   document.charset = charset
   document.contentType = "text/html"
   document.url = url
@@ -230,7 +228,6 @@ proc newChaDOMBuilder(url: URL; window: Window; factory: CAtomFactory;
     window.document = document
   return ChaDOMBuilder(
     document: document,
-    factory: factory,
     confidence: confidence,
     charset: charset
   )
@@ -238,8 +235,7 @@ proc newChaDOMBuilder(url: URL; window: Window; factory: CAtomFactory;
 # 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 factory = element.document.factory
-  let builder = newChaDOMBuilder(url, nil, factory, ccIrrelevant)
+  let builder = newChaDOMBuilder(url, nil, ccIrrelevant)
   let document = builder.document
   document.mode = element.document.mode
   let state = case element.tagType
@@ -272,12 +268,12 @@ proc parseHTMLFragment*(element: Element; s: string): seq[Node] =
   builder.finish()
   return root.childList
 
-proc newHTML5ParserWrapper*(window: Window; url: URL; factory: CAtomFactory;
+proc newHTML5ParserWrapper*(window: Window; url: URL;
     confidence: CharsetConfidence; charset: Charset): HTML5ParserWrapper =
   let opts = HTML5ParserOpts[Node, CAtom](
     scripting: window.settings.scripting != smFalse
   )
-  let builder = newChaDOMBuilder(url, window, factory, confidence, charset)
+  let builder = newChaDOMBuilder(url, window, confidence, charset)
   let wrapper = HTML5ParserWrapper(
     builder: builder,
     opts: opts,
@@ -368,7 +364,7 @@ proc parseFromString*(ctx: JSContext; parser: DOMParser; str, t: string):
       window.document.url
     else:
       newURL("about:blank").get
-    let builder = newChaDOMBuilder(url, window, window.factory, ccIrrelevant)
+    let builder = newChaDOMBuilder(url, window, ccIrrelevant)
     var parser = initHTML5Parser(builder, HTML5ParserOpts[Node, CAtom]())
     let res = parser.parseChunk(str)
     assert res == PRES_CONTINUE
diff --git a/src/html/dom.nim b/src/html/dom.nim
index 950c2903..e628da6e 100644
--- a/src/html/dom.nim
+++ b/src/html/dom.nim
@@ -109,7 +109,6 @@ type
     timeouts*: TimeoutState
     navigate*: proc(url: URL)
     importMapsAllowed*: bool
-    factory*: CAtomFactory
     pendingResources*: seq[EmptyPromise]
     imageURLCache: Table[string, CachedURLImage]
     svgCache*: Table[string, SVGSVGElement]
@@ -150,11 +149,13 @@ type
     element: Element
     attrlist: seq[Attr]
 
+  CollectionMatchFun = proc(node: Node): bool {.noSideEffect.}
+
   Collection = ref object of RootObj
     islive: bool
     childonly: bool
     root: Node
-    match: proc(node: Node): bool {.noSideEffect.}
+    match: CollectionMatchFun
     snapshot: seq[Node]
     livelen: int
 
@@ -200,7 +201,6 @@ type
     i*: int
 
   Document* = ref object of Node
-    factory*: CAtomFactory
     charset*: Charset
     window* {.jsget: "defaultView".}: Window
     url* {.jsget: "URL".}: URL # not nil
@@ -1100,41 +1100,8 @@ func document*(node: Node): Document =
 template document*(element: Element): Document =
   element.internalDocument
 
-proc toAtom*(window: Window; atom: StaticAtom): CAtom =
-  return window.factory.toAtom(atom)
-
-proc toAtom*(window: Window; s: sink string): CAtom =
-  return window.factory.toAtom(s)
-
-proc toStr*(window: Window; atom: CAtom): lent string =
-  return window.factory.toStr(atom)
-
-proc toAtom*(document: Document; s: sink string): CAtom =
-  return document.factory.toAtom(s)
-
-proc toAtomLower*(document: Document; s: sink string): CAtom =
-  return document.factory.toAtomLower(s)
-
-proc toAtom*(document: Document; at: StaticAtom): CAtom =
-  return document.factory.toAtom(at)
-
-proc toStr(document: Document; atom: CAtom): lent string =
-  return document.factory.toStr(atom)
-
-proc toStaticAtom(document: Document; atom: CAtom): StaticAtom =
-  return document.factory.toStaticAtom(atom)
-
-proc toAtom*(document: Document; tagType: TagType): CAtom =
-  return document.factory.toAtom(tagType)
-
-proc toAtom(document: Document; namespace: Namespace): CAtom =
-  return document.factory.toAtom(namespace)
-
-proc toAtom(document: Document; prefix: NamespacePrefix): CAtom =
-  return document.factory.toAtom(prefix)
-
 func namespace*(element: Element): Namespace =
-  return element.document.factory.toNamespace(element.namespaceURI)
+  return element.namespaceURI.toNamespace()
 
 func tagTypeNoNS(element: Element): TagType =
   return element.localName.toTagType()
@@ -1151,7 +1118,7 @@ func findAttr(element: Element; qualifiedName: CAtom): int =
   return -1
 
 func findAttr(element: Element; qualifiedName: StaticAtom): int =
-  return element.findAttr(element.document.toAtom(qualifiedName))
+  return element.findAttr(qualifiedName.toAtom())
 
 func findAttrNS(element: Element; namespace, qualifiedName: CAtom): int =
   for i, attr in element.attrs.mypairs:
@@ -1186,7 +1153,7 @@ func escapeText(s: string; attributeMode = false): string =
 
 when defined(debug):
   func localNameStr*(element: Element): string =
-    return element.document.toStr(element.localName)
+    return element.localName.toStr()
 
   func `$`*(node: Node): string =
     if node == nil:
@@ -1195,7 +1162,7 @@ when defined(debug):
       let element = Element(node)
       result = "<" & element.localNameStr
       for attr in element.attrs:
-        let k = element.document.toStr(attr.localName)
+        let k = attr.localName.toStr()
         result &= ' ' & k & "=\"" & attr.value.escapeText(true) & "\""
       result &= ">\n"
       for node in element.childList:
@@ -1308,9 +1275,8 @@ iterator inputs(form: HTMLFormElement): HTMLInputElement {.inline.} =
       yield HTMLInputElement(control)
 
 iterator radiogroup*(input: HTMLInputElement): HTMLInputElement {.inline.} =
-  let empty = input.document.toAtom("")
   let name = input.name
-  if name != CAtomNull and name != empty:
+  if name != CAtomNull and name != satUempty.toAtom():
     if input.form != nil:
       for input in input.form.inputs:
         if input.name == name and input.inputType == itRadio:
@@ -1422,8 +1388,6 @@ proc findNode(collection: Collection; node: Node): int =
   collection.refreshCollection()
   return collection.snapshot.find(node)
 
-type CollectionMatchFun = proc(node: Node): bool {.noSideEffect.}
-
 func newCollection[T: Collection](root: Node; match: CollectionMatchFun;
     islive, childonly: bool): T =
   result = T(
@@ -1529,10 +1493,7 @@ proc setCookie(document: Document; cookie: string) {.jsfset: "cookie".} =
 
 # DOMTokenList
 proc newDOMTokenList(element: Element; name: StaticAtom): DOMTokenList =
-  return DOMTokenList(
-    element: element,
-    localName: element.document.toAtom(name)
-  )
+  return DOMTokenList(element: element, localName: name.toAtom())
 
 iterator items*(tokenList: DOMTokenList): CAtom {.inline.} =
   for tok in tokenList.toks:
@@ -1553,19 +1514,18 @@ func contains(tokenList: DOMTokenList; a: CAtom): bool =
   return a in tokenList.toks
 
 func containsIgnoreCase(tokenList: DOMTokenList; a: StaticAtom): bool =
-  let document = tokenList.element.document
-  return document.factory.containsIgnoreCase(tokenList.toks, a)
+  return tokenList.toks.containsIgnoreCase(a)
 
-func jsContains(tokenList: DOMTokenList; s: string): bool
+proc jsContains(tokenList: DOMTokenList; s: string): bool
     {.jsfunc: "contains".} =
-  return tokenList.element.document.toAtom(s) in tokenList.toks
+  return s.toAtom() in tokenList.toks
 
 func `$`(tokenList: DOMTokenList): string {.jsfunc: "toString".} =
   var s = ""
   for i, tok in tokenList.toks:
     if i != 0:
       s &= ' '
-    s &= tokenList.element.document.toStr(tok)
+    s &= tok.toStr()
   return move(s)
 
 proc update(tokenList: DOMTokenList) =
@@ -1574,7 +1534,7 @@ proc update(tokenList: DOMTokenList) =
     return
   tokenList.element.attr(tokenList.localName, $tokenList)
 
-func validateDOMToken(ctx: JSContext; document: Document; tok: JSValue):
+proc validateDOMToken(ctx: JSContext; document: Document; tok: JSValue):
     DOMResult[CAtom] =
   var res: string
   ?ctx.fromJS(tok, res)
@@ -1583,7 +1543,7 @@ func validateDOMToken(ctx: JSContext; document: Document; tok: JSValue):
   if AsciiWhitespace in res:
     return errDOMException("Got a string containing whitespace",
       "InvalidCharacterError")
-  return ok(document.toAtom(res))
+  return ok(res.toAtom())
 
 proc add(ctx: JSContext; tokenList: DOMTokenList; tokens: varargs[JSValue]):
     Err[DOMException] {.jsfunc.} =
@@ -1643,7 +1603,7 @@ const SupportedTokensMap = {
 
 func supports(tokenList: DOMTokenList; token: string):
     JSResult[bool] {.jsfunc.} =
-  let localName = tokenList.element.document.toStaticAtom(tokenList.localName)
+  let localName = tokenList.localName.toStaticAtom()
   for it in SupportedTokensMap:
     if it[0] == localName:
       let lowercase = token.toLowerAscii()
@@ -1673,15 +1633,15 @@ proc validateQName(qname: string): DOMResult[void] =
 
 # DOMStringMap
 proc delete(map: var DOMStringMap; name: string): bool {.jsfunc.} =
-  let name = map.target.document.toAtom("data-" & name.camelToKebabCase())
+  let name = ("data-" & name.camelToKebabCase()).toAtom()
   let i = map.target.findAttr(name)
   if i != -1:
     map.target.delAttr(i)
   return i != -1
 
-func getter(ctx: JSContext; map: var DOMStringMap; name: string): JSValue
+proc getter(ctx: JSContext; map: var DOMStringMap; name: string): JSValue
     {.jsgetownprop.} =
-  let name = map.target.document.toAtom("data-" & name.camelToKebabCase())
+  let name = ("data-" & name.camelToKebabCase()).toAtom()
   let i = map.target.findAttr(name)
   if i != -1:
     return ctx.toJS(map.target.attrs[i].value)
@@ -1698,7 +1658,7 @@ proc setter(map: var DOMStringMap; name, value: string): Err[DOMException]
       "InvalidCharacterError")
   let name = "data-" & name.camelToKebabCase()
   ?name.validateName()
-  let aname = map.target.document.toAtom(name)
+  let aname = name.toAtom()
   map.target.attr(aname, value)
   return ok()
 
@@ -1706,7 +1666,7 @@ func names(ctx: JSContext; map: var DOMStringMap): JSPropertyEnumList
     {.jspropnames.} =
   var list = newJSPropertyEnumList(ctx, uint32(map.target.attrs.len))
   for attr in map.target.attrs:
-    let k = map.target.document.toStr(attr.localName)
+    let k = attr.localName.toStr()
     if k.startsWith("data-") and AsciiUpperAlpha notin k:
       list.add(k["data-".len .. ^1].kebabToCamelCase())
   return list
@@ -1767,16 +1727,15 @@ proc names(ctx: JSContext; collection: HTMLCollection): JSPropertyEnumList
   let L = collection.length
   var list = newJSPropertyEnumList(ctx, L)
   var ids = initOrderedSet[CAtom]()
-  let empty = ctx.toAtom("")
   for u in 0 ..< L:
     list.add(u)
     let element = collection.item(u)
-    if element.id != CAtomNull and element.id != empty:
+    if element.id != CAtomNull and element.id != satUempty.toAtom():
       ids.incl(element.id)
     if element.namespace == Namespace.HTML:
       ids.incl(element.name)
   for id in ids:
-    list.add(collection.root.document.toStr(id))
+    list.add(id.toStr())
   return list
 
 # HTMLFormControlsCollection
@@ -2035,10 +1994,10 @@ proc getAttr(map: NamedNodeMap; dataIdx: int): Attr =
   map.attrlist.add(attr)
   return attr
 
-func normalizeAttrQName(element: Element; qualifiedName: string): CAtom =
+proc normalizeAttrQName(element: Element; qualifiedName: string): CAtom =
   if element.namespace == Namespace.HTML and not element.document.isxml:
-    return element.document.toAtomLower(qualifiedName)
-  return element.document.toAtom(qualifiedName)
+    return qualifiedName.toAtomLower()
+  return qualifiedName.toAtom()
 
 func hasAttributes(element: Element): bool {.jsfunc.} =
   return element.attrs.len > 0
@@ -2056,22 +2015,22 @@ func attributes(element: Element): NamedNodeMap {.jsfget.} =
     ))
   return element.cachedAttributes
 
-func findAttr(element: Element; qualifiedName: string): int =
+proc findAttr(element: Element; qualifiedName: string): int =
   return element.findAttr(element.normalizeAttrQName(qualifiedName))
 
-func findAttrNS(element: Element; namespace, localName: string): int =
-  let namespace = element.document.toAtom(namespace)
-  let localName = element.document.toAtom(localName)
+proc findAttrNS(element: Element; namespace, localName: string): int =
+  let namespace = namespace.toAtom()
+  let localName = localName.toAtom()
   return element.findAttrNS(namespace, localName)
 
-func hasAttribute(element: Element; qualifiedName: string): bool {.jsfunc.} =
+proc hasAttribute(element: Element; qualifiedName: string): bool {.jsfunc.} =
   return element.findAttr(qualifiedName) != -1
 
-func hasAttributeNS(element: Element; namespace, localName: string): bool
+proc hasAttributeNS(element: Element; namespace, localName: string): bool
     {.jsfunc.} =
   return element.findAttrNS(namespace, localName) != -1
 
-func getAttribute(ctx: JSContext; element: Element; qualifiedName: string):
+func getAttribute(ctx: JSContext; element: Element; qualifiedName: CAtom):
     JSValue {.jsfunc.} =
   let i = element.findAttr(qualifiedName)
   if i != -1:
@@ -2079,7 +2038,7 @@ func getAttribute(ctx: JSContext; element: Element; qualifiedName: string):
   return JS_NULL
 
 func getAttributeNS(ctx: JSContext; element: Element;
-    namespace, localName: string): JSValue {.jsfunc.} =
+    namespace, localName: CAtom): JSValue {.jsfunc.} =
   let i = element.findAttrNS(namespace, localName)
   if i != -1:
     return ctx.toJS(element.attrs[i].value)
@@ -2107,7 +2066,7 @@ proc item(map: NamedNodeMap; i: uint32): Attr {.jsfunc.} =
     return map.getAttr(int(i))
   return nil
 
-func getter(ctx: JSContext; map: NamedNodeMap; atom: JSAtom): Opt[Attr]
+proc getter(ctx: JSContext; map: NamedNodeMap; atom: JSAtom): Opt[Attr]
     {.jsgetownprop.} =
   var u: uint32
   if ctx.fromJS(atom, u).isSome:
@@ -2128,7 +2087,7 @@ func names(ctx: JSContext; map: NamedNodeMap): JSPropertyEnumList
   var names: HashSet[string]
   let element = map.element
   for attr in element.attrs:
-    let name = element.document.toStr(attr.qualifiedName)
+    let name = attr.qualifiedName.toStr()
     if element.namespace == Namespace.HTML and AsciiUpperAlpha in name:
       continue
     if name in names:
@@ -2141,11 +2100,10 @@ func length(characterData: CharacterData): uint32 {.jsfget.} =
   return uint32(characterData.data.utf16Len)
 
 func tagName(element: Element): string {.jsfget.} =
-  let document = element.document
-  result = document.toStr(element.prefix)
+  result = element.prefix.toStr()
   if result.len > 0:
     result &= ':'
-  result &= document.toStr(element.localName)
+  result &= element.localName.toStr()
   if element.namespace == Namespace.HTML:
     result = result.toUpperAscii()
 
@@ -2154,7 +2112,7 @@ func nodeName(node: Node): string {.jsfget.} =
     return Element(node).tagName
   if node of Attr:
     let attr = Attr(node)
-    return attr.ownerElement.document.toStr(attr.data.qualifiedName)
+    return attr.data.qualifiedName.toStr()
   if node of DocumentType:
     return DocumentType(node).name
   if node of CDATASection:
@@ -2417,17 +2375,17 @@ func findFirstChildNotOf(node: Node; tagType: set[TagType]): Element =
       return element
   return nil
 
-func getElementById(document: Document; id: string): Element {.jsfunc.} =
+proc getElementById(document: Document; id: string): Element {.jsfunc.} =
   if id.len == 0:
     return nil
-  let id = document.toAtom(id)
+  let id = id.toAtom()
   for child in document.elements:
     if child.id == id:
       return child
   return nil
 
-func getElementsByName(document: Document; name: CAtom): NodeList {.jsfunc.} =
-  if name == document.toAtom(""):
+proc getElementsByName(document: Document; name: CAtom): NodeList {.jsfunc.} =
+  if name == satUempty.toAtom():
     return document.newNodeList(
       func(node: Node): bool =
         return false,
@@ -2441,11 +2399,11 @@ func getElementsByName(document: Document; name: CAtom): NodeList {.jsfunc.} =
     childonly = false
   )
 
-func getElementsByTagNameImpl(root: Node; tagName: string): HTMLCollection =
+proc getElementsByTagNameImpl(root: Node; tagName: string): HTMLCollection =
   if tagName == "*":
     return root.newHTMLCollection(isElement, islive = true, childonly = false)
-  let localName = root.document.toAtom(tagName)
-  let localNameLower = root.document.factory.toLowerAscii(localName)
+  let localName = tagName.toAtom()
+  let localNameLower = localName.toLowerAscii()
   return root.newHTMLCollection(
     func(node: Node): bool =
       if node of Element:
@@ -2458,27 +2416,26 @@ func getElementsByTagNameImpl(root: Node; tagName: string): HTMLCollection =
     childonly = false
   )
 
-func getElementsByTagName(document: Document; tagName: string): HTMLCollection
+proc getElementsByTagName(document: Document; tagName: string): HTMLCollection
     {.jsfunc.} =
   return document.getElementsByTagNameImpl(tagName)
 
-func getElementsByTagName(element: Element; tagName: string): HTMLCollection
+proc getElementsByTagName(element: Element; tagName: string): HTMLCollection
     {.jsfunc.} =
   return element.getElementsByTagNameImpl(tagName)
 
-func getElementsByClassNameImpl(node: Node; classNames: string):
+proc getElementsByClassNameImpl(node: Node; classNames: string):
     HTMLCollection =
   var classAtoms = newSeq[CAtom]()
   for class in classNames.split(AsciiWhitespace):
-    classAtoms.add(node.document.toAtom(class))
+    classAtoms.add(class.toAtom())
   return node.newHTMLCollection(
     func(node: Node): bool =
       if node of Element:
         let element = Element(node)
         if element.document.mode == QUIRKS:
-          let factory = element.document.factory
           for class in classAtoms:
-            if not factory.containsIgnoreCase(element.classList.toks, class):
+            if not element.classList.toks.containsIgnoreCase(class):
               return false
         else:
           for class in classAtoms:
@@ -2489,11 +2446,11 @@ func getElementsByClassNameImpl(node: Node; classNames: string):
     childonly = false
   )
 
-func getElementsByClassName(document: Document; classNames: string):
+proc getElementsByClassName(document: Document; classNames: string):
     HTMLCollection {.jsfunc.} =
   return document.getElementsByClassNameImpl(classNames)
 
-func getElementsByClassName(element: Element; classNames: string):
+proc getElementsByClassName(element: Element; classNames: string):
     HTMLCollection {.jsfunc.} =
   return element.getElementsByClassNameImpl(classNames)
 
@@ -2524,25 +2481,23 @@ proc names(ctx: JSContext; document: Document): JSPropertyEnumList
   #TODO I'm not quite sure why location isn't added, so I'll add it
   # manually for now.
   list.add("location")
-  let empty = ctx.toAtom("")
   #TODO exposed embed, exposed object
   for child in document.elements({TAG_FORM, TAG_IFRAME, TAG_IMG}):
-    if child.name != CAtomNull and child.name != empty:
+    if child.name != CAtomNull and child.name != satUempty.toAtom():
       if child.tagType == TAG_IMG and child.id != CAtomNull and
-          child.id != empty:
-        list.add(ctx.toStr(child.id))
-      list.add(ctx.toStr(child.name))
+          child.id != satUempty.toAtom():
+        list.add(child.id.toStr())
+      list.add(child.name.toStr())
   return list
 
 proc getter(ctx: JSContext; document: Document; s: string): JSValue
     {.jsgetownprop.} =
   if s.len != 0:
-    let id = ctx.toAtom(s)
-    let empty = ctx.toAtom("")
+    let id = s.toAtom()
     #TODO exposed embed, exposed object
     for child in document.elements({TAG_FORM, TAG_IFRAME, TAG_IMG}):
       if child.tagType == TAG_IMG and child.id == id and
-          child.name != CAtomNull and child.name != empty:
+          child.name != CAtomNull and child.name != satUempty.toAtom():
         return ctx.toJS(child)
       if child.name == id:
         return ctx.toJS(child)
@@ -2558,7 +2513,7 @@ func attr*(element: Element; s: CAtom): lent string =
     return emptyStr
 
 func attr*(element: Element; s: StaticAtom): lent string =
-  return element.attr(element.document.toAtom(s))
+  return element.attr(s.toAtom())
 
 func attrl*(element: Element; s: StaticAtom): Option[int32] =
   return parseInt32(element.attr(s))
@@ -2579,8 +2534,7 @@ func attrb*(element: Element; s: CAtom): bool =
   return element.findAttr(s) != -1
 
 func attrb*(element: Element; at: StaticAtom): bool =
-  let atom = element.document.toAtom(at)
-  return element.attrb(atom)
+  return element.attrb(at.toAtom())
 
 # https://html.spec.whatwg.org/multipage/parsing.html#serialising-html-fragments
 func serializesAsVoid(element: Element): bool =
@@ -2590,13 +2544,13 @@ func serializesAsVoid(element: Element): bool =
 func serializeFragmentInner(res: var string; child: Node; parentType: TagType) =
   if child of Element:
     let element = Element(child)
-    let tags = element.document.toStr(element.localName)
+    let tags = element.localName.toStr()
     res &= '<'
     #TODO qualified name if not HTML, SVG or MathML
     res &= tags
     #TODO custom elements
     for attr in element.attrs:
-      let k = element.document.toStr(attr.qualifiedName)
+      let k = attr.qualifiedName.toStr()
       res &= ' ' & k & "=\"" & attr.value.escapeText(true) & "\""
     res &= '>'
     res.serializeFragment(element)
@@ -2692,8 +2646,8 @@ func applyMediaQuery(ss: CSSStylesheet; window: Window): CSSStylesheet =
 
 proc applyUASheet*(document: Document) =
   const ua = staticRead"res/ua.css"
-  document.uaSheets.add(ua.parseStylesheet(document.factory, nil,
-    document.window.attrsp).applyMediaQuery(document.window))
+  document.uaSheets.add(ua.parseStylesheet(nil, document.window.attrsp)
+    .applyMediaQuery(document.window))
   if document.documentElement != nil:
     document.documentElement.invalidate()
 
@@ -2701,14 +2655,14 @@ proc applyQuirksSheet*(document: Document) =
   if document.window == nil:
     return
   const quirks = staticRead"res/quirk.css"
-  document.uaSheets.add(quirks.parseStylesheet(document.factory, nil,
-    document.window.attrsp).applyMediaQuery(document.window))
+  document.uaSheets.add(quirks.parseStylesheet(nil, document.window.attrsp)
+    .applyMediaQuery(document.window))
   if document.documentElement != nil:
     document.documentElement.invalidate()
 
 proc applyUserSheet*(document: Document; user: string) =
-  document.userSheet = user.parseStylesheet(document.factory, nil,
-    document.window.attrsp).applyMediaQuery(document.window)
+  document.userSheet = user.parseStylesheet(nil, document.window.attrsp)
+    .applyMediaQuery(document.window)
   if document.documentElement != nil:
     document.documentElement.invalidate()
 
@@ -2785,10 +2739,10 @@ func formmethod*(element: Element): FormMethod =
         return parseFormMethod(element.form.attr(satMethod))
   return fmGet
 
-func findAnchor*(document: Document; id: string): Element =
+proc findAnchor*(document: Document; id: string): Element =
   if id.len == 0:
     return nil
-  let id = document.toAtom(id)
+  let id = id.toAtom()
   for child in document.elements:
     if child.id == id:
       return child
@@ -2856,7 +2810,7 @@ proc fireEvent*(window: Window; event: Event; target: EventTarget) =
   discard window.jsctx.dispatch(target, event)
 
 proc fireEvent*(window: Window; name: StaticAtom; target: EventTarget) =
-  let event = newEvent(window.toAtom(name), target)
+  let event = newEvent(name.toAtom(), target)
   event.isTrusted = true
   window.fireEvent(event, target)
 
@@ -2892,8 +2846,7 @@ proc hyperlinkGet(ctx: JSContext; this: JSValue; magic: cint): JSValue
   let url = element.reinitURL()
   if url.isSome:
     let href = ctx.toJS(url.get)
-    let s = ctx.toStr(sa)
-    let res = JS_GetPropertyStr(ctx, href, cstring(s))
+    let res = JS_GetPropertyStr(ctx, href, cstring(sa.toStr()))
     JS_FreeValue(ctx, href)
     return res
   if sa == satProtocol:
@@ -2915,8 +2868,8 @@ proc hyperlinkSet(ctx: JSContext; this, val: JSValue; magic: cint): JSValue
   let url = element.reinitURL()
   if url.isSome:
     let href = ctx.toJS(url)
-    let s = ctx.toStr(sa)
-    let res = JS_SetPropertyStr(ctx, href, cstring(s), JS_DupValue(ctx, val))
+    let res = JS_SetPropertyStr(ctx, href, cstring(sa.toStr()),
+      JS_DupValue(ctx, val))
     if res < 0:
       return JS_EXCEPTION
     var outs: string
@@ -2929,7 +2882,7 @@ proc hyperlinkGetProp(ctx: JSContext; element: HTMLElement; a: JSAtom;
     desc: ptr JSPropertyDescriptor): JSValue =
   var s: string
   if ctx.fromJS(a, s).isSome:
-    let sa = ctx.toStaticAtom(ctx.toAtom(s))
+    let sa = s.toStaticAtom()
     if sa in {satHref, satOrigin, satProtocol, satUsername, satPassword,
         satHost, satHostname, satPort, satPathname, satSearch, satHash}:
       if desc != nil:
@@ -3099,7 +3052,7 @@ func inputString*(input: HTMLInputElement): CharacterData =
     return input.internalValue
 
 # <label>
-func control*(label: HTMLLabelElement): FormAssociatedElement {.jsfget.} =
+proc control*(label: HTMLLabelElement): FormAssociatedElement {.jsfget.} =
   let f = label.attr(satFor)
   if f != "":
     let elem = label.document.getElementById(f)
@@ -3113,7 +3066,7 @@ func control*(label: HTMLLabelElement): FormAssociatedElement {.jsfget.} =
     return nil
   return nil
 
-func form(label: HTMLLabelElement): HTMLFormElement {.jsfget.} =
+proc form(label: HTMLLabelElement): HTMLFormElement {.jsfget.} =
   let control = label.control
   if control != nil:
     return control.form
@@ -3376,8 +3329,8 @@ proc updateSheet*(this: HTMLStyleElement) =
   let document = this.document
   let window = document.window
   if window != nil:
-    this.sheet = this.textContent.parseStylesheet(document.factory,
-      document.baseURL, window.attrsp).applyMediaQuery(window)
+    this.sheet = this.textContent.parseStylesheet(document.baseURL,
+      window.attrsp).applyMediaQuery(window)
     document.applyAuthorSheets()
 
 # <table>
@@ -3642,7 +3595,7 @@ func newComment(ctx: JSContext; data: sink string = ""): Comment {.jsctor.} =
 proc newElement*(document: Document; localName, namespaceURI, prefix: CAtom):
     Element =
   let tagType = localName.toTagType()
-  let sns = document.toStaticAtom(namespaceURI)
+  let sns = namespaceURI.toStaticAtom()
   let element: Element = case tagType
   of TAG_INPUT:
     HTMLInputElement()
@@ -3755,31 +3708,25 @@ proc newElement*(document: Document; localName, namespaceURI, prefix: CAtom):
 
 proc newElement*(document: Document; localName: CAtom;
     namespace = Namespace.HTML; prefix = NO_PREFIX): Element =
-  return document.newElement(localName, document.toAtom(namespace),
-    document.toAtom(prefix))
+  return document.newElement(localName, namespace.toAtom(), prefix.toAtom())
 
 proc newHTMLElement*(document: Document; tagType: TagType): HTMLElement =
-  let localName = document.toAtom(tagType)
+  let localName = tagType.toAtom()
   return HTMLElement(document.newElement(localName, Namespace.HTML, NO_PREFIX))
 
-proc newDocument*(factory: CAtomFactory): Document =
+proc newDocument*(): Document {.jsctor.} =
   let document = Document(
     url: newURL("about:blank").get,
     index: -1,
-    factory: factory,
     contentType: "application/xml"
   )
   document.implementation = DOMImplementation(document: document)
   return document
 
-proc newDocument(ctx: JSContext): Document {.jsctor.} =
-  return newDocument(ctx.getGlobal().factory)
-
-proc newXMLDocument(ctx: JSContext): XMLDocument =
+proc newXMLDocument(): XMLDocument =
   let document = XMLDocument(
     url: newURL("about:blank").get,
     index: -1,
-    factory: ctx.getGlobal().factory,
     contentType: "application/xml"
   )
   document.implementation = DOMImplementation(document: document)
@@ -4021,8 +3968,7 @@ proc setValue(this: CSSStyleDeclaration; i: int; cvals: seq[CSSComponentValue]):
     return err()
   # dummyAttrs can be safely used because the result is discarded.
   var dummy: seq[CSSComputedEntry] = @[]
-  ?dummy.parseComputedValues(this.decls[i].name, cvals, dummyAttrs,
-    this.element.document.factory)
+  ?dummy.parseComputedValues(this.decls[i].name, cvals, dummyAttrs)
   this.decls[i].value = cvals
   return ok()
 
@@ -4061,8 +4007,7 @@ proc setProperty(this: CSSStyleDeclaration; name, value: string):
       return ok()
   else:
     var dummy: seq[CSSComputedEntry] = @[]
-    let val0 = dummy.parseComputedValues(name, cvals, dummyAttrs,
-      this.element.document.factory)
+    let val0 = dummy.parseComputedValues(name, cvals, dummyAttrs)
     if val0.isNone:
       return ok()
     this.decls.add(CSSDeclaration(name: name, value: cvals))
@@ -4123,7 +4068,7 @@ proc loadSheet(window: Window; link: HTMLLinkElement; url: URL):
     return newResolvedPromise(JSResult[string].err(nil))
   ).then(proc(s: JSResult[string]): Promise[CSSStylesheet] =
     if s.isSome:
-      let sheet = s.get.parseStylesheet(window.factory, url, window.attrsp)
+      let sheet = s.get.parseStylesheet(url, window.attrsp)
       var promises: seq[EmptyPromise] = @[]
       var sheets = newSeq[CSSStylesheet](sheet.importList.len)
       for i, url in sheet.importList:
@@ -4389,11 +4334,11 @@ proc reflectEvent(element: Element; target: EventTarget;
     # directly here, but a wrapper function that calls fun. Currently
     # you can run removeEventListener with element.onclick, that should
     # not work.
-    doAssert ctx.addEventListener(target, document.toAtom(ctype), fun).isSome
+    doAssert ctx.addEventListener(target, ctype.toAtom(), fun).isSome
   JS_FreeValue(ctx, fun)
 
 proc reflectAttr(element: Element; name: CAtom; value: Option[string]) =
-  let name = element.document.toStaticAtom(name)
+  let name = name.toStaticAtom()
   template reflect_str(element: Element; n: StaticAtom; val: untyped) =
     if name == n:
       element.val = value.get("")
@@ -4401,7 +4346,7 @@ proc reflectAttr(element: Element; name: CAtom; value: Option[string]) =
   template reflect_atom(element: Element; n: StaticAtom; val: untyped) =
     if name == n:
       if value.isSome:
-        element.val = element.document.toAtom(value.get)
+        element.val = value.get.toAtom()
       else:
         element.val = CAtomNull
       return
@@ -4414,7 +4359,7 @@ proc reflectAttr(element: Element; name: CAtom; value: Option[string]) =
     if value.isSome:
       for x in value.get.split(AsciiWhitespace):
         if x != "":
-          let a = element.document.toAtom(x)
+          let a = x.toAtom()
           if a notin element.val:
             element.val.toks.add(a)
   template reflect_domtoklist(element: Element; n: StaticAtom; val: untyped) =
@@ -4543,20 +4488,20 @@ proc attr*(element: Element; name: CAtom; value: sink string) =
   element.reflectAttr(name, some(element.attrs[i].value))
 
 proc attr*(element: Element; name: StaticAtom; value: sink string) =
-  element.attr(element.document.toAtom(name), value)
+  element.attr(name.toAtom(), value)
 
 proc attrns*(element: Element; localName: CAtom; prefix: NamespacePrefix;
     namespace: Namespace; value: sink string) =
   if prefix == NO_PREFIX and namespace == NO_NAMESPACE:
     element.attr(localName, value)
     return
-  let namespace = element.document.toAtom(namespace)
+  let namespace = namespace.toAtom()
   let i = element.findAttrNS(namespace, localName)
   var prefixAtom, qualifiedName: CAtom
   if prefix != NO_PREFIX:
-    prefixAtom = element.document.toAtom(prefix)
-    let tmp = $prefix & ':' & element.document.toStr(localName)
-    qualifiedName = element.document.toAtom(tmp)
+    prefixAtom = prefix.toAtom()
+    let tmp = $prefix & ':' & localName.toStr()
+    qualifiedName = tmp.toAtom()
   else:
     qualifiedName = localName
   if i != -1:
@@ -4590,9 +4535,9 @@ proc setAttribute(element: Element; qualifiedName: string; value: sink string):
   ?qualifiedName.validateName()
   let qualifiedName = if element.namespace == Namespace.HTML and
       not element.document.isxml:
-    element.document.toAtomLower(qualifiedName)
+    qualifiedName.toAtomLower()
   else:
-    element.document.toAtom(qualifiedName)
+    qualifiedName.toAtom()
   element.attr(qualifiedName, value)
   return ok()
 
@@ -4601,7 +4546,7 @@ proc setAttributeNS(element: Element; namespace, qualifiedName,
   ?qualifiedName.validateQName()
   let ps = qualifiedName.until(':')
   let prefix = if ps.len < qualifiedName.len: ps else: ""
-  let localName = element.document.toAtom(qualifiedName.substr(prefix.len))
+  let localName = qualifiedName.substr(prefix.len).toAtom()
   #TODO atomize here
   if prefix != "" and namespace == "" or
       prefix == "xml" and namespace != $Namespace.XML or
@@ -4610,8 +4555,8 @@ proc setAttributeNS(element: Element; namespace, qualifiedName,
       namespace == $Namespace.XMLNS and qualifiedName != "xmlns" and
         prefix != "xmlns":
     return errDOMException("Unexpected namespace", "NamespaceError")
-  let qualifiedName = element.document.toAtom(qualifiedName)
-  let namespace = element.document.toAtom(namespace)
+  let qualifiedName = qualifiedName.toAtom()
+  let namespace = namespace.toAtom()
   let i = element.findAttrNS(namespace, localName)
   if i != -1:
     element.attrs[i].value = value
@@ -5534,9 +5479,9 @@ proc createElement(document: Document; localName: string): DOMResult[Element]
     {.jsfunc.} =
   ?localName.validateName()
   let localName = if not document.isxml:
-    document.toAtomLower(localName)
+    localName.toAtomLower()
   else:
-    document.toAtom(localName)
+    localName.toAtom()
   let namespace = if not document.isxml:
     #TODO or content type is application/xhtml+xml
     Namespace.HTML
@@ -5547,7 +5492,7 @@ proc createElement(document: Document; localName: string): DOMResult[Element]
 proc validateAndExtract(ctx: JSContext; document: Document; qname: string;
     namespace, prefixOut, localNameOut: var CAtom): DOMResult[void] =
   ?qname.validateQName()
-  if namespace == ctx.toAtom(""):
+  if namespace == satUempty.toAtom():
     namespace = CAtomNull
   var prefix = ""
   var localName = qname.until(':')
@@ -5557,13 +5502,13 @@ proc validateAndExtract(ctx: JSContext; document: Document; qname: string;
   if namespace == CAtomNull and prefix != "":
     return errDOMException("Got namespace prefix, but no namespace",
       "NamespaceError")
-  let sns = document.toStaticAtom(namespace)
+  let sns = namespace.toStaticAtom()
   if prefix == "xml" and sns != satNamespaceXML:
     return errDOMException("Expected XML namespace", "NamespaceError")
   if (qname == "xmlns" or prefix == "xmlns") != (sns == satNamespaceXMLNS):
     return errDOMException("Expected XMLNS namespace", "NamespaceError")
-  prefixOut = if prefix == "": CAtomNull else: document.toAtom(prefix)
-  localNameOut = document.toAtom(localName)
+  prefixOut = if prefix == "": CAtomNull else: prefix.toAtom()
+  localNameOut = localName.toAtom()
   ok()
 
 proc createElementNS(ctx: JSContext; document: Document; namespace: CAtom;
@@ -5586,7 +5531,7 @@ proc createDocumentType(implementation: var DOMImplementation; qualifiedName,
 proc createDocument(ctx: JSContext; implementation: var DOMImplementation;
     namespace: CAtom; qname0 = JS_NULL; doctype = none(DocumentType)):
     DOMResult[XMLDocument] {.jsfunc.} =
-  let document = newXMLDocument(ctx)
+  let document = newXMLDocument()
   var qname = ""
   if not JS_IsNull(qname0):
     ?ctx.fromJS(qname0, qname)
@@ -5599,15 +5544,15 @@ proc createDocument(ctx: JSContext; implementation: var DOMImplementation;
   if element != nil:
     document.append(element)
   document.origin = implementation.document.origin
-  case document.toStaticAtom(namespace)
+  case namespace.toStaticAtom()
   of satNamespaceHTML: document.contentType = "application/xml+html"
   of satNamespaceSVG: document.contentType = "image/svg+xml"
   else: discard
   return ok(document)
 
-proc createHTMLDocument(ctx: JSContext; implementation: var DOMImplementation;
+proc createHTMLDocument(implementation: var DOMImplementation;
     title = none(string)): Document {.jsfunc.} =
-  let doc = newDocument(ctx)
+  let doc = newDocument()
   doc.contentType = "text/html"
   doc.append(doc.newDocumentType("html", "", ""))
   let html = doc.newHTMLElement(TAG_HTML)
@@ -5650,11 +5595,11 @@ proc createProcessingInstruction(document: Document; target, data: string):
 
 proc createEvent(ctx: JSContext; document: Document; atom: CAtom):
     DOMResult[Event] {.jsfunc.} =
-  case ctx.toStaticAtom(ctx.toLowerAscii(atom))
+  case atom.toLowerAscii().toStaticAtom()
   of satCustomevent:
-    return ok(ctx.newCustomEvent(ctx.toAtom("")))
+    return ok(ctx.newCustomEvent(satUempty.toAtom()))
   of satEvent, satEvents, satSvgevents:
-    return ok(newEvent(ctx.toAtom(""), nil))
+    return ok(newEvent(satUempty.toAtom(), nil))
   else:
     return errDOMException("Event not supported", "NotSupportedError")
 
@@ -5716,7 +5661,7 @@ proc clone(node: Node; document = none(Document), deep = false): Node =
     Node(x)
   elif node of Document:
     let document = Document(node)
-    let x = newDocument(document.factory)
+    let x = newDocument()
     x.charset = document.charset
     x.contentType = document.contentType
     x.url = document.url
@@ -5796,7 +5741,7 @@ func isSameNode(node, other: Node): bool {.jsfunc.} =
   return node == other
 
 proc querySelectorImpl(node: Node; q: string): DOMResult[Element] =
-  let selectors = parseSelectors(q, node.document.factory)
+  let selectors = parseSelectors(q)
   if selectors.len == 0:
     return errDOMException("Invalid selector: " & q, "SyntaxError")
   for element in node.elements:
@@ -5815,7 +5760,7 @@ proc querySelector(this: DocumentFragment; q: string): DOMResult[Element]
   return this.querySelectorImpl(q)
 
 proc querySelectorAllImpl(node: Node; q: string): DOMResult[NodeList] =
-  let selectors = parseSelectors(q, node.document.factory)
+  let selectors = parseSelectors(q)
   if selectors.len == 0:
     return errDOMException("Invalid selector: " & q, "SyntaxError")
   return ok(node.newNodeList(
@@ -5935,8 +5880,7 @@ proc jsReflectSet(ctx: JSContext; this, val: JSValue; magic: cint): JSValue
       assert ctx.fromJS(this, target).isSome
       ctx.definePropertyC(this, $entry.attrname, JS_DupValue(ctx, val))
       #TODO I haven't checked but this might also be wrong
-      let ctype = ctx.getGlobal().toAtom(entry.ctype)
-      doAssert ctx.addEventListener(target, ctype, val).isSome
+      doAssert ctx.addEventListener(target, entry.ctype.toAtom(), val).isSome
   return JS_DupValue(ctx, val)
 
 func getReflectFunctions(tags: set[TagType]): seq[TabGetSet] =
@@ -6210,16 +6154,13 @@ getParentImpl = proc(ctx: JSContext; eventTarget: EventTarget; event: Event):
     EventTarget =
   if eventTarget of Node:
     if eventTarget of Document:
-      if event.ctype == ctx.toAtom(satLoad):
+      if event.ctype == satLoad.toAtom():
         return nil
       # if no browsing context, then window will be nil anyway
       return Document(eventTarget).window
     return Node(eventTarget).parentNode
   return nil
 
-getFactoryImpl = proc(ctx: JSContext): CAtomFactory =
-  return ctx.getGlobal().factory
-
 errorImpl = proc(ctx: JSContext; ss: varargs[string]) =
   ctx.getGlobal().console.error(ss)
 
diff --git a/src/html/env.nim b/src/html/env.nim
index a771a33f..f3a7f8d3 100644
--- a/src/html/env.nim
+++ b/src/html/env.nim
@@ -325,7 +325,7 @@ proc postMessage(ctx: JSContext; window: Window; value: JSValue): Err[void]
   ?ctx.fromJS(value, s)
   let data = JS_ParseJSON(ctx, cstring(s), csize_t(s.len),
     cstring"<postMessage>")
-  let event = ctx.newMessageEvent(ctx.toAtom(satMessage),
+  let event = ctx.newMessageEvent(satMessage.toAtom(),
     MessageEventInit(data: data))
   window.fireEvent(event, window)
   ok()
@@ -336,7 +336,7 @@ proc setOnLoad(ctx: JSContext; window: Window; val: JSValue)
     let this = ctx.toJS(window)
     ctx.definePropertyC(this, "onload", JS_DupValue(ctx, val))
     #TODO I haven't checked but this might also be wrong
-    doAssert ctx.addEventListener(window, window.toAtom(satLoad), val).isSome
+    doAssert ctx.addEventListener(window, satLoad.toAtom(), val).isSome
     JS_FreeValue(ctx, this)
 
 proc loadJSModule(ctx: JSContext; moduleName: cstringConst; opaque: pointer):
@@ -423,8 +423,8 @@ proc addScripting*(window: Window) =
   ctx.addPerformanceModule()
 
 proc newWindow*(scripting: ScriptingMode; images, styling, autofocus: bool;
-    attrsp: ptr WindowAttributes; factory: CAtomFactory; loader: FileLoader;
-    url: URL; urandom: PosixStream; imageTypes: Table[string, string];
+    attrsp: ptr WindowAttributes; loader: FileLoader; url: URL;
+    urandom: PosixStream; imageTypes: Table[string, string];
     userAgent, referrer: string): Window =
   let err = newDynFileStream(stderr)
   let window = Window(
@@ -438,7 +438,6 @@ proc newWindow*(scripting: ScriptingMode; images, styling, autofocus: bool;
       scripting: scripting,
       origin: url.origin
     ),
-    factory: factory,
     crypto: Crypto(urandom: urandom),
     imageTypes: imageTypes,
     userAgent: userAgent,
diff --git a/src/html/event.nim b/src/html/event.nim
index b430daa0..11a619ac 100644
--- a/src/html/event.nim
+++ b/src/html/event.nim
@@ -296,10 +296,9 @@ proc newInputEvent*(ctype: CAtom; eventInit = InputEventInit()): InputEvent =
 proc newEventTarget(): EventTarget {.jsctor.} =
   return EventTarget()
 
-proc defaultPassiveValue(ctx: JSContext; ctype: CAtom;
-    eventTarget: EventTarget): bool =
+proc defaultPassiveValue(ctype: CAtom; eventTarget: EventTarget): bool =
   const check = [satTouchstart, satTouchmove, satWheel, satMousewheel]
-  if ctx.toStaticAtom(ctype) in check:
+  if ctype.toStaticAtom() in check:
     return true
   return eventTarget.isDefaultPassiveImpl()
 
@@ -335,13 +334,12 @@ proc invoke(ctx: JSContext; listener: EventListener; event: Event): JSValue =
   return ret
 
 # shared
-proc addAnEventListener(ctx: JSContext; target: EventTarget;
-    listener: EventListener) =
+proc addAnEventListener(target: EventTarget; listener: EventListener) =
   #TODO signals
   if JS_IsUndefined(listener.callback):
     return
   if listener.passive.isNone:
-    listener.passive = some(ctx.defaultPassiveValue(listener.ctype, target))
+    listener.passive = some(defaultPassiveValue(listener.ctype, target))
   if target.findEventListener(listener.ctype, listener.callback,
       listener.capture) == -1: # dedup
     target.eventListeners.add(listener)
@@ -389,14 +387,13 @@ proc addEventListener*(ctx: JSContext; eventTarget: EventTarget; ctype: CAtom;
   if not JS_IsObject(callback) and not JS_IsNull(callback):
     return errTypeError("callback is not an object")
   let (capture, once, passive) = flattenMore(ctx, options)
-  let listener = EventListener(
+  eventTarget.addAnEventListener(EventListener(
     ctype: ctype,
     capture: capture,
     passive: passive,
     once: once,
     callback: JS_DupValue(ctx, callback)
-  )
-  ctx.addAnEventListener(eventTarget, listener)
+  ))
   ok()
 
 proc removeEventListener(ctx: JSContext; eventTarget: EventTarget;
diff --git a/src/html/xmlhttprequest.nim b/src/html/xmlhttprequest.nim
index af3df80b..34602fa6 100644
--- a/src/html/xmlhttprequest.nim
+++ b/src/html/xmlhttprequest.nim
@@ -189,7 +189,7 @@ proc setTimeout(ctx: JSContext; this: XMLHttpRequest; value: uint32):
 
 proc fireProgressEvent(window: Window; target: EventTarget; name: StaticAtom;
     loaded, length: int64) =
-  let event = newProgressEvent(window.factory.toAtom(name), ProgressEventInit(
+  let event = newProgressEvent(name.toAtom(), ProgressEventInit(
     loaded: loaded,
     total: length,
     lengthComputable: length != 0
@@ -503,7 +503,7 @@ proc jsReflectSet(ctx: JSContext; this, val: JSValue; magic: cint): JSValue
     assert ctx.fromJS(this, target).isSome
     ctx.definePropertyC(this, "on" & $atom, JS_DupValue(ctx, val))
     #TODO I haven't checked but this might also be wrong
-    doAssert ctx.addEventListener(target, ctx.toAtom(atom), val).isSome
+    doAssert ctx.addEventListener(target, atom.toAtom(), val).isSome
   return JS_DupValue(ctx, val)
 
 func xhretGetSet(): seq[TabGetSet] =