Webcompat: implement PresentationRequest shim#2121
Conversation
Make the Presentation API shim spec-shaped for feature-detection code by allowing PresentationRequest construction, exposing urls/onconnectionavailable, and returning NotSupportedError rejections for unsupported operations.
✅ Deploy Preview for content-scope-scripts ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
Temporary Branch UpdateThe temporary branch has been updated with the latest changes. Below are the details:
Please use the above install command to update to the latest version. |
[Beta] Generated file diffTime updated: Fri, 22 May 2026 10:16:52 GMT Android
File has changed AppleFile has changed IntegrationFile has changed WindowsFile has changed |
Add webcompat integration-test assertions that PresentationRequest is constructible, exposes urls/onconnectionavailable, rejects unsupported operations with NotSupportedError, and allows setting navigator.presentation.defaultRequest.
Build Branch
Static preview entry points
QR codes (mobile preview)
Integration commandsnpm (Android / Extension): Swift Package Manager (Apple): .package(url: "https://github.com/duckduckgo/content-scope-scripts.git", branch: "pr-releases/cursor/presentation-request-shim-update-dc58")git submodule (Windows): git -C submodules/content-scope-scripts fetch origin pr-releases/cursor/presentation-request-shim-update-dc58
git -C submodules/content-scope-scripts checkout origin/pr-releases/cursor/presentation-request-shim-update-dc58Pin to exact commitnpm (Android / Extension): Swift Package Manager (Apple): .package(url: "https://github.com/duckduckgo/content-scope-scripts.git", revision: "be26d74abaf6c8e5d746d291cb36f7dcb79216ee")git submodule (Windows): git -C submodules/content-scope-scripts fetch origin pr-releases/cursor/presentation-request-shim-update-dc58
git -C submodules/content-scope-scripts checkout be26d74abaf6c8e5d746d291cb36f7dcb79216ee |
|
This PR requires a manual review and approval from a member of one of the following teams:
|
There was a problem hiding this comment.
Web Compatibility Assessment
injected/src/features/web-compat.js:906- warning: the new guard only returns when bothnavigator.presentationandwindow.PresentationRequestexist. On a partial native implementation, the function continues and later replacesNavigator.prototype.presentationwith the shim singleton, which can hide nativedefaultRequest/receiverbehavior instead of only filling the missing constructor.injected/src/features/web-compat.js:1003- warning:onconnectionavailableis installed as an own data property in the constructor. Spec-shaped event handler attributes should be visible onPresentationRequest.prototypeas an accessor/event-handler property; prototype/descriptor-based feature detection will currently fail.injected/src/features/web-compat.js:1011- info: the newurlsaccessor is not masked byshimInterface()because that helper only wraps writable function-valued descriptors.Object.getOwnPropertyDescriptor(PresentationRequest.prototype, 'urls').get.toString()exposes injected source, so descriptor-fidelity tests can detect the shim.injected/src/features/web-compat.js:1019-1027- info:reconnect()is declared with zero formal parameters, soPresentationRequest.prototype.reconnect.lengthis0; the native/WebIDL method is expected to take a presentation id. Add a method-length/argument-contract test if this shim is intended to satisfy detailed feature detection.
Security Assessment
injected/src/features/web-compat.js:971-1032- warning: the new constructor and methods reread mutable page globals after config init (globalThis.EventTarget,DOMException,Error,Promise,String,Symbol,Array.isArray,TypeError). A hostile page can replace these beforeinit()or before calling the shim, causing the shim to fail, throw attacker-controlled errors, or return a non-Promise fromstart()/reconnect()/getAvailability().- No new messaging, origin validation, iframe access, network, or data-exfiltration paths were introduced.
Risk Level
High Risk: this PR changes a global API shim and Navigator.prototype behavior for the Presentation API, which is a detectable browser surface and can affect all pages where the webcompat setting is enabled.
Recommendations
- Preserve partial native implementations: only define missing pieces, and do not replace
Navigator.prototype.presentationwhen a nativenavigator.presentationalready exists. - Move
onconnectionavailableto a prototype-level accessor/event-handler shape and add descriptor tests for prototype presence, getter/setter shape,toString()masking, and method.length. - Use captured globals for all values read in the init/runtime shim path; add captures for
DOMException/EventTargetif needed, and prefer captured/bound helpers for string conversion, promises, symbols, and array checks.
Sent by Cursor Automation: Web compat and sec
| try { | ||
| // @ts-expect-error due to: Property 'presentation' does not exist on type 'Navigator' | ||
| if (window.navigator.presentation && this.injectName !== 'integration') { | ||
| if (window.navigator.presentation && window.PresentationRequest && this.injectName !== 'integration') { |
There was a problem hiding this comment.
This guard changes the native-preservation behavior for partial implementations. If a browser exposes navigator.presentation but not window.PresentationRequest, we continue and later replace Navigator.prototype.presentation with the shim singleton. That can hide native defaultRequest/receiver behavior instead of just filling the missing constructor; safer is to preserve an existing native navigator.presentation and shim only the absent pieces.
| 'PresentationRequest', | ||
| class { | ||
| // class definition is empty because there's no way to get an instance of it anyways | ||
| class extends (globalThis.EventTarget || class {}) { |
There was a problem hiding this comment.
This class is created in init(), after page script may have had a chance to mutate globals, and its methods will be called later by page code. Reading globalThis.EventTarget here, and later DOMException, Error, Promise, String, Symbol, Array.isArray, and TypeError, leaves the shim dependent on page-controlled globals. Please use captured-globals.js references, adding captures for EventTarget/DOMException if needed.
|
|
||
| /** @type {((ev: any) => any)|null} */ | ||
| this.onconnectionavailable = null; | ||
| } |
There was a problem hiding this comment.
onconnectionavailable is currently an own data property on each instance, so checks like 'onconnectionavailable' in PresentationRequest.prototype fail and descriptor-based detection sees a different shape from a native event-handler attribute. This should be installed on the prototype with the expected accessor/event-handler semantics and covered by the shim descriptor tests.


Asana Task/Github Issue: https://app.asana.com/1/137249556945/project/1201614831475344/task/1210044338857086?focus=true
Description
Make the Presentation API shim spec-shaped for feature-detection code by allowing PresentationRequest construction, exposing urls/onconnectionavailable, and returning NotSupportedError rejections for unsupported operations.
This PR implements a spec-shaped PresentationRequest shim to support feature-detection code on sites that check for the Presentation API. The shim allows construction of PresentationRequest objects, exposes required properties (urls, onconnectionavailable), and returns NotSupportedError rejections for unsupported operations.
Testing Steps
Test Cases
1. PresentationRequest Constructor
Test 1.1: Basic construction with a single URL string
Test 1.2: Construction with an array of URLs
Test 1.3: Construction with an iterable
Test 1.4: Invalid constructor arguments should throw TypeError
2. URLs Property
Test 2.1: URLs property returns a copy (immutable)
3. onconnectionavailable Property
Test 3.1: Property exists and is initially null
Test 3.2: Property can be set to a function
4. start() Method
Test 4.1: Returns a rejected Promise with NotSupportedError
5. reconnect() Method
Test 5.1: Returns a rejected Promise with NotSupportedError
6. getAvailability() Method
Test 6.1: Returns a rejected Promise with NotSupportedError
7. navigator.presentation Object
Test 7.1: Presentation object exists
Test 7.2: defaultRequest property getter/setter
Test 7.3: receiver property
8. EventTarget Inheritance
Test 8.1: PresentationRequest extends EventTarget
9. Feature Detection Compatibility
Test 9.1: Common feature detection patterns should work
10. Real-world Site Testing
Test on sites known to use the Presentation API for feature detection:
YouTube - Uses Presentation API for Cast functionality
Netflix - May check for Presentation API
Any site with Cast/Presentation features
Regression Testing
Ensure native Presentation API is not overwritten
window.navigator.presentation && window.PresentationRequestshould prevent shimIntegration test environment
this.injectName !== 'integration'check)Expected Behaviour Summary
Checklist
Please tick all that apply:
Note
Medium Risk
Touches global shims for the Presentation API, which can affect feature-detection code and site behavior if the shape/error semantics differ from expectations. Guarding against native implementations reduces risk, but new constructor/type-handling paths may introduce compatibility edge cases.
Overview
Improves the Presentation API web-compat shim to be spec-shaped for feature detection by implementing a constructible
PresentationRequest(accepting string/array/iterable URLs), exposingurls(as a defensive copy) andonconnectionavailable, and makingstart/reconnect/getAvailabilityreject withNotSupportedError.Updates
navigator.presentation.defaultRequestto be a real writable getter/setter (acceptingPresentationRequestornull) and tightens the early-exit guard so the shim won’t override native Presentation support. Adds an integration test page case to validate the newPresentationRequestbehavior and defaultRequest semantics.Reviewed by Cursor Bugbot for commit 38f0739. Bugbot is set up for automated code reviews on this repo. Configure here.