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.
- package/LICENSE +21 -0
- package/dist/cli/basalt.d.ts +3 -0
- package/dist/cli/basalt.d.ts.map +1 -0
- package/dist/cli/basalt.js +47 -0
- package/dist/cli/basalt.js.map +1 -0
- package/dist/cli/bundle-cli.d.ts +12 -0
- package/dist/cli/bundle-cli.d.ts.map +1 -0
- package/dist/cli/bundle-cli.js +124 -0
- package/dist/cli/bundle-cli.js.map +1 -0
- package/dist/cli/bundleWithAssets.d.ts +79 -0
- package/dist/cli/bundleWithAssets.d.ts.map +1 -0
- package/dist/cli/bundleWithAssets.js +198 -0
- package/dist/cli/bundleWithAssets.js.map +1 -0
- package/dist/cli/desktop.d.ts +339 -0
- package/dist/cli/desktop.d.ts.map +1 -0
- package/dist/cli/desktop.js +1029 -0
- package/dist/cli/desktop.js.map +1 -0
- package/dist/cli/doctor.d.ts +31 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +283 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/init.d.ts +77 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +442 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/metro.d.ts +49 -0
- package/dist/cli/metro.d.ts.map +1 -0
- package/dist/cli/metro.js +148 -0
- package/dist/cli/metro.js.map +1 -0
- package/dist/cli/packageApp.d.ts +102 -0
- package/dist/cli/packageApp.d.ts.map +1 -0
- package/dist/cli/packageApp.js +319 -0
- package/dist/cli/packageApp.js.map +1 -0
- package/dist/cli/steps.d.ts +34 -0
- package/dist/cli/steps.d.ts.map +1 -0
- package/dist/cli/steps.js +53 -0
- package/dist/cli/steps.js.map +1 -0
- package/dist/metro-config.d.ts +168 -0
- package/dist/metro-config.d.ts.map +1 -0
- package/dist/metro-config.js +671 -0
- package/dist/metro-config.js.map +1 -0
- package/dist/react-native.config.d.ts +7 -0
- package/dist/react-native.config.d.ts.map +1 -0
- package/dist/react-native.config.js +80 -0
- package/dist/react-native.config.js.map +1 -0
- package/dist/src/DragSource.d.ts +52 -0
- package/dist/src/DragSource.d.ts.map +1 -0
- package/dist/src/DragSource.js +97 -0
- package/dist/src/DragSource.js.map +1 -0
- package/dist/src/DropTarget.d.ts +58 -0
- package/dist/src/DropTarget.d.ts.map +1 -0
- package/dist/src/DropTarget.js +131 -0
- package/dist/src/DropTarget.js.map +1 -0
- package/dist/src/KeyHandler.d.ts +57 -0
- package/dist/src/KeyHandler.d.ts.map +1 -0
- package/dist/src/KeyHandler.js +48 -0
- package/dist/src/KeyHandler.js.map +1 -0
- package/dist/src/Menu.d.ts +72 -0
- package/dist/src/Menu.d.ts.map +1 -0
- package/dist/src/Menu.js +179 -0
- package/dist/src/Menu.js.map +1 -0
- package/dist/src/TitleBar.d.ts +75 -0
- package/dist/src/TitleBar.d.ts.map +1 -0
- package/dist/src/TitleBar.js +236 -0
- package/dist/src/TitleBar.js.map +1 -0
- package/dist/src/Window.d.ts +87 -0
- package/dist/src/Window.d.ts.map +1 -0
- package/dist/src/Window.js +248 -0
- package/dist/src/Window.js.map +1 -0
- package/dist/src/closeRequest.d.ts +55 -0
- package/dist/src/closeRequest.d.ts.map +1 -0
- package/dist/src/closeRequest.js +134 -0
- package/dist/src/closeRequest.js.map +1 -0
- package/dist/src/index.d.ts +45 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +92 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/overrides/Alert.d.ts +69 -0
- package/dist/src/overrides/Alert.d.ts.map +1 -0
- package/dist/src/overrides/Alert.js +112 -0
- package/dist/src/overrides/Alert.js.map +1 -0
- package/dist/src/overrides/BaseViewConfig.d.ts +28 -0
- package/dist/src/overrides/BaseViewConfig.d.ts.map +1 -0
- package/dist/src/overrides/BaseViewConfig.js +72 -0
- package/dist/src/overrides/BaseViewConfig.js.map +1 -0
- package/dist/src/overrides/Platform.linux.d.ts +10 -0
- package/dist/src/overrides/Platform.linux.d.ts.map +1 -0
- package/dist/src/overrides/Platform.linux.js +15 -0
- package/dist/src/overrides/Platform.linux.js.map +1 -0
- package/dist/src/overrides/Platform.macos.d.ts +10 -0
- package/dist/src/overrides/Platform.macos.d.ts.map +1 -0
- package/dist/src/overrides/Platform.macos.js +15 -0
- package/dist/src/overrides/Platform.macos.js.map +1 -0
- package/dist/src/overrides/Platform.windows.d.ts +10 -0
- package/dist/src/overrides/Platform.windows.d.ts.map +1 -0
- package/dist/src/overrides/Platform.windows.js +15 -0
- package/dist/src/overrides/Platform.windows.js.map +1 -0
- package/dist/src/overrides/ReactDevToolsSettingsManager.d.ts +24 -0
- package/dist/src/overrides/ReactDevToolsSettingsManager.d.ts.map +1 -0
- package/dist/src/overrides/ReactDevToolsSettingsManager.js +30 -0
- package/dist/src/overrides/ReactDevToolsSettingsManager.js.map +1 -0
- package/dist/src/overrides/RefreshControl.d.ts +52 -0
- package/dist/src/overrides/RefreshControl.d.ts.map +1 -0
- package/dist/src/overrides/RefreshControl.js +92 -0
- package/dist/src/overrides/RefreshControl.js.map +1 -0
- package/dist/src/overrides/ScrollView.d.ts +39 -0
- package/dist/src/overrides/ScrollView.d.ts.map +1 -0
- package/dist/src/overrides/ScrollView.js +59 -0
- package/dist/src/overrides/ScrollView.js.map +1 -0
- package/dist/src/overrides/Share.d.ts +96 -0
- package/dist/src/overrides/Share.d.ts.map +1 -0
- package/dist/src/overrides/Share.js +97 -0
- package/dist/src/overrides/Share.js.map +1 -0
- package/dist/src/overrides/TextInput.d.ts +81 -0
- package/dist/src/overrides/TextInput.d.ts.map +1 -0
- package/dist/src/overrides/TextInput.js +213 -0
- package/dist/src/overrides/TextInput.js.map +1 -0
- package/dist/src/overrides/View.d.ts +36 -0
- package/dist/src/overrides/View.d.ts.map +1 -0
- package/dist/src/overrides/View.js +99 -0
- package/dist/src/overrides/View.js.map +1 -0
- package/dist/src/overrides/createPlatform.d.ts +32 -0
- package/dist/src/overrides/createPlatform.d.ts.map +1 -0
- package/dist/src/overrides/createPlatform.js +84 -0
- package/dist/src/overrides/createPlatform.js.map +1 -0
- package/dist/src/overrides/setUpDeveloperTools.d.ts +28 -0
- package/dist/src/overrides/setUpDeveloperTools.d.ts.map +1 -0
- package/dist/src/overrides/setUpDeveloperTools.js +84 -0
- package/dist/src/overrides/setUpDeveloperTools.js.map +1 -0
- package/dist/src/overrides/setUpXHR.d.ts +46 -0
- package/dist/src/overrides/setUpXHR.d.ts.map +1 -0
- package/dist/src/overrides/setUpXHR.js +236 -0
- package/dist/src/overrides/setUpXHR.js.map +1 -0
- package/dist/src/quitRequest.d.ts +55 -0
- package/dist/src/quitRequest.d.ts.map +1 -0
- package/dist/src/quitRequest.js +121 -0
- package/dist/src/quitRequest.js.map +1 -0
- package/dist/src/resourcePath.d.ts +18 -0
- package/dist/src/resourcePath.d.ts.map +1 -0
- package/dist/src/resourcePath.js +31 -0
- package/dist/src/resourcePath.js.map +1 -0
- package/dist/src/responseBody.d.ts +76 -0
- package/dist/src/responseBody.d.ts.map +1 -0
- package/dist/src/responseBody.js +94 -0
- package/dist/src/responseBody.js.map +1 -0
- package/dist/src/responseHeaders.d.ts +40 -0
- package/dist/src/responseHeaders.d.ts.map +1 -0
- package/dist/src/responseHeaders.js +75 -0
- package/dist/src/responseHeaders.js.map +1 -0
- package/dist/src/titleBarState.d.ts +43 -0
- package/dist/src/titleBarState.d.ts.map +1 -0
- package/dist/src/titleBarState.js +77 -0
- package/dist/src/titleBarState.js.map +1 -0
- package/dist/src/useContextMenu.d.ts +113 -0
- package/dist/src/useContextMenu.d.ts.map +1 -0
- package/dist/src/useContextMenu.js +109 -0
- package/dist/src/useContextMenu.js.map +1 -0
- package/dist/src/useDialog.d.ts +98 -0
- package/dist/src/useDialog.d.ts.map +1 -0
- package/dist/src/useDialog.js +112 -0
- package/dist/src/useDialog.js.map +1 -0
- package/dist/src/useDisplays.d.ts +70 -0
- package/dist/src/useDisplays.d.ts.map +1 -0
- package/dist/src/useDisplays.js +118 -0
- package/dist/src/useDisplays.js.map +1 -0
- package/dist/src/useHandledKeys.d.ts +38 -0
- package/dist/src/useHandledKeys.d.ts.map +1 -0
- package/dist/src/useHandledKeys.js +70 -0
- package/dist/src/useHandledKeys.js.map +1 -0
- package/dist/src/useWindow.d.ts +140 -0
- package/dist/src/useWindow.d.ts.map +1 -0
- package/dist/src/useWindow.js +233 -0
- package/dist/src/useWindow.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/native/CMakeLists.txt +355 -0
- package/native/bootstrap.sh +613 -0
- package/native/cmake/CompilerFlags.cmake +49 -0
- package/native/cmake/Expo.cmake +125 -0
- package/native/cmake/ReactNativeCore.cmake +612 -0
- package/native/cmake/Reanimated.cmake +108 -0
- package/native/cmake/Skia.cmake +379 -0
- package/native/cmake/ThirdParty.cmake +290 -0
- package/native/cmake/Worklets.cmake +67 -0
- package/native/cmake/codegen/CMakeLists.txt +32 -0
- package/native/core/AppIdentity.cpp +85 -0
- package/native/core/AppIdentity.h +59 -0
- package/native/core/AppPaths.cpp +96 -0
- package/native/core/AppPaths.h +53 -0
- package/native/core/AppearanceModule.cpp +60 -0
- package/native/core/AppearanceModule.h +40 -0
- package/native/core/Backface.h +59 -0
- package/native/core/BlobModule.cpp +283 -0
- package/native/core/BlobModule.h +76 -0
- package/native/core/BlobRegistry.cpp +75 -0
- package/native/core/BlobRegistry.h +44 -0
- package/native/core/ColorScheme.cpp +105 -0
- package/native/core/ColorScheme.h +59 -0
- package/native/core/ComponentRegistry.h +30 -0
- package/native/core/ControlMetrics.h +37 -0
- package/native/core/CoreModules.cpp +592 -0
- package/native/core/CoreModules.h +308 -0
- package/native/core/DebuggingOverlay.cpp +106 -0
- package/native/core/DebuggingOverlay.h +65 -0
- package/native/core/DesktopControls.cpp +232 -0
- package/native/core/DesktopControls.h +170 -0
- package/native/core/DevBundle.cpp +108 -0
- package/native/core/DevBundle.h +69 -0
- package/native/core/DevMenu.cpp +99 -0
- package/native/core/DevMenu.h +75 -0
- package/native/core/DialogModule.cpp +150 -0
- package/native/core/DialogModule.h +51 -0
- package/native/core/DragAndDrop.cpp +45 -0
- package/native/core/DragAndDrop.h +225 -0
- package/native/core/ExpoImageComponent.cpp +79 -0
- package/native/core/ExpoImageComponent.h +80 -0
- package/native/core/ExpoModules.cpp +415 -0
- package/native/core/ExpoModules.h +115 -0
- package/native/core/ExpoRuntime.cpp +366 -0
- package/native/core/ExpoRuntime.h +42 -0
- package/native/core/FocusRing.h +36 -0
- package/native/core/FontRegistry.h +51 -0
- package/native/core/GestureHandlerModule.cpp +136 -0
- package/native/core/GestureHandlerModule.h +71 -0
- package/native/core/Gestures.cpp +533 -0
- package/native/core/Gestures.h +145 -0
- package/native/core/HoverTracker.cpp +69 -0
- package/native/core/HoverTracker.h +109 -0
- package/native/core/HttpClient.cpp +282 -0
- package/native/core/ImageBytes.cpp +205 -0
- package/native/core/ImageBytes.h +29 -0
- package/native/core/ImageCache.cpp +51 -0
- package/native/core/ImageCache.h +89 -0
- package/native/core/JsRuntimeAccess.cpp +70 -0
- package/native/core/JsRuntimeAccess.h +60 -0
- package/native/core/JsiPromise.h +51 -0
- package/native/core/KeyEvents.cpp +116 -0
- package/native/core/KeyEvents.h +144 -0
- package/native/core/LogBoxSurface.cpp +46 -0
- package/native/core/LogBoxSurface.h +68 -0
- package/native/core/MenuModel.h +117 -0
- package/native/core/MenuModule.cpp +209 -0
- package/native/core/MenuModule.h +66 -0
- package/native/core/MountingWalk.h +524 -0
- package/native/core/PackageModules.cpp.in +25 -0
- package/native/core/PackageModules.h +50 -0
- package/native/core/PlatformConstantsModule.cpp +63 -0
- package/native/core/PlatformConstantsModule.h +64 -0
- package/native/core/PlatformServices.h +213 -0
- package/native/core/PointerButtons.h +63 -0
- package/native/core/PullToRefresh.cpp +57 -0
- package/native/core/PullToRefresh.h +73 -0
- package/native/core/ReanimatedCompat.h +47 -0
- package/native/core/ReanimatedModule.cpp +203 -0
- package/native/core/ReanimatedModule.h +55 -0
- package/native/core/ScrollAnimation.cpp +59 -0
- package/native/core/ScrollAnimation.h +70 -0
- package/native/core/ScrollBounds.cpp +26 -0
- package/native/core/ScrollBounds.h +68 -0
- package/native/core/ScrollIndicator.cpp +50 -0
- package/native/core/ScrollIndicator.h +69 -0
- package/native/core/ScrollMomentum.cpp +71 -0
- package/native/core/ScrollMomentum.h +73 -0
- package/native/core/ScrollSnap.cpp +123 -0
- package/native/core/ScrollSnap.h +80 -0
- package/native/core/ShareFallback.cpp +101 -0
- package/native/core/ShareFallback.h +45 -0
- package/native/core/SkiaPictureViewComponent.cpp +11 -0
- package/native/core/SkiaPictureViewComponent.h +47 -0
- package/native/core/SourceCodeModule.cpp +55 -0
- package/native/core/SourceCodeModule.h +53 -0
- package/native/core/StatusBarModule.cpp +23 -0
- package/native/core/StatusBarModule.h +42 -0
- package/native/core/TestDialog.cpp +148 -0
- package/native/core/TestDialog.h +96 -0
- package/native/core/TestKeys.cpp +78 -0
- package/native/core/TestKeys.h +50 -0
- package/native/core/TestQuitFile.cpp +33 -0
- package/native/core/TestQuitFile.h +60 -0
- package/native/core/TestSettle.h +192 -0
- package/native/core/TitleBarRegions.h +31 -0
- package/native/core/TurboModuleProxy.cpp +51 -0
- package/native/core/TurboModuleProxy.h +46 -0
- package/native/core/UIManagerAccess.cpp +43 -0
- package/native/core/UIManagerAccess.h +43 -0
- package/native/core/WindowBoundsCache.cpp +319 -0
- package/native/core/WindowControl.h +235 -0
- package/native/core/WindowHost.h +158 -0
- package/native/core/WindowMethods.cpp +167 -0
- package/native/core/WindowMethods.h +84 -0
- package/native/core/WindowsModule.cpp +448 -0
- package/native/core/WindowsModule.h +133 -0
- package/native/core/WorkletsModule.cpp +199 -0
- package/native/core/WorkletsModule.h +57 -0
- package/native/core/portability_probe.cpp +210 -0
- package/native/tests/EventRecorder.h +124 -0
- package/native/tests/TestHarness.cpp +109 -0
- package/native/tests/TestHarness.h +89 -0
- package/native/tests/TreeDump.h +49 -0
- package/native/tests/test_app_identity.cpp +114 -0
- package/native/tests/test_app_paths.cpp +48 -0
- package/native/tests/test_appearance.cpp +107 -0
- package/native/tests/test_backface.cpp +79 -0
- package/native/tests/test_blobs.cpp +157 -0
- package/native/tests/test_controls.cpp +185 -0
- package/native/tests/test_devbundle.cpp +103 -0
- package/native/tests/test_drag_and_drop.cpp +143 -0
- package/native/tests/test_gestures.cpp +331 -0
- package/native/tests/test_hover.cpp +92 -0
- package/native/tests/test_image_cache.cpp +109 -0
- package/native/tests/test_js_runtime_access.cpp +137 -0
- package/native/tests/test_key_events.cpp +235 -0
- package/native/tests/test_momentum.cpp +138 -0
- package/native/tests/test_quit_file.cpp +104 -0
- package/native/tests/test_scroll_animation.cpp +120 -0
- package/native/tests/test_scroll_bounds.cpp +66 -0
- package/native/tests/test_scroll_indicator.cpp +149 -0
- package/native/tests/test_scroll_snap.cpp +112 -0
- package/native/tests/test_settle.cpp +115 -0
- package/native/tests/test_share.cpp +75 -0
- package/native/tests/test_sourcecode.cpp +62 -0
- package/native/tests/test_window_close.cpp +119 -0
- package/native/tests/test_window_limits.cpp +123 -0
- package/package.json +43 -0
- package/react-native.config.js +86 -0
- 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 ®istry() {
|
|
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
|