ipc.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  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. * A runtime generated key to ensure an IPC call comes from an initialized frame.
  19. *
  20. * This is declared outside the `window.__TAURI_INVOKE__` definition to prevent
  21. * the key from being leaked by `window.__TAURI_INVOKE__.toString()`.
  22. */
  23. const __TAURI_INVOKE_KEY__ = __TEMPLATE_invoke_key__
  24. /**
  25. * @type {{queue: object[], ready: boolean, frame: HTMLElement | null}}
  26. */
  27. const isolation = Object.create(null)
  28. isolation.queue = []
  29. isolation.ready = false
  30. isolation.frame = null
  31. /**
  32. * Detects if a message event is a valid isolation message.
  33. *
  34. * @param {MessageEvent<object>} event - a message event that is expected to be an isolation message
  35. * @return {boolean} - if the event was a valid isolation message
  36. */
  37. function isIsolationMessage(event) {
  38. return (
  39. typeof event.data === 'object' &&
  40. 'nonce' in event.data &&
  41. 'payload' in event.data
  42. )
  43. }
  44. /**
  45. * Detects if data is able to transform into an isolation payload.
  46. *
  47. * @param {object} data - object that is expected to contain at least a callback and error identifier
  48. * @return {boolean} - if the data is able to transform into an isolation payload
  49. */
  50. function isIsolationPayload(data) {
  51. return typeof data === 'object' && 'callback' in data && 'error' in data
  52. }
  53. /**
  54. * Sends a properly formatted message to the isolation frame.
  55. *
  56. * @param {IsolationPayload} data - data that has been validated to be an isolation payload
  57. */
  58. function sendIsolationMessage(data) {
  59. // set the frame dom element if it's not been set before
  60. if (!isolation.frame) {
  61. const frame = document.querySelector('iframe#__tauri_isolation__')
  62. if (frame.src.startsWith(isolationOrigin)) {
  63. isolation.frame = frame
  64. } else {
  65. console.error(
  66. 'Tauri IPC found an isolation iframe, but it had the wrong origin'
  67. )
  68. }
  69. }
  70. // ensure we have the target to send the message to
  71. if (!isolation.frame || !isolation.frame.contentWindow) {
  72. console.error(
  73. 'Tauri "Isolation" Pattern could not find the Isolation iframe window'
  74. )
  75. return
  76. }
  77. isolation.frame.contentWindow.postMessage(
  78. data,
  79. '*' /* todo: set this to the secure origin */
  80. )
  81. }
  82. Object.defineProperty(window, '__TAURI_IPC__', {
  83. // todo: JSDoc this function
  84. value: Object.freeze((message) => {
  85. message.__TAURI_INVOKE_KEY__ = __TAURI_INVOKE_KEY__
  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. })()