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

Don't crash when calling stale remote listeners #8357

Merged
merged 14 commits into from Jan 25, 2017

Conversation

CharlieHess
Copy link
Contributor

@CharlieHess CharlieHess commented Jan 6, 2017

When Electron tries to call a method in a deceased renderer it throws an exception. This behavior makes the remote module a bit of a hornet's nest, because it's quite easy to listen to events from the main process but often non-trivial to clean them up properly. Instead of crashing, we could dig into the underlying EventEmitter and remove the listeners that are hooked to callIntoRenderer. We could also display a console.warn so that the user knows they did something bad and still has a chance to fix it.

In addition this PR adds the names of the remote events to the error message, to make them easier to find. Increasingly often methods are wrapped in higher-level abstractions such as Promise or Observable so meta.location is not enough information on its own. The new warning looks like:

Attempting to call a function in a renderer window that has been closed or released.
Function provided here: remote-event-handler.html:11:33
Remote event names: remote-handler, other-remote-handler

@kevinsawicki
Copy link
Contributor

This seems helpful, could you add a spec for this as well?

: eventsAttached[eventName] === callIntoRenderer
})

if (remoteEvents && remoteEvents.length > 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think Array.prototype.filter can return null so I think you can drop the first part of this check and just use remoteEvent.length > 0

let message = `Attempting to call a function in a renderer window that has been closed or released.` +
`\nFunction provided here: ${meta.location}`

if (!args || args.length === 0) return message
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

args will never be passed as null by the calling method so I think you can just use arg.length === 0 here.

@@ -146,6 +146,30 @@ const throwRPCError = function (message) {
throw error
}

const rendererMissingErrorMessage = function (meta, args, callIntoRenderer) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a new method, feel free to make this a => function if you'd like.

message += `\nRemote event names:`
remoteEvents.forEach((eventName) => {
message += ` ${eventName} `
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be simplified to:

message += `\nRemote event names: ${remoteEvents.join(' ')}`

@zeke
Copy link
Contributor

zeke commented Jan 11, 2017

This is great! Should save people time and energy when debugging. 👍

@CharlieHess
Copy link
Contributor Author

@kevinsawicki updated with your comments. As for the spec, I'm happy to write one but unsure where it should go? rpc-server doesn't have any specs; I could test this method in isolation but I dunno how meaningful those tests would be.

@kevinsawicki
Copy link
Contributor

As for the spec, I'm happy to write one but unsure where it should go?

Usually they go in api-ipc-spec.js.

See #6963 for a recent pull request with a test example.

@CharlieHess
Copy link
Contributor Author

@kevinsawicki I was able to get a spec in for the Attempting to call a function in a renderer window that has been closed or released error message, but only when it was wrapped by rpc-server.callFunction. Had a tougher time catching the exception from within the page (which is where we could check the events 😞).

@kevinsawicki
Copy link
Contributor

Had a tougher time catching the exception from within the page (which is where we could check the events 😞).

I'll take a look 👀

if (args.length === 0) return message
if (!args[0].sender || !args[0].sender._events) return message

const eventsAttached = args[0].sender._events
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this use https://nodejs.org/api/events.html#events_emitter_eventnames to get an array of event names?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could, but we'll get all the events. We need to compare the event handler with callIntoRenderer to know if it's a remote listener, so we'll still have to iterate.

@kevinsawicki
Copy link
Contributor

@CharlieHess I updated the spec, let me know what you think.

@CharlieHess
Copy link
Contributor Author

@kevinsawicki new spec looks good, thanks! ✨

@CharlieHess CharlieHess changed the title Display more information about remote event crashes Don't crash when calling stale remote listeners Jan 13, 2017
if (remoteEvents.length > 0) {
message += `\nRemote event names: ${remoteEvents.join(', ')}`
remoteEvents.forEach((eventName) => {
sender.removeListener(eventName, callIntoRenderer)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be great to assert this happens as well in the spec, perhaps by asserting listenerCount() goes down for each event name being listened to.

@@ -146,6 +146,28 @@ const throwRPCError = function (message) {
throw error
}

const rendererMissingErrorMessage = (meta, args, callIntoRenderer) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this method now removes the listeners as well, I think perhaps it should be renamed and do the console.warn directly in here.

Maybe a name like logMissingRendererMessageAndRemoveListeners?

@kevinsawicki
Copy link
Contributor

Thanks for this @CharlieHess 👍

@kevinsawicki kevinsawicki merged commit 8e69f94 into master Jan 25, 2017
@kevinsawicki kevinsawicki deleted the remote-events-error-message branch January 25, 2017 22:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants