Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
20 changes: 20 additions & 0 deletions packages/app/src/servers/connect/pairing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,23 @@ describe("pairing link", () => {
expect(legacyPairingLink("http://192.168.1.2:49374/auth/connect/abc")).toBe(false)
})
})

// iOS 17 has no URL.parse (#59186): the connect screen threw before it sent anything.
describe("pairing link on a browser without URL.parse", () => {
test("reads the same links as a browser that has it", () => {
const saved = URL.parse
// @ts-expect-error the point of the test: Safari before 18 has no URL.parse
URL.parse = undefined

try {
expect(pairingLink(" http://192.168.1.2:49374/auth/connect/abc_DEF-123 ")).toEqual({
urls: ["http://192.168.1.2:49374"],
code: "abc_DEF-123",
})
expect(legacyPairingLink("http://192.168.1.2:49374/connect#token")).toBe(true)
expect(legacyPairingLink("http://192.168.1.2:49374/pages")).toBe(false)
} finally {
URL.parse = saved
}
})
})
14 changes: 12 additions & 2 deletions packages/app/src/servers/connect/pairing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,16 @@ export function serverAddress(value: string) {

const CODE = /^[A-Za-z0-9_-]+$/

// URL.parse is Safari 18+; on iOS 17 it is undefined, so reading a pairing link threw a TypeError before any

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This fixes the two calls in this file, but the web app also bundles packages/gui-extensions. There, pairing/remote.ts (remoteAddress) and microsoft-office/links.ts (externalUrl) still call URL.parse, so on Safari 17 they'll throw the same TypeError. src/runtime/polyfills.ts already polyfills other missing APIs for older Safari (see #59007). A guarded if (!URL.parse) URL.parse = ... there, built on new URL in a try/catch, would cover every call site with one fix. This file could then keep using URL.parse.

// request went out and the connect screen blamed the address (#59186). new URL in a try/catch is the same test.
const parseUrl = (value: string) => {
try {
return new URL(value)
} catch {
return undefined
}
}

const decodePayload = Schema.decodeUnknownOption(
Schema.fromJsonString(Schema.Struct({ code: Schema.String, urls: Schema.Array(Schema.String) })),
)
Expand All @@ -39,7 +49,7 @@ export function pairingLink(value: string) {
return { urls: urls.filter((url) => url !== undefined), code: payload.code }
}

const url = URL.parse(trimmed)
const url = parseUrl(trimmed)

if (!url || (url.protocol !== "http:" && url.protocol !== "https:")) return
const code = /^\/auth\/connect\/([^/]+)$/.exec(url.pathname)?.[1]
Expand Down Expand Up @@ -67,7 +77,7 @@ export function bareServerAddress(value: string) {

// Servers before one-time links paired with `/connect#<credentials>` or `/connect?data=<credentials>`.
export function legacyPairingLink(value: string) {
const url = URL.parse(value.trim())
const url = parseUrl(value.trim())

if (!url || (url.protocol !== "http:" && url.protocol !== "https:")) return false

Expand Down
Loading