namespace Xilium.CefGlue { using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using Xilium.CefGlue.Interop; using System.Threading; using System.IO; /// /// Class used to implement a custom request handler interface. The methods of /// this class will be called on the IO thread unless otherwise indicated. /// public abstract unsafe partial class CefResourceHandler { /// /// set to 1 for keeping, otherwise 0 /// private int _keepObject; public void KeepObject() { if (Interlocked.CompareExchange(ref _keepObject, 1, 0) == 0) { add_ref(_self); } } public void ReleaseObject() { if (Interlocked.CompareExchange(ref _keepObject, 0, 1) == 1) { release(_self); } } private int open(cef_resource_handler_t* self, cef_request_t* request, int* handle_request, cef_callback_t* callback) { CheckSelf(self); var m_request = CefRequest.FromNative(request); var m_callback = CefCallback.FromNative(callback); var m_result = Open(m_request, out var m_handleRequest, m_callback); *handle_request = m_handleRequest ? 1 : 0; return m_result ? 1 : 0; } /// /// Open the response stream. To handle the request immediately set /// |handle_request| to true and return true. To decide at a later time set /// |handle_request| to false, return true, and execute |callback| to continue /// or cancel the request. To cancel the request immediately set /// |handle_request| to true and return false. This method will be called in /// sequence but not from a dedicated thread. For backwards compatibility set /// |handle_request| to false and return false and the ProcessRequest method /// will be called. /// protected abstract bool Open(CefRequest request, out bool handleRequest, CefCallback callback); private int process_request(cef_resource_handler_t* self, cef_request_t* request, cef_callback_t* callback) { CheckSelf(self); var m_request = CefRequest.FromNative(request); var m_callback = CefCallback.FromNative(callback); #pragma warning disable CS0618 // Type or member is obsolete var result = ProcessRequest(m_request, m_callback); #pragma warning restore CS0618 // Type or member is obsolete return result ? 1 : 0; } /// /// Begin processing the request. To handle the request return true and call /// CefCallback::Continue() once the response header information is available /// (CefCallback::Continue() can also be called from inside this method if /// header information is available immediately). To cancel the request return /// false. /// WARNING: This method is deprecated. Use Open instead. /// [Obsolete("This method is deprecated. Use Open instead.")] protected virtual bool ProcessRequest(CefRequest request, CefCallback callback) { request.Dispose(); callback.Dispose(); return false; } private void get_response_headers(cef_resource_handler_t* self, cef_response_t* response, long* response_length, cef_string_t* redirectUrl) { CheckSelf(self); var m_response = CefResponse.FromNative(response); long m_responseLength; string m_redirectUrl; GetResponseHeaders(m_response, out m_responseLength, out m_redirectUrl); *response_length = m_responseLength; if (!string.IsNullOrEmpty(m_redirectUrl)) { cef_string_t.Copy(m_redirectUrl, redirectUrl); } } /// /// Retrieve response header information. If the response length is not known /// set |response_length| to -1 and ReadResponse() will be called until it /// returns false. If the response length is known set |response_length| /// to a positive value and ReadResponse() will be called until it returns /// false or the specified number of bytes have been read. Use the |response| /// object to set the mime type, http status code and other optional header /// values. To redirect the request to a new URL set |redirectUrl| to the new /// URL. |redirectUrl| can be either a relative or fully qualified URL. /// It is also possible to set |response| to a redirect http status code /// and pass the new URL via a Location header. Likewise with |redirectUrl| it /// is valid to set a relative or fully qualified URL as the Location header /// value. If an error occured while setting up the request you can call /// SetError() on |response| to indicate the error condition. /// protected abstract void GetResponseHeaders(CefResponse response, out long responseLength, out string redirectUrl); private int skip(cef_resource_handler_t* self, long bytes_to_skip, long* bytes_skipped, cef_resource_skip_callback_t* callback) { CheckSelf(self); var m_callback = CefResourceSkipCallback.FromNative(callback); var m_result = Skip(bytes_to_skip, out var m_bytesSkipped, m_callback); *bytes_skipped = m_bytesSkipped; return m_result ? 1 : 0; } /// /// Skip response data when requested by a Range header. Skip over and discard /// |bytes_to_skip| bytes of response data. If data is available immediately /// set |bytes_skipped| to the number of bytes skipped and return true. To /// read the data at a later time set |bytes_skipped| to 0, return true and /// execute |callback| when the data is available. To indicate failure set /// |bytes_skipped| to < 0 (e.g. -2 for ERR_FAILED) and return false. This /// method will be called in sequence but not from a dedicated thread. /// protected abstract bool Skip(long bytesToSkip, out long bytesSkipped, CefResourceSkipCallback callback); private int read(cef_resource_handler_t* self, void* data_out, int bytes_to_read, int* bytes_read, cef_resource_read_callback_t* callback) { CheckSelf(self); var m_callback = CefResourceReadCallback.FromNative(callback); using (var m_stream = new UnmanagedMemoryStream((byte*)data_out, bytes_to_read, bytes_to_read, FileAccess.Write)) { var m_result = Read(m_stream, bytes_to_read, out var m_bytesRead, m_callback); *bytes_read = m_bytesRead; return m_result ? 1 : 0; } } /// /// Read response data. If data is available immediately copy up to /// |bytes_to_read| bytes into |response|, set |bytes_read| to the number of /// bytes copied, and return true. To read the data at a later time keep a /// pointer to |data_out|, set |bytes_read| to 0, return true and execute /// |callback| when the data is available (|response| will remain valid until /// the callback is executed). To indicate response completion set |bytes_read| /// to 0 and return false. To indicate failure set |bytes_read| to < 0 (e.g. -2 /// for ERR_FAILED) and return false. This method will be called in sequence /// but not from a dedicated thread. For backwards compatibility set /// |bytes_read| to -1 and return false and the ReadResponse method will be /// called. /// protected abstract bool Read(Stream response, int bytesToRead, out int bytesRead, CefResourceReadCallback callback); private int read_response(cef_resource_handler_t* self, void* data_out, int bytes_to_read, int* bytes_read, cef_callback_t* callback) { CheckSelf(self); var m_callback = CefCallback.FromNative(callback); using (var m_stream = new UnmanagedMemoryStream((byte*)data_out, bytes_to_read, bytes_to_read, FileAccess.Write)) { int m_bytesRead; #pragma warning disable CS0618 // Type or member is obsolete var result = ReadResponse(m_stream, bytes_to_read, out m_bytesRead, m_callback); #pragma warning restore CS0618 // Type or member is obsolete *bytes_read = m_bytesRead; return result ? 1 : 0; } } /// /// Read response data. If data is available immediately copy up to /// |bytes_to_read| bytes into |data_out|, set |bytes_read| to the number of /// bytes copied, and return true. To read the data at a later time set /// |bytes_read| to 0, return true and call CefCallback::Continue() when the /// data is available. To indicate response completion return false. /// WARNING: This method is deprecated. Use Skip and Read instead. /// [Obsolete("This method is deprecated. Use Skip and Read instead.")] protected virtual bool ReadResponse(Stream response, int bytesToRead, out int bytesRead, CefCallback callback) { bytesRead = 0; return false; } private void cancel(cef_resource_handler_t* self) { CheckSelf(self); Cancel(); } /// /// Request processing has been canceled. /// protected abstract void Cancel(); } }