Skip to content

Conversation

@suhaibabsi-inst
Copy link
Contributor

@suhaibabsi-inst suhaibabsi-inst commented Nov 23, 2025

refs: MBL-19551
affects: Student, Teacher, Parent
builds: Student, Teacher, Parent
release note: Introduced immersive experience for video players

Test Plan

  • Create a page with a couple of videos embedded through Studio LTI.
  • In iOS app, on page appearance, "Open in Details View" should show up right below each video
  • Make sure to turn on flag "RCE Studio Embed improvements" on course level in order to see "Open in Details View" button, you should also see that button be removed when that flag is off.
  • Upon tapping on one of those buttons, a modal for the immersive media player must show up. See ticket's description for the visual requirements of that screen.

Checklist

  • Follow-up e2e test ticket created
  • A11y checked
  • Tested on phone
  • Tested on tablet
  • Tested in dark mode
  • Tested in light mode
  • Approve from product

@suhaibabsi-inst suhaibabsi-inst self-assigned this Nov 23, 2025
@claude
Copy link

claude bot commented Nov 23, 2025

Claude Code Review

Updated: 2025-11-26

Build Status

⚠️ License Header Missing: CanvasLTIPostMessageHandler.js needs license header. Run yarn update-headers to fix.

Code Review

APPROVED - No critical issues after refactoring:

  • HTML escaping properly implemented (InsertStudioOpenInDetailButtons.js:138-145)
  • SVG content safely escaped via CoreWebView.jsString() before injection
  • All URL construction wrapped in try-catch error handlers
  • Null/optional checks prevent crashes (searchParams.get() results validated)
  • window.detailLinkSpecs has defensive fallback (line 74)
  • File loading with proper guard statements and RemoteLogger error logging
  • DOM injection uses safe APIs (appendChild, textContent) - no innerHTML risks

✅ Approved (fix license header before merge)

@inst-danger
Copy link
Contributor

inst-danger commented Nov 23, 2025

Warnings
⚠️ One or more files are below the minimum test coverage 50%
⚠️ The total test coverage is below the minimum 90%

Release Note:

Introduced immersive experience for video players

Affected Apps: Student, Teacher, Parent

Builds: Student, Teacher, Parent

MBL-19551

Coverage New % Master % Delta
Canvas iOS 81.09% 81.06% 0.03%
Horizon/Horizon/Sources/Features/Dashboard/SkillsHighlightsWidget/Data/ProficiencyLevel.swift 0% 0% 0%
Horizon/Horizon/Sources/Features/Notebook/Common/View/HighlightWebView/HighlightWebFeature.swift 0% 0% 0%
Horizon/Horizon/Sources/Features/Notebook/Common/View/HighlightWebView/EnableZoom.swift 0% 0% 0%
Horizon/Horizon/Sources/Common/Domain/GetCoursesInteractor.swift 38.16% 38.16% 0%
Horizon/Horizon/Sources/Features/LearningObjects/Assignment/SubmissionComment/Data/CommentAttachment.swift 0% 0% 0%
Horizon/Horizon/Sources/Features/Account/Notifications/Domain/NotificationSettingsInteractor.swift 0% 0% 0%
Horizon/Horizon/Sources/Common/Data/HModuleStatus.swift 0% 0% 0%
Horizon/Horizon/Sources/Common/Utilities/Double+.swift 0% 0% 0%
Horizon/Horizon/Sources/Features/Session/SessionInteractor.swift 32% 33.33% -1.33%
Horizon/Horizon/Sources/Features/Assist/AssistFlashCard/View/AssistFlashCardViewModel.swift 0% 0% 0%

Generated by 🚫 dangerJS against 8046ae2

@inst-danger
Copy link
Contributor

inst-danger commented Nov 23, 2025

Builds

Commit: Update InsertStudioOpenInDetailButtonsTests.swift (8046ae2)
Build Number: 898
Built At: Nov 27 22:55 CET (11/27 02:55 PM MST)

Student
Teacher
Parent

@claude
Copy link

claude bot commented Nov 26, 2025

Claude Code Review

Updated: 2025-11-26

Critical Issues Found

  • CSS Contains Unevaluated Swift Interpolation (InsertStudioOpenInDetailButtons.css:7): Literal \\(fontSize)px instead of actual CSS value. Browser receives invalid CSS, breaking button styling.

  • Fragile Query Parameter Detection (CoreWebViewStudioFeaturesInteractor.swift:197): Substring matching custom_arc_launch_type=immersive_view instead of parameter parsing. Could trigger on unintended query strings.

  • Partial Frame Replacement Silent Failures (StudioIFrameReplaceInteractor.swift:62): Changed from throwing error on first unmapped iframe to silently skipping and only throwing if ALL frames fail. Masks partial data issues.

❌ Issues found

@claude
Copy link

claude bot commented Nov 26, 2025

Claude Code Review

Updated: 2025-11-26

Critical Issues Found

  • CSS file contains unevaluated Swift interpolation (InsertStudioOpenInDetailButtons.css:7): Line contains literal \(fontSize)px instead of actual CSS value. This will render as invalid CSS and break button styling.

  • Query parameter detection fragile (CoreWebViewStudioFeaturesInteractor.swift:196): Using query.contains("custom_arc_launch_type=immersive_view") is substring matching, not parameter parsing. Could match unintended URLs.

  • Partial iframe replacement logic (StudioIFrameReplaceInteractor.swift:63): Changed from throwing error on first unmapped iframe to silently continuing. Now throws error only if ALL iframes fail to map, masking partial failures.

❌ Issues found

@claude
Copy link

claude bot commented Nov 26, 2025

Claude Code Review

APPROVED

Summary:
No critical issues found. The PR implements immersive video player functionality for Studio LTI media embeds with proper error handling and code organization.

Key observations:

  • ✅ StudioIFrameReplaceInteractor change is correct: changed from throwing when single iframe not found to only throwing if NO frames were replaced (allows partial replacements)
  • ✅ JavaScript LTI handler properly prevents duplicate initialization with guard flag
  • ✅ URL detection logic refactored correctly to handle both Canvas uploads and Studio embeds
  • ✅ Proper string encoding/decoding for URL parameters (removingPercentEncoding)
  • ✅ Feature injection pattern consistent with project conventions
  • ✅ Test coverage added for new Studio embed immersive view URL detection

@claude
Copy link

claude bot commented Nov 26, 2025

Claude Code Review - Updated 2025-11-26

Critical Issues:

  • InsertStudioOpenInDetailButtons.css line 7: Invalid CSS syntax - contains unprocessed placeholder text
  • InsertStudioOpenInDetailButtons.js line 105: innerHTML assignment with SVG creates XSS vector
  • CanvasLTIPostMessageHandler.js: No cleanup for global message listener - causes accumulation on reapply
  • InsertStudioOpenInDetailButtons.swift line 109: Appends empty DOM elements when title is nil

Issues found

@claude
Copy link

claude bot commented Nov 27, 2025

Claude Code Review

Updated: 2025-11-27

Status: ⚠️ Issues Found

Critical Issues:

  • XSS Vulnerability (InsertStudioOpenInDetailButtons.js:91-92): SVG icon injected via innerHTML without sanitization. Even though Swift escapes via CoreWebView.jsString(), innerHTML parsing bypasses escaping—allows execution of embedded scripts.
  • Unvalidated URL Construction (InsertStudioOpenInDetailButtons.js:46): searchParams.get() returns optional value used directly in new URL() without null check—can throw exception.
  • Silent Failures: Missing .js/.css files cause feature to silently fail without logging (InsertStudioOpenInDetailButtons.swift).
  • License Header Missing: CanvasLTIPostMessageHandler.js fails CI validation per dangerJS.

Secondary:

  • Navigation link detection order changed without test coverage.
  • Query string check uses contains() vs. exact parameter match.

🚫 Issues found.

@claude
Copy link

claude bot commented Nov 27, 2025

Claude Code Review

Updated: 2025-11-27

✅ No critical issues found.

  • CanvasLTIPostMessageHandler.js properly validates origin and handles data safely
  • Navigation detection adds Studio embed check correctly (path suffix + query param validation)
  • CSS/JS refactored to external files appropriately
  • JS properly handles both Canvas uploads and Studio LTI embeds with separate link generation
  • Duplicate prevention using ios-injected flag is correct
  • Test coverage validates Canvas upload button injection


.open_details_button {
font-weight: 400;
font-size: \(fontSize)px;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see where does this get its value.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ooops!.
I copied that from the Canvas upload one, and totally forgot about this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. We not longer need to calculate the size for that. Tested that on iOS 18, and iOS 26. With a fixed value of 14px would do the job for accessibility scaling.

iOS 26

ios_26

iOS 18

ios_18

/// later to set immersive video player title. This mainly useful when triggering the player
/// from a button that's internal to video-frame. (`Expand` button)
/// from a button that's internal to video-frame. (`Expand` button).
/// Only for Canvas uploads video players.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please fix this sentence


private var env: AppEnvironment = .defaultValue
private let webView = CoreWebView()
private let webView = CoreWebView(features: [.canvasLTIPostMessageHandler])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see this is added for pages, quiz details, student assignment details.
Should it not need to be added for other places?
Parent and teacher assignment details?
Like syllabus or Inbox?
Not sure about discussions (the ones using the viewcontroller, and the new ones opening the web version)

Or should this be used by default in all webviews?
Or this is needed only when coming from UIKit?

cc @vargaat for input

Copy link
Contributor Author

@suhaibabsi-inst suhaibabsi-inst Nov 27, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@rh12

It should be used for all webViews meant to load content as HTML text as they lack the scripts to solve the issue of LTI frame resizing.
Theoretically speaking, it won't be necessary for WebView loading full URLs as those can load the needed Javascript scripts required for that and much more.

.open_details_button {
font-weight: 400;
font-size: \(fontSize)px;
font-size: 14px;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wasn't the whole point that this should scale? As in the previous PR

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but turned out that fixing icon style solved the issue. Apparently the old style was blocking the scaling for the whole button.

@claude
Copy link

claude bot commented Nov 27, 2025

Claude Code Review - Updated 2025-11-27

Critical Issues Found

Security: Missing origin validation in LTI PostMessage handler - Message handler processes postMessage from ANY origin without validating event.origin. Allows malicious iframes to trigger frameResize or extract window size data. Requires whitelist validation.

Security: XSS via innerHTML in SVG icon injection - InsertStudioOpenInDetailButtons.js uses direct innerHTML for SVG without sanitization. If externalLinkData asset is compromised, enables arbitrary script execution.

Correctness: Changed error semantics in StudioIFrameReplaceInteractor - Changed from fail-fast to silent skipping. Only throws if ALL videos missing, not on partial replacements. Failures now silent, risking broken playback without warning.

Logic: Unsafe query string validation - Detection uses substring match instead of proper parameter parsing. Creates false positives if string appears anywhere in query value.

Correctness: Unsafe URL title decoding - Title from URL query parameter decoded without validation. Could accept null bytes or invalid Unicode sequences.

Memory: Multiple event listener accumulation - Script injections before flag is set create duplicate listeners. No cleanup on navigation.

❌ Issues found

@claude
Copy link

claude bot commented Nov 27, 2025

Claude Code Review

Updated: 2025-11-27

APPROVED - No critical issues.

Key observations:

  • ✅ StudioIFrameReplaceInteractor properly continues on unmapped iframe
  • ✅ Query parameter validation acceptable for immersive_view detection
  • ✅ CanvasLTIPostMessageHandler prevents duplicate initialization
  • ✅ JavaScript URL construction wrapped in try-catch handlers
  • ✅ HTML escaping properly applied via escapeHTML() and CoreWebView.jsString()
  • ✅ Proper null/optional checks throughout
  • ✅ CSS uses hardcoded font-size values
  • ✅ Feature flag gating controls feature availability correctly

@vargaat vargaat marked this pull request as draft November 28, 2025 11:05
Base automatically changed from feature/MBL-19480-Immersive-VideoPlayer-Canvas-Uploads to master November 28, 2025 14:46
@suhaibabsi-inst
Copy link
Contributor Author

suhaibabsi-inst commented Dec 2, 2025

Will be handled as part of other work. (MBL-19574)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants