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

fix: reset render_frame_disposed_ after render frame host change #31401

Merged
merged 2 commits into from Oct 14, 2021

Conversation

VerteDinde
Copy link
Member

Description of Change

This PR fixes a JavaScript crash that occurs if a webContents is trying to send or reload when a render frame has been marked as disposed. Previously, if this occurred, the send would fail silently and eventually recover and reload. Now, we throw an error in CheckRenderFrame, which appears to an app user as a hard crash.

This PR resets the value of render_frame_disposed_ after the render frame host has been updated, and prevents throwing a hard error on webFrameMain.send or webFrameMain.reload, while still checking for the existence of the render_frame_.

Checklist

Release Notes

Notes: Fixed a JavaScript exception from webContents if render frame was disposed in WebFrameMain, resets the value of render_frame_disposed_ after updating render frame host

@VerteDinde VerteDinde added semver/patch backwards-compatible bug fixes target/15-x-y labels Oct 12, 2021
@electron-cation electron-cation bot added the new-pr 🌱 PR opened in the last 24 hours label Oct 12, 2021
shell/browser/api/electron_api_web_frame_main.cc Outdated Show resolved Hide resolved
@@ -174,7 +178,7 @@ void WebFrameMain::Send(v8::Isolate* isolate,
return;
}

if (!CheckRenderFrame())
if (render_frame_disposed_)
Copy link
Member

Choose a reason for hiding this comment

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

Was this change added to silence the errors? I'm unsure if this is the best direction we should take.

I'm wondering if we should have something similar to WebContents.isDestroyed(). Otherwise we could add try/catch where this fails. Did we have internal code (in lib/*.ts) which was failing?

Copy link
Member Author

Choose a reason for hiding this comment

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

It was, yeah. From what I can tell, this is the call that's failing on the *.ts side: https://github.com/electron/electron/blob/main/lib/browser/api/web-contents.ts#L125 Since it connects directly to mainFrame, maybe we could add a try/catch on the send directly?

Copy link
Member Author

Choose a reason for hiding this comment

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

Added a try/catch to send within web-frame-main.ts, not webContents, to bring it a little closer to the source - I'm a little unsure about my original approach and the new try/catch, since it feels more like a band-aid than actually addressing the crash, but this should at least stop a hard JavaScript error from being thrown on send failure.

Copy link
Member

Choose a reason for hiding this comment

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

Although it's difficult to know for sure without being able to reproduce the crash, my theory is that WebFrameMain::MarkRenderFrameDisposed() was called prior to WebFrameMain::UpdateRenderFrameHost(). In that case, the new RFH would be set, but render_frame_disposed_ == true would still hold.

This could happen when the RFH is deleted

auto* web_frame = WebFrameMain::FromRenderFrameHost(render_frame_host);
if (web_frame && web_frame->render_frame_host() == render_frame_host)
web_frame->MarkRenderFrameDisposed();

followed by it being swapped for a new RFH
auto* web_frame =
WebFrameMain::FromFrameTreeNodeId(new_host->GetFrameTreeNodeId());
if (web_frame) {
web_frame->UpdateRenderFrameHost(new_host);
}

It may be possible that the changes in electron_api_web_frame_main.cc alone fix the problem, in which case the try/catch might be unnecessary.

Copy link
Member Author

Choose a reason for hiding this comment

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

@samuelmaddock I dug more into this yesterday; the MarkRenderFrameDisposed prior to the new RFH being updated and set is definitely what's keeping the renderer process in a continuous loop 🙂 I think fixing that should fix the main issue! I did test the crash with only that section fixed, just to verify, and it's still throwing one hard JS exception when you try to reload, which then keeps the renderer process for recovering:
Screen Shot 2021-10-13 at 4 34 54 PM

I'll keep the try/catch to avoid throwing a hard exception; this should allow apps with stalled renderer processes to reload and recover.

@VerteDinde VerteDinde force-pushed the fix-render-frame-renderer-hang branch from 73a902e to bf3462e Compare October 13, 2021 23:11
@electron-cation electron-cation bot removed the new-pr 🌱 PR opened in the last 24 hours label Oct 13, 2021
@VerteDinde VerteDinde merged commit bff20bd into main Oct 14, 2021
@VerteDinde VerteDinde deleted the fix-render-frame-renderer-hang branch October 14, 2021 16:44
@release-clerk
Copy link

release-clerk bot commented Oct 14, 2021

Release Notes Persisted

