diff --git a/CHANGELOG.md b/CHANGELOG.md index 522154c9247..a8e1bfbb6d3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,14 @@ [#51486]: https://github.com/dart-lang/sdk/issues/51486 [#52027]: https://github.com/dart-lang/sdk/issues/52027 +#### `dart:html` + +- **Breaking change to Window.open**: + `Window.open` will now throw an exception that can be caught + (`NullWindowException`) if the opened window is null. Previously, this null + window would be wrapped, and there would be surprising runtime errors when any + member is used on the wrapper. + #### `dart:js_interop` - **Object literal constructors**: diff --git a/sdk/lib/html/dart2js/html_dart2js.dart b/sdk/lib/html/dart2js/html_dart2js.dart index e4f5fbfef10..e8587eb2b35 100644 --- a/sdk/lib/html/dart2js/html_dart2js.dart +++ b/sdk/lib/html/dart2js/html_dart2js.dart @@ -32154,6 +32154,8 @@ class Window extends EventTarget /** * Opens a new window. * + * Throws a NullWindowException if the opened window is null. + * * ## Other resources * * * [Window.open](https://developer.mozilla.org/en-US/docs/Web/API/Window.open) @@ -32162,6 +32164,7 @@ class Window extends EventTarget WindowBase open(String url, String name, [String? options]) { final win = options == null ? _open2(url, name) : _open3(url, name, options); + if (win == null) throw new NullWindowException(); return _DOMWindowCrossFrame._createSafe(win); } @@ -33779,7 +33782,7 @@ class Window extends EventTarget class NullWindowException implements Exception { @override String toString() { - return 'Attempting to use a null window opened in Window.open.'; + return 'Attempted to call Window.open with a null window.'; } } // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file @@ -40121,12 +40124,7 @@ class _DOMWindowCrossFrame implements WindowBase { // Private window. Note, this is a window in another frame, so it // cannot be typed as "Window" as its prototype is not patched // properly. Its fields and methods can only be accessed via JavaScript. - final Object? __window; - - Object get _window { - if (__window == null) throw new NullWindowException(); - return __window!; - } + final Object _window; // Fields. HistoryBase get history => @@ -40163,7 +40161,7 @@ class _DOMWindowCrossFrame implements WindowBase { } // Implementation support. - _DOMWindowCrossFrame(this.__window); + _DOMWindowCrossFrame(this._window); static WindowBase _createSafe(w) { if (identical(w, window)) { diff --git a/tests/lib/html/window_test.dart b/tests/lib/html/window_test.dart index 9e6ff6a1bb3..2a3fc319527 100644 --- a/tests/lib/html/window_test.dart +++ b/tests/lib/html/window_test.dart @@ -12,15 +12,12 @@ main() { expect(window.scrollY, 0); }); test('open', () { - final valid = window.open('', 'blank'); - valid.closed; - // A blank page with no access to the original window (noopener) should - // result in null. - final invalid = window.open('', 'invalid', 'noopener=true'); + window.open('', 'blank'); try { - // Should result in an exception since the underlying window is null. - invalid.closed; - fail('Expected invalid.closed to throw.'); + // A blank page with no access to the original window (noopener) should + // result in null. + window.open('', 'invalid', 'noopener=true'); + fail('Expected Window.open to throw.'); } on NullWindowException {} }); } diff --git a/tests/lib_2/html/window_test.dart b/tests/lib_2/html/window_test.dart index 154cba6fe8a..2aa67fcdc39 100644 --- a/tests/lib_2/html/window_test.dart +++ b/tests/lib_2/html/window_test.dart @@ -13,16 +13,4 @@ main() { expect(window.scrollX, 0); expect(window.scrollY, 0); }); - test('open', () { - final valid = window.open('', 'blank'); - valid.closed; - // A blank page with no access to the original window (noopener) should - // result in null. - final invalid = window.open('', 'invalid', 'noopener=true'); - try { - // Should result in an exception since the underlying window is null. - invalid.closed; - fail('Expected invalid.closed to throw.'); - } on NullWindowException {} - }); } diff --git a/tools/dom/src/dart2js_DOMImplementation.dart b/tools/dom/src/dart2js_DOMImplementation.dart index 3fe4c7ff6fa..29ccba85ded 100644 --- a/tools/dom/src/dart2js_DOMImplementation.dart +++ b/tools/dom/src/dart2js_DOMImplementation.dart @@ -9,12 +9,7 @@ class _DOMWindowCrossFrame implements WindowBase { // Private window. Note, this is a window in another frame, so it // cannot be typed as "Window" as its prototype is not patched // properly. Its fields and methods can only be accessed via JavaScript. - final Object? __window; - - Object get _window { - if (__window == null) throw new NullWindowException(); - return __window!; - } + final Object _window; // Fields. HistoryBase get history => @@ -51,7 +46,7 @@ class _DOMWindowCrossFrame implements WindowBase { } // Implementation support. - _DOMWindowCrossFrame(this.__window); + _DOMWindowCrossFrame(this._window); static WindowBase _createSafe(w) { if (identical(w, window)) { diff --git a/tools/dom/templates/html/impl/impl_Window.darttemplate b/tools/dom/templates/html/impl/impl_Window.darttemplate index 0df2cd773c5..d270c55a280 100644 --- a/tools/dom/templates/html/impl/impl_Window.darttemplate +++ b/tools/dom/templates/html/impl/impl_Window.darttemplate @@ -50,6 +50,8 @@ $(CLASS_MODIFIERS)class $CLASSNAME$EXTENDS$IMPLEMENTS { /** * Opens a new window. * + * Throws a NullWindowException if the opened window is null. + * * ## Other resources * * * [Window.open](https://developer.mozilla.org/en-US/docs/Web/API/Window.open) @@ -58,6 +60,7 @@ $(CLASS_MODIFIERS)class $CLASSNAME$EXTENDS$IMPLEMENTS { WindowBase open(String url, String name, [String$NULLABLE options]) { final win = options == null ? _open2(url, name) : _open3(url, name, options); + if (win == null) throw new NullWindowException(); return _DOMWindowCrossFrame._createSafe(win); } @@ -256,6 +259,6 @@ $!MEMBERS class NullWindowException implements Exception { @override String toString() { - return 'Attempting to use a null window opened in Window.open.'; + return 'Attempted to call Window.open with a null window.'; } }