ipc.js 4.3 KB

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