未验证 提交 04182cde 编写于 作者: F Ferhat 提交者: GitHub

Switch ui.window.devicePixelRatio from browser logical to physical. (#17209)

* Switch ui.window.devicePixelRatio to browser dpi
* Add integration test for multi res image loading
* Update cirrus for new integration test
* update canvas golden test root transform
* Update compositing golden test with dpr transform
上级 e79aef61
......@@ -146,6 +146,7 @@ task:
- $FRAMEWORK_PATH/flutter/bin/flutter config --local-engine=host_debug_unopt --no-analytics --enable-web
- $FRAMEWORK_PATH/flutter/bin/flutter pub get --local-engine=host_debug_unopt
- $FRAMEWORK_PATH/flutter/bin/flutter drive -v --target=test_driver/text_editing_e2e.dart -d web-server --release --browser-name=chrome --local-engine=host_debug_unopt
- $FRAMEWORK_PATH/flutter/bin/flutter drive -v --target=test_driver/image_loading_e2e.dart -d web-server --release --browser-name=chrome --local-engine=host_debug_unopt
- name: build_and_test_web_linux_firefox
compile_host_script: |
......
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
void main() async {
const MethodChannel channel =
OptionalMethodChannel('flutter/web_test_e2e', JSONMethodCodec());
await channel.invokeMethod<void>(
'setDevicePixelRatio',
'1.5',
);
runApp(MyApp());
}
class MyApp extends StatefulWidget {
@override
MyAppState createState() => MyAppState();
}
class MyAppState extends State<MyApp> {
@override
Widget build(BuildContext context) {
return MaterialApp(
key: const Key('mainapp'),
title: 'Integration Test App',
home: Image.asset('assets/images/sample_image1.png')
);
}
}
......@@ -16,3 +16,7 @@ dev_dependencies:
e2e: 0.2.4+4
http: 0.12.0+2
test: any
flutter:
assets:
- assets/images/
\ No newline at end of file
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:html' as html;
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:regular_integration_tests/image_loading_main.dart' as app;
import 'package:e2e/e2e.dart';
void main() {
E2EWidgetsFlutterBinding.ensureInitialized() as E2EWidgetsFlutterBinding;
testWidgets('Image loads asset variant based on device pixel ratio',
(WidgetTester tester) async {
app.main();
await tester.pumpAndSettle();
final html.ImageElement imageElement = html.querySelector('img') as html.ImageElement;
expect(imageElement.naturalWidth, 1.5 * 100);
expect(imageElement.naturalHeight, 1.5 * 100);
expect(imageElement.width, 100);
expect(imageElement.height, 100);
});
}
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:io';
import 'package:flutter_driver/flutter_driver.dart';
Future<void> main() async {
final FlutterDriver driver = await FlutterDriver.connect();
final String dataRequest =
await driver.requestData(null, timeout: const Duration(seconds: 1));
await driver.close();
exit(dataRequest == 'pass' ? 0 : 1);
}
......@@ -69,9 +69,11 @@ class HtmlViewEmbedder {
switch (decoded.method) {
case 'create':
_create(decoded, callback);
window._replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true));
return;
case 'dispose':
_dispose(decoded, callback);
window._replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true));
return;
}
callback(null);
......
......@@ -419,12 +419,6 @@ flt-glass-pane * {
// DOM tree.
setElementAttribute(_sceneHostElement, 'aria-hidden', 'true');
// We treat browser pixels as device pixels because pointer events,
// position, and sizes all use browser pixel as the unit (i.e. "px" in CSS).
// Therefore, as far as the framework is concerned the device pixel ratio
// is 1.0.
window.debugOverrideDevicePixelRatio(1.0);
if (html.window.visualViewport == null && isWebKit) {
// Safari sometimes gives us bogus innerWidth/innerHeight values when the
// page loads. When it changes the values to correct ones it does not
......
......@@ -50,9 +50,11 @@ void handlePlatformViewCall(
switch (decoded.method) {
case 'create':
_createPlatformView(decoded, callback);
window._replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true));
return;
case 'dispose':
_disposePlatformView(decoded, callback);
window._replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true));
return;
}
callback(null);
......
......@@ -52,9 +52,9 @@ class AccessibilityAnnouncements {
html.HtmlElement get _domElement => _element ??= _createElement();
/// Decodes the message coming from the 'flutter/accessibility' channel.
void handleMessage(ByteData data) {
void handleMessage(StandardMessageCodec codec, ByteData data) {
final Map<dynamic, dynamic> inputMap =
const StandardMessageCodec().decodeMessage(data);
codec.decodeMessage(data);
final Map<dynamic, dynamic> dataMap = inputMap['data'];
final String message = dataMap['message'];
if (message != null && message.isNotEmpty) {
......
......@@ -10,7 +10,8 @@ class SurfaceSceneBuilder implements ui.SceneBuilder {
_surfaceStack.add(PersistedScene(_lastFrameScene));
}
final List<PersistedContainerSurface> _surfaceStack = <PersistedContainerSurface>[];
final List<PersistedContainerSurface> _surfaceStack =
<PersistedContainerSurface>[];
/// The scene built by this scene builder.
///
......@@ -19,7 +20,8 @@ class SurfaceSceneBuilder implements ui.SceneBuilder {
assert(() {
if (_surfaceStack.length != 1) {
final String surfacePrintout = _surfaceStack
.map<Type>((PersistedContainerSurface surface) => surface.runtimeType)
.map<Type>(
(PersistedContainerSurface surface) => surface.runtimeType)
.toList()
.join(', ');
throw Exception('Incorrect sequence of push/pop operations while '
......@@ -42,7 +44,8 @@ class SurfaceSceneBuilder implements ui.SceneBuilder {
// the live tree.
if (surface.oldLayer != null) {
assert(surface.oldLayer.runtimeType == surface.runtimeType);
assert(debugAssertSurfaceState(surface.oldLayer, PersistedSurfaceState.active));
assert(debugAssertSurfaceState(
surface.oldLayer, PersistedSurfaceState.active));
surface.oldLayer.state = PersistedSurfaceState.pendingUpdate;
}
_adoptSurface(surface);
......@@ -97,6 +100,16 @@ class SurfaceSceneBuilder implements ui.SceneBuilder {
if (matrix4.length != 16) {
throw ArgumentError('"matrix4" must have 16 entries.');
}
if (_surfaceStack.length == 1) {
// Top level transform contains view configuration to scale
// scene to devicepixelratio. Use identity instead since CSS uses
// logical device pixels.
if (!ui.debugEmulateFlutterTesterEnvironment) {
assert(matrix4[0] == window.devicePixelRatio &&
matrix4[5] == window.devicePixelRatio);
}
matrix4 = Matrix4.identity().storage;
}
return _pushSurface(PersistedTransform(oldLayer, matrix4));
}
......@@ -276,7 +289,8 @@ class SurfaceSceneBuilder implements ui.SceneBuilder {
void addRetained(ui.EngineLayer retainedLayer) {
final PersistedContainerSurface retainedSurface = retainedLayer;
if (assertionsEnabled) {
assert(debugAssertSurfaceState(retainedSurface, PersistedSurfaceState.active, PersistedSurfaceState.released));
assert(debugAssertSurfaceState(retainedSurface,
PersistedSurfaceState.active, PersistedSurfaceState.released));
}
retainedSurface.tryRetain();
_adoptSurface(retainedSurface);
......@@ -319,7 +333,8 @@ class SurfaceSceneBuilder implements ui.SceneBuilder {
/// for more details.
@override
void addPerformanceOverlay(int enabledOptions, ui.Rect bounds) {
_addPerformanceOverlay(enabledOptions, bounds.left, bounds.right, bounds.top, bounds.bottom);
_addPerformanceOverlay(
enabledOptions, bounds.left, bounds.right, bounds.top, bounds.bottom);
}
/// Whether we've already warned the user about the lack of the performance
......@@ -337,7 +352,8 @@ class SurfaceSceneBuilder implements ui.SceneBuilder {
) {
if (!_webOnlyDidWarnAboutPerformanceOverlay) {
_webOnlyDidWarnAboutPerformanceOverlay = true;
html.window.console.warn('The performance overlay isn\'t supported on the web');
html.window.console
.warn('The performance overlay isn\'t supported on the web');
}
}
......@@ -377,7 +393,8 @@ class SurfaceSceneBuilder implements ui.SceneBuilder {
_addTexture(offset.dx, offset.dy, width, height, textureId);
}
void _addTexture(double dx, double dy, double width, double height, int textureId) {
void _addTexture(
double dx, double dy, double width, double height, int textureId) {
// In test mode, allow this to be a no-op.
if (!ui.debugEmulateFlutterTesterEnvironment) {
throw UnimplementedError('Textures are not supported in Flutter Web');
......
......@@ -778,8 +778,11 @@ class TextEditingChannel {
final HybridTextEditing implementation;
/// Handles "flutter/textinput" platform messages received from the framework.
void handleTextInput(ByteData data) {
final MethodCall call = const JSONMethodCodec().decodeMethodCall(data);
void handleTextInput(
ByteData data,
ui.PlatformMessageResponseCallback callback) {
const JSONMethodCodec codec = JSONMethodCodec();
final MethodCall call = codec.decodeMethodCall(data);
switch (call.method) {
case 'TextInput.setClient':
implementation.setClient(
......@@ -815,6 +818,7 @@ class TextEditingChannel {
default:
throw StateError('Unsupported method call on the flutter/textinput channel: ${call.method}');
}
window._replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true));
}
/// Sends the 'TextInputClient.updateEditingState' message to the framework.
......
......@@ -15,19 +15,11 @@ class EngineWindow extends ui.Window {
}
@override
double get devicePixelRatio {
if (_debugDevicePixelRatio != null) {
return _debugDevicePixelRatio;
}
double get devicePixelRatio => _debugDevicePixelRatio != null
? _debugDevicePixelRatio
: browserDevicePixelRatio;
if (experimentalUseSkia) {
return browserDevicePixelRatio;
} else {
return 1.0;
}
}
/// Returns device pixel ratio returns by browser.
/// Returns device pixel ratio returned by browser.
static double get browserDevicePixelRatio {
double ratio = html.window.devicePixelRatio;
// Guard against WebOS returning 0.
......@@ -38,10 +30,7 @@ class EngineWindow extends ui.Window {
///
/// This is useful in tests to emulate screens of different dimensions.
void debugOverrideDevicePixelRatio(double value) {
assert(() {
_debugDevicePixelRatio = value;
return true;
}());
_debugDevicePixelRatio = value;
}
double _debugDevicePixelRatio;
......@@ -160,26 +149,38 @@ class EngineWindow extends ui.Window {
case 'HapticFeedback.vibrate':
final String type = decoded.arguments;
domRenderer.vibrate(_getHapticFeedbackDuration(type));
_replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true));
return;
case 'SystemChrome.setApplicationSwitcherDescription':
final Map<String, dynamic> arguments = decoded.arguments;
domRenderer.setTitle(arguments['label']);
domRenderer.setThemeColor(ui.Color(arguments['primaryColor']));
_replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true));
return;
case 'SystemSound.play':
// There are no default system sounds on web.
_replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true));
return;
case 'Clipboard.setData':
ClipboardMessageHandler().setDataMethodCall(decoded, callback);
_replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true));
return;
case 'Clipboard.getData':
ClipboardMessageHandler().getDataMethodCall(callback);
_replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true));
return;
}
break;
case 'flutter/textinput':
textEditing.channel.handleTextInput(data);
textEditing.channel.handleTextInput(data, callback);
return;
case 'flutter/web_test_e2e':
const MethodCodec codec = JSONMethodCodec();
_replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(
_handleWebTestEnd2EndMessage(codec, data)
));
return;
case 'flutter/platform_views':
......@@ -192,7 +193,9 @@ class EngineWindow extends ui.Window {
case 'flutter/accessibility':
// In widget tests we want to bypass processing of platform messages.
accessibilityAnnouncements.handleMessage(data);
final StandardMessageCodec codec = StandardMessageCodec();
accessibilityAnnouncements.handleMessage(codec, data);
_replyToPlatformMessage(callback, codec.encodeMessage(true));
return;
case 'flutter/navigation':
......@@ -204,9 +207,11 @@ class EngineWindow extends ui.Window {
case 'routePushed':
case 'routeReplaced':
_browserHistory.setRouteName(message['routeName']);
_replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true));
break;
case 'routePopped':
_browserHistory.setRouteName(message['previousRouteName']);
_replyToPlatformMessage(callback, codec.encodeSuccessEnvelope(true));
break;
}
return;
......@@ -250,7 +255,9 @@ class EngineWindow extends ui.Window {
ByteData data,
) {
Future<void>.delayed(Duration.zero).then((_) {
callback(data);
if (callback != null) {
callback(data);
}
});
}
......@@ -315,6 +322,20 @@ class EngineWindow extends ui.Window {
Rasterizer rasterizer = experimentalUseSkia ? Rasterizer(Surface()) : null;
}
bool _handleWebTestEnd2EndMessage(MethodCodec codec, ByteData data) {
final MethodCall decoded = codec.decodeMethodCall(data);
final Map<String, dynamic> message = decoded.arguments;
double ratio = double.parse(decoded.arguments);
bool result = false;
switch(decoded.method) {
case 'setDevicePixelRatio':
window.debugOverrideDevicePixelRatio(ratio);
window.onMetricsChanged();
return true;
}
return false;
}
/// The window singleton.
///
/// `dart:ui` window delegates to this value. However, this value has a wider
......
......@@ -29,7 +29,7 @@ Future<dynamic> ensureTestPlatformInitializedThenRunTest(
/// are available.
Future<void> _platformInitializedFuture;
/// Initializes domRenderer with specific devicePixelRation and physicalSize.
/// Initializes domRenderer with specific devicePixelRatio and physicalSize.
Future<void> webOnlyInitializeTestDomRenderer({double devicePixelRatio = 3.0}) {
// Force-initialize DomRenderer so it doesn't overwrite test pixel ratio.
engine.domRenderer;
......
......@@ -33,7 +33,8 @@ void main() {
// Initially there is no accessibility-element
expect(document.getElementById('accessibility-element'), isNull);
accessibilityAnnouncements.handleMessage(codec.encodeMessage(testInput));
accessibilityAnnouncements.handleMessage(codec,
codec.encodeMessage(testInput));
expect(
document.getElementById('accessibility-element'),
isNotNull,
......
......@@ -222,12 +222,15 @@ void main() async {
);
final SceneBuilder sb = SceneBuilder();
sb.pushTransform(Matrix4.diagonal3Values(EngineWindow.browserDevicePixelRatio,
EngineWindow.browserDevicePixelRatio, 1.0).storage);
sb.pushTransform(Matrix4.rotationZ(math.pi / 2).storage);
sb.pushOffset(0, -500);
sb.pushClipRect(canvasSize);
sb.pop();
sb.pop();
sb.pop();
sb.pop();
final SurfaceScene scene = sb.build();
final html.Element sceneElement = scene.webOnlyRootElement;
......
......@@ -405,6 +405,11 @@ void _testCullRectComputation() {
// enough to fit the rotated clip.
test('clips correctly when using 3d transforms', () async {
final SurfaceSceneBuilder builder = SurfaceSceneBuilder();
builder.pushTransform(Matrix4.diagonal3Values(
EngineWindow.browserDevicePixelRatio,
EngineWindow.browserDevicePixelRatio, 1.0).storage);
// TODO(yjbanov): see the TODO below.
// final double screenWidth = html.window.innerWidth.toDouble();
// final double screenHeight = html.window.innerHeight.toDouble();
......@@ -499,6 +504,7 @@ void _testCullRectComputation() {
builder.pop(); // pushClipRect
builder.pop(); // pushOffset
builder.pop(); // pushTransform scale
builder.pop(); // pushTransform scale devicepixelratio
html.document.body.append(builder.build().webOnlyRootElement);
await matchGoldenFile('compositing_3d_rotate1.png', region: region);
......
......@@ -560,7 +560,7 @@ void main() {
/// Emulates sending of a message by the framework to the engine.
void sendFrameworkMessage(dynamic message) {
textEditing.channel.handleTextInput(message);
textEditing.channel.handleTextInput(message, (ByteData data) {});
}
/// Sends the necessary platform messages to activate a text field and show
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册