BrowsingContextExtensions
A set of extensions for the browsing context.
using AngleSharp.Dom;
using AngleSharp.Dom.Html;
using AngleSharp.Dom.Svg;
using AngleSharp.Dom.Xml;
using AngleSharp.Extensions;
using AngleSharp.Network;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
namespace AngleSharp
{
[DebuggerStepThrough]
public static class BrowsingContextExtensions
{
public class VirtualResponse : IResponse, IDisposable
{
private Url address;
private HttpStatusCode status;
private Dictionary<string, string> headers;
private TextSource source;
private Stream content;
private bool dispose;
Url IResponse.Address {
get {
return address;
}
}
Stream IResponse.Content {
get {
return content;
}
}
IDictionary<string, string> IResponse.Headers {
get {
return headers;
}
}
HttpStatusCode IResponse.StatusCode {
get {
return status;
}
}
public VirtualResponse()
{
address = Url.Create("http://localhost/");
status = HttpStatusCode.OK;
headers = new Dictionary<string, string>();
content = Stream.Null;
source = null;
dispose = false;
}
public VirtualResponse Address(Url url)
{
address = url;
return this;
}
public VirtualResponse Address(string address)
{
return Address(Url.Create(address ?? string.Empty));
}
public VirtualResponse Address(Uri url)
{
return Address(Url.Convert(url));
}
public VirtualResponse Status(HttpStatusCode code)
{
status = code;
return this;
}
public VirtualResponse Status(int code)
{
return Status((HttpStatusCode)code);
}
public VirtualResponse Header(string name, string value)
{
headers[name] = value;
return this;
}
public VirtualResponse Headers(object obj)
{
Dictionary<string, string> dictionary = obj.ToDictionary();
return Headers(dictionary);
}
public VirtualResponse Headers(IDictionary<string, string> headers)
{
foreach (KeyValuePair<string, string> header in headers) {
Header(header.Key, header.Value);
}
return this;
}
public VirtualResponse Content(string text)
{
Release();
source = new TextSource(text);
return this;
}
public VirtualResponse Content(Stream stream, bool shouldDispose = false)
{
Release();
content = stream;
dispose = shouldDispose;
return this;
}
private void Release()
{
if (content != null && dispose)
content.Dispose();
else if (source != null) {
source.Dispose();
}
dispose = false;
source = null;
content = null;
}
void IDisposable.Dispose()
{
Release();
}
internal TextSource CreateSourceFor(IConfiguration configuration)
{
if (source != null)
return source;
if (content != null)
return new TextSource(content, configuration.DefaultEncoding());
return new TextSource(string.Empty);
}
}
public static Task<IDocument> OpenNewAsync(this IBrowsingContext context, string url = null)
{
return context.OpenAsync(delegate(VirtualResponse m) {
m.Address(url);
});
}
public static async Task<IDocument> OpenAsync(this IBrowsingContext context, IResponse response, CancellationToken cancel)
{
if (response == null)
throw new ArgumentNullException("response");
if (context == null)
context = BrowsingContext.New(null);
TextSource source = new TextSource(response.Content, context.Configuration.DefaultEncoding());
IDocument document = await context.LoadDocumentAsync(response, source, cancel).ConfigureAwait(false);
context.NavigateTo(document);
return document;
}
public static async Task<IDocument> OpenAsync(this IBrowsingContext context, DocumentRequest request, CancellationToken cancel)
{
if (request == null)
throw new ArgumentNullException("request");
using (IResponse response = await context.Loader.SendAsync(request, cancel).ConfigureAwait(false)) {
if (response != null)
return await context.OpenAsync(response, cancel).ConfigureAwait(false);
}
return await context.OpenNewAsync(request.Target.Href).ConfigureAwait(false);
}
public static Task<IDocument> OpenAsync(this IBrowsingContext context, Url url, CancellationToken cancel)
{
if (url == null)
throw new ArgumentNullException("url");
DocumentRequest documentRequest = DocumentRequest.Get(url, null, null);
if (context != null && context.Active != null)
documentRequest.Referer = context.Active.DocumentUri;
return context.OpenAsync(documentRequest, cancel);
}
public static async Task<IDocument> OpenAsync(this IBrowsingContext context, Action<VirtualResponse> request, CancellationToken cancel)
{
if (request == null)
throw new ArgumentNullException("request");
if (context == null)
context = BrowsingContext.New(null);
using (VirtualResponse response = new VirtualResponse()) {
request(response);
TextSource source = response.CreateSourceFor(context.Configuration);
IDocument document = await context.LoadDocumentAsync(response, source, cancel).ConfigureAwait(false);
context.NavigateTo(document);
return document;
}
}
public static Task<IDocument> OpenAsync(this IBrowsingContext context, Action<VirtualResponse> request)
{
return context.OpenAsync(request, CancellationToken.None);
}
public static Task<IDocument> OpenAsync(this IBrowsingContext context, Url url)
{
return context.OpenAsync(url, CancellationToken.None);
}
public static Task<IDocument> OpenAsync(this IBrowsingContext context, string address)
{
if (address == null)
throw new ArgumentNullException("address");
return context.OpenAsync(Url.Create(address), CancellationToken.None);
}
private static async Task<IDocument> LoadDocumentAsync(this IBrowsingContext context, IResponse response, TextSource source, CancellationToken cancel)
{
string contentType = response.Headers.GetOrDefault(HeaderNames.ContentType, MimeTypes.Html);
if (contentType.IndexOf(';') > 0)
contentType = contentType.Substring(0, contentType.IndexOf(';')).Trim();
if (contentType.Equals(MimeTypes.Xml, StringComparison.OrdinalIgnoreCase) || contentType.Equals(MimeTypes.ApplicationXml, StringComparison.OrdinalIgnoreCase))
return await XmlDocument.LoadAsync(context, response, source, cancel).ConfigureAwait(false);
if (contentType.Equals(MimeTypes.Svg, StringComparison.OrdinalIgnoreCase))
return await SvgDocument.LoadAsync(context, response, source, cancel);
return await HtmlDocument.LoadAsync(context, response, source, cancel).ConfigureAwait(false);
}
}
}