1 /*
2  * Copyright 2016-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3  */
4 
5 package kotlinx.coroutines
6 
7 import kotlinx.browser.*
8 import kotlinx.coroutines.internal.*
9 import kotlin.coroutines.*
10 
11 private external val navigator: dynamic
12 private const val UNDEFINED = "undefined"
13 internal external val process: dynamic
14 
createDefaultDispatchernull15 internal fun createDefaultDispatcher(): CoroutineDispatcher = when {
16     // Check if we are running under jsdom. WindowDispatcher doesn't work under jsdom because it accesses MessageEvent#source.
17     // It is not implemented in jsdom, see https://github.com/jsdom/jsdom/blob/master/Changelog.md
18     // "It's missing a few semantics, especially around origins, as well as MessageEvent source."
19     isJsdom() -> NodeDispatcher
20     // Check if we are in the browser and must use window.postMessage to avoid setTimeout throttling
21     jsTypeOf(window) != UNDEFINED && window.asDynamic() != null && jsTypeOf(window.asDynamic().addEventListener) != UNDEFINED ->
22         window.asCoroutineDispatcher()
23     // If process is undefined (e.g. in NativeScript, #1404), use SetTimeout-based dispatcher
24     jsTypeOf(process) == UNDEFINED || jsTypeOf(process.nextTick) == UNDEFINED -> SetTimeoutDispatcher
25     // Fallback to NodeDispatcher when browser environment is not detected
26     else -> NodeDispatcher
27 }
28 
isJsdomnull29 private fun isJsdom() = jsTypeOf(navigator) != UNDEFINED &&
30     navigator != null &&
31     navigator.userAgent != null &&
32     jsTypeOf(navigator.userAgent) != UNDEFINED &&
33     jsTypeOf(navigator.userAgent.match) != UNDEFINED &&
34     navigator.userAgent.match("\\bjsdom\\b")
35 
36 internal actual val DefaultDelay: Delay
37     get() = Dispatchers.Default as Delay
38 
39 public actual fun CoroutineScope.newCoroutineContext(context: CoroutineContext): CoroutineContext {
40     val combined = coroutineContext + context
41     return if (combined !== Dispatchers.Default && combined[ContinuationInterceptor] == null)
42         combined + Dispatchers.Default else combined
43 }
44 
newCoroutineContextnull45 public actual fun CoroutineContext.newCoroutineContext(addedContext: CoroutineContext): CoroutineContext {
46     return this + addedContext
47 }
48 
49 // No debugging facilities on JS
withCoroutineContextnull50 internal actual inline fun <T> withCoroutineContext(context: CoroutineContext, countOrElement: Any?, block: () -> T): T = block()
51 internal actual inline fun <T> withContinuationContext(continuation: Continuation<*>, countOrElement: Any?, block: () -> T): T = block()
52 internal actual fun Continuation<*>.toDebugString(): String = toString()
53 internal actual val CoroutineContext.coroutineName: String? get() = null // not supported on JS
54 
55 internal actual class UndispatchedCoroutine<in T> actual constructor(
56     context: CoroutineContext,
57     uCont: Continuation<T>
58 ) : ScopeCoroutine<T>(context, uCont) {
59     override fun afterResume(state: Any?) = uCont.resumeWith(recoverResult(state, uCont))
60 }
61