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,145 @@
|
|
|
1
|
+
// react-native-gesture-handler's recognisers, on the desktop.
|
|
2
|
+
//
|
|
3
|
+
// RNGH is not an Expo module and has no portable C++ to compile: it ships
|
|
4
|
+
// Objective-C for Apple, Kotlin for Android, and a TypeScript engine for the
|
|
5
|
+
// web. What is portable is its *contract*, which is small and well defined --
|
|
6
|
+
// eight module methods, a six-value state machine, and two device events -- and
|
|
7
|
+
// that contract is what this file implements.
|
|
8
|
+
//
|
|
9
|
+
// The division is the same one the rest of this project uses. Everything here
|
|
10
|
+
// is platform-independent: a handler is a state machine fed with a pointer
|
|
11
|
+
// position and a list of the views under it, and what it produces is a payload
|
|
12
|
+
// to hand to JavaScript. The toolkits contribute two things and no more --
|
|
13
|
+
// where the pointer is, and which views are under it -- which is exactly what
|
|
14
|
+
// each one's touch dispatcher already works out for React Native's own touches.
|
|
15
|
+
//
|
|
16
|
+
// ## What a handler is
|
|
17
|
+
//
|
|
18
|
+
// `createGestureHandler('PanGestureHandler', 7, config)` makes one.
|
|
19
|
+
// `attachGestureHandler(7, viewTag)` says which view it watches. From then on
|
|
20
|
+
// every pointer event that lands on that view, or anything inside it, is
|
|
21
|
+
// offered to the handler, and the handler decides whether this is the gesture
|
|
22
|
+
// it is looking for.
|
|
23
|
+
//
|
|
24
|
+
// The states are RNGH's, and the order matters to its JavaScript:
|
|
25
|
+
// UNDETERMINED -> BEGAN when a pointer lands, BEGAN -> ACTIVE when the
|
|
26
|
+
// recogniser is satisfied, ACTIVE -> END when the pointer lifts. FAILED and
|
|
27
|
+
// CANCELLED are the two ways out.
|
|
28
|
+
//
|
|
29
|
+
// ## What is recognised, and what is not
|
|
30
|
+
//
|
|
31
|
+
// Tap, long press, pan, fling, native and manual. Pinch, rotation and force
|
|
32
|
+
// touch are created and never activate, because they need a second finger or a
|
|
33
|
+
// pressure reading that a mouse does not have; saying so here is better than a
|
|
34
|
+
// handler that looks attached and quietly never fires.
|
|
35
|
+
//
|
|
36
|
+
// One pointer. A desktop has one cursor, so `numberOfPointers` is always 1 and
|
|
37
|
+
// a gesture configured to need two never activates.
|
|
38
|
+
//
|
|
39
|
+
// ## Conflicts
|
|
40
|
+
//
|
|
41
|
+
// When a handler activates it cancels every other handler tracking the same
|
|
42
|
+
// pointer, unless the two were declared simultaneous. `waitFor` holds a handler
|
|
43
|
+
// at BEGAN until the handler it waits for has failed. That is the useful half
|
|
44
|
+
// of RNGH's conflict resolution; what is missing is listed in
|
|
45
|
+
// docs/backlog/expo.md, which is the file that changes when it stops being
|
|
46
|
+
|
|
47
|
+
#pragma once
|
|
48
|
+
|
|
49
|
+
#include <folly/dynamic.h>
|
|
50
|
+
|
|
51
|
+
#include <functional>
|
|
52
|
+
#include <string>
|
|
53
|
+
#include <vector>
|
|
54
|
+
|
|
55
|
+
namespace basalt {
|
|
56
|
+
|
|
57
|
+
// RNGH's State enum, from its State.ts. The numbers are part of the contract:
|
|
58
|
+
// they cross into JavaScript as-is.
|
|
59
|
+
enum class GestureState {
|
|
60
|
+
Undetermined = 0,
|
|
61
|
+
Failed = 1,
|
|
62
|
+
Began = 2,
|
|
63
|
+
Cancelled = 3,
|
|
64
|
+
Active = 4,
|
|
65
|
+
End = 5,
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// One view under the pointer, with its origin in the surface root's
|
|
69
|
+
// coordinates. The origin is what turns a root-relative pointer position into
|
|
70
|
+
// the view-relative `x`/`y` a gesture payload carries, without this file
|
|
71
|
+
// knowing anything about either toolkit's geometry.
|
|
72
|
+
struct HitView {
|
|
73
|
+
int tag{0};
|
|
74
|
+
double originX{0};
|
|
75
|
+
double originY{0};
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
class GestureRegistry {
|
|
79
|
+
public:
|
|
80
|
+
// How a payload reaches JavaScript. RNGH listens on DeviceEventEmitter for
|
|
81
|
+
// `onGestureHandlerEvent` and `onGestureHandlerStateChange`, so this is set
|
|
82
|
+
// by the TurboModule, which is the thing that can emit one.
|
|
83
|
+
using Emitter = std::function<void(const std::string &eventName, folly::dynamic payload)>;
|
|
84
|
+
void setEmitter(Emitter emitter);
|
|
85
|
+
|
|
86
|
+
// The module's methods.
|
|
87
|
+
void create(const std::string &handlerName, int handlerTag, folly::dynamic config);
|
|
88
|
+
void update(int handlerTag, folly::dynamic config);
|
|
89
|
+
void attach(int handlerTag, int viewTag);
|
|
90
|
+
void drop(int handlerTag);
|
|
91
|
+
|
|
92
|
+
// Input, from a platform's touch dispatcher. `chain` is the views under the
|
|
93
|
+
// point, innermost first, which is the order handlers are offered the gesture
|
|
94
|
+
// in. Positions are in the surface root's coordinates.
|
|
95
|
+
void pointerDown(const std::vector<HitView> &chain, double x, double y, double timestampMs);
|
|
96
|
+
void pointerMove(double x, double y, double timestampMs);
|
|
97
|
+
void pointerUp(double x, double y, double timestampMs);
|
|
98
|
+
void pointerCancel();
|
|
99
|
+
|
|
100
|
+
// Forces a handler into a state, which is what RNGH's `GestureStateManager`
|
|
101
|
+
// does from a worklet: `manager.activate()` inside `onTouchesMove` is a
|
|
102
|
+
// gesture claiming itself rather than waiting for a recogniser to decide.
|
|
103
|
+
// Reanimated is the only caller -- the manager needs a worklet runtime to run
|
|
104
|
+
// in -- so this does nothing useful until phase 38.
|
|
105
|
+
void setStateFromWorklet(int handlerTag, int state);
|
|
106
|
+
|
|
107
|
+
// True once a handler has activated during the current gesture. A touch
|
|
108
|
+
// dispatcher reads this to cancel React Native's own touch, which is what
|
|
109
|
+
// `setJSResponder` does on the platforms RNGH was written for: without it a
|
|
110
|
+
// pan over a <Pressable> both pans and presses.
|
|
111
|
+
bool hasActiveHandler() const;
|
|
112
|
+
|
|
113
|
+
// Whether anything is attached at all. Checked before the work of building a
|
|
114
|
+
// hit chain, because an app that does not use RNGH must not pay for it.
|
|
115
|
+
bool empty() const;
|
|
116
|
+
|
|
117
|
+
private:
|
|
118
|
+
struct Handler;
|
|
119
|
+
|
|
120
|
+
void beginTracking(Handler &handler, const HitView &view, double x, double y, double timestampMs);
|
|
121
|
+
void setState(Handler &handler, GestureState next, double x, double y, double timestampMs);
|
|
122
|
+
bool tryActivate(Handler &handler, double x, double y, double timestampMs);
|
|
123
|
+
void cancelOthers(const Handler &activated, double x, double y, double timestampMs);
|
|
124
|
+
void emitUpdate(const Handler &handler, double x, double y, double timestampMs);
|
|
125
|
+
folly::dynamic payloadFor(const Handler &handler, double x, double y) const;
|
|
126
|
+
Handler *find(int handlerTag);
|
|
127
|
+
|
|
128
|
+
std::vector<Handler> handlers_;
|
|
129
|
+
Emitter emit_;
|
|
130
|
+
|
|
131
|
+
// The gesture in progress. Empty between gestures.
|
|
132
|
+
std::vector<int> tracking_;
|
|
133
|
+
bool pointerIsDown_{false};
|
|
134
|
+
int generation_{0};
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
GestureRegistry &gestures();
|
|
138
|
+
|
|
139
|
+
// A monotonic clock in milliseconds, shared so that both platforms' input
|
|
140
|
+
// timestamps mean the same thing -- durations and velocities are compared
|
|
141
|
+
// against constants from RNGH's own recognisers, which are wall-clock
|
|
142
|
+
// milliseconds.
|
|
143
|
+
double monotonicMilliseconds();
|
|
144
|
+
|
|
145
|
+
} // namespace basalt
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#include "HoverTracker.h"
|
|
2
|
+
|
|
3
|
+
namespace basalt {
|
|
4
|
+
|
|
5
|
+
using facebook::react::ViewEvents;
|
|
6
|
+
|
|
7
|
+
std::uint16_t hoverListenersFrom(const ViewEvents &events) {
|
|
8
|
+
using Offset = ViewEvents::Offset;
|
|
9
|
+
std::uint16_t mask = HoverListenerNone;
|
|
10
|
+
if (events[Offset::PointerEnter]) {
|
|
11
|
+
mask |= HoverListenerEnter;
|
|
12
|
+
}
|
|
13
|
+
if (events[Offset::PointerLeave]) {
|
|
14
|
+
mask |= HoverListenerLeave;
|
|
15
|
+
}
|
|
16
|
+
if (events[Offset::PointerMove]) {
|
|
17
|
+
mask |= HoverListenerMove;
|
|
18
|
+
}
|
|
19
|
+
if (events[Offset::PointerOver]) {
|
|
20
|
+
mask |= HoverListenerOver;
|
|
21
|
+
}
|
|
22
|
+
if (events[Offset::PointerOut]) {
|
|
23
|
+
mask |= HoverListenerOut;
|
|
24
|
+
}
|
|
25
|
+
if (events[Offset::PointerEnterCapture]) {
|
|
26
|
+
mask |= HoverListenerEnterCapture;
|
|
27
|
+
}
|
|
28
|
+
if (events[Offset::PointerLeaveCapture]) {
|
|
29
|
+
mask |= HoverListenerLeaveCapture;
|
|
30
|
+
}
|
|
31
|
+
if (events[Offset::PointerMoveCapture]) {
|
|
32
|
+
mask |= HoverListenerMoveCapture;
|
|
33
|
+
}
|
|
34
|
+
if (events[Offset::PointerOverCapture]) {
|
|
35
|
+
mask |= HoverListenerOverCapture;
|
|
36
|
+
}
|
|
37
|
+
if (events[Offset::PointerOutCapture]) {
|
|
38
|
+
mask |= HoverListenerOutCapture;
|
|
39
|
+
}
|
|
40
|
+
return mask;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
bool HoverTracker::admitMove(int tag, std::uint16_t listenersOnPath) {
|
|
44
|
+
if (listenersOnPath != HoverListenerNone) {
|
|
45
|
+
emittedAt_ = tag;
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
// Nothing here listens. If something did a moment ago, one more move has to
|
|
49
|
+
// go through: it is what tells the processor the cursor left, and it is
|
|
50
|
+
// dispatched at the view the cursor is on now, which is what makes the path
|
|
51
|
+
// diverge from the one it is holding.
|
|
52
|
+
if (emittedAt_ != 0) {
|
|
53
|
+
emittedAt_ = 0;
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
int HoverTracker::admitLeave() {
|
|
60
|
+
const int tag = emittedAt_;
|
|
61
|
+
emittedAt_ = 0;
|
|
62
|
+
return tag;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
void HoverTracker::forget() {
|
|
66
|
+
emittedAt_ = 0;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
} // namespace basalt
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// Hover, which is the one piece of input a desktop has and a phone does not.
|
|
2
|
+
//
|
|
3
|
+
// React Native's touch events -- what Pressability and every `onPress` run on
|
|
4
|
+
// -- have no concept of a pointer that is present but not pressed. W3C pointer
|
|
5
|
+
// events do, and React Native carries them alongside touches: `onPointerEnter`,
|
|
6
|
+
// `onPointerLeave`, `onPointerOver`, `onPointerOut` and `onPointerMove` are
|
|
7
|
+
// real props, registered in the view config and parsed into `ViewProps::events`
|
|
8
|
+
// by ReactCommon. Nothing on this platform was emitting them.
|
|
9
|
+
//
|
|
10
|
+
// ## What a platform owes, and what it must not do
|
|
11
|
+
//
|
|
12
|
+
// Much less than it looks. `PointerEventsProcessor` in ReactCommon already
|
|
13
|
+
// implements the whole hover model: it remembers the path the cursor was on,
|
|
14
|
+
// diffs it against the new one, and emits `out`, `leave`, `over` and `enter` in
|
|
15
|
+
// the right order with the right listener filtering, all from a single
|
|
16
|
+
// `pointerMove`. It also filters the move itself by whether anything on the
|
|
17
|
+
// path is listening. So a host emits *moves* and nothing else, and every other
|
|
18
|
+
// pointer event an app sees is React Native's own work.
|
|
19
|
+
//
|
|
20
|
+
// This is worth stating because emitting enter and leave as well looks right
|
|
21
|
+
// and is wrong twice over: the processor runs its hover tracking on every
|
|
22
|
+
// pointer event that reaches it, so each extra one produces a second complete
|
|
23
|
+
// round of enter/leave, and the platform's own is then forwarded on top. One
|
|
24
|
+
// `over` from here came out in JavaScript as `over`, `enter` on the ancestor
|
|
25
|
+
// and `enter` on the target -- correct events, three times too many.
|
|
26
|
+
//
|
|
27
|
+
// The one exception is the cursor leaving the surface, which no move can
|
|
28
|
+
// express: a `pointerLeave` from a platform is read by the processor as "the
|
|
29
|
+
// pointer is gone", and it unwinds the whole path itself rather than forwarding
|
|
30
|
+
// what it was sent.
|
|
31
|
+
//
|
|
32
|
+
// ## So what is left here
|
|
33
|
+
//
|
|
34
|
+
// A gate. A cursor crossing a window produces motion at the refresh rate, and
|
|
35
|
+
// turning each one into an event that crosses into JavaScript, for an app that
|
|
36
|
+
// listens to none of them, is a cost every app would pay. React Native on
|
|
37
|
+
// Android has the same problem and answers it the same way -- ask whether
|
|
38
|
+
// anyone is listening before dispatching -- and the answer is in
|
|
39
|
+
// `ViewProps::events`, which the mounting walk already sees on every Create and
|
|
40
|
+
// Update.
|
|
41
|
+
//
|
|
42
|
+
// The gate has one subtlety, and it is the reason this is a small state machine
|
|
43
|
+
// rather than an `if`. A move must also be dispatched on the *first* motion
|
|
44
|
+
// after the cursor leaves everything that was listening: without it the
|
|
45
|
+
// processor never learns the pointer left, and the last hovered view keeps its
|
|
46
|
+
// hover state for good.
|
|
47
|
+
|
|
48
|
+
#pragma once
|
|
49
|
+
|
|
50
|
+
#include <react/renderer/components/view/primitives.h>
|
|
51
|
+
|
|
52
|
+
#include <cstdint>
|
|
53
|
+
|
|
54
|
+
namespace basalt {
|
|
55
|
+
|
|
56
|
+
// Which hover events a view listens for. A subset of React Native's
|
|
57
|
+
// `ViewEvents`, narrowed to the bits this file acts on so that a host can hold
|
|
58
|
+
// one small integer per view instead of a 64-bit set it would have to
|
|
59
|
+
// understand.
|
|
60
|
+
enum HoverListener : std::uint16_t {
|
|
61
|
+
HoverListenerEnter = 1U << 0U,
|
|
62
|
+
HoverListenerLeave = 1U << 1U,
|
|
63
|
+
HoverListenerMove = 1U << 2U,
|
|
64
|
+
HoverListenerOver = 1U << 3U,
|
|
65
|
+
HoverListenerOut = 1U << 4U,
|
|
66
|
+
// The capture phase of each. A listener in the capture phase is on an
|
|
67
|
+
// *ancestor* of the view the event is dispatched to, so it counts towards
|
|
68
|
+
// whether anything on the path is listening.
|
|
69
|
+
HoverListenerEnterCapture = 1U << 5U,
|
|
70
|
+
HoverListenerLeaveCapture = 1U << 6U,
|
|
71
|
+
HoverListenerMoveCapture = 1U << 7U,
|
|
72
|
+
HoverListenerOverCapture = 1U << 8U,
|
|
73
|
+
HoverListenerOutCapture = 1U << 9U,
|
|
74
|
+
|
|
75
|
+
// Nothing here is listened for, which is the common case and the one worth
|
|
76
|
+
// being able to test for in a single comparison.
|
|
77
|
+
HoverListenerNone = 0U,
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
// The mask for a view, from the props ReactCommon parsed. `ViewEvents` is a
|
|
81
|
+
// 64-bit set with a documented bit per prop; this keeps the translation in one
|
|
82
|
+
// place so no host has to know the offsets.
|
|
83
|
+
std::uint16_t hoverListenersFrom(const facebook::react::ViewEvents &events);
|
|
84
|
+
|
|
85
|
+
class HoverTracker {
|
|
86
|
+
public:
|
|
87
|
+
// The cursor is over `tag`, and `listenersOnPath` is the masks of every view
|
|
88
|
+
// from it to the surface root, ORed together. Returns whether a
|
|
89
|
+
// `pointerMove` should be dispatched at `tag`.
|
|
90
|
+
bool admitMove(int tag, std::uint16_t listenersOnPath);
|
|
91
|
+
|
|
92
|
+
// The cursor left the surface. Returns the tag to dispatch a `pointerLeave`
|
|
93
|
+
// at, or 0 for nothing -- which is the case when no move was ever dispatched,
|
|
94
|
+
// because the processor has no path to unwind.
|
|
95
|
+
int admitLeave();
|
|
96
|
+
|
|
97
|
+
// Forget where the cursor was without emitting anything, for a surface
|
|
98
|
+
// teardown: the views in the remembered path are about to stop existing.
|
|
99
|
+
void forget();
|
|
100
|
+
|
|
101
|
+
private:
|
|
102
|
+
// The view the last dispatched move was reported against, or 0 if the last
|
|
103
|
+
// motion was not dispatched. Not simply "the last view under the cursor":
|
|
104
|
+
// what matters is what React Native was told, because that is what it is
|
|
105
|
+
// holding a hover path for.
|
|
106
|
+
int emittedAt_{0};
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
} // namespace basalt
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
// The http seam for the Linux platform, backed by libcurl.
|
|
2
|
+
//
|
|
3
|
+
// `getHttpClientFactory()` is declared by ReactCxxPlatform and defined nowhere
|
|
4
|
+
// in it, exactly like `getDefaultComponentRegistryFactory()`: every host writes
|
|
5
|
+
// its own. `ReactHost` refuses to construct without one, so this file is not
|
|
6
|
+
// optional even for a host that never makes a request.
|
|
7
|
+
//
|
|
8
|
+
// Its counterpart, `getWebSocketClientFactory()`, is *not* here. React Native
|
|
9
|
+
// already ships a working boost::beast client at
|
|
10
|
+
// ReactCxxPlatform/react/http/platform/cxx/WebSocketClient.cpp that its own
|
|
11
|
+
// CMakeLists never compiles; this project builds it as `rn_websocket` rather
|
|
12
|
+
// than writing a second one. See cmake/ReactNativeCore.cmake.
|
|
13
|
+
//
|
|
14
|
+
// Two callers matter:
|
|
15
|
+
//
|
|
16
|
+
// - `DevServerHelper::downloadBundleResourceSync` fetches the Metro bundle
|
|
17
|
+
// and blocks on a std::future that only `onBody` completes. A client that
|
|
18
|
+
// drops its callbacks does not fail there, it hangs, so every exit path
|
|
19
|
+
// below has to end in `onResponseComplete`.
|
|
20
|
+
// - `NetworkingModule` backs fetch/XHR in JS.
|
|
21
|
+
//
|
|
22
|
+
// Threading: one thread per request. Callbacks run on that thread, never on the
|
|
23
|
+
// GTK main thread or the JS thread. Both callers above expect that -- the dev
|
|
24
|
+
// server hands the result to a promise, and NetworkingModule bounces through
|
|
25
|
+
// its CallInvoker.
|
|
26
|
+
|
|
27
|
+
#include "BlobRegistry.h"
|
|
28
|
+
#include "DevBundle.h"
|
|
29
|
+
|
|
30
|
+
#include <react/http/IHttpClient.h>
|
|
31
|
+
|
|
32
|
+
#include <curl/curl.h>
|
|
33
|
+
#include <folly/io/IOBuf.h>
|
|
34
|
+
#include <glog/logging.h>
|
|
35
|
+
|
|
36
|
+
#include <algorithm>
|
|
37
|
+
#include <atomic>
|
|
38
|
+
// For the fixed-width types in IHttpClient's signatures. Reached through
|
|
39
|
+
// DevBundle.h today, which is not a dependency worth relying on.
|
|
40
|
+
#include <cstdint>
|
|
41
|
+
#include <memory>
|
|
42
|
+
#include <mutex>
|
|
43
|
+
#include <string>
|
|
44
|
+
#include <thread>
|
|
45
|
+
#include <utility>
|
|
46
|
+
#include <vector>
|
|
47
|
+
|
|
48
|
+
namespace facebook::react {
|
|
49
|
+
|
|
50
|
+
namespace {
|
|
51
|
+
|
|
52
|
+
void ensureCurlInitialised() {
|
|
53
|
+
static std::once_flag once;
|
|
54
|
+
std::call_once(once, []() { curl_global_init(CURL_GLOBAL_DEFAULT); });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Shared between the caller and the request thread. `cancelled` is the only
|
|
58
|
+
// thing the caller touches after handing the request off.
|
|
59
|
+
struct RequestState {
|
|
60
|
+
std::atomic<bool> cancelled{false};
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
class CurlRequestToken final : public http::IRequestToken {
|
|
64
|
+
public:
|
|
65
|
+
explicit CurlRequestToken(std::shared_ptr<RequestState> state) : state_(std::move(state)) {}
|
|
66
|
+
|
|
67
|
+
void cancel() noexcept override {
|
|
68
|
+
state_->cancelled = true;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private:
|
|
72
|
+
std::shared_ptr<RequestState> state_;
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
size_t appendToString(char *data, size_t size, size_t count, void *userData) {
|
|
76
|
+
auto *out = static_cast<std::string *>(userData);
|
|
77
|
+
out->append(data, size * count);
|
|
78
|
+
return size * count;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
size_t collectHeader(char *data, size_t size, size_t count, void *userData) {
|
|
82
|
+
auto *headers = static_cast<http::Headers *>(userData);
|
|
83
|
+
const std::string line(data, size * count);
|
|
84
|
+
|
|
85
|
+
// curl hands over the status line and the blank terminator too; neither is a
|
|
86
|
+
// header, and only lines with a colon are.
|
|
87
|
+
const auto colon = line.find(':');
|
|
88
|
+
if (colon != std::string::npos) {
|
|
89
|
+
std::string name = line.substr(0, colon);
|
|
90
|
+
std::string value = line.substr(colon + 1);
|
|
91
|
+
const auto notSpace = [](unsigned char c) { return !std::isspace(c); };
|
|
92
|
+
value.erase(value.begin(), std::find_if(value.begin(), value.end(), notSpace));
|
|
93
|
+
value.erase(std::find_if(value.rbegin(), value.rend(), notSpace).base(), value.end());
|
|
94
|
+
headers->emplace_back(std::move(name), std::move(value));
|
|
95
|
+
}
|
|
96
|
+
return size * count;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
int reportProgress(void *userData,
|
|
100
|
+
curl_off_t downloadTotal,
|
|
101
|
+
curl_off_t downloadNow,
|
|
102
|
+
curl_off_t uploadTotal,
|
|
103
|
+
curl_off_t uploadNow) {
|
|
104
|
+
auto *state = static_cast<RequestState *>(userData);
|
|
105
|
+
(void)downloadTotal;
|
|
106
|
+
(void)downloadNow;
|
|
107
|
+
(void)uploadTotal;
|
|
108
|
+
(void)uploadNow;
|
|
109
|
+
// A non-zero return aborts the transfer, which surfaces as CURLE_ABORTED_BY_CALLBACK.
|
|
110
|
+
return state->cancelled ? 1 : 0;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Runs on the request thread. Always ends in onResponseComplete.
|
|
114
|
+
void performRequest(http::NetworkCallbacks callbacks,
|
|
115
|
+
std::string method,
|
|
116
|
+
std::string url,
|
|
117
|
+
http::Headers requestHeaders,
|
|
118
|
+
std::string requestBody,
|
|
119
|
+
bool hasBody,
|
|
120
|
+
uint32_t timeoutSeconds,
|
|
121
|
+
std::shared_ptr<RequestState> state) {
|
|
122
|
+
CURL *handle = curl_easy_init();
|
|
123
|
+
if (handle == nullptr) {
|
|
124
|
+
if (callbacks.onResponseComplete) {
|
|
125
|
+
callbacks.onResponseComplete("could not initialise libcurl", false);
|
|
126
|
+
}
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
std::string responseBody;
|
|
131
|
+
http::Headers responseHeaders;
|
|
132
|
+
|
|
133
|
+
curl_easy_setopt(handle, CURLOPT_URL, url.c_str());
|
|
134
|
+
curl_easy_setopt(handle, CURLOPT_CUSTOMREQUEST, method.c_str());
|
|
135
|
+
curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1L);
|
|
136
|
+
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, appendToString);
|
|
137
|
+
curl_easy_setopt(handle, CURLOPT_WRITEDATA, &responseBody);
|
|
138
|
+
curl_easy_setopt(handle, CURLOPT_HEADERFUNCTION, collectHeader);
|
|
139
|
+
curl_easy_setopt(handle, CURLOPT_HEADERDATA, &responseHeaders);
|
|
140
|
+
curl_easy_setopt(handle, CURLOPT_NOPROGRESS, 0L);
|
|
141
|
+
curl_easy_setopt(handle, CURLOPT_XFERINFOFUNCTION, reportProgress);
|
|
142
|
+
curl_easy_setopt(handle, CURLOPT_XFERINFODATA, state.get());
|
|
143
|
+
// Without this, libcurl installs a SIGALRM-based resolver timeout that is not
|
|
144
|
+
// safe to use off the main thread.
|
|
145
|
+
curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1L);
|
|
146
|
+
|
|
147
|
+
if (timeoutSeconds > 0) {
|
|
148
|
+
curl_easy_setopt(handle, CURLOPT_TIMEOUT, static_cast<long>(timeoutSeconds));
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (hasBody) {
|
|
152
|
+
curl_easy_setopt(handle, CURLOPT_POSTFIELDS, requestBody.c_str());
|
|
153
|
+
curl_easy_setopt(handle, CURLOPT_POSTFIELDSIZE, static_cast<long>(requestBody.size()));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
curl_slist *headerList = nullptr;
|
|
157
|
+
for (const auto &[name, value] : requestHeaders) {
|
|
158
|
+
headerList = curl_slist_append(headerList, (name + ": " + value).c_str());
|
|
159
|
+
}
|
|
160
|
+
if (headerList != nullptr) {
|
|
161
|
+
curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headerList);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const CURLcode result = curl_easy_perform(handle);
|
|
165
|
+
|
|
166
|
+
long statusCode = 0;
|
|
167
|
+
curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &statusCode);
|
|
168
|
+
|
|
169
|
+
// A failed bundle fetch is the one case where the body must not be delivered.
|
|
170
|
+
// `DevServerHelper` hands whatever arrives to the JS engine without reading
|
|
171
|
+
// the status, so Metro's 500 -- which is how it reports every error in your
|
|
172
|
+
// app -- gets compiled as JavaScript. Reporting it as an error instead means
|
|
173
|
+
// `ReactHost` never sees a script, and core/DevBundle.h keeps the message
|
|
174
|
+
// that was in the body. Only the bundle request: an app's own fetch() of a
|
|
175
|
+
// 404 or a 500 needs its body, which is what the status code is for.
|
|
176
|
+
const bool bundleFailed = result == CURLE_OK && (statusCode < 200 || statusCode >= 300) &&
|
|
177
|
+
basalt::isDevBundleRequest(method, url);
|
|
178
|
+
|
|
179
|
+
if (bundleFailed) {
|
|
180
|
+
basalt::recordDevBundleError(statusCode, url, responseBody);
|
|
181
|
+
if (callbacks.onResponseComplete) {
|
|
182
|
+
callbacks.onResponseComplete("Metro answered " + std::to_string(statusCode), false);
|
|
183
|
+
}
|
|
184
|
+
} else if (result == CURLE_OK) {
|
|
185
|
+
// The other half of the same question: a bundle that *did* come from Metro,
|
|
186
|
+
// so the host can say so rather than naming the file it would have used.
|
|
187
|
+
if (statusCode >= 200 && statusCode < 300 && basalt::isDevBundleRequest(method, url)) {
|
|
188
|
+
basalt::recordDevBundleFetched(url);
|
|
189
|
+
}
|
|
190
|
+
if (callbacks.onResponse) {
|
|
191
|
+
callbacks.onResponse(static_cast<uint16_t>(statusCode), responseHeaders);
|
|
192
|
+
}
|
|
193
|
+
if (callbacks.onBody) {
|
|
194
|
+
callbacks.onBody(folly::IOBuf::copyBuffer(responseBody));
|
|
195
|
+
}
|
|
196
|
+
if (callbacks.onResponseComplete) {
|
|
197
|
+
callbacks.onResponseComplete("", false);
|
|
198
|
+
}
|
|
199
|
+
} else {
|
|
200
|
+
const bool timedOut = result == CURLE_OPERATION_TIMEDOUT;
|
|
201
|
+
std::string error = curl_easy_strerror(result);
|
|
202
|
+
if (result == CURLE_ABORTED_BY_CALLBACK) {
|
|
203
|
+
error = "request cancelled";
|
|
204
|
+
}
|
|
205
|
+
if (callbacks.onResponseComplete) {
|
|
206
|
+
// A timeout reports through the dedicated flag, and DevServerHelper
|
|
207
|
+
// treats an empty error string with that flag set as "Timeout".
|
|
208
|
+
callbacks.onResponseComplete(timedOut ? "" : error, timedOut);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (headerList != nullptr) {
|
|
213
|
+
curl_slist_free_all(headerList);
|
|
214
|
+
}
|
|
215
|
+
curl_easy_cleanup(handle);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
class CurlHttpClient final : public IHttpClient {
|
|
219
|
+
public:
|
|
220
|
+
std::unique_ptr<http::IRequestToken> sendRequest(
|
|
221
|
+
http::NetworkCallbacks &&callbacks,
|
|
222
|
+
const std::string &method,
|
|
223
|
+
const std::string &url,
|
|
224
|
+
const http::Headers &headers = {},
|
|
225
|
+
const http::Body &body = {},
|
|
226
|
+
uint32_t timeout = 0,
|
|
227
|
+
std::optional<std::string> /*loggingId*/ = std::nullopt) override {
|
|
228
|
+
ensureCurlInitialised();
|
|
229
|
+
|
|
230
|
+
// A string body, or a blob's bytes looked up by id.
|
|
231
|
+
//
|
|
232
|
+
// `http::Body::blob` is typed `std::optional<std::string>` upstream, and
|
|
233
|
+
// JavaScript sends `{blobId, offset, size}` -- an object. So a Blob body
|
|
234
|
+
// cannot currently reach here at all: ReactCxxPlatform's bridging tries to
|
|
235
|
+
// read that object as a string before this code is ever called. Handling it
|
|
236
|
+
// anyway, as an id, because that is what the type says it is and because
|
|
237
|
+
// the day upstream fixes the type this becomes correct rather than needing
|
|
238
|
+
// to be written.
|
|
239
|
+
std::string requestBody = body.string.value_or(std::string{});
|
|
240
|
+
bool hasBody = body.string.has_value();
|
|
241
|
+
|
|
242
|
+
if (body.blob) {
|
|
243
|
+
if (auto bytes = basalt::blobBytes(*body.blob)) {
|
|
244
|
+
requestBody = std::move(*bytes);
|
|
245
|
+
hasBody = true;
|
|
246
|
+
} else {
|
|
247
|
+
LOG(WARNING) << "http: request body names an unknown blob '" << *body.blob << "' ("
|
|
248
|
+
<< method << " " << url << ")";
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Form-data and base64 bodies come from JS APIs nothing here can reach yet:
|
|
253
|
+
// there is no file picker and no camera.
|
|
254
|
+
if (body.formData || body.base64) {
|
|
255
|
+
LOG(WARNING) << "http: form-data and base64 request bodies are not supported (" << method
|
|
256
|
+
<< " " << url << ")";
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
auto state = std::make_shared<RequestState>();
|
|
260
|
+
|
|
261
|
+
std::thread(performRequest,
|
|
262
|
+
std::move(callbacks),
|
|
263
|
+
method,
|
|
264
|
+
url,
|
|
265
|
+
headers,
|
|
266
|
+
std::move(requestBody),
|
|
267
|
+
hasBody,
|
|
268
|
+
timeout,
|
|
269
|
+
state)
|
|
270
|
+
.detach();
|
|
271
|
+
|
|
272
|
+
return std::make_unique<CurlRequestToken>(state);
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
} // namespace
|
|
277
|
+
|
|
278
|
+
HttpClientFactory getHttpClientFactory() {
|
|
279
|
+
return []() { return std::make_unique<CurlHttpClient>(); };
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
} // namespace facebook::react
|