Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d36bde24 authored by Ben Murdoch's avatar Ben Murdoch Committed by Android Git Automerger
Browse files

am 93e3bf12: Merge "Fix up the WebCoreThreadWatchdog" into jb-dev

* commit '93e3bf12':
  Fix up the WebCoreThreadWatchdog
parents 125a45ed 93e3bf12
Loading
Loading
Loading
Loading
+111 −42
Original line number Diff line number Diff line
@@ -26,6 +26,10 @@ import android.os.Message;
import android.os.Process;
import android.webkit.WebViewCore.EventHub;

import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

// A Runnable that will monitor if the WebCore thread is still
// processing messages by pinging it every so often. It is safe
// to call the public methods of this class from any thread.
@@ -51,25 +55,31 @@ class WebCoreThreadWatchdog implements Runnable {
    // After the first timeout, use a shorter period before re-prompting the user.
    private static final int SUBSEQUENT_TIMEOUT_PERIOD = 15 * 1000;

    private Context mContext;
    private Handler mWebCoreThreadHandler;
    private Handler mHandler;
    private boolean mPaused;

    private Set<WebViewClassic> mWebViews;

    private static WebCoreThreadWatchdog sInstance;

    public synchronized static WebCoreThreadWatchdog start(Context context,
            Handler webCoreThreadHandler) {
    public synchronized static WebCoreThreadWatchdog start(Handler webCoreThreadHandler) {
        if (sInstance == null) {
            sInstance = new WebCoreThreadWatchdog(context, webCoreThreadHandler);
            sInstance = new WebCoreThreadWatchdog(webCoreThreadHandler);
            new Thread(sInstance, "WebCoreThreadWatchdog").start();
        }
        return sInstance;
    }

    public synchronized static void updateContext(Context context) {
    public synchronized static void registerWebView(WebViewClassic w) {
        if (sInstance != null) {
            sInstance.addWebView(w);
        }
    }

    public synchronized static void unregisterWebView(WebViewClassic w) {
        if (sInstance != null) {
            sInstance.setContext(context);
            sInstance.removeWebView(w);
        }
    }

@@ -85,12 +95,18 @@ class WebCoreThreadWatchdog implements Runnable {
        }
    }

    private void setContext(Context context) {
        mContext = context;
    private void addWebView(WebViewClassic w) {
        if (mWebViews == null) {
            mWebViews = new HashSet<WebViewClassic>();
        }
        mWebViews.add(w);
    }

    private WebCoreThreadWatchdog(Context context, Handler webCoreThreadHandler) {
        mContext = context;
    private void removeWebView(WebViewClassic w) {
        mWebViews.remove(w);
    }

    private WebCoreThreadWatchdog(Handler webCoreThreadHandler) {
        mWebCoreThreadHandler = webCoreThreadHandler;
    }

@@ -147,7 +163,80 @@ class WebCoreThreadWatchdog implements Runnable {
                        break;

                    case TIMED_OUT:
                        if ((mContext == null) || !(mContext instanceof Activity)) return;
                        boolean postedDialog = false;
                        synchronized (WebCoreThreadWatchdog.class) {
                            Iterator<WebViewClassic> it = mWebViews.iterator();
                            // Check each WebView we are aware of and find one that is capable of
                            // showing the user a prompt dialog.
                            while (it.hasNext()) {
                                WebView activeView = it.next().getWebView();

                                if (activeView.getWindowToken() != null &&
                                        activeView.getViewRootImpl() != null) {
                                    postedDialog = activeView.post(new PageNotRespondingRunnable(
                                            activeView.getContext(), this));

                                    if (postedDialog) {
                                        // We placed the message into the UI thread for an attached
                                        // WebView so we've made our best attempt to display the
                                        // "page not responding" dialog to the user. Although the
                                        // message is in the queue, there is no guarantee when/if
                                        // the runnable will execute. In the case that the runnable
                                        // never executes, the user will need to terminate the
                                        // process manually.
                                        break;
                                    }
                                }
                            }

                            if (!postedDialog) {
                                // There's no active webview we can use to show the dialog, so
                                // wait again. If we never get a usable view, the user will
                                // never get the chance to terminate the process, and will
                                // need to do it manually.
                                sendMessageDelayed(obtainMessage(TIMED_OUT),
                                        SUBSEQUENT_TIMEOUT_PERIOD);
                            }
                        }
                        break;
                    }
                }
            };
        }
    }

    @Override
    public void run() {
        Looper.prepare();

        createHandler();

        // Send the initial control to WebViewCore and start the timeout timer as long as we aren't
        // paused.
        synchronized (WebCoreThreadWatchdog.class) {
            if (!mPaused) {
                mWebCoreThreadHandler.obtainMessage(EventHub.HEARTBEAT,
                        mHandler.obtainMessage(IS_ALIVE)).sendToTarget();
                mHandler.sendMessageDelayed(mHandler.obtainMessage(TIMED_OUT), TIMEOUT_PERIOD);
            }
        }

        Looper.loop();
    }

    private class PageNotRespondingRunnable implements Runnable {
        Context mContext;
        private Handler mWatchdogHandler;

        public PageNotRespondingRunnable(Context context, Handler watchdogHandler) {
            mContext = context;
            mWatchdogHandler = watchdogHandler;
        }

        @Override
        public void run() {
            // This must run on the UI thread as it is displaying an AlertDialog.
            assert Looper.getMainLooper().getThread() == Thread.currentThread();
            new AlertDialog.Builder(mContext)
                    .setMessage(com.android.internal.R.string.webpage_unresponsive)
                    .setPositiveButton(com.android.internal.R.string.force_close,
@@ -167,42 +256,22 @@ class WebCoreThreadWatchdog implements Runnable {
                                    // we need to do is post another TIMED_OUT so that the
                                    // user will get prompted again if the WebCore thread
                                    // doesn't sort itself out.
                                            sendMessageDelayed(obtainMessage(TIMED_OUT),
                                    mWatchdogHandler.sendMessageDelayed(
                                            mWatchdogHandler.obtainMessage(TIMED_OUT),
                                            SUBSEQUENT_TIMEOUT_PERIOD);
                                }
                            })
                            .setOnCancelListener(new DialogInterface.OnCancelListener() {
                    .setOnCancelListener(
                            new DialogInterface.OnCancelListener() {
                                @Override
                                public void onCancel(DialogInterface dialog) {
                                        sendMessageDelayed(obtainMessage(TIMED_OUT),
                                    mWatchdogHandler.sendMessageDelayed(
                                            mWatchdogHandler.obtainMessage(TIMED_OUT),
                                            SUBSEQUENT_TIMEOUT_PERIOD);
                                }
                            })
                    .setIcon(android.R.drawable.ic_dialog_alert)
                    .show();
                        break;
                    }
                }
            };
        }
    }

    @Override
    public void run() {
        Looper.prepare();

        createHandler();

        // Send the initial control to WebViewCore and start the timeout timer as long as we aren't
        // paused.
        synchronized (WebCoreThreadWatchdog.class) {
            if (!mPaused) {
                mWebCoreThreadHandler.obtainMessage(EventHub.HEARTBEAT,
                        mHandler.obtainMessage(IS_ALIVE)).sendToTarget();
                mHandler.sendMessageDelayed(mHandler.obtainMessage(TIMED_OUT), TIMEOUT_PERIOD);
            }
        }

        Looper.loop();
    }
}
+0 −5
Original line number Diff line number Diff line
@@ -3365,11 +3365,6 @@ public final class WebViewClassic implements WebViewProvider, WebViewProvider.Sc
                nativeSetPauseDrawing(mNativeClass, false);
            }
        }
        // Ensure that the watchdog has a currently valid Context to be able to display
        // a prompt dialog. For example, if the Activity was finished whilst the WebCore
        // thread was blocked and the Activity is started again, we may reuse the blocked
        // thread, but we'll have a new Activity.
        WebCoreThreadWatchdog.updateContext(mContext);
        // We get a call to onResume for new WebViews (i.e. mIsPaused will be false). We need
        // to ensure that the Watchdog thread is running for the new WebView, so call
        // it outside the if block above.
+4 −1
Original line number Diff line number Diff line
@@ -178,8 +178,10 @@ public final class WebViewCore {

                // Start the singleton watchdog which will monitor the WebCore thread
                // to verify it's still processing messages.
                WebCoreThreadWatchdog.start(context, sWebCoreHandler);
                WebCoreThreadWatchdog.start(sWebCoreHandler);
            }
            // Make sure the Watchdog is aware of this new WebView.
            WebCoreThreadWatchdog.registerWebView(w);
        }
        // Create an EventHub to handle messages before and after the thread is
        // ready.
@@ -1979,6 +1981,7 @@ public final class WebViewCore {
            mEventHub.sendMessageAtFrontOfQueue(
                    Message.obtain(null, EventHub.DESTROY));
            mEventHub.blockMessages();
            WebCoreThreadWatchdog.unregisterWebView(mWebViewClassic);
        }
    }