public async void clear(ICallback callback) { var error = default(JObject); await _mutex.WaitAsync().ConfigureAwait(false); try { var storageFolder = await GetAsyncStorageFolder(false).ConfigureAwait(false); if (storageFolder != null) { await storageFolder.DeleteAsync().ConfigureAwait(false); } } catch (Exception ex) { error = AsyncStorageHelpers.GetError(ex); } finally { _mutex.Release(); } if (error != null) { RnLog.Warn(ReactConstants.RNW, $"Error in AsyncStorageModule.clear: {error}"); callback.Invoke(error); } else { callback.Invoke(); } }
public void UpdateView() { if (_connectedViewTag == -1) { return; } foreach (var entry in _propNodeMapping) { var node = _manager.GetNodeById(entry.Value); if (node is StyleAnimatedNode styleNode) { styleNode.CollectViewUpdates(_propMap); } else if (node is ValueAnimatedNode valueNode) { _propMap[entry.Key] = valueNode.Value; } else { throw new InvalidOperationException($"Unsupported type of node used in property node '{node.GetType()}'."); } } var updated = _uiImplementation.SynchronouslyUpdateViewOnDispatcherThread( _connectedViewTag, _propMap); if (!updated) { RnLog.Warn(ReactConstants.RNW, $"Native animation workaround, frame lost as result of race condition."); } }
public async void multiRemove(string[] keys, ICallback callback) { if (keys == null || keys.Length == 0) { callback.Invoke(AsyncStorageHelpers.GetInvalidKeyError(null)); return; } var error = default(JObject); await _mutex.WaitAsync().ConfigureAwait(false); try { foreach (var key in keys) { if (key == null) { error = AsyncStorageHelpers.GetInvalidKeyError(null); break; } error = await RemoveAsync(key).ConfigureAwait(false); if (error != null) { break; } } } catch (Exception ex) { error = AsyncStorageHelpers.GetError(ex); } finally { _mutex.Release(); } if (error != null) { RnLog.Warn(ReactConstants.RNW, $"Error in AsyncStorageModule.multiRemove: {error}"); callback.Invoke(error); } else { callback.Invoke(); } }
/// <summary> /// Method that gives JavaScript the opportunity to consume the back /// button event. If JavaScript does not consume the event, the /// default back press action will be invoked at the end of the /// roundtrip to JavaScript. /// </summary> public void OnBackPressed() { DispatcherHelpers.AssertOnDispatcher(); var reactContext = _currentReactContext; if (reactContext == null) { RnLog.Warn(ReactConstants.RNW, $"ReactInstanceManager: OnBackPressed: Instance detached from instance manager."); InvokeDefaultOnBackPressed(); } else { reactContext.GetNativeModule <DeviceEventManagerModule>().EmitHardwareBackPressed(); } }
public async void multiGet(string[] keys, ICallback callback) { if (keys == null) { callback.Invoke(AsyncStorageHelpers.GetInvalidKeyError(null), null); return; } var error = default(JObject); var data = new JArray(); await _mutex.WaitAsync().ConfigureAwait(false); try { foreach (var key in keys) { if (key == null) { error = AsyncStorageHelpers.GetInvalidKeyError(null); break; } var value = await GetAsync(key).ConfigureAwait(false); data.Add(new JArray(key, value)); } } catch (Exception ex) { error = AsyncStorageHelpers.GetError(ex); } finally { _mutex.Release(); } if (error != null) { RnLog.Warn(ReactConstants.RNW, $"Error in AsyncStorageModule.multiGet: {error}"); callback.Invoke(error); } else { callback.Invoke(null, data); } }
public async void getAllKeys(ICallback callback) { var error = default(JObject); var keys = new JArray(); await _mutex.WaitAsync().ConfigureAwait(false); try { var storageFolder = await GetAsyncStorageFolder(false).ConfigureAwait(false); if (storageFolder != null) { var items = await storageFolder.GetItemsAsync().AsTask().ConfigureAwait(false); foreach (var item in items) { var itemName = item.Name; if (itemName.EndsWith(AsyncStorageHelpers.FileExtension)) { keys.Add(AsyncStorageHelpers.GetKeyName(itemName)); } } } } catch (Exception ex) { error = AsyncStorageHelpers.GetError(ex); } finally { _mutex.Release(); } if (error != null) { RnLog.Warn(ReactConstants.RNW, $"Error in AsyncStorageModule.getAllKeys: {error}"); callback.Invoke(error); } else { callback.Invoke(null, keys); } }
/// <summary> /// Used by the native animated module to bypass the process of /// updating the values through the shadow view hierarchy. This method /// will directly update the native views, which means that updates for /// layout-related props won't be handled properly. /// </summary> /// <param name="tag">The view tag.</param> /// <param name="props">The props.</param> /// <remarks> /// Make sure you know what you're doing before calling this method :) /// </remarks> public bool SynchronouslyUpdateViewOnDispatcherThread(int tag, JObject props) { // The native animations module is a single threaded implementation affinitized to the "main" dispatcher thread. // As a result all calls of this method are on main dispatcher thread. // Yet, for secondary views we have to dispatch to correct dispatcher as fast as possible DispatcherHelpers.AssertOnDispatcher(); UIViewOperationQueueInstance queue = GetQueueByTag(tag, true); if (queue == null) { // Returns false as per the caller's expectation return(false); } if (queue == MainUIViewOperationQueue) { // Main queue case. Just forward. if (!MainUIViewOperationQueue.NativeViewHierarchyManager.ViewExists(tag)) { return(false); } MainUIViewOperationQueue.NativeViewHierarchyManager.UpdateProps(tag, props); } else { // Dispatch to the correct thread. DispatcherHelpers.RunOnDispatcher(queue.Dispatcher, CoreDispatcherPriority.High, () => { if (queue.NativeViewHierarchyManager.ViewExists(tag)) { queue.NativeViewHierarchyManager.UpdateProps(tag, props); } else { RnLog.Warn(nameof(UIViewOperationQueue), $"View with tag '{tag}' not found due to race condition"); } }); } return(true); }
public void InvokeCallback(int callbackId, JArray arguments) { if (IsDisposed) { RnLog.Warn(ReactConstants.RNW, $"Invoking JS callback after bridge has been destroyed."); return; } QueueConfiguration.JavaScriptQueue.Dispatch(() => { QueueConfiguration.JavaScriptQueue.AssertOnThread(); if (IsDisposed) { return; } using (Tracer.Trace(Tracer.TRACE_TAG_REACT_BRIDGE, "<callback>").Start()) { _bridge.InvokeCallback(callbackId, arguments); } }); }
public void close(ushort code, string reason, int id) { if (!_webSocketConnections.TryGetValue(id, out var webSocket)) { RnLog.Warn(ReactConstants.RNW, $"Cannot close WebSocket. Unknown WebSocket id {id}."); return; } try { var writer = _dataWriters[id]; _dataWriters.Remove(id); _webSocketConnections.Remove(id); writer.Dispose(); webSocket.Close(code, reason); } catch (Exception ex) { RnLog.Error(ReactConstants.RNW, ex, $"Could not close WebSocket connection for id '{id}'."); } }
private static void JsExecutorOnNewLogLine(ChakraBridge.LogLevel logLevel, string logline) { // ChakraBridge.LogLevel value is lost when it gets marshaled to .Net native optimized code, // we need to do a proper marshaling to get actual value // Also, JavaScript already has a log level in the message, hence just RnLog.Info string tag = "JS"; FormattableString message = $"{logline}"; switch (logLevel) { case ChakraBridge.LogLevel.Error: RnLog.Error(tag, message); break; case ChakraBridge.LogLevel.Warning: RnLog.Warn(tag, message); break; case ChakraBridge.LogLevel.Info: case ChakraBridge.LogLevel.Trace: RnLog.Info(tag, message); break; } }
public async void multiSet(string[][] keyValueArray, ICallback callback) { if (keyValueArray == null || keyValueArray.Length == 0) { callback.Invoke(AsyncStorageHelpers.GetInvalidKeyError(null)); return; } var error = default(JObject); await _mutex.WaitAsync().ConfigureAwait(false); try { foreach (var pair in keyValueArray) { if (pair.Length != 2) { error = AsyncStorageHelpers.GetInvalidValueError(null); break; } if (pair[0] == null) { error = AsyncStorageHelpers.GetInvalidKeyError(null); break; } if (pair[1] == null) { error = AsyncStorageHelpers.GetInvalidValueError(pair[0]); break; } error = await SetAsync(pair[0], pair[1]).ConfigureAwait(false); if (error != null) { break; } } } catch (Exception ex) { error = AsyncStorageHelpers.GetError(ex); } finally { _mutex.Release(); } if (error != null) { RnLog.Warn(ReactConstants.RNW, $"Error in AsyncStorageModule.multiSet: {error}"); callback.Invoke(error); } else { callback.Invoke(); } }
public void reportSoftException(string title, JArray details, int exceptionId) { var stackTrace = StackTraceHelper.ConvertJavaScriptStackTrace(details); RnLog.Warn(ReactConstants.RNW, $"Soft Exception: {title}\n{stackTrace.PrettyPrint()}"); }
/// <summary> /// Runs the JavaScript at the given path. /// </summary> /// <param name="sourcePath">The source path.</param> /// <param name="sourceUrl">The source URL.</param> public void RunScript(string sourcePath, string sourceUrl) { if (sourcePath == null) { throw new ArgumentNullException(nameof(sourcePath)); } if (sourceUrl == null) { throw new ArgumentNullException(nameof(sourceUrl)); } try { var binPath = Path.Combine(ApplicationData.Current.LocalFolder.Path, BytecodeFileName); if (_useSerialization) { var srcFileInfo = new FileInfo(sourcePath); var binFileInfo = new FileInfo(binPath); bool ranSuccessfully = false; // The idea is to run the JS bundle and generate bytecode for it on a background thread. // This eliminates the need to delay the first start when the app doesn't have bytecode. // Next time the app starts, it checks if bytecode is still good and runs it directly. if (binFileInfo.Exists && binFileInfo.LastWriteTime > srcFileInfo.LastWriteTime) { try { Native.ThrowIfError((JavaScriptErrorCode)_executor.RunSerializedScript(sourcePath, binPath, sourceUrl)); ranSuccessfully = true; } catch (JavaScriptUsageException exc) { if (exc.ErrorCode == JavaScriptErrorCode.BadSerializedScript) { // Bytecode format is dependent on Chakra engine version, so an OS upgrade may require a recompilation RnLog.Warn(ReactConstants.RNW, $"Serialized bytecode script is corrupted or wrong format, will generate new one"); } else { // Some more severe error. We still have a chance (recompiling), so we keep continuing. RnLog.Error(ReactConstants.RNW, exc, $"Failed to run serialized bytecode script, will generate new one"); } File.Delete(binPath); } } else { RnLog.Info(ReactConstants.RNW, $"Serialized bytecode script doesn't exist or is obsolete, will generate one"); } if (!ranSuccessfully) { Task.Run(() => { try { // In Chakra JS engine, only one runtime can be active on a particular thread at a time, // and a runtime can only be active on one thread at a time. However it's possible to // create two runtimes and let them run on different threads. var rt = new ChakraBridge.NativeJavaScriptExecutor(); Native.ThrowIfError((JavaScriptErrorCode)rt.InitializeHost()); Native.ThrowIfError((JavaScriptErrorCode)rt.SerializeScript(sourcePath, binPath)); Native.ThrowIfError((JavaScriptErrorCode)rt.DisposeHost()); } catch (Exception ex) { RnLog.Error(ReactConstants.RNW, ex, $"Failed to generate serialized bytecode script."); // It's fine if the bytecode couldn't be generated: RN can still use the JS bundle. } }); Native.ThrowIfError((JavaScriptErrorCode)_executor.RunScript(sourcePath, sourceUrl)); } } else { Native.ThrowIfError((JavaScriptErrorCode)_executor.RunScript(sourcePath, sourceUrl)); } } catch (JavaScriptScriptException ex) { var jsonError = JavaScriptValueToJTokenConverter.Convert(ex.Error); var message = jsonError.Value <string>("message"); var stackTrace = jsonError.Value <string>("stack"); throw new Modules.Core.JavaScriptException(message ?? ex.Message, stackTrace, ex); } }