import deques import macros import options import sets import streams import strutils import tables import css/sheet import data/charset import encoding/decoderstream import html/tags import io/loader import io/request import js/javascript import types/mime import types/referer import types/url import utils/twtstr type FormMethod* = enum FORM_METHOD_GET, FORM_METHOD_POST, FORM_METHOD_DIALOG FormEncodingType* = enum FORM_ENCODING_TYPE_URLENCODED = "application/x-www-form-urlencoded", FORM_ENCODING_TYPE_MULTIPART = "multipart/form-data", FORM_ENCODING_TYPE_TEXT_PLAIN = "text/plain" QuirksMode* = enum NO_QUIRKS, QUIRKS, LIMITED_QUIRKS Namespace* = enum NO_NAMESPACE = "", HTML = "http://www.w3.org/1999/xhtml", MATHML = "http://www.w3.org/1998/Math/MathML", SVG = "http://www.w3.org/2000/svg", XLINK = "http://www.w3.org/1999/xlink", XML = "http://www.w3.org/XML/1998/namespace", XMLNS = "http://www.w3.org/2000/xmlns/" ScriptType = enum NO_SCRIPTTYPE, CLASSIC, MODULE, IMPORTMAP ParserMetadata = enum PARSER_INSERTED, NOT_PARSER_INSERTED ScriptResultType = enum RESULT_NULL, RESULT_UNINITIALIZED, RESULT_SCRIPT, RESULT_IMPORT_MAP_PARSE type Script = object #TODO setings baseURL: URL options: ScriptOptions mutedErrors: bool #TODO parse error/error to rethrow record: string #TODO should be a record... ScriptOptions = object nonce: string integrity: string parserMetadata: ParserMetadata credentialsMode: CredentialsMode referrerPolicy: Option[ReferrerPolicy] renderBlocking: bool ScriptResult = object case t: ScriptResultType of RESULT_NULL, RESULT_UNINITIALIZED: discard of RESULT_SCRIPT: script: Script of RESULT_IMPORT_MAP_PARSE: discard #TODO type Window* = ref object console* {.jsget.}: console navigator* {.jsget.}: Navigator settings*: EnvironmentSettings loader*: Option[FileLoader] jsrt*: JSRuntime jsctx*: JSContext document* {.jsget.}: Document # Navigator stuff Navigator* = ref object plugins: PluginArray PluginArray* = ref object MimeTypeArray* = ref object # "For historical reasons, console is lowercased." # Also, for a more practical reason: so the javascript macros don't confuse # this and the Client console. # TODO: merge those two console* = ref object err*: Stream NamedNodeMap = ref object element: Element attrlist: seq[Attr] EnvironmentSettings* = object scripting*: bool EventTarget* = ref object of RootObj Collection = ref CollectionObj CollectionObj = object of RootObj islive: bool childonly: bool root: Node match: proc(node: Node): bool {.noSideEffect.} snapshot: seq[Node] livelen: int id: int NodeList = ref object of Collection HTMLCollection = ref object of Collection DOMTokenList = ref object toks*: seq[string] element: Element localName: string Node* = ref object of EventTarget nodeType* {.jsget.}: NodeType childList*: seq[Node] parentNode* {.jsget.}: Node parentElement* {.jsget.}: Element root: Node document*: Document index*: int # Index in parents children. -1 for nodes without a parent. # Live collection cache: ids of live collections are saved in all # nodes they refer to. These are removed when the collection is destroyed, # and invalidated when the owner node's children or attributes change. # (We can't just store pointers, because those may be invalidated by # the JavaScript finalizers.) liveCollections: HashSet[int] Attr* = ref object of Node namespaceURI* {.jsget.}: string prefix* {.jsget.}: string localName* {.jsget.}: string value* {.jsget.}: string ownerElement* {.jsget.}: Element DOMImplementation = ref object document: Document Document* = ref object of Node charset*: Charset window*: Window url*: URL #TODO expose as URL (capitalized) location {.jsget.}: URL #TODO should be location mode*: QuirksMode currentScript: HTMLScriptElement isxml*: bool implementation {.jsget.}: DOMImplementation origin: Origin scriptsToExecSoon*: seq[HTMLScriptElement] scriptsToExecInOrder*: Deque[HTMLScriptElement] scriptsToExecOnLoad*: Deque[HTMLScriptElement] parserBlockingScript*: HTMLScriptElement parser_cannot_change_the_mode_flag*: bool is_iframe_srcdoc*: bool focus*: Element contentType* {.jsget.}: string renderBlockingElements: seq[Element] invalidCollections: HashSet[int] # collection ids colln: int cachedSheets: seq[CSSStylesheet] cachedSheetsInvalid: bool CharacterData* = ref object of Node data* {.jsget.}: string Text* = ref object of CharacterData Comment* = ref object of CharacterData CDATASection = ref object of CharacterData ProcessingInstruction = ref object of CharacterData target {.jsget.}: string DocumentFragment* = ref object of Node host*: Element DocumentType* = ref object of Node name*: string publicId*: string systemId*: string Element* = ref object of Node namespace*: Namespace namespacePrefix*: Option[string] prefix*: string localName*: string tagType*: TagType id* {.jsget.}: string classList* {.jsget.}: DOMTokenList attrs*: Table[string, string] attributes* {.jsget.}: NamedNodeMap hover*: bool invalid*: bool HTMLElement* = ref object of Element FormAssociatedElement* = ref object of HTMLElement parserInserted*: bool HTMLInputElement* = ref object of FormAssociatedElement form* {.jsget.}: HTMLFormElement inputType*: InputType value* {.jsget.}: string checked*: bool xcoord*: int ycoord*: int file*: Option[Url] HTMLAnchorElement* = ref object of HTMLElement HTMLSelectElement* = ref object of FormAssociatedElement form* {.jsget.}: HTMLFormElement HTMLSpanElement* = ref object of HTMLElement HTMLOptGroupElement* = ref object of HTMLElement HTMLOptionElement* = ref object of HTMLElement selected*: bool HTMLHeadingElement* = ref object of HTMLElement rank*: uint16 HTMLBRElement* = ref object of HTMLElement HTMLMenuElement* = ref object of HTMLElement HTMLUListElement* = ref object of HTMLElement HTMLOListElement* = ref object of HTMLElement start*: Option[int] HTMLLIElement* = ref object of HTMLElement value* {.jsget.}: Option[int] HTMLStyleElement* = ref object of HTMLElement sheet*: CSSStylesheet HTMLLinkElement* = ref object of HTMLElement sheet*: CSSStylesheet HTMLFormElement* = ref object of HTMLElement name*: string smethod*: string enctype*: string novalidate*: bool constructingentrylist*: bool controls*: seq[FormAssociatedElement] HTMLTemplateElement* = ref object of HTMLElement content*: DocumentFragment HTMLUnknownElement* = ref object of HTMLElement HTMLScriptElement* = ref object of HTMLElement parserDocument*: Document preparationTimeDocument*: Document forceAsync*: bool fromAnExternalFile*: bool readyForParserExec*: bool alreadyStarted*: bool delayingTheLoadEvent: bool ctype: ScriptType internalNonce: string scriptResult*: ScriptResult onReady: (proc()) HTMLBaseElement* = ref object of HTMLElement HTMLAreaElement* = ref object of HTMLElement HTMLButtonElement* = ref object of FormAssociatedElement form* {.jsget.}: HTMLFormElement ctype*: ButtonType value* {.jsget, jsset.}: string HTMLTextAreaElement* = ref object of FormAssociatedElement form* {.jsget.}: HTMLFormElement value* {.jsget.}: string HTMLLabelElement* = ref object of HTMLElement # Reflected attributes. type ReflectType = enum REFLECT_STR, REFLECT_BOOL, REFLECT_INT, REFLECT_INT_GREATER_ZERO, REFLECT_INT_GREATER_EQUAL_ZERO ReflectEntry = tuple[ attrname: string, funcname: string, t: ReflectType, tags: set[TagType], i: int ] template toset(ts: openarray[TagType]): set[TagType] = var tags: system.set[TagType] for tag in ts: tags.incl(tag) tags template makes(name: string, ts: set[TagType]): ReflectEntry = (name, name, REFLECT_STR, ts, 0) template makes(attrname: string, funcname: string, ts: set[TagType]): ReflectEntry = (attrname, funcname, REFLECT_STR, ts, 0) template makes(name: string, ts: varargs[TagType]): ReflectEntry = makes(name, toset(ts)) template makes(attrname: string, funcname: string, ts: varargs[TagType]): ReflectEntry = makes(attrname, funcname, toset(ts)) template makeb(name: string, ts: varargs[TagType]): ReflectEntry = (name, name, REFLECT_BOOL, toset(ts), 0) template makeigz(name: string, ts: varargs[TagType], default = 0): ReflectEntry = (name, name, REFLECT_INT_GREATER_ZERO, toset(ts), default) const ReflectTable0 = [ # non-global attributes makes("target", TAG_A, TAG_AREA, TAG_LABEL, TAG_LINK), makes("href", TAG_LINK), makeb("required", TAG_INPUT, TAG_SELECT, TAG_TEXTAREA), makes("rel", "relList", TAG_A, TAG_LINK, TAG_LABEL), makes("for", "htmlFor", TAG_LABEL), makeigz("cols", TAG_TEXTAREA, 20), makeigz("rows", TAG_TEXTAREA, 1), #