ipc.js 4.1 KB

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