7

For my chrome extension, I want to perform an action when the browserAction popup window closes. I understand that there no built-in events are triggered when this happens. I found this suggestion to open a connection with the background script, and then use the connection's port.onDisconnect event to detect that the popup window is closing.

However, when the popup window closes, I see the following error in the Developer Console for the background script:

(BLESSED_EXTENSION context for glkehflnlfekdijfhacccflbffbjhgbd) extensions::messaging:102: Uncaught TypeError: Cannot read property 'destroy_' of undefined{TypeError: Cannot read property 'destroy_' of undefined
   at PortImpl.destroy_ (extensions::messaging:102:37)
   at dispatchOnDisconnect (extensions::messaging:322:29)}

The scripts that I use are detailed below.

Can you see where I am going wrong?


manifest.json

{ "manifest_version": 2

, "name": "Detect when popup closes"
, "version": "0.1"

, "browser_action": {
    "default_icon": "popup.png"
  , "default_popup": "popup.html"
  }

, "background": {
    "scripts": [
      "background.js"
    ]
  }
}

popup.html

<!DOCTYPE html>
<body>
  <h1>Test</h1>

  <script src="popup.js"></script>  
</body>
</html>

popup.js

var port = chrome.runtime.connect()

background.js

chrome.runtime.onConnect.addListener(function (externalPort) {
  externalPort.onDisconnect = function () {
    try { 
      var ignoreError = chrome.runtime.lastError
    } catch (error) {
      console.log("onDisconnect")
    }
  }
)
Community
  • 1
  • 1
James Newton
  • 6,623
  • 8
  • 49
  • 113
  • 2
    The suggestion was to detect the event in background script. The port is disconnected *because* the popup was destroyed so there are no event listeners left to be invoked on port disconnection. – wOxxOm Sep 27 '16 at 17:18
  • @wOxxOm I've rewritten my bare-bones extension to use `port.onDisconnect` in the background script, as you suggest. However, this merely displaces the error to the moment when the popup window closes. – James Newton Sep 27 '16 at 17:59
  • The error in this case is documented and may be ignored by simple assignment `var ignoreError = chrome.runtime.lastError`. The important thing is that the disconnect listener will actually fire. – wOxxOm Sep 27 '16 at 18:02
  • @wOxxOm I've added the assignment that you suggested, but I doubt that I have put it in the right place. Perhaps the disconnect listener fires, but the error prevents me from getting it to do anything useful. – James Newton Sep 27 '16 at 18:19
  • 1
    @JamesNewton I rolled back your "SOLVED" edit; in future, please post your own solution (if it's appropriate) as a separate answer - self-answering is absolutely okay, but mixing answers into questions is not. Also, there's no need to tag your question as solved in the title (the fact that there is an accepted answer is enough). – Xan Sep 28 '16 at 09:57

2 Answers2

13

For reference, here's the working version of the background.js script:

chrome.runtime.onConnect.addListener(function (externalPort) {
  externalPort.onDisconnect.addListener(function () {
    console.log("onDisconnect")
    // Do stuff that should happen when popup window closes here
  })

  console.log("onConnect")
})
James Newton
  • 6,623
  • 8
  • 49
  • 113
7

onDisconnect is not an assignable property.
It's an object that provides addListener method to register a callback:

externalPort.onDisconnect.addListener(function() {
    var ignoreError = chrome.runtime.lastError;
    console.log("onDisconnect");
});
wOxxOm
  • 65,848
  • 11
  • 132
  • 136
  • I would also recommend saving `externalPort` to a variable in the outside scope; I think otherwise it risks garbage-collection. – Xan Sep 27 '16 at 19:04
  • 1
    Yes, but only to modify the port's behavior later. The listener itself is registered in the browser internals so it'll stay. – wOxxOm Sep 27 '16 at 19:10
  • How do you get `.onDisconnect` to fire when the popup closes? Whenever I implement it, it only fires when the actual extension is uninstalled. – Mudlabs Aug 15 '17 at 06:29
  • It should be in the background page. – wOxxOm Aug 15 '17 at 06:31
  • Ok sweet! I was trying to setup a message port between my `popup.js` and `content script`. But you have to pass through `background.js`, like it's the post office and `popup.js` and `content script` are two people trying to send a letter, Yes? – Mudlabs Aug 15 '17 at 07:34
  • Content script should also work. I meant onDisconnect shouldn't be in the popup itself. – wOxxOm Aug 15 '17 at 07:40
  • Maybe it's an execution time/order problem then? – Mudlabs Aug 15 '17 at 08:20
  • Maybe. Why not. – wOxxOm Aug 15 '17 at 08:21
  • 1
    @wOxxOm is it possible to place the onDisconnect listener in the Content Script instead of in `background.js`? I tried, but could not get it to work. It would save me having to create another script to inject, as the method i wish to run when closing the popup resides inside the Content Script. – Magnus Apr 18 '18 at 18:58
  • Sure. Simply use chrome.**tabs**.connect with a tab id in the popup. – wOxxOm Apr 18 '18 at 19:12