/// <summary>
        /// Detach given <paramref name="rootView"/> from the current react
        /// instance. This method is idempotent and can be called multiple
        /// times on the same <see cref="ReactRootView"/> instance.
        /// WARNING! Has to be called by the thread assocviated with the view.
        /// </summary>
        /// <param name="rootView">The root view.</param>
        public async Task DetachRootViewAsync(ReactRootView rootView)
        {
            if (rootView == null)
            {
                throw new ArgumentNullException(nameof(rootView));
            }

            DispatcherHelpers.AssertOnDispatcher(rootView);

            await DispatcherHelpers.CallOnDispatcher(() =>
            {
                if (_attachedRootViews.Remove(rootView))
                {
                    var currentReactContext = _currentReactContext;
                    if (currentReactContext != null && currentReactContext.HasActiveReactInstance)
                    {
                        return(DetachViewFromInstanceAsync(rootView, currentReactContext.ReactInstance));
                    }
                }

                // return Task.CompletedTask;
                return(Net46.Net45.Task.CompletedTask);
            }, true /* inlining allowed */).Unwrap(); // await inner task
        }
        /// <summary>
        /// Activate the callback for the given key.
        /// </summary>
        /// <param name="callbackKey">The callback key.</param>
        public void ActivateCallback(string callbackKey)
        {
            bool subscribe;

            lock (_gate)
            {
                var isSubscribed  = Volatile.Read(ref _isSubscribed);
                var isSubscribing = Volatile.Read(ref _isSubscribing);
                var isDisposed    = Volatile.Read(ref _isDisposed);
                subscribe = _isSubscribing =
                    !isDisposed &&
                    _callbackKeys.Add(callbackKey) &&
                    _callbackKeys.Count == 1 &&
                    !isSubscribed &&
                    !isSubscribing;
            }

            if (subscribe)
            {
                DispatcherHelpers.RunOnDispatcher(
#if WINDOWS_UWP
                    _coreDispatcher,
#else
                    DispatcherHelpers.MainDispatcher,
#endif
                    ActivatePriority,
                    () =>
                {
                    lock (_gate)
                    {
                        Subscribe();
                        _isSubscribing = false;
                    }
                });
            }
        }
        private static YogaSize MeasureText(ReactTextShadowNode textNode, YogaNode node, float width, YogaMeasureMode widthMode, float height, YogaMeasureMode heightMode)
        {
            // This is not a terribly efficient way of projecting the height of
            // the text elements. It requires that we have access to the
            // dispatcher in order to do measurement, which, for obvious
            // reasons, can cause perceived performance issues as it will block
            // the UI thread from handling other work.
            //
            // TODO: determine another way to measure text elements.
            var task = DispatcherHelpers.CallOnDispatcher(() =>
            {
                var textBlock = new TextBlock
                {
                    TextAlignment = TextAlignment.Left,
                    TextWrapping  = TextWrapping.Wrap,
                    TextTrimming  = TextTrimming.CharacterEllipsis,
                };

                textNode.UpdateTextBlockCore(textBlock, true);

                for (var i = 0; i < textNode.ChildCount; ++i)
                {
                    var child = textNode.GetChildAt(i);
                    textBlock.Inlines.Add(ReactInlineShadowNodeVisitor.Apply(child));
                }

                var normalizedWidth  = YogaConstants.IsUndefined(width) ? double.PositiveInfinity : width;
                var normalizedHeight = YogaConstants.IsUndefined(height) ? double.PositiveInfinity : height;
                textBlock.Measure(new Size(normalizedWidth, normalizedHeight));
                return(MeasureOutput.Make(
                           (float)Math.Ceiling(textBlock.DesiredSize.Width),
                           (float)Math.Ceiling(textBlock.DesiredSize.Height)));
            });

            return(task.Result);
        }
예제 #4
0
        public async Task UIManagerModule_Constants_ViewManagerOverrides()
        {
            await DispatcherHelpers.RunOnDispatcherAsync(ReactChoreographer.Initialize);

            var context      = new ReactContext();
            var viewManagers = new List <IViewManager> {
                new TestViewManager()
            };
            var uiImplementationProvider = new UIImplementationProvider();

            using (var actionQueue = new ActionQueue(ex => { }))
            {
                var module = await DispatcherHelpers.CallOnDispatcherAsync(() => new UIManagerModule(context, viewManagers, uiImplementationProvider, actionQueue));

                var constants = module.Constants.GetMap("Test");

                Assert.AreEqual(42, constants.GetMap("directEventTypes").GetValue("otherSelectionChange"));
                Assert.AreEqual(42, constants.GetMap("directEventTypes").GetMap("topSelectionChange").GetValue("registrationName"));
                Assert.AreEqual(42, constants.GetMap("directEventTypes").GetMap("topLoadingStart").GetValue("foo"));
                Assert.AreEqual(42, constants.GetMap("directEventTypes").GetValue("topLoadingError"));
            }

            await DispatcherHelpers.RunOnDispatcherAsync(ReactChoreographer.Dispose);
        }
