about summary refs log tree commit diff stats
path: root/src/html
diff options
context:
space:
mode:
authorbptato <nincsnevem662@gmail.com>2023-12-25 00:11:14 +0100
committerbptato <nincsnevem662@gmail.com>2023-12-25 00:16:08 +0100
commit1bc63e1cd59bff8e7e266bec80ba961fecf772b7 (patch)
treeada6fa013d78d672da25c1d566458d8503b63ddb /src/html
parente34eb7093944aac9dac543bcf97b77c868251b10 (diff)
downloadchawan-1bc63e1cd59bff8e7e266bec80ba961fecf772b7.tar.gz
dom: use JS_EvalFunction; add module fetching stubs
(still no module support in buffer...)
Diffstat (limited to 'src/html')
-rw-r--r--src/html/dom.nim130
-rw-r--r--src/html/env.nim6
-rw-r--r--src/html/script.nim67
3 files changed, 153 insertions, 50 deletions
diff --git a/src/html/dom.nim b/src/html/dom.nim
index 9aca72e2..89da6c4e 100644
--- a/src/html/dom.nim
+++ b/src/html/dom.nim
@@ -13,6 +13,7 @@ import css/values
 import display/winattrs
 import html/enums
 import html/event
+import html/script
 import img/bitmap
 import img/painter
 import img/path
@@ -53,41 +54,6 @@ type
     FORM_ENCODING_TYPE_MULTIPART = "multipart/form-data",
     FORM_ENCODING_TYPE_TEXT_PLAIN = "text/plain"
 
-  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 DocumentReadyState* = enum
   READY_STATE_LOADING = "loading"
   READY_STATE_INTERACTIVE = "interactive"
@@ -109,6 +75,7 @@ type
     document* {.jsufget.}: Document
     timeouts*: TimeoutState
     navigate*: proc(url: URL)
+    importMapsAllowed: bool
 
   # Navigator stuff
   Navigator* = object
@@ -122,9 +89,6 @@ type
     element: Element
     attrlist: seq[Attr]
 
-  EnvironmentSettings* = object
-    scripting*: bool
-
   Collection = ref CollectionObj
   CollectionObj = object of RootObj
     islive: bool
@@ -2981,9 +2945,12 @@ proc markAsReady(element: HTMLScriptElement, res: ScriptResult) =
     element.onReady = nil
   element.delayingTheLoadEvent = false
 
