Skip to content

Conversation

@cmonfortep
Copy link
Contributor

@cmonfortep cmonfortep commented Dec 6, 2019

Task/Issue URL: https://app.asana.com/0/0/1152512237237203/f
Tech Design URL: https://app.asana.com/0/0/1151307893053517/f
CC:

Description:

Introducing gracefully handling WebView crash errors into our Browser.
A WebView render process can be gone due to a rendering crash or memory pressure situations. When we receive such event from WebViewClient we will avoid crashing, and instead, we will show a snackbar to inform about what happened and the possibility to reload the current URL. Also, application will still be usable so users will be able to perform other actions as closing the tab or navigate to a new URL.

Steps to test this PR:

As we can't reproduce easily memory pressure situations or crashes, we will need to change our source code a bit in order to manually produce a crash.
Easiest way is to call recoverFromRenderProcessGone() when user clicks in a menu option, example will be inside onBrokenSiteSelected().

Scenarios to test

Scenario to test:

  • Open app
  • submit an URL
  • crash the app (with hardcoded code)
  • Verify user can't navigate back/forward/find a page/report a site

Scenario to test:

  • Open app
  • submit an URL
  • crash the app (with hardcoded code)
  • Verify snackbar appears
  • Reload from snackbar
  • Verify current tab is closed and a new one is opened with same URL

Scenario to test:

  • Open app
  • submit an URL
  • crash the app (with hardcoded code)
  • Verify snackbar appears
  • Reload from option menu
  • Verify current tab is closed and a new one is opened with same URL

Scenario to test:

  • Open app
  • submit an URL
  • Navigate to produce some history navigation
  • crash the app (with hardcoded code)
  • Verify snackbar appears
  • press back
  • Verify home is shown
  • press back
  • Verify exits the app

Scenario to test:

  • Open app
  • submit an URL
  • crash the app (with hardcoded code)
  • Verify snackbar appears
  • Submit current or a new URL
  • Verify current tab is closed and a new one is opened with expected URL

Scenario to test:

  • Open app
  • submit an URL
  • crash the app (with hardcoded code)
  • Verify snackbar appears
  • Open privacy settings and disable or enable toogle
  • Go back to the tab
  • Verify current tab is closed and a new one is opened with same URL

Scenario to test:

  • Open app
  • submit an URL
  • crash the app (with hardcoded code)
  • Open a new tab
  • submit an URL
  • crash the app (with hardcoded code)
  • Change between tabs, snackbar should appear

Scenario to test:

  • Open app
  • submit an URL
  • crash the app (with hardcoded code)
  • Open a new tab
  • submit an URL
  • crash the app (with hardcoded code)
  • Send app to background
  • Bring back to foreground the app
  • Snakbar should appear

Internal references:

Software Engineering Expectations
Technical Design Template

* Error forwarded from WebViewClient to ViewModel
* Introduced a new command to show an error with recovery action.
* GlobalLayoutViewState as sealed class to model invalidated state or Browser.
* Fragment invalidates WebView after crash destroying it.
…outViewState.

Detected some issues when having multiple tabs crashed. Snackbar is like a singleton view that will be added only to one view hierarchy. We need to avoid adding the error snackbar in a non visible instance of BrowserTabFragment.
@cmonfortep cmonfortep changed the title Feature/cm/recover from webview crashes Allow users to recover from WebView renderer failures Dec 6, 2019
* Disabling navigation should affect also webNavigationState
* Included a new StateChanged and new WebNavigationState to clear user navigations without affecting Site properties
* Clear navigation using navigationStateChanged
* Cover new logic and types with tests
@cmonfortep cmonfortep force-pushed the feature/cm/recover_from_webview_crashes branch from b174a94 to 0435a69 Compare December 7, 2019 19:21
@cmonfortep cmonfortep marked this pull request as ready for review December 8, 2019 16:57
@subsymbolic subsymbolic self-assigned this Dec 9, 2019
Copy link
Contributor

@subsymbolic subsymbolic left a comment

Choose a reason for hiding this comment

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

This is looking great so far however the new state acts as if the user has "gone home" which they haven't and introduces odd behavior like showing the logo and call-to-action (CTA)
cta_interference
home_showing


showErrorWithAction.action()

assertCommandIssued<Command.OpenInNewTab>()
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we check the URL too?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, I will add a new testcase to cover that part.


<resources>
<string name="crashedWebViewErrorMessage" translatable="false">"The webpage could not be displayed."</string>
<string name="crashedWebViewErrorAction" translatable="false">"Reload"</string>
Copy link
Contributor

Choose a reason for hiding this comment

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

Update translation="false" to tools:ignore="MissingTranslation", that way when we open the translation editor in android studio the missing translations are highlighted.

}

override fun onRenderProcessGone(view: WebView?, detail: RenderProcessGoneDetail?): Boolean {
viewModel.onSurveyFailedToLoad()
Copy link
Contributor

@subsymbolic subsymbolic Dec 9, 2019

Choose a reason for hiding this comment

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

Is this valid? It eventually leads to a call to webView.gone() however according to the docs:

The given WebView can't be used, and should be removed from the view hierarchy, all references to it should be cleaned up

In the case of an onRenderProcessGone failure the WebView should removed and set to null not just hidden.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, it's valid to call webView.gone() but not enough. I will improve this part in order to do a better clean up for that WebView.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I went for adding extra logic inside showError in order to fully remove the WebView, instead of creating a new method in SurveyViewModel + a new Command to destroy the WebView.
Since currently when an error happens user can't interact with Survey, I just destroy the WebView too, there's no need to keep it alive.

browserLayout.show()
}
}
is GlobalLayoutViewState.Invalidated -> destroyWebView()
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this valid? It eventually calls webview.destroy() however according to the docs:

The given WebView can't be used, and should be removed from the view hierarchy, all references to it should be cleaned up

I recommend creating an invalidate method that removes and nulls out the WebView rather than reusing this method that interacts with the Webview instance when we don't know what state it is in.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point, I had the same doubt because "WebView can't be used" is not clear enough.
I did some code investigations and tests, and I deduced that they refer to continue rendering in that WebView instance, or any action that will directly involve the renderer process.

While reading again the docs, their sample is using exactly that method call, so I would say that it's safe and correct in order to clean up WebView internal references.

// Remove the inflated view from the container and cleanup
// the dead webview specifically (if it is not GC'ed it will cause
// problems later, the next time the renderer dies)
(...)
view.destroy();

* show error when using is browing, not when is in home page.
* when goes to home page after a crash, disable forward navigation.
Copy link
Contributor

@subsymbolic subsymbolic left a comment

Choose a reason for hiding this comment

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

Great work!

@cmonfortep cmonfortep merged commit 69d826a into develop Dec 11, 2019
@cmonfortep cmonfortep deleted the feature/cm/recover_from_webview_crashes branch December 11, 2019 20:39
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

Successfully merging this pull request may close these issues.

2 participants