1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
// Copyright 2019-2023 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 () {
// Workaround for webview2 injecting scripts into subframes, unlike webkitgtk/wkwebview.
const osName = __TEMPLATE_os_name__
if (osName === 'windows' && window.location !== window.parent.location) {
return
}
/**
* @type {string}
*/
const pattern = window.__TAURI_PATTERN__.pattern
/**
* @type {string}
*/
const isolationOrigin = __TEMPLATE_isolation_origin__
/**
* A runtime generated key to ensure an IPC call comes from an initialized frame.
*
* This is declared outside the `window.__TAURI_INVOKE__` definition to prevent
* the key from being leaked by `window.__TAURI_INVOKE__.toString()`.
*/
const __TAURI_INVOKE_KEY__ = __TEMPLATE_invoke_key__
/**
* @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) {
return (
typeof event.data === 'object' &&
'nonce' in event.data &&
'payload' in event.data
)
}
/**
* 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
}
/**
* 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
}
isolation.frame.contentWindow.postMessage(
data,
'*' /* todo: set this to the secure origin */
)
}
Object.defineProperty(window, '__TAURI_IPC__', {
// todo: JSDoc this function
value: Object.freeze((message) => {
message.__TAURI_INVOKE_KEY__ = __TAURI_INVOKE_KEY__
switch (pattern) {
case 'brownfield':
window.__TAURI_POST_MESSAGE__(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_POST_MESSAGE__(event.data)
}
},
false
)
}
})()