ipc.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  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. * @var {string} __TEMPLATE_invoke_key__
  23. */
  24. const __TAURI_INVOKE_KEY__ = __TEMPLATE_invoke_key__
  25. /**
  26. * @type {{queue: object[], ready: boolean, frame: HTMLElement | null}}
  27. */
  28. const isolation = Object.create(null)
  29. isolation.queue = []
  30. isolation.ready = false
  31. isolation.frame = null
  32. /**
  33. * Detects if a message event is a valid isolation message.
  34. *
  35. * @param {MessageEvent<object>} event - a message event that is expected to be an isolation message
  36. * @return {boolean} - if the event was a valid isolation message
  37. */
  38. function isIsolationMessage(event) {
  39. return (
  40. typeof event.data === 'object' &&
  41. 'nonce' in event.data &&
  42. 'payload' in event.data
  43. )
  44. }
  45. /**
  46. * Detects if data is able to transform into an isolation payload.
  47. *
  48. * @param {object} data - object that is expected to contain at least a callback and error identifier
  49. * @return {boolean} - if the data is able to transform into an isolation payload
  50. */
  51. function isIsolationPayload(data) {
  52. return typeof data === 'object' && 'callback' in data && 'error' in data
  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. message.__TAURI_INVOKE_KEY__ = __TAURI_INVOKE_KEY__
  87. switch (pattern) {
  88. case 'brownfield':
  89. window.__TAURI_POST_MESSAGE__(message)
  90. break
  91. case 'isolation':
  92. if (!isIsolationPayload(message)) {
  93. console.error(
  94. 'Tauri "Isolation" Pattern found an invalid isolation message payload',
  95. message
  96. )
  97. break
  98. }
  99. if (isolation.ready) {
  100. sendIsolationMessage(message)
  101. } else {
  102. isolation.queue.push(message)
  103. }
  104. break
  105. case 'error':
  106. console.error(
  107. 'Tauri IPC found a Tauri Pattern, but it was an error. Check for other log messages to find the cause.'
  108. )
  109. break
  110. default:
  111. console.error(
  112. 'Tauri IPC did not find a Tauri Pattern that it understood.'
  113. )
  114. break
  115. }
  116. })
  117. })
  118. /**
  119. * IMPORTANT: See isolation_secure.js for the isolation frame implementation.
  120. * main frame -> isolation frame = isolation payload
  121. * isolation frame -> main frame = isolation message
  122. */
  123. if (pattern === 'isolation') {
  124. window.addEventListener(
  125. 'message',
  126. (event) => {
  127. // watch for the isolation frame being ready and flush any queued messages
  128. if (event.data === '__TAURI_ISOLATION_READY__') {
  129. isolation.ready = true
  130. for (const message of isolation.queue) {
  131. sendIsolationMessage(message)
  132. }
  133. isolation.queue = []
  134. return
  135. }
  136. if (isIsolationMessage(event)) {
  137. window.__TAURI_POST_MESSAGE__(event.data)
  138. }
  139. },
  140. false
  141. )
  142. }
  143. })()