CB-11292 fix broken MessageChannel after plugins are recreated

This closes #307
This commit is contained in:
Tony Homer 2016-05-20 16:13:46 -04:00 committed by Joe Bowser
parent 9738079c42
commit ecb99c963d
2 changed files with 111 additions and 0 deletions

View File

@ -135,6 +135,7 @@ public class CordovaWebViewImpl implements CordovaWebView {
if (recreatePlugins) {
// Don't re-initialize on first load.
if (loadedUrl != null) {
appPlugin = null;
pluginManager.init();
}
loadedUrl = url;

View File

@ -0,0 +1,110 @@
package org.apache.cordova.test;
/*
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
import android.view.KeyEvent;
import android.view.inputmethod.BaseInputConnection;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.CordovaWebViewImpl;
import org.apache.cordova.PluginManager;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class MessageChannelMultiPageTest extends BaseCordovaIntegrationTest {
private static final String START_URL = "file:///android_asset/www/backbuttonmultipage/index.html";
@Override
public void setUp() throws Exception {
super.setUp();
setUpWithStartUrl(START_URL);
}
//test that after a page load the cached callback id and the live callback id match
//this is to prevent a regression
//the issue was that CordovaWebViewImpl's cached instance of CoreAndroid would become stale on page load
//this is because the cached instance was not being cleared when the pluginManager was reset on page load
//the plugin manager would get a new instance which would be updated with a new callback id
//the cached instance's message channel callback id would become stale
//effectively this caused message channel events to not be delivered
public void testThatCachedCallbackIdIsValid() throws Throwable {
Class cordovaWebViewImpl = CordovaWebViewImpl.class;
//send a test event - this initializes cordovaWebViewImpl.appPlugin (the cached instance of CoreAndroid)
Method method = cordovaWebViewImpl.getDeclaredMethod("sendJavascriptEvent", String.class);
method.setAccessible(true);
method.invoke(cordovaWebView, "testEvent");
sleep(1000);
//load a page - this resets the plugin manager and nulls cordovaWebViewImpl.appPlugin
//(previously this resets plugin manager but did not null cordovaWebViewImpl.appPlugin, leading to the issue)
runTestOnUiThread(new Runnable() {
public void run() {
cordovaWebView.loadUrl(START_URL);
}
});
assertEquals(START_URL, testActivity.onPageFinishedUrl.take());
//send a test event - this initializes cordovaWebViewImpl.appPlugin (the cached instance of CoreAndroid)
method.invoke(cordovaWebView, "testEvent");
sleep(1000);
//get reference to package protected class CoreAndroid
Class coreAndroid = Class.forName("org.apache.cordova.CoreAndroid");
//get cached CoreAndroid
Field appPluginField = cordovaWebViewImpl.getDeclaredField("appPlugin");
appPluginField.setAccessible(true);
Object cachedAppPlugin = appPluginField.get(cordovaWebView);
//get cached CallbackContext
Field messageChannelField = coreAndroid.getDeclaredField("messageChannel");
messageChannelField.setAccessible(true);
CallbackContext cachedCallbackContext = (CallbackContext) messageChannelField.get(cachedAppPlugin);
//get live CoreAndroid
PluginManager pluginManager = MessageChannelMultiPageTest.this.cordovaWebView.getPluginManager();
Field coreAndroidPluginNameField = coreAndroid.getField("PLUGIN_NAME");
String coreAndroidPluginName = (String) coreAndroidPluginNameField.get(null);
Object liveAppPlugin = pluginManager.getPlugin(coreAndroidPluginName);
//get live CallbackContext
CallbackContext liveCallbackContext = (CallbackContext) messageChannelField.get(liveAppPlugin);
//get callback id from live callbackcontext
String liveCallbackId = (liveCallbackContext != null) ? liveCallbackContext.getCallbackId() : null;
//get callback id from cached callbackcontext
String cachedCallbackId = (cachedCallbackContext != null) ? cachedCallbackContext.getCallbackId() : null;
//verify that the live message channel has been initialized
assertNotNull(liveCallbackId);
//verify that the cached message channel and the live message channel have the same id
assertEquals(liveCallbackId, cachedCallbackId);
}
private void sleep(int timeout) {
try {
Thread.sleep(timeout);
} catch (InterruptedException e) {
fail("Unexpected Timeout");
}
}
}