mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
[web] fix screen orientation lock bug (flutter/engine#36253)
This commit is contained in:
parent
9dffe4a887
commit
f72b62d672
@ -7,7 +7,6 @@ import 'package:ui/ui.dart' as ui;
|
||||
import 'browser_detection.dart';
|
||||
import 'dom.dart';
|
||||
import 'services.dart';
|
||||
import 'util.dart';
|
||||
|
||||
/// Handles clipboard related platform messages.
|
||||
class ClipboardMessageHandler {
|
||||
@ -89,7 +88,7 @@ class ClipboardMessageHandler {
|
||||
/// APIs and the browser.
|
||||
abstract class CopyToClipboardStrategy {
|
||||
factory CopyToClipboardStrategy() {
|
||||
return !unsafeIsNull(domWindow.navigator.clipboard)
|
||||
return domWindow.navigator.clipboard != null
|
||||
? ClipboardAPICopyStrategy()
|
||||
: ExecCommandCopyStrategy();
|
||||
}
|
||||
@ -109,7 +108,7 @@ abstract class CopyToClipboardStrategy {
|
||||
abstract class PasteFromClipboardStrategy {
|
||||
factory PasteFromClipboardStrategy() {
|
||||
return (browserEngine == BrowserEngine.firefox ||
|
||||
unsafeIsNull(domWindow.navigator.clipboard))
|
||||
domWindow.navigator.clipboard == null)
|
||||
? ExecCommandPasteStrategy()
|
||||
: ClipboardAPIPasteStrategy();
|
||||
}
|
||||
|
||||
@ -1298,8 +1298,10 @@ extension DomScreenExtension on DomScreen {
|
||||
class DomScreenOrientation extends DomEventTarget {}
|
||||
|
||||
extension DomScreenOrientationExtension on DomScreenOrientation {
|
||||
Future<dynamic> lock(String orientation) => js_util
|
||||
.promiseToFuture(js_util.callMethod(this, 'lock', <String>[orientation]));
|
||||
Future<dynamic> lock(String orientation) {
|
||||
final Object jsResult = js_util.callMethod<Object>(this, 'lock', <String>[orientation]);
|
||||
return js_util.promiseToFuture(jsResult);
|
||||
}
|
||||
external void unlock();
|
||||
}
|
||||
|
||||
|
||||
@ -437,12 +437,12 @@ class FlutterViewEmbedder {
|
||||
///
|
||||
/// See w3c screen api: https://www.w3.org/TR/screen-orientation/
|
||||
Future<bool> setPreferredOrientation(List<dynamic> orientations) {
|
||||
final DomScreen screen = domWindow.screen!;
|
||||
if (!unsafeIsNull(screen)) {
|
||||
final DomScreen? screen = domWindow.screen;
|
||||
if (screen != null) {
|
||||
final DomScreenOrientation? screenOrientation = screen.orientation;
|
||||
if (!unsafeIsNull(screenOrientation)) {
|
||||
if (screenOrientation != null) {
|
||||
if (orientations.isEmpty) {
|
||||
screenOrientation!.unlock();
|
||||
screenOrientation.unlock();
|
||||
return Future<bool>.value(true);
|
||||
} else {
|
||||
final String? lockType =
|
||||
@ -450,7 +450,7 @@ class FlutterViewEmbedder {
|
||||
if (lockType != null) {
|
||||
final Completer<bool> completer = Completer<bool>();
|
||||
try {
|
||||
screenOrientation!.lock(lockType).then((dynamic _) {
|
||||
screenOrientation.lock(lockType).then((dynamic _) {
|
||||
completer.complete(true);
|
||||
}).catchError((dynamic error) {
|
||||
// On Chrome desktop an error with 'not supported on this device
|
||||
@ -470,13 +470,15 @@ class FlutterViewEmbedder {
|
||||
}
|
||||
|
||||
// Converts device orientation to w3c OrientationLockType enum.
|
||||
//
|
||||
// See also: https://developer.mozilla.org/en-US/docs/Web/API/ScreenOrientation/lock
|
||||
static String? _deviceOrientationToLockType(String? deviceOrientation) {
|
||||
switch (deviceOrientation) {
|
||||
case 'DeviceOrientation.portraitUp':
|
||||
return orientationLockTypePortraitPrimary;
|
||||
case 'DeviceOrientation.landscapeLeft':
|
||||
return orientationLockTypePortraitSecondary;
|
||||
case 'DeviceOrientation.portraitDown':
|
||||
return orientationLockTypePortraitSecondary;
|
||||
case 'DeviceOrientation.landscapeLeft':
|
||||
return orientationLockTypeLandscapePrimary;
|
||||
case 'DeviceOrientation.landscapeRight':
|
||||
return orientationLockTypeLandscapeSecondary;
|
||||
|
||||
@ -242,15 +242,19 @@ void debugResetBrowserSupportsImageDecoder() {
|
||||
_imageDecoderConstructor != null;
|
||||
}
|
||||
|
||||
/// The signature of the function passed to the constructor of JavaScript `Promise`.
|
||||
typedef JsPromiseCallback = void Function(void Function(Object? value) resolve, void Function(Object? error) reject);
|
||||
|
||||
/// Corresponds to JavaScript's `Promise`.
|
||||
///
|
||||
/// This type doesn't need any members. Instead, it should be first converted
|
||||
/// to Dart's [Future] using [promiseToFuture] then interacted with through the
|
||||
/// [Future] API.
|
||||
@JS()
|
||||
@anonymous
|
||||
@JS('window.Promise')
|
||||
@staticInterop
|
||||
class JsPromise {}
|
||||
class JsPromise {
|
||||
external factory JsPromise(JsPromiseCallback callback);
|
||||
}
|
||||
|
||||
/// Corresponds to the browser's `ImageDecoder` type.
|
||||
///
|
||||
|
||||
@ -552,11 +552,6 @@ String blurSigmasToCssString(double sigmaX, double sigmaY) {
|
||||
return 'blur(${(sigmaX + sigmaY) * 0.5}px)';
|
||||
}
|
||||
|
||||
/// Checks if the dynamic [object] is equal to null.
|
||||
bool unsafeIsNull(dynamic object) {
|
||||
return object == null;
|
||||
}
|
||||
|
||||
/// A typed variant of [domWindow.fetch].
|
||||
Future<DomResponse> httpFetch(String url) async {
|
||||
final Object? result = await domWindow.fetch(url);
|
||||
|
||||
@ -299,25 +299,112 @@ Future<void> testMain() async {
|
||||
expect(responded, isTrue);
|
||||
});
|
||||
|
||||
/// Regression test for https://github.com/flutter/flutter/issues/66128.
|
||||
test("setPreferredOrientation responds even if browser doesn't support api", () async {
|
||||
final DomScreen screen = domWindow.screen!;
|
||||
js_util.setProperty(screen, 'orientation', null);
|
||||
|
||||
// Emulates the framework sending a request for screen orientation lock.
|
||||
Future<bool> sendSetPreferredOrientations(List<dynamic> orientations) {
|
||||
final Completer<bool> completer = Completer<bool>();
|
||||
final ByteData inputData = const JSONMethodCodec().encodeMethodCall(const MethodCall(
|
||||
'SystemChrome.setPreferredOrientations',
|
||||
<dynamic>[]))!;
|
||||
final ByteData? inputData = const JSONMethodCodec().encodeMethodCall(MethodCall(
|
||||
'SystemChrome.setPreferredOrientations',
|
||||
orientations,
|
||||
));
|
||||
|
||||
window.sendPlatformMessage(
|
||||
'flutter/platform',
|
||||
inputData,
|
||||
(ByteData? outputData) {
|
||||
completer.complete(true);
|
||||
inputData,
|
||||
(ByteData? outputData) {
|
||||
const MethodCodec codec = JSONMethodCodec();
|
||||
completer.complete(codec.decodeEnvelope(outputData!) as bool);
|
||||
},
|
||||
);
|
||||
|
||||
expect(await completer.future, isTrue);
|
||||
return completer.future;
|
||||
}
|
||||
|
||||
// Regression test for https://github.com/flutter/flutter/issues/88269
|
||||
test('sets preferred screen orientation', () async {
|
||||
final DomScreen original = domWindow.screen!;
|
||||
|
||||
final List<String> lockCalls = <String>[];
|
||||
int unlockCount = 0;
|
||||
bool simulateError = false;
|
||||
|
||||
// The `orientation` property cannot be overridden, so this test overrides the entire `screen`.
|
||||
js_util.setProperty(domWindow, 'screen', js_util.jsify(<Object?, Object?>{
|
||||
'orientation': <Object?, Object?>{
|
||||
'lock': allowInterop((String lockType) {
|
||||
lockCalls.add(lockType);
|
||||
return JsPromise(allowInterop((Function(Object? value) resolve, Function reject) {
|
||||
if (!simulateError) {
|
||||
resolve(null);
|
||||
} else {
|
||||
reject('Simulating error');
|
||||
}
|
||||
}));
|
||||
}),
|
||||
'unlock': allowInterop(() {
|
||||
unlockCount += 1;
|
||||
}),
|
||||
},
|
||||
}));
|
||||
|
||||
// Sanity-check the test setup.
|
||||
expect(lockCalls, <String>[]);
|
||||
expect(unlockCount, 0);
|
||||
await domWindow.screen!.orientation!.lock('hi');
|
||||
domWindow.screen!.orientation!.unlock();
|
||||
expect(lockCalls, <String>['hi']);
|
||||
expect(unlockCount, 1);
|
||||
lockCalls.clear();
|
||||
unlockCount = 0;
|
||||
|
||||
expect(await sendSetPreferredOrientations(<dynamic>['DeviceOrientation.portraitUp']), isTrue);
|
||||
expect(lockCalls, <String>[FlutterViewEmbedder.orientationLockTypePortraitPrimary]);
|
||||
expect(unlockCount, 0);
|
||||
lockCalls.clear();
|
||||
unlockCount = 0;
|
||||
|
||||
expect(await sendSetPreferredOrientations(<dynamic>['DeviceOrientation.portraitDown']), isTrue);
|
||||
expect(lockCalls, <String>[FlutterViewEmbedder.orientationLockTypePortraitSecondary]);
|
||||
expect(unlockCount, 0);
|
||||
lockCalls.clear();
|
||||
unlockCount = 0;
|
||||
|
||||
expect(await sendSetPreferredOrientations(<dynamic>['DeviceOrientation.landscapeLeft']), isTrue);
|
||||
expect(lockCalls, <String>[FlutterViewEmbedder.orientationLockTypeLandscapePrimary]);
|
||||
expect(unlockCount, 0);
|
||||
lockCalls.clear();
|
||||
unlockCount = 0;
|
||||
|
||||
expect(await sendSetPreferredOrientations(<dynamic>['DeviceOrientation.landscapeRight']), isTrue);
|
||||
expect(lockCalls, <String>[FlutterViewEmbedder.orientationLockTypeLandscapeSecondary]);
|
||||
expect(unlockCount, 0);
|
||||
lockCalls.clear();
|
||||
unlockCount = 0;
|
||||
|
||||
expect(await sendSetPreferredOrientations(<dynamic>[]), isTrue);
|
||||
expect(lockCalls, <String>[]);
|
||||
expect(unlockCount, 1);
|
||||
lockCalls.clear();
|
||||
unlockCount = 0;
|
||||
|
||||
simulateError = true;
|
||||
expect(await sendSetPreferredOrientations(<dynamic>['DeviceOrientation.portraitDown']), isFalse);
|
||||
expect(lockCalls, <String>[FlutterViewEmbedder.orientationLockTypePortraitSecondary]);
|
||||
expect(unlockCount, 0);
|
||||
|
||||
js_util.setProperty(domWindow, 'screen', original);
|
||||
});
|
||||
|
||||
/// Regression test for https://github.com/flutter/flutter/issues/66128.
|
||||
test("setPreferredOrientation responds even if browser doesn't support api", () async {
|
||||
final DomScreen original = domWindow.screen!;
|
||||
|
||||
// The `orientation` property cannot be overridden, so this test overrides the entire `screen`.
|
||||
js_util.setProperty(domWindow, 'screen', js_util.jsify(<Object?, Object?>{
|
||||
'orientation': null,
|
||||
}));
|
||||
expect(domWindow.screen!.orientation, isNull);
|
||||
expect(await sendSetPreferredOrientations(<dynamic>[]), isFalse);
|
||||
js_util.setProperty(domWindow, 'screen', original);
|
||||
});
|
||||
|
||||
test('SingletonFlutterWindow implements locale, locales, and locale change notifications', () async {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user