public bool AddLocalPlugin(object pluginObject)
        {
            var plugin = pluginObject as JavaScriptPlugin;

            if (plugin == null)
            {
                plugin = JavaScriptPlugin.CreateFromObject(_managerProcess, pluginObject);
                if (plugin == null)
                {
                    Logger.Error("Failed to wrap unknown plugin type");
                    return(false);
                }
            }

            if (!_localPluginsById.TryAdd(plugin.Descriptor.PluginId, plugin))
            {
                Logger.Warn("Attempt to add a duplicate plugin");
                return(false);
            }

            if (plugin.IsDynamic)
            {
                plugin.DynamicPluginDisposed += DynamicPluginDisposed;
            }

            Logger.Info("Local plugin added successfully: " + plugin.Descriptor.PluginId);
            return(true);
        }
        public void RemoveEventListener(JavaScriptPlugin targetPlugin, string eventName,
                                        IV8Callback callback)
        {
            Logger.Info("RemoveEventListener Local Plugin {0} Event {1}", targetPlugin.Descriptor.PluginId, eventName);

            if (!EnsureOnRendererThread())
            {
                return;
            }

            try
            {
                var info = (LocalRenderCallInfo)_pendingCallbacks.Remove(callback.Identifier);
                info.Dispose();

                targetPlugin.RemoveEventListener(eventName, info);
            }
            catch (Exception exception)
            {
                Logger.Error("RemoveEventListener Failed Local Plugin {0} Event {1}: {2}",
                             targetPlugin.Descriptor.PluginId,
                             eventName,
                             exception);
            }
        }
        private void OnPluginRemoved(JavaScriptPlugin handler)
        {
            if (handler == null)
            {
                return;
            }

            // Only remove event listeners and parameter callbacks - locking inside JavaScriptPlugin means any
            // inflight calls will still complete, so leave those to be removed and disposed as per usual
            var retainedCallbacksAndListeners = _pendingCallbacks.RemoveWhere(info =>
                                                                              info.PluginId == handler.Descriptor.PluginId && info.IsRetained);

            foreach (var eventListener in retainedCallbacksAndListeners)
            {
                // Tell the remote plugin process that owns the listener or parameter callbacks to dispose of it
                var removeListenerMessage = new PluginMessage
                {
                    MessageId   = eventListener.RequestMessage.MessageId,
                    MessageType = PluginMessageType.RemoveRetained,
                    PluginId    = eventListener.RequestMessage.PluginId,
                    MemberName  = eventListener.RequestMessage.MemberName,
                    Data        = string.Empty,
                    BrowserId   = eventListener.RequestMessage.BrowserId,
                    ContextId   = eventListener.RequestMessage.ContextId
                };

                SendMessage(eventListener.Browser, removeListenerMessage);
                eventListener.Dispose();
            }
        }
