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(); } }
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); } }
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)); }