Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
heavenkiller2018 opened this issue Nov 16, 2022 · 2 comments

Comments

@heavenkiller2018
Copy link

heavenkiller2018 commented Nov 16, 2022

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
Copy link
Author

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 [email protected] 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
Copy link

mubaidr commented Oct 6, 2023

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 [email protected] 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants