react-native-windows 0.69.0-preview.2 → 0.69.0-preview.5

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 (64) hide show
  1. package/Directory.Build.props +4 -0
  2. package/Folly/Folly.vcxproj +4 -5
  3. package/Libraries/Core/ReactNativeVersion.js +1 -1
  4. package/Libraries/Network/RCTNetworkingWinShared.js +7 -0
  5. package/Libraries/Utilities/codegenNativeComponent.js +5 -6
  6. package/Libraries/Utilities/useColorScheme.js +9 -15
  7. package/Microsoft.ReactNative/Base/CoreNativeModules.cpp +3 -1
  8. package/Microsoft.ReactNative/IReactContext.cpp +17 -0
  9. package/Microsoft.ReactNative/IReactContext.h +2 -0
  10. package/Microsoft.ReactNative/IReactContext.idl +27 -0
  11. package/Microsoft.ReactNative/JsiApi.cpp +9 -0
  12. package/Microsoft.ReactNative/JsiApi.h +1 -0
  13. package/Microsoft.ReactNative/JsiApi.idl +1 -0
  14. package/Microsoft.ReactNative.Cxx/JSI/JsiAbiApi.cpp +3 -5
  15. package/Microsoft.ReactNative.Cxx/JSI/JsiAbiApi.h +1 -1
  16. package/Microsoft.ReactNative.Cxx/JSI/NodeApiJsiRuntime.cpp +31 -9
  17. package/Microsoft.ReactNative.Cxx/JSI/NodeApiJsiRuntime.h +48 -0
  18. package/Microsoft.ReactNative.Cxx/Microsoft.ReactNative.Cxx.vcxitems +2 -2
  19. package/Microsoft.ReactNative.Cxx/Microsoft.ReactNative.Cxx.vcxitems.filters +1 -1
  20. package/Microsoft.ReactNative.Managed/packages.lock.json +57 -2
  21. package/PropertySheets/Generated/PackageVersion.g.props +1 -1
  22. package/PropertySheets/JSEngine.props +2 -2
  23. package/Scripts/Tfs/Layout-MSRN-Headers.ps1 +9 -5
  24. package/Shared/CreateModules.h +17 -2
  25. package/Shared/InspectorPackagerConnection.cpp +6 -7
  26. package/Shared/InspectorPackagerConnection.h +1 -1
  27. package/Shared/JSI/ChakraRuntime.cpp +5 -0
  28. package/Shared/JSI/ChakraRuntime.h +1 -0
  29. package/Shared/JSI/NapiJsiV8RuntimeHolder.cpp +72 -2
  30. package/Shared/JSI/NapiJsiV8RuntimeHolder.h +2 -0
  31. package/Shared/Modules/BlobModule.cpp +376 -0
  32. package/Shared/Modules/BlobModule.h +153 -0
  33. package/Shared/Modules/CxxModuleUtilities.cpp +19 -0
  34. package/Shared/Modules/CxxModuleUtilities.h +23 -0
  35. package/Shared/Modules/FileReaderModule.cpp +156 -0
  36. package/Shared/Modules/FileReaderModule.h +54 -0
  37. package/Shared/Modules/HttpModule.cpp +72 -69
  38. package/Shared/Modules/HttpModule.h +8 -1
  39. package/Shared/Modules/IBlobPersistor.h +30 -0
  40. package/Shared/Modules/IHttpModuleProxy.h +30 -0
  41. package/Shared/Modules/IRequestBodyHandler.h +52 -0
  42. package/Shared/Modules/IResponseHandler.h +27 -0
  43. package/Shared/Modules/IUriHandler.h +37 -0
  44. package/Shared/Modules/IWebSocketModuleContentHandler.h +26 -0
  45. package/Shared/Modules/IWebSocketModuleProxy.h +22 -0
  46. package/Shared/Modules/NetworkingModule.cpp +1 -1
  47. package/Shared/Modules/WebSocketModule.cpp +92 -22
  48. package/Shared/Modules/WebSocketModule.h +27 -1
  49. package/Shared/Networking/IHttpResource.h +50 -2
  50. package/Shared/Networking/WinRTHttpResource.cpp +169 -51
  51. package/Shared/Networking/WinRTHttpResource.h +27 -8
  52. package/Shared/Networking/WinRTTypes.h +5 -2
  53. package/Shared/OInstance.cpp +22 -5
  54. package/Shared/OInstance.h +8 -4
  55. package/Shared/RuntimeOptions.cpp +6 -3
  56. package/Shared/RuntimeOptions.h +14 -3
  57. package/Shared/Shared.vcxitems +13 -0
  58. package/Shared/Shared.vcxitems.filters +40 -1
  59. package/fmt/fmt.vcxproj +4 -5
  60. package/package.json +14 -12
  61. package/template/cs-app-WinAppSDK/proj/NuGet.Config +3 -1
  62. package/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/JSCRuntime.cpp +0 -1480
  63. package/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/decorator.h +0 -753
  64. package/ReactCommon/TEMP_UntilReactCommonUpdate/jsi/jsi/jsi.h +0 -1331
