Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 6 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 @@ -2005,6 +2005,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 @@ -4545,6 +4547,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
38 changes: 19 additions & 19 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,
})),
(_) {},
);
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
60 changes: 7 additions & 53 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 Down Expand Up @@ -150,15 +102,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((DomEvent event) =>
fn((event as DomPopStateEvent).state)
);

@override
String getPath() => delegate.getPath();
Expand Down
20 changes: 8 additions & 12 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 @@ -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
17 changes: 9 additions & 8 deletions lib/web_ui/lib/src/engine/window.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand All @@ -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<void> debugInitializeHistory(
UrlStrategy? strategy, {
ui_web.UrlStrategy? strategy, {
required bool useSingle,
}) async {
// Prevent any further customization of URL strategy.
Expand Down Expand Up @@ -334,7 +335,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();
Expand Down
8 changes: 8 additions & 0 deletions lib/web_ui/lib/ui_web/src/ui_web.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// 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
library ui_web;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
library ui_web;
library;

We now support name-less libraries.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I definitely don't want a nameless library though. This is a library that the framework (and maybe eventually users) will use by name.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name will map to the file name, though. tis fine

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See also: #40669

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sdk_rewriter will not be able to rewrite this file properly without this statement.


export 'ui_web/url_strategy.dart';
Loading