ZeroFrame.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. /*
  2. Original code is Copyright (c) 2020 ZeroNet project.
  3. Original code was released under the GPLv2 license by ZeroNet project, December 2016.
  4. Original code was rereleased under the MIT license by ZeroNet project, March 2020.
  5. Permission is hereby granted, free of charge, to any person obtaining
  6. a copy of this software and associated documentation files (the
  7. "Software"), to deal in the Software without restriction, including
  8. without limitation the rights to use, copy, modify, merge, publish,
  9. distribute, sublicense, and/or sell copies of the Software, and to
  10. permit persons to whom the Software is furnished to do so, subject to
  11. the following conditions:
  12. The above copyright notice and this permission notice shall be
  13. included in all copies or substantial portions of the Software.
  14. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  15. EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  16. MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  17. NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  18. LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  19. OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  20. WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  21. */
  22. // Version 1.0.0 - Initial release
  23. // Version 1.1.0 (2017-08-02) - Added cmdp function that returns promise instead of using callback
  24. // Version 1.2.0 (2017-08-02) - Added Ajax monkey patch to emulate XMLHttpRequest over ZeroFrame API
  25. // Version 1.3.0 (2018-12-05) - Added monkey patch for fetch API
  26. // Version 1.3.1 (2019-09-02) - Fix memory leak while handling responses
  27. // Version 1.4.0 (2019-12-11) - Awaitable monkeyPatchAjax function
  28. const CMD_INNER_READY = 'innerReady'
  29. const CMD_RESPONSE = 'response'
  30. const CMD_WRAPPER_READY = 'wrapperReady'
  31. const CMD_PING = 'ping'
  32. const CMD_PONG = 'pong'
  33. const CMD_WRAPPER_OPENED_WEBSOCKET = 'wrapperOpenedWebsocket'
  34. const CMD_WRAPPER_CLOSE_WEBSOCKET = 'wrapperClosedWebsocket'
  35. class ZeroFrame {
  36. constructor(url) {
  37. this.url = url
  38. this.waiting_cb = {}
  39. this.wrapper_nonce = document.location.href.replace(/.*wrapper_nonce=([A-Za-z0-9]+).*/, "$1")
  40. this.connect()
  41. this.next_message_id = 1
  42. this.init()
  43. }
  44. init() {
  45. return this
  46. }
  47. connect() {
  48. this.target = window.parent
  49. window.addEventListener('message', e => this.onMessage(e), false)
  50. this.cmd(CMD_INNER_READY)
  51. }
  52. onMessage(e) {
  53. let message = e.data
  54. let cmd = message.cmd
  55. if (cmd === CMD_RESPONSE) {
  56. if (this.waiting_cb[message.to] !== undefined) {
  57. this.waiting_cb[message.to](message.result)
  58. delete this.waiting_cb[message.to]
  59. }
  60. else {
  61. this.log("Websocket callback not found:", message)
  62. }
  63. } else if (cmd === CMD_WRAPPER_READY) {
  64. this.cmd(CMD_INNER_READY)
  65. } else if (cmd === CMD_PING) {
  66. this.response(message.id, CMD_PONG)
  67. } else if (cmd === CMD_WRAPPER_OPENED_WEBSOCKET) {
  68. this.onOpenWebsocket()
  69. } else if (cmd === CMD_WRAPPER_CLOSE_WEBSOCKET) {
  70. this.onCloseWebsocket()
  71. } else {
  72. this.onRequest(cmd, message)
  73. }
  74. }
  75. onRequest(cmd, message) {
  76. this.log("Unknown request", message)
  77. }
  78. response(to, result) {
  79. this.send({
  80. cmd: CMD_RESPONSE,
  81. to: to,
  82. result: result
  83. })
  84. }
  85. cmd(cmd, params={}, cb=null) {
  86. this.send({
  87. cmd: cmd,
  88. params: params
  89. }, cb)
  90. }
  91. cmdp(cmd, params={}) {
  92. return new Promise((resolve, reject) => {
  93. this.cmd(cmd, params, (res) => {
  94. if (res && res.error) {
  95. reject(res.error)
  96. } else {
  97. resolve(res)
  98. }
  99. })
  100. })
  101. }
  102. send(message, cb=null) {
  103. message.wrapper_nonce = this.wrapper_nonce
  104. message.id = this.next_message_id
  105. this.next_message_id++
  106. this.target.postMessage(message, '*')
  107. if (cb) {
  108. this.waiting_cb[message.id] = cb
  109. }
  110. }
  111. log(...args) {
  112. console.log.apply(console, ['[ZeroFrame]'].concat(args))
  113. }
  114. onOpenWebsocket() {
  115. this.log('Websocket open')
  116. }
  117. onCloseWebsocket() {
  118. this.log('Websocket close')
  119. }
  120. async monkeyPatchAjax() {
  121. var page = this
  122. XMLHttpRequest.prototype.realOpen = XMLHttpRequest.prototype.open
  123. var newOpen = function (method, url, async) {
  124. url += "?ajax_key=" + page.ajax_key
  125. return this.realOpen(method, url, async)
  126. }
  127. XMLHttpRequest.prototype.open = newOpen
  128. window.realFetch = window.fetch
  129. var newFetch = function (url) {
  130. url += "?ajax_key=" + page.ajax_key
  131. return window.realFetch(url)
  132. }
  133. window.fetch = newFetch
  134. this.ajax_key = await page.cmdp("wrapperGetAjaxKey", [])
  135. }
  136. }