Beispiel #1
0
        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();
            }
        }
Beispiel #2
0
        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.");
            }
        }
Beispiel #3
0
        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();
            }
        }
Beispiel #5
0
        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);
            }
        }
Beispiel #6
0
        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);
        }
Beispiel #8
0
        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;
            }
        }
Beispiel #11
0
        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();
            }
        }
Beispiel #12
0
        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);
            }
        }