init lserp cs 5.0
This commit is contained in:
@@ -0,0 +1,535 @@
|
||||
namespace Cef3.Wrapper
|
||||
{
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Text;
|
||||
|
||||
using BrowserRequestInfoMap = CefBrowserInfoMap<System.Collections.Generic.KeyValuePair<int, int>, Cef3.Wrapper.CefMessageRouterRendererSide.RequestInfo>;
|
||||
|
||||
/// <summary>
|
||||
/// Implements the renderer side of query routing. The methods of this class must
|
||||
/// be called on the render process main thread.
|
||||
/// </summary>
|
||||
public sealed class CefMessageRouterRendererSide
|
||||
{
|
||||
#region V8HandlerImpl
|
||||
|
||||
private sealed class V8HandlerImpl : CefV8Handler
|
||||
{
|
||||
private readonly CefMessageRouterRendererSide _router;
|
||||
private readonly CefMessageRouterConfig _config;
|
||||
private int _contextId;
|
||||
|
||||
public V8HandlerImpl(CefMessageRouterRendererSide router, CefMessageRouterConfig config)
|
||||
{
|
||||
_router = router;
|
||||
_config = config;
|
||||
_contextId = CefMessageRouter.ReservedId;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Don't create the context ID until it's actually needed.
|
||||
private int GetIDForContext(CefV8Context context)
|
||||
{
|
||||
if (_contextId == CefMessageRouter.ReservedId)
|
||||
_contextId = _router.CreateIDForContext(context);
|
||||
return _contextId;
|
||||
}
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
private readonly CefMessageRouterConfig _config;
|
||||
|
||||
private readonly string _queryMessageName;
|
||||
private readonly string _cancelMessageName;
|
||||
|
||||
private readonly CefMessageRouter.IdGeneratorInt32 _contextIdGenerator = new CefMessageRouter.IdGeneratorInt32();
|
||||
private readonly CefMessageRouter.IdGeneratorInt32 _requestIdGenerator = new CefMessageRouter.IdGeneratorInt32();
|
||||
|
||||
// Map of (request ID, context ID) to RequestInfo for pending queries. An
|
||||
// entry is added when a request is initiated via the bound function and
|
||||
// removed when either the request completes, is canceled via the bound
|
||||
// function, or the associated context is released.
|
||||
private readonly BrowserRequestInfoMap _browserRequestInfoMap = new BrowserRequestInfoMap();
|
||||
|
||||
// Map of context ID to CefV8Context for existing contexts. An entry is added
|
||||
// when a bound function is executed for the first time in the context and
|
||||
// removed when the context is released.
|
||||
private readonly Dictionary<int, CefV8Context> _contextMap = new Dictionary<int, CefV8Context>();
|
||||
|
||||
/// <summary>
|
||||
/// Create a new router with the specified configuration.
|
||||
/// </summary>
|
||||
public CefMessageRouterRendererSide(CefMessageRouterConfig config)
|
||||
{
|
||||
if (!config.Validate()) throw new ArgumentException("Invalid configuration.");
|
||||
|
||||
_config = config;
|
||||
_queryMessageName = config.JSQueryFunction + CefMessageRouter.MessageSuffix;
|
||||
_cancelMessageName = config.JSCancelFunction + CefMessageRouter.MessageSuffix;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new router with the specified configuration.
|
||||
/// </summary>
|
||||
public static CefMessageRouterRendererSide Create(CefMessageRouterConfig config)
|
||||
{
|
||||
return new CefMessageRouterRendererSide(config);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of queries currently pending for the specified |browser|
|
||||
/// and/or |context|. Either or both values may be empty.
|
||||
/// </summary>
|
||||
public int GetPendingCount(CefBrowser browser = null, CefV8Context context = null)
|
||||
{
|
||||
Helpers.RequireRendererThread();
|
||||
|
||||
if (_browserRequestInfoMap.IsEmpty) return 0;
|
||||
|
||||
if (context != null)
|
||||
{
|
||||
var contextId = GetIDForContext(context, false);
|
||||
if (contextId == CefMessageRouter.ReservedId)
|
||||
return 0; // Nothing associated with the specified context.
|
||||
|
||||
int count = 0;
|
||||
BrowserRequestInfoMap.Visitor visitor = (int browserId, KeyValuePair<int, int> infoId, RequestInfo info, ref bool remove) =>
|
||||
{
|
||||
if (infoId.Key == contextId) count++;
|
||||
return true;
|
||||
};
|
||||
|
||||
if (browser != null)
|
||||
{
|
||||
// Count requests associated with the specified browser.
|
||||
_browserRequestInfoMap.FindAll(browser.Identifier, visitor);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Count all requests for all browsers.
|
||||
_browserRequestInfoMap.FindAll(visitor);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
else if (browser != null)
|
||||
{
|
||||
return _browserRequestInfoMap.Count(browser.Identifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
return _browserRequestInfoMap.Count();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#region The below methods should be called from other CEF handlers. They must be called exactly as documented for the router to function correctly.
|
||||
|
||||
/// <summary>
|
||||
/// Call from CefRenderProcessHandler::OnContextCreated. Registers the
|
||||
/// JavaScripts functions with the new context.
|
||||
/// </summary>
|
||||
public void OnContextCreated(CefBrowser browser, CefFrame frame, CefV8Context context)
|
||||
{
|
||||
Helpers.RequireRendererThread();
|
||||
|
||||
// Register function handlers with the 'window' object.
|
||||
var window = context.GetGlobal();
|
||||
|
||||
var handler = new V8HandlerImpl(this, _config);
|
||||
CefV8PropertyAttribute attributes = CefV8PropertyAttribute.ReadOnly | CefV8PropertyAttribute.DontEnum | CefV8PropertyAttribute.DontDelete;
|
||||
|
||||
// Add the query function.
|
||||
var queryFunc = CefV8Value.CreateFunction(_config.JSQueryFunction, handler);
|
||||
window.SetValue(_config.JSQueryFunction, queryFunc, attributes);
|
||||
|
||||
// Add the cancel function.
|
||||
var cancelFunc = CefV8Value.CreateFunction(_config.JSCancelFunction, handler);
|
||||
window.SetValue(_config.JSCancelFunction, cancelFunc, attributes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call from CefRenderProcessHandler::OnContextReleased. Any pending queries
|
||||
/// associated with the released context will be canceled and
|
||||
/// Handler::OnQueryCanceled will be called in the browser process.
|
||||
/// </summary>
|
||||
public void OnContextReleased(CefBrowser browser, CefFrame frame, CefV8Context context)
|
||||
{
|
||||
Helpers.RequireRendererThread();
|
||||
|
||||
// Get the context ID and remove the context from the map.
|
||||
var contextId = GetIDForContext(context, true);
|
||||
if (contextId != CefMessageRouter.ReservedId)
|
||||
{
|
||||
// Cancel all pending requests for the context.
|
||||
SendCancel(browser, frame.Identifier, contextId, CefMessageRouter.ReservedId);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Call from CefRenderProcessHandler::OnProcessMessageReceived. Returns true
|
||||
/// if the message is handled by this router or false otherwise.
|
||||
/// </summary>
|
||||
public bool OnProcessMessageReceived(CefBrowser browser, CefProcessId sourceProcess, CefProcessMessage message)
|
||||
{
|
||||
Helpers.RequireRendererThread();
|
||||
|
||||
var messageName = message.Name;
|
||||
if (messageName == _queryMessageName)
|
||||
{
|
||||
var args = message.Arguments;
|
||||
Debug.Assert(args.Count > 3);
|
||||
|
||||
var contextId = args.GetInt(0);
|
||||
var requestId = args.GetInt(1);
|
||||
var isSuccess = args.GetBool(2);
|
||||
|
||||
if (isSuccess)
|
||||
{
|
||||
Debug.Assert(args.Count == 4);
|
||||
string response = args.GetString(3);
|
||||
Helpers.PostTaskUncertainty(CefThreadId.Renderer,
|
||||
Helpers.Apply(this.ExecuteSuccessCallback, browser.Identifier, contextId, requestId, response)
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert(args.Count == 5);
|
||||
var errorCode = args.GetInt(3);
|
||||
string errorMessage = args.GetString(4);
|
||||
Helpers.PostTaskUncertainty(CefThreadId.Renderer,
|
||||
Helpers.Apply(this.ExecuteFailureCallback, browser.Identifier, contextId, requestId, errorCode, errorMessage)
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// Structure representing a pending request.
|
||||
internal class RequestInfo
|
||||
{
|
||||
// True if the request is persistent.
|
||||
public bool Persistent;
|
||||
|
||||
// Success callback function. May be NULL.
|
||||
public CefV8Value SuccessCallback;
|
||||
|
||||
// Failure callback function. May be NULL.
|
||||
public CefV8Value FailureCallback;
|
||||
|
||||
internal void Dispose()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Retrieve a RequestInfo object from the map based on the renderer-side
|
||||
// IDs. If |always_remove| is true then the RequestInfo object will always be
|
||||
// removed from the map. Othewise, the RequestInfo object will only be removed
|
||||
// if the query is non-persistent. If |removed| is true the caller is
|
||||
// responsible for deleting the returned QueryInfo object.
|
||||
private RequestInfo GetRequestInfo(int browserId, int requestId, int contextId, bool alwaysRemove, ref bool removed)
|
||||
{
|
||||
var removedTemp = false;
|
||||
BrowserRequestInfoMap.Visitor visitor = (int vBrowserId, KeyValuePair<int, int> vInfoId, RequestInfo vInfo, ref bool vRemove) =>
|
||||
{
|
||||
vRemove = removedTemp = (alwaysRemove || !vInfo.Persistent);
|
||||
return true;
|
||||
};
|
||||
|
||||
var info = _browserRequestInfoMap.Find(browserId, new KeyValuePair<int, int>(requestId, contextId), visitor);
|
||||
if (info != null) removed = removedTemp;
|
||||
return info;
|
||||
}
|
||||
|
||||
// Returns the new request ID.
|
||||
private int SendQuery(CefBrowser browser, long frameId, int contextId, string request, bool persistent, CefV8Value successCallback, CefV8Value failureCallback)
|
||||
{
|
||||
Helpers.RequireRendererThread();
|
||||
|
||||
var requestId = _requestIdGenerator.GetNextId();
|
||||
|
||||
var info = new RequestInfo
|
||||
{
|
||||
Persistent = persistent,
|
||||
SuccessCallback = successCallback,
|
||||
FailureCallback = failureCallback,
|
||||
};
|
||||
_browserRequestInfoMap.Add(browser.Identifier, new KeyValuePair<int, int>(contextId, requestId), info);
|
||||
|
||||
var message = CefProcessMessage.Create(_queryMessageName);
|
||||
var args = message.Arguments;
|
||||
args.SetInt(0, Helpers.Int64GetLow(frameId));
|
||||
args.SetInt(1, Helpers.Int64GetHigh(frameId));
|
||||
args.SetInt(2, contextId);
|
||||
args.SetInt(3, requestId);
|
||||
args.SetString(4, request);
|
||||
args.SetBool(5, persistent);
|
||||
|
||||
browser.SendProcessMessage(CefProcessId.Browser, message);
|
||||
|
||||
args.Dispose();
|
||||
message.Dispose();
|
||||
|
||||
return requestId;
|
||||
}
|
||||
|
||||
// If |requestId| is kReservedId all requests associated with |contextId|
|
||||
// will be canceled, otherwise only the specified |requestId| will be
|
||||
// canceled. Returns true if any request was canceled.
|
||||
private bool SendCancel(CefBrowser browser, long frameId, int contextId, int requestId)
|
||||
{
|
||||
Helpers.RequireRendererThread();
|
||||
|
||||
var browserId = browser.Identifier;
|
||||
|
||||
int cancelCount = 0;
|
||||
if (requestId != CefMessageRouter.ReservedId)
|
||||
{
|
||||
// Cancel a single request.
|
||||
bool removed = false;
|
||||
var info = GetRequestInfo(browserId, contextId, requestId, true, ref removed);
|
||||
if (info != null)
|
||||
{
|
||||
Debug.Assert(removed);
|
||||
info.Dispose();
|
||||
cancelCount = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cancel all requests with the specified context ID.
|
||||
BrowserRequestInfoMap.Visitor visitor = (int vBrowserId, KeyValuePair<int, int> vInfoId, RequestInfo vInfo, ref bool vRemove) =>
|
||||
{
|
||||
if (vInfoId.Key == contextId)
|
||||
{
|
||||
vRemove = true;
|
||||
vInfo.Dispose();
|
||||
cancelCount++;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
_browserRequestInfoMap.FindAll(browserId, visitor);
|
||||
}
|
||||
|
||||
if (cancelCount > 0)
|
||||
{
|
||||
var message = CefProcessMessage.Create(_cancelMessageName);
|
||||
|
||||
var args = message.Arguments;
|
||||
args.SetInt(0, contextId);
|
||||
args.SetInt(1, requestId);
|
||||
|
||||
browser.SendProcessMessage(CefProcessId.Browser, message);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Execute the onSuccess JavaScript callback.
|
||||
private void ExecuteSuccessCallback(int browserId, int contextId, int requestId, string response)
|
||||
{
|
||||
Helpers.RequireRendererThread();
|
||||
|
||||
bool removed = false;
|
||||
var info = GetRequestInfo(browserId, contextId, requestId, false, ref removed);
|
||||
if (info == null) return;
|
||||
|
||||
var context = GetContextByID(contextId);
|
||||
if (context != null && info.SuccessCallback != null)
|
||||
{
|
||||
var args = new[] { CefV8Value.CreateString(response) };
|
||||
info.SuccessCallback.ExecuteFunctionWithContext(context, null, args);
|
||||
}
|
||||
|
||||
if (removed) info.Dispose();
|
||||
}
|
||||
|
||||
// Execute the onFailure JavaScript callback.
|
||||
void ExecuteFailureCallback(int browserId, int contextId, int requestId,
|
||||
int errorCode, string errorMessage)
|
||||
{
|
||||
Helpers.RequireRendererThread();
|
||||
|
||||
bool removed = false;
|
||||
var info = GetRequestInfo(browserId, contextId, requestId, true, ref removed);
|
||||
if (info == null) return;
|
||||
|
||||
var context = GetContextByID(contextId);
|
||||
if (context != null && info.FailureCallback != null)
|
||||
{
|
||||
var args = new[] {
|
||||
CefV8Value.CreateInt(errorCode),
|
||||
CefV8Value.CreateString(errorMessage)
|
||||
};
|
||||
info.FailureCallback.ExecuteFunctionWithContext(context, null, args);
|
||||
}
|
||||
|
||||
Debug.Assert(removed);
|
||||
info.Dispose();
|
||||
}
|
||||
|
||||
private int CreateIDForContext(CefV8Context context)
|
||||
{
|
||||
Helpers.RequireRendererThread();
|
||||
|
||||
// The context should not already have an associated ID.
|
||||
Debug.Assert(GetIDForContext(context, false) == CefMessageRouter.ReservedId);
|
||||
|
||||
var contextId = _contextIdGenerator.GetNextId();
|
||||
_contextMap.Add(contextId, context);
|
||||
return contextId;
|
||||
}
|
||||
|
||||
// Retrieves the existing ID value associated with the specified |context|.
|
||||
// If |remove| is true the context will also be removed from the map.
|
||||
private int GetIDForContext(CefV8Context context, bool remove)
|
||||
{
|
||||
Helpers.RequireRendererThread();
|
||||
|
||||
int contextId = CefMessageRouter.ReservedId;
|
||||
int? removeContextId = null;
|
||||
foreach (var kv in _contextMap)
|
||||
{
|
||||
if (kv.Value.IsSame(context))
|
||||
{
|
||||
contextId = kv.Key;
|
||||
if (remove)
|
||||
{
|
||||
removeContextId = contextId;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (removeContextId.HasValue) _contextMap.Remove(removeContextId.Value);
|
||||
|
||||
return contextId;
|
||||
}
|
||||
|
||||
private CefV8Context GetContextByID(int contextId)
|
||||
{
|
||||
Helpers.RequireRendererThread();
|
||||
|
||||
CefV8Context context;
|
||||
if (_contextMap.TryGetValue(contextId, out context)) return context;
|
||||
else return null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user