예제 #5
0
        public async Task ReactInstance_GetModules()
        {
            var module = new TestNativeModule();

            var registry = new NativeModuleRegistry.Builder()
                           .Add(module)
                           .Build();

            var executor = new MockJavaScriptExecutor
            {
                OnCallFunctionReturnFlushedQueue = (_, __, ___) => JValue.CreateNull(),
                OnFlushQueue = () => JValue.CreateNull(),
                OnInvokeCallbackAndReturnFlushedQueue = (_, __) => JValue.CreateNull()
            };
            var builder = new ReactInstance.Builder()
            {
                QueueConfigurationSpec = ReactQueueConfigurationSpec.Default,
                Registry = registry,
                JavaScriptExecutorFactory = () => executor,
                BundleLoader = JavaScriptBundleLoader.CreateFileLoader("ms-appx:///Resources/test.js"),
                NativeModuleCallExceptionHandler = _ => { }
            };

            var instance = await DispatcherHelpers.CallOnDispatcherAsync(() => builder.Build());

            var actualModule = instance.GetNativeModule <TestNativeModule>();

            Assert.AreSame(module, actualModule);

            var firstJSModule  = instance.GetJavaScriptModule <TestJavaScriptModule>();
            var secondJSModule = instance.GetJavaScriptModule <TestJavaScriptModule>();

            Assert.AreSame(firstJSModule, secondJSModule);

            await DispatcherHelpers.CallOnDispatcherAsync(instance.DisposeAsync);
        }
예제 #6
0
        public void fOpen(string fileUri, int mode, IPromise promise)
        {
            DispatcherHelpers.RunOnDispatcher(() => {
                Task.Run(() => {
                    try
                    {
                        FileWrapper fileWrapper;
                        FileStream fileStream;

                        switch (mode)
                        {
                        case FILE_OPEN_MODE_READ:
                            fileStream  = new FileStream(fileUri, FileMode.Open, FileAccess.Read);
                            fileWrapper = new FileWrapper(fileStream);
                            break;

                        case FILE_OPEN_MODE_WRITE:
                            fileStream  = new FileStream(fileUri, FileMode.Append, FileAccess.Write);
                            fileWrapper = new FileWrapper(fileStream);
                            break;

                        default:
                            throw new ArgumentException("Invalid open mode " + mode);
                        }


                        files.Add(fileWrapper.refId, fileWrapper);
                        promise.Resolve(fileWrapper.refId);
                    }
                    catch (Exception e)
                    {
                        promise.Reject(ERROR_FATAL, e);
                    }
                });
            });
        }
예제 #7
0
        public async Task Timing_ManyTimers()
        {
            await DispatcherHelpers.RunOnDispatcherAsync(ReactChoreographer.Initialize);

            var count     = 1000;
            var ids       = new List <int>(count);
            var countdown = new CountdownEvent(count);
            var context   = CreateReactContext(new MockInvocationHandler((name, args) =>
            {
                Assert.AreEqual(name, nameof(JSTimers.callTimers));
                var currentIds = (IList <int>)args[0];
                ids.AddRange(currentIds);
                for (var i = 0; i < currentIds.Count; ++i)
                {
                    countdown.Signal();
                }
            }));

            var timing = new Timing(context);

            timing.Initialize();
            await DispatcherHelpers.RunOnDispatcherAsync(context.OnResume);

            var now = DateTimeOffset.Now.ToUnixTimeMilliseconds();

            for (var i = 0; i < count; ++i)
            {
                timing.createTimer(i, i, now, false);
            }

            Assert.IsTrue(countdown.Wait(count * 2));

            await DispatcherHelpers.CallOnDispatcherAsync(context.DisposeAsync);

            await DispatcherHelpers.RunOnDispatcherAsync(ReactChoreographer.Dispose);
        }
