diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 8f58af66f6183..cf6bc16b71a90 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -2040,6 +2040,8 @@ ORIGIN: ../../../flutter/lib/web_ui/lib/src/engine/window.dart + ../../../flutte ORIGIN: ../../../flutter/lib/web_ui/lib/text.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/tile_mode.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/ui.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web.dart + ../../../flutter/LICENSE +ORIGIN: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web/url_strategy.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/lib/window.dart + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/skwasm/canvas.cpp + ../../../flutter/LICENSE ORIGIN: ../../../flutter/lib/web_ui/skwasm/contour_measure.cpp + ../../../flutter/LICENSE @@ -4615,6 +4617,8 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/window.dart FILE: ../../../flutter/lib/web_ui/lib/text.dart FILE: ../../../flutter/lib/web_ui/lib/tile_mode.dart FILE: ../../../flutter/lib/web_ui/lib/ui.dart +FILE: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web.dart +FILE: ../../../flutter/lib/web_ui/lib/ui_web/src/ui_web/url_strategy.dart FILE: ../../../flutter/lib/web_ui/lib/window.dart FILE: ../../../flutter/lib/web_ui/skwasm/canvas.cpp FILE: ../../../flutter/lib/web_ui/skwasm/contour_measure.cpp diff --git a/lib/web_ui/lib/src/engine/navigation/history.dart b/lib/web_ui/lib/src/engine/navigation/history.dart index d785449095d4f..dce3ba42131f8 100644 --- a/lib/web_ui/lib/src/engine/navigation/history.dart +++ b/lib/web_ui/lib/src/engine/navigation/history.dart @@ -4,18 +4,18 @@ import 'package:meta/meta.dart'; import 'package:ui/ui.dart' as ui; +import 'package:ui/ui_web/src/ui_web.dart' as ui_web; import '../dom.dart'; import '../platform_dispatcher.dart'; import '../services/message_codec.dart'; import '../services/message_codecs.dart'; -import 'url_strategy.dart'; /// Infers the history mode from the existing browser history state, then /// creates the appropriate instance of [BrowserHistory] for it. /// /// If it can't infer, it creates a [MultiEntriesBrowserHistory] by default. -BrowserHistory createHistoryForExistingState(UrlStrategy? urlStrategy) { +BrowserHistory createHistoryForExistingState(ui_web.UrlStrategy? urlStrategy) { if (urlStrategy != null) { final Object? state = urlStrategy.getState(); if (SingleEntryBrowserHistory._isOriginEntry(state) || SingleEntryBrowserHistory._isFlutterEntry(state)) { @@ -45,13 +45,13 @@ abstract class BrowserHistory { late ui.VoidCallback _unsubscribe; /// The strategy to interact with html browser history. - UrlStrategy? get urlStrategy; + ui_web.UrlStrategy? get urlStrategy; bool _isTornDown = false; bool _isDisposed = false; - void _setupStrategy(UrlStrategy strategy) { - _unsubscribe = strategy.addPopStateListener(onPopState as DomEventListener); + void _setupStrategy(ui_web.UrlStrategy strategy) { + _unsubscribe = strategy.addPopStateListener(onPopState); } /// Release any resources held by this [BrowserHistory] instance. @@ -100,7 +100,7 @@ abstract class BrowserHistory { /// /// Subclasses should send appropriate system messages to update the flutter /// applications accordingly. - void onPopState(covariant DomPopStateEvent event); + void onPopState(Object? state); /// Restore any modifications to the html browser history during the lifetime /// of this class. @@ -123,7 +123,7 @@ abstract class BrowserHistory { /// a Router for routing. class MultiEntriesBrowserHistory extends BrowserHistory { MultiEntriesBrowserHistory({required this.urlStrategy}) { - final UrlStrategy? strategy = urlStrategy; + final ui_web.UrlStrategy? strategy = urlStrategy; if (strategy == null) { return; } @@ -138,7 +138,7 @@ class MultiEntriesBrowserHistory extends BrowserHistory { } @override - final UrlStrategy? urlStrategy; + final ui_web.UrlStrategy? urlStrategy; late int _lastSeenSerialCount; int get _currentSerialCount { @@ -183,15 +183,15 @@ class MultiEntriesBrowserHistory extends BrowserHistory { } @override - void onPopState(covariant DomPopStateEvent event) { + void onPopState(Object? state) { assert(urlStrategy != null); // May be a result of direct url access while the flutter application is // already running. - if (!_hasSerialCount(event.state)) { + if (!_hasSerialCount(state)) { // In this case we assume this will be the next history entry from the // last seen entry. urlStrategy!.replaceState( - _tagWithSerialCount(event.state, _lastSeenSerialCount + 1), + _tagWithSerialCount(state, _lastSeenSerialCount + 1), 'flutter', currentPath); } @@ -201,7 +201,7 @@ class MultiEntriesBrowserHistory extends BrowserHistory { const JSONMethodCodec().encodeMethodCall( MethodCall('pushRouteInformation', { 'location': currentPath, - 'state': event.state?['state'], + 'state': (state as Map?)?['state'], })), (_) {}, ); @@ -220,7 +220,7 @@ class MultiEntriesBrowserHistory extends BrowserHistory { assert(_hasSerialCount(currentState)); final int backCount = _currentSerialCount; if (backCount > 0) { - await urlStrategy!.go(-backCount.toDouble()); + await urlStrategy!.go(-backCount); } // Unwrap state. assert(_hasSerialCount(currentState) && _currentSerialCount == 0); @@ -252,7 +252,7 @@ class MultiEntriesBrowserHistory extends BrowserHistory { /// Router for routing. class SingleEntryBrowserHistory extends BrowserHistory { SingleEntryBrowserHistory({required this.urlStrategy}) { - final UrlStrategy? strategy = urlStrategy; + final ui_web.UrlStrategy? strategy = urlStrategy; if (strategy == null) { return; } @@ -271,7 +271,7 @@ class SingleEntryBrowserHistory extends BrowserHistory { } @override - final UrlStrategy? urlStrategy; + final ui_web.UrlStrategy? urlStrategy; static const MethodCall _popRouteMethodCall = MethodCall('popRoute'); static const String _kFlutterTag = 'flutter'; @@ -311,8 +311,8 @@ class SingleEntryBrowserHistory extends BrowserHistory { String? _userProvidedRouteName; @override - void onPopState(covariant DomPopStateEvent event) { - if (_isOriginEntry(event.state)) { + void onPopState(Object? state) { + if (_isOriginEntry(state)) { _setupFlutterEntry(urlStrategy!); // 2. Send a 'popRoute' platform message so the app can handle it accordingly. @@ -321,7 +321,7 @@ class SingleEntryBrowserHistory extends BrowserHistory { const JSONMethodCodec().encodeMethodCall(_popRouteMethodCall), (_) {}, ); - } else if (_isFlutterEntry(event.state)) { + } else if (_isFlutterEntry(state)) { // We get into this scenario when the user changes the url manually. It // causes a new entry to be pushed on top of our "flutter" one. When this // happens it first goes to the "else" section below where we capture the @@ -358,14 +358,14 @@ class SingleEntryBrowserHistory extends BrowserHistory { /// This method should be called when the Origin Entry is active. It just /// replaces the state of the entry so that we can recognize it later using /// [_isOriginEntry] inside [_popStateListener]. - void _setupOriginEntry(UrlStrategy strategy) { + void _setupOriginEntry(ui_web.UrlStrategy strategy) { strategy.replaceState(_wrapOriginState(currentState), 'origin', ''); } /// This method is used manipulate the Flutter Entry which is always the /// active entry while the Flutter app is running. void _setupFlutterEntry( - UrlStrategy strategy, { + ui_web.UrlStrategy strategy, { bool replace = false, String? path, }) { diff --git a/lib/web_ui/lib/src/engine/navigation/url_strategy.dart b/lib/web_ui/lib/src/engine/navigation/url_strategy.dart index c9b01b0372414..6c211e2fd7d27 100644 --- a/lib/web_ui/lib/src/engine/navigation/url_strategy.dart +++ b/lib/web_ui/lib/src/engine/navigation/url_strategy.dart @@ -5,60 +5,12 @@ import 'dart:async'; import 'package:ui/ui.dart' as ui; +import 'package:ui/ui_web/src/ui_web.dart' as ui_web; import '../dom.dart'; import '../safe_browser_api.dart'; import 'js_url_strategy.dart'; -/// Represents and reads route state from the browser's URL. -/// -/// By default, the [HashUrlStrategy] subclass is used if the app doesn't -/// specify one. -abstract class UrlStrategy { - /// Abstract const constructor. This constructor enables subclasses to provide - /// const constructors so that they can be used in const expressions. - const UrlStrategy(); - - /// Adds a listener to the `popstate` event and returns a function that, when - /// invoked, removes the listener. - ui.VoidCallback addPopStateListener(DomEventListener fn); - - /// Returns the active path in the browser. - String getPath(); - - /// The state of the current browser history entry. - /// - /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/state - Object? getState(); - - /// Given a path that's internal to the app, create the external url that - /// will be used in the browser. - String prepareExternalUrl(String internalUrl); - - /// Push a new history entry. - /// - /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/pushState - void pushState(Object? state, String title, String url); - - /// Replace the currently active history entry. - /// - /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState - void replaceState(Object? state, String title, String url); - - /// Moves forwards or backwards through the history stack. - /// - /// A negative [count] value causes a backward move in the history stack. And - /// a positive [count] value causs a forward move. - /// - /// Examples: - /// - /// * `go(-2)` moves back 2 steps in history. - /// * `go(3)` moves forward 3 steps in hisotry. - /// - /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/go - Future go(double count); -} - /// This is an implementation of [UrlStrategy] that uses the browser URL's /// [hash fragments](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax) /// to represent its state. @@ -71,7 +23,7 @@ abstract class UrlStrategy { /// // Somewhere before calling `runApp()` do: /// setUrlStrategy(const HashUrlStrategy()); /// ``` -class HashUrlStrategy extends UrlStrategy { +class HashUrlStrategy extends ui_web.UrlStrategy { /// Creates an instance of [HashUrlStrategy]. /// /// The [PlatformLocation] parameter is useful for testing to mock out browser @@ -82,8 +34,9 @@ class HashUrlStrategy extends UrlStrategy { final PlatformLocation _platformLocation; @override - ui.VoidCallback addPopStateListener(DomEventListener fn) { - final DomEventListener wrappedFn = allowInterop(fn); + ui.VoidCallback addPopStateListener(ui_web.PopStateListener fn) { + final DomEventListener wrappedFn = + allowInterop((DomEvent event) => fn((event as DomPopStateEvent).state)); _platformLocation.addPopStateListener(wrappedFn); return () => _platformLocation.removePopStateListener(wrappedFn); } @@ -128,8 +81,8 @@ class HashUrlStrategy extends UrlStrategy { } @override - Future go(double count) { - _platformLocation.go(count); + Future go(int count) { + _platformLocation.go(count.toDouble()); return _waitForPopState(); } @@ -150,15 +103,17 @@ class HashUrlStrategy extends UrlStrategy { /// Wraps a custom implementation of [UrlStrategy] that was previously converted /// to a [JsUrlStrategy]. -class CustomUrlStrategy extends UrlStrategy { +class CustomUrlStrategy extends ui_web.UrlStrategy { /// Wraps the [delegate] in a [CustomUrlStrategy] instance. CustomUrlStrategy.fromJs(this.delegate); final JsUrlStrategy delegate; @override - ui.VoidCallback addPopStateListener(DomEventListener fn) => - delegate.addPopStateListener(allowInterop(fn)); + ui.VoidCallback addPopStateListener(ui_web.PopStateListener fn) => + delegate.addPopStateListener(allowInterop((DomEvent event) => + fn((event as DomPopStateEvent).state) + )); @override String getPath() => delegate.getPath(); @@ -179,7 +134,7 @@ class CustomUrlStrategy extends UrlStrategy { delegate.replaceState(state, title, url); @override - Future go(double count) => delegate.go(count); + Future go(int count) => delegate.go(count.toDouble()); } /// Encapsulates all calls to DOM apis, which allows the [UrlStrategy] classes diff --git a/lib/web_ui/lib/src/engine/test_embedding.dart b/lib/web_ui/lib/src/engine/test_embedding.dart index 05ccd86466758..103830680348e 100644 --- a/lib/web_ui/lib/src/engine/test_embedding.dart +++ b/lib/web_ui/lib/src/engine/test_embedding.dart @@ -8,6 +8,7 @@ import 'dart:async'; import 'package:ui/ui.dart' as ui; +import 'package:ui/ui_web/src/ui_web.dart' as ui_web; import '../engine.dart'; @@ -54,7 +55,7 @@ class TestHistoryEntry { /// /// It keeps a list of history entries and event listeners in memory and /// manipulates them in order to achieve the desired functionality. -class TestUrlStrategy extends UrlStrategy { +class TestUrlStrategy extends ui_web.UrlStrategy { /// Creates a instance of [TestUrlStrategy] with an empty string as the /// path. factory TestUrlStrategy() => TestUrlStrategy.fromEntry(const TestHistoryEntry(null, null, '')); @@ -132,12 +133,12 @@ class TestUrlStrategy extends UrlStrategy { } @override - Future go(double count) { + Future go(int count) { assert(withinAppHistory); // Browsers don't move in history immediately. They do it at the next // event loop. So let's simulate that. return _nextEventLoop(() { - _currentEntryIndex = _currentEntryIndex + count.round(); + _currentEntryIndex = _currentEntryIndex + count; if (withinAppHistory) { _firePopStateEvent(); } @@ -148,16 +149,15 @@ class TestUrlStrategy extends UrlStrategy { }); } - final List listeners = []; + final List listeners = []; @override - ui.VoidCallback addPopStateListener(DomEventListener fn) { - final DomEventListener wrappedFn = allowInterop(fn); - listeners.add(wrappedFn); + ui.VoidCallback addPopStateListener(ui_web.PopStateListener fn) { + listeners.add(fn); return () { // Schedule a micro task here to avoid removing the listener during // iteration in [_firePopStateEvent]. - scheduleMicrotask(() => listeners.remove(wrappedFn)); + scheduleMicrotask(() => listeners.remove(fn)); }; } @@ -172,16 +172,12 @@ class TestUrlStrategy extends UrlStrategy { /// like a real browser. void _firePopStateEvent() { assert(withinAppHistory); - final DomPopStateEvent event = createDomPopStateEvent( - 'popstate', - {'state': currentEntry.state}, - ); for (int i = 0; i < listeners.length; i++) { - listeners[i](event); + listeners[i](currentEntry.state); } if (_debugLogHistoryActions) { - print('$runtimeType: fired popstate event $event'); + print('$runtimeType: fired popstate with state ${currentEntry.state}'); } } diff --git a/lib/web_ui/lib/src/engine/window.dart b/lib/web_ui/lib/src/engine/window.dart index f48f7909a089e..720611ec1f536 100644 --- a/lib/web_ui/lib/src/engine/window.dart +++ b/lib/web_ui/lib/src/engine/window.dart @@ -11,6 +11,7 @@ import 'dart:typed_data'; import 'package:js/js.dart'; import 'package:meta/meta.dart'; import 'package:ui/ui.dart' as ui; +import 'package:ui/ui_web/src/ui_web.dart' as ui_web; import '../engine.dart' show DimensionsProvider, registerHotRestartListener, renderer; import 'dom.dart'; @@ -37,8 +38,8 @@ const int kImplicitViewId = 0; bool _isUrlStrategySet = false; /// A custom URL strategy set by the app before running. -UrlStrategy? _customUrlStrategy; -set customUrlStrategy(UrlStrategy? strategy) { +ui_web.UrlStrategy? _customUrlStrategy; +set customUrlStrategy(ui_web.UrlStrategy? strategy) { assert(!_isUrlStrategySet, 'Cannot set URL strategy more than once.'); _isUrlStrategySet = true; _customUrlStrategy = strategy; @@ -74,8 +75,8 @@ class EngineFlutterWindow extends ui.SingletonFlutterWindow { createHistoryForExistingState(_urlStrategyForInitialization); } - UrlStrategy? get _urlStrategyForInitialization { - final UrlStrategy? urlStrategy = + ui_web.UrlStrategy? get _urlStrategyForInitialization { + final ui_web.UrlStrategy? urlStrategy = _isUrlStrategySet ? _customUrlStrategy : _createDefaultUrlStrategy(); // Prevent any further customization of URL strategy. _isUrlStrategySet = true; @@ -104,7 +105,7 @@ class EngineFlutterWindow extends ui.SingletonFlutterWindow { // At this point, we know that `_browserHistory` is a non-null // `MultiEntriesBrowserHistory` instance. - final UrlStrategy? strategy = _browserHistory?.urlStrategy; + final ui_web.UrlStrategy? strategy = _browserHistory?.urlStrategy; await _browserHistory?.tearDown(); _browserHistory = SingleEntryBrowserHistory(urlStrategy: strategy); } @@ -128,14 +129,14 @@ class EngineFlutterWindow extends ui.SingletonFlutterWindow { // At this point, we know that `_browserHistory` is a non-null // `SingleEntryBrowserHistory` instance. - final UrlStrategy? strategy = _browserHistory?.urlStrategy; + final ui_web.UrlStrategy? strategy = _browserHistory?.urlStrategy; await _browserHistory?.tearDown(); _browserHistory = MultiEntriesBrowserHistory(urlStrategy: strategy); } @visibleForTesting Future debugInitializeHistory( - UrlStrategy? strategy, { + ui_web.UrlStrategy? strategy, { required bool useSingle, }) async { // Prevent any further customization of URL strategy. @@ -349,7 +350,7 @@ typedef _JsSetUrlStrategy = void Function(JsUrlStrategy?); @JS('_flutter_web_set_location_strategy') external set jsSetUrlStrategy(_JsSetUrlStrategy? newJsSetUrlStrategy); -UrlStrategy? _createDefaultUrlStrategy() { +ui_web.UrlStrategy? _createDefaultUrlStrategy() { return ui.debugEmulateFlutterTesterEnvironment ? TestUrlStrategy.fromEntry(const TestHistoryEntry('default', null, '/')) : const HashUrlStrategy(); diff --git a/lib/web_ui/lib/ui_web/src/ui_web.dart b/lib/web_ui/lib/ui_web/src/ui_web.dart new file mode 100644 index 0000000000000..fe605222a4b4d --- /dev/null +++ b/lib/web_ui/lib/ui_web/src/ui_web.dart @@ -0,0 +1,11 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This library defines the web-specific additions that go along with dart:ui +// +// The web_sdk/sdk_rewriter.dart uses this directive. +// ignore: unnecessary_library_directive +library ui_web; + +export 'ui_web/url_strategy.dart'; diff --git a/lib/web_ui/lib/ui_web/src/ui_web/url_strategy.dart b/lib/web_ui/lib/ui_web/src/ui_web/url_strategy.dart new file mode 100644 index 0000000000000..f403cc0eb4204 --- /dev/null +++ b/lib/web_ui/lib/ui_web/src/ui_web/url_strategy.dart @@ -0,0 +1,60 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:ui/ui.dart' as ui; + +import '../../../src/engine.dart'; + +set urlStrategy(UrlStrategy? strategy) => customUrlStrategy = strategy; + +typedef PopStateListener = void Function(Object? state); + +/// Represents and reads route state from the browser's URL. +/// +/// By default, the [HashUrlStrategy] subclass is used if the app doesn't +/// specify one. +abstract class UrlStrategy { + /// Abstract const constructor. This constructor enables subclasses to provide + /// const constructors so that they can be used in const expressions. + const UrlStrategy(); + + /// Adds a listener to the `popstate` event and returns a function that, when + /// invoked, removes the listener. + ui.VoidCallback addPopStateListener(PopStateListener fn); + + /// Returns the active path in the browser. + String getPath(); + + /// The state of the current browser history entry. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/state + Object? getState(); + + /// Given a path that's internal to the app, create the external url that + /// will be used in the browser. + String prepareExternalUrl(String internalUrl); + + /// Push a new history entry. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/pushState + void pushState(Object? state, String title, String url); + + /// Replace the currently active history entry. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState + void replaceState(Object? state, String title, String url); + + /// Moves forwards or backwards through the history stack. + /// + /// A negative [count] value causes a backward move in the history stack. And + /// a positive [count] value causs a forward move. + /// + /// Examples: + /// + /// * `go(-2)` moves back 2 steps in history. + /// * `go(3)` moves forward 3 steps in hisotry. + /// + /// See: https://developer.mozilla.org/en-US/docs/Web/API/History/go + Future go(int count); +} diff --git a/web_sdk/BUILD.gn b/web_sdk/BUILD.gn index 957510f08ef93..b46b4388fb7b5 100644 --- a/web_sdk/BUILD.gn +++ b/web_sdk/BUILD.gn @@ -20,6 +20,7 @@ web_engine_libraries = [ ":skwasm_stub_library", ":web_engine_library", ":web_ui_library", + ":web_ui_ui_web", ":web_ui_library_sources", ":web_unicode_library", ":web_test_fonts_library", @@ -77,6 +78,11 @@ template("sdk_rewriter") { "--stamp", rebase_path(stamp_location, root_build_dir), ] + + if (defined(invoker.is_public) && invoker.is_public) { + args += [ "--public" ] + } + if (ui) { args += [ "--ui" ] } else { @@ -104,6 +110,14 @@ sdk_rewriter("web_ui_library_sources") { exclude_patterns = [ rebase_path("//flutter/lib/web_ui/lib/src/*") ] } +sdk_rewriter("web_ui_ui_web") { + library_name = "ui_web" + is_public = true + api_file = "//flutter/lib/web_ui/lib/ui_web/src/ui_web.dart" + input_dir = "//flutter/lib/web_ui/lib/ui_web/src/ui_web/" + output_dir = "$root_out_dir/flutter_web_sdk/lib/ui_web/" +} + sdk_rewriter("web_engine_library") { library_name = "engine" api_file = "//flutter/lib/web_ui/lib/src/engine.dart" @@ -307,6 +321,8 @@ template("_compile_platform") { "--source", "dart:ui", "--source", + "dart:ui_web", + "--source", "dart:_engine", "--source", "dart:_skwasm_stub", @@ -442,6 +458,7 @@ template("_compile_ddc_modules") { # Additional Flutter web dart libraries "dart:ui", + "dart:ui_web", "dart:_engine", "dart:_skwasm_stub", "dart:_web_unicode", diff --git a/web_sdk/libraries.json b/web_sdk/libraries.json index b55cd2bac65eb..044a1ec05a1f5 100644 --- a/web_sdk/libraries.json +++ b/web_sdk/libraries.json @@ -12,6 +12,9 @@ "ui": { "uri": "lib/ui/ui.dart" }, + "ui_web": { + "uri": "lib/ui_web/ui_web.dart" + }, "_engine": { "uri": "lib/_engine/engine.dart" }, @@ -40,6 +43,9 @@ "ui": { "uri": "lib/ui/ui.dart" }, + "ui_web": { + "uri": "lib/ui_web/ui_web.dart" + }, "_engine": { "uri": "lib/_engine/engine.dart" }, @@ -68,6 +74,9 @@ "ui": { "uri": "lib/ui/ui.dart" }, + "ui_web": { + "uri": "lib/ui_web/ui_web.dart" + }, "_engine": { "uri": "lib/_engine/engine.dart" }, diff --git a/web_sdk/libraries.yaml b/web_sdk/libraries.yaml index d447436aea738..46ab52b18de2c 100644 --- a/web_sdk/libraries.yaml +++ b/web_sdk/libraries.yaml @@ -21,6 +21,9 @@ dartdevc: ui: uri: "lib/ui/ui.dart" + ui_web: + uri: "lib/ui_web/ui_web.dart" + _engine: uri: "lib/_engine/engine.dart" @@ -44,6 +47,9 @@ dart2js: ui: uri: "lib/ui/ui.dart" + ui_web: + uri: "lib/ui_web/ui_web.dart" + _engine: uri: "lib/_engine/engine.dart" @@ -67,6 +73,9 @@ wasm: ui: uri: "lib/ui/ui.dart" + ui_web: + uri: "lib/ui_web/ui_web.dart" + _engine: uri: "lib/_engine/engine.dart" diff --git a/web_sdk/sdk_rewriter.dart b/web_sdk/sdk_rewriter.dart index fd1d759757d13..4aa31a81dfbe0 100644 --- a/web_sdk/sdk_rewriter.dart +++ b/web_sdk/sdk_rewriter.dart @@ -11,6 +11,7 @@ final ArgParser argParser = ArgParser() ..addOption('output-dir') ..addOption('input-dir') ..addFlag('ui') + ..addFlag('public') ..addOption('library-name') ..addOption('api-file') ..addMultiOption('source-file') @@ -34,11 +35,12 @@ export 'dart:_engine' ), ]; -List generateApiFilePatterns(String libraryName, List extraImports) { +List generateApiFilePatterns(String libraryName, bool isPublic, List extraImports) { + final String libraryPrefix = isPublic ? '' : '_'; return [ AllReplacer(RegExp('library\\s+$libraryName;'), ''' @JS() -library dart._$libraryName; +library dart.$libraryPrefix$libraryName; import 'dart:async'; import 'dart:collection'; @@ -63,9 +65,10 @@ part '$libraryName/${match.group(1)}'; ]; } -List generatePartsPatterns(String libraryName) { +List generatePartsPatterns(String libraryName, bool isPublic) { + final String libraryPrefix = isPublic ? '' : '_'; return [ - AllReplacer(RegExp('part\\s+of\\s+$libraryName;'), 'part of dart._$libraryName;'), + AllReplacer(RegExp('part\\s+of\\s+$libraryName;'), 'part of dart.$libraryPrefix$libraryName;'), // Remove library-level JS annotations. AllReplacer(RegExp(r'\n@JS(.*)\nlibrary .+;'), ''), // Remove library directives. @@ -86,6 +89,7 @@ final List stripMetaPatterns = [ ]; const Set rootLibraryNames = { + 'ui_web', 'engine', 'skwasm_stub', 'skwasm_impl', @@ -93,6 +97,7 @@ const Set rootLibraryNames = { final Map extraImportsMap = { RegExp('skwasm_(stub|impl)'): "import 'dart:_skwasm_stub' if (dart.library.ffi) 'dart:_skwasm_impl';", + 'ui_web': "import 'dart:ui_web' as ui_web;", 'engine': "import 'dart:_engine';", 'web_unicode': "import 'dart:_web_unicode';", 'web_test_fonts': "import 'dart:_web_test_fonts';", @@ -110,6 +115,8 @@ void main(List arguments) { List replacementPatterns; String? libraryName; + final bool isPublic = results['public'] as bool; + if (results['ui'] as bool) { replacementPatterns = uiPatterns; } else { @@ -118,7 +125,7 @@ void main(List arguments) { throw Exception('library-name must be specified if not rewriting ui'); } preprocessor = (String source) => preprocessPartFile(source, libraryName!); - replacementPatterns = generatePartsPatterns(libraryName); + replacementPatterns = generatePartsPatterns(libraryName, isPublic); } for (final String inputFilePath in results['source-file'] as Iterable) { String pathSuffix = inputFilePath.substring(inputDirectoryPath.length); @@ -139,7 +146,7 @@ void main(List arguments) { directory.path, path.basename(inputFilePath)); final List extraImports = getExtraImportsForLibrary(libraryName); - replacementPatterns = generateApiFilePatterns(libraryName, extraImports); + replacementPatterns = generateApiFilePatterns(libraryName, isPublic, extraImports); processFile( inputFilePath, diff --git a/web_sdk/test/sdk_rewriter_test.dart b/web_sdk/test/sdk_rewriter_test.dart index d8e96199fb64d..ae948258149c2 100644 --- a/web_sdk/test/sdk_rewriter_test.dart +++ b/web_sdk/test/sdk_rewriter_test.dart @@ -52,7 +52,41 @@ part 'engine/file3.dart'; '/path/to/lib/web_ui/lib/src/engine.dart', source, 'engine'), - generateApiFilePatterns('engine', ["import 'dart:extra';"]), + generateApiFilePatterns('engine', false, ["import 'dart:extra';"]), + ); + expect(result, expected); + }); + + test('underscore is not added to library name for public library in API file', () { + const String source = ''' +library engine; +'''; + + const String expected = ''' +@JS() +library dart.engine; + +import 'dart:async'; +import 'dart:collection'; +import 'dart:convert' hide Codec; +import 'dart:developer' as developer; +import 'dart:js_util' as js_util; +import 'dart:_js_annotations'; +import 'dart:js_interop' hide JS; +import 'dart:math' as math; +import 'dart:typed_data'; +import 'dart:ui' as ui; +import 'dart:extra'; + +'''; + + final String result = processSource( + source, + (String source) => validateApiFile( + '/path/to/lib/web_ui/lib/src/engine.dart', + source, + 'engine'), + generateApiFilePatterns('engine', true, ["import 'dart:extra';"]), ); expect(result, expected); }); @@ -74,7 +108,7 @@ export 'engine/file3.dart'; '/path/to/lib/web_ui/lib/src/engine.dart', source, 'engine'), - generateApiFilePatterns('engine', []), + generateApiFilePatterns('engine', false, []), ); } catch(error) { caught = error; @@ -121,7 +155,7 @@ void printSomething() { final String result = processSource( source, (String source) => preprocessPartFile(source, 'engine'), - generatePartsPatterns('engine'), + generatePartsPatterns('engine', false), ); expect(result, expected); }); @@ -130,17 +164,20 @@ void printSomething() { // Root libraries. expect(getExtraImportsForLibrary('engine'), [ "import 'dart:_skwasm_stub' if (dart.library.ffi) 'dart:_skwasm_impl';", + "import 'dart:ui_web' as ui_web;", "import 'dart:_web_unicode';", "import 'dart:_web_test_fonts';", "import 'dart:_web_locale_keymap' as locale_keymap;", ]); expect(getExtraImportsForLibrary('skwasm_stub'), [ + "import 'dart:ui_web' as ui_web;", "import 'dart:_engine';", "import 'dart:_web_unicode';", "import 'dart:_web_test_fonts';", "import 'dart:_web_locale_keymap' as locale_keymap;", ]); expect(getExtraImportsForLibrary('skwasm_impl'), [ + "import 'dart:ui_web' as ui_web;", "import 'dart:_engine';", "import 'dart:_web_unicode';", "import 'dart:_web_test_fonts';",