From 0df6520cbb079acf3fa8dc15db215803edcb5111 Mon Sep 17 00:00:00 2001 From: Florian Rappl Date: Sun, 25 Oct 2026 21:59:22 +0100 Subject: [PATCH] Fixed global overrides --- CHANGELOG.md | 6 ++++ src/AngleSharp.Js.Docs/package.json | 2 +- src/AngleSharp.Js.Tests/GlobalScopeTests.cs | 40 +++++++++++++++++++++ src/AngleSharp.Js/EngineInstance.cs | 13 +++++-- src/Directory.Build.props | 2 +- 5 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 src/AngleSharp.Js.Tests/GlobalScopeTests.cs diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bfc820..09d0aed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 1.1.2 + +Released on Sunday, October 25 2026. + +- Fixed globals named like a member of the window (e.g. `var item`, `var open`, `function close() {}`) keeping that member, as the members were copied to the global scope as read-only + # 1.1.1 Released on Wednesday, October 7 2026. diff --git a/src/AngleSharp.Js.Docs/package.json b/src/AngleSharp.Js.Docs/package.json index b7ca4a5..820ca56 100644 --- a/src/AngleSharp.Js.Docs/package.json +++ b/src/AngleSharp.Js.Docs/package.json @@ -1,6 +1,6 @@ { "name": "@anglesharp/js", - "version": "1.1.1", + "version": "1.1.2", "preview": true, "description": "The doclet for the AngleSharp.Js documentation.", "keywords": [ diff --git a/src/AngleSharp.Js.Tests/GlobalScopeTests.cs b/src/AngleSharp.Js.Tests/GlobalScopeTests.cs new file mode 100644 index 0000000..0aa8555 --- /dev/null +++ b/src/AngleSharp.Js.Tests/GlobalScopeTests.cs @@ -0,0 +1,40 @@ +namespace AngleSharp.Js.Tests +{ + using NUnit.Framework; + using System; + using System.Threading.Tasks; + + /// + /// The members of the window are configurable and writable, so a page's own globals replace + /// them as in a browser - a script declaring var item lost to window's item(), and every use + /// of it failed. + /// + [TestFixture] + public class GlobalScopeTests + { + [TestCase("var item = 1; typeof item", "number")] + [TestCase("var open = 'x'; typeof open", "string")] + [TestCase("var stop = 1; stop + 1", "2")] + [TestCase("function close() { return 'mine'; } close()", "mine")] + [TestCase("var Node = 5; typeof Node", "number")] + [TestCase("var unrelated = 1; typeof unrelated", "number")] + public async Task GlobalsNamedLikeWindowMembersReplaceThem(String script, String expected) + { + var context = BrowsingContext.New(Configuration.Default.WithJs()); + var document = await context.OpenAsync(m => m.Content("

x

")); + + Assert.AreEqual(expected, document.ExecuteScript(script)?.ToString()); + } + + [Test] + public async Task WindowMembersStayAvailableWithoutOwnGlobals() + { + var context = BrowsingContext.New(Configuration.Default.WithJs()); + var document = await context.OpenAsync(m => m.Content("

x

")); + + Assert.AreEqual("function", document.ExecuteScript("typeof setTimeout")); + Assert.AreEqual("function", document.ExecuteScript("typeof Node")); + Assert.AreEqual("P", document.ExecuteScript("document.querySelector('p').tagName")); + } + } +} diff --git a/src/AngleSharp.Js/EngineInstance.cs b/src/AngleSharp.Js/EngineInstance.cs index 24a9856..a027ca1 100644 --- a/src/AngleSharp.Js/EngineInstance.cs +++ b/src/AngleSharp.Js/EngineInstance.cs @@ -9,6 +9,7 @@ namespace AngleSharp.Js using Jint.Native.Json; using Jint.Native.Object; using Jint.Runtime; + using Jint.Runtime.Descriptors; using Jint.Runtime.Interop; using System; using System.Collections.Generic; @@ -69,12 +70,12 @@ public EngineInstance(IWindow window, IDictionary assignments, I foreach (var property in _window.GetOwnProperties()) { - _engine.Global.FastSetProperty(property.Key.ToString(), property.Value); + _engine.Global.FastSetProperty(property.Key.ToString(), AsGlobalProperty(property.Value)); } foreach (var prototypeProperty in Window.Prototype.GetOwnProperties()) { - _engine.Global.FastSetProperty(prototypeProperty.Key.ToString(), prototypeProperty.Value); + _engine.Global.FastSetProperty(prototypeProperty.Key.ToString(), AsGlobalProperty(prototypeProperty.Value)); } _engine.Global.Prototype = _window.Prototype; @@ -249,6 +250,14 @@ private JsValue ImportModule(String specifier, String source) #region Helpers + // The members of the window are configurable (and its operations and interface objects + // writable) per WebIDL, so that a page's own global replaces them as in a browser, e.g. + // var item = ..., var open = ... or var Node = ...; an accessor like name keeps its setter. + private static PropertyDescriptor AsGlobalProperty(PropertyDescriptor descriptor) => + descriptor.IsAccessorDescriptor() + ? new GetSetPropertyDescriptor(descriptor.Get, descriptor.Set, descriptor.Enumerable, true) + : new PropertyDescriptor(descriptor.Value, true, descriptor.Enumerable, true); + // A collection is projected by the array-like proxy, which lets the engine read its // indices and length directly; everything else by the ordinary one. The decision is a // property of the type, so it is resolved once per type for the whole process. diff --git a/src/Directory.Build.props b/src/Directory.Build.props index dc8033a..4063434 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -10,7 +10,7 @@ entry of CHANGELOG.md, which build/Build.cs passes in as -p:Version, so this value only affects a plain `dotnet build` and no longer needs bumping for a release. --> - 1.1.1 + 1.1.2