예제 #8
0
        /// <summary>
        /// Attach given <paramref name="rootView"/> to a React instance
        /// manager and start the JavaScript application using the JavaScript
        /// module provided by the <see cref="ReactRootView.JavaScriptModuleName"/>. If
        /// the React context is currently being (re-)created, or if the react
        /// context has not been created yet, the JavaScript application
        /// associated with the provided root view will be started
        /// asynchronously. This view will then be tracked by this manager and
        /// in case of React instance restart, it will be re-attached.
        /// </summary>
        /// <param name="rootView">The root view.</param>
        public void AttachMeasuredRootView(ReactRootView rootView)
        {
            Log.Info(ReactConstants.Tag, "[ReactInstanceManager] Enter AttachMeasuredRootView ");
            if (rootView == null)
            {
                throw new ArgumentNullException(nameof(rootView));
            }

            DispatcherHelpers.AssertOnDispatcher();

            _attachedRootViews.Add(rootView);

            // If the React context is being created in the background, the
            // JavaScript application will be started automatically when
            // creation completes, as root view is part of the attached root
            // view list.
            var currentReactContext = _currentReactContext;

            if (_contextInitializationTask == null && currentReactContext != null)
            {
                AttachMeasuredRootViewToInstance(rootView, currentReactContext.ReactInstance);
            }
            Log.Info(ReactConstants.Tag, "[ReactInstanceManager] Exit AttachMeasuredRootView ");
        }
        /// <summary>
        /// Destroy the <see cref="ReactInstanceManager"/>.
        /// </summary>
        public async Task DisposeAsync()
        {
            DispatcherHelpers.AssertOnDispatcher();

            // TODO: memory pressure hooks
            if (_useDeveloperSupport)
            {
                _devSupportManager.IsEnabled = false;
            }

            MoveToBeforeCreateLifecycleState();

            var currentReactContext = _currentReactContext;

            if (currentReactContext != null)
            {
                await currentReactContext.DisposeAsync();

                _currentReactContext = null;
                _hasStartedCreatingInitialContext = false;
            }

            ReactChoreographer.Dispose();
        }
        /// <summary>
        /// Creates a view with the given tag and class name.
        /// </summary>
        /// <param name="themedContext">The context.</param>
        /// <param name="tag">The tag.</param>
        /// <param name="className">The class name.</param>
        /// <param name="initialProperties">The properties.</param>
        public void CreateView(ThemedReactContext themedContext, int tag, string className, ReactStylesDiffMap initialProperties)
        {
            DispatcherHelpers.AssertOnDispatcher();
            using (Tracer.Trace(Tracer.TRACE_TAG_REACT_VIEW, "NativeViewHierarcyManager.CreateView")
                   .With("tag", tag)
                   .With("className", className)
                   .Start())
            {
                var viewManager = _viewManagers.Get(className);
                var view        = viewManager.CreateView(themedContext, _jsResponderHandler);
                _tagsToViews.Add(tag, view);
                _tagsToViewManagers.Add(tag, viewManager);

                // Uses an extension method and `Tag` property on
                // DependencyObject to store the tag of the view.
                view.SetTag(tag);
                view.SetReactContext(themedContext);

                if (initialProperties != null)
                {
                    viewManager.UpdateProperties(view, initialProperties);
                }
            }
        }
        private void DropView(DependencyObject view)
        {
            DispatcherHelpers.AssertOnDispatcher();
            var tag = view.GetTag();

            if (!_rootTags.ContainsKey(tag))
            {
                // For non-root views, we notify the view manager with `OnDropViewInstance`
                var mgr = ResolveViewManager(tag);
                mgr.OnDropViewInstance(view.GetReactContext(), view);
            }

            var viewManager = default(IViewManager);

            if (_tagsToViewManagers.TryGetValue(tag, out viewManager))
            {
                var viewParentManager = viewManager as IViewParentManager;
                if (viewParentManager != null)
                {
                    for (var i = viewParentManager.GetChildCount(view) - 1; i >= 0; --i)
                    {
                        var child        = viewParentManager.GetChildAt(view, i);
                        var managedChild = default(DependencyObject);
                        if (_tagsToViews.TryGetValue(child.GetTag(), out managedChild))
                        {
                            DropView(managedChild);
                        }
                    }

                    viewParentManager.RemoveAllChildren(view);
                }
            }

            _tagsToViews.Remove(tag);
            _tagsToViewManagers.Remove(tag);
        }