Пример #4
0
        public static JavaScriptPlugin CreateFromType(PluginProcess pluginProcess, Type type, bool isKernel)
        {
            if (type == null)
            {
                return(null);
            }

            var pluginAttr = type.GetCustomAttributes(typeof(JavaScriptPluginAttribute), true);

            if (pluginAttr.Length == 0)
            {
                return(null);
            }

            var pAttr = pluginAttr[0] as JavaScriptPluginAttribute;

            if (pAttr == null)
            {
                return(null);
            }

            if (isKernel &&
                ((pluginProcess == PluginProcess.Browser && !pAttr.IsBrowserSide) ||
                 (pluginProcess == PluginProcess.Renderer && pAttr.IsBrowserSide)))
            {
                return(null);
            }

            var plugin = new JavaScriptPlugin(pluginProcess, Activator.CreateInstance(type), pAttr);

            return(plugin.IsValid ? plugin : null);
        }
        private BrowserCallInfo CreateAndAddCall(CefBrowser browser, PluginMessage pluginMessage,
                                                 JavaScriptPlugin handler, IJavaScriptParameterCallback parameterCallback)
        {
            var pluginId = handler != null ? handler.Descriptor.PluginId : null;
            var info     = new BrowserCallInfo(this, browser, pluginMessage, pluginId, parameterCallback);

            _pendingCallbacks.Add(info);
            return(info);
        }
        /// <summary>
        /// Invoke a local function.
        /// </summary>
        /// <param name="context">
        /// The current V8 context that is making the function call.
        /// </param>
        /// <param name="targetPlugin">
        /// The local plugin that owns the target method.
        /// </param>
        /// <param name="methodDescriptor">
        /// The method to invoke.
        /// </param>
        /// <param name="parameters">
        /// An interface for the local plugin to obtain the parameters from before invoking the function.
        /// </param>
        /// <param name="callback">
        /// Optional callback into V8.
        /// </param>
        public void InvokeFunction(
            CefV8Context context,
            JavaScriptPlugin targetPlugin,
            MethodDescriptor methodDescriptor,
            IJavaScriptParameters parameters,
            IV8Callback callback)
        {
            Logger.Info("InvokeFunction Local Plugin {0} Method {1}", targetPlugin.Descriptor.PluginId, methodDescriptor.MethodName);

            if (!EnsureOnRendererThread())
            {
                return;
            }

            if (!targetPlugin.IsValid)
            {
                Logger.Warn("InvokeFunction Local Plugin {0} is invalid", targetPlugin.Descriptor.PluginId);
                if (callback != null)
                {
                    callback.Invoke(this, context, null, CallInfo.ErrorCodeCallCanceled, CallInfo.ErrorCallCanceled);
                }
                return;
            }

            var parameterCallbackInfo = GetOrCreateParameterCallback(context, methodDescriptor, callback);

            var functionInvokeMessage = CreateMessage(context, targetPlugin.Descriptor, methodDescriptor.MethodName, callback);

            functionInvokeMessage.MessageId   = Guid.NewGuid();
            functionInvokeMessage.MessageType = PluginMessageType.FunctionInvoke;

            // Add the call info into the pending calls for the browser
            var info = methodDescriptor.HasCallbackParameter
                ? AddLocalCallback(functionInvokeMessage, null, parameterCallbackInfo)
                : AddLocalCallback(functionInvokeMessage, callback, null);

            try
            {
                targetPlugin.InvokeFunctionDirect(
                    null,
                    functionInvokeMessage.BrowserId,
                    functionInvokeMessage.FrameId,
                    functionInvokeMessage.ContextId,
                    methodDescriptor.MethodName,
                    parameters,
                    info);
            }
            catch (Exception ex)
            {
                LocalCallbackInvoked(info, null, -1, ex.Message);
                Logger.Error("InvokeFunction Failed Local Plugin {0} Method {1}: {2}",
                             targetPlugin.Descriptor.PluginId,
                             methodDescriptor.MethodName,
                             ex);
            }
        }
        public LocalV8Plugin(IV8PluginRouter router, IPluginContext pluginContext, JavaScriptPlugin plugin)
            : base(pluginContext, plugin.Descriptor)
        {
            if (router == null)
            {
                throw new ArgumentNullException("router");
            }

            _router = router;
            _plugin = plugin;
        }
        private void OnAddListener(CefBrowser browser, PluginMessage pluginMessage, JavaScriptPlugin handler)
        {
            var info = CreateAndAddCall(browser, pluginMessage, handler, null);

            if (!handler.IsValid)
            {
                RemoveAndCancelCall(info);
                return;
            }

            var handled = handler.AddEventListener(pluginMessage.MemberName, info);

            if (!handled)
            {
                RemoveAndCancelCall(info);
            }
        }
