-
-
Notifications
You must be signed in to change notification settings - Fork 237
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
redact screenshots via view hierarchy #2361
base: main
Are you sure you want to change the base?
Changes from 7 commits
42be7f3
49a413b
d283df9
fdc7d6d
ea94d47
981039c
4671c53
f722191
22f22b0
9bb14ad
0969b7b
73a4224
c2daf00
3606202
35ebb0b
ba079eb
57a0823
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,12 @@ | ||
import 'dart:async'; | ||
import 'dart:math'; | ||
import 'dart:typed_data'; | ||
import 'dart:ui'; | ||
|
||
import 'package:sentry/sentry.dart'; | ||
import '../screenshot/recorder.dart'; | ||
import '../screenshot/recorder_config.dart'; | ||
import '../screenshot/sentry_screenshot_widget.dart'; | ||
import '../sentry_flutter_options.dart'; | ||
import 'package:flutter/rendering.dart'; | ||
import '../renderer/renderer.dart'; | ||
import 'package:flutter/widgets.dart' as widget; | ||
|
||
|
@@ -75,83 +75,34 @@ | |
return event; | ||
} | ||
|
||
final bytes = await _createScreenshot(); | ||
if (bytes != null) { | ||
hint.screenshot = SentryAttachment.fromScreenshotData(bytes); | ||
} | ||
return event; | ||
} | ||
// ignore: deprecated_member_use | ||
var recorder = ScreenshotRecorder( | ||
ScreenshotRecorderConfig(quality: _options.screenshotQuality), | ||
_options); | ||
|
||
Future<Uint8List?> _createScreenshot() async { | ||
try { | ||
final renderObject = | ||
sentryScreenshotWidgetGlobalKey.currentContext?.findRenderObject(); | ||
if (renderObject is RenderRepaintBoundary) { | ||
// ignore: deprecated_member_use | ||
final pixelRatio = window.devicePixelRatio; | ||
var imageResult = _getImage(renderObject, pixelRatio); | ||
Image image; | ||
if (imageResult is Future<Image>) { | ||
image = await imageResult; | ||
} else { | ||
image = imageResult; | ||
} | ||
// At the time of writing there's no other image format available which | ||
// Sentry understands. | ||
Uint8List? _screenshotCache; | ||
martinhaintz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
if (image.width == 0 || image.height == 0) { | ||
_options.logger(SentryLevel.debug, | ||
'View\'s width and height is zeroed, not taking screenshot.'); | ||
return null; | ||
} | ||
|
||
final targetResolution = _options.screenshotQuality.targetResolution(); | ||
if (targetResolution != null) { | ||
var ratioWidth = targetResolution / image.width; | ||
var ratioHeight = targetResolution / image.height; | ||
var ratio = min(ratioWidth, ratioHeight); | ||
if (ratio > 0.0 && ratio < 1.0) { | ||
imageResult = _getImage(renderObject, ratio * pixelRatio); | ||
if (imageResult is Future<Image>) { | ||
image = await imageResult; | ||
} else { | ||
image = imageResult; | ||
} | ||
} | ||
} | ||
final byteData = await image.toByteData(format: ImageByteFormat.png); | ||
await recorder.capture((Image image) async { | ||
_screenshotCache = await _convertImageToUint8List(image); | ||
}); | ||
|
||
final bytes = byteData?.buffer.asUint8List(); | ||
if (bytes?.isNotEmpty == true) { | ||
return bytes; | ||
} else { | ||
_options.logger(SentryLevel.debug, | ||
'Screenshot is 0 bytes, not attaching the image.'); | ||
return null; | ||
} | ||
} | ||
} catch (exception, stackTrace) { | ||
_options.logger( | ||
SentryLevel.error, | ||
'Taking screenshot failed.', | ||
exception: exception, | ||
stackTrace: stackTrace, | ||
); | ||
if (_options.automatedTestMode) { | ||
rethrow; | ||
} | ||
if (_screenshotCache != null) { | ||
hint.screenshot = SentryAttachment.fromScreenshotData(_screenshotCache!); | ||
} | ||
return null; | ||
|
||
return event; | ||
} | ||
|
||
FutureOr<Image> _getImage( | ||
RenderRepaintBoundary repaintBoundary, double pixelRatio) { | ||
// This one is a hack to use https://api.flutter.dev/flutter/rendering/RenderRepaintBoundary/toImage.html on versions older than 3.7 and https://api.flutter.dev/flutter/rendering/RenderRepaintBoundary/toImageSync.html on versions equal or newer than 3.7 | ||
try { | ||
return (repaintBoundary as dynamic).toImageSync(pixelRatio: pixelRatio) | ||
as Image; | ||
} on NoSuchMethodError catch (_) { | ||
return repaintBoundary.toImage(pixelRatio: pixelRatio); | ||
Future<Uint8List?> _convertImageToUint8List(Image image) async { | ||
final byteData = await image.toByteData(format: ImageByteFormat.png); | ||
|
||
final bytes = byteData?.buffer.asUint8List(); | ||
if (bytes?.isNotEmpty == true) { | ||
return bytes; | ||
} else { | ||
_options.logger( | ||
SentryLevel.debug, 'Screenshot is 0 bytes, not attaching the image.'); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ever happened to you while testing? Looks like a bug. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @vaind I looked up the |
||
return null; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import '../screenshot/recorder_config.dart'; | ||
|
||
class ScheduledScreenshotRecorderConfig extends ScreenshotRecorderConfig { | ||
final int frameRate; | ||
|
||
const ScheduledScreenshotRecorderConfig({ | ||
super.width, | ||
super.height, | ||
required this.frameRate, | ||
}); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the wording is a bit confusing from a user perspective we can improve it by saying
Allow Screenshots to be masked for privacy reasons
or something like thatAlso show an exampl how a user can enable it and please add the screenshot & replay masking behaviour (the one that I commented) as sub bullet points for more context so it's clear how it behaves