123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204 |
- // Copyright 2019-2024 Tauri Programme within The Commons Conservancy
- // SPDX-License-Identifier: Apache-2.0
- // SPDX-License-Identifier: MIT
- /**
- * @typedef {{callback: string, error: string, data: *}} IsolationPayload - a valid isolation payload
- */
- ;(function () {
- /**
- * @type {string}
- */
- const pattern = window.__TAURI_INTERNALS__.__TAURI_PATTERN__.pattern
- /**
- * @type {string}
- */
- const isolationOrigin = __TEMPLATE_isolation_origin__
- /**
- * @type {{queue: object[], ready: boolean, frame: HTMLElement | null}}
- */
- const isolation = Object.create(null)
- isolation.queue = []
- isolation.ready = false
- isolation.frame = null
- /**
- * Detects if a message event is a valid isolation message.
- *
- * @param {MessageEvent<object>} event - a message event that is expected to be an isolation message
- * @return {boolean} - if the event was a valid isolation message
- */
- function isIsolationMessage(event) {
- if (
- typeof event.data === 'object' &&
- typeof event.data.payload === 'object'
- ) {
- const keys = Object.keys(event.data.payload || {})
- return (
- keys.length > 0 &&
- keys.every(
- (key) => key === 'contentType' || key === 'nonce' || key === 'payload'
- )
- )
- }
- return false
- }
- /**
- * Detects if data is able to transform into an isolation payload.
- *
- * @param {object} data - object that is expected to contain at least a callback and error identifier
- * @return {boolean} - if the data is able to transform into an isolation payload
- */
- function isIsolationPayload(data) {
- return (
- typeof data === 'object' &&
- 'callback' in data &&
- 'error' in data &&
- !isIsolationMessage(data)
- )
- }
- /**
- * Sends a properly formatted message to the isolation frame.
- *
- * @param {IsolationPayload} data - data that has been validated to be an isolation payload
- */
- function sendIsolationMessage(data) {
- // set the frame dom element if it's not been set before
- if (!isolation.frame) {
- const frame = document.querySelector('iframe#__tauri_isolation__')
- if (frame.src.startsWith(isolationOrigin)) {
- isolation.frame = frame
- } else {
- console.error(
- 'Tauri IPC found an isolation iframe, but it had the wrong origin'
- )
- }
- }
- // ensure we have the target to send the message to
- if (!isolation.frame || !isolation.frame.contentWindow) {
- console.error(
- 'Tauri "Isolation" Pattern could not find the Isolation iframe window'
- )
- return
- }
- // `postMessage` uses `structuredClone` to serialize the data before sending it
- // unlike `JSON.stringify`, we can't extend a type with a method similar to `toJSON`
- // so that `structuredClone` would use, so until https://github.com/whatwg/html/issues/7428
- // we manually call `toIPC`
- function processToIPCSymbol(data) {
- // if this value changes, make sure to update it in:
- // 1. process-ipc-message-fn.js
- // 2. core.ts
- const ToIPCSymbol = '__TAURI_TO_IPC__'
- if (
- typeof data === 'object' &&
- 'constructor' in data &&
- data.constructor === Array
- ) {
- return data.map((v) => processToIPCSymbol(v))
- }
- if (typeof data === 'object' && ToIPCSymbol in data) {
- return data[ToIPCSymbol]()
- }
- if (
- typeof data === 'object' &&
- 'constructor' in data &&
- data.constructor === Object
- ) {
- const acc = {}
- Object.entries(data).forEach(([k, v]) => {
- acc[k] = processToIPCSymbol(v)
- })
- return acc
- }
- return data
- }
- data.payload = processToIPCSymbol(data.payload)
- isolation.frame.contentWindow.postMessage(
- data,
- '*' /* todo: set this to the secure origin */
- )
- }
- Object.defineProperty(window.__TAURI_INTERNALS__, 'ipc', {
- // todo: JSDoc this function
- value: Object.freeze((message) => {
- switch (pattern) {
- case 'brownfield':
- window.__TAURI_INTERNALS__.postMessage(message)
- break
- case 'isolation':
- if (!isIsolationPayload(message)) {
- console.error(
- 'Tauri "Isolation" Pattern found an invalid isolation message payload',
- message
- )
- break
- }
- if (isolation.ready) {
- sendIsolationMessage(message)
- } else {
- isolation.queue.push(message)
- }
- break
- case 'error':
- console.error(
- 'Tauri IPC found a Tauri Pattern, but it was an error. Check for other log messages to find the cause.'
- )
- break
- default:
- console.error(
- 'Tauri IPC did not find a Tauri Pattern that it understood.'
- )
- break
- }
- })
- })
- /**
- * IMPORTANT: See isolation_secure.js for the isolation frame implementation.
- * main frame -> isolation frame = isolation payload
- * isolation frame -> main frame = isolation message
- */
- if (pattern === 'isolation') {
- window.addEventListener(
- 'message',
- (event) => {
- // watch for the isolation frame being ready and flush any queued messages
- if (event.data === '__TAURI_ISOLATION_READY__') {
- isolation.ready = true
- for (const message of isolation.queue) {
- sendIsolationMessage(message)
- }
- isolation.queue = []
- return
- }
- if (isIsolationMessage(event)) {
- window.__TAURI_INTERNALS__.postMessage(event.data)
- }
- },
- false
- )
- }
- })()
|