Loading core/java/android/webkit/WebCoreThreadWatchdog.java +111 −42 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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); } } Loading @@ -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; } Loading Loading @@ -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, Loading @@ -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(); } } core/java/android/webkit/WebViewClassic.java +0 −5 Original line number Diff line number Diff line Loading @@ -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. Loading core/java/android/webkit/WebViewCore.java +4 −1 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -1979,6 +1981,7 @@ public final class WebViewCore { mEventHub.sendMessageAtFrontOfQueue( Message.obtain(null, EventHub.DESTROY)); mEventHub.blockMessages(); WebCoreThreadWatchdog.unregisterWebView(mWebViewClassic); } } Loading Loading
core/java/android/webkit/WebCoreThreadWatchdog.java +111 −42 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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); } } Loading @@ -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; } Loading Loading @@ -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, Loading @@ -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(); } }
core/java/android/webkit/WebViewClassic.java +0 −5 Original line number Diff line number Diff line Loading @@ -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. Loading
core/java/android/webkit/WebViewCore.java +4 −1 Original line number Diff line number Diff line Loading @@ -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. Loading Loading @@ -1979,6 +1981,7 @@ public final class WebViewCore { mEventHub.sendMessageAtFrontOfQueue( Message.obtain(null, EventHub.DESTROY)); mEventHub.blockMessages(); WebCoreThreadWatchdog.unregisterWebView(mWebViewClassic); } } Loading