diff --git a/src/HtmlAgilityPack.Shared/HtmlAgilityPack.Shared.projitems b/src/HtmlAgilityPack.Shared/HtmlAgilityPack.Shared.projitems
index fa59426f..f2af311d 100644
--- a/src/HtmlAgilityPack.Shared/HtmlAgilityPack.Shared.projitems
+++ b/src/HtmlAgilityPack.Shared/HtmlAgilityPack.Shared.projitems
@@ -57,5 +57,8 @@
+
+
+
\ No newline at end of file
diff --git a/src/HtmlAgilityPack.Shared/XPathContext.cs b/src/HtmlAgilityPack.Shared/XPathContext.cs
new file mode 100644
index 00000000..5152552b
--- /dev/null
+++ b/src/HtmlAgilityPack.Shared/XPathContext.cs
@@ -0,0 +1,219 @@
+/* First released in Dawnx library, it is now available to HAP under the MIT License */
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Xml.XPath;
+using System.Xml.Xsl;
+
+namespace HtmlAgilityPack
+{
+ public abstract partial class XPathContext : XsltContext
+ {
+ private class ContextFunction
+ {
+ public string Namespace { get; set; }
+ public string Name { get; set; }
+ public Type[] ArgTypes { get; set; }
+ public Type[] RealArgTypes { get; set; }
+ public MethodInfo Method { get; set; }
+ }
+
+ private HashSet CustomFunctions = new HashSet();
+
+ public XPathContext(string prefix) : this()
+ {
+ AddNamespace(prefix, DefaultNamespace);
+ }
+ public XPathContext()
+ {
+ var contextMethods = GetType().GetMethods();
+ foreach (var method in contextMethods)
+ {
+ var attr = method
+ .GetCustomAttributes(typeof(XPathFunctionAttribute), true)
+ .FirstOrDefault() as XPathFunctionAttribute;
+ if (attr != null)
+ {
+ CustomFunctions.Add(new ContextFunction
+ {
+ Namespace = attr.Namespace ?? DefaultNamespace,
+ Name = attr.Name ?? method.Name,
+ ArgTypes = method.GetParameters()
+ .Where(x => x.ParameterType != typeof(XPathNavigator))
+ .Select(x => x.ParameterType)
+ .ToArray(),
+ RealArgTypes = method.GetParameters().Select(x => x.ParameterType).ToArray(),
+ Method = method,
+ });
+ }
+ }
+ }
+
+ ///
+ /// Gets all the defined argumennts in the context.
+ ///
+ public XsltArgumentList ArgList { get; private set; } = new XsltArgumentList();
+
+ ///
+ /// Evaluates whether to preserve white space nodes or strip them for the given context.
+ ///
+ ///
+ ///
+ public override bool PreserveWhitespace(XPathNavigator node) => false;
+
+ ///
+ /// Compares the base Uniform Resource Identifiers
+ /// (URIs) of two documents based upon the order the documents were loaded by the
+ /// XSLT processor (that is, the System.Xml.Xsl.XslTransform class)
+ ///
+ ///
+ ///
+ ///
+ public override int CompareDocument(string baseUri, string nextbaseUri) => 0;
+
+ ///
+ /// Gets a value indicating whether to include white space nodes in the output.
+ ///
+ public override bool Whitespace => true;
+
+ ///
+ /// Resolves a function reference and returns
+ /// an representing the function. The
+ /// is used at execution time to get the return value of the function.
+ ///
+ ///
+ ///
+ ///
+ ///
+ public override IXsltContextFunction ResolveFunction(string prefix, string name, XPathResultType[] argTypes)
+ => new XPathFunctionAgent(LookupNamespace(prefix), name);
+
+ ///
+ /// Resolves a variable reference and returns
+ /// an System.Xml.Xsl.IXsltContextVariable representing the variable.
+ ///
+ ///
+ ///
+ ///
+ public override IXsltContextVariable ResolveVariable(string prefix, string name)
+ => new XPathVariable(prefix, name);
+
+ ///
+ /// Adds a argument to and associates it with the namespace qualified name.
+ ///
+ ///
+ ///
+ ///
+ public void AddParam(string name, string namespaceUri, object parameter)
+ => ArgList.AddParam(name, namespaceUri, parameter);
+
+ ///
+ /// Adds a argument to and associates it with empty namespace.
+ ///
+ ///
+ ///
+ public void AddParam(string name, object parameter)
+ => ArgList.AddParam(name, "", parameter);
+
+ ///
+ /// Compiles the XPath expression specified and returns an System.Xml.XPath.XPathExpression
+ /// object representing the XPath expression.
+ ///
+ ///
+ ///
+ public XPathExpression Compile(string xpath)
+ {
+ var xExp = XPathExpression.Compile(xpath);
+ xExp.SetContext(this);
+ return xExp;
+ }
+ public XPathExpression this[string xpath] => Compile(xpath);
+
+ public class XPathFunctionAgent : IXsltContextFunction
+ {
+ private string Namespace;
+ private string Name;
+
+ public XPathFunctionAgent(string @namespace, string name)
+ {
+ Namespace = @namespace;
+ Name = name;
+ }
+
+ public int Minargs => throw new NotSupportedException();
+ public int Maxargs => throw new NotSupportedException();
+ public XPathResultType ReturnType => XPathResultType.Any;
+ public XPathResultType[] ArgTypes => throw new NotSupportedException();
+
+ public object Invoke(XsltContext xsltContext, object[] args, XPathNavigator docContext)
+ {
+ var context = xsltContext as XPathContext;
+ var argTypes = args.Select(x =>
+ {
+ switch (x.GetType().FullName)
+ {
+ case "MS.Internal.Xml.XPath.XPathSelectionIterator": return typeof(string);
+ default: return x.GetType();
+ }
+ });
+ var customFunc = context.CustomFunctions
+ .FirstOrDefault(x => x.Namespace == Namespace && x.Name == Name
+ && Enumerable.SequenceEqual(argTypes, x.ArgTypes));
+
+ if (customFunc != null)
+ {
+ var methodParameterLength = customFunc.Method.GetParameters().Count();
+ var funcArgs = args.Select