@@ -86,8 +86,8 @@ struct InspectorProtocol {
86
86
  for (const facebook::react::InspectorPage2 &page : pages) {
87
87
  folly::dynamic pageDyn = folly::dynamic::object;
88
88
  pageDyn["id"] = page.id;
89
- pageDyn["title"] = std::string(page.title->c_str());
90
- pageDyn["vm"] = std::string(page.vm->c_str());
89
+ pageDyn["title"] = std::string(page.title);
90
+ pageDyn["vm"] = std::string(page.vm);
91
91
 
92
92
  pageDyn["isLastBundleDownloadSuccess"] = bundleStatus.m_isLastDownloadSucess;
93
93
  pageDyn["bundleUpdateTimestamp"] = bundleStatus.m_updateTimestamp;
@@ -105,8 +105,8 @@ struct InspectorProtocol {
105
105
  const facebook::react::InspectorPage2 page = pages->getPage(p);
106
106
  folly::dynamic pageDyn = folly::dynamic::object;
107
107
  pageDyn["id"] = page.id;
108
- pageDyn["title"] = page.title->c_str();
109
- pageDyn["vm"] = page.vm->c_str();
108
+ pageDyn["title"] = page.title;
109
+ pageDyn["vm"] = page.vm;
110
110
 
111
111
  pageDyn["isLastBundleDownloadSuccess"] = bundleStatus.m_isLastDownloadSucess;
112
112
  pageDyn["bundleUpdateTimestamp"] = bundleStatus.m_updateTimestamp;
@@ -155,11 +155,10 @@ void RemoteConnection::onDisconnect() {
155
155
  RemoteConnection2::RemoteConnection2(int64_t pageId, const InspectorPackagerConnection &packagerConnection)
156
156
  : m_packagerConnection(packagerConnection), m_pageId(pageId) {}
157
157
 
158
- void RemoteConnection2::onMessage(std::unique_ptr<facebook::react::IHermesString> message) {
159
- std::string msg(message->c_str());
158
+ void RemoteConnection2::onMessage(std::string message) {
160
159
  folly::dynamic response = InspectorProtocol::constructResponseForPackager(
161
160
  InspectorProtocol::EventType::WrappedEvent,
162
- InspectorProtocol::constructVMResponsePayloadForPackager(m_pageId, std::move(msg)));
161
+ InspectorProtocol::constructVMResponsePayloadForPackager(m_pageId, std::move(message)));
163
162
  std::string responsestr = folly::toJson(response);
164
163
  m_packagerConnection.sendMessageToPackager(std::move(responsestr));
165
164
  }
@@ -61,7 +61,7 @@ class RemoteConnection final : public facebook::react::IRemoteConnection {
61
61
  class RemoteConnection2 final : public facebook::react::IRemoteConnection2 {
62
62
  public:
63
63
  RemoteConnection2(int64_t pageId, const InspectorPackagerConnection &packagerConnection);
64
- void onMessage(std::unique_ptr<facebook::react::IHermesString> message) override;
64
+ void onMessage(std::string message) override;
65
65
  void onDisconnect() override;
66
66
 
67
67
  private:
@@ -311,6 +311,11 @@ facebook::jsi::PropNameID ChakraRuntime::createPropNameIDFromString(const facebo
311
311
  return MakePointer<facebook::jsi::PropNameID>(propertyId);
312
312
  }
313
313
 
314
+ facebook::jsi::PropNameID ChakraRuntime::createPropNameIDFromSymbol(const facebook::jsi::Symbol &sym) {
315
+ const JsPropertyIdRef propSym = GetPropertyIdFromSymbol(GetJsRef(sym));
316
+ return MakePointer<facebook::jsi::PropNameID>(propSym);
317
+ }
318
+
314
319
  std::string ChakraRuntime::utf8(const facebook::jsi::PropNameID &id) {
315
320
  return Common::Unicode::Utf16ToUtf8(GetPropertyNameFromId(GetJsRef(id)));
316
321
  }
@@ -63,6 +63,7 @@ class ChakraRuntime : public facebook::jsi::Runtime, public ChakraApi, ChakraApi
63
63
  facebook::jsi::PropNameID createPropNameIDFromAscii(const char *str, size_t length) override;
64
64
  facebook::jsi::PropNameID createPropNameIDFromUtf8(const uint8_t *utf8, size_t length) override;
65
65
  facebook::jsi::PropNameID createPropNameIDFromString(const facebook::jsi::String &str) override;
66
+ facebook::jsi::PropNameID createPropNameIDFromSymbol(const facebook::jsi::Symbol &sym) override;
66
67
  std::string utf8(const facebook::jsi::PropNameID &id) override;
67
68
  bool compare(const facebook::jsi::PropNameID &lhs, const facebook::jsi::PropNameID &rhs) override;
68
69
 
@@ -77,17 +77,20 @@ NapiJsiV8RuntimeHolder::NapiJsiV8RuntimeHolder(
77
77
  m_preparedScriptStore{std::move(preparedScriptStore)} {}
78
78
 
79
79
  void NapiJsiV8RuntimeHolder::InitRuntime() noexcept {
80
- napi_env env{};
81
80
  napi_ext_env_settings settings{};
82
81
  settings.this_size = sizeof(settings);
83
- settings.flags.enable_gc_api = true;
84
82
  if (m_debuggerPort > 0)
85
83
  settings.inspector_port = m_debuggerPort;
86
84
 
87
85
  settings.flags.enable_inspector = m_useDirectDebugger;
88
86
  settings.flags.wait_for_debugger = m_debuggerBreakOnNextLine;
87
+ // TODO: args.debuggerRuntimeName = debuggerRuntimeName_;
89
88
  settings.foreground_scheduler = &NapiJsiV8RuntimeHolder::ScheduleTaskCallback;
90
89
 
90
+ napi_ext_script_cache scriptCache = InitScriptCache(std::move(m_preparedScriptStore));
91
+ settings.script_cache = &scriptCache;
92
+
93
+ napi_env env{};
91
94
  napi_ext_create_env(&settings, &env);
92
95
  // Associate environment to holder.
93
96
  napi_set_instance_data(env, this, nullptr /*finalize_cb*/, nullptr /*finalize_hint*/);
@@ -96,6 +99,73 @@ void NapiJsiV8RuntimeHolder::InitRuntime() noexcept {
96
99
  m_ownThreadId = std::this_thread::get_id();
97
100
  }
98
101
 
102
+ struct NodeApiJsiBuffer : facebook::jsi::Buffer {
103
+ static std::shared_ptr<const facebook::jsi::Buffer> CreateJsiBuffer(const napi_ext_buffer *buffer) {
104
+ if (buffer && buffer->data) {
105
+ return std::shared_ptr<const facebook::jsi::Buffer>(new NodeApiJsiBuffer(buffer));
106
+ } else {
107
+ return {};
108
+ }
109
+ }
110
+
111
+ NodeApiJsiBuffer(const napi_ext_buffer *buffer) noexcept : buffer_(*buffer) {}
112
+
113
+ ~NodeApiJsiBuffer() override {
114
+ if (buffer_.buffer_object.finalize_cb) {
115
+ buffer_.buffer_object.finalize_cb(nullptr, buffer_.buffer_object.data, buffer_.buffer_object.finalize_hint);
116
+ }
117
+ }
118
+
119
+ const uint8_t *data() const override {
120
+ return buffer_.data;
121
+ }
122
+
123
+ size_t size() const override {
124
+ return buffer_.byte_size;
125
+ }
126
+
127
+ private:
128
+ napi_ext_buffer buffer_;
129
+ };
130
+
131
+ napi_ext_script_cache NapiJsiV8RuntimeHolder::InitScriptCache(
132
+ unique_ptr<PreparedScriptStore> &&preparedScriptStore) noexcept {
133
+ napi_ext_script_cache scriptCache{};
134
+ scriptCache.cache_object = NativeObjectWrapper<unique_ptr<PreparedScriptStore>>::Wrap(std::move(preparedScriptStore));
135
+ scriptCache.load_cached_script = [](napi_env env,
136
+ napi_ext_script_cache *script_cache,
137
+ napi_ext_cached_script_metadata *script_metadata,
138
+ napi_ext_buffer *result) -> napi_status {
139
+ PreparedScriptStore *scriptStore = reinterpret_cast<PreparedScriptStore *>(script_cache->cache_object.data);
140
+ std::shared_ptr<const facebook::jsi::Buffer> buffer = scriptStore->tryGetPreparedScript(
141
+ ScriptSignature{script_metadata->source_url, script_metadata->source_hash},
142
+ JSRuntimeSignature{script_metadata->runtime_name, script_metadata->runtime_version},
143
+ script_metadata->tag);
144
+ if (buffer) {
145
+ result->buffer_object = NativeObjectWrapper<std::shared_ptr<const facebook::jsi::Buffer>>::Wrap(
146
+ std::shared_ptr<const facebook::jsi::Buffer>{buffer});
147
+ result->data = buffer->data();
148
+ result->byte_size = buffer->size();
149
+ } else {
150
+ *result = napi_ext_buffer{};
151
+ }
152
+ return napi_ok;
153
+ };
154
+ scriptCache.store_cached_script = [](napi_env env,
155
+ napi_ext_script_cache *script_cache,
156
+ napi_ext_cached_script_metadata *script_metadata,
157
+ const napi_ext_buffer *buffer) -> napi_status {
158
+ PreparedScriptStore *scriptStore = reinterpret_cast<PreparedScriptStore *>(script_cache->cache_object.data);
159
+ scriptStore->persistPreparedScript(
160
+ NodeApiJsiBuffer::CreateJsiBuffer(buffer),
161
+ ScriptSignature{script_metadata->source_url, script_metadata->source_hash},
162
+ JSRuntimeSignature{script_metadata->runtime_name, script_metadata->runtime_version},
163
+ script_metadata->tag);
164
+ return napi_ok;
165
+ };
166
+ return scriptCache;
167
+ }
168
+
99
169
  #pragma region Microsoft::JSI::RuntimeHolderLazyInit
100
170
 
101
171
  facebook::react::JSIEngineOverride NapiJsiV8RuntimeHolder::getRuntimeType() noexcept {
@@ -31,6 +31,8 @@ class NapiJsiV8RuntimeHolder : public Microsoft::JSI::RuntimeHolderLazyInit {
31
31
  void *finalizeHint);
32
32
 
33
33
  void InitRuntime() noexcept;
34
+ napi_ext_script_cache InitScriptCache(
35
+ std::unique_ptr<facebook::jsi::PreparedScriptStore> &&preparedScriptStore) noexcept;
34
36
 
35
37
  std::shared_ptr<facebook::jsi::Runtime> m_runtime;
36
38
  std::shared_ptr<facebook::react::MessageQueueThread> m_jsQueue;
@@ -0,0 +1,376 @@
1
+ // Copyright (c) Microsoft Corporation.
2
+ // Licensed under the MIT License.
3
+
4
+ #include "BlobModule.h"
5
+
6
+ #include <Modules/CxxModuleUtilities.h>
7
+ #include <Modules/IHttpModuleProxy.h>
8
+ #include <Modules/IWebSocketModuleProxy.h>
9
+ #include <ReactPropertyBag.h>
10
+ #include <unicode.h>
11
+
12
+ // React Native
13
+ #include <cxxreact/JsArgumentHelpers.h>
14
+
15
+ // Windows API
16
+ #include <winrt/Windows.Foundation.h>
17
+ #include <winrt/Windows.Security.Cryptography.h>
18
+
19
+ // Standard Library
20
+ #include <chrono>
21
+ #include <filesystem>
22
+ #include <fstream>
23
+
24
+ using namespace facebook::xplat;
25
+
26
+ using folly::dynamic;
27
+ using std::scoped_lock;
28
+ using std::shared_ptr;
29
+ using std::string;
30
+ using std::vector;
31
+ using std::weak_ptr;
32
+ using winrt::Microsoft::ReactNative::IReactPropertyBag;
33
+ using winrt::Microsoft::ReactNative::ReactNonAbiValue;
34
+ using winrt::Microsoft::ReactNative::ReactPropertyBag;
35
+ using winrt::Microsoft::ReactNative::ReactPropertyId;
36
+ using winrt::Windows::Foundation::GuidHelper;
37
+ using winrt::Windows::Foundation::IInspectable;
38
+ using winrt::Windows::Foundation::Uri;
39
+ using winrt::Windows::Security::Cryptography::CryptographicBuffer;
40
+
41
+ namespace fs = std::filesystem;
42
+
43
+ namespace {
44
+ constexpr char moduleName[] = "BlobModule";
45
+ constexpr char blobKey[] = "blob";
46
+ constexpr char blobIdKey[] = "blobId";
47
+ constexpr char offsetKey[] = "offset";
48
+ constexpr char sizeKey[] = "size";
49
+ constexpr char typeKey[] = "type";
50
+ constexpr char dataKey[] = "data";
51
+ } // namespace
52
+
53
+ namespace Microsoft::React {
54
+
55
+ #pragma region BlobModule
56
+
57
+ BlobModule::BlobModule(winrt::Windows::Foundation::IInspectable const &inspectableProperties) noexcept
58
+ : m_sharedState{std::make_shared<SharedState>()},
59
+ m_blobPersistor{std::make_shared<MemoryBlobPersistor>()},
60
+ m_contentHandler{std::make_shared<BlobWebSocketModuleContentHandler>(m_blobPersistor)},
61
+ m_requestBodyHandler{std::make_shared<BlobModuleRequestBodyHandler>(m_blobPersistor)},
62
+ m_responseHandler{std::make_shared<BlobModuleResponseHandler>(m_blobPersistor)},
63
+ m_inspectableProperties{inspectableProperties} {
64
+ auto propBag = ReactPropertyBag{m_inspectableProperties.try_as<IReactPropertyBag>()};
65
+
66
+ auto contentHandlerPropId =
67
+ ReactPropertyId<ReactNonAbiValue<weak_ptr<IWebSocketModuleContentHandler>>>{L"BlobModule.ContentHandler"};
68
+ auto contentHandler = weak_ptr<IWebSocketModuleContentHandler>{m_contentHandler};
69
+ propBag.Set(contentHandlerPropId, std::move(contentHandler));
70
+
71
+ auto blobPersistorPropId = ReactPropertyId<ReactNonAbiValue<weak_ptr<IBlobPersistor>>>{L"Blob.Persistor"};
72
+ auto blobPersistor = weak_ptr<IBlobPersistor>{m_blobPersistor};
73
+ propBag.Set(blobPersistorPropId, std::move(blobPersistor));
74
+
75
+ m_sharedState->Module = this;
76
+ }
77
+
78
+ BlobModule::~BlobModule() noexcept /*override*/ {
79
+ m_sharedState->Module = nullptr;
80
+ }
81
+
82
+ #pragma region CxxModule
83
+
84
+ string BlobModule::getName() {
85
+ return moduleName;
86
+ }
87
+
88
+ std::map<string, dynamic> BlobModule::getConstants() {
89
+ return {{"BLOB_URI_SCHEME", blobKey}, {"BLOB_URI_HOST", {}}};
90
+ }
91
+
92
+ vector<module::CxxModule::Method> BlobModule::getMethods() {
93
+ return {
94
+ {"addNetworkingHandler",
95
+ [propBag = ReactPropertyBag{m_inspectableProperties.try_as<IReactPropertyBag>()},
96
+ requestBodyHandler = m_requestBodyHandler,
97
+ responseHandler = m_responseHandler](dynamic args) {
98
+ auto propId = ReactPropertyId<ReactNonAbiValue<weak_ptr<IHttpModuleProxy>>>{L"HttpModule.Proxy"};
99
+
100
+ if (auto prop = propBag.Get(propId)) {
101
+ if (auto httpHandler = prop.Value().lock()) {
102
+ httpHandler->AddRequestBodyHandler(requestBodyHandler);
103
+ httpHandler->AddResponseHandler(responseHandler);
104
+ }
105
+ }
106
+ // TODO: else emit error?
107
+ }},
108
+
109
+ {"addWebSocketHandler",
110
+ [contentHandler = m_contentHandler](dynamic args) {
111
+ auto id = jsArgAsInt(args, 0);
112
+
113
+ contentHandler->Register(id);
114
+ }},
115
+
116
+ {"removeWebSocketHandler",
117
+ [contentHandler = m_contentHandler](dynamic args) {
118
+ auto id = jsArgAsInt(args, 0);
119
+
120
+ contentHandler->Unregister(id);
121
+ }},
122
+
123
+ {"sendOverSocket",
124
+ [weakState = weak_ptr<SharedState>(m_sharedState),
125
+ persistor = m_blobPersistor,
126
+ propBag = ReactPropertyBag{m_inspectableProperties.try_as<IReactPropertyBag>()}](dynamic args) {
127
+ auto propId = ReactPropertyId<ReactNonAbiValue<weak_ptr<IWebSocketModuleProxy>>>{L"WebSocketModule.Proxy"};
128
+ shared_ptr<IWebSocketModuleProxy> wsProxy;
129
+ if (auto prop = propBag.Get(propId)) {
130
+ wsProxy = prop.Value().lock();
131
+ }
132
+ if (!wsProxy) {
133
+ return;
134
+ }
135
+
136
+ auto blob = jsArgAsObject(args, 0);
137
+ auto blobId = blob[blobIdKey].getString();
138
+ auto offset = blob[offsetKey].getInt();
139
+ auto size = blob[sizeKey].getInt();
140
+ auto socketID = jsArgAsInt(args, 1);
141
+
142
+ winrt::array_view<uint8_t> data;
143
+ try {
144
+ data = persistor->ResolveMessage(std::move(blobId), offset, size);
145
+ } catch (const std::exception &e) {
146
+ if (auto sharedState = weakState.lock()) {
147
+ Modules::SendEvent(sharedState->Module->getInstance(), "blobFailed", e.what());
148
+ }
149
+ return;
150
+ }
151
+
152
+ auto buffer = CryptographicBuffer::CreateFromByteArray(data);
153
+ auto winrtString = CryptographicBuffer::EncodeToBase64String(std::move(buffer));
154
+ auto base64String = Common::Unicode::Utf16ToUtf8(std::move(winrtString));
155
+
156
+ wsProxy->SendBinary(std::move(base64String), socketID);
157
+ }},
158
+
159
+ {"createFromParts",
160
+ // As of React Native 0.67, instance is set AFTER CxxModule::getMethods() is invoked.
161
+ // Use getInstance() directly once
162
+ // https://github.com/facebook/react-native/commit/1d45b20b6c6ba66df0485cdb9be36463d96cf182 becomes available.
163
+ [persistor = m_blobPersistor, weakState = weak_ptr<SharedState>(m_sharedState)](dynamic args) {
164
+ auto parts = jsArgAsArray(args, 0); // Array<Object>
165
+ auto blobId = jsArgAsString(args, 1);
166
+ vector<uint8_t> buffer{};
167
+
168
+ for (const auto &part : parts) {
169
+ auto type = part[typeKey].asString();
170
+ if (blobKey == type) {
171
+ auto blob = part[dataKey];
172
+ winrt::array_view<uint8_t> bufferPart;
173
+ try {
174
+ bufferPart = persistor->ResolveMessage(
175
+ blob[blobIdKey].asString(), blob[offsetKey].asInt(), blob[sizeKey].asInt());
176
+ } catch (const std::exception &e) {
177
+ if (auto sharedState = weakState.lock()) {
178
+ Modules::SendEvent(sharedState->Module->getInstance(), "blobFailed", e.what());
179
+ }
180
+ return;
181
+ }
182
+
183
+ buffer.reserve(buffer.size() + bufferPart.size());
184
+ buffer.insert(buffer.end(), bufferPart.begin(), bufferPart.end());
185
+ } else if ("string" == type) {
186
+ auto data = part[dataKey].asString();
187
+
188
+ buffer.reserve(buffer.size() + data.size());
189
+ buffer.insert(buffer.end(), data.begin(), data.end());
190
+ } else {
191
+ if (auto state = weakState.lock()) {
192
+ auto message = "Invalid type for blob: " + type;
193
+ Modules::SendEvent(state->Module->getInstance(), "blobFailed", std::move(message));
194
+ }
195
+ return;
196
+ }
197
+ }
198
+
199
+ persistor->StoreMessage(std::move(buffer), std::move(blobId));
200
+ }},
201
+
202
+ {"release",
203
+ [persistor = m_blobPersistor](dynamic args) // blobId: string
204
+ {
205
+ auto blobId = jsArgAsString(args, 0);
206
+
207
+ persistor->RemoveMessage(std::move(blobId));
208
+ }}};
209
+ }
210
+
211
+ #pragma endregion CxxModule
212
+
213
+ #pragma endregion BlobModule
214
+
215
+ #pragma region MemoryBlobPersistor
216
+
217
+ #pragma region IBlobPersistor
218
+
219
+ winrt::array_view<uint8_t> MemoryBlobPersistor::ResolveMessage(string &&blobId, int64_t offset, int64_t size) {
220
+ if (size < 1)
221
+ return {};
222
+
223
+ scoped_lock lock{m_mutex};
224
+
225
+ auto dataItr = m_blobs.find(std::move(blobId));
226
+ // Not found.
227
+ if (dataItr == m_blobs.cend())
228
+ throw std::invalid_argument("Blob object not found");
229
+
230
+ auto &bytes = (*dataItr).second;
231
+ auto endBound = static_cast<size_t>(offset + size);
232
+ // Out of bounds.
233
+ if (endBound > bytes.size() || offset >= static_cast<int64_t>(bytes.size()) || offset < 0)
234
+ throw std::out_of_range("Offset or size out of range");
235
+
236
+ return winrt::array_view<uint8_t>(bytes.data() + offset, bytes.data() + endBound);
237
+ }
238
+
239
+ void MemoryBlobPersistor::RemoveMessage(string &&blobId) noexcept {
240
+ scoped_lock lock{m_mutex};
241
+
242
+ m_blobs.erase(std::move(blobId));
243
+ }
244
+
245
+ void MemoryBlobPersistor::StoreMessage(vector<uint8_t> &&message, string &&blobId) noexcept {
246
+ scoped_lock lock{m_mutex};
247
+
248
+ m_blobs.insert_or_assign(std::move(blobId), std::move(message));
249
+ }
250
+
251
+ string MemoryBlobPersistor::StoreMessage(vector<uint8_t> &&message) noexcept {
252
+ // substr(1, 36) strips curly braces from a GUID.
253
+ auto blobId = winrt::to_string(winrt::to_hstring(GuidHelper::CreateNewGuid())).substr(1, 36);
254
+
255
+ scoped_lock lock{m_mutex};
256
+ m_blobs.insert_or_assign(blobId, std::move(message));
257
+
258
+ return blobId;
259
+ }
260
+
261
+ #pragma endregion IBlobPersistor
262
+
263
+ #pragma endregion MemoryBlobPersistor
264
+
265
+ #pragma region BlobWebSocketModuleContentHandler
266
+
267
+ BlobWebSocketModuleContentHandler::BlobWebSocketModuleContentHandler(shared_ptr<IBlobPersistor> blobPersistor) noexcept
268
+ : m_blobPersistor{blobPersistor} {}
269
+
270
+ #pragma region IWebSocketModuleContentHandler
271
+
272
+ void BlobWebSocketModuleContentHandler::ProcessMessage(string &&message, dynamic &params) /*override*/ {
273
+ params[dataKey] = std::move(message);
274
+ }
275
+
276
+ void BlobWebSocketModuleContentHandler::ProcessMessage(vector<uint8_t> &&message, dynamic &params) /*override*/ {
277
+ auto blob = dynamic::object();
278
+ blob(offsetKey, 0);
279
+ blob(sizeKey, message.size());
280
+ blob(blobIdKey, m_blobPersistor->StoreMessage(std::move(message)));
281
+
282
+ params[dataKey] = std::move(blob);
283
+ params[typeKey] = blobKey;
284
+ }
285
+ #pragma endregion IWebSocketModuleContentHandler
286
+
287
+ void BlobWebSocketModuleContentHandler::Register(int64_t socketID) noexcept {
288
+ scoped_lock lock{m_mutex};
289
+ m_socketIds.insert(socketID);
290
+ }
291
+
292
+ void BlobWebSocketModuleContentHandler::Unregister(int64_t socketID) noexcept {
293
+ scoped_lock lock{m_mutex};
294
+
295
+ auto itr = m_socketIds.find(socketID);
296
+ if (itr != m_socketIds.end())
297
+ m_socketIds.erase(itr);
298
+ }
299
+
300
+ #pragma endregion BlobWebSocketModuleContentHandler
301
+
302
+ #pragma region BlobModuleRequestBodyHandler
303
+
304
+ BlobModuleRequestBodyHandler::BlobModuleRequestBodyHandler(shared_ptr<IBlobPersistor> blobPersistor) noexcept
305
+ : m_blobPersistor{blobPersistor} {}
306
+
307
+ #pragma region IRequestBodyHandler
308
+
309
+ bool BlobModuleRequestBodyHandler::Supports(dynamic &data) /*override*/ {
310
+ auto itr = data.find(blobKey);
311
+
312
+ return itr != data.items().end() && !(*itr).second.empty();
313
+ }
314
+
315
+ dynamic BlobModuleRequestBodyHandler::ToRequestBody(dynamic &data, string &contentType) /*override*/ {
316
+ auto type = contentType;
317
+ if (!data[typeKey].isNull() && !data[typeKey].asString().empty()) {
318
+ type = data[typeKey].asString();
319
+ }
320
+ if (type.empty()) {
321
+ type = "application/octet-stream";
322
+ }
323
+
324
+ auto blob = data[blobKey];
325
+ auto blobId = blob[blobIdKey].asString();
326
+ auto bytes = m_blobPersistor->ResolveMessage(std::move(blobId), blob[offsetKey].asInt(), blob[sizeKey].asInt());
327
+
328
+ auto result = dynamic::object();
329
+ result(typeKey, type);
330
+ result(sizeKey, bytes.size());
331
+ result("bytes", dynamic(bytes.cbegin(), bytes.cend()));
332
+
333
+ return result;
334
+ }
335
+
336
+ #pragma endregion IRequestBodyHandler
337
+
338
+ #pragma endregion BlobModuleRequestBodyHandler
339
+
340
+ #pragma region BlobModuleResponseHandler
341
+
342
+ BlobModuleResponseHandler::BlobModuleResponseHandler(shared_ptr<IBlobPersistor> blobPersistor) noexcept
343
+ : m_blobPersistor{blobPersistor} {}
344
+
345
+ #pragma region IResponseHandler
346
+
347
+ bool BlobModuleResponseHandler::Supports(string &responseType) /*override*/ {
348
+ return blobKey == responseType;
349
+ }
350
+
351
+ dynamic BlobModuleResponseHandler::ToResponseData(vector<uint8_t> &&content) /*override*/ {
352
+ auto blob = dynamic::object();
353
+ blob(offsetKey, 0);
354
+ blob(sizeKey, content.size());
355
+ blob(blobIdKey, m_blobPersistor->StoreMessage(std::move(content)));
356
+
357
+ return blob;
358
+ }
359
+
360
+ #pragma endregion IResponseHandler
361
+
362
+ #pragma endregion BlobModuleResponseHandler
363
+
364
+ /*extern*/ const char *GetBlobModuleName() noexcept {
365
+ return moduleName;
366
+ }
367
+
368
+ /*extern*/ std::unique_ptr<facebook::xplat::module::CxxModule> CreateBlobModule(
369
+ IInspectable const &inspectableProperties) noexcept {
370
+ if (auto properties = inspectableProperties.try_as<IReactPropertyBag>())
371
+ return std::make_unique<BlobModule>(properties);
372
+
373
+ return nullptr;
374
+ }
375
+
376
+ } // namespace Microsoft::React