basalt-core 0.0.0-rc5

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 (325) hide show
  1. package/LICENSE +21 -0
  2. package/dist/cli/basalt.d.ts +3 -0
  3. package/dist/cli/basalt.d.ts.map +1 -0
  4. package/dist/cli/basalt.js +47 -0
  5. package/dist/cli/basalt.js.map +1 -0
  6. package/dist/cli/bundle-cli.d.ts +12 -0
  7. package/dist/cli/bundle-cli.d.ts.map +1 -0
  8. package/dist/cli/bundle-cli.js +124 -0
  9. package/dist/cli/bundle-cli.js.map +1 -0
  10. package/dist/cli/bundleWithAssets.d.ts +79 -0
  11. package/dist/cli/bundleWithAssets.d.ts.map +1 -0
  12. package/dist/cli/bundleWithAssets.js +198 -0
  13. package/dist/cli/bundleWithAssets.js.map +1 -0
  14. package/dist/cli/desktop.d.ts +339 -0
  15. package/dist/cli/desktop.d.ts.map +1 -0
  16. package/dist/cli/desktop.js +1029 -0
  17. package/dist/cli/desktop.js.map +1 -0
  18. package/dist/cli/doctor.d.ts +31 -0
  19. package/dist/cli/doctor.d.ts.map +1 -0
  20. package/dist/cli/doctor.js +283 -0
  21. package/dist/cli/doctor.js.map +1 -0
  22. package/dist/cli/init.d.ts +77 -0
  23. package/dist/cli/init.d.ts.map +1 -0
  24. package/dist/cli/init.js +442 -0
  25. package/dist/cli/init.js.map +1 -0
  26. package/dist/cli/metro.d.ts +49 -0
  27. package/dist/cli/metro.d.ts.map +1 -0
  28. package/dist/cli/metro.js +148 -0
  29. package/dist/cli/metro.js.map +1 -0
  30. package/dist/cli/packageApp.d.ts +102 -0
  31. package/dist/cli/packageApp.d.ts.map +1 -0
  32. package/dist/cli/packageApp.js +319 -0
  33. package/dist/cli/packageApp.js.map +1 -0
  34. package/dist/cli/steps.d.ts +34 -0
  35. package/dist/cli/steps.d.ts.map +1 -0
  36. package/dist/cli/steps.js +53 -0
  37. package/dist/cli/steps.js.map +1 -0
  38. package/dist/metro-config.d.ts +168 -0
  39. package/dist/metro-config.d.ts.map +1 -0
  40. package/dist/metro-config.js +671 -0
  41. package/dist/metro-config.js.map +1 -0
  42. package/dist/react-native.config.d.ts +7 -0
  43. package/dist/react-native.config.d.ts.map +1 -0
  44. package/dist/react-native.config.js +80 -0
  45. package/dist/react-native.config.js.map +1 -0
  46. package/dist/src/DragSource.d.ts +52 -0
  47. package/dist/src/DragSource.d.ts.map +1 -0
  48. package/dist/src/DragSource.js +97 -0
  49. package/dist/src/DragSource.js.map +1 -0
  50. package/dist/src/DropTarget.d.ts +58 -0
  51. package/dist/src/DropTarget.d.ts.map +1 -0
  52. package/dist/src/DropTarget.js +131 -0
  53. package/dist/src/DropTarget.js.map +1 -0
  54. package/dist/src/KeyHandler.d.ts +57 -0
  55. package/dist/src/KeyHandler.d.ts.map +1 -0
  56. package/dist/src/KeyHandler.js +48 -0
  57. package/dist/src/KeyHandler.js.map +1 -0
  58. package/dist/src/Menu.d.ts +72 -0
  59. package/dist/src/Menu.d.ts.map +1 -0
  60. package/dist/src/Menu.js +179 -0
  61. package/dist/src/Menu.js.map +1 -0
  62. package/dist/src/TitleBar.d.ts +75 -0
  63. package/dist/src/TitleBar.d.ts.map +1 -0
  64. package/dist/src/TitleBar.js +236 -0
  65. package/dist/src/TitleBar.js.map +1 -0
  66. package/dist/src/Window.d.ts +87 -0
  67. package/dist/src/Window.d.ts.map +1 -0
  68. package/dist/src/Window.js +248 -0
  69. package/dist/src/Window.js.map +1 -0
  70. package/dist/src/closeRequest.d.ts +55 -0
  71. package/dist/src/closeRequest.d.ts.map +1 -0
  72. package/dist/src/closeRequest.js +134 -0
  73. package/dist/src/closeRequest.js.map +1 -0
  74. package/dist/src/index.d.ts +45 -0
  75. package/dist/src/index.d.ts.map +1 -0
  76. package/dist/src/index.js +92 -0
  77. package/dist/src/index.js.map +1 -0
  78. package/dist/src/overrides/Alert.d.ts +69 -0
  79. package/dist/src/overrides/Alert.d.ts.map +1 -0
  80. package/dist/src/overrides/Alert.js +112 -0
  81. package/dist/src/overrides/Alert.js.map +1 -0
  82. package/dist/src/overrides/BaseViewConfig.d.ts +28 -0
  83. package/dist/src/overrides/BaseViewConfig.d.ts.map +1 -0
  84. package/dist/src/overrides/BaseViewConfig.js +72 -0
  85. package/dist/src/overrides/BaseViewConfig.js.map +1 -0
  86. package/dist/src/overrides/Platform.linux.d.ts +10 -0
  87. package/dist/src/overrides/Platform.linux.d.ts.map +1 -0
  88. package/dist/src/overrides/Platform.linux.js +15 -0
  89. package/dist/src/overrides/Platform.linux.js.map +1 -0
  90. package/dist/src/overrides/Platform.macos.d.ts +10 -0
  91. package/dist/src/overrides/Platform.macos.d.ts.map +1 -0
  92. package/dist/src/overrides/Platform.macos.js +15 -0
  93. package/dist/src/overrides/Platform.macos.js.map +1 -0
  94. package/dist/src/overrides/Platform.windows.d.ts +10 -0
  95. package/dist/src/overrides/Platform.windows.d.ts.map +1 -0
  96. package/dist/src/overrides/Platform.windows.js +15 -0
  97. package/dist/src/overrides/Platform.windows.js.map +1 -0
  98. package/dist/src/overrides/ReactDevToolsSettingsManager.d.ts +24 -0
  99. package/dist/src/overrides/ReactDevToolsSettingsManager.d.ts.map +1 -0
  100. package/dist/src/overrides/ReactDevToolsSettingsManager.js +30 -0
  101. package/dist/src/overrides/ReactDevToolsSettingsManager.js.map +1 -0
  102. package/dist/src/overrides/RefreshControl.d.ts +52 -0
  103. package/dist/src/overrides/RefreshControl.d.ts.map +1 -0
  104. package/dist/src/overrides/RefreshControl.js +92 -0
  105. package/dist/src/overrides/RefreshControl.js.map +1 -0
  106. package/dist/src/overrides/ScrollView.d.ts +39 -0
  107. package/dist/src/overrides/ScrollView.d.ts.map +1 -0
  108. package/dist/src/overrides/ScrollView.js +59 -0
  109. package/dist/src/overrides/ScrollView.js.map +1 -0
  110. package/dist/src/overrides/Share.d.ts +96 -0
  111. package/dist/src/overrides/Share.d.ts.map +1 -0
  112. package/dist/src/overrides/Share.js +97 -0
  113. package/dist/src/overrides/Share.js.map +1 -0
  114. package/dist/src/overrides/TextInput.d.ts +81 -0
  115. package/dist/src/overrides/TextInput.d.ts.map +1 -0
  116. package/dist/src/overrides/TextInput.js +213 -0
  117. package/dist/src/overrides/TextInput.js.map +1 -0
  118. package/dist/src/overrides/View.d.ts +36 -0
  119. package/dist/src/overrides/View.d.ts.map +1 -0
  120. package/dist/src/overrides/View.js +99 -0
  121. package/dist/src/overrides/View.js.map +1 -0
  122. package/dist/src/overrides/createPlatform.d.ts +32 -0
  123. package/dist/src/overrides/createPlatform.d.ts.map +1 -0
  124. package/dist/src/overrides/createPlatform.js +84 -0
  125. package/dist/src/overrides/createPlatform.js.map +1 -0
  126. package/dist/src/overrides/setUpDeveloperTools.d.ts +28 -0
  127. package/dist/src/overrides/setUpDeveloperTools.d.ts.map +1 -0
  128. package/dist/src/overrides/setUpDeveloperTools.js +84 -0
  129. package/dist/src/overrides/setUpDeveloperTools.js.map +1 -0
  130. package/dist/src/overrides/setUpXHR.d.ts +46 -0
  131. package/dist/src/overrides/setUpXHR.d.ts.map +1 -0
  132. package/dist/src/overrides/setUpXHR.js +236 -0
  133. package/dist/src/overrides/setUpXHR.js.map +1 -0
  134. package/dist/src/quitRequest.d.ts +55 -0
  135. package/dist/src/quitRequest.d.ts.map +1 -0
  136. package/dist/src/quitRequest.js +121 -0
  137. package/dist/src/quitRequest.js.map +1 -0
  138. package/dist/src/resourcePath.d.ts +18 -0
  139. package/dist/src/resourcePath.d.ts.map +1 -0
  140. package/dist/src/resourcePath.js +31 -0
  141. package/dist/src/resourcePath.js.map +1 -0
  142. package/dist/src/responseBody.d.ts +76 -0
  143. package/dist/src/responseBody.d.ts.map +1 -0
  144. package/dist/src/responseBody.js +94 -0
  145. package/dist/src/responseBody.js.map +1 -0
  146. package/dist/src/responseHeaders.d.ts +40 -0
  147. package/dist/src/responseHeaders.d.ts.map +1 -0
  148. package/dist/src/responseHeaders.js +75 -0
  149. package/dist/src/responseHeaders.js.map +1 -0
  150. package/dist/src/titleBarState.d.ts +43 -0
  151. package/dist/src/titleBarState.d.ts.map +1 -0
  152. package/dist/src/titleBarState.js +77 -0
  153. package/dist/src/titleBarState.js.map +1 -0
  154. package/dist/src/useContextMenu.d.ts +113 -0
  155. package/dist/src/useContextMenu.d.ts.map +1 -0
  156. package/dist/src/useContextMenu.js +109 -0
  157. package/dist/src/useContextMenu.js.map +1 -0
  158. package/dist/src/useDialog.d.ts +98 -0
  159. package/dist/src/useDialog.d.ts.map +1 -0
  160. package/dist/src/useDialog.js +112 -0
  161. package/dist/src/useDialog.js.map +1 -0
  162. package/dist/src/useDisplays.d.ts +70 -0
  163. package/dist/src/useDisplays.d.ts.map +1 -0
  164. package/dist/src/useDisplays.js +118 -0
  165. package/dist/src/useDisplays.js.map +1 -0
  166. package/dist/src/useHandledKeys.d.ts +38 -0
  167. package/dist/src/useHandledKeys.d.ts.map +1 -0
  168. package/dist/src/useHandledKeys.js +70 -0
  169. package/dist/src/useHandledKeys.js.map +1 -0
  170. package/dist/src/useWindow.d.ts +140 -0
  171. package/dist/src/useWindow.d.ts.map +1 -0
  172. package/dist/src/useWindow.js +233 -0
  173. package/dist/src/useWindow.js.map +1 -0
  174. package/dist/tsconfig.tsbuildinfo +1 -0
  175. package/native/CMakeLists.txt +355 -0
  176. package/native/bootstrap.sh +613 -0
  177. package/native/cmake/CompilerFlags.cmake +49 -0
  178. package/native/cmake/Expo.cmake +125 -0
  179. package/native/cmake/ReactNativeCore.cmake +612 -0
  180. package/native/cmake/Reanimated.cmake +108 -0
  181. package/native/cmake/Skia.cmake +379 -0
  182. package/native/cmake/ThirdParty.cmake +290 -0
  183. package/native/cmake/Worklets.cmake +67 -0
  184. package/native/cmake/codegen/CMakeLists.txt +32 -0
  185. package/native/core/AppIdentity.cpp +85 -0
  186. package/native/core/AppIdentity.h +59 -0
  187. package/native/core/AppPaths.cpp +96 -0
  188. package/native/core/AppPaths.h +53 -0
  189. package/native/core/AppearanceModule.cpp +60 -0
  190. package/native/core/AppearanceModule.h +40 -0
  191. package/native/core/Backface.h +59 -0
  192. package/native/core/BlobModule.cpp +283 -0
  193. package/native/core/BlobModule.h +76 -0
  194. package/native/core/BlobRegistry.cpp +75 -0
  195. package/native/core/BlobRegistry.h +44 -0
  196. package/native/core/ColorScheme.cpp +105 -0
  197. package/native/core/ColorScheme.h +59 -0
  198. package/native/core/ComponentRegistry.h +30 -0
  199. package/native/core/ControlMetrics.h +37 -0
  200. package/native/core/CoreModules.cpp +592 -0
  201. package/native/core/CoreModules.h +308 -0
  202. package/native/core/DebuggingOverlay.cpp +106 -0
  203. package/native/core/DebuggingOverlay.h +65 -0
  204. package/native/core/DesktopControls.cpp +232 -0
  205. package/native/core/DesktopControls.h +170 -0
  206. package/native/core/DevBundle.cpp +108 -0
  207. package/native/core/DevBundle.h +69 -0
  208. package/native/core/DevMenu.cpp +99 -0
  209. package/native/core/DevMenu.h +75 -0
  210. package/native/core/DialogModule.cpp +150 -0
  211. package/native/core/DialogModule.h +51 -0
  212. package/native/core/DragAndDrop.cpp +45 -0
  213. package/native/core/DragAndDrop.h +225 -0
  214. package/native/core/ExpoImageComponent.cpp +79 -0
  215. package/native/core/ExpoImageComponent.h +80 -0
  216. package/native/core/ExpoModules.cpp +415 -0
  217. package/native/core/ExpoModules.h +115 -0
  218. package/native/core/ExpoRuntime.cpp +366 -0
  219. package/native/core/ExpoRuntime.h +42 -0
  220. package/native/core/FocusRing.h +36 -0
  221. package/native/core/FontRegistry.h +51 -0
  222. package/native/core/GestureHandlerModule.cpp +136 -0
  223. package/native/core/GestureHandlerModule.h +71 -0
  224. package/native/core/Gestures.cpp +533 -0
  225. package/native/core/Gestures.h +145 -0
  226. package/native/core/HoverTracker.cpp +69 -0
  227. package/native/core/HoverTracker.h +109 -0
  228. package/native/core/HttpClient.cpp +282 -0
  229. package/native/core/ImageBytes.cpp +205 -0
  230. package/native/core/ImageBytes.h +29 -0
  231. package/native/core/ImageCache.cpp +51 -0
  232. package/native/core/ImageCache.h +89 -0
  233. package/native/core/JsRuntimeAccess.cpp +70 -0
  234. package/native/core/JsRuntimeAccess.h +60 -0
  235. package/native/core/JsiPromise.h +51 -0
  236. package/native/core/KeyEvents.cpp +116 -0
  237. package/native/core/KeyEvents.h +144 -0
  238. package/native/core/LogBoxSurface.cpp +46 -0
  239. package/native/core/LogBoxSurface.h +68 -0
  240. package/native/core/MenuModel.h +117 -0
  241. package/native/core/MenuModule.cpp +209 -0
  242. package/native/core/MenuModule.h +66 -0
  243. package/native/core/MountingWalk.h +524 -0
  244. package/native/core/PackageModules.cpp.in +25 -0
  245. package/native/core/PackageModules.h +50 -0
  246. package/native/core/PlatformConstantsModule.cpp +63 -0
  247. package/native/core/PlatformConstantsModule.h +64 -0
  248. package/native/core/PlatformServices.h +213 -0
  249. package/native/core/PointerButtons.h +63 -0
  250. package/native/core/PullToRefresh.cpp +57 -0
  251. package/native/core/PullToRefresh.h +73 -0
  252. package/native/core/ReanimatedCompat.h +47 -0
  253. package/native/core/ReanimatedModule.cpp +203 -0
  254. package/native/core/ReanimatedModule.h +55 -0
  255. package/native/core/ScrollAnimation.cpp +59 -0
  256. package/native/core/ScrollAnimation.h +70 -0
  257. package/native/core/ScrollBounds.cpp +26 -0
  258. package/native/core/ScrollBounds.h +68 -0
  259. package/native/core/ScrollIndicator.cpp +50 -0
  260. package/native/core/ScrollIndicator.h +69 -0
  261. package/native/core/ScrollMomentum.cpp +71 -0
  262. package/native/core/ScrollMomentum.h +73 -0
  263. package/native/core/ScrollSnap.cpp +123 -0
  264. package/native/core/ScrollSnap.h +80 -0
  265. package/native/core/ShareFallback.cpp +101 -0
  266. package/native/core/ShareFallback.h +45 -0
  267. package/native/core/SkiaPictureViewComponent.cpp +11 -0
  268. package/native/core/SkiaPictureViewComponent.h +47 -0
  269. package/native/core/SourceCodeModule.cpp +55 -0
  270. package/native/core/SourceCodeModule.h +53 -0
  271. package/native/core/StatusBarModule.cpp +23 -0
  272. package/native/core/StatusBarModule.h +42 -0
  273. package/native/core/TestDialog.cpp +148 -0
  274. package/native/core/TestDialog.h +96 -0
  275. package/native/core/TestKeys.cpp +78 -0
  276. package/native/core/TestKeys.h +50 -0
  277. package/native/core/TestQuitFile.cpp +33 -0
  278. package/native/core/TestQuitFile.h +60 -0
  279. package/native/core/TestSettle.h +192 -0
  280. package/native/core/TitleBarRegions.h +31 -0
  281. package/native/core/TurboModuleProxy.cpp +51 -0
  282. package/native/core/TurboModuleProxy.h +46 -0
  283. package/native/core/UIManagerAccess.cpp +43 -0
  284. package/native/core/UIManagerAccess.h +43 -0
  285. package/native/core/WindowBoundsCache.cpp +319 -0
  286. package/native/core/WindowControl.h +235 -0
  287. package/native/core/WindowHost.h +158 -0
  288. package/native/core/WindowMethods.cpp +167 -0
  289. package/native/core/WindowMethods.h +84 -0
  290. package/native/core/WindowsModule.cpp +448 -0
  291. package/native/core/WindowsModule.h +133 -0
  292. package/native/core/WorkletsModule.cpp +199 -0
  293. package/native/core/WorkletsModule.h +57 -0
  294. package/native/core/portability_probe.cpp +210 -0
  295. package/native/tests/EventRecorder.h +124 -0
  296. package/native/tests/TestHarness.cpp +109 -0
  297. package/native/tests/TestHarness.h +89 -0
  298. package/native/tests/TreeDump.h +49 -0
  299. package/native/tests/test_app_identity.cpp +114 -0
  300. package/native/tests/test_app_paths.cpp +48 -0
  301. package/native/tests/test_appearance.cpp +107 -0
  302. package/native/tests/test_backface.cpp +79 -0
  303. package/native/tests/test_blobs.cpp +157 -0
  304. package/native/tests/test_controls.cpp +185 -0
  305. package/native/tests/test_devbundle.cpp +103 -0
  306. package/native/tests/test_drag_and_drop.cpp +143 -0
  307. package/native/tests/test_gestures.cpp +331 -0
  308. package/native/tests/test_hover.cpp +92 -0
  309. package/native/tests/test_image_cache.cpp +109 -0
  310. package/native/tests/test_js_runtime_access.cpp +137 -0
  311. package/native/tests/test_key_events.cpp +235 -0
  312. package/native/tests/test_momentum.cpp +138 -0
  313. package/native/tests/test_quit_file.cpp +104 -0
  314. package/native/tests/test_scroll_animation.cpp +120 -0
  315. package/native/tests/test_scroll_bounds.cpp +66 -0
  316. package/native/tests/test_scroll_indicator.cpp +149 -0
  317. package/native/tests/test_scroll_snap.cpp +112 -0
  318. package/native/tests/test_settle.cpp +115 -0
  319. package/native/tests/test_share.cpp +75 -0
  320. package/native/tests/test_sourcecode.cpp +62 -0
  321. package/native/tests/test_window_close.cpp +119 -0
  322. package/native/tests/test_window_limits.cpp +123 -0
  323. package/package.json +43 -0
  324. package/react-native.config.js +86 -0
  325. package/supported-versions.json +66 -0
