Skip to content
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

Fix: Session replay opacity animation masking #4532

Merged
merged 7 commits into from
Nov 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
- Concurrency crash with Swift 6 (#4512)
- Make `Scope.span` fully thread safe (#4519)
- Finish TTFD when not calling reportFullyDisplayed before binding a new transaction to the scope (#4526).
- Session replay opacity animation masking (#4532)

## 8.40.1

Expand Down
6 changes: 3 additions & 3 deletions Sources/Swift/Tools/UIRedactBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -238,9 +238,8 @@ class UIRedactBuilder {
}

private func mapRedactRegion(fromView view: UIView, relativeTo parentLayer: CALayer?, redacting: inout [RedactRegion], rootFrame: CGRect, forceRedact: Bool = false) {
guard !redactClassesIdentifiers.isEmpty && !view.isHidden && view.alpha != 0 else { return }

let layer = view.layer.presentation() ?? view.layer
guard !redactClassesIdentifiers.isEmpty && !layer.isHidden && layer.opacity != 0 else { return }

let newTransform = getTranform(from: layer, withParent: parentLayer)

Expand Down Expand Up @@ -308,7 +307,8 @@ class UIRedactBuilder {
Indicates whether the view is opaque and will block other view behind it
*/
private func isOpaque(_ view: UIView) -> Bool {
return SentryRedactViewHelper.shouldClipOut(view) || (view.alpha == 1 && view.backgroundColor != nil && (view.backgroundColor?.cgColor.alpha ?? 0) == 1)
let layer = view.layer.presentation() ?? view.layer
return SentryRedactViewHelper.shouldClipOut(view) || (layer.opacity == 1 && view.backgroundColor != nil && (view.backgroundColor?.cgColor.alpha ?? 0) == 1)
}
}

Expand Down
37 changes: 37 additions & 0 deletions Tests/SentryTests/UIRedactBuilderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,17 @@ class RedactOptions: SentryRedactOptions {
}

class UIRedactBuilderTests: XCTestCase {
private class CustomVisibilityView: UIView {
class CustomLayer: CALayer {
override var opacity: Float {
get { 0.5 }
set { }
}
}
override class var layerClass: AnyClass {
return CustomLayer.self
}
}

private let rootView = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))

Expand Down Expand Up @@ -394,6 +405,32 @@ class UIRedactBuilderTests: XCTestCase {
XCTAssertTrue(sut.containsIgnoreClass(element), "\(element) not found")
}
}

func testLayerIsNotFullyTransparentRedacted() {
let sut = getSut()
let view = CustomVisibilityView(frame: CGRect(x: 20, y: 20, width: 40, height: 40))
view.alpha = 0
view.sentryReplayMask()

view.backgroundColor = .purple
rootView.addSubview(view)

let result = sut.redactRegionsFor(view: rootView)
XCTAssertEqual(result.count, 1)
brustolin marked this conversation as resolved.
Show resolved Hide resolved
}

func testViewLayerOnTopIsNotFullyTransparentRedacted() {
let sut = getSut()
let view = CustomVisibilityView(frame: CGRect(x: 20, y: 20, width: 40, height: 40))
let label = UILabel(frame: CGRect(x: 20, y: 20, width: 40, height: 40))
view.backgroundColor = .purple
rootView.addSubview(label)
rootView.addSubview(view)

let result = sut.redactRegionsFor(view: rootView)
XCTAssertEqual(result.first?.type, .redact)
XCTAssertEqual(result.count, 1)
brustolin marked this conversation as resolved.
Show resolved Hide resolved
}
}

#endif
Loading