예제 #12
0
        public void ScheduleError()
        {
            var ex = new Exception();

            var id   = Thread.CurrentThread.ManagedThreadId;
            var disp = DispatcherHelpers.EnsureDispatcher();
            var evt  = new ManualResetEvent(false);

            Exception thrownEx = null;

            disp.UnhandledException += (o, e) =>
            {
                thrownEx = e.Exception;
                evt.Set();
                e.Handled = true;
            };
            var sch = new DispatcherScheduler(disp);

            sch.Schedule(() => { throw ex; });
            evt.WaitOne();
            disp.InvokeShutdown();

            Assert.Same(ex, thrownEx);
        }
        /// <summary>
        /// Updates the layout of a view.
        /// </summary>
        /// <param name="parentTag">The parent view tag.</param>
        /// <param name="tag">The view tag.</param>
        /// <param name="x">The left coordinate.</param>
        /// <param name="y">The top coordinate.</param>
        /// <param name="width">The layout width.</param>
        /// <param name="height">The layout height.</param>
        public void UpdateLayout(int parentTag, int tag, int x, int y, int width, int height)
        {
            DispatcherHelpers.AssertOnDispatcher();
            using (Tracer.Trace(Tracer.TRACE_TAG_REACT_VIEW, "NativeViewHierarcyManager.UpdateLayout")
                   .With("parentTag", parentTag)
                   .With("tag", tag))
            {
                var viewToUpdate = ResolveView(tag);

                var parentViewManager       = default(IViewManager);
                var parentViewParentManager = default(IViewParentManager);
                if (!_tagsToViewManagers.TryGetValue(parentTag, out parentViewManager) ||
                    (parentViewParentManager = parentViewManager as IViewParentManager) == null)
                {
                    throw new InvalidOperationException(
                              $"Trying to use view with tag '{tag}' as a parent, but its manager doesn't extend ViewParentManager.");
                }

                if (!parentViewParentManager.NeedsCustomLayoutForChildren)
                {
                    UpdateLayout(viewToUpdate, x, y, width, height);
                }
            }
        }
        public void ShowDevOptionsDialog()
        {
            if (_devOptionsDialog != null || !IsEnabled)
            {
                return;
            }

            DispatcherHelpers.RunOnDispatcher(() =>
            {
                var options = new[]
                {
                    new DevOptionHandler(
                        "Reload JavaScript",
                        HandleReloadJavaScript),
                    new DevOptionHandler(
                        IsRemoteDebuggingEnabled
                            ? "Stop JS Remote Debugging"
                            : "Start JS Remote Debugging",
                        () =>
                    {
                        IsRemoteDebuggingEnabled = !IsRemoteDebuggingEnabled;
                        HandleReloadJavaScript();
                    }),
                    new DevOptionHandler(
                        _devSettings.IsHotModuleReplacementEnabled
                            ? "Disable Hot Reloading"
                            : "Enable Hot Reloading",
                        () =>
                    {
                        _devSettings.IsHotModuleReplacementEnabled = !_devSettings.IsHotModuleReplacementEnabled;
                        HandleReloadJavaScript();
                    }),
                    new DevOptionHandler(
                        _devSettings.IsReloadOnJavaScriptChangeEnabled
                            ? "Disable Live Reload"
                            : "Enable Live Reload",
                        () =>
                        _devSettings.IsReloadOnJavaScriptChangeEnabled =
                            !_devSettings.IsReloadOnJavaScriptChangeEnabled),
                    new DevOptionHandler(
                        _devSettings.IsElementInspectorEnabled
                            ? "Hide Inspector"
                            : "Show Inspector",
                        () =>
                    {
                        _devSettings.IsElementInspectorEnabled = !_devSettings.IsElementInspectorEnabled;
                        _reactInstanceCommandsHandler.ToggleElementInspector();
                    }),
                };

                _devOptionsDialogOpen     = true;
                _devOptionsDialog         = new DevOptionDialog();
                _devOptionsDialog.Closed += (_, __) =>
                {
                    _devOptionsDialogOpen    = false;
                    _dismissDevOptionsDialog = null;
                    _devOptionsDialog        = null;
                };

                foreach (var option in options)
                {
                    _devOptionsDialog.Add(option.Name, option.OnSelect);
                }

                if (_redBoxDialog != null)
                {
                    _dismissRedBoxDialog();
                }

#if WINDOWS_UWP
                var asyncInfo            = _devOptionsDialog.ShowAsync();
                _dismissDevOptionsDialog = asyncInfo.Cancel;

                foreach (var option in options)
                {
                    option.HideDialog = _dismissDevOptionsDialog;
                }
#else
                if (Application.Current != null && Application.Current.MainWindow != null && Application.Current.MainWindow.IsLoaded)
                {
                    _devOptionsDialog.Owner = Application.Current.MainWindow;
                }
                else
                {
                    _devOptionsDialog.Topmost = true;
                    _devOptionsDialog.WindowStartupLocation = WindowStartupLocation.CenterScreen;
                }

                _dismissDevOptionsDialog = _devOptionsDialog.Close;
                _devOptionsDialog.Show();

                foreach (var option in options)
                {
                    option.HideDialog = _dismissDevOptionsDialog;
                }
#endif
            });
        }
예제 #15
0
 private void ClearCallback()
 {
     DispatcherHelpers.AssertOnDispatcher();
     CompositionTarget.Rendering -= ScheduleDispatch;
 }
        public void ShowDevOptionsDialog()
        {
            if (_devOptionsDialog != null || !IsEnabled)
            {
                return;
            }
            Log.Info(ReactConstants.Tag, "Before build DevOptionHandler");
            DispatcherHelpers.RunOnDispatcher(() =>
            {
                var options = new[]
                {
                    new DevOptionHandler(
                        "Reload JavaScript",
                        HandleReloadJavaScript),
                    new DevOptionHandler(
                        IsRemoteDebuggingEnabled
                            ? "Stop JS Remote Debugging"
                            : "Start JS Remote Debugging",
                        () =>
                    {
                        IsRemoteDebuggingEnabled = !IsRemoteDebuggingEnabled;
                        HandleReloadJavaScript();
                    }),
                    new DevOptionHandler(
                        _devSettings.IsHotModuleReplacementEnabled
                            ? "Disable Hot Reloading"
                            : "Enable Hot Reloading",
                        () =>
                    {
                        _devSettings.IsHotModuleReplacementEnabled = !_devSettings.IsHotModuleReplacementEnabled;
                        HandleReloadJavaScript();
                    }),
                    new DevOptionHandler(
                        _devSettings.IsReloadOnJavaScriptChangeEnabled
                            ? "Disable Live Reload"
                            : "Enable Live Reload",
                        () =>
                        _devSettings.IsReloadOnJavaScriptChangeEnabled =
                            !_devSettings.IsReloadOnJavaScriptChangeEnabled),
                    new DevOptionHandler(
                        _devSettings.IsElementInspectorEnabled
                            ? "Hide Inspector"
                            : "Show Inspector",
                        () =>
                    {
                        _devSettings.IsElementInspectorEnabled = !_devSettings.IsElementInspectorEnabled;
                        _reactInstanceCommandsHandler.ToggleElementInspector();
                    }),
                    new DevOptionHandler(
                        "Set Host IP Address",
                        () =>
                    {
                        _devSettings.DebugServerHost = "109.123.120.200:8084";
                        _devServerHostDialog         = DevServerHostDialog.GetInstance();
                        HideDevOptionsDialog();
                        _devServerHostDialog.Show();
                        _devServerHostDialog.ResetCloseEvent();
                        _devServerHostDialog.Closed += (dialog, __) =>
                        {
                            var ipText = ((DevServerHostDialog)dialog).text;
                            if (ipText.IndexOf(':') == -1)
                            {
                                ipText += ":8081";
                            }
                            _devSettings.DebugServerHost = ipText;
                            Log.Info(ReactConstants.Tag, "IP is " + ipText);
                        };
                    }),
                };

                _devOptionsDialogOpen = true;
                _devOptionsDialog     = DevOptionDialog.GetInstance();
                _devOptionsDialog.ResetCloseEvent();
                _devOptionsDialog.Closed += (_, __) =>
                {
                    _devOptionsDialogOpen    = false;
                    _dismissDevOptionsDialog = null;
                    _devOptionsDialog        = null;
                };

                foreach (var option in options)
                {
                    _devOptionsDialog.Add(option);
                }

                if (_redBoxDialog != null)
                {
                    _dismissRedBoxDialog();
                }


                if (Application.Current != null && ReactProgram.RctWindow != null)
                {
                    _devOptionsDialog.Owner = ReactProgram.RctWindow;
                }

                _dismissDevOptionsDialog = _devOptionsDialog.Close;
                _devOptionsDialog.Show();

                foreach (var option in options)
                {
                    option.HideDialog = _dismissDevOptionsDialog;
                }
            });
        }
예제 #17
0
 /// <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 properties won't be handled properly.
 /// </summary>
 /// <param name="tag">The view tag.</param>
 /// <param name="props">The properties</param>
 /// <remarks>
 /// Make sure you know what you're doing before calling this method :)
 /// </remarks>
 public void SynchronouslyUpdateViewOnDispatcherThread(int tag, ReactStylesDiffMap props)
 {
     DispatcherHelpers.AssertOnDispatcher();
     _operationsQueue.NativeViewHierarchyManager.UpdateProperties(tag, props);
 }
예제 #18
0
 /// <summary>
 /// Called before a <see cref="IReactInstance"/> is disposed.
 /// </summary>
 public override Task OnReactInstanceDisposeAsync()
 {
     DispatcherHelpers.RunOnDispatcher(_uiImplementation.OnDestroy);
     _eventDispatcher.OnReactInstanceDispose();
     return(Task.CompletedTask);
 }
예제 #19
0
 /// <summary>
 /// Called when the host is leaving background mode.
 /// </summary>
 public void OnLeavingBackground()
 {
     DispatcherHelpers.AssertOnDispatcher();
     MoveToResumedLifecycleState(false);
 }
예제 #20
0
 /// <summary>
 /// Called when the host entered background mode.
 /// </summary>
 public void OnEnteredBackground()
 {
     DispatcherHelpers.AssertOnDispatcher();
     MoveToBackgroundLifecycleState();
 }
예제 #21
0
 /// <summary>
 /// Invokes the action to execute on dispatcher thread associated with view specified by <paramref name="tag" />.
 /// Action is not queued on operation queue, but it goes to dispatcher directly.
 /// </summary>
 /// <param name="tag">The react tag which specifies view.</param>
 /// <param name="action">The action to invoke.</param>
 public void InvokeAction(int tag, Action action)
 {
     DispatcherHelpers.RunOnDispatcher(GetQueueByTag(tag).Dispatcher, () => action());
 }
