expo-web-browser 10.2.1 → 12.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/android/build.gradle +3 -2
  3. package/android/src/main/java/expo/modules/webbrowser/CustomTabsActivitiesHelper.kt +123 -0
  4. package/android/src/main/java/expo/modules/webbrowser/CustomTabsConnectionHelper.kt +97 -0
  5. package/android/src/main/java/expo/modules/webbrowser/DeferredClientActionsQueue.kt +45 -0
  6. package/android/src/main/java/expo/modules/webbrowser/WebBrowserExceptions.kt +13 -0
  7. package/android/src/main/java/expo/modules/webbrowser/WebBrowserModule.kt +164 -0
  8. package/android/src/main/java/expo/modules/webbrowser/WebBrowserOptions.kt +15 -0
  9. package/build/ExpoWebBrowser.web.js +5 -5
  10. package/build/ExpoWebBrowser.web.js.map +1 -1
  11. package/build/WebBrowser.d.ts +6 -6
  12. package/build/WebBrowser.d.ts.map +1 -1
  13. package/build/WebBrowser.js +25 -16
  14. package/build/WebBrowser.js.map +1 -1
  15. package/build/WebBrowser.types.d.ts +3 -3
  16. package/build/WebBrowser.types.js.map +1 -1
  17. package/expo-module.config.json +3 -0
  18. package/ios/ExpoWebBrowser.podspec +1 -1
  19. package/ios/WebAuthSession.swift +2 -2
  20. package/ios/WebBrowserModule.swift +9 -9
  21. package/package.json +3 -3
  22. package/src/ExpoWebBrowser.web.ts +6 -6
  23. package/src/WebBrowser.ts +38 -19
  24. package/src/WebBrowser.types.ts +3 -3
  25. package/tsconfig.json +1 -1
  26. package/android/src/main/java/expo/modules/webbrowser/CustomTabsActivitiesHelper.java +0 -33
  27. package/android/src/main/java/expo/modules/webbrowser/CustomTabsConnectionHelper.java +0 -13
  28. package/android/src/main/java/expo/modules/webbrowser/DeferredClientActionsQueue.java +0 -50
  29. package/android/src/main/java/expo/modules/webbrowser/InternalCustomTabsActivitiesHelper.java +0 -127
  30. package/android/src/main/java/expo/modules/webbrowser/InternalCustomTabsConnectionHelper.java +0 -126
  31. package/android/src/main/java/expo/modules/webbrowser/WebBrowserModule.java +0 -218
  32. package/android/src/main/java/expo/modules/webbrowser/WebBrowserPackage.java +0 -27
  33. package/android/src/main/java/expo/modules/webbrowser/error/NoPreferredPackageFound.java +0 -15
  34. package/android/src/main/java/expo/modules/webbrowser/error/PackageManagerNotFoundException.java +0 -10