-proc createClassicScript(source: string, baseURL: URL, options: ScriptOptions, mutedErrors = false): Script =
+proc createClassicScript(ctx: JSContext, source: string, baseURL: URL,
+    options: ScriptOptions, mutedErrors = false): Script =
+  let urls = baseURL.serialize(excludepassword = true)
+  let record = compileScript(ctx, source, cstring(urls))
   return Script(
-    record: source,
+    record: record,
     baseURL: baseURL,
     options: options,
     mutedErrors: mutedErrors
@@ -3010,8 +2977,70 @@ proc fetchClassicScript(element: HTMLScriptElement, url: URL,
     cs
   let decoder = newDecoderStream(response.body, cs = cs)
   let source = newEncoderStream(decoder).readAll()
-  let script = createClassicScript(source, url, options, false)
-  element.markAsReady(ScriptResult(t: RESULT_SCRIPT, script: script))
+  let script = window.jsctx.createClassicScript(source, url, options, false)
+  element.onComplete(ScriptResult(t: RESULT_SCRIPT, script: script))
+
+#TODO settings object
+proc fetchDescendantsAndLink(element: HTMLScriptElement, script: Script,
+    destination: RequestDestination, onComplete: OnCompleteProc)
+proc fetchSingleModule(element: HTMLScriptElement, url: URL,
+    destination: RequestDestination, options: ScriptOptions,
+    referrer: URL, isTopLevel: bool, onComplete: OnCompleteProc)
+
+#TODO settings object
+proc fetchExternalModuleGraph(element: HTMLScriptElement, url: URL,
+    options: ScriptOptions, onComplete: OnCompleteProc) =
+  let window = element.document.window
+  if not element.scriptingEnabled or window.loader.isNone:
+    element.onComplete(ScriptResult(t: RESULT_NULL))
+    return
+  window.importMapsAllowed = false
+  element.fetchSingleModule(
+    url,
+    RequestDestination.SCRIPT,
+    options,
+    parseURL("about:client").get,
+    isTopLevel = true,
+    onComplete = proc(element: HTMLScriptElement, res: ScriptResult) =
+      if res.t == RESULT_NULL:
+        element.onComplete(res)
+      else:
+        element.fetchDescendantsAndLink(res.script, RequestDestination.SCRIPT,
+          onComplete)
+  )
+
+proc fetchDescendantsAndLink(element: HTMLScriptElement, script: Script,
+    destination: RequestDestination, onComplete: OnCompleteProc) =
+  discard
+
+#TODO settings object
+proc fetchSingleModule(element: HTMLScriptElement, url: URL,
+    destination: RequestDestination, options: ScriptOptions,
+    referrer: URL, isTopLevel: bool, onComplete: OnCompleteProc) =
+  let moduleType = "javascript"
+  #TODO moduleRequest
+  let settings = element.document.window.settings
+  let i = settings.moduleMap.find(url, moduleType)
+  if i != -1:
+    if settings.moduleMap[i].value.t == RESULT_FETCHING:
+      #TODO await value
+      assert false
+    element.onComplete(settings.moduleMap[i].value)
+    return
+  let destination = fetchDestinationFromModuleType(destination, moduleType)
+  let mode = if destination in {WORKER, SHAREDWORKER, SERVICEWORKER}:
+    RequestMode.SAME_ORIGIN
+  else:
+    RequestMode.CORS
+  #TODO client
+  #TODO initiator type
+  let request = newRequest(
+    url,
+    mode = mode,
+    referrer = referrer,
+    destination = destination
+  )
+  discard request #TODO
 
 proc execute*(element: HTMLScriptElement) =
   let document = element.document
@@ -3020,6 +3049,10 @@ proc execute*(element: HTMLScriptElement) =
   let i = document.renderBlockingElements.find(element)
   if i != -1:
     document.renderBlockingElements.delete(i)
+  #TODO this should work eventually (when module & importmap are implemented)
+  #assert element.scriptResult != nil
+  if element.scriptResult == nil:
+    return
   if element.scriptResult.t == RESULT_NULL:
     #TODO fire error event
     return
@@ -3028,10 +3061,11 @@ proc execute*(element: HTMLScriptElement) =
     let oldCurrentScript = document.currentScript
     #TODO not if shadow root
     document.currentScript = element
-    if document.window != nil and document.window.jsctx != nil:
+    let window = document.window
+    if window != nil and window.jsctx != nil:
       let script = element.scriptResult.script
       let urls = script.baseURL.serialize(excludepassword = true)
-      let ret = document.window.jsctx.eval(script.record, urls, JS_EVAL_TYPE_GLOBAL)
+      let ret = window.jsctx.evalFunction(script.record)
       if JS_IsException(ret):
         let ss = newStringStream()
         document.window.jsctx.writeException(ss)
@@ -3118,12 +3152,12 @@ proc prepare*(element: HTMLScriptElement) =
     if element.ctype == CLASSIC:
       element.fetchClassicScript(url.get, options, classicCORS, encoding, markAsReady)
     else:
-      #TODO MODULE
-      element.markAsReady(ScriptResult(t: RESULT_NULL))
+      element.fetchExternalModuleGraph(url.get, options, markAsReady)
   else:
     let baseURL = element.document.baseURL
     if element.ctype == CLASSIC:
-      let script = createClassicScript(sourceText, baseURL, options)
+      let ctx = element.document.window.jsctx
+      let script = ctx.createClassicScript(sourceText, baseURL, options)
       element.markAsReady(ScriptResult(t: RESULT_SCRIPT, script: script))
     else:
       #TODO MODULE, IMPORTMAP
@@ -3143,7 +3177,7 @@ proc prepare*(element: HTMLScriptElement) =
         if prepdoc.scriptsToExecInOrder.len > 0 and prepdoc.scriptsToExecInOrder[0] != element:
           while prepdoc.scriptsToExecInOrder.len > 0:
             let script = prepdoc.scriptsToExecInOrder[0]
-            if script.scriptResult.t == RESULT_UNINITIALIZED:
+            if script.scriptResult == nil:
               break
             script.execute()
             prepdoc.scriptsToExecInOrder.shrink(1)
diff --git a/src/html/env.nim b/src/html/env.nim
index 4c8d4f0c..43a40508 100644
--- a/src/html/env.nim
+++ b/src/html/env.nim
@@ -1,11 +1,12 @@
-import selectors
-import streams
+import std/selectors
+import std/streams
 
 import bindings/quickjs
 import display/winattrs
 import html/chadombuilder
 import html/dom
 import html/event
+import html/script
 import io/promise
 import js/base64
 import js/console
@@ -135,6 +136,7 @@ proc addScripting*(window: Window, selector: Selector[int]) =
   let ctx = rt.newJSContext()
   window.jsrt = rt
   window.jsctx = ctx
+  window.importMapsAllowed = true
   window.timeouts = newTimeoutState(
     selector = selector,
     jsctx = ctx,
diff --git a/src/html/script.nim b/src/html/script.nim
new file mode 100644
index 00000000..05e0b5cc
--- /dev/null
+++ b/src/html/script.nim
@@ -0,0 +1,67 @@
+import js/javascript
+import loader/request
+import types/referer
+import types/url
+
+type
+  ParserMetadata* = enum
+    PARSER_INSERTED, NOT_PARSER_INSERTED
+
+  ScriptType* = enum
+    NO_SCRIPTTYPE, CLASSIC, MODULE, IMPORTMAP
+
+  ScriptResultType* = enum
+    RESULT_NULL, RESULT_SCRIPT, RESULT_IMPORT_MAP_PARSE, RESULT_FETCHING
+
+type
+  EnvironmentSettings* = ref object
+    scripting*: bool
+    moduleMap*: ModuleMap
+
+  Script* = object
+    #TODO setings
+    baseURL*: URL
+    options*: ScriptOptions
+    mutedErrors*: bool
+    #TODO parse error/error to rethrow
+    record*: JSValue
+
+  ScriptOptions* = object
+    nonce*: string
+    integrity*: string
+    parserMetadata*: ParserMetadata
+    credentialsMode*: CredentialsMode
+    referrerPolicy*: Option[ReferrerPolicy]
+    renderBlocking*: bool
+
+  ScriptResult* = ref object
+    case t*: ScriptResultType
+    of RESULT_NULL:
+      discard
+    of RESULT_SCRIPT:
+      script*: Script
+    of RESULT_FETCHING:
+      discard
+    of RESULT_IMPORT_MAP_PARSE:
+      discard #TODO
+
+  ModuleMapEntry = object
+    key: tuple[url, moduleType: string]
+    value*: ScriptResult
+
+  ModuleMap* = seq[ModuleMapEntry]
+
+proc find*(moduleMap: ModuleMap, url: URL, moduleType: string): int =
+  let surl = $url
+  for i, entry in moduleMap:
+    if entry.key.moduleType == moduleType and entry.key.url == surl:
+      return i
+  return -1
+
+func fetchDestinationFromModuleType*(defaultDestination: RequestDestination,
+    moduleType: string): RequestDestination =
+  if moduleType == "json":
+    return RequestDestination.JSON
+  if moduleType == "css":
+    return RequestDestination.STYLE
+  return defaultDestination