Fixed a JavaScript exception from webContents if render frame was disposed in WebFrameMain, resets the value of render_frame_disposed_ after updating render frame host

@trop
Copy link
Contributor

trop bot commented Oct 14, 2021

I have automatically backported this PR to "14-x-y", please check out #31425

@trop
Copy link
Contributor

trop bot commented Oct 14, 2021

I have automatically backported this PR to "15-x-y", please check out #31426

@trop
Copy link
Contributor

trop bot commented Oct 14, 2021

I have automatically backported this PR to "16-x-y", please check out #31427

jaked added a commit to jaked/programmable-matter that referenced this pull request Oct 19, 2021
ran into electron/electron#31401
because the web contents were destroyed before sending the events?
but I guess those events were never actually sent
t57ser pushed a commit to t57ser/electron that referenced this pull request Oct 27, 2021
…ctron#31401)

* fix: reset render_frame_disposed_ after hang

* fix: handle exception in webContents.send
t57ser pushed a commit to t57ser/electron that referenced this pull request Oct 27, 2021
…ctron#31401)

* fix: reset render_frame_disposed_ after hang

* fix: handle exception in webContents.send
t57ser pushed a commit to t57ser/electron that referenced this pull request Oct 29, 2021
…ctron#31401)

* fix: reset render_frame_disposed_ after hang

* fix: handle exception in webContents.send
t57ser pushed a commit to t57ser/electron that referenced this pull request Oct 29, 2021
…ctron#31401)

* fix: reset render_frame_disposed_ after hang

* fix: handle exception in webContents.send
t57ser pushed a commit to t57ser/electron that referenced this pull request Oct 29, 2021
…ctron#31401)

* fix: reset render_frame_disposed_ after hang

* fix: handle exception in webContents.send
t57ser pushed a commit to t57ser/electron that referenced this pull request Oct 29, 2021
…ctron#31401)

* fix: reset render_frame_disposed_ after hang

* fix: handle exception in webContents.send
@miniak
Copy link
Contributor

miniak commented Dec 8, 2021

/trop run backport-to 13-x-y

@trop
Copy link
Contributor

trop bot commented Dec 8, 2021

The backport process for this PR has been manually initiated - sending your PR to 13-x-y!

@trop
Copy link
Contributor

trop bot commented Dec 8, 2021

I have automatically backported this PR to "13-x-y", please check out #32130

@trop trop bot added the in-flight/13-x-y label Dec 8, 2021
return this._send(false /* internal */, channel, args);
try {
return this._send(false /* internal */, channel, args);
} catch (e) {
Copy link
Contributor

@poiru poiru Dec 8, 2021

Choose a reason for hiding this comment

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

I don't think this is an ideal fix. I don't think we want to swallow all exceptions like for example:

if (!gin::ConvertFromV8(isolate, args, &message)) {
isolate->ThrowException(v8::Exception::Error(
gin::StringToV8(isolate, "Failed to serialize arguments")));
return;
}

That should continue to throw. We only want to avoid the exception from:

bool WebFrameMain::CheckRenderFrame() const {
if (render_frame_disposed_) {
v8::Isolate* isolate = JavascriptEnvironment::GetIsolate();
v8::Locker locker(isolate);
v8::HandleScope scope(isolate);
gin_helper::ErrorThrower(isolate).ThrowError(
"Render frame was disposed before WebFrameMain could be accessed");
return false;
}
return true;
}

IMO we should instead avoid throwing in the first place in WebFrameMain::Send, WebFrameMain::PostMessage, and perhaps WebContents::MessageTo. We could do that by replacing the CheckRenderFrame() call with something that just checks the status instead of also throwing.

cc @VerteDinde

Copy link
Member Author

Choose a reason for hiding this comment

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

Thanks @poiru! Just saw your comments here, will open a new PR with those improvements. We're in the middle of a December quiet period, so we may not have a new release with the fix until January, but will work on getting that fix ready.

Copy link
Contributor

Choose a reason for hiding this comment

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

Perfect, thank you @VerteDinde!

Copy link
Contributor

Choose a reason for hiding this comment

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

@VerteDinde Friendly reminder about this!

return this._send(true /* internal */, channel, args);
} catch (e) {
console.error('Error sending from webFrameMain: ', e);
}
};

WebFrameMain.prototype.postMessage = function (...args) {
Copy link
Contributor

Choose a reason for hiding this comment

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

The this._postMessage call below can also throw in the same way as the calls above. We should fix it too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
semver/patch backwards-compatible bug fixes
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants