/// <summary> /// Flushes all queued messages from the JavaScript bridge /// </summary> /// <returns>A <see cref="Task"/>.</returns> /// <exception cref="NotSupportedException">If the requested callback identifier is not in the list of <see cref="ScriptingDelegates"/>.</exception> /// <seealso cref="ScriptingDelegates"/> /// <remarks> /// If an exception is encountered while processing the handler it is captured and returned to the caller via <see cref="JavaScriptBridgeMessage.ErrorData"/>. /// Each message is handled independently and dispatched back to the bridge in the order in which they are received. /// </remarks> private async Task FlushAsync() { { Trace.WriteLine($"Calling {Constants.BridgeJavaScript} method JavaScriptBridge.fetchQueue()", Constants.TraceName); List <JavaScriptBridgeMessage> messages; try { messages = await WebView.InvokeScriptFunctionAsync <List <JavaScriptBridgeMessage> >( "JavaScriptBridge", "fetchQueue"); } catch (Exception) { // TODO: Catch a more specific exception here throw; } if (messages != null) { Trace.WriteLine($"Retrieved {messages.Count} message{(messages.Count != 1 ? "s" : string.Empty)}", Constants.TraceName); foreach (var message in messages) { try { var handlerName = message.Handler; if (ScriptingDelegates.ContainsKey(handlerName)) { message.ResponseData = ScriptingDelegates[handlerName](message.HandlerData); } else { throw new NotSupportedException($"Handler '{handlerName}' not supported."); } } catch (Exception exception) { Trace.WriteLine( $"Error while processing message: [{exception.GetType()}] {exception.Message}", Constants.TraceName); // Encountered an excpetion while processing the message // Mask any potential disclosure issues by wrapping the exception as our own var e = new JavaScriptBridgeException(exception.Message) { Source = Constants.TraceName }; message.ErrorData = e; } finally { if (!string.IsNullOrEmpty(message.CallbackId)) { WebView.DispatchMessage(message); } } } } } }
/// <summary> /// Adds the scripting delegate with a given <paramref name="handlerId"/>. /// </summary> /// <param name="handlerId">The identity of the delegate to be used by JavaScript.</param> /// <param name="action">The action to be performed.</param> /// <remarks> /// The <paramref name="action"/> is a callback to be performed when invoking the delegate by id. The callback accepts a named parameter collection /// via <see cref="Dictionary{String,Object}"/> and permits a return of <see cref="Object"/>. The callback itself need not accept any parameters, nor /// need it return a value. If no parameters are passed, an empty <see cref="Dictionary{String,Object}"/> is passed for the parameters. If the callback /// does not produce a return value, <see langword="null" /> is returned. /// </remarks> public void AddScriptingHandler(string handlerId, Func <Dictionary <string, object>, object> action) { ScriptingDelegates.Add(handlerId, action); }