Skip to content

Commit 03e636f

Browse files
committed
Don't alter accessibility JS APIs unless a page is about to load.
Previously we were adding and removing the APIs on window attach and detach, but a page's life cycle is not related to attach/detach. This patch also ensures that ChromeVox is not null before calling. Includes formatting fixes and extra comments in the waitForResultTimedLocked method to improve readability. Fixes parsing of integer resultId, which was being parsed using Long. Bug: 7328348 Change-Id: I6b81a8e4d8209f8e99419da9b8250abe57e25048
1 parent f881448 commit 03e636f

File tree

2 files changed

+96
-45
lines changed

2 files changed

+96
-45
lines changed

core/java/android/webkit/AccessibilityInjector.java

Lines changed: 90 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,12 @@ class AccessibilityInjector {
9797
// Template for JavaScript that performs AndroidVox actions.
9898
private static final String ACCESSIBILITY_ANDROIDVOX_TEMPLATE =
9999
"(function() {" +
100-
" if ((typeof(cvox) != 'undefined')"+
100+
" if ((typeof(cvox) != 'undefined')" +
101+
" && (cvox != null)" +
101102
" && (typeof(cvox.ChromeVox) != 'undefined')" +
103+
" && (cvox.ChromeVox != null)" +
102104
" && (typeof(cvox.AndroidVox) != 'undefined')" +
105+
" && (cvox.AndroidVox != null)" +
103106
" && cvox.ChromeVox.isActive) {" +
104107
" return cvox.AndroidVox.performAction('%1s');" +
105108
" } else {" +
@@ -110,9 +113,12 @@ class AccessibilityInjector {
110113
// JS code used to shut down an active AndroidVox instance.
111114
private static final String TOGGLE_CVOX_TEMPLATE =
112115
"javascript:(function() {" +
113-
" if ((typeof(cvox) != 'undefined')"+
116+
" if ((typeof(cvox) != 'undefined')" +
117+
" && (cvox != null)" +
114118
" && (typeof(cvox.ChromeVox) != 'undefined')" +
115-
" && (typeof(cvox.ChromeVox.host) != 'undefined')) {" +
119+
" && (cvox.ChromeVox != null)" +
120+
" && (typeof(cvox.ChromeVox.host) != 'undefined')" +
121+
" && (cvox.ChromeVox.host != null)) {" +
116122
" cvox.ChromeVox.host.activateOrDeactivateChromeVox(%b);" +
117123
" }" +
118124
"})();";
@@ -131,34 +137,61 @@ public AccessibilityInjector(WebViewClassic webViewClassic) {
131137
mAccessibilityManager = AccessibilityManager.getInstance(mContext);
132138
}
133139

140+
/**
141+
* If JavaScript is enabled, pauses or resumes AndroidVox.
142+
*
143+
* @param enabled Whether feedback should be enabled.
144+
*/
145+
public void toggleAccessibilityFeedback(boolean enabled) {
146+
if (!isAccessibilityEnabled() || !isJavaScriptEnabled()) {
147+
return;
148+
}
149+
150+
toggleAndroidVox(enabled);
151+
152+
if (!enabled && (mTextToSpeech != null)) {
153+
mTextToSpeech.stop();
154+
}
155+
}
156+
134157
/**
135158
* Attempts to load scripting interfaces for accessibility.
136159
* <p>
137-
* This should be called when the window is attached.
138-
* </p>
160+
* This should only be called before a page loads.
139161
*/
140-
public void addAccessibilityApisIfNecessary() {
162+
private void addAccessibilityApisIfNecessary() {
141163
if (!isAccessibilityEnabled() || !isJavaScriptEnabled()) {
142164
return;
143165
}
144166

145167
addTtsApis();
146168
addCallbackApis();
147-
toggleAndroidVox(true);
148169
}
149170

150171
/**
151172
* Attempts to unload scripting interfaces for accessibility.
152173
* <p>
153-
* This should be called when the window is detached.
154-
* </p>
174+
* This should only be called before a page loads.
155175
*/
156-
public void removeAccessibilityApisIfNecessary() {
157-
toggleAndroidVox(false);
176+
private void removeAccessibilityApisIfNecessary() {
158177
removeTtsApis();
159178
removeCallbackApis();
160179
}
161180

181+
/**
182+
* Destroys this accessibility injector.
183+
*/
184+
public void destroy() {
185+
if (mTextToSpeech != null) {
186+
mTextToSpeech.shutdown();
187+
mTextToSpeech = null;
188+
}
189+
190+
if (mCallback != null) {
191+
mCallback = null;
192+
}
193+
}
194+
162195
private void toggleAndroidVox(boolean state) {
163196
if (!mAccessibilityScriptInjected) {
164197
return;
@@ -517,7 +550,12 @@ private String getScreenReaderInjectionUrl() {
517550
* settings.
518551
*/
519552
private boolean isJavaScriptEnabled() {
520-
return mWebView.getSettings().getJavaScriptEnabled();
553+
final WebSettings settings = mWebView.getSettings();
554+
if (settings == null) {
555+
return false;
556+
}
557+
558+
return settings.getJavaScriptEnabled();
521559
}
522560

523561
/**
@@ -732,7 +770,7 @@ private static class CallbackHandler {
732770
private final String mInterfaceName;
733771

734772
private boolean mResult = false;
735-
private long mResultId = -1;
773+
private int mResultId = -1;
736774

737775
private CallbackHandler(String interfaceName) {
738776
mInterfaceName = interfaceName;
@@ -784,34 +822,46 @@ private void clearResultLocked() {
784822
* @return Whether the result was received.
785823
*/
786824
private boolean waitForResultTimedLocked(int resultId) {
787-
if (DEBUG)
788-
Log.d(TAG, "Waiting for CVOX result...");
789-
long waitTimeMillis = RESULT_TIMEOUT;
790825
final long startTimeMillis = SystemClock.uptimeMillis();
826+
827+
if (DEBUG)
828+
Log.d(TAG, "Waiting for CVOX result with ID " + resultId + "...");
829+
791830
while (true) {
831+
// Fail if we received a callback from the future.
832+
if (mResultId > resultId) {
833+
if (DEBUG)
834+
Log.w(TAG, "Aborted CVOX result");
835+
return false;
836+
}
837+
838+
final long elapsedTimeMillis = (SystemClock.uptimeMillis() - startTimeMillis);
839+
840+
// Succeed if we received the callback we were expecting.
841+
if (DEBUG)
842+
Log.w(TAG, "Check " + mResultId + " versus expected " + resultId);
843+
if (mResultId == resultId) {
844+
if (DEBUG)
845+
Log.w(TAG, "Received CVOX result after " + elapsedTimeMillis + " ms");
846+
return true;
847+
}
848+
849+
final long waitTimeMillis = (RESULT_TIMEOUT - elapsedTimeMillis);
850+
851+
// Fail if we've already exceeded the timeout.
852+
if (waitTimeMillis <= 0) {
853+
if (DEBUG)
854+
Log.w(TAG, "Timed out while waiting for CVOX result");
855+
return false;
856+
}
857+
792858
try {
793-
if (mResultId == resultId) {
794-
if (DEBUG)
795-
Log.w(TAG, "Received CVOX result");
796-
return true;
797-
}
798-
if (mResultId > resultId) {
799-
if (DEBUG)
800-
Log.w(TAG, "Obsolete CVOX result");
801-
return false;
802-
}
803-
final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis;
804-
waitTimeMillis = RESULT_TIMEOUT - elapsedTimeMillis;
805-
if (waitTimeMillis <= 0) {
806-
if (DEBUG)
807-
Log.w(TAG, "Timed out while waiting for CVOX result");
808-
return false;
809-
}
859+
if (DEBUG)
860+
Log.w(TAG, "Start waiting...");
810861
mResultLock.wait(waitTimeMillis);
811862
} catch (InterruptedException ie) {
812863
if (DEBUG)
813864
Log.w(TAG, "Interrupted while waiting for CVOX result");
814-
/* ignore */
815865
}
816866
}
817867
}
@@ -827,11 +877,11 @@ private boolean waitForResultTimedLocked(int resultId) {
827877
@SuppressWarnings("unused")
828878
public void onResult(String id, String result) {
829879
if (DEBUG)
830-
Log.w(TAG, "Saw CVOX result of '" + result + "'");
831-
final long resultId;
880+
Log.w(TAG, "Saw CVOX result of '" + result + "' for ID " + id);
881+
final int resultId;
832882

833883
try {
834-
resultId = Long.parseLong(id);
884+
resultId = Integer.parseInt(id);
835885
} catch (NumberFormatException e) {
836886
return;
837887
}
@@ -840,6 +890,9 @@ public void onResult(String id, String result) {
840890
if (resultId > mResultId) {
841891
mResult = Boolean.parseBoolean(result);
842892
mResultId = resultId;
893+
} else {
894+
if (DEBUG)
895+
Log.w(TAG, "Result with ID " + resultId + " was stale vesus " + mResultId);
843896
}
844897
mResultLock.notifyAll();
845898
}

core/java/android/webkit/WebViewClassic.java

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2132,6 +2132,10 @@ private void ensureFunctorDetached() {
21322132

21332133
private void destroyJava() {
21342134
mCallbackProxy.blockMessages();
2135+
if (mAccessibilityInjector != null) {
2136+
mAccessibilityInjector.destroy();
2137+
mAccessibilityInjector = null;
2138+
}
21352139
if (mWebViewCore != null) {
21362140
// Tell WebViewCore to destroy itself
21372141
synchronized (this) {
@@ -3967,8 +3971,6 @@ private boolean setContentScrollBy(int cx, int cy, boolean animate) {
39673971
// null, and that will be the case
39683972
mWebView.setCertificate(null);
39693973

3970-
// reset the flag since we set to true in if need after
3971-
// loading is see onPageFinished(Url)
39723974
if (isAccessibilityInjectionEnabled()) {
39733975
getAccessibilityInjector().onPageStarted(url);
39743976
}
@@ -5384,7 +5386,7 @@ public void onAttachedToWindow() {
53845386
if (mWebView.hasWindowFocus()) setActive(true);
53855387

53865388
if (isAccessibilityInjectionEnabled()) {
5387-
getAccessibilityInjector().addAccessibilityApisIfNecessary();
5389+
getAccessibilityInjector().toggleAccessibilityFeedback(true);
53885390
}
53895391

53905392
updateHwAccelerated();
@@ -5397,11 +5399,7 @@ public void onDetachedFromWindow() {
53975399
if (mWebView.hasWindowFocus()) setActive(false);
53985400

53995401
if (isAccessibilityInjectionEnabled()) {
5400-
getAccessibilityInjector().removeAccessibilityApisIfNecessary();
5401-
} else {
5402-
// Ensure the injector is cleared if we're detaching from the window
5403-
// and accessibility is disabled.
5404-
mAccessibilityInjector = null;
5402+
getAccessibilityInjector().toggleAccessibilityFeedback(false);
54055403
}
54065404

54075405
updateHwAccelerated();

0 commit comments

Comments
 (0)