/// <summary> /// Remove a local V8 callback from a remote event. /// </summary> /// <param name="context"> /// The current V8 context that is removing the listener. /// </param> /// <param name="targetPlugin"> /// The remote plugin that owns the event. /// </param> /// <param name="eventName"> /// The name of the event to detach from. /// </param> /// <param name="callback"> /// The callback to remove.</param> public void RemoveEventListener(CefV8Context context, PluginDescriptor targetPlugin, string eventName, IV8Callback callback) { Logger.Info("RemoveEventListener Remote Plugin {0} Event {1}", targetPlugin.PluginId, eventName); if (!EnsureOnRendererThread()) { return; } var callId = callback.Identifier; var info = _pendingCallbacks.Remove(callId); info.Dispose(); var removeListenerMessage = CreateMessage(context, targetPlugin, eventName, callback); // Use identifier from callback as ID for listener message. // Remote process will find and remove listener based on the MessageId. removeListenerMessage.MessageId = callId; removeListenerMessage.MessageType = PluginMessageType.RemoveRetained; removeListenerMessage.Data = string.Empty; // Send the request message to the browser try { using (var browser = context.GetBrowser()) { SendMessage(browser, removeListenerMessage); } } catch (Exception ex) { Logger.Error("RemoveEventListener Failed Remote Plugin {0} Event {1}: {2}", targetPlugin.PluginId, eventName, ex); } }
public void Execute(CallbackExecution <CefValue> execution) { CefV8Exception exception = null; using (new ContextHelper(context)) { var cefV8Values = execution.Parameters.Select(s => (CefV8Value)v8Serializer.Deserialize(s, typeof(CefV8Value))).ToArray(); var result = function.ExecuteFunction(null, cefV8Values); var browser = context.GetBrowser(); if (result == null && function.HasException) { exception = function.GetException(); function.ClearException(); } if (promiseService.IsPromise(result, context)) { promiseService.Then(result, context, a => CallbackDone(a, browser, execution.ExecutionId)); } else { CallbackDone(new PromiseResult { Success = result != null, Result = result, Error = exception?.Message }, browser, execution.ExecutionId); } } }
public static int GetBrowserId(this CefV8Context context) { if (context == null) { throw new ArgumentNullException("context"); } using (var browser = context.GetBrowser()) { return(browser.Identifier); } }
/// <summary> /// Invoke a remote function. /// </summary> /// <param name="context"> /// The current V8 context that is making the function call. /// </param> /// <param name="targetPlugin"> /// The remote plugin that owns the target method. /// </param> /// <param name="methodDescriptor"> /// The method to invoke. /// </param> /// <param name="parameters"> /// The parameters to pass to the remote function. /// </param> /// <param name="callback"> /// Optional callback into V8. /// </param> public void InvokeFunction( CefV8Context context, PluginDescriptor targetPlugin, MethodDescriptor methodDescriptor, JArray parameters, IV8Callback callback) { Logger.Debug("InvokeFunction Remote Plugin {0} Method {1}", targetPlugin.PluginId, methodDescriptor.MethodName); if (!EnsureOnRendererThread()) { return; } GetOrCreateParameterCallback(context, methodDescriptor, callback); var functionInvokeMessage = CreateMessage(context, targetPlugin, methodDescriptor.MethodName, callback); functionInvokeMessage.MessageId = Guid.NewGuid(); functionInvokeMessage.MessageType = PluginMessageType.FunctionInvoke; functionInvokeMessage.Data = parameters != null?parameters.ToString() : string.Empty; // Add the call info into the pending calls for the browser if the return type // is not void or the method has a callback arg. // TODO: We should verify that the signature of the JavaScript call matches the plugin method and throw an exception for invalid invocations. if (!methodDescriptor.IsVoid || methodDescriptor.HasCallbackParameter || callback != null) { AddRemoteCallback(functionInvokeMessage, methodDescriptor.HasCallbackParameter ? null : callback); } try { using (var browser = context.GetBrowser()) { // Send the request message to the browser SendMessage(browser, functionInvokeMessage); } } catch (Exception ex) { // If the request could not be sent, remove the call from the list var error = new ResultData { ErrorCode = -1, Error = ex.Message }; OnBrowserCallbackInvokeReceived(functionInvokeMessage, error); Logger.Error("InvokeFunction Failed Remote Plugin {0} Method {1}: {2}", targetPlugin.PluginId, methodDescriptor.MethodName, ex); } }
protected override void DoAsyncWork(CefV8Context ctx, PromiseTask promiseTask) { int promiseId = pendingPromises.AddPromise(promiseTask); var browser = ctx.GetBrowser(); var frame = browser.GetMainFrame(); var msg = CefProcessMessage.Create("openFileRequest"); msg.Arguments.SetInt(0, (int)promiseId); // This kicks off the RFC chain. frame.SendProcessMessage(CefProcessId.Browser, msg); }
/// <summary> /// Add a local V8 callback to a remote event as a listener. /// </summary> /// <param name="context"> /// The current V8 context that is adding the listener. /// </param> /// <param name="targetPlugin"> /// The remote 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 remote event is raised. /// </param> public void AddEventListener(CefV8Context context, PluginDescriptor targetPlugin, string eventName, IV8Callback callback) { Logger.Info("AddEventListener Remote Plugin {0} Event {1}", targetPlugin.PluginId, eventName); if (!EnsureOnRendererThread()) { return; } var addListenerMessage = CreateMessage(context, targetPlugin, eventName, callback); // Use identifier from callback as ID for listener message. // When remote event fired, renderer will receive an EventFired message with this same ID as its MessageId. addListenerMessage.MessageId = callback.Identifier; addListenerMessage.MessageType = PluginMessageType.AddListener; addListenerMessage.Data = string.Empty; // Add the call info into the pending calls for the browser var info = AddRemoteCallback(addListenerMessage, callback); // Send the request message to the browser try { using (var browser = context.GetBrowser()) { SendMessage(browser, addListenerMessage); } } catch (Exception ex) { // If the request could not be sent, remove the call from the list _pendingCallbacks.Remove(info); info.Dispose(); Logger.Error("AddEventListener Failed Remote Plugin {0} Event {1}: {2}", targetPlugin.PluginId, eventName, ex); } }
protected override bool Execute(string name, CefV8Value obj, CefV8Value[] arguments, out CefV8Value returnValue, out string exception) { if (name == _config.JSQueryFunction) { if (arguments.Length != 1 || !arguments[0].IsObject) { returnValue = null; exception = "Invalid arguments; expecting a single object"; return(true); } var arg = arguments[0]; var requestVal = arg.GetValue(CefMessageRouter.MemberRequest); if (requestVal == null || !requestVal.IsString) { returnValue = null; exception = "Invalid arguments; object member '" + CefMessageRouter.MemberRequest + "' is required and must " + "have type string"; return(true); } CefV8Value successVal = null; if (arg.HasValue(CefMessageRouter.MemberOnSuccess)) { successVal = arg.GetValue(CefMessageRouter.MemberOnSuccess); if (!successVal.IsFunction) { returnValue = null; exception = "Invalid arguments; object member '" + CefMessageRouter.MemberOnSuccess + "' must have type " + "function"; return(true); } } CefV8Value failureVal = null; if (arg.HasValue(CefMessageRouter.MemberOnFailure)) { failureVal = arg.GetValue(CefMessageRouter.MemberOnFailure); if (!failureVal.IsFunction) { returnValue = null; exception = "Invalid arguments; object member '" + CefMessageRouter.MemberOnFailure + "' must have type " + "function"; return(true); } } CefV8Value persistentVal = null; if (arg.HasValue(CefMessageRouter.MemberPersistent)) { persistentVal = arg.GetValue(CefMessageRouter.MemberPersistent); if (!persistentVal.IsBool) { returnValue = null; exception = "Invalid arguments; object member '" + CefMessageRouter.MemberPersistent + "' must have type " + "boolean"; return(true); } } var context = CefV8Context.GetCurrentContext(); var contextId = GetIDForContext(context); var frameId = context.GetFrame().Identifier; var persistent = (persistentVal != null && persistentVal.GetBoolValue()); var requestId = _router.SendQuery(context.GetBrowser(), frameId, contextId, requestVal.GetStringValue(), persistent, successVal, failureVal); returnValue = CefV8Value.CreateInt(requestId); exception = null; return(true); } else if (name == _config.JSCancelFunction) { if (arguments.Length != 1 || !arguments[0].IsInt) { returnValue = null; exception = "Invalid arguments; expecting a single integer"; return(true); } var result = false; var requestId = arguments[0].GetIntValue(); if (requestId != CefMessageRouter.ReservedId) { CefV8Context context = CefV8Context.GetCurrentContext(); var contextId = GetIDForContext(context); var frameId = context.GetFrame().Identifier; result = _router.SendCancel(context.GetBrowser(), frameId, contextId, requestId); } returnValue = CefV8Value.CreateBool(result); exception = null; return(true); } returnValue = null; exception = null; return(false); }
protected override bool Execute(string name, CefV8Value obj, CefV8Value[] arguments, out CefV8Value returnValue, out string exception) { if (name == _name) { var bid = _context.GetBrowser()?.Identifier ?? 0; var fid = _context.GetFrame()?.Identifier ?? 0; var request = MessageBridgeRequest.Create(JavaScriptCommunicationBridge.EXECUTE_JAVASCRIPT_FUNCTION, bid, fid, _context.GetHashCode()); request.Arguments.Add(MessageValue.CreateString(_parentKey)); request.Arguments.Add(MessageValue.CreateString(_name)); var args = JavaScriptValue.CreateArray(); var index = 0; foreach (var arg in arguments) { var value = arg.ToJSValue(); if (value != null) { args.SetValue(index++, value); } } request.Arguments.Add(MessageValue.CreateString(args.ToDefinition())); var guid = Guid.NewGuid(); request.Arguments.Add(MessageValue.CreateString($"{guid}")); var response = _bridge.SendExecutionRequest(request); if (response.IsSuccess) { if (_functionInfo.IsAsync) { var callback = CefV8Value.CreateObject(); var successFunc = CefV8Value.CreateFunction("success", new JavaScriptBridgeFunctionCallbackHandler(/*_parentKey, _name,*/ guid, _context)); var errorFunc = CefV8Value.CreateFunction("error", new JavaScriptBridgeFunctionCallbackHandler(/*_parentKey, _name,*/ guid, _context)); callback.SetValue("success", successFunc); callback.SetValue("error", errorFunc); returnValue = callback; exception = null; } else { var retval = JavaScriptValue.FromJson(response.Arguments[0].GetString())?.ToCefV8Value(); exception = null; if (retval != null) { returnValue = retval; } else { returnValue = CefV8Value.CreateUndefined(); } } } else { exception = response.ExceptionMessage; returnValue = null; } return(true); } returnValue = null; exception = $"{name} is not defined."; return(true); }