ipc.js 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. // Copyright 2019-2024 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. /**
  5. * @typedef {{callback: string, error: string, data: *}} IsolationPayload - a valid isolation payload
  6. */
  7. ;(function () {
  8. /**
  9. * @type {string}
  10. */
  11. const pattern = window.__TAURI_INTERNALS__.__TAURI_PATTERN__.pattern
  12. /**
  13. * @type {string}
  14. */
  15. const isolationOrigin = __TEMPLATE_isolation_origin__
  16. /**
  17. * @type {{queue: object[], ready: boolean, frame: HTMLElement | null}}
  18. */
  19. const isolation = Object.create(null)
  20. isolation.queue = []
  21. isolation.ready = false
  22. isolation.frame = null
  23. /**
  24. * Detects if a message event is a valid isolation message.
  25. *
  26. * @param {MessageEvent<object>} event - a message event that is expected to be an isolation message
  27. * @return {boolean} - if the event was a valid isolation message
  28. */
  29. function isIsolationMessage(event) {
  30. if (
  31. typeof event.data === 'object' &&
  32. typeof event.data.payload === 'object'
  33. ) {
  34. const keys = Object.keys(event.data.payload || {})
  35. return (
  36. keys.length > 0 &&
  37. keys.every((key) => key === 'nonce' || key === 'payload')
  38. )
  39. }
  40. return false
  41. }
  42. /**
  43. * Detects if data is able to transform into an isolation payload.
  44. *
  45. * @param {object} data - object that is expected to contain at least a callback and error identifier
  46. * @return {boolean} - if the data is able to transform into an isolation payload
  47. */
  48. function isIsolationPayload(data) {
  49. return (
  50. typeof data === 'object' &&
  51. 'callback' in data &&
  52. 'error' in data &&
  53. !isIsolationMessage(data)
  54. )
  55. }
  56. /**
  57. * Sends a properly formatted message to the isolation frame.
  58. *
  59. * @param {IsolationPayload} data - data that has been validated to be an isolation payload
  60. */
  61. function sendIsolationMessage(data) {
  62. // set the frame dom element if it's not been set before
  63. if (!isolation.frame) {
  64. const frame = document.querySelector('iframe#__tauri_isolation__')
  65. if (frame.src.startsWith(isolationOrigin)) {
  66. isolation.frame = frame
  67. } else {
  68. console.error(
  69. 'Tauri IPC found an isolation iframe, but it had the wrong origin'
  70. )
  71. }
  72. }
  73. // ensure we have the target to send the message to
  74. if (!isolation.frame || !isolation.frame.contentWindow) {
  75. console.error(
  76. 'Tauri "Isolation" Pattern could not find the Isolation iframe window'
  77. )
  78. return
  79. }
  80. isolation.frame.contentWindow.postMessage(
  81. data,
  82. '*' /* todo: set this to the secure origin */
  83. )
  84. }
  85. Object.defineProperty(window.__TAURI_INTERNALS__, 'ipc', {
  86. // todo: JSDoc this function
  87. value: Object.freeze((message) => {
  88. switch (pattern) {
  89. case 'brownfield':
  90. window.__TAURI_INTERNALS__.postMessage(message)
  91. break
  92. case 'isolation':
  93. if (!isIsolationPayload(message)) {
  94. console.error(
  95. 'Tauri "Isolation" Pattern found an invalid isolation message payload',
  96. message
  97. )
  98. break
  99. }
  100. if (isolation.ready) {
  101. sendIsolationMessage(message)
  102. } else {
  103. isolation.queue.push(message)
  104. }
  105. break
  106. case 'error':
  107. console.error(
  108. 'Tauri IPC found a Tauri Pattern, but it was an error. Check for other log messages to find the cause.'
  109. )
  110. break
  111. default:
  112. console.error(
  113. 'Tauri IPC did not find a Tauri Pattern that it understood.'
  114. )
  115. break
  116. }
  117. })
  118. })
  119. /**
  120. * IMPORTANT: See isolation_secure.js for the isolation frame implementation.
  121. * main frame -> isolation frame = isolation payload
  122. * isolation frame -> main frame = isolation message
  123. */
  124. if (pattern === 'isolation') {
  125. window.addEventListener(
  126. 'message',
  127. (event) => {
  128. // watch for the isolation frame being ready and flush any queued messages
  129. if (event.data === '__TAURI_ISOLATION_READY__') {
  130. isolation.ready = true
  131. for (const message of isolation.queue) {
  132. sendIsolationMessage(message)
  133. }
  134. isolation.queue = []
  135. return
  136. }
  137. if (isIsolationMessage(event)) {
  138. window.__TAURI_INTERNALS__.postMessage(event.data)
  139. }
  140. },
  141. false
  142. )
  143. }
  144. })()