/// <summary>
        /// Trigger an event callback in a remote process.
        /// Can be called on any thread as the CEF API involved (<see cref="CefBrowser.SendProcessMessage"/>) can be called on any thread when in the browser process.
        /// </summary>
        /// <param name="listener">
        /// The message that requested to listen to the triggered event.
        /// </param>
        /// <param name="result">
        /// The data payload of the triggered event.
        /// </param>
        public void SendEvent(BrowserCallInfo listener, ResultData result)
        {
            if (listener == null)
            {
                throw new ArgumentNullException("listener");
            }

            if (result == null)
            {
                throw new ArgumentNullException("result");
            }

            var eventMessage = new PluginMessage
            {
                MessageId   = listener.RequestMessage.MessageId,
                MessageType = PluginMessageType.EventFired,
                PluginId    = string.Empty,
                MemberName  = string.Empty,
                Data        = ParagonJsonSerializer.Serialize(result),
                BrowserId   = listener.RequestMessage.BrowserId,
                ContextId   = listener.RequestMessage.ContextId
            };

            SendMessage(listener.Browser, eventMessage);
        }
        /// <summary>
        /// Send a response to a function call, which can include the call being cancelled.
        /// Can be called on any thread as the CEF API involved (<see cref="CefBrowser.SendProcessMessage"/>) can be called on any thread when in the browser process.
        /// </summary>
        /// <param name="browser">
        /// The browser to send the response to.
        /// </param>
        /// <param name="invocationMessage">
        /// The message that requested the function call.
        /// </param>
        /// <param name="result">
        /// The result of the function (result, errorCode, error).
        /// </param>
        private void SendFunctionResponse(CefBrowser browser, PluginMessage invocationMessage, ResultData result)
        {
            var responseMessage = new PluginMessage
            {
                MessageId   = invocationMessage.MessageId,
                MessageType = PluginMessageType.FunctionCallback,
                PluginId    = invocationMessage.PluginId,
                MemberName  = invocationMessage.MemberName,
                Data        = ParagonJsonSerializer.Serialize(result),
                BrowserId   = invocationMessage.BrowserId,
                ContextId   = invocationMessage.ContextId
            };

            SendMessage(browser, responseMessage);
        }
        private void OnRenderProcessInitialize(object sender, RenderProcessInitEventArgs e)
        {
            var index             = 0;
            var pluginInitMessage = MessageRouter.CreatePluginInitMessage();

            if (pluginInitMessage != null)
            {
                e.InitArgs.SetList(index++, pluginInitMessage);
            }

            if (_renderPlugins != null)
            {
                var renderPluginInfo = ParagonJsonSerializer.Serialize(_renderPlugins);
                e.InitArgs.SetString(index, renderPluginInfo);
            }
            ParagonRuntime.RenderProcessInitialize -= OnRenderProcessInitialize;
        }
        protected override void ReceiveMessage(CefBrowser browser, PluginMessage pluginMessage)
        {
            switch (pluginMessage.MessageType)
            {
            case PluginMessageType.FunctionCallback:
            case PluginMessageType.EventFired:
                var payload = ParagonJsonSerializer.Deserialize <ResultData>(pluginMessage.Data);
                OnBrowserCallbackInvokeReceived(pluginMessage, payload);
                break;

            case PluginMessageType.RemoveRetained:
                // Remove a listener in the render process (handles browser side dynamic plugin dispose scenario)
                var retainedCallback = _pendingCallbacks.Remove(pluginMessage.MessageId);
                if (retainedCallback != null)
                {
                    retainedCallback.Dispose();
                }
                break;

            default:
                throw new ArgumentOutOfRangeException();
            }
            browser.Dispose();
        }