# `InfoReport.ar`

> Contract 2.0.0 · package sha256 `a857eca95b35921109faf997094bb4192f12527d9d0fcd4be7e97f06425bad6e` · generated from `configurator-runtime.d.ts` by `services/onsite-scripter/scripts/build-contract-docs.mjs`; edit the source, not this file.

Property of [`InfoReport`](InfoReport.md) · stable · since 2.0.0 · called as `session.info().ar`

```ts
readonly ar: {
  readonly enabled: boolean;
  readonly modes: ReadonlyArray<'scene-viewer' | 'quick-look' | 'webxr' | 'other'>;
  readonly fileModes: ReadonlyArray<'scene-viewer' | 'quick-look' | 'other'>;
  readonly pending: boolean;
};
```

AR on this device for this account: read it before showing an AR control.

## Fields

<a id="enabled"></a>
### `enabled`

```ts
readonly enabled: boolean;
```

The merged document's AR switch.

<a id="modes"></a>
### `modes`

```ts
readonly modes: ReadonlyArray<'scene-viewer' | 'quick-look' | 'webxr' | 'other'>;
```

The viewers [`activateAR`](Session.activateAR.md) can open here, in the account's preference order; empty means hide
the AR control ([`activateAR`](Session.activateAR.md) would resolve `reason: 'disabled'` or `'unsupported'`). Scene
Viewer and Quick Look are known when [`boot`](boot.md) resolves; Scene Viewer opens for a call with an
http(s) `glbUrl`, or without one for a product with a public plain twin
([`Session.activateAR`](Session.activateAR.md)). 'scene-viewer' leaves the list only while the committed product is
encrypted with no public plain twin (a `.holo` among several placed models, or one encrypted
ModelNode without a `plainGlbUrl`), where no file of the product can open it; an unencrypted
product keeps it, as 1.x lists it. It joins or leaves with the commit that changes that,
'ar:modes' telling it. A page that passes its own http(s) `glbUrl` (a merged file of the
whole product) reads `fileModes` instead. 'quick-look' is listed where
`document.createElement('a').relList.supports('ar')` is true (Safari, a Home Screen app) and
in Chrome, Firefox, Edge and DuckDuckGo on iOS (a hosted or `blob:` `usdzUrl` alike);
'scene-viewer' on Android outside WebViews and Firefox. Neither is listed in the Google app, a
social in-app browser (Facebook, Instagram, TikTok and the like) or a WebView, where a tap
would open nothing (spec section 5.11). 'webxr' names Holostep's in-page viewer on either
route (WebXR on Android, the camera tracker on iOS) and is listed only where the next
[`activateAR()`](Session.activateAR.md) without `glbUrl` would open it (the routing in [`Session.activateAR`](Session.activateAR.md), with the
tap's activation assumed), or where its escape sheet is the only AR route (a host's deny row,
an app shell not yet measured): there it is the only mode, so the AR control shows and its
tap opens the sheet. For an Android account that opts in, the runtime loads its AR probe at
idle after [`boot`](boot.md) (`document.featurePolicy` first, then
`navigator.xr.isSessionSupported('immersive-ar')`) and never waits for it, so boot takes no
longer; elsewhere no probe runs. 'ar:modes' fires when 'webxr' joins: an AR control decided
at install follows that event.

<a id="filemodes"></a>
### `fileModes`

```ts
readonly fileModes: ReadonlyArray<'scene-viewer' | 'quick-look' | 'other'>;
```

The native viewers an [`activateAR`](Session.activateAR.md) call with your own file opens here, whatever the product
on screen: 'scene-viewer' with an http(s) `glbUrl`, 'quick-look' (with a `usdzUrl`, or the
runtime's own file), on the devices `modes` names them for, in the account's order; empty
with AR off. Use it to show the AR control on a page that always passes `glbUrl`, such as
an encrypted modular product whose merged file the page uploads; `modes` stays the list for
a plain call.

**Default:** [] (AR off, or a device with no native viewer)

<a id="pending"></a>
### `pending`

```ts
readonly pending: boolean;
```

True until the in-page route can be answered: the probe answered or timed out, the AR code
loaded and the scene's size is known; on an iOS page that opts in, until the in-page code
and the scene's size are in. False at [`boot`](boot.md) where nothing is to be answered (an account that
doesn't opt in, an Android device without `navigator.xr`). 'ar:modes' fires once when it
turns false, whether or not 'webxr' joined, so a page whose layout depends on 'webxr' (an AR
page that launches AR or shows a QR code) holds
that layout while it is true. An empty `modes` alone can't tell "still checking" from "none".

## Generic-API test (spec 3.1)

| Rule | Result |
| --- | --- |
| 1. Neutral name | pass: `ar` names no product |
| 2. A precedent | pass: [XRSystem.isSessionSupported()](https://developer.mozilla.org/en-US/docs/Web/API/XRSystem/isSessionSupported) (row `G61`) |
| 3. Two apps, or labs | pass: 12 apps (ledger: app-4fd23bc869, app-822bb9d6aa, app-8675b3225c, app-c1384707fc, app-e3b47169bc, app-e9a33606d2, app-f07e9033d9, app-f3598fad77 and 4 more; fleet cross-check: app-fd09090420) |
| 4. The cheaper fix first | decided per need in Appendix B: rows `G61` at tier stable (a member only where no rule, data, asset or app code serves the need) |
