HtmlScriptElement
sealed class HtmlScriptElement : HtmlElement, IHtmlScriptElement, IHtmlElement, IElement, INode, IEventTarget, IMarkupFormattable, IParentNode, IChildNode, INonDocumentTypeChildNode, IElementCssInlineStyle, IDisposable
Represents an HTML script element.
using AngleSharp.Dom.Css;
using AngleSharp.Extensions;
using AngleSharp.Html;
using AngleSharp.Network;
using AngleSharp.Scripting;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace AngleSharp.Dom.Html
{
internal sealed class HtmlScriptElement : HtmlElement, IHtmlScriptElement, IHtmlElement, IElement, INode, IEventTarget, IMarkupFormattable, IParentNode, IChildNode, INonDocumentTypeChildNode, IElementCssInlineStyle, IDisposable
{
private bool _started;
private bool _parserInserted;
private bool _wasParserInserted;
private bool _forceAsync;
private bool _readyToBeExecuted;
private CancellationTokenSource _cts;
private Task<IResponse> _loadingTask;
internal bool IsReady => _readyToBeExecuted;
internal bool IsParserInserted {
get {
return _parserInserted;
}
set {
_parserInserted = value;
}
}
internal bool IsAlreadyStarted {
get {
return _started;
}
set {
_started = value;
}
}
public string ScriptLanguage {
get {
string ownAttribute = GetOwnAttribute(AttributeNames.Language);
string text = Type ?? ((ownAttribute != null) ? ("text/" + ownAttribute) : null);
if (!string.IsNullOrEmpty(text))
return text;
return MimeTypes.DefaultJavaScript;
}
}
public string Source {
get {
return GetOwnAttribute(AttributeNames.Src);
}
set {
SetOwnAttribute(AttributeNames.Src, value);
}
}
public string Type {
get {
return GetOwnAttribute(AttributeNames.Type);
}
set {
SetOwnAttribute(AttributeNames.Type, value);
}
}
public string CharacterSet {
get {
return GetOwnAttribute(AttributeNames.Charset);
}
set {
SetOwnAttribute(AttributeNames.Charset, value);
}
}
public string Text {
get {
return TextContent;
}
set {
TextContent = value;
}
}
public string CrossOrigin {
get {
return GetOwnAttribute(AttributeNames.CrossOrigin);
}
set {
SetOwnAttribute(AttributeNames.CrossOrigin, value);
}
}
public bool IsDeferred {
get {
return GetOwnAttribute(AttributeNames.Defer) != null;
}
set {
SetOwnAttribute(AttributeNames.Defer, value ? string.Empty : null);
}
}
public bool IsAsync {
get {
return GetOwnAttribute(AttributeNames.Async) != null;
}
set {
SetOwnAttribute(AttributeNames.Async, value ? string.Empty : null);
}
}
public HtmlScriptElement(Document owner, string prefix = null)
: base(owner, Tags.Script, prefix, NodeFlags.Special | NodeFlags.LiteralText)
{
}
internal void Run()
{
if (_loadingTask != null) {
if (_loadingTask.Exception != null || _loadingTask.IsFaulted)
Error();
else if (!CancelledBeforeScriptExecute()) {
using (IResponse response = _loadingTask.Result)
base.Owner.Options.RunScript(response, CreateOptions(), ScriptLanguage);
AfterScriptExecute();
if (Source != null)
Load();
else
base.Owner.QueueTask(Load);
}
}
}
internal void Prepare()
{
if (!_started && base.Owner != null) {
IConfiguration options = base.Owner.Options;
_wasParserInserted = _parserInserted;
_parserInserted = false;
_forceAsync = (_wasParserInserted && !IsAsync);
if ((!string.IsNullOrEmpty(Source) || !string.IsNullOrEmpty(Text)) && options.GetScriptEngine(ScriptLanguage) != null) {
if (_wasParserInserted) {
_parserInserted = true;
_forceAsync = false;
}
_started = true;
if (base.Owner.Options.IsScripting()) {
string ownAttribute = GetOwnAttribute(AttributeNames.Event);
string ownAttribute2 = GetOwnAttribute(AttributeNames.For);
if (!string.IsNullOrEmpty(ownAttribute) && !string.IsNullOrEmpty(ownAttribute2)) {
ownAttribute = ownAttribute.Trim();
ownAttribute2 = ownAttribute2.Trim();
if (ownAttribute.EndsWith("()"))
ownAttribute = ownAttribute.Substring(0, ownAttribute.Length - 2);
if (!ownAttribute2.Equals(AttributeNames.Window, StringComparison.OrdinalIgnoreCase) || !ownAttribute.Equals("onload", StringComparison.OrdinalIgnoreCase))
return;
}
string source = Source;
if (source != null) {
if (source == string.Empty)
base.Owner.QueueTask(Error);
else {
Url url = this.HyperReference(source);
_cts = new CancellationTokenSource();
_loadingTask = PrepareAsync(url, _cts.Token);
}
} else if (_parserInserted && base.Owner.HasScriptBlockingStyleSheet()) {
_readyToBeExecuted = true;
} else {
options.RunScript(Text, CreateOptions(), ScriptLanguage);
}
}
}
}
}
private async Task<IResponse> PrepareAsync(Url url, CancellationToken cancel)
{
if (_parserInserted && !IsAsync) {
if (IsDeferred)
Owner.AddScript(this);
} else if (!IsAsync && !_forceAsync) {
Owner.AddScript(this);
} else {
Owner.AddScript(this);
}
ResourceRequest request = this.CreateRequestFor(url);
IResponse result = await Owner.Loader.FetchWithCorsAsync(request, CrossOrigin.ToEnum(CorsSetting.None), OriginBehavior.Taint, cancel);
if (_parserInserted && !IsAsync)
_readyToBeExecuted = true;
return result;
}
private void Load()
{
this.FireSimpleEvent(EventNames.Load, false, false);
}
private void Error()
{
this.FireSimpleEvent(EventNames.Error, false, false);
}
private bool CancelledBeforeScriptExecute()
{
return this.FireSimpleEvent(EventNames.BeforeScriptExecute, false, true);
}
private void AfterScriptExecute()
{
this.FireSimpleEvent(EventNames.AfterScriptExecute, true, false);
}
private ScriptOptions CreateOptions()
{
return new ScriptOptions {
Context = base.Owner.DefaultView,
Document = base.Owner,
Element = this,
Encoding = TextEncoding.Resolve(CharacterSet)
};
}
public void Dispose()
{
if (_cts != null)
_cts.Cancel();
_cts = null;
_loadingTask = null;
}
}
}