@@ -0,0 +1,60 @@
1
+ #include "AppearanceModule.h"
2
+
3
+ #include "ColorScheme.h"
4
+
5
+ namespace basalt {
6
+
7
+ using facebook::jsi::Runtime;
8
+ using facebook::jsi::String;
9
+
10
+ DesktopAppearanceModule::DesktopAppearanceModule(
11
+ std::shared_ptr<facebook::react::CallInvoker> jsInvoker)
12
+ : NativeAppearanceCxxSpec(std::move(jsInvoker)) {
13
+ // Subscribed for the module's whole life rather than when JavaScript adds a
14
+ // listener. `Appearance.addChangeListener` is a JavaScript-side subscription
15
+ // on an event emitter; this is the thing that feeds it, and it has to be
16
+ // running before the first listener arrives or the first change is lost.
17
+ observerToken_ = addColorSchemeObserver([this](ColorScheme scheme) {
18
+ emitDeviceEvent("appearanceChanged",
19
+ [scheme](Runtime &rt, std::vector<facebook::jsi::Value> &args) {
20
+ // NativeAppearancePreferences: { colorScheme }.
21
+ auto preferences = facebook::jsi::Object(rt);
22
+ preferences.setProperty(
23
+ rt, "colorScheme", String::createFromUtf8(rt, colorSchemeName(scheme)));
24
+ args.emplace_back(rt, preferences);
25
+ });
26
+ });
27
+ }
28
+
29
+ DesktopAppearanceModule::~DesktopAppearanceModule() {
30
+ removeColorSchemeObserver(observerToken_);
31
+ }
32
+
33
+ String DesktopAppearanceModule::getColorScheme(Runtime &rt) {
34
+ return String::createFromUtf8(rt, colorSchemeName(effectiveColorScheme()));
35
+ }
36
+
37
+ void DesktopAppearanceModule::setColorScheme(Runtime &rt, std::string colorScheme) {
38
+ (void)rt;
39
+ // ColorSchemeOverride is 'light' | 'dark' | 'auto' | 'unspecified', and the
40
+ // last two both mean "stop overriding". Handling only one of them is the kind
41
+ // of gap an app finds and a test does not, because which spelling reaches
42
+ // here depends on what the app passed.
43
+ if (colorScheme == "unspecified" || colorScheme == "auto") {
44
+ clearColorSchemeOverride();
45
+ return;
46
+ }
47
+ setColorSchemeOverride(colorScheme);
48
+ }
49
+
50
+ void DesktopAppearanceModule::addListener(Runtime &rt, std::string eventName) {
51
+ (void)rt;
52
+ (void)eventName;
53
+ }
54
+
55
+ void DesktopAppearanceModule::removeListeners(Runtime &rt, double count) {
56
+ (void)rt;
57
+ (void)count;
58
+ }
59
+
60
+ } // namespace basalt
@@ -0,0 +1,40 @@
1
+ // `Appearance`, which is how an app learns it should be dark.
2
+ //
3
+ // Everything here is portable. Which scheme the system is in is not, and is
4
+ // behind the seam in ColorScheme.h.
5
+ //
6
+ // React Native looks this module up with `getEnforcing`, so its absence is not
7
+ // a quiet no-op: `Appearance.getColorScheme()` throws and takes the first render
8
+ // with it. That is what a stock Expo app hit in phase 29.
9
+
10
+ #pragma once
11
+
12
+ #include <FBReactNativeSpec/FBReactNativeSpecJSI.h>
13
+
14
+ #include <string>
15
+
16
+ namespace basalt {
17
+
18
+ class DesktopAppearanceModule
19
+ : public facebook::react::NativeAppearanceCxxSpec<DesktopAppearanceModule> {
20
+ public:
21
+ explicit DesktopAppearanceModule(std::shared_ptr<facebook::react::CallInvoker> jsInvoker);
22
+ ~DesktopAppearanceModule() override;
23
+
24
+ static constexpr const char *kModuleName = "Appearance";
25
+
26
+ facebook::jsi::String getColorScheme(facebook::jsi::Runtime &rt);
27
+ void setColorScheme(facebook::jsi::Runtime &rt, std::string colorScheme);
28
+
29
+ // The listener bookkeeping React Native's NativeEventEmitter contract wants.
30
+ // The real subscription is the one taken out in the constructor: JavaScript
31
+ // adding a listener does not change what this module watches, only whether
32
+ // anybody is there when it fires.
33
+ void addListener(facebook::jsi::Runtime &rt, std::string eventName);
34
+ void removeListeners(facebook::jsi::Runtime &rt, double count);
35
+
36
+ private:
37
+ int observerToken_{0};
38
+ };
39
+
40
+ } // namespace basalt
@@ -0,0 +1,59 @@
1
+ // Whether a transform has turned a view away from the viewer.
2
+ //
3
+ // `backfaceVisibility: 'hidden'` is what stops the back of a card being drawn
4
+ // mirrored halfway through a flip. Without it a rotation past ninety degrees
5
+ // shows the front face reversed, which reads as a rendering bug rather than a
6
+ // missing prop -- and all three hosts ignored the prop entirely.
7
+ //
8
+ // ## The test is the sign of the determinant
9
+ //
10
+ // A transform faces away exactly when it mirrors: the 2D part of the matrix
11
+ // has a negative determinant. `rotateY(180deg)` and `rotateX(180deg)` both
12
+ // produce one, `rotateZ(180deg)` does not -- turning something in its own
13
+ // plane never shows its back -- and `scaleX(-1)` does, which is right, because
14
+ // CSS hides that too.
15
+ //
16
+ // ## Why not CALayer's `doubleSided`
17
+ //
18
+ // macOS has this prop natively and the others do not. Using it there would be
19
+ // less code and would answer differently the day perspective lands: Core
20
+ // Animation would consider the whole 4x4 and the other two would consider the
21
+ // 2D part. One rule that all three obey is worth more than one host being
22
+ // idiomatic, and it is the same argument core/ScrollIndicator.h and
23
+ // core/ScrollBounds.h make. Nothing here draws perspective yet;
24
+ // docs/backlog/correctness.md records that.
25
+
26
+ // Header-only on purpose. `RnView.cpp` and `RnWin32View.cpp` are in the
27
+ // view-layer libraries, which link no React Native and no core -- that is what
28
+ // lets `demo_layout_gtk` and the view tests build without one. A .cpp here
29
+ // would put a link dependency between the two, the same reason
30
+ // core/ScrollIndicator.h keeps its constants inline.
31
+
32
+ #pragma once
33
+
34
+ namespace basalt {
35
+
36
+ // `matrix` is sixteen floats in CSS `matrix3d` order, which is what React
37
+ // Native produces and what every host here already stores.
38
+ //
39
+ // False for an identity or any rotation in the plane, so a view with no
40
+ // transform is never hidden by this.
41
+ // The rule from the four numbers that matter. A host that keeps only the 2D
42
+ // affine -- Windows does, because that is all its paint and hit testing need
43
+ // -- comes in here; everything else comes in below.
44
+ inline bool facesAway(float a, float b, float c, float d) {
45
+ // A negative determinant means the transform mirrors, and a mirrored view is
46
+ // one you are looking at the back of.
47
+ return (a * d - b * c) < 0.0f;
48
+ }
49
+
50
+ inline bool facesAway(const float matrix[16]) {
51
+ if (matrix == nullptr) {
52
+ return false;
53
+ }
54
+ // The 2D part, in `matrix3d` order: m11 m12 are the first column and m21 m22
55
+ // the second, so the four that matter are 0, 1, 4 and 5.
56
+ return facesAway(matrix[0], matrix[1], matrix[4], matrix[5]);
57
+ }
58
+
59
+ } // namespace basalt
@@ -0,0 +1,283 @@
1
+ #include "BlobModule.h"
2
+
3
+ #include "BlobRegistry.h"
4
+
5
+ #include <glog/logging.h>
6
+
7
+ #include <array>
8
+ #include <cstdint>
9
+
10
+ namespace basalt {
11
+
12
+ using facebook::jsi::Array;
13
+ using facebook::jsi::Object;
14
+ using facebook::jsi::Runtime;
15
+ using facebook::jsi::String;
16
+ using facebook::jsi::Value;
17
+
18
+ namespace {
19
+
20
+ // A blob handle as JavaScript passes it: {blobId, offset, size}.
21
+ struct BlobHandle {
22
+ std::string blobId;
23
+ size_t offset{0};
24
+ size_t size{0};
25
+ };
26
+
27
+ BlobHandle readHandle(Runtime &rt, const Object &blob) {
28
+ BlobHandle handle;
29
+ handle.blobId = blob.getProperty(rt, "blobId").asString(rt).utf8(rt);
30
+ const Value offset = blob.getProperty(rt, "offset");
31
+ const Value size = blob.getProperty(rt, "size");
32
+ handle.offset = offset.isNumber() ? static_cast<size_t>(offset.asNumber()) : 0;
33
+ handle.size = size.isNumber() ? static_cast<size_t>(size.asNumber()) : 0;
34
+ return handle;
35
+ }
36
+
37
+ // A promise already settled, since the bytes never left memory.
38
+ //
39
+ // Through JavaScript's own `Promise.resolve` rather than through jsi's
40
+ // AsyncPromise: this needs no thread hop and no invoker, and borrowing the
41
+ // machinery for one would only add a way for it to be wrong.
42
+ Value resolved(Runtime &rt, Value &&value) {
43
+ auto promise = rt.global().getPropertyAsObject(rt, "Promise");
44
+ return promise.getPropertyAsFunction(rt, "resolve").callWithThis(rt, promise, value);
45
+ }
46
+
47
+ Value rejected(Runtime &rt, const std::string &message) {
48
+ auto promise = rt.global().getPropertyAsObject(rt, "Promise");
49
+ auto error = rt.global()
50
+ .getPropertyAsFunction(rt, "Error")
51
+ .callAsConstructor(rt, String::createFromUtf8(rt, message));
52
+ return promise.getPropertyAsFunction(rt, "reject").callWithThis(rt, promise, error);
53
+ }
54
+
55
+ } // namespace
56
+
57
+ namespace detail {
58
+
59
+ std::string base64Encode(const std::string &bytes) {
60
+ static constexpr std::string_view kAlphabet =
61
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
62
+
63
+ std::string out;
64
+ out.reserve(((bytes.size() + 2) / 3) * 4);
65
+
66
+ size_t i = 0;
67
+ while (i + 2 < bytes.size()) {
68
+ const uint32_t triple = (static_cast<unsigned char>(bytes[i]) << 16) |
69
+ (static_cast<unsigned char>(bytes[i + 1]) << 8) |
70
+ static_cast<unsigned char>(bytes[i + 2]);
71
+ out.push_back(kAlphabet[(triple >> 18) & 0x3F]);
72
+ out.push_back(kAlphabet[(triple >> 12) & 0x3F]);
73
+ out.push_back(kAlphabet[(triple >> 6) & 0x3F]);
74
+ out.push_back(kAlphabet[triple & 0x3F]);
75
+ i += 3;
76
+ }
77
+
78
+ // The tail, padded. Getting this wrong is the classic base64 bug and it only
79
+ // shows on inputs whose length is not a multiple of three.
80
+ if (i < bytes.size()) {
81
+ const size_t remaining = bytes.size() - i;
82
+ uint32_t triple = static_cast<unsigned char>(bytes[i]) << 16;
83
+ if (remaining == 2) {
84
+ triple |= static_cast<unsigned char>(bytes[i + 1]) << 8;
85
+ }
86
+ out.push_back(kAlphabet[(triple >> 18) & 0x3F]);
87
+ out.push_back(kAlphabet[(triple >> 12) & 0x3F]);
88
+ out.push_back(remaining == 2 ? kAlphabet[(triple >> 6) & 0x3F] : '=');
89
+ out.push_back('=');
90
+ }
91
+
92
+ return out;
93
+ }
94
+
95
+ // The inverse, for the `base64` part type below.
96
+ //
97
+ // Skips anything outside the alphabet rather than rejecting it, which covers
98
+ // the padding and any newlines a transport introduced. A truncated group -- one
99
+ // leftover character, which encodes nothing -- is dropped.
100
+ std::string base64Decode(const std::string &text) {
101
+ static constexpr std::string_view kAlphabet =
102
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
103
+
104
+ std::string out;
105
+ out.reserve((text.size() / 4) * 3);
106
+
107
+ uint32_t group = 0;
108
+ int digits = 0;
109
+ for (const char c : text) {
110
+ const size_t value = kAlphabet.find(c);
111
+ if (value == std::string_view::npos) {
112
+ continue;
113
+ }
114
+ group = (group << 6) | static_cast<uint32_t>(value);
115
+ if (++digits == 4) {
116
+ out.push_back(static_cast<char>((group >> 16) & 0xFF));
117
+ out.push_back(static_cast<char>((group >> 8) & 0xFF));
118
+ out.push_back(static_cast<char>(group & 0xFF));
119
+ group = 0;
120
+ digits = 0;
121
+ }
122
+ }
123
+
124
+ // A trailing group of two digits carries one byte, three carries two.
125
+ if (digits == 2) {
126
+ out.push_back(static_cast<char>((group >> 4) & 0xFF));
127
+ } else if (digits == 3) {
128
+ out.push_back(static_cast<char>((group >> 10) & 0xFF));
129
+ out.push_back(static_cast<char>((group >> 2) & 0xFF));
130
+ }
131
+
132
+ return out;
133
+ }
134
+
135
+ } // namespace detail
136
+
137
+ // ---------------------------------------------------------------------------
138
+ // BlobModule
139
+ // ---------------------------------------------------------------------------
140
+
141
+ Object DesktopBlobModule::getConstants(Runtime &rt) {
142
+ Object constants(rt);
143
+ // `blob:`, which is what the web and iOS use. Android says `content:` because
144
+ // its blobs are served through a ContentProvider; nothing here is.
145
+ //
146
+ // BLOB_URI_HOST is deliberately absent: URL.js only prefixes a host when this
147
+ // is a string, and a blob URL with an empty authority -- `blob://` -- is a
148
+ // worse thing to hand anybody than one without.
149
+ constants.setProperty(rt, "BLOB_URI_SCHEME", String::createFromUtf8(rt, "blob"));
150
+ return constants;
151
+ }
152
+
153
+ void DesktopBlobModule::createFromParts(Runtime &rt, Array parts, String blobId) {
154
+ std::string bytes;
155
+
156
+ const size_t count = parts.size(rt);
157
+ for (size_t i = 0; i < count; i++) {
158
+ Object part = parts.getValueAtIndex(rt, i).asObject(rt);
159
+ const std::string type = part.getProperty(rt, "type").asString(rt).utf8(rt);
160
+
161
+ if (type == "string") {
162
+ bytes += part.getProperty(rt, "data").asString(rt).utf8(rt);
163
+ continue;
164
+ }
165
+
166
+ // Not one of React Native's own part types. `fetch` on this platform routes
167
+ // a `responseType: 'blob'` response through base64 -- see
168
+ // src/overrides/setUpXHR.js -- because ReactCxxPlatform's NetworkingModule
169
+ // delivers every body as a JavaScript string, and a string cannot carry
170
+ // arbitrary bytes intact. Base64 can, and this is where it is turned back
171
+ // into bytes.
172
+ if (type == "base64") {
173
+ bytes += detail::base64Decode(part.getProperty(rt, "data").asString(rt).utf8(rt));
174
+ continue;
175
+ }
176
+
177
+ if (type == "blob") {
178
+ const BlobHandle handle = readHandle(rt, part.getProperty(rt, "data").asObject(rt));
179
+ if (auto slice = blobSlice(handle.blobId, handle.offset, handle.size)) {
180
+ bytes += *slice;
181
+ } else {
182
+ // A part naming a blob that has been released. Skipping it makes the
183
+ // result quietly short; saying so at least leaves a trace, and throwing
184
+ // would take down a Blob constructor that JavaScript considers valid.
185
+ LOG(WARNING) << "blob part refers to released blob " << handle.blobId;
186
+ }
187
+ continue;
188
+ }
189
+
190
+ LOG(WARNING) << "unknown blob part type '" << type << "'";
191
+ }
192
+
193
+ storeBlob(blobId.utf8(rt), std::move(bytes));
194
+ }
195
+
196
+ void DesktopBlobModule::release(Runtime &rt, String blobId) {
197
+ releaseBlob(blobId.utf8(rt));
198
+ }
199
+
200
+ void DesktopBlobModule::addNetworkingHandler(Runtime &rt) {
201
+ (void)rt;
202
+ // On iOS and Android this registers a handler with the networking module so
203
+ // that a request body of `{blob: ...}` is sent as bytes and a response with
204
+ // `responseType: 'blob'` comes back as one.
205
+ //
206
+ // ReactCxxPlatform's NetworkingModule has no such hook -- it does not mention
207
+ // blobs at all -- so there is nothing to register with. Request bodies still
208
+ // work, because core/HttpClient.cpp resolves a blob body through the registry
209
+ // itself; `responseType: 'blob'` does not, and cannot until upstream's
210
+ // networking module grows a seam.
211
+ //
212
+ // Silent rather than warning: JavaScript calls this once at startup on every
213
+ // platform, and a warning every app sees and nobody can act on is noise.
214
+ }
215
+
216
+ void DesktopBlobModule::addWebSocketHandler(Runtime &rt, double id) {
217
+ (void)rt;
218
+ (void)id;
219
+ }
220
+
221
+ void DesktopBlobModule::removeWebSocketHandler(Runtime &rt, double id) {
222
+ (void)rt;
223
+ (void)id;
224
+ }
225
+
226
+ void DesktopBlobModule::sendOverSocket(Runtime &rt, Object blob, double socketID) {
227
+ (void)blob;
228
+ (void)socketID;
229
+ (void)rt;
230
+ // Sending a blob over a websocket needs the socket, and WebSocketModule is
231
+ // ReactCxxPlatform's rather than ours. Unlike the handlers above this one is
232
+ // a call an app makes deliberately, so it says so rather than vanishing.
233
+ LOG(WARNING) << "sendOverSocket is not implemented: binary websocket frames "
234
+ "need a hook ReactCxxPlatform's WebSocketModule does not have";
235
+ }
236
+
237
+ // ---------------------------------------------------------------------------
238
+ // FileReaderModule
239
+ // ---------------------------------------------------------------------------
240
+
241
+ Value DesktopFileReaderModule::readAsDataURL(Runtime &rt, Object blob) {
242
+ const BlobHandle handle = readHandle(rt, blob);
243
+ auto slice = blobSlice(handle.blobId, handle.offset, handle.size);
244
+ if (!slice) {
245
+ return rejected(rt, "Unable to resolve data for blob: " + handle.blobId);
246
+ }
247
+
248
+ // The type is on the handle, not in the registry: two Blobs can share bytes
249
+ // and disagree about what they are.
250
+ std::string type;
251
+ const Value typeValue = blob.getProperty(rt, "type");
252
+ if (typeValue.isString()) {
253
+ type = typeValue.asString(rt).utf8(rt);
254
+ }
255
+ if (type.empty()) {
256
+ // What the web does for a Blob with no type, and what iOS does here.
257
+ type = "application/octet-stream";
258
+ }
259
+
260
+ const std::string url = "data:" + type + ";base64," + detail::base64Encode(*slice);
261
+ return resolved(rt, Value(String::createFromUtf8(rt, url)));
262
+ }
263
+
264
+ Value DesktopFileReaderModule::readAsText(Runtime &rt, Object blob, String encoding) {
265
+ const BlobHandle handle = readHandle(rt, blob);
266
+ auto slice = blobSlice(handle.blobId, handle.offset, handle.size);
267
+ if (!slice) {
268
+ return rejected(rt, "Unable to resolve data for blob: " + handle.blobId);
269
+ }
270
+
271
+ // Only UTF-8. React Native's JavaScript defaults to it and nothing in the
272
+ // library asks for another, but an app can, and a wrong answer that looks
273
+ // right is worse than a rejection that names the encoding.
274
+ const std::string requested = encoding.utf8(rt);
275
+ if (!requested.empty() && requested != "UTF-8" && requested != "utf-8" &&
276
+ requested != "utf8") {
277
+ return rejected(rt, "Unsupported encoding for readAsText: " + requested);
278
+ }
279
+
280
+ return resolved(rt, Value(String::createFromUtf8(rt, *slice)));
281
+ }
282
+
283
+ } // namespace basalt
@@ -0,0 +1,76 @@
1
+ // `BlobModule` and `FileReaderModule`: what `Blob`, `File` and `FileReader`
2
+ // stand on.
3
+ //
4
+ // A stock Expo app asks for BlobModule during startup -- `fetch` and
5
+ // `XMLHttpRequest` both register a blob handler if one exists -- and phase 29
6
+ // found it was the one missing module a real app would notice. Anything
7
+ // fetching binary data needs it.
8
+ //
9
+ // Both modules are portable in full. A blob is a byte array with a name on
10
+ // every platform, and reading one as text or as a data URL is arithmetic. The
11
+ // bytes live in BlobRegistry.h; these two are the JavaScript-facing half.
12
+
13
+ #pragma once
14
+
15
+ #include <FBReactNativeSpec/FBReactNativeSpecJSI.h>
16
+
17
+ #include <string>
18
+
19
+ namespace basalt {
20
+
21
+ // base64, both ways.
22
+ //
23
+ // Exposed rather than kept file-local because every `fetch` on this platform
24
+ // now goes through the decoder -- see src/overrides/setUpXHR.js -- and because
25
+ // the padded tail is the classic base64 bug, which only shows on inputs whose
26
+ // length is not a multiple of three. native/tests/test_blobs.cpp covers it.
27
+ namespace detail {
28
+ std::string base64Encode(const std::string &bytes);
29
+ std::string base64Decode(const std::string &text);
30
+ } // namespace detail
31
+
32
+ class DesktopBlobModule : public facebook::react::NativeBlobModuleCxxSpec<DesktopBlobModule> {
33
+ public:
34
+ explicit DesktopBlobModule(std::shared_ptr<facebook::react::CallInvoker> jsInvoker)
35
+ : NativeBlobModuleCxxSpec(std::move(jsInvoker)) {}
36
+
37
+ static constexpr const char *kModuleName = "BlobModule";
38
+
39
+ facebook::jsi::Object getConstants(facebook::jsi::Runtime &rt);
40
+
41
+ // `parts` is an array of `{data, type}`: a `string` part carries its text,
42
+ // and a `blob` part carries another blob's `{blobId, offset, size}`. The
43
+ // concatenation is stored under `blobId`.
44
+ void createFromParts(facebook::jsi::Runtime &rt,
45
+ facebook::jsi::Array parts,
46
+ facebook::jsi::String blobId);
47
+
48
+ void release(facebook::jsi::Runtime &rt, facebook::jsi::String blobId);
49
+
50
+ // The three below are about wiring blobs into networking and websockets, and
51
+ // none of them can do anything here. See the .cpp for why, which is a
52
+ // limitation of ReactCxxPlatform rather than of this platform.
53
+ void addNetworkingHandler(facebook::jsi::Runtime &rt);
54
+ void addWebSocketHandler(facebook::jsi::Runtime &rt, double id);
55
+ void removeWebSocketHandler(facebook::jsi::Runtime &rt, double id);
56
+ void sendOverSocket(facebook::jsi::Runtime &rt, facebook::jsi::Object blob, double socketID);
57
+ };
58
+
59
+ class DesktopFileReaderModule
60
+ : public facebook::react::NativeFileReaderModuleCxxSpec<DesktopFileReaderModule> {
61
+ public:
62
+ explicit DesktopFileReaderModule(std::shared_ptr<facebook::react::CallInvoker> jsInvoker)
63
+ : NativeFileReaderModuleCxxSpec(std::move(jsInvoker)) {}
64
+
65
+ static constexpr const char *kModuleName = "FileReaderModule";
66
+
67
+ // Both take a blob handle -- `{blobId, offset, size}` -- and return a
68
+ // promise, because that is what the spec says even though the bytes are
69
+ // already in memory and the answer is immediate.
70
+ facebook::jsi::Value readAsDataURL(facebook::jsi::Runtime &rt, facebook::jsi::Object blob);
71
+ facebook::jsi::Value readAsText(facebook::jsi::Runtime &rt,
72
+ facebook::jsi::Object blob,
73
+ facebook::jsi::String encoding);
74
+ };
75
+
76
+ } // namespace basalt
@@ -0,0 +1,75 @@
1
+ #include "BlobRegistry.h"
2
+
3
+ // For std::min, which libstdc++ does not promise through <string>.
4
+ #include <algorithm>
5
+ #include <mutex>
6
+ #include <unordered_map>
7
+
8
+ namespace basalt {
9
+
10
+ namespace {
11
+
12
+ struct Registry {
13
+ std::mutex mutex;
14
+ std::unordered_map<std::string, std::string> blobs;
15
+ };
16
+
17
+ Registry &registry() {
18
+ static Registry instance;
19
+ return instance;
20
+ }
21
+
22
+ } // namespace
23
+
24
+ void storeBlob(const std::string &blobId, std::string bytes) {
25
+ Registry &state = registry();
26
+ const std::lock_guard<std::mutex> lock(state.mutex);
27
+ state.blobs[blobId] = std::move(bytes);
28
+ }
29
+
30
+ std::optional<std::string> blobSlice(const std::string &blobId, size_t offset, size_t size) {
31
+ Registry &state = registry();
32
+ const std::lock_guard<std::mutex> lock(state.mutex);
33
+
34
+ const auto it = state.blobs.find(blobId);
35
+ if (it == state.blobs.end()) {
36
+ return std::nullopt;
37
+ }
38
+
39
+ const std::string &bytes = it->second;
40
+ if (offset >= bytes.size()) {
41
+ return std::string{};
42
+ }
43
+ return bytes.substr(offset, std::min(size, bytes.size() - offset));
44
+ }
45
+
46
+ std::optional<std::string> blobBytes(const std::string &blobId) {
47
+ Registry &state = registry();
48
+ const std::lock_guard<std::mutex> lock(state.mutex);
49
+ const auto it = state.blobs.find(blobId);
50
+ if (it == state.blobs.end()) {
51
+ return std::nullopt;
52
+ }
53
+ return it->second;
54
+ }
55
+
56
+ size_t blobSize(const std::string &blobId) {
57
+ Registry &state = registry();
58
+ const std::lock_guard<std::mutex> lock(state.mutex);
59
+ const auto it = state.blobs.find(blobId);
60
+ return it == state.blobs.end() ? 0 : it->second.size();
61
+ }
62
+
63
+ void releaseBlob(const std::string &blobId) {
64
+ Registry &state = registry();
65
+ const std::lock_guard<std::mutex> lock(state.mutex);
66
+ state.blobs.erase(blobId);
67
+ }
68
+
69
+ size_t blobCount() {
70
+ Registry &state = registry();
71
+ const std::lock_guard<std::mutex> lock(state.mutex);
72
+ return state.blobs.size();
73
+ }
74
+
75
+ } // namespace basalt
@@ -0,0 +1,44 @@
1
+ // The bytes behind a Blob.
2
+ //
3
+ // React Native's `Blob` is a handle, not data: JavaScript holds
4
+ // `{blobId, offset, size}` and the bytes live natively, so that a 20MB file
5
+ // picked by a user is never copied into the JavaScript heap. This is where they
6
+ // live.
7
+ //
8
+ // Entirely portable -- a blob is a byte array with a name, on every platform --
9
+ // which is why it is here and not behind a seam. The only part of blobs that
10
+ // touches an operating system is sending one as an HTTP body, and that is in
11
+ // HttpClient.cpp, which is also shared.
12
+
13
+ #pragma once
14
+
15
+ #include <cstddef>
16
+ #include <optional>
17
+ #include <string>
18
+
19
+ namespace basalt {
20
+
21
+ // Stores `bytes` under `blobId`, replacing anything already there.
22
+ void storeBlob(const std::string &blobId, std::string bytes);
23
+
24
+ // A slice of a stored blob, or nullopt if the id is unknown.
25
+ //
26
+ // The range is clamped rather than rejected: JavaScript computes offsets from
27
+ // sizes it was told, and a rounding disagreement should produce a short read
28
+ // rather than an exception three layers from the cause.
29
+ std::optional<std::string> blobSlice(const std::string &blobId, size_t offset, size_t size);
30
+
31
+ // The whole of a stored blob, or nullopt if the id is unknown.
32
+ std::optional<std::string> blobBytes(const std::string &blobId);
33
+
34
+ size_t blobSize(const std::string &blobId);
35
+
36
+ // `BlobModule.release`. JavaScript owns the lifetime: a Blob that goes out of
37
+ // scope there has to say so, because nothing here can tell.
38
+ void releaseBlob(const std::string &blobId);
39
+
40
+ // How many blobs are held. For tests and for a future leak check -- nothing
41
+ // evicts on its own, by design.
42
+ size_t blobCount();
43
+
44
+ } // namespace basalt