Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
qq_34031325
engine
提交
3905b9ec
E
engine
项目概览
qq_34031325
/
engine
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
E
engine
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
未验证
提交
3905b9ec
编写于
10月 07, 2020
作者:
D
Dan Field
提交者:
GitHub
10月 07, 2020
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Revert "[web] Support custom url strategies (#19134)" (#21687)
This reverts commit
02324994
.
上级
fbe68591
变更
11
隐藏空白更改
内联
并排
Showing
11 changed file
with
533 addition
and
444 deletion
+533
-444
ci/licenses_golden/licenses_flutter
ci/licenses_golden/licenses_flutter
+2
-3
lib/web_ui/lib/src/engine.dart
lib/web_ui/lib/src/engine.dart
+2
-3
lib/web_ui/lib/src/engine/browser_location.dart
lib/web_ui/lib/src/engine/browser_location.dart
+211
-0
lib/web_ui/lib/src/engine/history.dart
lib/web_ui/lib/src/engine/history.dart
+107
-106
lib/web_ui/lib/src/engine/navigation/js_url_strategy.dart
lib/web_ui/lib/src/engine/navigation/js_url_strategy.dart
+0
-78
lib/web_ui/lib/src/engine/test_embedding.dart
lib/web_ui/lib/src/engine/test_embedding.dart
+14
-12
lib/web_ui/lib/src/engine/window.dart
lib/web_ui/lib/src/engine/window.dart
+91
-111
lib/web_ui/lib/src/ui/initialization.dart
lib/web_ui/lib/src/ui/initialization.dart
+4
-0
lib/web_ui/test/engine/history_test.dart
lib/web_ui/test/engine/history_test.dart
+52
-75
lib/web_ui/test/engine/navigation_test.dart
lib/web_ui/test/engine/navigation_test.dart
+6
-8
lib/web_ui/test/window_test.dart
lib/web_ui/test/window_test.dart
+44
-48
未找到文件。
ci/licenses_golden/licenses_flutter
浏览文件 @
3905b9ec
...
...
@@ -426,6 +426,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/alarm_clock.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/assets.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/bitmap_canvas.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/browser_detection.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/browser_location.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvas_pool.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/canvas.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/canvaskit/canvaskit_api.dart
...
...
@@ -462,9 +463,7 @@ FILE: ../../../flutter/lib/web_ui/lib/src/engine/dom_canvas.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/dom_renderer.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/engine_canvas.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/frame_reference.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/history/history.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/history/js_url_strategy.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/history/url_strategy.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/history.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/backdrop_filter.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/canvas.dart
FILE: ../../../flutter/lib/web_ui/lib/src/engine/html/clip.dart
...
...
lib/web_ui/lib/src/engine.dart
浏览文件 @
3905b9ec
...
...
@@ -26,6 +26,7 @@ part 'engine/alarm_clock.dart';
part
'engine/assets.dart'
;
part
'engine/bitmap_canvas.dart'
;
part
'engine/browser_detection.dart'
;
part
'engine/browser_location.dart'
;
part
'engine/canvaskit/canvas.dart'
;
part
'engine/canvaskit/canvaskit_canvas.dart'
;
part
'engine/canvaskit/canvaskit_api.dart'
;
...
...
@@ -62,9 +63,7 @@ part 'engine/dom_canvas.dart';
part
'engine/dom_renderer.dart'
;
part
'engine/engine_canvas.dart'
;
part
'engine/frame_reference.dart'
;
part
'engine/navigation/history.dart'
;
part
'engine/navigation/js_url_strategy.dart'
;
part
'engine/navigation/url_strategy.dart'
;
part
'engine/history.dart'
;
part
'engine/html/backdrop_filter.dart'
;
part
'engine/html/canvas.dart'
;
part
'engine/html/clip.dart'
;
...
...
lib/web_ui/lib/src/engine/
navigation/url_strategy
.dart
→
lib/web_ui/lib/src/engine/
browser_location
.dart
浏览文件 @
3905b9ec
...
...
@@ -5,88 +5,76 @@
// @dart = 2.10
part of
engine
;
/// Represents and reads route state from the browser's URL.
// TODO(mdebbar): add other strategies.
// Some parts of this file were inspired/copied from the AngularDart router.
/// [LocationStrategy] is responsible for representing and reading 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
();
/// At the moment, only one strategy is implemented: [HashLocationStrategy].
///
/// This is used by [BrowserHistory] to interact with browser history APIs.
abstract
class
LocationStrategy
{
const
LocationStrategy
();
///
Adds a listener to the `popstate` event and returns a function that, when
///
invoked, removes the listener
.
ui
.
VoidCallback
addPopStateListener
(
html
.
EventListener
fn
);
///
Subscribes to popstate events and returns a function that could be used to
///
unsubscribe from popstate events
.
ui
.
VoidCallback
onPopState
(
html
.
EventListener
fn
);
///
Returns the active path in the browser
.
String
get
Path
()
;
///
The active path in the browser history
.
String
get
path
;
/// The state of the current browser history entry.
///
/// See: https://developer.mozilla.org/en-US/docs/Web/API/History/state
Object
?
getState
();
dynamic
get
state
;
/// 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
);
void
pushState
(
dynamic
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
);
void
replaceState
(
dynamic
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
(
int
count
);
/// Go to the previous history entry.
Future
<
void
>
back
({
int
count
=
1
});
}
/// This is an implementation of [
Url
Strategy] that uses the browser URL's
/// This is an implementation of [
Location
Strategy] that uses the browser URL's
/// [hash fragments](https://en.wikipedia.org/wiki/Uniform_Resource_Locator#Syntax)
/// to represent its state.
///
/// In order to use this [UrlStrategy] for an app, it needs to be set like this:
/// In order to use this [LocationStrategy] for an app, it needs to be set in
/// [ui.window.locationStrategy]:
///
/// ```dart
/// import 'package:flutter_web_plugins/flutter_web_plugins.dart';
/// import 'package:flutter_web/material.dart';
/// import 'package:flutter_web/ui.dart' as ui;
///
/// // Somewhere before calling `runApp()` do:
/// setUrlStrategy(const HashUrlStrategy());
/// void main() {
/// ui.window.locationStrategy = const ui.HashLocationStrategy();
/// runApp(MyApp());
/// }
/// ```
class
HashUrlStrategy
extends
UrlStrategy
{
/// Creates an instance of [HashUrlStrategy].
///
/// The [PlatformLocation] parameter is useful for testing to mock out browser
/// interations.
const
HashUrlStrategy
(
[
this
.
_platformLocation
=
const
BrowserPlatformLocation
()]);
class
HashLocationStrategy
extends
LocationStrategy
{
final
PlatformLocation
_platformLocation
;
const
HashLocationStrategy
(
[
this
.
_platformLocation
=
const
BrowserPlatformLocation
()]);
@override
ui
.
VoidCallback
addPopStateListener
(
html
.
EventListener
fn
)
{
_platformLocation
.
addPopStateListener
(
fn
);
return
()
=>
_platformLocation
.
removePopStateListener
(
fn
);
ui
.
VoidCallback
onPopState
(
html
.
EventListener
fn
)
{
_platformLocation
.
onPopState
(
fn
);
return
()
=>
_platformLocation
.
offPopState
(
fn
);
}
@override
String
get
Path
()
{
String
get
path
{
// the hash value is always prefixed with a `#`
// and if it is empty then it will stay empty
final
String
path
=
_platformLocation
.
hash
??
''
;
String
path
=
_platformLocation
.
hash
??
''
;
assert
(
path
.
isEmpty
||
path
.
startsWith
(
'#'
));
// We don't want to return an empty string as a path. Instead we default to "/".
...
...
@@ -98,7 +86,7 @@ class HashUrlStrategy extends UrlStrategy {
}
@override
Object
?
getState
()
=>
_platformLocation
.
state
;
dynamic
get
state
=>
_platformLocation
.
state
;
@override
String
prepareExternalUrl
(
String
internalUrl
)
{
...
...
@@ -112,29 +100,29 @@ class HashUrlStrategy extends UrlStrategy {
}
@override
void
pushState
(
Object
?
state
,
String
title
,
String
url
)
{
void
pushState
(
dynamic
state
,
String
title
,
String
url
)
{
_platformLocation
.
pushState
(
state
,
title
,
prepareExternalUrl
(
url
));
}
@override
void
replaceState
(
Object
?
state
,
String
title
,
String
url
)
{
void
replaceState
(
dynamic
state
,
String
title
,
String
url
)
{
_platformLocation
.
replaceState
(
state
,
title
,
prepareExternalUrl
(
url
));
}
@override
Future
<
void
>
go
(
int
count
)
{
_platformLocation
.
go
(
count
);
Future
<
void
>
back
({
int
count
=
1
}
)
{
_platformLocation
.
back
(
count
);
return
_waitForPopState
();
}
/// Waits until the next popstate event is fired.
///
/// This is useful
, for example,
to wait until the browser has handled the
/// This is useful
for example
to wait until the browser has handled the
/// `history.back` transition.
Future
<
void
>
_waitForPopState
()
{
final
Completer
<
void
>
completer
=
Completer
<
void
>();
late
ui
.
VoidCallback
unsubscribe
;
unsubscribe
=
addPopStateListener
((
_
)
{
unsubscribe
=
onPopState
((
_
)
{
unsubscribe
();
completer
.
complete
();
});
...
...
@@ -142,128 +130,58 @@ class HashUrlStrategy extends UrlStrategy {
}
}
/// Wraps a custom implementation of [UrlStrategy] that was previously converted
/// to a [JsUrlStrategy].
class
CustomUrlStrategy
extends
UrlStrategy
{
/// Wraps the [delegate] in a [CustomUrlStrategy] instance.
CustomUrlStrategy
.
fromJs
(
this
.
delegate
);
final
JsUrlStrategy
delegate
;
@override
ui
.
VoidCallback
addPopStateListener
(
html
.
EventListener
fn
)
=>
delegate
.
addPopStateListener
(
fn
);
@override
String
getPath
()
=>
delegate
.
getPath
();
@override
Object
?
getState
()
=>
delegate
.
getState
();
@override
String
prepareExternalUrl
(
String
internalUrl
)
=>
delegate
.
prepareExternalUrl
(
internalUrl
);
@override
void
pushState
(
Object
?
state
,
String
title
,
String
url
)
=>
delegate
.
pushState
(
state
,
title
,
url
);
@override
void
replaceState
(
Object
?
state
,
String
title
,
String
url
)
=>
delegate
.
replaceState
(
state
,
title
,
url
);
@override
Future
<
void
>
go
(
int
count
)
=>
delegate
.
go
(
count
);
}
/// Encapsulates all calls to DOM apis, which allows the [UrlStrategy] classes
/// to be platform agnostic and testable.
/// [PlatformLocation] encapsulates all calls to DOM apis, which allows the
/// [LocationStrategy] classes to be platform agnostic and testable.
///
/// For convenience, the [PlatformLocation] class can be used by implementations
/// of [UrlStrategy] to interact with DOM apis like pushState, popState, etc.
/// The [PlatformLocation] class is used directly by all implementations of
/// [LocationStrategy] when they need to interact with the DOM apis like
/// pushState, popState, etc...
abstract
class
PlatformLocation
{
/// Abstract const constructor. This constructor enables subclasses to provide
/// const constructors so that they can be used in const expressions.
const
PlatformLocation
();
/// Registers an event listener for the `popstate` event.
///
/// See: https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onpopstate
void
addPopStateListener
(
html
.
EventListener
fn
);
void
onPopState
(
html
.
EventListener
fn
);
void
offPopState
(
html
.
EventListener
fn
);
/// Unregisters the given listener (added by [addPopStateListener]) from the
/// `popstate` event.
///
/// See: https://developer.mozilla.org/en-US/docs/Web/API/WindowEventHandlers/onpopstate
void
removePopStateListener
(
html
.
EventListener
fn
);
void
onHashChange
(
html
.
EventListener
fn
);
void
offHashChange
(
html
.
EventListener
fn
);
/// The `pathname` part of the URL in the browser address bar.
///
/// See: https://developer.mozilla.org/en-US/docs/Web/API/Location/pathname
String
get
pathname
;
/// The `query` part of the URL in the browser address bar.
///
/// See: https://developer.mozilla.org/en-US/docs/Web/API/Location/search
String
get
search
;
/// The `hash]` part of the URL in the browser address bar.
///
/// See: https://developer.mozilla.org/en-US/docs/Web/API/Location/hash
String
?
get
hash
;
dynamic
get
state
;
/// The `state` in the current history entry.
///
/// See: https://developer.mozilla.org/en-US/docs/Web/API/History/state
Object
?
get
state
;
/// Adds a new entry to the browser history stack.
///
/// See: https://developer.mozilla.org/en-US/docs/Web/API/History/pushState
void
pushState
(
Object
?
state
,
String
title
,
String
url
);
/// Replaces the current entry in the browser history stack.
///
/// 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
void
go
(
int
count
);
/// The base href where the Flutter app is being served.
///
/// See: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
String
?
getBaseHref
();
void
pushState
(
dynamic
state
,
String
title
,
String
url
);
void
replaceState
(
dynamic
state
,
String
title
,
String
url
);
void
back
(
int
count
);
}
///
Delegates to real browser APIs to provide platform location functionality
.
///
An implementation of [PlatformLocation] for the browser
.
class
BrowserPlatformLocation
extends
PlatformLocation
{
/// Default constructor for [BrowserPlatformLocation].
const
BrowserPlatformLocation
();
html
.
Location
get
_location
=>
html
.
window
.
location
;
html
.
History
get
_history
=>
html
.
window
.
history
;
const
BrowserPlatformLocation
();
@override
void
addPopStateListener
(
html
.
EventListener
fn
)
{
void
onPopState
(
html
.
EventListener
fn
)
{
html
.
window
.
addEventListener
(
'popstate'
,
fn
);
}
@override
void
removePopStateListener
(
html
.
EventListener
fn
)
{
void
offPopState
(
html
.
EventListener
fn
)
{
html
.
window
.
removeEventListener
(
'popstate'
,
fn
);
}
@override
void
onHashChange
(
html
.
EventListener
fn
)
{
html
.
window
.
addEventListener
(
'hashchange'
,
fn
);
}
@override
void
offHashChange
(
html
.
EventListener
fn
)
{
html
.
window
.
removeEventListener
(
'hashchange'
,
fn
);
}
@override
String
get
pathname
=>
_location
.
pathname
!;
...
...
@@ -274,23 +192,20 @@ class BrowserPlatformLocation extends PlatformLocation {
String
get
hash
=>
_location
.
hash
;
@override
Object
?
get
state
=>
_history
.
state
;
dynamic
get
state
=>
_history
.
state
;
@override
void
pushState
(
Object
?
state
,
String
title
,
String
url
)
{
void
pushState
(
dynamic
state
,
String
title
,
String
url
)
{
_history
.
pushState
(
state
,
title
,
url
);
}
@override
void
replaceState
(
Object
?
state
,
String
title
,
String
url
)
{
void
replaceState
(
dynamic
state
,
String
title
,
String
url
)
{
_history
.
replaceState
(
state
,
title
,
url
);
}
@override
void
go
(
int
count
)
{
_history
.
go
(
count
);
void
back
(
int
count
)
{
_history
.
go
(
-
count
);
}
@override
String
?
getBaseHref
()
=>
html
.
document
.
baseUri
;
}
lib/web_ui/lib/src/engine/
navigation/
history.dart
→
lib/web_ui/lib/src/engine/history.dart
浏览文件 @
3905b9ec
...
...
@@ -25,39 +25,64 @@ abstract class BrowserHistory {
late
ui
.
VoidCallback
_unsubscribe
;
/// The strategy to interact with html browser history.
UrlStrategy
?
get
urlStrategy
;
LocationStrategy
?
get
locationStrategy
=>
_locationStrategy
;
LocationStrategy
?
_locationStrategy
;
/// Updates the strategy.
///
/// This method will also remove any previous modifications to the html
/// browser history and start anew.
Future
<
void
>
setLocationStrategy
(
LocationStrategy
?
strategy
)
async
{
if
(
strategy
!=
_locationStrategy
)
{
await
_tearoffStrategy
(
_locationStrategy
);
_locationStrategy
=
strategy
;
await
_setupStrategy
(
_locationStrategy
);
}
}
bool
_isDisposed
=
false
;
Future
<
void
>
_setupStrategy
(
LocationStrategy
?
strategy
)
async
{
if
(
strategy
==
null
)
{
return
;
}
_unsubscribe
=
strategy
.
onPopState
(
onPopState
as
dynamic
Function
(
html
.
Event
));
await
setup
();
}
void
_setupStrategy
(
UrlStrategy
strategy
)
{
_unsubscribe
=
strategy
.
addPopStateListener
(
onPopState
as
html
.
EventListener
,
);
Future
<
void
>
_tearoffStrategy
(
LocationStrategy
?
strategy
)
async
{
if
(
strategy
==
null
)
{
return
;
}
_unsubscribe
();
await
tearDown
();
}
/// Exit this application and return to the previous page.
Future
<
void
>
exit
()
async
{
if
(
url
Strategy
!=
null
)
{
await
tearDown
(
);
if
(
_location
Strategy
!=
null
)
{
await
_tearoffStrategy
(
_locationStrategy
);
// Now the history should be in the original state, back one more time to
// exit the application.
await
urlStrategy
!.
go
(-
1
);
await
_locationStrategy
!.
back
();
_locationStrategy
=
null
;
}
}
/// This method does the same thing as the browser back button.
Future
<
void
>
back
()
async
{
return
urlStrategy
?.
go
(-
1
);
Future
<
void
>
back
()
{
if
(
locationStrategy
!=
null
)
{
return
locationStrategy
!.
back
();
}
return
Future
<
void
>.
value
();
}
/// The path of the current location of the user's browser.
String
get
currentPath
=>
urlStrategy
?.
getPath
()
??
'/'
;
String
get
currentPath
=>
locationStrategy
?.
path
??
'/'
;
/// The state of the current location of the user's browser.
Object
?
get
currentState
=>
urlStrategy
?.
getState
()
;
dynamic
get
currentState
=>
locationStrategy
?.
state
;
/// Update the url with the given [routeName] and [state].
void
setRouteName
(
String
?
routeName
,
{
Object
?
state
});
void
setRouteName
(
String
?
routeName
,
{
dynamic
?
state
});
/// A callback method to handle browser backward or forward buttons.
///
...
...
@@ -65,9 +90,12 @@ abstract class BrowserHistory {
/// applications accordingly.
void
onPopState
(
covariant
html
.
PopStateEvent
event
);
/// Sets up any prerequisites to use this browser history class.
Future
<
void
>
setup
()
=>
Future
<
void
>.
value
();
/// Restore any modifications to the html browser history during the lifetime
/// of this class.
Future
<
void
>
tearDown
();
Future
<
void
>
tearDown
()
=>
Future
<
void
>.
value
()
;
}
/// A browser history class that creates a set of browser history entries to
...
...
@@ -85,51 +113,31 @@ abstract class BrowserHistory {
/// * [SingleEntryBrowserHistory], which is used when the framework does not use
/// a Router for routing.
class
MultiEntriesBrowserHistory
extends
BrowserHistory
{
MultiEntriesBrowserHistory
({
required
this
.
urlStrategy
})
{
final
UrlStrategy
?
strategy
=
urlStrategy
;
if
(
strategy
==
null
)
{
return
;
}
_setupStrategy
(
strategy
);
if
(!
_hasSerialCount
(
currentState
))
{
strategy
.
replaceState
(
_tagWithSerialCount
(
currentState
,
0
),
'flutter'
,
currentPath
);
}
// If we restore from a page refresh, the _currentSerialCount may not be 0.
_lastSeenSerialCount
=
_currentSerialCount
;
}
@override
final
UrlStrategy
?
urlStrategy
;
late
int
_lastSeenSerialCount
;
int
get
_currentSerialCount
{
if
(
_hasSerialCount
(
currentState
))
{
final
Map
<
dynamic
,
dynamic
>
stateMap
=
currentState
as
Map
<
dynamic
,
dynamic
>;
return
stateMap
[
'serialCount'
]
as
int
;
return
currentState
[
'serialCount'
]
as
int
;
}
return
0
;
}
Object
_tagWithSerialCount
(
Object
?
originialState
,
int
count
)
{
return
<
dynamic
,
dynamic
>{
dynamic
_tagWithSerialCount
(
dynamic
originialState
,
int
count
)
{
return
<
dynamic
,
dynamic
>
{
'serialCount'
:
count
,
'state'
:
originialState
,
};
}
bool
_hasSerialCount
(
Object
?
state
)
{
bool
_hasSerialCount
(
dynamic
state
)
{
return
state
is
Map
&&
state
[
'serialCount'
]
!=
null
;
}
@override
void
setRouteName
(
String
?
routeName
,
{
Object
?
state
})
{
if
(
url
Strategy
!=
null
)
{
void
setRouteName
(
String
?
routeName
,
{
dynamic
?
state
})
{
if
(
location
Strategy
!=
null
)
{
assert
(
routeName
!=
null
);
_lastSeenSerialCount
+=
1
;
url
Strategy
!.
pushState
(
location
Strategy
!.
pushState
(
_tagWithSerialCount
(
state
,
_lastSeenSerialCount
),
'flutter'
,
routeName
!,
...
...
@@ -139,51 +147,58 @@ class MultiEntriesBrowserHistory extends BrowserHistory {
@override
void
onPopState
(
covariant
html
.
PopStateEvent
event
)
{
assert
(
url
Strategy
!=
null
);
assert
(
location
Strategy
!=
null
);
// May be a result of direct url access while the flutter application is
// already running.
if
(!
_hasSerialCount
(
event
.
state
))
{
// In this case we assume this will be the next history entry from the
// last seen entry.
url
Strategy
!.
replaceState
(
_tagWithSerialCount
(
event
.
state
,
_lastSeenSerialCount
+
1
),
'flutter'
,
currentPath
);
location
Strategy
!.
replaceState
(
_tagWithSerialCount
(
event
.
state
,
_lastSeenSerialCount
+
1
),
'flutter'
,
currentPath
);
}
_lastSeenSerialCount
=
_currentSerialCount
;
if
(
window
.
_onPlatformMessage
!=
null
)
{
window
.
invokeOnPlatformMessage
(
'flutter/navigation'
,
const
JSONMethodCodec
().
encodeMethodCall
(
MethodCall
(
'pushRouteInformation'
,
<
dynamic
,
dynamic
>{
'location'
:
currentPath
,
'state'
:
event
.
state
?[
'state'
],
})),
MethodCall
(
'pushRouteInformation'
,
<
dynamic
,
dynamic
>{
'location'
:
currentPath
,
'state'
:
event
.
state
?[
'state'
],
})
),
(
_
)
{},
);
}
}
@override
Future
<
void
>
tearDown
()
async
{
if
(
_isDisposed
||
urlStrategy
==
null
)
{
return
;
Future
<
void
>
setup
()
{
if
(!
_hasSerialCount
(
currentState
))
{
locationStrategy
!.
replaceState
(
_tagWithSerialCount
(
currentState
,
0
),
'flutter'
,
currentPath
);
}
_isDisposed
=
true
;
_unsubscribe
();
// If we retore from a page refresh, the _currentSerialCount may not be 0.
_lastSeenSerialCount
=
_currentSerialCount
;
return
Future
<
void
>.
value
();
}
@override
Future
<
void
>
tearDown
()
async
{
// Restores the html browser history.
assert
(
_hasSerialCount
(
currentState
));
int
backCount
=
_currentSerialCount
;
if
(
backCount
>
0
)
{
await
urlStrategy
!.
go
(-
backCount
);
await
locationStrategy
!.
back
(
count:
backCount
);
}
// Unwrap state.
assert
(
_hasSerialCount
(
currentState
)
&&
_currentSerialCount
==
0
);
final
Map
<
dynamic
,
dynamic
>
stateMap
=
currentState
as
Map
<
dynamic
,
dynamic
>;
urlStrategy
!.
replaceState
(
stateMap
[
'state'
],
locationStrategy
!.
replaceState
(
currentState
[
'state'
],
'flutter'
,
currentPath
,
);
...
...
@@ -207,61 +222,37 @@ class MultiEntriesBrowserHistory extends BrowserHistory {
/// * [MultiEntriesBrowserHistory], which is used when the framework uses a
/// Router for routing.
class
SingleEntryBrowserHistory
extends
BrowserHistory
{
SingleEntryBrowserHistory
({
required
this
.
urlStrategy
})
{
final
UrlStrategy
?
strategy
=
urlStrategy
;
if
(
strategy
==
null
)
{
return
;
}
_setupStrategy
(
strategy
);
final
String
path
=
currentPath
;
if
(!
_isFlutterEntry
(
html
.
window
.
history
.
state
))
{
// An entry may not have come from Flutter, for example, when the user
// refreshes the page. They land directly on the "flutter" entry, so
// there's no need to setup the "origin" and "flutter" entries, we can
// safely assume they are already setup.
_setupOriginEntry
(
strategy
);
_setupFlutterEntry
(
strategy
,
replace:
false
,
path:
path
);
}
}
@override
final
UrlStrategy
?
urlStrategy
;
static
const
MethodCall
_popRouteMethodCall
=
MethodCall
(
'popRoute'
);
static
const
String
_kFlutterTag
=
'flutter'
;
static
const
String
_kOriginTag
=
'origin'
;
Map
<
String
,
dynamic
>
_wrapOriginState
(
Object
?
state
)
{
Map
<
String
,
dynamic
>
_wrapOriginState
(
dynamic
state
)
{
return
<
String
,
dynamic
>{
_kOriginTag:
true
,
'state'
:
state
};
}
Object
?
_unwrapOriginState
(
Object
?
state
)
{
dynamic
_unwrapOriginState
(
dynamic
state
)
{
assert
(
_isOriginEntry
(
state
));
final
Map
<
dynamic
,
dynamic
>
originState
=
state
as
Map
<
dynamic
,
dynamic
>;
return
originState
[
'state'
];
}
Map
<
String
,
bool
>
_flutterState
=
<
String
,
bool
>{
_kFlutterTag:
true
};
/// The origin entry is the history entry that the Flutter app landed on. It's
/// created by the browser when the user navigates to the url of the app.
bool
_isOriginEntry
(
Object
?
state
)
{
bool
_isOriginEntry
(
dynamic
state
)
{
return
state
is
Map
&&
state
[
_kOriginTag
]
==
true
;
}
/// The flutter entry is a history entry that we maintain on top of the origin
/// entry. It allows us to catch popstate events when the user hits the back
/// button.
bool
_isFlutterEntry
(
Object
?
state
)
{
bool
_isFlutterEntry
(
dynamic
state
)
{
return
state
is
Map
&&
state
[
_kFlutterTag
]
==
true
;
}
@override
void
setRouteName
(
String
?
routeName
,
{
Object
?
state
})
{
if
(
url
Strategy
!=
null
)
{
_setupFlutterEntry
(
url
Strategy
!,
replace:
true
,
path:
routeName
);
void
setRouteName
(
String
?
routeName
,
{
dynamic
?
state
})
{
if
(
location
Strategy
!=
null
)
{
_setupFlutterEntry
(
location
Strategy
!,
replace:
true
,
path:
routeName
);
}
}
...
...
@@ -269,7 +260,7 @@ class SingleEntryBrowserHistory extends BrowserHistory {
@override
void
onPopState
(
covariant
html
.
PopStateEvent
event
)
{
if
(
_isOriginEntry
(
event
.
state
))
{
_setupFlutterEntry
(
url
Strategy
!);
_setupFlutterEntry
(
_location
Strategy
!);
// 2. Send a 'popRoute' platform message so the app can handle it accordingly.
if
(
window
.
_onPlatformMessage
!=
null
)
{
...
...
@@ -311,14 +302,14 @@ class SingleEntryBrowserHistory extends BrowserHistory {
// 2. Then we remove the new entry.
// This will take us back to our "flutter" entry and it causes a new
// popstate event that will be handled in the "else if" section above.
urlStrategy
!.
go
(-
1
);
_locationStrategy
!.
back
(
);
}
}
/// 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
(
Url
Strategy
strategy
)
{
void
_setupOriginEntry
(
Location
Strategy
strategy
)
{
assert
(
strategy
!=
null
);
// ignore: unnecessary_null_comparison
strategy
.
replaceState
(
_wrapOriginState
(
currentState
),
'origin'
,
''
);
}
...
...
@@ -326,7 +317,7 @@ class SingleEntryBrowserHistory extends BrowserHistory {
/// This method is used manipulate the Flutter Entry which is always the
/// active entry while the Flutter app is running.
void
_setupFlutterEntry
(
Url
Strategy
strategy
,
{
Location
Strategy
strategy
,
{
bool
replace
=
false
,
String
?
path
,
})
{
...
...
@@ -340,17 +331,27 @@ class SingleEntryBrowserHistory extends BrowserHistory {
}
@override
Future
<
void
>
tearDown
()
async
{
if
(
_isDisposed
||
urlStrategy
==
null
)
{
return
;
Future
<
void
>
setup
()
{
final
String
path
=
currentPath
;
if
(
_isFlutterEntry
(
html
.
window
.
history
.
state
))
{
// This could happen if the user, for example, refreshes the page. They
// will land directly on the "flutter" entry, so there's no need to setup
// the "origin" and "flutter" entries, we can safely assume they are
// already setup.
}
else
{
_setupOriginEntry
(
locationStrategy
!);
_setupFlutterEntry
(
locationStrategy
!,
replace:
false
,
path:
path
);
}
_isDisposed
=
true
;
_unsubscribe
();
return
Future
<
void
>.
value
()
;
}
// We need to remove the flutter entry that we pushed in setup.
await
urlStrategy
!.
go
(-
1
);
// Restores original state.
urlStrategy
!
.
replaceState
(
_unwrapOriginState
(
currentState
),
'flutter'
,
currentPath
);
@override
Future
<
void
>
tearDown
()
async
{
if
(
locationStrategy
!=
null
)
{
// We need to remove the flutter entry that we pushed in setup.
await
locationStrategy
!.
back
();
// Restores original state.
locationStrategy
!.
replaceState
(
_unwrapOriginState
(
currentState
),
'flutter'
,
currentPath
);
}
}
}
lib/web_ui/lib/src/engine/navigation/js_url_strategy.dart
已删除
100644 → 0
浏览文件 @
fbe68591
// 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.
// @dart = 2.10
part of
engine
;
typedef
_PathGetter
=
String
Function
();
typedef
_StateGetter
=
Object
?
Function
();
typedef
_AddPopStateListener
=
ui
.
VoidCallback
Function
(
html
.
EventListener
);
typedef
_StringToString
=
String
Function
(
String
);
typedef
_StateOperation
=
void
Function
(
Object
?
state
,
String
title
,
String
url
);
typedef
_HistoryMove
=
Future
<
void
>
Function
(
int
count
);
/// The JavaScript representation of a URL strategy.
///
/// This is used to pass URL strategy implementations across a JS-interop
/// bridge from the app to the engine.
@JS
()
@anonymous
abstract
class
JsUrlStrategy
{
/// Creates an instance of [JsUrlStrategy] from a bag of URL strategy
/// functions.
external
factory
JsUrlStrategy
({
required
_PathGetter
getPath
,
required
_StateGetter
getState
,
required
_AddPopStateListener
addPopStateListener
,
required
_StringToString
prepareExternalUrl
,
required
_StateOperation
pushState
,
required
_StateOperation
replaceState
,
required
_HistoryMove
go
,
});
/// Adds a listener to the `popstate` event and returns a function that, when
/// invoked, removes the listener.
external
ui
.
VoidCallback
addPopStateListener
(
html
.
EventListener
fn
);
/// Returns the active path in the browser.
external
String
getPath
();
/// Returns the history state in the browser.
///
/// See: https://developer.mozilla.org/en-US/docs/Web/API/History/state
external
Object
?
getState
();
/// Given a path that's internal to the app, create the external url that
/// will be used in the browser.
external
String
prepareExternalUrl
(
String
internalUrl
);
/// Push a new history entry.
///
/// See: https://developer.mozilla.org/en-US/docs/Web/API/History/pushState
external
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
external
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
external
Future
<
void
>
go
(
int
count
);
}
lib/web_ui/lib/src/engine/test_embedding.dart
浏览文件 @
3905b9ec
...
...
@@ -20,27 +20,29 @@ class TestHistoryEntry {
}
}
/// This
URL
strategy mimics the browser's history as closely as possible
/// This
location
strategy mimics the browser's history as closely as possible
/// while doing it all in memory with no interaction with the browser.
///
/// It keeps a list of history entries and event listeners in memory and
/// manipulates them in order to achieve the desired functionality.
class
Test
UrlStrategy
extends
Url
Strategy
{
/// Creates a instance of [Test
Url
Strategy] with an empty string as the
class
Test
LocationStrategy
extends
Location
Strategy
{
/// Creates a instance of [Test
Location
Strategy] with an empty string as the
/// path.
factory
Test
UrlStrategy
()
=>
TestUrl
Strategy
.
fromEntry
(
TestHistoryEntry
(
null
,
null
,
''
));
factory
Test
LocationStrategy
()
=>
TestLocation
Strategy
.
fromEntry
(
TestHistoryEntry
(
null
,
null
,
''
));
/// Creates an instance of [Test
Url
Strategy] and populates it with a list
/// Creates an instance of [Test
Location
Strategy] and populates it with a list
/// that has [initialEntry] as the only item.
Test
Url
Strategy
.
fromEntry
(
TestHistoryEntry
initialEntry
)
Test
Location
Strategy
.
fromEntry
(
TestHistoryEntry
initialEntry
)
:
_currentEntryIndex
=
0
,
history
=
<
TestHistoryEntry
>[
initialEntry
];
@override
String
get
Path
()
=>
currentEntry
.
url
;
String
get
path
=>
currentEntry
.
url
;
@override
dynamic
getState
()
=>
currentEntry
.
state
;
dynamic
get
state
{
return
currentEntry
.
state
;
}
int
_currentEntryIndex
;
int
get
currentEntryIndex
=>
_currentEntryIndex
;
...
...
@@ -103,12 +105,12 @@ class TestUrlStrategy extends UrlStrategy {
}
@override
Future
<
void
>
go
(
int
count
)
{
Future
<
void
>
back
({
int
count
=
1
}
)
{
assert
(
withinAppHistory
);
// Browsers don't move in history immediately. They do it at the next
// Browsers don't move
back
in history immediately. They do it at the next
// event loop. So let's simulate that.
return
_nextEventLoop
(()
{
_currentEntryIndex
=
_currentEntryIndex
+
count
;
_currentEntryIndex
=
_currentEntryIndex
-
count
;
if
(
withinAppHistory
)
{
_firePopStateEvent
();
}
...
...
@@ -122,7 +124,7 @@ class TestUrlStrategy extends UrlStrategy {
final
List
<
html
.
EventListener
>
listeners
=
<
html
.
EventListener
>[];
@override
ui
.
VoidCallback
addPopStateListener
(
html
.
EventListener
fn
)
{
ui
.
VoidCallback
onPopState
(
html
.
EventListener
fn
)
{
listeners
.
add
(
fn
);
return
()
{
// Schedule a micro task here to avoid removing the listener during
...
...
lib/web_ui/lib/src/engine/window.dart
浏览文件 @
3905b9ec
...
...
@@ -13,27 +13,20 @@ const bool _debugPrintPlatformMessages = false;
/// This may be overridden in tests, for example, to pump fake frames.
ui
.
VoidCallback
?
scheduleFrameCallback
;
typedef
_JsSetUrlStrategy
=
void
Function
(
JsUrlStrategy
?);
/// A JavaScript hook to customize the URL strategy of a Flutter app.
//
// Keep this js name in sync with flutter_web_plugins. Find it at:
// https://github.com/flutter/flutter/blob/custom_location_strategy/packages/flutter_web_plugins/lib/src/navigation/js_url_strategy.dart
//
// TODO: Add integration test https://github.com/flutter/flutter/issues/66852
@JS
(
'_flutter_web_set_location_strategy'
)
external
set
_jsSetUrlStrategy
(
_JsSetUrlStrategy
?
newJsSetUrlStrategy
);
/// The Web implementation of [ui.Window].
class
EngineWindow
extends
ui
.
Window
{
EngineWindow
()
{
_addBrightnessMediaQueryListener
();
_addUrlStrategyListener
();
js
.
context
[
'_flutter_web_set_location_strategy'
]
=
(
LocationStrategy
strategy
)
{
locationStrategy
=
strategy
;
};
registerHotRestartListener
(()
{
js
.
context
[
'_flutter_web_set_location_strategy'
]
=
null
;
});
}
@override
double
get
devicePixelRatio
=>
_debugDevicePixelRatio
??
browserDevicePixelRatio
;
double
get
devicePixelRatio
=>
_debugDevicePixelRatio
??
browserDevicePixelRatio
;
/// Returns device pixel ratio returned by browser.
static
double
get
browserDevicePixelRatio
{
...
...
@@ -124,8 +117,7 @@ class EngineWindow extends ui.Window {
double
height
=
0
;
double
width
=
0
;
if
(
html
.
window
.
visualViewport
!=
null
)
{
height
=
html
.
window
.
visualViewport
!.
height
!.
toDouble
()
*
devicePixelRatio
;
height
=
html
.
window
.
visualViewport
!.
height
!.
toDouble
()
*
devicePixelRatio
;
width
=
html
.
window
.
visualViewport
!.
width
!.
toDouble
()
*
devicePixelRatio
;
}
else
{
height
=
html
.
window
.
innerHeight
!
*
devicePixelRatio
;
...
...
@@ -134,7 +126,7 @@ class EngineWindow extends ui.Window {
// This method compares the new dimensions with the previous ones.
// Return false if the previous dimensions are not set.
if
(
_physicalSize
!=
null
)
{
if
(
_physicalSize
!=
null
)
{
// First confirm both height and width are effected.
if
(
_physicalSize
!.
height
!=
height
&&
_physicalSize
!.
width
!=
width
)
{
// If prior to rotation height is bigger than width it should be the
...
...
@@ -162,41 +154,78 @@ class EngineWindow extends ui.Window {
/// Handles the browser history integration to allow users to use the back
/// button, etc.
@visibleForTesting
BrowserHistory
get
browserHistory
{
return
_browserHistory
??=
MultiEntriesBrowserHistory
(
urlStrategy:
const
HashUrlStrategy
());
}
BrowserHistory
get
browserHistory
=>
_browserHistory
;
BrowserHistory
_browserHistory
=
MultiEntriesBrowserHistory
();
BrowserHistory
?
_browserHistory
;
@visibleForTesting
Future
<
void
>
debugSwitchBrowserHistory
({
required
bool
useSingle
})
async
{
if
(
useSingle
)
await
_useSingleEntryBrowserHistory
();
else
await
_useMultiEntryBrowserHistory
();
}
/// This function should only be used for test setup. In real application, we
/// only allow one time switch from the MultiEntriesBrowserHistory to
/// the SingleEntryBrowserHistory to prevent the application to switch back
/// forth between router and non-router.
Future
<
void
>
_useMultiEntryBrowserHistory
()
async
{
if
(
_browserHistory
is
MultiEntriesBrowserHistory
)
{
return
;
}
final
LocationStrategy
?
strategy
=
_browserHistory
.
locationStrategy
;
if
(
strategy
!=
null
)
await
_browserHistory
.
setLocationStrategy
(
null
);
_browserHistory
=
MultiEntriesBrowserHistory
();
if
(
strategy
!=
null
)
await
_browserHistory
.
setLocationStrategy
(
strategy
);
}
Future
<
void
>
_useSingleEntryBrowserHistory
()
async
{
if
(
_browserHistory
is
SingleEntryBrowserHistory
)
{
return
;
}
final
UrlStrategy
?
strategy
=
_browserHistory
?.
urlStrategy
;
await
_browserHistory
?.
tearDown
();
_browserHistory
=
SingleEntryBrowserHistory
(
urlStrategy:
strategy
);
final
LocationStrategy
?
strategy
=
_browserHistory
.
locationStrategy
;
if
(
strategy
!=
null
)
await
_browserHistory
.
setLocationStrategy
(
null
);
_browserHistory
=
SingleEntryBrowserHistory
();
if
(
strategy
!=
null
)
await
_browserHistory
.
setLocationStrategy
(
strategy
);
}
/// Simulates clicking the browser's back button.
Future
<
void
>
webOnlyBack
()
=>
_browserHistory
.
back
();
/// Lazily initialized when the `defaultRouteName` getter is invoked.
///
/// The reason for the lazy initialization is to give enough time for the app to set [
url
Strategy]
/// The reason for the lazy initialization is to give enough time for the app to set [
location
Strategy]
/// in `lib/src/ui/initialization.dart`.
String
?
_defaultRouteName
;
@override
String
get
defaultRouteName
{
return
_defaultRouteName
??=
browserHistory
.
currentPath
;
}
String
get
defaultRouteName
=>
_defaultRouteName
??=
_browserHistory
.
currentPath
;
@override
void
scheduleFrame
()
{
if
(
scheduleFrameCallback
==
null
)
{
throw
new
Exception
(
'scheduleFrameCallback must be initialized first.'
);
throw
new
Exception
(
'scheduleFrameCallback must be initialized first.'
);
}
scheduleFrameCallback
!();
}
/// Change the strategy to use for handling browser history location.
/// Setting this member will automatically update [_browserHistory].
///
/// By setting this to null, the browser history will be disabled.
set
locationStrategy
(
LocationStrategy
?
strategy
)
{
_browserHistory
.
setLocationStrategy
(
strategy
);
}
/// Returns the currently active location strategy.
@visibleForTesting
LocationStrategy
?
get
locationStrategy
=>
_browserHistory
.
locationStrategy
;
@override
ui
.
VoidCallback
?
get
onTextScaleFactorChanged
=>
_onTextScaleFactorChanged
;
ui
.
VoidCallback
?
_onTextScaleFactorChanged
;
...
...
@@ -448,8 +477,8 @@ class EngineWindow extends ui.Window {
/// Engine code should use this method instead of the callback directly.
/// Otherwise zones won't work properly.
void
invokeOnPlatformMessage
(
String
name
,
ByteData
?
data
,
ui
.
PlatformMessageResponseCallback
callback
)
{
void
invokeOnPlatformMessage
(
String
name
,
ByteData
?
data
,
ui
.
PlatformMessageResponseCallback
callback
)
{
_invoke3
<
String
,
ByteData
?,
ui
.
PlatformMessageResponseCallback
>(
_onPlatformMessage
,
_onPlatformMessageZone
,
...
...
@@ -471,9 +500,7 @@ class EngineWindow extends ui.Window {
/// Wraps the given [callback] in another callback that ensures that the
/// original callback is called in the zone it was registered in.
static
ui
.
PlatformMessageResponseCallback
?
_zonedPlatformMessageResponseCallback
(
ui
.
PlatformMessageResponseCallback
?
callback
)
{
static
ui
.
PlatformMessageResponseCallback
?
_zonedPlatformMessageResponseCallback
(
ui
.
PlatformMessageResponseCallback
?
callback
)
{
if
(
callback
==
null
)
{
return
null
;
}
...
...
@@ -537,7 +564,7 @@ class EngineWindow extends ui.Window {
final
MethodCall
decoded
=
codec
.
decodeMethodCall
(
data
);
switch
(
decoded
.
method
)
{
case
'SystemNavigator.pop'
:
browserHistory
.
exit
().
then
((
_
)
{
_
browserHistory
.
exit
().
then
((
_
)
{
_replyToPlatformMessage
(
callback
,
codec
.
encodeSuccessEnvelope
(
true
));
});
...
...
@@ -558,8 +585,8 @@ class EngineWindow extends ui.Window {
case
'SystemChrome.setPreferredOrientations'
:
final
List
<
dynamic
>?
arguments
=
decoded
.
arguments
;
domRenderer
.
setPreferredOrientation
(
arguments
).
then
((
bool
success
)
{
_replyToPlatformMessage
(
callback
,
codec
.
encodeSuccessEnvelope
(
success
));
_replyToPlatformMessage
(
callback
,
codec
.
encodeSuccessEnvelope
(
success
));
});
return
;
case
'SystemSound.play'
:
...
...
@@ -605,8 +632,7 @@ class EngineWindow extends ui.Window {
case
'flutter/platform_views'
:
if
(
experimentalUseSkia
)
{
rasterizer
!.
surface
.
viewEmbedder
.
handlePlatformViewCall
(
data
,
callback
);
rasterizer
!.
surface
.
viewEmbedder
.
handlePlatformViewCall
(
data
,
callback
);
}
else
{
ui
.
handlePlatformViewCall
(
data
!,
callback
!);
}
...
...
@@ -620,11 +646,27 @@ class EngineWindow extends ui.Window {
return
;
case
'flutter/navigation'
:
_handleNavigationMessage
(
data
,
callback
).
then
((
handled
)
{
if
(!
handled
&&
callback
!=
null
)
{
callback
(
null
);
}
});
const
MethodCodec
codec
=
JSONMethodCodec
();
final
MethodCall
decoded
=
codec
.
decodeMethodCall
(
data
);
final
Map
<
String
,
dynamic
>
message
=
decoded
.
arguments
as
Map
<
String
,
dynamic
>;
switch
(
decoded
.
method
)
{
case
'routeUpdated'
:
_useSingleEntryBrowserHistory
().
then
((
void
data
)
{
_browserHistory
.
setRouteName
(
message
[
'routeName'
]);
_replyToPlatformMessage
(
callback
,
codec
.
encodeSuccessEnvelope
(
true
));
});
break
;
case
'routeInformationUpdated'
:
assert
(
_browserHistory
is
MultiEntriesBrowserHistory
);
_browserHistory
.
setRouteName
(
message
[
'location'
],
state:
message
[
'state'
],
);
_replyToPlatformMessage
(
callback
,
codec
.
encodeSuccessEnvelope
(
true
));
break
;
}
// As soon as Flutter starts taking control of the app navigation, we
// should reset [_defaultRouteName] to "/" so it doesn't have any
// further effect after this point.
...
...
@@ -643,51 +685,6 @@ class EngineWindow extends ui.Window {
_replyToPlatformMessage
(
callback
,
null
);
}
@visibleForTesting
Future
<
void
>
debugInitializeHistory
(
UrlStrategy
?
strategy
,
{
required
bool
useSingle
,
})
async
{
await
_browserHistory
?.
tearDown
();
if
(
useSingle
)
{
_browserHistory
=
SingleEntryBrowserHistory
(
urlStrategy:
strategy
);
}
else
{
_browserHistory
=
MultiEntriesBrowserHistory
(
urlStrategy:
strategy
);
}
}
@visibleForTesting
Future
<
void
>
debugResetHistory
()
async
{
await
_browserHistory
?.
tearDown
();
_browserHistory
=
null
;
}
Future
<
bool
>
_handleNavigationMessage
(
ByteData
?
data
,
ui
.
PlatformMessageResponseCallback
?
callback
,
)
async
{
const
MethodCodec
codec
=
JSONMethodCodec
();
final
MethodCall
decoded
=
codec
.
decodeMethodCall
(
data
);
final
Map
<
String
,
dynamic
>
arguments
=
decoded
.
arguments
;
switch
(
decoded
.
method
)
{
case
'routeUpdated'
:
await
_useSingleEntryBrowserHistory
();
browserHistory
.
setRouteName
(
arguments
[
'routeName'
]);
_replyToPlatformMessage
(
callback
,
codec
.
encodeSuccessEnvelope
(
true
));
return
true
;
case
'routeInformationUpdated'
:
assert
(
browserHistory
is
MultiEntriesBrowserHistory
);
browserHistory
.
setRouteName
(
arguments
[
'location'
],
state:
arguments
[
'state'
],
);
_replyToPlatformMessage
(
callback
,
codec
.
encodeSuccessEnvelope
(
true
));
return
true
;
}
return
false
;
}
int
_getHapticFeedbackDuration
(
String
?
type
)
{
switch
(
type
)
{
case
'HapticFeedbackType.lightImpact'
:
...
...
@@ -749,8 +746,7 @@ class EngineWindow extends ui.Window {
:
ui
.
Brightness
.
light
);
_brightnessMediaQueryListener
=
(
html
.
Event
event
)
{
final
html
.
MediaQueryListEvent
mqEvent
=
event
as
html
.
MediaQueryListEvent
;
final
html
.
MediaQueryListEvent
mqEvent
=
event
as
html
.
MediaQueryListEvent
;
_updatePlatformBrightness
(
mqEvent
.
matches
!
?
ui
.
Brightness
.
dark
:
ui
.
Brightness
.
light
);
};
...
...
@@ -760,21 +756,6 @@ class EngineWindow extends ui.Window {
});
}
void
_addUrlStrategyListener
()
{
_jsSetUrlStrategy
=
allowInterop
((
JsUrlStrategy
?
jsStrategy
)
{
assert
(
_browserHistory
==
null
,
'Cannot set URL strategy more than once.'
,
);
final
UrlStrategy
?
strategy
=
jsStrategy
==
null
?
null
:
CustomUrlStrategy
.
fromJs
(
jsStrategy
);
_browserHistory
=
MultiEntriesBrowserHistory
(
urlStrategy:
strategy
);
});
registerHotRestartListener
(()
{
_jsSetUrlStrategy
=
null
;
});
}
/// Remove the callback function for listening changes in [_brightnessMediaQuery] value.
void
_removeBrightnessMediaQueryListener
()
{
_brightnessMediaQuery
.
removeListener
(
_brightnessMediaQueryListener
);
...
...
@@ -804,8 +785,7 @@ class EngineWindow extends ui.Window {
}
@visibleForTesting
late
Rasterizer
?
rasterizer
=
experimentalUseSkia
?
Rasterizer
(
Surface
(
HtmlViewEmbedder
()))
:
null
;
late
Rasterizer
?
rasterizer
=
experimentalUseSkia
?
Rasterizer
(
Surface
(
HtmlViewEmbedder
()))
:
null
;
}
bool
_handleWebTestEnd2EndMessage
(
MethodCodec
codec
,
ByteData
?
data
)
{
...
...
@@ -851,8 +831,8 @@ void _invoke1<A>(void callback(A a)?, Zone? zone, A arg) {
}
/// Invokes [callback] inside the given [zone] passing it [arg1], [arg2], and [arg3].
void
_invoke3
<
A1
,
A2
,
A3
>(
void
callback
(
A1
a1
,
A2
a2
,
A3
a3
)?,
Zone
?
zone
,
A1
arg1
,
A2
arg2
,
A3
arg3
)
{
void
_invoke3
<
A1
,
A2
,
A3
>(
void
callback
(
A1
a1
,
A2
a2
,
A3
a3
)?,
Zone
?
zone
,
A1
arg1
,
A2
arg2
,
A3
arg3
)
{
if
(
callback
==
null
)
{
return
;
}
...
...
lib/web_ui/lib/src/ui/initialization.dart
浏览文件 @
3905b9ec
...
...
@@ -21,6 +21,10 @@ Future<void> webOnlyInitializePlatform({
Future
<
void
>
_initializePlatform
({
engine
.
AssetManager
?
assetManager
,
})
async
{
if
(!
debugEmulateFlutterTesterEnvironment
)
{
engine
.
window
.
locationStrategy
=
const
engine
.
HashLocationStrategy
();
}
engine
.
initializeEngine
();
// This needs to be after `webOnlyInitializeEngine` because that is where the
...
...
lib/web_ui/test/engine/history_test.dart
浏览文件 @
3905b9ec
...
...
@@ -16,6 +16,11 @@ import 'package:ui/src/engine.dart';
import
'../spy.dart'
;
TestLocationStrategy
get
strategy
=>
window
.
browserHistory
.
locationStrategy
;
Future
<
void
>
setStrategy
(
TestLocationStrategy
newStrategy
)
async
{
await
window
.
browserHistory
.
setLocationStrategy
(
newStrategy
);
}
Map
<
String
,
dynamic
>
_wrapOriginState
(
dynamic
state
)
{
return
<
String
,
dynamic
>{
'origin'
:
true
,
'state'
:
state
};
}
...
...
@@ -43,19 +48,18 @@ void testMain() {
final
PlatformMessagesSpy
spy
=
PlatformMessagesSpy
();
setUp
(()
async
{
await
window
.
debugSwitchBrowserHistory
(
useSingle:
true
);
spy
.
setUp
();
});
tearDown
(()
async
{
spy
.
tearDown
();
await
window
.
debugResetHistory
(
);
await
setStrategy
(
null
);
});
test
(
'basic setup works'
,
()
async
{
final
TestUrlStrategy
strategy
=
TestUrlStrategy
.
fromEntry
(
TestHistoryEntry
(
'initial state'
,
null
,
'/initial'
),
);
await
window
.
debugInitializeHistory
(
strategy
,
useSingle:
true
);
await
setStrategy
(
TestLocationStrategy
.
fromEntry
(
TestHistoryEntry
(
'initial state'
,
null
,
'/initial'
)));
// There should be two entries: origin and flutter.
expect
(
strategy
.
history
,
hasLength
(
2
));
...
...
@@ -78,11 +82,7 @@ void testMain() {
skip:
browserEngine
==
BrowserEngine
.
edge
);
test
(
'browser back button pops routes correctly'
,
()
async
{
final
TestUrlStrategy
strategy
=
TestUrlStrategy
.
fromEntry
(
TestHistoryEntry
(
null
,
null
,
'/home'
),
);
await
window
.
debugInitializeHistory
(
strategy
,
useSingle:
true
);
await
setStrategy
(
TestLocationStrategy
.
fromEntry
(
TestHistoryEntry
(
null
,
null
,
'/home'
)));
// Initially, we should be on the flutter entry.
expect
(
strategy
.
history
,
hasLength
(
2
));
expect
(
strategy
.
currentEntry
.
state
,
flutterState
);
...
...
@@ -98,7 +98,7 @@ void testMain() {
// No platform messages have been sent so far.
expect
(
spy
.
messages
,
isEmpty
);
// Clicking back should take us to page1.
await
strategy
.
go
(-
1
);
await
strategy
.
back
(
);
// First, the framework should've received a `popRoute` platform message.
expect
(
spy
.
messages
,
hasLength
(
1
));
expect
(
spy
.
messages
[
0
].
channel
,
'flutter/navigation'
);
...
...
@@ -115,10 +115,7 @@ void testMain() {
skip:
browserEngine
==
BrowserEngine
.
edge
);
test
(
'multiple browser back clicks'
,
()
async
{
final
TestUrlStrategy
strategy
=
TestUrlStrategy
.
fromEntry
(
TestHistoryEntry
(
null
,
null
,
'/home'
),
);
await
window
.
debugInitializeHistory
(
strategy
,
useSingle:
true
);
await
setStrategy
(
TestLocationStrategy
.
fromEntry
(
TestHistoryEntry
(
null
,
null
,
'/home'
)));
await
routeUpdated
(
'/page1'
);
await
routeUpdated
(
'/page2'
);
...
...
@@ -130,7 +127,7 @@ void testMain() {
expect
(
strategy
.
currentEntry
.
url
,
'/page2'
);
// Back to page1.
await
strategy
.
go
(-
1
);
await
strategy
.
back
(
);
// 1. The engine sends a `popRoute` platform message.
expect
(
spy
.
messages
,
hasLength
(
1
));
expect
(
spy
.
messages
[
0
].
channel
,
'flutter/navigation'
);
...
...
@@ -146,7 +143,7 @@ void testMain() {
expect
(
strategy
.
currentEntry
.
url
,
'/page1'
);
// Back to home.
await
strategy
.
go
(-
1
);
await
strategy
.
back
(
);
// 1. The engine sends a `popRoute` platform message.
expect
(
spy
.
messages
,
hasLength
(
1
));
expect
(
spy
.
messages
[
0
].
channel
,
'flutter/navigation'
);
...
...
@@ -164,8 +161,8 @@ void testMain() {
// The next browser back will exit the app. We store the strategy locally
// because it will be remove from the browser history class once it exits
// the app.
Test
Url
Strategy
originalStrategy
=
strategy
;
await
originalStrategy
.
go
(-
1
);
Test
Location
Strategy
originalStrategy
=
strategy
;
await
originalStrategy
.
back
(
);
// 1. The engine sends a `popRoute` platform message.
expect
(
spy
.
messages
,
hasLength
(
1
));
expect
(
spy
.
messages
[
0
].
channel
,
'flutter/navigation'
);
...
...
@@ -184,10 +181,7 @@ void testMain() {
browserEngine
==
BrowserEngine
.
webkit
);
test
(
'handle user-provided url'
,
()
async
{
final
TestUrlStrategy
strategy
=
TestUrlStrategy
.
fromEntry
(
TestHistoryEntry
(
null
,
null
,
'/home'
),
);
await
window
.
debugInitializeHistory
(
strategy
,
useSingle:
true
);
await
setStrategy
(
TestLocationStrategy
.
fromEntry
(
TestHistoryEntry
(
null
,
null
,
'/home'
)));
await
strategy
.
simulateUserTypingUrl
(
'/page3'
);
// This delay is necessary to wait for [BrowserHistory] because it
...
...
@@ -208,7 +202,7 @@ void testMain() {
expect
(
strategy
.
currentEntry
.
url
,
'/page3'
);
// Back to home.
await
strategy
.
go
(-
1
);
await
strategy
.
back
(
);
// 1. The engine sends a `popRoute` platform message.
expect
(
spy
.
messages
,
hasLength
(
1
));
expect
(
spy
.
messages
[
0
].
channel
,
'flutter/navigation'
);
...
...
@@ -227,10 +221,7 @@ void testMain() {
skip:
browserEngine
==
BrowserEngine
.
edge
);
test
(
'user types unknown url'
,
()
async
{
final
TestUrlStrategy
strategy
=
TestUrlStrategy
.
fromEntry
(
TestHistoryEntry
(
null
,
null
,
'/home'
),
);
await
window
.
debugInitializeHistory
(
strategy
,
useSingle:
true
);
await
setStrategy
(
TestLocationStrategy
.
fromEntry
(
TestHistoryEntry
(
null
,
null
,
'/home'
)));
await
strategy
.
simulateUserTypingUrl
(
'/unknown'
);
// This delay is necessary to wait for [BrowserHistory] because it
...
...
@@ -257,19 +248,18 @@ void testMain() {
final
PlatformMessagesSpy
spy
=
PlatformMessagesSpy
();
setUp
(()
async
{
await
window
.
debugSwitchBrowserHistory
(
useSingle:
false
);
spy
.
setUp
();
});
tearDown
(()
async
{
spy
.
tearDown
();
await
window
.
debugResetHistory
(
);
await
setStrategy
(
null
);
});
test
(
'basic setup works'
,
()
async
{
final
TestUrlStrategy
strategy
=
TestUrlStrategy
.
fromEntry
(
TestHistoryEntry
(
'initial state'
,
null
,
'/initial'
),
);
await
window
.
debugInitializeHistory
(
strategy
,
useSingle:
false
);
await
setStrategy
(
TestLocationStrategy
.
fromEntry
(
TestHistoryEntry
(
'initial state'
,
null
,
'/initial'
)));
// There should be only one entry.
expect
(
strategy
.
history
,
hasLength
(
1
));
...
...
@@ -283,11 +273,7 @@ void testMain() {
skip:
browserEngine
==
BrowserEngine
.
edge
);
test
(
'browser back button push route infromation correctly'
,
()
async
{
final
TestUrlStrategy
strategy
=
TestUrlStrategy
.
fromEntry
(
TestHistoryEntry
(
'initial state'
,
null
,
'/home'
),
);
await
window
.
debugInitializeHistory
(
strategy
,
useSingle:
false
);
await
setStrategy
(
TestLocationStrategy
.
fromEntry
(
TestHistoryEntry
(
'initial state'
,
null
,
'/home'
)));
// Initially, we should be on the flutter entry.
expect
(
strategy
.
history
,
hasLength
(
1
));
expect
(
strategy
.
currentEntry
.
state
,
_tagStateWithSerialCount
(
'initial state'
,
0
));
...
...
@@ -303,7 +289,7 @@ void testMain() {
// No platform messages have been sent so far.
expect
(
spy
.
messages
,
isEmpty
);
// Clicking back should take us to page1.
await
strategy
.
go
(-
1
);
await
strategy
.
back
(
);
// First, the framework should've received a `pushRouteInformation`
// platform message.
expect
(
spy
.
messages
,
hasLength
(
1
));
...
...
@@ -324,10 +310,7 @@ void testMain() {
skip:
browserEngine
==
BrowserEngine
.
edge
);
test
(
'multiple browser back clicks'
,
()
async
{
final
TestUrlStrategy
strategy
=
TestUrlStrategy
.
fromEntry
(
TestHistoryEntry
(
'initial state'
,
null
,
'/home'
),
);
await
window
.
debugInitializeHistory
(
strategy
,
useSingle:
false
);
await
setStrategy
(
TestLocationStrategy
.
fromEntry
(
TestHistoryEntry
(
'initial state'
,
null
,
'/home'
)));
await
routeInfomrationUpdated
(
'/page1'
,
'page1 state'
);
await
routeInfomrationUpdated
(
'/page2'
,
'page2 state'
);
...
...
@@ -339,7 +322,7 @@ void testMain() {
expect
(
strategy
.
currentEntry
.
url
,
'/page2'
);
// Back to page1.
await
strategy
.
go
(-
1
);
await
strategy
.
back
(
);
// 1. The engine sends a `pushRouteInformation` platform message.
expect
(
spy
.
messages
,
hasLength
(
1
));
expect
(
spy
.
messages
[
0
].
channel
,
'flutter/navigation'
);
...
...
@@ -355,7 +338,7 @@ void testMain() {
expect
(
strategy
.
currentEntry
.
state
,
_tagStateWithSerialCount
(
'page1 state'
,
1
));
expect
(
strategy
.
currentEntry
.
url
,
'/page1'
);
// Back to home.
await
strategy
.
go
(-
1
);
await
strategy
.
back
(
);
// 1. The engine sends a `pushRouteInformation` platform message.
expect
(
spy
.
messages
,
hasLength
(
1
));
expect
(
spy
.
messages
[
0
].
channel
,
'flutter/navigation'
);
...
...
@@ -376,10 +359,7 @@ void testMain() {
browserEngine
==
BrowserEngine
.
webkit
);
test
(
'handle user-provided url'
,
()
async
{
final
TestUrlStrategy
strategy
=
TestUrlStrategy
.
fromEntry
(
TestHistoryEntry
(
'initial state'
,
null
,
'/home'
),
);
await
window
.
debugInitializeHistory
(
strategy
,
useSingle:
false
);
await
setStrategy
(
TestLocationStrategy
.
fromEntry
(
TestHistoryEntry
(
'initial state'
,
null
,
'/home'
)));
await
strategy
.
simulateUserTypingUrl
(
'/page3'
);
// This delay is necessary to wait for [BrowserHistory] because it
...
...
@@ -401,7 +381,7 @@ void testMain() {
expect
(
strategy
.
currentEntry
.
url
,
'/page3'
);
// Back to home.
await
strategy
.
go
(-
1
);
await
strategy
.
back
(
);
// 1. The engine sends a `pushRouteInformation` platform message.
expect
(
spy
.
messages
,
hasLength
(
1
));
expect
(
spy
.
messages
[
0
].
channel
,
'flutter/navigation'
);
...
...
@@ -421,10 +401,7 @@ void testMain() {
skip:
browserEngine
==
BrowserEngine
.
edge
);
test
(
'forward button works'
,
()
async
{
final
TestUrlStrategy
strategy
=
TestUrlStrategy
.
fromEntry
(
TestHistoryEntry
(
'initial state'
,
null
,
'/home'
),
);
await
window
.
debugInitializeHistory
(
strategy
,
useSingle:
false
);
await
setStrategy
(
TestLocationStrategy
.
fromEntry
(
TestHistoryEntry
(
'initial state'
,
null
,
'/home'
)));
await
routeInfomrationUpdated
(
'/page1'
,
'page1 state'
);
await
routeInfomrationUpdated
(
'/page2'
,
'page2 state'
);
...
...
@@ -436,7 +413,7 @@ void testMain() {
expect
(
strategy
.
currentEntry
.
url
,
'/page2'
);
// Back to page1.
await
strategy
.
go
(-
1
);
await
strategy
.
back
(
);
// 1. The engine sends a `pushRouteInformation` platform message.
expect
(
spy
.
messages
,
hasLength
(
1
));
expect
(
spy
.
messages
[
0
].
channel
,
'flutter/navigation'
);
...
...
@@ -453,7 +430,7 @@ void testMain() {
expect
(
strategy
.
currentEntry
.
url
,
'/page1'
);
// Forward to page2
await
strategy
.
go
(
1
);
await
strategy
.
back
(
count:
-
1
);
// 1. The engine sends a `pushRouteInformation` platform message.
expect
(
spy
.
messages
,
hasLength
(
1
));
expect
(
spy
.
messages
[
0
].
channel
,
'flutter/navigation'
);
...
...
@@ -473,7 +450,7 @@ void testMain() {
skip:
browserEngine
==
BrowserEngine
.
edge
);
});
group
(
'
$Hash
Url
Strategy
'
,
()
{
group
(
'
$Hash
Location
Strategy
'
,
()
{
TestPlatformLocation
location
;
setUp
(()
{
...
...
@@ -485,26 +462,26 @@ void testMain() {
});
test
(
'leading slash is optional'
,
()
{
final
Hash
UrlStrategy
strategy
=
HashUrl
Strategy
(
location
);
final
Hash
LocationStrategy
strategy
=
HashLocation
Strategy
(
location
);
location
.
hash
=
'#/'
;
expect
(
strategy
.
getPath
()
,
'/'
);
expect
(
strategy
.
path
,
'/'
);
location
.
hash
=
'#/foo'
;
expect
(
strategy
.
getPath
()
,
'/foo'
);
expect
(
strategy
.
path
,
'/foo'
);
location
.
hash
=
'#foo'
;
expect
(
strategy
.
getPath
()
,
'foo'
);
expect
(
strategy
.
path
,
'foo'
);
});
test
(
'path should not be empty'
,
()
{
final
Hash
UrlStrategy
strategy
=
HashUrl
Strategy
(
location
);
final
Hash
LocationStrategy
strategy
=
HashLocation
Strategy
(
location
);
location
.
hash
=
''
;
expect
(
strategy
.
getPath
()
,
'/'
);
expect
(
strategy
.
path
,
'/'
);
location
.
hash
=
'#'
;
expect
(
strategy
.
getPath
()
,
'/'
);
expect
(
strategy
.
path
,
'/'
);
});
});
}
...
...
@@ -552,31 +529,31 @@ class TestPlatformLocation extends PlatformLocation {
String
hash
;
dynamic
state
;
@override
void
addPopStateListener
(
html
.
EventListener
fn
)
{
void
onPopState
(
html
.
EventListener
fn
)
{
throw
UnimplementedError
();
}
@override
void
removePopStateListener
(
html
.
EventListener
fn
)
{
void
offPopState
(
html
.
EventListener
fn
)
{
throw
UnimplementedError
();
}
void
onHashChange
(
html
.
EventListener
fn
)
{
throw
UnimplementedError
();
}
void
offHashChange
(
html
.
EventListener
fn
)
{
throw
UnimplementedError
();
}
@override
void
pushState
(
dynamic
state
,
String
title
,
String
url
)
{
throw
UnimplementedError
();
}
@override
void
replaceState
(
dynamic
state
,
String
title
,
String
url
)
{
throw
UnimplementedError
();
}
@override
void
go
(
int
count
)
{
void
back
(
int
count
)
{
throw
UnimplementedError
();
}
@override
String
getBaseHref
()
=>
'/'
;
}
lib/web_ui/test/engine/navigation_test.dart
浏览文件 @
3905b9ec
...
...
@@ -10,7 +10,7 @@ import 'package:test/bootstrap/browser.dart';
import
'package:test/test.dart'
;
import
'package:ui/src/engine.dart'
as
engine
;
engine
.
Test
Url
Strategy
_strategy
;
engine
.
Test
Location
Strategy
_strategy
;
const
engine
.
MethodCodec
codec
=
engine
.
JSONMethodCodec
();
...
...
@@ -21,14 +21,12 @@ void main() {
}
void
testMain
(
)
{
setUp
(()
async
{
_strategy
=
engine
.
TestUrlStrategy
();
await
engine
.
window
.
debugInitializeHistory
(
_strategy
,
useSingle:
true
);
setUp
(()
{
engine
.
window
.
locationStrategy
=
_strategy
=
engine
.
TestLocationStrategy
();
});
tearDown
(()
async
{
_strategy
=
null
;
await
engine
.
window
.
debugResetHistory
();
tearDown
(()
{
engine
.
window
.
locationStrategy
=
_strategy
=
null
;
});
test
(
'Tracks pushed, replaced and popped routes'
,
()
async
{
...
...
@@ -42,6 +40,6 @@ void testMain() {
(
_
)
=>
completer
.
complete
(),
);
await
completer
.
future
;
expect
(
_strategy
.
getPath
()
,
'/foo'
);
expect
(
_strategy
.
path
,
'/foo'
);
});
}
lib/web_ui/test/window_test.dart
浏览文件 @
3905b9ec
...
...
@@ -3,6 +3,7 @@
// found in the LICENSE file.
// @dart = 2.6
import
'dart:async'
;
import
'dart:html'
as
html
;
import
'dart:js_util'
as
js_util
;
import
'dart:typed_data'
;
...
...
@@ -11,39 +12,34 @@ import 'package:test/bootstrap/browser.dart';
import
'package:test/test.dart'
;
import
'package:ui/src/engine.dart'
;
import
'engine/history_test.dart'
;
import
'matchers.dart'
;
const
MethodCodec
codec
=
JSONMethodCodec
();
void
emptyCallback
(
ByteData
data
)
{}
void
emptyCallback
(
ByteData
date
)
{}
Future
<
void
>
setStrategy
(
TestLocationStrategy
newStrategy
)
async
{
await
window
.
browserHistory
.
setLocationStrategy
(
newStrategy
);
}
void
main
(
)
{
internalBootstrapBrowserTest
(()
=>
testMain
);
}
void
testMain
(
)
{
tearDown
(()
async
{
await
window
.
debug
ResetHistory
(
);
setUp
(()
async
{
await
window
.
debug
SwitchBrowserHistory
(
useSingle:
true
);
});
test
(
'window.defaultRouteName should not change'
,
()
async
{
final
TestUrlStrategy
strategy
=
TestUrlStrategy
.
fromEntry
(
TestHistoryEntry
(
'initial state'
,
null
,
'/initial'
),
);
await
window
.
debugInitializeHistory
(
strategy
,
useSingle:
true
);
await
setStrategy
(
TestLocationStrategy
.
fromEntry
(
TestHistoryEntry
(
'initial state'
,
null
,
'/initial'
)));
expect
(
window
.
defaultRouteName
,
'/initial'
);
// Changing the URL in the address bar later shouldn't affect [window.defaultRouteName].
s
trategy
.
replaceState
(
null
,
null
,
'/newpath'
);
window
.
locationS
trategy
.
replaceState
(
null
,
null
,
'/newpath'
);
expect
(
window
.
defaultRouteName
,
'/initial'
);
});
test
(
'window.defaultRouteName should reset after navigation platform message'
,
()
async
{
await
window
.
debugInitializeHistory
(
TestUrlStrategy
.
fromEntry
(
TestHistoryEntry
(
'initial state'
,
null
,
'/initial'
),
),
useSingle:
true
);
test
(
'window.defaultRouteName should reset after navigation platform message'
,
()
async
{
await
setStrategy
(
TestLocationStrategy
.
fromEntry
(
TestHistoryEntry
(
'initial state'
,
null
,
'/initial'
)));
// Reading it multiple times should return the same value.
expect
(
window
.
defaultRouteName
,
'/initial'
);
expect
(
window
.
defaultRouteName
,
'/initial'
);
...
...
@@ -61,45 +57,45 @@ void testMain() {
});
test
(
'can disable location strategy'
,
()
async
{
// Disable URL strategy.
expect
(()
=>
jsSetUrlStrategy
(
null
),
returnsNormally
);
// History should be initialized.
expect
(
window
.
browserHistory
,
isNotNull
);
// But without a URL strategy.
expect
(
window
.
browserHistory
.
urlStrategy
,
isNull
);
// Current path is always "/" in this case.
expect
(
window
.
browserHistory
.
currentPath
,
'/'
);
// Perform some navigation operations.
routeInfomrationUpdated
(
'/foo/bar'
,
null
);
// Path should not be updated because URL strategy is disabled.
expect
(
window
.
browserHistory
.
currentPath
,
'/'
);
});
test
(
'js interop throws on wrong type'
,
()
{
expect
(()
=>
jsSetUrlStrategy
(
123
),
throwsA
(
anything
));
expect
(()
=>
jsSetUrlStrategy
(
'foo'
),
throwsA
(
anything
));
expect
(()
=>
jsSetUrlStrategy
(
false
),
throwsA
(
anything
));
});
test
(
'cannot set url strategy after it is initialized'
,
()
async
{
final
testStrategy
=
TestUrlStrategy
.
fromEntry
(
await
window
.
debugSwitchBrowserHistory
(
useSingle:
true
);
final
testStrategy
=
TestLocationStrategy
.
fromEntry
(
TestHistoryEntry
(
'initial state'
,
null
,
'/'
),
);
await
window
.
debugInitializeHistory
(
testStrategy
,
useSingle:
true
);
expect
(()
=>
jsSetUrlStrategy
(
null
),
throwsA
(
isAssertionError
));
await
setStrategy
(
testStrategy
);
expect
(
window
.
locationStrategy
,
testStrategy
);
// A single listener should've been setup.
expect
(
testStrategy
.
listeners
,
hasLength
(
1
));
// The initial entry should be there, plus another "flutter" entry.
expect
(
testStrategy
.
history
,
hasLength
(
2
));
expect
(
testStrategy
.
history
[
0
].
state
,
<
String
,
dynamic
>{
'origin'
:
true
,
'state'
:
'initial state'
});
expect
(
testStrategy
.
history
[
1
].
state
,
<
String
,
bool
>{
'flutter'
:
true
});
expect
(
testStrategy
.
currentEntry
,
testStrategy
.
history
[
1
]);
// Now, let's disable location strategy and make sure things get cleaned up.
expect
(()
=>
jsSetLocationStrategy
(
null
),
returnsNormally
);
// The locationStrategy is teared down asynchronously.
await
Future
<
void
>.
delayed
(
Duration
.
zero
);
expect
(
window
.
locationStrategy
,
isNull
);
// The listener is removed asynchronously.
await
Future
<
void
>.
delayed
(
const
Duration
(
milliseconds:
10
));
// No more listeners.
expect
(
testStrategy
.
listeners
,
isEmpty
);
// History should've moved back to the initial state.
expect
(
testStrategy
.
history
[
0
].
state
,
"initial state"
);
expect
(
testStrategy
.
currentEntry
,
testStrategy
.
history
[
0
]);
});
test
(
'cannot set url strategy more than once'
,
()
async
{
// First time is okay.
expect
(()
=>
jsSetUrlStrategy
(
null
),
returnsNormally
);
// Second time is not allowed.
expect
(()
=>
jsSetUrlStrategy
(
null
),
throwsA
(
isAssertionError
));
test
(
'js interop throws on wrong type'
,
()
{
expect
(()
=>
jsSetLocationStrategy
(
123
),
throwsA
(
anything
));
expect
(()
=>
jsSetLocationStrategy
(
'foo'
),
throwsA
(
anything
));
expect
(()
=>
jsSetLocationStrategy
(
false
),
throwsA
(
anything
));
});
}
void
jsSet
Url
Strategy
(
dynamic
strategy
)
{
void
jsSet
Location
Strategy
(
dynamic
strategy
)
{
js_util
.
callMethod
(
html
.
window
,
'_flutter_web_set_location_strategy'
,
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录