Пример #9
0
        private ResultItem ToResultItem(object item)
        {
            var resultItem = new ResultItem();

            if (item != null)
            {
                if (JavaScriptPlugin.IsDynamicPluginType(item.GetType()))
                {
                    resultItem.DynamicPlugin = _responseHandler.AddDynamicPlugin(item);
                }
                else
                {
                    resultItem.PlainData = JToken.FromObject(item, JsonSerializer.Create(ParagonJsonSerializer.Settings));
                }
            }
            return(resultItem);
        }
        public object AddApplicationPlugin(string type, Assembly assembly)
        {
            try
            {
                var t = assembly.GetType(type);
                if (t.GetCustomAttributes(typeof(JavaScriptPluginAttribute), true).Length == 0)
                {
                    Logger.Error("Plugin attribute not found on type: " + type);
                    return(null);
                }

                var plugin = JavaScriptPlugin.CreateFromType(_managerProcess, t, false);
                if (plugin == null)
                {
                    Logger.Error("Failed to create plugin instance for type: " + type);
                    return(null);
                }

                if (plugin.IsDynamic)
                {
                    Logger.Error("Dynamic application plugins are not allowed - type: " + type);
                    return(null);
                }

                if (!_localPluginsById.TryAdd(plugin.Descriptor.PluginId, plugin))
                {
                    Logger.Error("Failed to add application plugin for type: " + type);
                    return(null);
                }

                Logger.Info("Application plugin added successfully: " + plugin.Descriptor.PluginId);

                return(plugin.NativeObject);
            }
            catch (Exception e)
            {
                Logger.Error("Error adding application plugin: " + e);
                return(null);
            }
        }
        private void OnPluginRemoved(JavaScriptPlugin handler)
        {
            if (handler == null)
            {
                return;
            }

            Logger.Info("PluginRemoved ID {0}", handler.Descriptor.PluginId);

            // Only remove event listeners and parameter callbacks - locking inside JavaScriptPlugin means any
            // inflight calls will still complete, so leave those to be removed and disposed as per usual
            var pending = _pendingCallbacks.RemoveWhere(info => info.PluginId == handler.Descriptor.PluginId && info.IsRetained);

            foreach (var retainedCall in pending.OfType <LocalRenderCallInfo>())
            {
                if (retainedCall.IsEventListener)
                {
                    handler.RemoveEventListener(retainedCall.RequestMessage.MemberName, retainedCall);
                }
                retainedCall.Dispose();
            }
        }
        /// <summary>
        /// Add a local V8 callback to a local event as a listener.
        /// </summary>
        /// <param name="context">
        /// The current V8 context that is adding the listener.
        /// </param>
        /// <param name="targetPlugin">
        /// The local plugin that owns the event.
        /// </param>
        /// <param name="eventName">
        /// The name of the event to attach to.
        /// </param>
        /// <param name="callback">
        /// The callback to V8 to invoke when the event is raised.
        /// </param>
        public void AddEventListener(CefV8Context context, JavaScriptPlugin targetPlugin, string eventName, IV8Callback callback)
        {
            Logger.Info("AddEventListener Local Plugin {0} Event {1}", targetPlugin.Descriptor.PluginId, eventName);

            if (!EnsureOnRendererThread())
            {
                return;
            }

            if (!targetPlugin.IsValid)
            {
                Logger.Warn("AddEventListener Local Plugin {0} is invalid", targetPlugin.Descriptor.PluginId);
                if (callback != null)
                {
                    callback.Invoke(this, context, null, CallInfo.ErrorCodeCallCanceled, CallInfo.ErrorCallCanceled);
                }
                return;
            }

            var addListenerMessage = CreateMessage(context, targetPlugin.Descriptor, eventName, callback);

            addListenerMessage.MessageId   = callback.Identifier;
            addListenerMessage.MessageType = PluginMessageType.AddListener;

            // Add the call info into the pending calls for the browser
            var info = AddLocalCallback(addListenerMessage, callback, null);

            try
            {
                targetPlugin.AddEventListener(eventName, info);
            }
            catch (Exception ex)
            {
                // Remove listener from calls cache
                _pendingCallbacks.Remove(info);
                info.Dispose();
                Logger.Error("AddEventListener Failed Local Plugin {0} Event {1}: {2}", targetPlugin.Descriptor.PluginId, eventName, ex);
            }
        }
        public PluginDescriptor AddDynamicPlugin(object pluginObject)
        {
            // Remove any dead references that may exist.
            _dynamicPluginRefs.RemoveAll(r => !r.IsAlive);

            // Check if a dynamic plugin has already been created for the specified target.
            var pluginRef = _dynamicPluginRefs.FirstOrDefault(wr => wr.MatchesTarget(pluginObject));

            if (pluginRef != null)
            {
                // Found one, return it's descriptor.
                return(pluginRef.Plugin.Descriptor);
            }

            // No existing dynamic plugin found. Create one and store a reference to it.
            var dynPlugin = JavaScriptPlugin.CreateFromObject(PluginProcess.Browser, pluginObject);

            pluginRef = new DynamicPluginRef(pluginObject, dynPlugin);
            _dynamicPluginRefs.Add(pluginRef);

            // Add the dynamic plugin to the plugin manager.
            PluginManager.AddLocalPlugin(dynPlugin);
            return(dynPlugin.Descriptor);
        }
        /// <summary>
        /// Create an adapter for a local (in-process) plugin.
        /// </summary>
        /// <param name="router"></param>
        /// <param name="pluginContext"></param>
        /// <param name="jsPlugin"></param>
        /// <returns></returns>
        public static V8PluginAdapter CreateLocal(IV8PluginRouter router, IPluginContext pluginContext, JavaScriptPlugin jsPlugin)
        {
            if (jsPlugin == null)
            {
                return(null);
            }

            var plugin        = new LocalV8Plugin(router, pluginContext, jsPlugin);
            var pluginAdapter = new V8PluginAdapter(plugin);

            return(pluginAdapter);
        }
        private void OnInvokeFunction(CefBrowser browser, PluginMessage pluginMessage, JavaScriptPlugin handler)
        {
            var methodDescriptor = handler.Descriptor.Methods.Find(descriptor => descriptor.MethodName == pluginMessage.MemberName);
            IJavaScriptPluginCallback returnCallback    = null;
            BrowserCallInfo           parameterCallback = null;

            if (pluginMessage.V8CallbackId != Guid.Empty)
            {
                if (methodDescriptor.HasCallbackParameter)
                {
                    // Create a second stored callback info which represents the V8 callback function itself
                    // rather than the method that is being invoked now. This allows the callback function
                    // to be passed to and invoked by multiple native methods that accept a callback parameter.
                    parameterCallback = _pendingCallbacks.Get(pluginMessage.V8CallbackId);
                    if (parameterCallback == null)
                    {
                        var parameterCallbackMessage = new PluginMessage
                        {
                            MessageId    = pluginMessage.V8CallbackId,
                            MessageType  = PluginMessageType.ParameterCallback,
                            PluginId     = string.Empty,
                            MemberName   = string.Empty,
                            BrowserId    = pluginMessage.BrowserId,
                            ContextId    = pluginMessage.ContextId,
                            FrameId      = pluginMessage.FrameId,
                            V8CallbackId = Guid.Empty
                        };

                        parameterCallback = CreateAndAddCall(browser.Clone(), parameterCallbackMessage, null, null);
                    }
                }

                var returnCallInfo = CreateAndAddCall(browser, pluginMessage, handler, parameterCallback);
                if (!handler.IsValid)
                {
                    RemoveAndCancelCall(returnCallInfo);
                    return;
                }

                returnCallback = returnCallInfo;
            }

            JArray callArgs = null;

            if (!string.IsNullOrEmpty(pluginMessage.Data))
            {
                callArgs = JArray.Parse(pluginMessage.Data);
            }

            handler.InvokeFunction(
                _pluginManager,
                pluginMessage.BrowserId,
                pluginMessage.FrameId,
                pluginMessage.ContextId,
                pluginMessage.MemberName,
                new JArrayJavaScriptParameters(callArgs),
                returnCallback);
        }
        private void OnRemoveRetained(CefBrowser browser, PluginMessage pluginMessage, JavaScriptPlugin handler)
        {
            var info = _pendingCallbacks.Remove(pluginMessage.MessageId);

            if (info != null)
            {
                if (info.IsEventListener)
                {
                    handler.RemoveEventListener(pluginMessage.MemberName, info);
                }

                info.Dispose();
            }

            browser.Dispose();
        }
 public DynamicPluginRef(object target, JavaScriptPlugin plugin)
 {
     _targetRef = new WeakReference(target);
     _pluginRef = new WeakReference(plugin);
 }
 private void DynamicPluginDisposed(JavaScriptPlugin dynamicPlugin)
 {
     RemoveLocalPlugin(dynamicPlugin.Descriptor.PluginId);
 }
        protected CefV8Value ToCefV8Value(IV8PluginRouter router, object result)
        {
            if (result == null)
            {
                return(CefV8Value.CreateNull());
            }

            // VALUES FROM REMOTE PLUGINS
            var remoteResult = result as ResultData;

            if (remoteResult != null)
            {
                switch (remoteResult.DataType)
                {
                case ResultDataType.Scalar:
                    if (remoteResult.Items != null && remoteResult.Items.Count != 0)
                    {
                        return(ToCefV8Value(router, remoteResult.Items[0]));
                    }
                    return(CefV8Value.CreateNull());

                case ResultDataType.Array:
                {
                    var cefArray = CefV8Value.CreateArray(remoteResult.Items.Count);
                    if (remoteResult.Items != null)
                    {
                        for (var resultIndex = 0; resultIndex < remoteResult.Items.Count; ++resultIndex)
                        {
                            var cefValue = ToCefV8Value(router, remoteResult.Items[resultIndex]);
                            cefArray.SetValue(resultIndex, cefValue);
                        }
                    }
                    return(cefArray);
                }

                case ResultDataType.Dictionary:
                {
                    var cefObject = CefV8Value.CreateObject(null);
                    if (remoteResult.Items != null)
                    {
                        foreach (var dictionaryItem in remoteResult.Items)
                        {
                            if (string.IsNullOrEmpty(dictionaryItem.Name))
                            {
                                continue;
                            }
                            var cefValue = ToCefV8Value(router, dictionaryItem);
                            cefObject.SetValue(dictionaryItem.Name, cefValue, CefV8PropertyAttribute.None);
                        }
                    }
                    return(cefObject);
                }
                }
            }
            var resultItem = result as ResultItem;

            if (resultItem != null)
            {
                return(ToCefV8Value(router, (object)resultItem.DynamicPlugin ?? resultItem.PlainData));
            }
            var pluginObjectDescriptor = result as PluginDescriptor;

            if (pluginObjectDescriptor != null)
            {
                return(V8PluginAdapter.CreateRemote(router, _plugin.PluginContext, pluginObjectDescriptor).V8Object);
            }

            // VALUES FROM REMOTE OR LOCAL PLUGINS
            var plainData = result as JToken;

            if (plainData != null)
            {
                return(CefJsonValueConverter.ToCef(plainData));
            }

            // VALUES FROM LOCAL PLUGINS
            var localArray = result as object[];

            if (localArray != null)
            {
                var cefArray = CefV8Value.CreateArray(localArray.Length);
                for (var resultIndex = 0; resultIndex < localArray.Length; ++resultIndex)
                {
                    var cefValue = ToCefV8Value(router, localArray[resultIndex]);
                    cefArray.SetValue(resultIndex, cefValue);
                }
                return(cefArray);
            }
            var localPlugin = result as JavaScriptPlugin;

            if (localPlugin != null)
            {
                return(V8PluginAdapter.CreateLocal(router, _plugin.PluginContext, localPlugin).V8Object);
            }
            if (JavaScriptPlugin.IsDynamicPlugin(result))
            {
                var dynPlugin = JavaScriptPlugin.CreateFromObject(PluginProcess.Renderer, result);
                _plugin.PluginContext.PluginManager.AddLocalPlugin(dynPlugin);
                return(V8PluginAdapter.CreateLocal(router, _plugin.PluginContext, dynPlugin).V8Object);
            }

            // local C# POCO
            return(CefNativeValueConverter.ToCef(result));
        }