Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

ui_web library #40608

Merged
merged 17 commits into from
Mar 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -2028,6 +2028,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
Expand Down Expand Up @@ -4593,6 +4595,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
Expand Down
40 changes: 20 additions & 20 deletions lib/web_ui/lib/src/engine/navigation/history.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand All @@ -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;
}
Expand All @@ -138,7 +138,7 @@ class MultiEntriesBrowserHistory extends BrowserHistory {
}

@override
final UrlStrategy? urlStrategy;
final ui_web.UrlStrategy? urlStrategy;

late int _lastSeenSerialCount;
int get _currentSerialCount {
Expand Down Expand Up @@ -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);
}
Expand All @@ -201,7 +201,7 @@ class MultiEntriesBrowserHistory extends BrowserHistory {
const JSONMethodCodec().encodeMethodCall(
MethodCall('pushRouteInformation', <dynamic, dynamic>{
'location': currentPath,
'state': event.state?['state'],
'state': (state as Map<dynamic, dynamic>?)?['state'],
})),
(_) {},
);
Expand All @@ -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);
Expand Down Expand Up @@ -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;
}
Expand All @@ -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';
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -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,
}) {
Expand Down
71 changes: 13 additions & 58 deletions lib/web_ui/lib/src/engine/navigation/url_strategy.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<void> 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.
Expand All @@ -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
Expand All @@ -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);
}
Expand Down Expand Up @@ -128,8 +81,8 @@ class HashUrlStrategy extends UrlStrategy {
}

@override
Future<void> go(double count) {
_platformLocation.go(count);
Future<void> go(int count) {
_platformLocation.go(count.toDouble());
return _waitForPopState();
}

Expand All @@ -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();
Expand All @@ -179,7 +134,7 @@ class CustomUrlStrategy extends UrlStrategy {
delegate.replaceState(state, title, url);

@override
Future<void> go(double count) => delegate.go(count);
Future<void> go(int count) => delegate.go(count.toDouble());
}

/// Encapsulates all calls to DOM apis, which allows the [UrlStrategy] classes
Expand Down
24 changes: 10 additions & 14 deletions lib/web_ui/lib/src/engine/test_embedding.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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, ''));
Expand Down Expand Up @@ -132,12 +133,12 @@ class TestUrlStrategy extends UrlStrategy {
}

@override
Future<void> go(double count) {
Future<void> 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();
}
Expand All @@ -148,16 +149,15 @@ class TestUrlStrategy extends UrlStrategy {
});
}

final List<DomEventListener> listeners = <DomEventListener>[];
final List<ui_web.PopStateListener> listeners = <ui_web.PopStateListener>[];

@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));
};
}

Expand All @@ -172,16 +172,12 @@ class TestUrlStrategy extends UrlStrategy {
/// like a real browser.
void _firePopStateEvent() {
assert(withinAppHistory);
final DomPopStateEvent event = createDomPopStateEvent(
'popstate',
<String, dynamic>{'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}');
}
}

Expand Down
Loading