package/CHANGELOG.md CHANGED
@@ -10,6 +10,38 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 12.0.0 — 2022-10-25
14
+
15
+ ### 🛠 Breaking changes
16
+
17
+ - Bumped iOS deployment target to 13.0 and deprecated support for iOS 12. ([#18873](https://github.com/expo/expo/pull/18873) by [@tsapeta](https://github.com/tsapeta))
18
+
19
+ ### 🎉 New features
20
+
21
+ - Support CSS colors in `controlsColor`, `toolbarColor` and `secondaryToolbarColor` options. ([#18586](https://github.com/expo/expo/pull/18586) by [@janicduplessis](https://github.com/janicduplessis))
22
+
23
+ ### 💡 Others
24
+
25
+ - Update docs to remove mentions of `expo start:web`. ([#18419](https://github.com/expo/expo/pull/18419) by [@EvanBacon](https://github.com/EvanBacon))
26
+
27
+ ## 11.0.0 — 2022-07-07
28
+
29
+ ### 🎉 New features
30
+
31
+ - Native module on Android is now written in Kotlin using the new API. ([#17454](https://github.com/expo/expo/pull/17454) by [@barthap](https://github.com/barthap))
32
+
33
+ ### 🐛 Bug fixes
34
+
35
+ - Fixed `removeListener(): Method has been deprecated` warning. ([#17645](https://github.com/expo/expo/pull/17645) by [@barthap](https://github.com/barthap))
36
+ - Fixed `service not registered` exception on Android. ([#17855](https://github.com/expo/expo/pull/17855) by [@lukmccall](https://github.com/lukmccall))
37
+ - Fixed `redirectUrl` auth session argument to be optional and thus match documentation. ([#17953](https://github.com/expo/expo/pull/17953) by [@barthap](https://github.com/barthap))
38
+ - Fixed `windowFeatures` property not being properly recognized on web. ([#18106](https://github.com/expo/expo/pull/18106) by [@barthap](https://github.com/barthap))
39
+
40
+ ### 💡 Others
41
+
42
+ - Migrated Expo modules definitions to the new naming convention. ([#17193](https://github.com/expo/expo/pull/17193) by [@tsapeta](https://github.com/tsapeta))
43
+ - Rewritten Android code to Kotlin. ([#17195](https://github.com/expo/expo/pull/17195) by [@barthap](https://github.com/barthap))
44
+
13
45
  ## 10.2.1 — 2022-05-24
14
46
 
15
47
  ### 🐛 Bug fixes
@@ -3,7 +3,7 @@ apply plugin: 'kotlin-android'
3
3
  apply plugin: 'maven-publish'
4
4
 
5
5
  group = 'host.exp.exponent'
6
- version = '10.2.1'
6
+ version = '12.0.0'
7
7
 
8
8
 
9
9
  buildscript {
@@ -75,7 +75,7 @@ android {
75
75
  minSdkVersion safeExtGet("minSdkVersion", 21)
76
76
  targetSdkVersion safeExtGet("targetSdkVersion", 31)
77
77
  versionCode 18
78
- versionName '10.2.1'
78
+ versionName '12.0.0'
79
79
  }
80
80
  lintOptions {
81
81
  abortOnError false
@@ -88,6 +88,7 @@ dependencies {
88
88
  api "androidx.browser:browser:1.2.0"
89
89
 
90
90
  implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}"
91
+ implementation "androidx.core:core-ktx:1.7.0"
91
92
 
92
93
  if (project.findProject(':expo-modules-test-core')) {
93
94
  testImplementation project(':expo-modules-test-core')
@@ -0,0 +1,123 @@
1
+ package expo.modules.webbrowser
2
+
3
+ import android.app.Activity
4
+ import expo.modules.core.errors.CurrentActivityNotFoundException
5
+ import android.content.pm.ResolveInfo
6
+ import android.content.Intent
7
+ import android.content.pm.PackageManager
8
+ import android.net.Uri
9
+ import androidx.browser.customtabs.CustomTabsClient
10
+ import androidx.browser.customtabs.CustomTabsIntent
11
+ import androidx.browser.customtabs.CustomTabsService
12
+ import expo.modules.core.interfaces.ActivityProvider
13
+ import java.util.ArrayList
14
+ import java.util.LinkedHashSet
15
+
16
+ private const val DUMMY_URL = "https://expo.dev"
17
+
18
+ internal class CustomTabsActivitiesHelper(
19
+ private val activityProvider: ActivityProvider?
20
+ ) {
21
+
22
+ // region Actual custom tabs activities helper methods
23
+
24
+ /**
25
+ * @throws CurrentActivityNotFoundException
26
+ * @throws PackageManagerNotFoundException
27
+ */
28
+ fun canResolveIntent(intent: Intent): Boolean = getResolvingActivities(intent).isNotEmpty()
29
+
30
+ /**
31
+ * @throws PackageManagerNotFoundException
32
+ * @throws CurrentActivityNotFoundException
33
+ */
34
+ val customTabsResolvingActivities: ArrayList<String>
35
+ get() = getResolvingActivities(createDefaultCustomTabsIntent())
36
+ .mapToDistinctArrayList { resolveInfo: ResolveInfo ->
37
+ resolveInfo.activityInfo.packageName
38
+ }
39
+
40
+ /**
41
+ * @throws PackageManagerNotFoundException
42
+ * @throws CurrentActivityNotFoundException
43
+ */
44
+ val customTabsResolvingServices: ArrayList<String>
45
+ get() = packageManager.queryIntentServices(createDefaultCustomTabsServiceIntent(), 0)
46
+ .mapToDistinctArrayList { resolveInfo: ResolveInfo ->
47
+ resolveInfo.serviceInfo.packageName
48
+ }
49
+
50
+ /**
51
+ * @throws PackageManagerNotFoundException
52
+ * @throws CurrentActivityNotFoundException
53
+ */
54
+ fun getPreferredCustomTabsResolvingActivity(packages: List<String?>?): String? {
55
+ val resolvedPackages = packages ?: customTabsResolvingActivities
56
+ return CustomTabsClient.getPackageName(currentActivity, resolvedPackages)
57
+ }
58
+
59
+ /**
60
+ * @throws PackageManagerNotFoundException
61
+ * @throws CurrentActivityNotFoundException
62
+ */
63
+ val defaultCustomTabsResolvingActivity: String?
64
+ get() {
65
+ val info = packageManager.resolveActivity(createDefaultCustomTabsIntent(), 0)
66
+ return info?.activityInfo?.packageName
67
+ }
68
+
69
+ /**
70
+ * @throws CurrentActivityNotFoundException
71
+ */
72
+ fun startCustomTabs(intent: Intent) {
73
+ currentActivity.startActivity(intent)
74
+ }
75
+
76
+ // endregion
77
+
78
+ // region Private helpers
79
+
80
+ /**
81
+ * @throws CurrentActivityNotFoundException
82
+ * @throws PackageManagerNotFoundException
83
+ */
84
+ private fun getResolvingActivities(intent: Intent): List<ResolveInfo> {
85
+ return packageManager.queryIntentActivities(intent, 0)
86
+ }
87
+
88
+ /**
89
+ * @throws CurrentActivityNotFoundException
90
+ * @throws PackageManagerNotFoundException
91
+ */
92
+ private val packageManager: PackageManager
93
+ get() = currentActivity.packageManager ?: throw PackageManagerNotFoundException()
94
+
95
+ /**
96
+ * @throws CurrentActivityNotFoundException
97
+ */
98
+ private val currentActivity: Activity
99
+ get() {
100
+ return activityProvider?.currentActivity ?: throw CurrentActivityNotFoundException()
101
+ }
102
+
103
+ // endregion
104
+ }
105
+
106
+ private inline fun <T, R> Collection<T>.mapToDistinctArrayList(mapper: (T) -> R): ArrayList<R> {
107
+ val resultSet = LinkedHashSet<R>()
108
+ for (element in this) {
109
+ resultSet.add(mapper.invoke(element))
110
+ }
111
+ return ArrayList(resultSet)
112
+ }
113
+
114
+ private fun createDefaultCustomTabsIntent(): Intent {
115
+ val customTabsIntent = CustomTabsIntent.Builder().build()
116
+ return customTabsIntent.intent.apply {
117
+ data = Uri.parse(DUMMY_URL)
118
+ }
119
+ }
120
+
121
+ private fun createDefaultCustomTabsServiceIntent() = Intent().apply {
122
+ action = CustomTabsService.ACTION_CUSTOM_TABS_CONNECTION
123
+ }
@@ -0,0 +1,97 @@
1
+ package expo.modules.webbrowser
2
+
3
+ import android.content.ComponentName
4
+ import android.content.Context
5
+ import android.net.Uri
6
+ import androidx.browser.customtabs.CustomTabsClient
7
+ import androidx.browser.customtabs.CustomTabsServiceConnection
8
+ import androidx.browser.customtabs.CustomTabsSession
9
+
10
+ internal class CustomTabsConnectionHelper(
11
+ private val context: Context
12
+ ) : CustomTabsServiceConnection() {
13
+ private var currentPackageName: String? = null
14
+ private val clientActions = DeferredClientActionsQueue<CustomTabsClient>()
15
+ private val sessionActions = DeferredClientActionsQueue<CustomTabsSession?>()
16
+
17
+ // region lifecycle methods
18
+ fun destroy() = clearConnection()
19
+ // endregion
20
+
21
+ // region Actual connection helper methods
22
+ fun warmUp(packageName: String) {
23
+ clientActions.executeOrQueueAction { client: CustomTabsClient -> client.warmup(0) }
24
+ ensureConnection(packageName)
25
+ }
26
+
27
+ fun mayInitWithUrl(packageName: String, uri: Uri) {
28
+ sessionActions.executeOrQueueAction { session: CustomTabsSession? ->
29
+ session?.mayLaunchUrl(uri, null, null)
30
+ }
31
+ ensureConnection(packageName)
32
+ ensureSession()
33
+ }
34
+
35
+ fun coolDown(packageName: String): Boolean {
36
+ if (isConnectionStarted(packageName)) {
37
+ clearConnection()
38
+ return true
39
+ }
40
+ return false
41
+ }
42
+ // endregion
43
+
44
+ // region CustomTabsServiceConnection implementation
45
+ override fun onBindingDied(componentName: ComponentName) {
46
+ if (isConnectionStarted(componentName.packageName)) {
47
+ clearConnection()
48
+ }
49
+ }
50
+
51
+ override fun onCustomTabsServiceConnected(componentName: ComponentName, client: CustomTabsClient) {
52
+ if (isConnectionStarted(componentName.packageName)) {
53
+ clientActions.setClient(client)
54
+ }
55
+ }
56
+
57
+ override fun onServiceDisconnected(componentName: ComponentName) {
58
+ if (isConnectionStarted(componentName.packageName)) {
59
+ clearConnection()
60
+ }
61
+ }
62
+ // endregion
63
+
64
+ private fun ensureSession() {
65
+ if (sessionActions.hasClient()) {
66
+ return
67
+ }
68
+
69
+ clientActions.executeOrQueueAction { client: CustomTabsClient ->
70
+ sessionActions.setClient(client.newSession(null))
71
+ }
72
+ }
73
+
74
+ private fun ensureConnection(packageName: String) {
75
+ if (currentPackageName != null && currentPackageName != packageName) {
76
+ clearConnection()
77
+ }
78
+ if (!isConnectionStarted(packageName)) {
79
+ CustomTabsClient.bindCustomTabsService(context, packageName, this)
80
+ currentPackageName = packageName
81
+ }
82
+ }
83
+
84
+ private fun isConnectionStarted(packageName: String): Boolean {
85
+ return packageName == currentPackageName
86
+ }
87
+
88
+ private fun clearConnection() {
89
+ if (currentPackageName != null) {
90
+ context.unbindService(this)
91
+ }
92
+
93
+ currentPackageName = null
94
+ clientActions.clear()
95
+ sessionActions.clear()
96
+ }
97
+ }
@@ -0,0 +1,45 @@
1
+ package expo.modules.webbrowser
2
+
3
+ import expo.modules.core.interfaces.Consumer
4
+ import java.util.*
5
+
6
+ class DeferredClientActionsQueue<T> {
7
+ private val actions: Queue<Consumer<T>> = LinkedList()
8
+ private var client: T? = null
9
+
10
+ fun setClient(client: T) {
11
+ this.client = client
12
+ executeQueuedActions()
13
+ }
14
+
15
+ fun hasClient(): Boolean = client != null
16
+
17
+ fun executeOrQueueAction(action: Consumer<T>) {
18
+ if (client != null) {
19
+ action.apply(client)
20
+ } else {
21
+ addActionToQueue(action)
22
+ }
23
+ }
24
+
25
+ fun clear() {
26
+ client = null
27
+ actions.clear()
28
+ }
29
+
30
+ private fun executeQueuedActions() {
31
+ if (client == null) {
32
+ return
33
+ }
34
+
35
+ var action = actions.poll()
36
+ while (action != null) {
37
+ action.apply(client)
38
+ action = actions.poll()
39
+ }
40
+ }
41
+
42
+ private fun addActionToQueue(consumer: Consumer<T>) {
43
+ actions.add(consumer)
44
+ }
45
+ }
@@ -0,0 +1,13 @@
1
+ package expo.modules.webbrowser
2
+
3
+ import expo.modules.kotlin.exception.CodedException
4
+
5
+ internal class NoPreferredPackageFound : CodedException(
6
+ code = "PREFERRED_PACKAGE_NOT_FOUND",
7
+ message = "Cannot determine preferred package without satisfying it",
8
+ cause = null
9
+ )
10
+
11
+ internal class PackageManagerNotFoundException : CodedException("Package Manager not found")
12
+
13
+ internal class NoMatchingActivityException : CodedException("No matching browser activity found")
@@ -0,0 +1,164 @@
1
+ package expo.modules.webbrowser
2
+
3
+ import expo.modules.core.errors.CurrentActivityNotFoundException
4
+ import android.content.Intent
5
+ import android.net.Uri
6
+ import android.os.Bundle
7
+ import android.text.TextUtils
8
+ import androidx.browser.customtabs.CustomTabsIntent
9
+ import androidx.core.os.bundleOf
10
+ import expo.modules.core.utilities.ifNull
11
+ import expo.modules.kotlin.modules.Module
12
+ import expo.modules.kotlin.modules.ModuleDefinition
13
+
14
+ private const val SERVICE_PACKAGE_KEY = "servicePackage"
15
+ private const val BROWSER_PACKAGES_KEY = "browserPackages"
16
+ private const val SERVICE_PACKAGES_KEY = "servicePackages"
17
+ private const val PREFERRED_BROWSER_PACKAGE = "preferredBrowserPackage"
18
+ private const val DEFAULT_BROWSER_PACKAGE = "defaultBrowserPackage"
19
+
20
+ private const val MODULE_NAME = "ExpoWebBrowser"
21
+
22
+ class WebBrowserModule : Module() {
23
+ override fun definition() = ModuleDefinition {
24
+ Name(MODULE_NAME)
25
+
26
+ OnCreate {
27
+ customTabsResolver = CustomTabsActivitiesHelper(appContext.activityProvider)
28
+ connectionHelper = CustomTabsConnectionHelper(
29
+ requireNotNull(appContext.reactContext) {
30
+ "Cannot initialize WebBrowser, ReactContext is null"
31
+ }
32
+ )
33
+ }
34
+
35
+ OnActivityDestroys {
36
+ connectionHelper.destroy()
37
+ }
38
+
39
+ AsyncFunction("warmUpAsync") { packageName: String? ->
40
+ val resolvedPackageName = givenOrPreferredPackageName(packageName)
41
+ connectionHelper.warmUp(resolvedPackageName)
42
+ return@AsyncFunction bundleOf(
43
+ SERVICE_PACKAGE_KEY to resolvedPackageName
44
+ )
45
+ }
46
+
47
+ AsyncFunction("coolDownAsync") { packageName: String? ->
48
+ val resolvedPackageName = givenOrPreferredPackageName(packageName)
49
+ val result = if (connectionHelper.coolDown(resolvedPackageName)) {
50
+ bundleOf(
51
+ SERVICE_PACKAGE_KEY to resolvedPackageName
52
+ )
53
+ } else {
54
+ Bundle()
55
+ }
56
+ return@AsyncFunction result
57
+ }
58
+
59
+ AsyncFunction("mayInitWithUrlAsync") { url: String, packageName: String? ->
60
+ val resolvedPackageName = givenOrPreferredPackageName(packageName)
61
+ connectionHelper.mayInitWithUrl(resolvedPackageName, Uri.parse(url))
62
+ return@AsyncFunction bundleOf(
63
+ SERVICE_PACKAGE_KEY to resolvedPackageName
64
+ )
65
+ }
66
+
67
+ // throws CurrentActivityNotFoundException
68
+ AsyncFunction("getCustomTabsSupportingBrowsersAsync") {
69
+ val activities = customTabsResolver.customTabsResolvingActivities
70
+ val services = customTabsResolver.customTabsResolvingServices
71
+ val preferredPackage = customTabsResolver.getPreferredCustomTabsResolvingActivity(activities)
72
+ val defaultPackage = customTabsResolver.defaultCustomTabsResolvingActivity
73
+
74
+ // It might happen, that default activity does not support Chrome Tabs. Then it will be ResolvingActivity and we don't want to return it as a result.
75
+ val defaultCustomTabsPackage: String? = defaultPackage.takeIf { activities.contains(it) }
76
+
77
+ return@AsyncFunction Bundle().apply {
78
+ putStringArrayList(BROWSER_PACKAGES_KEY, activities)
79
+ putStringArrayList(SERVICE_PACKAGES_KEY, services)
80
+ putString(PREFERRED_BROWSER_PACKAGE, preferredPackage)
81
+ putString(DEFAULT_BROWSER_PACKAGE, defaultCustomTabsPackage)
82
+ }
83
+ }
84
+
85
+ // throws CurrentActivityNotFoundException
86
+ AsyncFunction("openBrowserAsync") { url: String, options: OpenBrowserOptions ->
87
+ val intent = createCustomTabsIntent(options).apply {
88
+ data = Uri.parse(url)
89
+ }
90
+
91
+ if (!customTabsResolver.canResolveIntent(intent)) {
92
+ throw NoMatchingActivityException()
93
+ }
94
+
95
+ customTabsResolver.startCustomTabs(intent)
96
+
97
+ return@AsyncFunction bundleOf(
98
+ "type" to "opened"
99
+ )
100
+ }
101
+ }
102
+
103
+ // these must be `internal` to be able to be injected in tests
104
+ internal lateinit var customTabsResolver: CustomTabsActivitiesHelper
105
+ internal lateinit var connectionHelper: CustomTabsConnectionHelper
106
+
107
+ private fun createCustomTabsIntent(options: OpenBrowserOptions): Intent {
108
+ val builder = CustomTabsIntent.Builder()
109
+
110
+ val color = options.toolbarColor
111
+ if (color != null) {
112
+ builder.setToolbarColor(color)
113
+ }
114
+
115
+ val secondaryColor = options.secondaryToolbarColor
116
+ if (secondaryColor != null) {
117
+ builder.setSecondaryToolbarColor(secondaryColor)
118
+ }
119
+
120
+ builder.setShowTitle(options.showTitle)
121
+
122
+ if (options.enableDefaultShareMenuItem) {
123
+ builder.addDefaultShareMenuItem()
124
+ }
125
+
126
+ return builder.build().intent.apply {
127
+ // We cannot use the builder's method enableUrlBarHiding, because there is
128
+ // no corresponding disable method and some browsers enable it by default.
129
+ putExtra(CustomTabsIntent.EXTRA_ENABLE_URLBAR_HIDING, options.enableBarCollapsing)
130
+
131
+ val packageName = options.browserPackage
132
+ if (!TextUtils.isEmpty(packageName)) {
133
+ setPackage(packageName)
134
+ }
135
+
136
+ if (options.shouldCreateTask) {
137
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
138
+
139
+ if (!options.showInRecents) {
140
+ addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS)
141
+ addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY)
142
+ }
143
+ }
144
+ }
145
+ }
146
+
147
+ /**
148
+ * @throws NoPreferredPackageFound
149
+ */
150
+ private fun givenOrPreferredPackageName(packageName: String?): String {
151
+ val resolvedPackageName: String? = try {
152
+ packageName?.takeIf { it.isNotEmpty() }.ifNull {
153
+ customTabsResolver.getPreferredCustomTabsResolvingActivity(null)
154
+ }
155
+ } catch (ex: CurrentActivityNotFoundException) {
156
+ throw NoPreferredPackageFound()
157
+ } catch (ex: PackageManagerNotFoundException) {
158
+ throw NoPreferredPackageFound()
159
+ }
160
+
161
+ return resolvedPackageName?.takeIf { it.isNotEmpty() }
162
+ ?: throw NoPreferredPackageFound()
163
+ }
164
+ }
@@ -0,0 +1,15 @@
1
+ package expo.modules.webbrowser
2
+
3
+ import expo.modules.kotlin.records.Field
4
+ import expo.modules.kotlin.records.Record
5
+
6
+ internal data class OpenBrowserOptions(
7
+ @Field var toolbarColor: Int? = null,
8
+ @Field var secondaryToolbarColor: Int? = null,
9
+ @Field var browserPackage: String? = null,
10
+ @Field var showTitle: Boolean = false,
11
+ @Field var enableDefaultShareMenuItem: Boolean = false,
12
+ @Field var enableBarCollapsing: Boolean = false,
13
+ @Field var showInRecents: Boolean = false,
14
+ @Field(key = "createTask") var shouldCreateTask: Boolean = true,
15
+ ) : Record
@@ -15,10 +15,10 @@ function dismissPopup() {
15
15
  }
16
16
  popupWindow.close();
17
17
  if (listenerMap.has(popupWindow)) {
18
- const { listener, appStateListener, interval } = listenerMap.get(popupWindow);
18
+ const { listener, appStateSubscription, interval } = listenerMap.get(popupWindow);
19
19
  clearInterval(interval);
20
20
  window.removeEventListener('message', listener);
21
- AppState.removeEventListener('change', appStateListener);
21
+ appStateSubscription.remove();
22
22
  listenerMap.delete(popupWindow);
23
23
  const handle = window.localStorage.getItem(getHandle());
24
24
  if (handle) {
@@ -138,7 +138,7 @@ export default {
138
138
  }
139
139
  }
140
140
  };
141
- AppState.addEventListener('change', appStateListener);
141
+ const appStateSubscription = AppState.addEventListener('change', appStateListener);
142
142
  // Check if the window has been closed every second.
143
143
  const interval = setInterval(() => {
144
144
  if (popupWindow?.closed) {
@@ -152,7 +152,7 @@ export default {
152
152
  listenerMap.set(popupWindow, {
153
153
  listener,
154
154
  interval,
155
- appStateListener,
155
+ appStateSubscription,
156
156
  });
157
157
  });
158
158
  },
@@ -234,7 +234,7 @@ function normalizePopupFeaturesString(options) {
234
234
  for (const pair of windowFeaturePairs) {
235
235
  const [key, value] = pair.trim().split('=');
236
236
  if (key && value) {
237
- windowFeaturePairs[key] = value;
237
+ windowFeatures[key] = value;
238
238
  }
239
239
  }
240
240
  }
@@ -1 +1 @@
1
- {"version":3,"file":"ExpoWebBrowser.web.js","sourceRoot":"","sources":["../src/ExpoWebBrowser.web.ts"],"names":[],"mappings":"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","sourcesContent":["import compareUrls from 'compare-urls';\nimport { CodedError, Platform } from 'expo-modules-core';\nimport { AppState, Dimensions, AppStateStatus } from 'react-native';\n\nimport {\n WebBrowserAuthSessionResult,\n WebBrowserOpenOptions,\n WebBrowserResult,\n WebBrowserResultType,\n WebBrowserWindowFeatures,\n} from './WebBrowser.types';\n\nconst POPUP_WIDTH = 500;\nconst POPUP_HEIGHT = 650;\n\nlet popupWindow: Window | null = null;\n\nconst listenerMap = new Map();\n\nconst getHandle = () => 'ExpoWebBrowserRedirectHandle';\nconst getOriginUrlHandle = (hash: string) => `ExpoWebBrowser_OriginUrl_${hash}`;\nconst getRedirectUrlHandle = (hash: string) => `ExpoWebBrowser_RedirectUrl_${hash}`;\n\nfunction dismissPopup() {\n if (!popupWindow) {\n return;\n }\n popupWindow.close();\n if (listenerMap.has(popupWindow)) {\n const { listener, appStateListener, interval } = listenerMap.get(popupWindow);\n clearInterval(interval);\n window.removeEventListener('message', listener);\n AppState.removeEventListener('change', appStateListener);\n listenerMap.delete(popupWindow);\n\n const handle = window.localStorage.getItem(getHandle());\n if (handle) {\n window.localStorage.removeItem(getHandle());\n window.localStorage.removeItem(getOriginUrlHandle(handle));\n window.localStorage.removeItem(getRedirectUrlHandle(handle));\n }\n\n popupWindow = null;\n }\n}\n\nexport default {\n get name() {\n return 'ExpoWebBrowser';\n },\n async openBrowserAsync(\n url: string,\n browserParams: WebBrowserOpenOptions = {}\n ): Promise<WebBrowserResult> {\n if (!Platform.isDOMAvailable) return { type: WebBrowserResultType.CANCEL };\n const { windowName = '_blank', windowFeatures } = browserParams;\n const features = getPopupFeaturesString(windowFeatures);\n window.open(url, windowName, features);\n return { type: WebBrowserResultType.OPENED };\n },\n dismissAuthSession() {\n if (!Platform.isDOMAvailable) return;\n dismissPopup();\n },\n maybeCompleteAuthSession({ skipRedirectCheck }: { skipRedirectCheck?: boolean }): {\n type: 'success' | 'failed';\n message: string;\n } {\n if (!Platform.isDOMAvailable) {\n return {\n type: 'failed',\n message: 'Cannot use expo-web-browser in a non-browser environment',\n };\n }\n const handle = window.localStorage.getItem(getHandle());\n\n if (!handle) {\n return { type: 'failed', message: 'No auth session is currently in progress' };\n }\n\n const url = window.location.href;\n\n if (skipRedirectCheck !== true) {\n const redirectUrl = window.localStorage.getItem(getRedirectUrlHandle(handle));\n // Compare the original redirect url against the current url with it's query params removed.\n const currentUrl = window.location.origin + window.location.pathname;\n if (!compareUrls(redirectUrl, currentUrl)) {\n return {\n type: 'failed',\n message: `Current URL \"${currentUrl}\" and original redirect URL \"${redirectUrl}\" do not match.`,\n };\n }\n }\n\n // Save the link for app state listener\n window.localStorage.setItem(getOriginUrlHandle(handle), url);\n\n // Get the window that created the current popup\n const parent = window.opener ?? window.parent;\n if (!parent) {\n throw new CodedError(\n 'ERR_WEB_BROWSER_REDIRECT',\n `The window cannot complete the redirect request because the invoking window doesn't have a reference to it's parent. This can happen if the parent window was reloaded.`\n );\n }\n // Send the URL back to the opening window.\n parent.postMessage({ url, expoSender: handle }, parent.location.toString());\n return { type: 'success', message: `Attempting to complete auth` };\n\n // Maybe set timer to throw an error if the window is still open after attempting to complete.\n },\n // This method should be invoked from user input.\n async openAuthSessionAsync(\n url: string,\n redirectUrl?: string,\n openOptions?: WebBrowserOpenOptions\n ): Promise<WebBrowserAuthSessionResult> {\n if (!Platform.isDOMAvailable) return { type: WebBrowserResultType.CANCEL };\n\n redirectUrl = redirectUrl ?? getRedirectUrlFromUrlOrGenerate(url);\n\n if (popupWindow == null || popupWindow?.closed) {\n const features = getPopupFeaturesString(openOptions?.windowFeatures);\n popupWindow = window.open(url, openOptions?.windowName, features);\n\n if (popupWindow) {\n try {\n popupWindow.focus();\n } catch {}\n } else {\n throw new CodedError(\n 'ERR_WEB_BROWSER_BLOCKED',\n 'Popup window was blocked by the browser or failed to open. This can happen in mobile browsers when the window.open() method was invoked too long after a user input was fired.'\n );\n }\n }\n\n const state = await getStateFromUrlOrGenerateAsync(url);\n\n // Save handle for session\n window.localStorage.setItem(getHandle(), state);\n // Save redirect Url for further verification\n window.localStorage.setItem(getRedirectUrlHandle(state), redirectUrl);\n\n return new Promise(async (resolve) => {\n // Create a listener for messages sent from the popup\n const listener = (event: MessageEvent) => {\n if (!event.isTrusted) return;\n // Ensure we trust the sender.\n if (event.origin !== window.location.origin) {\n return;\n }\n const { data } = event;\n // Use a crypto hash to invalid message.\n const handle = window.localStorage.getItem(getHandle());\n // Ensure the sender is also from expo-web-browser\n if (data.expoSender === handle) {\n dismissPopup();\n resolve({ type: 'success', url: data.url });\n }\n };\n\n // Add a listener for receiving messages from the popup.\n window.addEventListener('message', listener, false);\n\n // Create an app state listener as a fallback to the popup listener\n const appStateListener = (state: AppStateStatus) => {\n if (state !== 'active') {\n return;\n }\n const handle = window.localStorage.getItem(getHandle());\n if (handle) {\n const url = window.localStorage.getItem(getOriginUrlHandle(handle));\n if (url) {\n dismissPopup();\n resolve({ type: 'success', url });\n }\n }\n };\n\n AppState.addEventListener('change', appStateListener);\n\n // Check if the window has been closed every second.\n const interval = setInterval(() => {\n if (popupWindow?.closed) {\n if (resolve) resolve({ type: WebBrowserResultType.DISMISS });\n clearInterval(interval);\n dismissPopup();\n }\n }, 1000);\n\n // Store the listener and interval for clean up.\n listenerMap.set(popupWindow, {\n listener,\n interval,\n appStateListener,\n });\n });\n },\n};\n\n// Crypto\nfunction isCryptoAvailable(): boolean {\n if (!Platform.isDOMAvailable) return false;\n return !!(window?.crypto as any);\n}\n\nfunction isSubtleCryptoAvailable(): boolean {\n if (!isCryptoAvailable()) return false;\n return !!(window.crypto.subtle as any);\n}\n\nasync function getStateFromUrlOrGenerateAsync(inputUrl: string): Promise<string> {\n const url = new URL(inputUrl);\n if (url.searchParams.has('state') && typeof url.searchParams.get('state') === 'string') {\n // Ensure we reuse the auth state if it's passed in.\n return url.searchParams.get('state')!;\n }\n // Generate a crypto state for verifying the return popup.\n return await generateStateAsync();\n}\n\nfunction getRedirectUrlFromUrlOrGenerate(inputUrl: string): string {\n const url = new URL(inputUrl);\n if (\n url.searchParams.has('redirect_uri') &&\n typeof url.searchParams.get('redirect_uri') === 'string'\n ) {\n // Ensure we reuse the redirect_uri if it's passed in the input url.\n return url.searchParams.get('redirect_uri')!;\n }\n // Emulate how native uses Constants.linkingUrl\n return location.origin + location.pathname;\n}\n\nconst CHARSET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n\nasync function generateStateAsync(): Promise<string> {\n if (!isSubtleCryptoAvailable()) {\n throw new CodedError(\n 'ERR_WEB_BROWSER_CRYPTO',\n `The current environment doesn't support crypto. Ensure you are running from a secure origin (https).`\n );\n }\n const encoder = new TextEncoder();\n\n const data = generateRandom(10);\n const buffer = encoder.encode(data);\n const hashedData = await crypto.subtle.digest('SHA-256', buffer);\n const state = btoa(String.fromCharCode(...new Uint8Array(hashedData)));\n return state;\n}\n\nfunction generateRandom(size: number): string {\n let arr = new Uint8Array(size);\n if (arr.byteLength !== arr.length) {\n arr = new Uint8Array(arr.buffer);\n }\n const array = new Uint8Array(arr.length);\n if (isCryptoAvailable()) {\n window.crypto.getRandomValues(array);\n } else {\n for (let i = 0; i < size; i += 1) {\n array[i] = (Math.random() * CHARSET.length) | 0;\n }\n }\n return bufferToString(array);\n}\n\nfunction bufferToString(buffer): string {\n const state: string[] = [];\n for (let i = 0; i < buffer.byteLength; i += 1) {\n const index = buffer[i] % CHARSET.length;\n state.push(CHARSET[index]);\n }\n return state.join('');\n}\n\n// Window Features\n\n// Ensure feature string is an object\nfunction normalizePopupFeaturesString(\n options?: WebBrowserWindowFeatures | string\n): Record<string, any> {\n let windowFeatures: Record<string, any> = {};\n // This should be avoided because it adds extra time to the popup command.\n if (typeof options === 'string') {\n // Convert string of `key=value,foo=bar` into an object\n const windowFeaturePairs = options.split(',');\n for (const pair of windowFeaturePairs) {\n const [key, value] = pair.trim().split('=');\n if (key && value) {\n windowFeaturePairs[key] = value;\n }\n }\n } else if (options) {\n windowFeatures = options;\n }\n return windowFeatures;\n}\n\n// Apply default values to the input feature set\nfunction getPopupFeaturesString(options?: WebBrowserWindowFeatures | string): string {\n const windowFeatures = normalizePopupFeaturesString(options);\n\n const width = windowFeatures.width ?? POPUP_WIDTH;\n const height = windowFeatures.height ?? POPUP_HEIGHT;\n\n const dimensions = Dimensions.get('screen');\n const top = windowFeatures.top ?? Math.max(0, (dimensions.height - height) * 0.5);\n const left = windowFeatures.left ?? Math.max(0, (dimensions.width - width) * 0.5);\n\n // Create a reasonable popup\n // https://developer.mozilla.org/en-US/docs/Web/API/Window/open#Window_features\n return featureObjectToString({\n ...windowFeatures,\n // Toolbar buttons (Back, Forward, Reload, Stop buttons).\n toolbar: windowFeatures.toolbar ?? 'no',\n menubar: windowFeatures.menubar ?? 'no',\n // Shows the location bar or the address bar.\n location: windowFeatures.location ?? 'yes',\n resizable: windowFeatures.resizable ?? 'yes',\n // If this feature is on, then the new secondary window has a status bar.\n status: windowFeatures.status ?? 'no',\n scrollbars: windowFeatures.scrollbars ?? 'yes',\n top,\n left,\n width,\n height,\n });\n}\n\nexport function featureObjectToString(features: Record<string, any>): string {\n return Object.keys(features).reduce<string>((prev, current) => {\n let value = features[current];\n if (typeof value === 'boolean') {\n value = value ? 'yes' : 'no';\n }\n if (current && value) {\n if (prev) prev += ',';\n return `${prev}${current}=${value}`;\n }\n return prev;\n }, '');\n}\n"]}
1
+ {"version":3,"file":"ExpoWebBrowser.web.js","sourceRoot":"","sources":["../src/ExpoWebBrowser.web.ts"],"names":[],"mappings":"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","sourcesContent":["import compareUrls from 'compare-urls';\nimport { CodedError, Platform } from 'expo-modules-core';\nimport { AppState, Dimensions, AppStateStatus, NativeEventSubscription } from 'react-native';\n\nimport {\n WebBrowserAuthSessionResult,\n WebBrowserOpenOptions,\n WebBrowserResult,\n WebBrowserResultType,\n WebBrowserWindowFeatures,\n} from './WebBrowser.types';\n\nconst POPUP_WIDTH = 500;\nconst POPUP_HEIGHT = 650;\n\nlet popupWindow: Window | null = null;\n\nconst listenerMap = new Map();\n\nconst getHandle = () => 'ExpoWebBrowserRedirectHandle';\nconst getOriginUrlHandle = (hash: string) => `ExpoWebBrowser_OriginUrl_${hash}`;\nconst getRedirectUrlHandle = (hash: string) => `ExpoWebBrowser_RedirectUrl_${hash}`;\n\nfunction dismissPopup() {\n if (!popupWindow) {\n return;\n }\n popupWindow.close();\n if (listenerMap.has(popupWindow)) {\n const { listener, appStateSubscription, interval } = listenerMap.get(popupWindow);\n clearInterval(interval);\n window.removeEventListener('message', listener);\n (appStateSubscription as NativeEventSubscription).remove();\n listenerMap.delete(popupWindow);\n\n const handle = window.localStorage.getItem(getHandle());\n if (handle) {\n window.localStorage.removeItem(getHandle());\n window.localStorage.removeItem(getOriginUrlHandle(handle));\n window.localStorage.removeItem(getRedirectUrlHandle(handle));\n }\n\n popupWindow = null;\n }\n}\n\nexport default {\n get name() {\n return 'ExpoWebBrowser';\n },\n async openBrowserAsync(\n url: string,\n browserParams: WebBrowserOpenOptions = {}\n ): Promise<WebBrowserResult> {\n if (!Platform.isDOMAvailable) return { type: WebBrowserResultType.CANCEL };\n const { windowName = '_blank', windowFeatures } = browserParams;\n const features = getPopupFeaturesString(windowFeatures);\n window.open(url, windowName, features);\n return { type: WebBrowserResultType.OPENED };\n },\n dismissAuthSession() {\n if (!Platform.isDOMAvailable) return;\n dismissPopup();\n },\n maybeCompleteAuthSession({ skipRedirectCheck }: { skipRedirectCheck?: boolean }): {\n type: 'success' | 'failed';\n message: string;\n } {\n if (!Platform.isDOMAvailable) {\n return {\n type: 'failed',\n message: 'Cannot use expo-web-browser in a non-browser environment',\n };\n }\n const handle = window.localStorage.getItem(getHandle());\n\n if (!handle) {\n return { type: 'failed', message: 'No auth session is currently in progress' };\n }\n\n const url = window.location.href;\n\n if (skipRedirectCheck !== true) {\n const redirectUrl = window.localStorage.getItem(getRedirectUrlHandle(handle));\n // Compare the original redirect url against the current url with it's query params removed.\n const currentUrl = window.location.origin + window.location.pathname;\n if (!compareUrls(redirectUrl, currentUrl)) {\n return {\n type: 'failed',\n message: `Current URL \"${currentUrl}\" and original redirect URL \"${redirectUrl}\" do not match.`,\n };\n }\n }\n\n // Save the link for app state listener\n window.localStorage.setItem(getOriginUrlHandle(handle), url);\n\n // Get the window that created the current popup\n const parent = window.opener ?? window.parent;\n if (!parent) {\n throw new CodedError(\n 'ERR_WEB_BROWSER_REDIRECT',\n `The window cannot complete the redirect request because the invoking window doesn't have a reference to it's parent. This can happen if the parent window was reloaded.`\n );\n }\n // Send the URL back to the opening window.\n parent.postMessage({ url, expoSender: handle }, parent.location.toString());\n return { type: 'success', message: `Attempting to complete auth` };\n\n // Maybe set timer to throw an error if the window is still open after attempting to complete.\n },\n // This method should be invoked from user input.\n async openAuthSessionAsync(\n url: string,\n redirectUrl?: string,\n openOptions?: WebBrowserOpenOptions\n ): Promise<WebBrowserAuthSessionResult> {\n if (!Platform.isDOMAvailable) return { type: WebBrowserResultType.CANCEL };\n\n redirectUrl = redirectUrl ?? getRedirectUrlFromUrlOrGenerate(url);\n\n if (popupWindow == null || popupWindow?.closed) {\n const features = getPopupFeaturesString(openOptions?.windowFeatures);\n popupWindow = window.open(url, openOptions?.windowName, features);\n\n if (popupWindow) {\n try {\n popupWindow.focus();\n } catch {}\n } else {\n throw new CodedError(\n 'ERR_WEB_BROWSER_BLOCKED',\n 'Popup window was blocked by the browser or failed to open. This can happen in mobile browsers when the window.open() method was invoked too long after a user input was fired.'\n );\n }\n }\n\n const state = await getStateFromUrlOrGenerateAsync(url);\n\n // Save handle for session\n window.localStorage.setItem(getHandle(), state);\n // Save redirect Url for further verification\n window.localStorage.setItem(getRedirectUrlHandle(state), redirectUrl);\n\n return new Promise(async (resolve) => {\n // Create a listener for messages sent from the popup\n const listener = (event: MessageEvent) => {\n if (!event.isTrusted) return;\n // Ensure we trust the sender.\n if (event.origin !== window.location.origin) {\n return;\n }\n const { data } = event;\n // Use a crypto hash to invalid message.\n const handle = window.localStorage.getItem(getHandle());\n // Ensure the sender is also from expo-web-browser\n if (data.expoSender === handle) {\n dismissPopup();\n resolve({ type: 'success', url: data.url });\n }\n };\n\n // Add a listener for receiving messages from the popup.\n window.addEventListener('message', listener, false);\n\n // Create an app state listener as a fallback to the popup listener\n const appStateListener = (state: AppStateStatus) => {\n if (state !== 'active') {\n return;\n }\n const handle = window.localStorage.getItem(getHandle());\n if (handle) {\n const url = window.localStorage.getItem(getOriginUrlHandle(handle));\n if (url) {\n dismissPopup();\n resolve({ type: 'success', url });\n }\n }\n };\n\n const appStateSubscription = AppState.addEventListener('change', appStateListener);\n\n // Check if the window has been closed every second.\n const interval = setInterval(() => {\n if (popupWindow?.closed) {\n if (resolve) resolve({ type: WebBrowserResultType.DISMISS });\n clearInterval(interval);\n dismissPopup();\n }\n }, 1000);\n\n // Store the listener and interval for clean up.\n listenerMap.set(popupWindow, {\n listener,\n interval,\n appStateSubscription,\n });\n });\n },\n};\n\n// Crypto\nfunction isCryptoAvailable(): boolean {\n if (!Platform.isDOMAvailable) return false;\n return !!(window?.crypto as any);\n}\n\nfunction isSubtleCryptoAvailable(): boolean {\n if (!isCryptoAvailable()) return false;\n return !!(window.crypto.subtle as any);\n}\n\nasync function getStateFromUrlOrGenerateAsync(inputUrl: string): Promise<string> {\n const url = new URL(inputUrl);\n if (url.searchParams.has('state') && typeof url.searchParams.get('state') === 'string') {\n // Ensure we reuse the auth state if it's passed in.\n return url.searchParams.get('state')!;\n }\n // Generate a crypto state for verifying the return popup.\n return await generateStateAsync();\n}\n\nfunction getRedirectUrlFromUrlOrGenerate(inputUrl: string): string {\n const url = new URL(inputUrl);\n if (\n url.searchParams.has('redirect_uri') &&\n typeof url.searchParams.get('redirect_uri') === 'string'\n ) {\n // Ensure we reuse the redirect_uri if it's passed in the input url.\n return url.searchParams.get('redirect_uri')!;\n }\n // Emulate how native uses Constants.linkingUrl\n return location.origin + location.pathname;\n}\n\nconst CHARSET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';\n\nasync function generateStateAsync(): Promise<string> {\n if (!isSubtleCryptoAvailable()) {\n throw new CodedError(\n 'ERR_WEB_BROWSER_CRYPTO',\n `The current environment doesn't support crypto. Ensure you are running from a secure origin (https).`\n );\n }\n const encoder = new TextEncoder();\n\n const data = generateRandom(10);\n const buffer = encoder.encode(data);\n const hashedData = await crypto.subtle.digest('SHA-256', buffer);\n const state = btoa(String.fromCharCode(...new Uint8Array(hashedData)));\n return state;\n}\n\nfunction generateRandom(size: number): string {\n let arr = new Uint8Array(size);\n if (arr.byteLength !== arr.length) {\n arr = new Uint8Array(arr.buffer);\n }\n const array = new Uint8Array(arr.length);\n if (isCryptoAvailable()) {\n window.crypto.getRandomValues(array);\n } else {\n for (let i = 0; i < size; i += 1) {\n array[i] = (Math.random() * CHARSET.length) | 0;\n }\n }\n return bufferToString(array);\n}\n\nfunction bufferToString(buffer): string {\n const state: string[] = [];\n for (let i = 0; i < buffer.byteLength; i += 1) {\n const index = buffer[i] % CHARSET.length;\n state.push(CHARSET[index]);\n }\n return state.join('');\n}\n\n// Window Features\n\n// Ensure feature string is an object\nfunction normalizePopupFeaturesString(\n options?: WebBrowserWindowFeatures | string\n): Record<string, any> {\n let windowFeatures: Record<string, any> = {};\n // This should be avoided because it adds extra time to the popup command.\n if (typeof options === 'string') {\n // Convert string of `key=value,foo=bar` into an object\n const windowFeaturePairs = options.split(',');\n for (const pair of windowFeaturePairs) {\n const [key, value] = pair.trim().split('=');\n if (key && value) {\n windowFeatures[key] = value;\n }\n }\n } else if (options) {\n windowFeatures = options;\n }\n return windowFeatures;\n}\n\n// Apply default values to the input feature set\nfunction getPopupFeaturesString(options?: WebBrowserWindowFeatures | string): string {\n const windowFeatures = normalizePopupFeaturesString(options);\n\n const width = windowFeatures.width ?? POPUP_WIDTH;\n const height = windowFeatures.height ?? POPUP_HEIGHT;\n\n const dimensions = Dimensions.get('screen');\n const top = windowFeatures.top ?? Math.max(0, (dimensions.height - height) * 0.5);\n const left = windowFeatures.left ?? Math.max(0, (dimensions.width - width) * 0.5);\n\n // Create a reasonable popup\n // https://developer.mozilla.org/en-US/docs/Web/API/Window/open#Window_features\n return featureObjectToString({\n ...windowFeatures,\n // Toolbar buttons (Back, Forward, Reload, Stop buttons).\n toolbar: windowFeatures.toolbar ?? 'no',\n menubar: windowFeatures.menubar ?? 'no',\n // Shows the location bar or the address bar.\n location: windowFeatures.location ?? 'yes',\n resizable: windowFeatures.resizable ?? 'yes',\n // If this feature is on, then the new secondary window has a status bar.\n status: windowFeatures.status ?? 'no',\n scrollbars: windowFeatures.scrollbars ?? 'yes',\n top,\n left,\n width,\n height,\n });\n}\n\nexport function featureObjectToString(features: Record<string, any>): string {\n return Object.keys(features).reduce<string>((prev, current) => {\n let value = features[current];\n if (typeof value === 'boolean') {\n value = value ? 'yes' : 'no';\n }\n if (current && value) {\n if (prev) prev += ',';\n return `${prev}${current}=${value}`;\n }\n return prev;\n }, '');\n}\n"]}