Uploaded image for project: 'Apache Cordova'
  1. Apache Cordova
  2. CB-6936

app crashes if webview is destroyed while dialog box open

    Details

    • Type: Bug
    • Status: Open
    • Priority: Minor
    • Resolution: Unresolved
    • Affects Version/s: 3.4.0
    • Fix Version/s: None
    • Component/s: cordova-android
    • Labels:
      None
    • Environment:

      Android 4.x on physical device
      Or any Android environment

      Description

      We have an Android application which implements an embedded WebView "container" in which it executes customer Cordova apps.

      Under certain conditions our container needs to terminate the customer app, and during this termination it calls CordovaWebView.destroy() to disable CordovaWebView.

      But application may entirely crash during this termination in some scenarios. For example:

      1. call navigator.notification.alert, confirm or prompt and leave popup dialog open
      2. this termination is done for some reason while dialog box is till open
      3. all views on CordovaWebView are released but dialog box still remains
      4. attempt to close dialog by pressing button in the dialog
      5. application crashes with Unknown exception.
      06-13 21:45:19.765: E/FSP_INTS-MAPS_AG(11176): 81508: Unknown exception occurred.
      06-13 21:45:19.765: E/FSP_INTS-MAPS_AG(11176): java.lang.NullPointerException
      06-13 21:45:19.765: E/FSP_INTS-MAPS_AG(11176): 	at android.webkit.WebView.setNetworkAvailable(WebView.java:2639)
      06-13 21:45:19.765: E/FSP_INTS-MAPS_AG(11176): 	at org.apache.cordova.NativeToJsMessageQueue$OnlineEventsBridgeMode$1.run(NativeToJsMessageQueue.java:305)
      06-13 21:45:19.765: E/FSP_INTS-MAPS_AG(11176): 	at android.app.Activity.runOnUiThread(Activity.java:4175)
      06-13 21:45:19.765: E/FSP_INTS-MAPS_AG(11176): 	at org.apache.cordova.NativeToJsMessageQueue$OnlineEventsBridgeMode.onNativeToJsMessageAvailable(NativeToJsMessageQueue.java:313)
      06-13 21:45:19.765: E/FSP_INTS-MAPS_AG(11176): 	at org.apache.cordova.NativeToJsMessageQueue.enqueueMessage(NativeToJsMessageQueue.java:253)
      06-13 21:45:19.765: E/FSP_INTS-MAPS_AG(11176): 	at org.apache.cordova.NativeToJsMessageQueue.addPluginResult(NativeToJsMessageQueue.java:246)
      06-13 21:45:19.765: E/FSP_INTS-MAPS_AG(11176): 	at org.apache.cordova.CordovaWebView.sendPluginResult(CordovaWebView.java:572)
      06-13 21:45:19.765: E/FSP_INTS-MAPS_AG(11176): 	at org.apache.cordova.CallbackContext.sendPluginResult(CallbackContext.java:64)
      06-13 21:45:19.765: E/FSP_INTS-MAPS_AG(11176): 	at org.apache.cordova.dialogs.Notification$1$1.onClick(Notification.java:150)
      06-13 21:45:19.765: E/FSP_INTS-MAPS_AG(11176): 	at com.android.internal.app.AlertController$ButtonHandler.handleMessage(AlertController.java:167)
      

      Problem

      Obviously CordovaWebView is already not available but dialog still exists. If a user press button in dialog, onClick() gets called and consequently CordovaWebView.setNetworkAvailable() gets called to send message from queue to JavaScript even though WebView is already destroyed. As a result, entire application crashes.

      Workaround

      We made a method to check if WebView is destroyed or not.

          private boolean isWebViewDestroyed() {
          	final String url = webView.getUrl();
          	if (url == null ||
          		url.equals("about:blank")) {
          		return true;
          	} else {
              	return false;
          	}
          }
      

      And check this before callbackContext.sendPluginResult() is called.

          public synchronized void alert(final String message, final String title, final String buttonLabel, final CallbackContext callbackContext) {
      
              final CordovaInterface cordova = this.cordova;
      
              Runnable runnable = new Runnable() {
                  public void run() {
      
                      AlertDialog.Builder dlg = new AlertDialog.Builder(cordova.getActivity());
                      dlg.setMessage(message);
                      dlg.setTitle(title);
                      dlg.setCancelable(true);
                      dlg.setPositiveButton(buttonLabel,
                              new AlertDialog.OnClickListener() {
                                  public void onClick(DialogInterface dialog, int which) {
                                      dialog.dismiss();
                                      if (!isWebViewDestroyed()) {
                                          callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 0));
                                      }
                                  }
                              });
                      dlg.setOnCancelListener(new AlertDialog.OnCancelListener() {
                          public void onCancel(DialogInterface dialog)
                          {
                              dialog.dismiss();
                              if (!isWebViewDestroyed()) {
                                  callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, 0));
                              }
                          }
                      });
      
                      dlg.create();
                      dlg.show();
                  };
              };
              this.cordova.getActivity().runOnUiThread(runnable);
          }
      

      Similar changes are also applied to confirm() and prompt(). This workaround works fine on my application.

      Question

      I want to know if this workaround is correct.

      • Do you know how to check if WebView is destroyed or not? I end up choosing getURL() as a result of experimental test. I appreciate any official or popular way.
      • isWebViewDestroyed() could be before webView.sendPluginResult(pluginResult, callbackId) in CallbackContext.sendPluginResult()? I think this way would prevent any APIs from this problem. But I'm afraid if this idea may impacts on other plugin API calls.

        Attachments

          Activity

            People

            • Assignee:
              bowserj Joe Bowser
              Reporter:
              shingot Shingo Toda
            • Votes:
              1 Vote for this issue
              Watchers:
              7 Start watching this issue

              Dates

              • Created:
                Updated: