serversideup / webext-bridge

💬 Messaging in Web Extensions made easy. Batteries included.
https://serversideup.net/open-source/webext-bridge
MIT License
547 stars 50 forks source link

disfunction to send message from an iframe page to background and others #47

Open heavenkiller2018 opened 2 years ago

heavenkiller2018 commented 2 years ago

wrap one of the extension pages as an iframe is an new extension design pattern, which is recommended by quasar. but there are some communication problems when I use webext-bridge to this pattern.

for example, I use the options/index.html as the iframe page, (https://github.com/antfu/vitesse-webext as the base template) so, in contentScripts/index.ts

  const iFrame = document.createElement('iframe')
  const defaultFrameHeight = '200px'

  /**
   * Set the height of our iFrame housing our BEX
   * @param height
   */
  const setIFrameHeight = (height) => {
    iFrame.height = height
  }

  /**
   * Reset the iFrame to its default height e.g The height of the top bar.
   */
  const resetIFrameHeight = () => {
    setIFrameHeight(defaultFrameHeight)
  }

  /**
   * The code below will get everything going. Initialize the iFrame with defaults and add it to the page.
   * @type {string}
   */
  iFrame.id = 'bex-app-iframe'
  iFrame.width = '100%'
  resetIFrameHeight()

  // Assign some styling so it looks seamless
  Object.assign(iFrame.style, {
    position: 'fixed',
    top: '0',
    right: '0',
    bottom: '0',
    left: '0',
    border: '0',
    // scrolling: 'no',
    allowfullscreen: 'true',
    zIndex: '9999999', // Make sure it's on top
    overflow: 'visible',
  })

  iFrame.src = browser.runtime.getURL('dist/options/index.html')
  document.body.prepend(iFrame)

then, in options/index.html, I add an button of which the handler function is test to start the sendmessage test

async function test(){
  console.group('webext-bridge')
  const result = await sendMessage('test', 'ping')
  console.log(result)
  console.groupEnd('webext-bridge')
}

in background.ts

onMessage('test', async (payload) => {
  console.log(payload)
  return 'pong'
})

send I open an new page and clicked the test button in the iframe some times, the console show as the follow: image

console of background (nothing!)

it proved that it's failed to send messages to background.

that I open the options/index.html directly through the menu on popup icon. and replay the test again.

this time, it's successful.

console of options/index.html

image

console of background

image

@antfu @zikaari How can I do to enable the communication from an iframe to background with webext-bridge?

heavenkiller2018 commented 2 years ago

I found in such context that the portId has the format option.[frameId], which can't be parsed by function parseEndpoint correctly.

internal.ts line: 104:

  if (context === 'background') {
    browser.runtime.onConnect.addListener((incomingPort) => {
      // when coming from devtools, it's should pre-fabricated with inspected tab as linked tab id

      let portId = incomingPort.name || `content-script@${incomingPort.sender.tab.id}`

      const portFrame = incomingPort.sender.frameId

      if (portFrame)
        portId = `${portId}.${portFrame}`
...

for example: the result of parseEndpoint("options.123") is {context: undefined, tabId: NaN, frameId: undefined} which can't be handled by the program well. (it only consider the situations such as options@567.123 which can be parsed to {context: 'options', tabId: 567, frameId: 123})

so I temporarily modified two places of code: internal.ts

line: 108

      let portId = incomingPort.name || `content-script@${incomingPort.sender.tab.id}`

      const portFrame = incomingPort.sender.frameId

change to

      const portTabId = incomingPort.sender?.tab?.id
      if (portId === 'options' && portTabId)
        portId = `${portId}@${portTabId}`
      const portFrame = incomingPort.sender.frameId

line 212:

      let resolvedDestination = ['popup', 'options'].includes(destName)
        ? destName
        : (`${(destName === 'window' ? 'content-script' : destName)}@${(destTabId || srcTabId)}`)

change to

      let resolvedDestination = ['popup', 'options'].includes(destName)
        ? `${destName}@${destTabId}`
        : (`${(destName === 'window' ? 'content-script' : destName)}@${(destTabId || srcTabId)}`)

now, send message from an iframe to background can effects!

mubaidr commented 1 year ago

I found in such context that the portId has the format option.[frameId], which can't be parsed by function parseEndpoint correctly.

internal.ts line: 104:

  if (context === 'background') {
    browser.runtime.onConnect.addListener((incomingPort) => {
      // when coming from devtools, it's should pre-fabricated with inspected tab as linked tab id

      let portId = incomingPort.name || `content-script@${incomingPort.sender.tab.id}`

      const portFrame = incomingPort.sender.frameId

      if (portFrame)
        portId = `${portId}.${portFrame}`
...

for example: the result of parseEndpoint("options.123") is {context: undefined, tabId: NaN, frameId: undefined} which can't be handled by the program well. (it only consider the situations such as options@567.123 which can be parsed to {context: 'options', tabId: 567, frameId: 123})

so I temporarily modified two places of code: internal.ts

line: 108

      let portId = incomingPort.name || `content-script@${incomingPort.sender.tab.id}`

      const portFrame = incomingPort.sender.frameId

change to

      const portTabId = incomingPort.sender?.tab?.id
      if (portId === 'options' && portTabId)
        portId = `${portId}@${portTabId}`
      const portFrame = incomingPort.sender.frameId

line 212:

      let resolvedDestination = ['popup', 'options'].includes(destName)
        ? destName
        : (`${(destName === 'window' ? 'content-script' : destName)}@${(destTabId || srcTabId)}`)

change to

      let resolvedDestination = ['popup', 'options'].includes(destName)
        ? `${destName}@${destTabId}`
        : (`${(destName === 'window' ? 'content-script' : destName)}@${(destTabId || srcTabId)}`)

now, send message from an iframe to background can effects!

This is a good find. You should create a pull request for this change.