예제 #22
0
 internal void CleanupSafe()
 {
     // Inlining allowed
     DispatcherHelpers.RunOnDispatcher(this.Dispatcher, Cleanup, true);
 }
 private void InvokeDefaultOnBackPressed()
 {
     DispatcherHelpers.AssertOnDispatcher();
     _defaultBackButtonHandler?.Invoke();
 }
        /// <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)
        {
            DispatcherHelpers.AssertOnDispatcher();

            return(_operationsQueue.SynchronouslyUpdateViewOnDispatcherThread(tag, props));
        }
 /// <summary>
 /// Called when the host is leaving background mode.
 /// </summary>
 public void OnLeavingBackground()
 {
     DispatcherHelpers.AssertOnDispatcher();
     _lifecycleStateMachine.OnLeavingBackground();
 }
예제 #26
0
 public void HandleException(Exception exception)
 {
     DispatcherHelpers.RunOnDispatcher(() => ExceptionDispatchInfo.Capture(exception).Throw());
 }
 private void DetachViewFromInstance(ReactRootView rootView, IReactInstance reactInstance)
 {
     DispatcherHelpers.AssertOnDispatcher();
     reactInstance.GetJavaScriptModule <AppRegistry>().unmountApplicationComponentAtRootTag(rootView.GetTag());
 }
        /// <summary>
        /// Adds a root view to the hierarchy.
        /// </summary>
        /// <param name="tag">The root view tag.</param>
        /// <param name="rootView">The root view.</param>
        /// <param name="themedRootContext">The React context.</param>
        public void AddRootView(
            int tag,
            SizeMonitoringCanvas rootView,
            ThemedReactContext themedRootContext)
        {
            // This is called on layout manager thread

            // Extract dispatcher
            var rootViewDispatcher = rootView.Dispatcher;

            // _dispatcherToOperationQueueInfo contains a mapping of CoreDispatcher to <UIViewOperationQueueInstance, # of ReactRootView instances>.
            // Operation queue instances are created lazily whenever an "unknown" CoreDispatcher is detected. Each operation queue instance
            // works together with one dedicated NativeViewHierarchyManager and one ReactChoreographer.
            // One operation queue is the "main" one:
            // - is coupled with the CoreApplication.MainView dispatcher
            // - drives animations in ALL views
            if (!_dispatcherToOperationQueueInfo.TryGetValue(rootViewDispatcher, out var queueInfo))
            {
                // Queue instance doesn't exist for this dispatcher, we need to create

                // Find the CoreApplicationView associated to the new CoreDispatcher
                CoreApplicationView foundView = CoreApplication.Views.First(v => v.Dispatcher == rootViewDispatcher);

                // Create new ReactChoreographer for this view/dispatcher. It will only be used for its DispatchUICallback services
                var reactChoreographer = ReactChoreographer.CreateSecondaryInstance(foundView);

                queueInfo = new QueueInstanceInfo()
                {
                    queueInstance = new UIViewOperationQueueInstance(
                        _reactContext,
                        new NativeViewHierarchyManager(_viewManagerRegistry, rootViewDispatcher, OnViewsDropped),
                        reactChoreographer),
                    rootViewCount = 1
                };

                lock (_lock)
                {
                    // Add new tuple to map
                    _dispatcherToOperationQueueInfo.AddOrUpdate(rootViewDispatcher, queueInfo, (k, v) => throw new InvalidOperationException("Duplicate key"));

                    if (_active)
                    {
                        // Simulate an OnResume from the correct dispatcher thread
                        // (OnResume/OnSuspend/OnDestroy have this thread affinity, all other methods do enqueuings in a thread safe manner)
                        // (No inlining here so we don't hold lock during call outs. Not a big deal since inlining
                        // is only useful for main UI thread, and this code is not executed for that one)
                        DispatcherHelpers.RunOnDispatcher(rootViewDispatcher, queueInfo.queueInstance.OnResume);
                    }
                }
            }
            else
            {
                // Queue instance does exist.
                // Increment the count of root views. This is helpful for the case the count reaches 0 so we can cleanup the queue.
                queueInfo.rootViewCount++;
            }

            // Add tag
            _reactTagToOperationQueue.Add(tag, queueInfo.queueInstance);

            // Send forward
            queueInfo.queueInstance.AddRootView(tag, rootView, themedRootContext);
        }
        /// <summary>
        /// Fetches the encoded BitmapImage.
        /// </summary>
        /// <param name="uri">The image uri.</param>
        /// <param name="token">The cancellation token.</param>
        /// <param name="dispatcher">
        /// The current view's dispatcher, used to create BitmapImage.
        /// </param>
        /// <returns>The encoded BitmapImage.</returns>
        /// <exception cref="IOException">
        /// If the image uri can't be found.
        /// </exception>
        public Task <BitmapImage> FetchEncodedBitmapImageAsync(
            Uri uri,
            CancellationToken token   = default(CancellationToken),
            CoreDispatcher dispatcher = null)
        {
            var taskCompletionSource = new TaskCompletionSource <BitmapImage>();
            var dataSource           = FetchEncodedImage(ImageRequest.FromUri(uri), null);
            var dataSubscriber       = new BaseDataSubscriberImpl <CloseableReference <IPooledByteBuffer> >(
                async response =>
            {
                CloseableReference <IPooledByteBuffer> reference = response.GetResult();
                if (reference != null)
                {
                    //----------------------------------------------------------------------
                    // Phong Cao: InMemoryRandomAccessStream can't write anything < 16KB.
                    // http://stackoverflow.com/questions/25928408/inmemoryrandomaccessstream-incorrect-behavior
                    //----------------------------------------------------------------------
                    IPooledByteBuffer inputStream = reference.Get();
                    int supportedSize             = Math.Max(16 * ByteConstants.KB, inputStream.Size);

                    // Allocate temp buffer for stream convert
                    byte[] bytesArray = default(byte[]);
                    CloseableReference <byte[]> bytesArrayRef = default(CloseableReference <byte[]>);

                    try
                    {
                        bytesArrayRef = _flexByteArrayPool.Get(supportedSize);
                        bytesArray    = bytesArrayRef.Get();
                    }
                    catch (Exception)
                    {
                        // Allocates the byte array since the pool couldn't provide one
                        bytesArray = new byte[supportedSize];
                    }

                    try
                    {
                        inputStream.Read(0, bytesArray, 0, inputStream.Size);
                        await DispatcherHelpers.CallOnDispatcherAsync(async() =>
                        {
                            using (var outStream = new InMemoryRandomAccessStream())
                                using (var writeStream = outStream.AsStreamForWrite())
                                {
                                    await writeStream.WriteAsync(bytesArray, 0, supportedSize);
                                    outStream.Seek(0);
                                    BitmapImage bitmapImage = new BitmapImage();
                                    await bitmapImage.SetSourceAsync(outStream).AsTask().ConfigureAwait(false);
                                    taskCompletionSource.SetResult(bitmapImage);
                                }
                        },
                                                                      dispatcher).ConfigureAwait(false);
                    }
                    catch (Exception e)
                    {
                        taskCompletionSource.SetException(e);
                    }
                    finally
                    {
                        CloseableReference <IPooledByteBuffer> .CloseSafely(reference);
                        CloseableReference <byte[]> .CloseSafely(bytesArrayRef);
                    }
                }
                else
                {
                    taskCompletionSource.SetResult(null);
                }
            },
                response =>
            {
                taskCompletionSource.SetException(response.GetFailureCause());
            });

            dataSource.Subscribe(dataSubscriber, _handleResultExecutor);
            token.Register(() =>
            {
                dataSource.Close();
                taskCompletionSource.TrySetCanceled();
            });

            return(taskCompletionSource.Task);
        }
        /// <summary>
        /// Animation loop performs BFS over the graph of animated nodes.
        /// </summary>
        /// <remarks>
        /// We use incremented <see cref="_animatedGraphBFSColor"/> to mark
        /// nodes as visited in each of the BFS passes, which saves additional
        /// loops for clearing "visited" states.
        ///
        /// First BFS starts with nodes that are in <see cref="_updatedNodes" />
        /// (this is, their value has been modified from JS in the last batch
        /// of JS operations) or directly attached to an active animation
        /// (thus linked to objects from <see cref="_activeAnimations"/>). In
        /// that step we calculate an attribute <see cref="AnimatedNode.ActiveIncomingNodes"/>.
        /// The second BFS runs in topological order over the sub-graph of
        /// *active* nodes. This is done by adding nodes to the BFS queue only
        /// if all its "predecessors" have already been visited.
        /// </remarks>
        /// <param name="renderingTime">Frame rendering time.</param>
        public void RunUpdates(TimeSpan renderingTime)
        {
            DispatcherHelpers.AssertOnDispatcher();
            var activeNodesCount      = 0;
            var updatedNodesCount     = 0;
            var hasFinishedAnimations = false;

            // STEP 1.
            // BFS over graph of nodes starting from ones from `_updatedNodes` and ones that are attached to
            // active animations (from `mActiveAnimations)`. Update `ActiveIncomingNodes` property for each node
            // during that BFS. Store number of visited nodes in `activeNodesCount`. We "execute" active
            // animations as a part of this step.

            _animatedGraphBFSColor++; /* use new color */

            var nodesQueue = new Queue <AnimatedNode>();

            foreach (var node in _updatedNodes)
            {
                if (node.BfsColor != _animatedGraphBFSColor)
                {
                    node.BfsColor = _animatedGraphBFSColor;
                    activeNodesCount++;
                    nodesQueue.Enqueue(node);
                }
            }

            foreach (var animation in _activeAnimations)
            {
                animation.RunAnimationStep(renderingTime);
                var valueNode = animation.AnimatedValue;
                if (valueNode.BfsColor != _animatedGraphBFSColor)
                {
                    valueNode.BfsColor = _animatedGraphBFSColor;
                    activeNodesCount++;
                    nodesQueue.Enqueue(valueNode);
                }

                if (animation.HasFinished)
                {
                    hasFinishedAnimations = true;
                }
            }

            while (nodesQueue.Count > 0)
            {
                var nextNode = nodesQueue.Dequeue();
                if (nextNode.Children != null)
                {
                    foreach (var child in nextNode.Children)
                    {
                        child.ActiveIncomingNodes++;
                        if (child.BfsColor != _animatedGraphBFSColor)
                        {
                            child.BfsColor = _animatedGraphBFSColor;
                            activeNodesCount++;
                            nodesQueue.Enqueue(child);
                        }
                    }
                }
            }

            // STEP 2
            // BFS over the graph of active nodes in topological order -> visit node only when all its
            // "predecessors" in the graph have already been visited. It is important to visit nodes in that
            // order as they may often use values of their predecessors in order to calculate "next state"
            // of their own. We start by determining the starting set of nodes by looking for nodes with
            // `ActiveIncomingNodes = 0` (those can only be the ones that we start BFS in the previous
            // step). We store number of visited nodes in this step in `updatedNodesCount`

            _animatedGraphBFSColor++;

            // find nodes with zero "incoming nodes", those can be either nodes from `mUpdatedNodes` or
            // ones connected to active animations
            foreach (var node in _updatedNodes)
            {
                if (node.ActiveIncomingNodes == 0 && node.BfsColor != _animatedGraphBFSColor)
                {
                    node.BfsColor = _animatedGraphBFSColor;
                    updatedNodesCount++;
                    nodesQueue.Enqueue(node);
                }
            }

            foreach (var animation in _activeAnimations)
            {
                var valueNode = animation.AnimatedValue;
                if (valueNode.ActiveIncomingNodes == 0 && valueNode.BfsColor != _animatedGraphBFSColor)
                {
                    valueNode.BfsColor = _animatedGraphBFSColor;
                    updatedNodesCount++;
                    nodesQueue.Enqueue(valueNode);
                }
            }

            // Run main "update" loop
            while (nodesQueue.Count > 0)
            {
                var nextNode = nodesQueue.Dequeue();
                nextNode.Update();

                var propsNode = nextNode as PropsAnimatedNode;
                var valueNode = default(ValueAnimatedNode);
                if (propsNode != null)
                {
                    propsNode.UpdateView(_uiImplementation);
                }
                else if ((valueNode = nextNode as ValueAnimatedNode) != null)
                {
                    // Potentially send events to JS when the node's value is updated
                    valueNode.OnValueUpdate();
                }

                if (nextNode.Children != null)
                {
                    foreach (var child in nextNode.Children)
                    {
                        child.ActiveIncomingNodes--;
                        if (child.BfsColor != _animatedGraphBFSColor && child.ActiveIncomingNodes == 0)
                        {
                            child.BfsColor = _animatedGraphBFSColor;
                            updatedNodesCount++;
                            nodesQueue.Enqueue(child);
                        }
                    }
                }
            }

            // Verify that we've visited *all* active nodes. Throw otherwise as this would mean there is a
            // cycle in animated node graph. We also take advantage of the fact that all active nodes are
            // visited in the step above so that all the nodes properties `mActiveIncomingNodes` are set to
            // zero
            if (activeNodesCount != updatedNodesCount)
            {
                throw new InvalidOperationException(
                          Invariant($"Looks like animated nodes graph has cycles, there are {activeNodesCount} but visited only {updatedNodesCount}."));
            }

            // Clean _updatedNodes queue
            _updatedNodes.Clear();

            // Cleanup finished animations. Iterate over the array of animations and override ones that has
            // finished, then resize `_activeAnimations`.
            if (hasFinishedAnimations)
            {
                int dest = 0;
                for (var i = 0; i < _activeAnimations.Count; ++i)
                {
                    var animation = _activeAnimations[i];
                    if (!animation.HasFinished)
                    {
                        _activeAnimations[dest++] = animation;
                    }
                    else
                    {
                        animation.EndCallback.Invoke(new JObject
                        {
                            { "finished", true },
                        });
                    }
                }

                for (var i = _activeAnimations.Count - 1; i >= dest; --i)
                {
                    _activeAnimations.RemoveAt(i);
                }
            }
        }