diff --git a/framework/src/org/apache/cordova/AndroidChromeClient.java b/framework/src/org/apache/cordova/AndroidChromeClient.java index a683ec69..e4ebe968 100755 --- a/framework/src/org/apache/cordova/AndroidChromeClient.java +++ b/framework/src/org/apache/cordova/AndroidChromeClient.java @@ -20,8 +20,6 @@ package org.apache.cordova; import org.apache.cordova.CordovaInterface; import org.apache.cordova.LOG; -import org.json.JSONArray; -import org.json.JSONException; import android.annotation.TargetApi; import android.app.AlertDialog; @@ -44,7 +42,6 @@ import android.widget.EditText; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.RelativeLayout; -import android.util.Log; /** * This class is the WebChromeClient that implements callbacks for our web view. @@ -57,7 +54,7 @@ import android.util.Log; * @see CordovaWebViewClient * @see CordovaWebView */ -public class AndroidChromeClient extends WebChromeClient implements CordovaChromeClient { +public class AndroidChromeClient extends WebChromeClient { public static final int FILECHOOSER_RESULTCODE = 5173; private static final String LOG_TAG = "AndroidChromeClient"; @@ -69,7 +66,7 @@ public class AndroidChromeClient extends WebChromeClient implements CordovaChrom private View mVideoProgressView; // File Chooser - public ValueCallback mUploadMessage; + protected ValueCallback mUploadMessage; public AndroidChromeClient(CordovaInterface ctx, AndroidWebView app) { this.cordova = ctx; @@ -182,67 +179,9 @@ public class AndroidChromeClient extends WebChromeClient implements CordovaChrom @Override public boolean onJsPrompt(WebView view, String origin, String message, String defaultValue, JsPromptResult result) { // Unlike the @JavascriptInterface bridge, this method is always called on the UI thread. - if (defaultValue != null && defaultValue.length() > 3 && defaultValue.startsWith("gap:")) { - JSONArray array; - try { - array = new JSONArray(defaultValue.substring(4)); - int bridgeSecret = array.getInt(0); - String service = array.getString(1); - String action = array.getString(2); - String callbackId = array.getString(3); - String r = appView.exposedJsApi.exec(bridgeSecret, service, action, callbackId, message); - result.confirm(r == null ? "" : r); - } catch (JSONException e) { - e.printStackTrace(); - result.cancel(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - result.cancel(); - } - } - - // Sets the native->JS bridge mode. - else if (defaultValue != null && defaultValue.startsWith("gap_bridge_mode:")) { - try { - int bridgeSecret = Integer.parseInt(defaultValue.substring(16)); - appView.exposedJsApi.setNativeToJsBridgeMode(bridgeSecret, Integer.parseInt(message)); - result.cancel(); - } catch (NumberFormatException e){ - e.printStackTrace(); - result.cancel(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - result.cancel(); - } - } - - // Polling for JavaScript messages - else if (defaultValue != null && defaultValue.startsWith("gap_poll:")) { - int bridgeSecret = Integer.parseInt(defaultValue.substring(9)); - try { - String r = appView.exposedJsApi.retrieveJsMessages(bridgeSecret, "1".equals(message)); - result.confirm(r == null ? "" : r); - } catch (IllegalAccessException e) { - e.printStackTrace(); - result.cancel(); - } - } - - else if (defaultValue != null && defaultValue.startsWith("gap_init:")) { - // Protect against random iframes being able to talk through the bridge. - // Trust only file URLs and the start URL's domain. - // The extra origin.startsWith("http") is to protect against iframes with data: having "" as origin. - if (origin.startsWith("file:") || (origin.startsWith("http") && appView.loadedUrl.startsWith(origin))) { - // Enable the bridge - int bridgeMode = Integer.parseInt(defaultValue.substring(9)); - appView.jsMessageQueue.setBridgeMode(bridgeMode); - // Tell JS the bridge secret. - int secret = appView.exposedJsApi.generateBridgeSecret(); - result.confirm(""+secret); - } else { - Log.e(LOG_TAG, "gap_init called from restricted origin: " + origin); - result.cancel(); - } + String handledRet = appView.bridge.promptOnJsPrompt(origin, message, defaultValue); + if (handledRet != null) { + result.confirm(handledRet); } else { // Returning false would also show a dialog, but the default one shows the origin (ugly). final JsPromptResult res = result; @@ -375,8 +314,4 @@ public class AndroidChromeClient extends WebChromeClient implements CordovaChrom this.cordova.getActivity().startActivityForResult(Intent.createChooser(i, "File Browser"), FILECHOOSER_RESULTCODE); } - - public ValueCallback getValueCallback() { - return this.mUploadMessage; - } } diff --git a/framework/src/org/apache/cordova/AndroidExposedJsApi.java b/framework/src/org/apache/cordova/AndroidExposedJsApi.java index 5f53774e..5ed4d409 100755 --- a/framework/src/org/apache/cordova/AndroidExposedJsApi.java +++ b/framework/src/org/apache/cordova/AndroidExposedJsApi.java @@ -19,7 +19,6 @@ package org.apache.cordova; import android.webkit.JavascriptInterface; -import org.apache.cordova.PluginManager; import org.json.JSONException; /** @@ -28,70 +27,24 @@ import org.json.JSONException; * cordova-js/lib/android/plugin/android/promptbasednativeapi.js */ class AndroidExposedJsApi implements ExposedJsApi { + private final CordovaBridge bridge; - private PluginManager pluginManager; - private NativeToJsMessageQueue jsMessageQueue; - private volatile int bridgeSecret = -1; // written by UI thread, read by JS thread. - - public AndroidExposedJsApi(PluginManager pluginManager, NativeToJsMessageQueue jsMessageQueue) { - this.pluginManager = pluginManager; - this.jsMessageQueue = jsMessageQueue; + AndroidExposedJsApi(CordovaBridge bridge) { + this.bridge = bridge; } @JavascriptInterface public String exec(int bridgeSecret, String service, String action, String callbackId, String arguments) throws JSONException, IllegalAccessException { - verifySecret(bridgeSecret); - // If the arguments weren't received, send a message back to JS. It will switch bridge modes and try again. See CB-2666. - // We send a message meant specifically for this case. It starts with "@" so no other message can be encoded into the same string. - if (arguments == null) { - return "@Null arguments."; - } - - jsMessageQueue.setPaused(true); - try { - // Tell the resourceApi what thread the JS is running on. - CordovaResourceApi.jsThread = Thread.currentThread(); - - pluginManager.exec(service, action, callbackId, arguments); - String ret = ""; - if (!NativeToJsMessageQueue.DISABLE_EXEC_CHAINING) { - ret = jsMessageQueue.popAndEncode(false); - } - return ret; - } catch (Throwable e) { - e.printStackTrace(); - return ""; - } finally { - jsMessageQueue.setPaused(false); - } + return bridge.jsExec(bridgeSecret, service, action, callbackId, arguments); } @JavascriptInterface public void setNativeToJsBridgeMode(int bridgeSecret, int value) throws IllegalAccessException { - verifySecret(bridgeSecret); - jsMessageQueue.setBridgeMode(value); + bridge.jsSetNativeToJsBridgeMode(bridgeSecret, value); } @JavascriptInterface public String retrieveJsMessages(int bridgeSecret, boolean fromOnlineEvent) throws IllegalAccessException { - verifySecret(bridgeSecret); - return jsMessageQueue.popAndEncode(fromOnlineEvent); - } - - private void verifySecret(int value) throws IllegalAccessException { - if (bridgeSecret < 0 || value != bridgeSecret) { - throw new IllegalAccessException(); - } - } - - /** Called on page transitions */ - void clearBridgeSecret() { - bridgeSecret = -1; - } - - /** Called by cordova.js to initialize the bridge. */ - int generateBridgeSecret() { - bridgeSecret = (int)(Math.random() * Integer.MAX_VALUE); - return bridgeSecret; + return bridge.jsRetrieveJsMessages(bridgeSecret, fromOnlineEvent); } } diff --git a/framework/src/org/apache/cordova/AndroidWebView.java b/framework/src/org/apache/cordova/AndroidWebView.java index 8499caf8..135920cd 100755 --- a/framework/src/org/apache/cordova/AndroidWebView.java +++ b/framework/src/org/apache/cordova/AndroidWebView.java @@ -24,7 +24,6 @@ import java.lang.reflect.Method; import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Locale; import android.annotation.SuppressLint; import android.annotation.TargetApi; @@ -66,23 +65,21 @@ public class AndroidWebView extends WebView implements CordovaWebView { private HashSet boundKeyCodes = new HashSet(); PluginManager pluginManager; - private boolean paused; private BroadcastReceiver receiver; /** Activities and other important classes **/ private CordovaInterface cordova; - CordovaWebViewClient viewClient; - private CordovaChromeClient chromeClient; + AndroidWebViewClient viewClient; + private AndroidChromeClient chromeClient; // Flag to track that a loadUrl timeout occurred int loadUrlTimeout = 0; private long lastMenuEventTime = 0; - NativeToJsMessageQueue jsMessageQueue; - AndroidExposedJsApi exposedJsApi; + CordovaBridge bridge; /** custom view created by the browser (a video player for example) */ private View mCustomView; @@ -94,21 +91,6 @@ public class AndroidWebView extends WebView implements CordovaWebView { // The URL passed to loadUrl(), not necessarily the URL of the current page. String loadedUrl; - class ActivityResult { - - int request; - int result; - Intent incoming; - - public ActivityResult(int req, int res, Intent intent) { - request = req; - result = res; - incoming = intent; - } - - - } - static final FrameLayout.LayoutParams COVER_SCREEN_GRAVITY_CENTER = new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, @@ -127,24 +109,30 @@ public class AndroidWebView extends WebView implements CordovaWebView { // Use two-phase init so that the control will work with XML layouts. @Override - public void init(CordovaInterface cordova, CordovaWebViewClient webViewClient, CordovaChromeClient webChromeClient, - List pluginEntries, Whitelist whitelist, CordovaPreferences preferences) { + public void init(CordovaInterface cordova, List pluginEntries, + Whitelist whitelist, CordovaPreferences preferences) { if (this.cordova != null) { throw new IllegalStateException(); } this.cordova = cordova; - setWebChromeClient(webChromeClient); - setWebViewClient(webViewClient); this.whitelist = whitelist; this.preferences = preferences; pluginManager = new PluginManager(this, this.cordova, pluginEntries); - jsMessageQueue = new NativeToJsMessageQueue(this, cordova); - exposedJsApi = new AndroidExposedJsApi(pluginManager, jsMessageQueue); resourceApi = new CordovaResourceApi(this.getContext(), pluginManager); - + bridge = new CordovaBridge(pluginManager, new NativeToJsMessageQueue(this, cordova)); pluginManager.addService("App", "org.apache.cordova.CoreAndroid"); initWebViewSettings(); + + if (this.viewClient == null) { + setWebViewClient(Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH ? + new AndroidWebViewClient(cordova, this) : + new IceCreamCordovaWebViewClient(cordova, this)); + } + if (this.chromeClient == null) { + setWebChromeClient(new AndroidChromeClient(cordova, this)); + } + exposeJsInterface(); } @@ -253,19 +241,6 @@ public class AndroidWebView extends WebView implements CordovaWebView { } } - @Override - public CordovaChromeClient makeWebChromeClient(CordovaInterface cordova) { - return new AndroidChromeClient(cordova, this); - } - - @Override - public CordovaWebViewClient makeWebViewClient(CordovaInterface cordova) { - if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) { - return new AndroidWebViewClient(cordova, this); - } - return new IceCreamCordovaWebViewClient(cordova, this); - } - /** * Override this method to decide whether or not you need to request the * focus when your application start @@ -284,80 +259,42 @@ public class AndroidWebView extends WebView implements CordovaWebView { // use the prompt bridge instead. return; } + AndroidExposedJsApi exposedJsApi = new AndroidExposedJsApi(bridge); this.addJavascriptInterface(exposedJsApi, "_cordovaNative"); } @Override public void setWebViewClient(WebViewClient client) { - this.viewClient = (CordovaWebViewClient)client; + this.viewClient = (AndroidWebViewClient)client; super.setWebViewClient(client); } @Override public void setWebChromeClient(WebChromeClient client) { - this.chromeClient = (CordovaChromeClient)client; + this.chromeClient = (AndroidChromeClient)client; super.setWebChromeClient(client); } - @Override - public void setWebViewClient(CordovaWebViewClient client) { - setWebViewClient((WebViewClient) client); - } - - @Override - public void setWebChromeClient(CordovaChromeClient client) { - setWebChromeClient((WebChromeClient) client); - } - - public CordovaChromeClient getWebChromeClient() { - return this.chromeClient; - } - /** * Load the url into the webview. - * - * @param url */ @Override public void loadUrl(String url) { - if (url.equals("about:blank") || url.startsWith("javascript:")) { - this.loadUrlNow(url); - } - else { - this.loadUrlIntoView(url); - } - } - - /** - * Load the url into the webview after waiting for period of time. - * This is used to display the splashscreen for certain amount of time. - * - * @param url - * @param time The number of ms to wait before loading webview - */ - @Deprecated - public void loadUrl(final String url, int time) { - if(url == null) - { - this.loadUrlIntoView(Config.getStartUrl()); - } - else - { - this.loadUrlIntoView(url); - } - } - - public void loadUrlIntoView(final String url) { - loadUrlIntoView(url, true); + this.loadUrlIntoView(url, true); } /** * Load the url into the webview. - * - * @param url */ + @Override public void loadUrlIntoView(final String url, boolean recreatePlugins) { + if (url.equals("about:blank") || url.startsWith("javascript:")) { + this.loadUrlNow(url); + return; + } + LOG.d(TAG, ">>> loadUrl(" + url + ")"); + recreatePlugins = recreatePlugins || (loadedUrl == null); if (recreatePlugins) { this.loadedUrl = url; @@ -367,7 +304,7 @@ public class AndroidWebView extends WebView implements CordovaWebView { // Create a timeout timer for loadUrl final AndroidWebView me = this; final int currentLoadUrlTimeout = me.loadUrlTimeout; - final int loadUrlTimeoutValue = Integer.parseInt(this.getProperty("LoadUrlTimeoutValue", "20000")); + final int loadUrlTimeoutValue = preferences.getInteger("LoadUrlTimeoutValue", 20000); // Timeout error method final Runnable loadError = new Runnable() { @@ -375,7 +312,7 @@ public class AndroidWebView extends WebView implements CordovaWebView { me.stopLoading(); LOG.e(TAG, "CordovaWebView: TIMEOUT ERROR!"); if (viewClient != null) { - viewClient.onReceivedError(-6, "The connection to the server was unsuccessful.", url); + viewClient.onReceivedError(AndroidWebView.this, -6, "The connection to the server was unsuccessful.", url); } } }; @@ -409,10 +346,8 @@ public class AndroidWebView extends WebView implements CordovaWebView { /** * Load URL in webview. - * - * @param url */ - public void loadUrlNow(String url) { + private void loadUrlNow(String url) { if (LOG.isLoggable(LOG.DEBUG) && !url.startsWith("javascript:")) { LOG.d(TAG, ">>> loadUrlNow()"); } @@ -421,33 +356,6 @@ public class AndroidWebView extends WebView implements CordovaWebView { } } - /** - * Load the url into the webview after waiting for period of time. - * This is used to display the splashscreen for certain amount of time. - * - * @param url - * @param time The number of ms to wait before loading webview - */ - public void loadUrlIntoView(final String url, final int time) { - - // If not first page of app, then load immediately - // Add support for browser history if we use it. - if ((url.startsWith("javascript:")) || this.canGoBack()) { - } - - // If first page, then show splashscreen - else { - - LOG.d(TAG, "loadUrlIntoView(%s, %d)", url, time); - - // Send message to show splashscreen now if desired - this.postMessage("splashscreen", "show"); - } - - // Load url - this.loadUrlIntoView(url); - } - @Override public void stopLoading() { //viewClient.isCurrentlyLoading = false; @@ -459,43 +367,24 @@ public class AndroidWebView extends WebView implements CordovaWebView { super.onScrollChanged(l, t, oldl, oldt); //We should post a message that the scroll changed ScrollEvent myEvent = new ScrollEvent(l, t, oldl, oldt, this); - this.postMessage("onScrollChanged", myEvent); + pluginManager.postMessage("onScrollChanged", myEvent); } /** * Send JavaScript statement back to JavaScript. * (This is a convenience method) - * - * @param statement */ public void sendJavascript(String statement) { - this.jsMessageQueue.addJavaScript(statement); + bridge.getMessageQueue().addJavaScript(statement); } /** * Send a plugin result back to JavaScript. - * (This is a convenience method) - * - * @param result - * @param callbackId */ public void sendPluginResult(PluginResult result, String callbackId) { - this.jsMessageQueue.addPluginResult(result, callbackId); + bridge.getMessageQueue().addPluginResult(result, callbackId); } - /** - * Send a message to all plugins. - * - * @param id The message id - * @param data The message data - */ - public void postMessage(String id, Object data) { - if (this.pluginManager != null) { - this.pluginManager.postMessage(id, data); - } - } - - /** * Go to previous page in history. (We manage our own history) * @@ -540,7 +429,7 @@ public class AndroidWebView extends WebView implements CordovaWebView { if (url.startsWith("file://") || whitelist.isUrlWhiteListed(url)) { // TODO: What about params? // Load new URL - this.loadUrl(url); + loadUrlIntoView(url, true); return; } // Load in default viewer if not @@ -562,26 +451,6 @@ public class AndroidWebView extends WebView implements CordovaWebView { } } - /** - * Get string property for activity. - * - * @param name - * @param defaultValue - * @return the String value for the named property - */ - public String getProperty(String name, String defaultValue) { - Bundle bundle = this.cordova.getActivity().getIntent().getExtras(); - if (bundle == null) { - return defaultValue; - } - name = name.toLowerCase(Locale.getDefault()); - Object p = bundle.get(name); - if (p == null) { - return defaultValue; - } - return p.toString(); - } - /* * onKeyDown */ @@ -705,13 +574,10 @@ public class AndroidWebView extends WebView implements CordovaWebView { // Pause JavaScript timers (including setInterval) this.pauseTimers(); } - paused = true; - } public void handleResume(boolean keepRunning, boolean activityResultKeepRunning) { - this.loadUrl("javascript:try{cordova.fireDocumentEvent('resume');}catch(e){console.log('exception firing resume event from native');};"); // Forward to plugins @@ -721,7 +587,6 @@ public class AndroidWebView extends WebView implements CordovaWebView { // Resume JavaScript timers (including setInterval) this.resumeTimers(); - paused = false; } public void handleDestroy() @@ -755,11 +620,6 @@ public class AndroidWebView extends WebView implements CordovaWebView { } } - public boolean isPaused() - { - return paused; - } - // Wrapping these functions in their own class prevents warnings in adb like: // VFY: unable to resolve virtual method 285: Landroid/webkit/WebSettings;.setAllowUniversalAccessFromFileURLs @TargetApi(16) @@ -862,57 +722,17 @@ public class AndroidWebView extends WebView implements CordovaWebView { return resourceApi; } - @Override - public void setLayoutParams( - android.widget.LinearLayout.LayoutParams layoutParams) { - super.setLayoutParams(layoutParams); - } - - @Override - public void setOverScrollMode(int mode) { - super.setOverScrollMode(mode); - } - - @Override - public void addJavascript(String statement) { - this.jsMessageQueue.addJavaScript(statement); - } - - @Override - public CordovaPlugin getPlugin(String initCallbackClass) { - // TODO Auto-generated method stub - return this.pluginManager.getPlugin(initCallbackClass); - } - - @Override - public boolean onOverrideUrlLoading(String url) { - return this.pluginManager.onOverrideUrlLoading(url); - } - void onPageReset() { boundKeyCodes.clear(); pluginManager.onReset(); - jsMessageQueue.reset(); - exposedJsApi.clearBridgeSecret(); - } - - @Override - public void incUrlTimeout() { - this.loadUrlTimeout++; + bridge.reset(loadedUrl); } @Override public PluginManager getPluginManager() { - // TODO Auto-generated method stub return this.pluginManager; } - @Override - public void setLayoutParams( - android.widget.FrameLayout.LayoutParams layoutParams) { - super.setLayoutParams(layoutParams); - } - @Override public View getView() { return this; @@ -927,4 +747,17 @@ public class AndroidWebView extends WebView implements CordovaWebView { public CordovaPreferences getPreferences() { return preferences; } + + @Override + public void onFilePickerResult(Uri uri) { + if (null == chromeClient.mUploadMessage) + return; + chromeClient.mUploadMessage.onReceiveValue(uri); + chromeClient.mUploadMessage = null; + } + + @Override + public Object postMessage(String id, Object data) { + return pluginManager.postMessage(id, data); + } } diff --git a/framework/src/org/apache/cordova/AndroidWebViewClient.java b/framework/src/org/apache/cordova/AndroidWebViewClient.java index 047e1af2..44862a6b 100755 --- a/framework/src/org/apache/cordova/AndroidWebViewClient.java +++ b/framework/src/org/apache/cordova/AndroidWebViewClient.java @@ -48,7 +48,7 @@ import android.webkit.WebViewClient; * @see CordovaChromeClient * @see CordovaWebView */ -public class AndroidWebViewClient extends WebViewClient implements CordovaWebViewClient{ +public class AndroidWebViewClient extends WebViewClient { private static final String TAG = "AndroidWebViewClient"; protected final CordovaInterface cordova; @@ -60,12 +60,6 @@ public class AndroidWebViewClient extends WebViewClient implements CordovaWebVie /** The authorization tokens. */ private Hashtable authenticationTokens = new Hashtable(); - /** - * Constructor. - * - * @param cordova - * @param view - */ public AndroidWebViewClient(CordovaInterface cordova, AndroidWebView view) { this.cordova = cordova; this.appView = view; @@ -82,17 +76,12 @@ public class AndroidWebViewClient extends WebViewClient implements CordovaWebVie */ @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { - return helper.shouldOverrideUrlLoading(view, url); + return helper.shouldOverrideUrlLoading(url); } /** * On received http auth request. * The method reacts on all registered authentication tokens. There is one and only one authentication token for any host + realm combination - * - * @param view - * @param handler - * @param host - * @param realm */ @Override public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm) { @@ -126,7 +115,7 @@ public class AndroidWebViewClient extends WebViewClient implements CordovaWebVie this.appView.onPageReset(); // Broadcast message that page has loaded - this.appView.postMessage("onPageStarted", url); + this.appView.getPluginManager().postMessage("onPageStarted", url); } /** @@ -159,10 +148,10 @@ public class AndroidWebViewClient extends WebViewClient implements CordovaWebVie } // Clear timeout flag - this.appView.incUrlTimeout(); + appView.loadUrlTimeout++; // Broadcast message that page has loaded - this.appView.postMessage("onPageFinished", url); + this.appView.getPluginManager().postMessage("onPageFinished", url); // Make app visible after 2 sec in case there was a JS error and Cordova JS never initialized correctly if (this.appView.getVisibility() == View.INVISIBLE) { @@ -172,7 +161,7 @@ public class AndroidWebViewClient extends WebViewClient implements CordovaWebVie Thread.sleep(2000); cordova.getActivity().runOnUiThread(new Runnable() { public void run() { - appView.postMessage("spinner", "stop"); + appView.getPluginManager().postMessage("spinner", "stop"); } }); } catch (InterruptedException e) { @@ -184,7 +173,7 @@ public class AndroidWebViewClient extends WebViewClient implements CordovaWebVie // Shutdown if blank loaded if (url.equals("about:blank")) { - appView.postMessage("exit", null); + appView.getPluginManager().postMessage("exit", null); } } @@ -206,7 +195,7 @@ public class AndroidWebViewClient extends WebViewClient implements CordovaWebVie LOG.d(TAG, "CordovaWebViewClient.onReceivedError: Error code=%s Description=%s URL=%s", errorCode, description, failingUrl); // Clear timeout flag - this.appView.incUrlTimeout(); + appView.loadUrlTimeout++; // Handle error JSONObject data = new JSONObject(); @@ -217,7 +206,7 @@ public class AndroidWebViewClient extends WebViewClient implements CordovaWebVie } catch (JSONException e) { e.printStackTrace(); } - this.appView.postMessage("onReceivedError", data); + this.appView.getPluginManager().postMessage("onReceivedError", data); } /** @@ -326,10 +315,4 @@ public class AndroidWebViewClient extends WebViewClient implements CordovaWebVie public void clearAuthenticationTokens() { this.authenticationTokens.clear(); } - - @Override - public void onReceivedError(int errorCode, String description, String url) { - this.onReceivedError(appView, errorCode, description, url); - } - } diff --git a/framework/src/org/apache/cordova/CordovaActivity.java b/framework/src/org/apache/cordova/CordovaActivity.java index dbb6fee5..4bf65fd5 100755 --- a/framework/src/org/apache/cordova/CordovaActivity.java +++ b/framework/src/org/apache/cordova/CordovaActivity.java @@ -50,7 +50,6 @@ import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; -import android.webkit.ValueCallback; import android.webkit.WebViewClient; import android.widget.LinearLayout; @@ -162,11 +161,10 @@ public class CordovaActivity extends Activity implements CordovaInterface { } appView = makeWebView(); - appView.init(this, makeWebViewClient(appView), makeChromeClient(appView), pluginEntries, whitelist, preferences); // TODO: Have the views set this themselves. if (preferences.getBoolean("DisallowOverscroll", false)) { - appView.setOverScrollMode(View.OVER_SCROLL_NEVER); + appView.getView().setOverScrollMode(View.OVER_SCROLL_NEVER); } createViews(); @@ -190,7 +188,6 @@ public class CordovaActivity extends Activity implements CordovaInterface { @SuppressWarnings("deprecation") protected void createViews() { - // This builds the view. We could probably get away with NOT having a LinearLayout, but I like having a bucket! // This builds the view. We could probably get away with NOT having a LinearLayout, but I like having a bucket! Display display = getWindowManager().getDefaultDisplay(); int width = display.getWidth(); @@ -201,15 +198,15 @@ public class CordovaActivity extends Activity implements CordovaInterface { root.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 0.0F)); - appView.setId(100); - appView.setLayoutParams(new LinearLayout.LayoutParams( + appView.getView().setId(100); + appView.getView().setLayoutParams(new LinearLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 1.0F)); // Add web view but make it invisible while loading URL - appView.setVisibility(View.INVISIBLE); - root.addView((View) appView); + appView.getView().setVisibility(View.INVISIBLE); + root.addView(appView.getView()); setContentView(root); // TODO: Setting this on the appView causes it to show when . @@ -231,63 +228,34 @@ public class CordovaActivity extends Activity implements CordovaInterface { * require a more specialized web view. */ protected CordovaWebView makeWebView() { - String r = preferences.getString("webView", "org.apache.cordova.AndroidWebView"); - - try { - Class webViewClass = Class.forName(r); - Constructor[] webViewConstructors = webViewClass.getConstructors(); - - if(CordovaWebView.class.isAssignableFrom(webViewClass)) { - for (Constructor constructor : webViewConstructors) { - try { - CordovaWebView webView = (CordovaWebView) constructor.newInstance((Context)this); - return webView; - } catch (IllegalArgumentException e) { - LOG.d(TAG, "Illegal arguments; trying next constructor."); - } - } + String r = preferences.getString("webView", null); + CordovaWebView ret = null; + if (r != null) { + try { + Class webViewClass = Class.forName(r); + Constructor constructor = webViewClass.getConstructor(Context.class); + ret = (CordovaWebView) constructor.newInstance((Context)this); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); } - LOG.e(TAG, "The WebView Engine is NOT a proper WebView, defaulting to system WebView"); - } catch (ClassNotFoundException e) { - LOG.e(TAG, "The WebView Engine was not found, defaulting to system WebView"); - } catch (InstantiationException e) { - LOG.e(TAG, "Unable to instantiate the WebView, defaulting to system WebView"); - } catch (IllegalAccessException e) { - LOG.e(TAG, "Illegal Access to Constructor. This should never happen, defaulting to system WebView"); - } catch (IllegalArgumentException e) { - LOG.e(TAG, "The WebView does not implement the default constructor, defaulting to system WebView"); - } catch (InvocationTargetException e) { - LOG.e(TAG, "Invocation Target Exception! Reflection is hard, defaulting to system WebView"); } - - // If all else fails, return a default WebView - return (CordovaWebView) new AndroidWebView(this); - } - - /** - * Construct the client for the default web view object. - * - * This is intended to be overridable by subclasses of CordovaActivity which - * require a more specialized web view. By default, it allows the webView - * to create its own client objects. - * - * @param webView the default constructed web view object - */ - protected CordovaWebViewClient makeWebViewClient(CordovaWebView webView) { - return webView.makeWebViewClient(this); - } - - /** - * Construct the chrome client for the default web view object. - * - * This is intended to be overridable by subclasses of CordovaActivity which - * require a more specialized web view. By default, it allows the webView - * to create its own client objects. - * - * @param webView the default constructed web view object - */ - protected CordovaChromeClient makeChromeClient(CordovaWebView webView) { - return webView.makeWebChromeClient(this); + + if (ret == null) { + // If all else fails, return a default WebView + ret = new AndroidWebView(this); + } + ret.init(this, pluginEntries, whitelist, preferences); + return ret; } /** @@ -309,21 +277,17 @@ public class CordovaActivity extends Activity implements CordovaInterface { this.keepRunning = preferences.getBoolean("KeepRunning", true); //Check if the view is attached to anything - if(appView.getParent() != null) + if(appView.getView().getParent() != null) { // Then load the spinner this.loadSpinner(); } //Load the correct splashscreen - if(this.splashscreen != 0) { - this.appView.loadUrl(url, this.splashscreenTime); - } - else - { - this.appView.loadUrl(url); + appView.getPluginManager().postMessage("splashscreen", "show"); } + this.appView.loadUrlIntoView(url, true); } /** @@ -458,15 +422,6 @@ public class CordovaActivity extends Activity implements CordovaInterface { } } - /** - * Send a message to all plugins. - */ - public void postMessage(String id, Object data) { - if (this.appView != null) { - this.appView.postMessage(id, data); - } - } - /** * Show the spinner. Must be called from the UI thread. * @@ -542,21 +497,15 @@ public class CordovaActivity extends Activity implements CordovaInterface { super.onActivityResult(requestCode, resultCode, intent); Log.d(TAG, "Request code = " + requestCode); if (appView != null && requestCode == AndroidChromeClient.FILECHOOSER_RESULTCODE) { - ValueCallback mUploadMessage = ((CordovaChromeClient) this.appView.getWebChromeClient()).getValueCallback(); - Log.d(TAG, "did we get here?"); - if (null == mUploadMessage) - return; Uri result = intent == null || resultCode != Activity.RESULT_OK ? null : intent.getData(); - Log.d(TAG, "result = " + result); - mUploadMessage.onReceiveValue(result); - mUploadMessage = null; + appView.onFilePickerResult(result); } CordovaPlugin callback = this.activityResultCallback; if(callback == null && initCallbackClass != null) { // The application was restarted, but had defined an initial callback // before being shut down. //this.activityResultCallback = appView.pluginManager.getPlugin(initCallbackClass); - this.activityResultCallback = appView.getPlugin(initCallbackClass); + this.activityResultCallback = appView.getPluginManager().getPlugin(initCallbackClass); callback = this.activityResultCallback; } if(callback != null) { @@ -598,7 +547,7 @@ public class CordovaActivity extends Activity implements CordovaInterface { me.runOnUiThread(new Runnable() { public void run() { if (exit) { - me.appView.setVisibility(View.GONE); + me.appView.getView().setVisibility(View.GONE); me.displayError("Application Error", description + " (" + failingUrl + ")", "OK", exit); } } @@ -636,32 +585,24 @@ public class CordovaActivity extends Activity implements CordovaInterface { }); } - /** - * Determine if URL is in approved list of URLs to load. - */ - @Deprecated // Use whitelist object directly. - public boolean isUrlWhiteListed(String url) { - return whitelist.isUrlWhiteListed(url); - } - /* * Hook in Cordova for menu plugins */ @Override public boolean onCreateOptionsMenu(Menu menu) { - this.postMessage("onCreateOptionsMenu", menu); + appView.getPluginManager().postMessage("onCreateOptionsMenu", menu); return super.onCreateOptionsMenu(menu); } @Override public boolean onPrepareOptionsMenu(Menu menu) { - this.postMessage("onPrepareOptionsMenu", menu); + appView.getPluginManager().postMessage("onPrepareOptionsMenu", menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { - this.postMessage("onOptionsItemSelected", item); + appView.getPluginManager().postMessage("onOptionsItemSelected", item); return true; } @@ -753,7 +694,7 @@ public class CordovaActivity extends Activity implements CordovaInterface { else if ("spinner".equals(id)) { if ("stop".equals(data.toString())) { this.spinnerStop(); - this.appView.setVisibility(View.VISIBLE); + this.appView.getView().setVisibility(View.VISIBLE); } } else if ("onReceivedError".equals(id)) { diff --git a/framework/src/org/apache/cordova/CordovaBridge.java b/framework/src/org/apache/cordova/CordovaBridge.java new file mode 100644 index 00000000..d9efb3ba --- /dev/null +++ b/framework/src/org/apache/cordova/CordovaBridge.java @@ -0,0 +1,167 @@ +/* + 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. +*/ +package org.apache.cordova; + +import org.apache.cordova.PluginManager; +import org.json.JSONArray; +import org.json.JSONException; + +import android.util.Log; + +/** + * Contains APIs that the JS can call. All functions in here should also have + * an equivalent entry in CordovaChromeClient.java, and be added to + * cordova-js/lib/android/plugin/android/promptbasednativeapi.js + */ +public class CordovaBridge { + private static final String LOG_TAG = "CordovaBridge"; + private PluginManager pluginManager; + private NativeToJsMessageQueue jsMessageQueue; + private volatile int bridgeSecret = -1; // written by UI thread, read by JS thread. + private String loadedUrl; + + public CordovaBridge(PluginManager pluginManager, NativeToJsMessageQueue jsMessageQueue) { + this.pluginManager = pluginManager; + this.jsMessageQueue = jsMessageQueue; + } + + public String jsExec(int bridgeSecret, String service, String action, String callbackId, String arguments) throws JSONException, IllegalAccessException { + verifySecret(bridgeSecret); + // If the arguments weren't received, send a message back to JS. It will switch bridge modes and try again. See CB-2666. + // We send a message meant specifically for this case. It starts with "@" so no other message can be encoded into the same string. + if (arguments == null) { + return "@Null arguments."; + } + + jsMessageQueue.setPaused(true); + try { + // Tell the resourceApi what thread the JS is running on. + CordovaResourceApi.jsThread = Thread.currentThread(); + + pluginManager.exec(service, action, callbackId, arguments); + String ret = ""; + if (!NativeToJsMessageQueue.DISABLE_EXEC_CHAINING) { + ret = jsMessageQueue.popAndEncode(false); + } + return ret; + } catch (Throwable e) { + e.printStackTrace(); + return ""; + } finally { + jsMessageQueue.setPaused(false); + } + } + + public void jsSetNativeToJsBridgeMode(int bridgeSecret, int value) throws IllegalAccessException { + verifySecret(bridgeSecret); + jsMessageQueue.setBridgeMode(value); + } + + public String jsRetrieveJsMessages(int bridgeSecret, boolean fromOnlineEvent) throws IllegalAccessException { + verifySecret(bridgeSecret); + return jsMessageQueue.popAndEncode(fromOnlineEvent); + } + + private void verifySecret(int value) throws IllegalAccessException { + if (bridgeSecret < 0 || value != bridgeSecret) { + throw new IllegalAccessException(); + } + } + + /** Called on page transitions */ + void clearBridgeSecret() { + bridgeSecret = -1; + } + + /** Called by cordova.js to initialize the bridge. */ + int generateBridgeSecret() { + bridgeSecret = (int)(Math.random() * Integer.MAX_VALUE); + return bridgeSecret; + } + + public void reset(String loadedUrl) { + jsMessageQueue.reset(); + clearBridgeSecret(); + this.loadedUrl = loadedUrl; + } + + public String promptOnJsPrompt(String origin, String message, String defaultValue) { + if (defaultValue != null && defaultValue.length() > 3 && defaultValue.startsWith("gap:")) { + JSONArray array; + try { + array = new JSONArray(defaultValue.substring(4)); + int bridgeSecret = array.getInt(0); + String service = array.getString(1); + String action = array.getString(2); + String callbackId = array.getString(3); + String r = jsExec(bridgeSecret, service, action, callbackId, message); + return r == null ? "" : r; + } catch (JSONException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + return ""; + } + // Sets the native->JS bridge mode. + else if (defaultValue != null && defaultValue.startsWith("gap_bridge_mode:")) { + try { + int bridgeSecret = Integer.parseInt(defaultValue.substring(16)); + jsSetNativeToJsBridgeMode(bridgeSecret, Integer.parseInt(message)); + } catch (NumberFormatException e){ + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + return ""; + } + // Polling for JavaScript messages + else if (defaultValue != null && defaultValue.startsWith("gap_poll:")) { + int bridgeSecret = Integer.parseInt(defaultValue.substring(9)); + try { + String r = jsRetrieveJsMessages(bridgeSecret, "1".equals(message)); + return r == null ? "" : r; + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + return ""; + } + else if (defaultValue != null && defaultValue.startsWith("gap_init:")) { + // Protect against random iframes being able to talk through the bridge. + // Trust only file URLs and the start URL's domain. + // The extra origin.startsWith("http") is to protect against iframes with data: having "" as origin. + if (origin.startsWith("file:") || (origin.startsWith("http") && loadedUrl.startsWith(origin))) { + // Enable the bridge + int bridgeMode = Integer.parseInt(defaultValue.substring(9)); + jsMessageQueue.setBridgeMode(bridgeMode); + // Tell JS the bridge secret. + int secret = generateBridgeSecret(); + return ""+secret; + } else { + Log.e(LOG_TAG, "gap_init called from restricted origin: " + origin); + } + return ""; + } + return null; + } + + public NativeToJsMessageQueue getMessageQueue() { + return jsMessageQueue; + } +} diff --git a/framework/src/org/apache/cordova/CordovaChromeClient.java b/framework/src/org/apache/cordova/CordovaChromeClient.java deleted file mode 100644 index f5f470fa..00000000 --- a/framework/src/org/apache/cordova/CordovaChromeClient.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - 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. -*/ -package org.apache.cordova; - -import android.net.Uri; -import android.webkit.ValueCallback; - -public interface CordovaChromeClient { - ValueCallback getValueCallback(); -} diff --git a/framework/src/org/apache/cordova/CordovaUriHelper.java b/framework/src/org/apache/cordova/CordovaUriHelper.java index 2d3f3d89..077e425e 100644 --- a/framework/src/org/apache/cordova/CordovaUriHelper.java +++ b/framework/src/org/apache/cordova/CordovaUriHelper.java @@ -21,16 +21,15 @@ package org.apache.cordova; import android.content.Intent; import android.net.Uri; -import android.webkit.WebView; -class CordovaUriHelper { +public class CordovaUriHelper { private static final String TAG = "CordovaUriHelper"; private CordovaWebView appView; private CordovaInterface cordova; - CordovaUriHelper(CordovaInterface cdv, CordovaWebView webView) + public CordovaUriHelper(CordovaInterface cdv, CordovaWebView webView) { appView = webView; cordova = cdv; @@ -44,7 +43,7 @@ class CordovaUriHelper { * @param url The url to be loaded. * @return true to override, false for default behavior */ - boolean shouldOverrideUrlLoading(WebView view, String url) { + public boolean shouldOverrideUrlLoading(String url) { // The WebView should support http and https when going on the Internet if(url.startsWith("http:") || url.startsWith("https:")) { @@ -55,7 +54,7 @@ class CordovaUriHelper { } } // Give plugins the chance to handle the url - else if (this.appView.onOverrideUrlLoading(url)) { + else if (this.appView.getPluginManager().onOverrideUrlLoading(url)) { } else if(url.startsWith("file://") | url.startsWith("data:")) diff --git a/framework/src/org/apache/cordova/CordovaWebView.java b/framework/src/org/apache/cordova/CordovaWebView.java index 977ad4db..366acff3 100644 --- a/framework/src/org/apache/cordova/CordovaWebView.java +++ b/framework/src/org/apache/cordova/CordovaWebView.java @@ -5,45 +5,19 @@ import java.util.List; import android.content.Context; import android.content.Intent; +import android.net.Uri; import android.view.View; import android.webkit.WebChromeClient.CustomViewCallback; -import android.widget.LinearLayout.LayoutParams; public interface CordovaWebView { public static final String CORDOVA_VERSION = "4.0.0-dev"; - void init(CordovaInterface cordova, CordovaWebViewClient webViewClient, CordovaChromeClient webChromeClient, - List pluginEntries, Whitelist whitelist, CordovaPreferences preferences); + void init(CordovaInterface cordova, List pluginEntries, + Whitelist whitelist, CordovaPreferences preferences); View getView(); - CordovaWebViewClient makeWebViewClient(CordovaInterface cordova); - - CordovaChromeClient makeWebChromeClient(CordovaInterface cordova); - - void setWebViewClient(CordovaWebViewClient webViewClient); - - void setWebChromeClient(CordovaChromeClient webChromeClient); - - void setId(int i); - - void setLayoutParams(LayoutParams layoutParams); - - void setVisibility(int invisible); - - Object getParent(); - - void loadUrl(String url); - - void loadUrl(String url, int splashscreenTime); - - void loadUrlNow(String url); - - void loadUrlIntoView(final String url); - - void loadUrlIntoView(final String url, boolean recreatePlugins); - - void loadUrlIntoView(final String url, final int splashscreenTime); + void loadUrlIntoView(String url, boolean recreatePlugins); void stopLoading(); @@ -63,10 +37,6 @@ public interface CordovaWebView { void handleDestroy(); - void postMessage(String id, Object data); - - void addJavascript(String statement); - /** * Send JavaScript statement back to JavaScript. * (This is a convenience method) @@ -93,32 +63,14 @@ public interface CordovaWebView { @Deprecated void sendJavascript(String statememt); - CordovaChromeClient getWebChromeClient(); - - CordovaPlugin getPlugin(String initCallbackClass); - void showWebPage(String errorUrl, boolean b, boolean c, HashMap params); - Object getFocusedChild(); - boolean isCustomViewShowing(); void showCustomView(View view, CustomViewCallback callback); void hideCustomView(); - Context getContext(); - - boolean onOverrideUrlLoading(String url); - - int getVisibility(); - - void incUrlTimeout(); - - void setOverScrollMode(int overScrollNever); - - void setNetworkAvailable(boolean online); - CordovaResourceApi getResourceApi(); void setButtonPlumbedToJs(int keyCode, boolean override); @@ -128,12 +80,15 @@ public interface CordovaWebView { PluginManager getPluginManager(); - void setLayoutParams(android.widget.FrameLayout.LayoutParams layoutParams); - - // Required for test - String getUrl(); - boolean isPaused(); - Whitelist getWhitelist(); CordovaPreferences getPreferences(); + + void onFilePickerResult(Uri uri); + + void setNetworkAvailable(boolean online); + + // TODO: Work on deleting these by removing refs from plugins. + Context getContext(); + void loadUrl(String url); + Object postMessage(String id, Object data); } diff --git a/framework/src/org/apache/cordova/CordovaWebViewClient.java b/framework/src/org/apache/cordova/CordovaWebViewClient.java deleted file mode 100644 index 671e2273..00000000 --- a/framework/src/org/apache/cordova/CordovaWebViewClient.java +++ /dev/null @@ -1,5 +0,0 @@ -package org.apache.cordova; - -public interface CordovaWebViewClient { - void onReceivedError(int errorCode, String description, String url); -} diff --git a/framework/src/org/apache/cordova/CoreAndroid.java b/framework/src/org/apache/cordova/CoreAndroid.java index 64036733..55cf9fbb 100755 --- a/framework/src/org/apache/cordova/CoreAndroid.java +++ b/framework/src/org/apache/cordova/CoreAndroid.java @@ -75,7 +75,7 @@ public class CoreAndroid extends CordovaPlugin { // indicative of what this actually does (shows the webview). cordova.getActivity().runOnUiThread(new Runnable() { public void run() { - webView.postMessage("spinner", "stop"); + webView.getPluginManager().postMessage("spinner", "stop"); } }); } @@ -247,7 +247,7 @@ public class CoreAndroid extends CordovaPlugin { * Exit the Android application. */ public void exitApp() { - this.webView.postMessage("exit", null); + this.webView.getPluginManager().postMessage("exit", null); } @@ -271,15 +271,15 @@ public class CoreAndroid extends CordovaPlugin { String extraData = intent.getStringExtra(TelephonyManager.EXTRA_STATE); if (extraData.equals(TelephonyManager.EXTRA_STATE_RINGING)) { LOG.i(TAG, "Telephone RINGING"); - webView.postMessage("telephone", "ringing"); + webView.getPluginManager().postMessage("telephone", "ringing"); } else if (extraData.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) { LOG.i(TAG, "Telephone OFFHOOK"); - webView.postMessage("telephone", "offhook"); + webView.getPluginManager().postMessage("telephone", "offhook"); } else if (extraData.equals(TelephonyManager.EXTRA_STATE_IDLE)) { LOG.i(TAG, "Telephone IDLE"); - webView.postMessage("telephone", "idle"); + webView.getPluginManager().postMessage("telephone", "idle"); } } } diff --git a/framework/src/org/apache/cordova/IceCreamCordovaWebViewClient.java b/framework/src/org/apache/cordova/IceCreamCordovaWebViewClient.java index 4938091d..68f87419 100644 --- a/framework/src/org/apache/cordova/IceCreamCordovaWebViewClient.java +++ b/framework/src/org/apache/cordova/IceCreamCordovaWebViewClient.java @@ -32,7 +32,7 @@ import android.webkit.WebResourceResponse; import android.webkit.WebView; @TargetApi(Build.VERSION_CODES.HONEYCOMB) -public class IceCreamCordovaWebViewClient extends AndroidWebViewClient implements CordovaWebViewClient{ +public class IceCreamCordovaWebViewClient extends AndroidWebViewClient { private static final String TAG = "IceCreamCordovaWebViewClient"; diff --git a/framework/src/org/apache/cordova/NativeToJsMessageQueue.java b/framework/src/org/apache/cordova/NativeToJsMessageQueue.java index b822800e..08ca00ea 100755 --- a/framework/src/org/apache/cordova/NativeToJsMessageQueue.java +++ b/framework/src/org/apache/cordova/NativeToJsMessageQueue.java @@ -294,7 +294,7 @@ public class NativeToJsMessageQueue { public void run() { String js = popAndEncodeAsJs(); if (js != null) { - webView.loadUrlNow("javascript:" + js); + webView.loadUrlIntoView("javascript:" + js, false); } } }; diff --git a/framework/src/org/apache/cordova/PluginManager.java b/framework/src/org/apache/cordova/PluginManager.java index 9a283616..699f7cb2 100755 --- a/framework/src/org/apache/cordova/PluginManager.java +++ b/framework/src/org/apache/cordova/PluginManager.java @@ -53,22 +53,14 @@ public class PluginManager { private Set pluginIdWhitelist; - PluginManager(CordovaWebView cordovaWebView, CordovaInterface cordova, List pluginEntries) { + public PluginManager(CordovaWebView cordovaWebView, CordovaInterface cordova, List pluginEntries) { this.ctx = cordova; this.app = cordovaWebView; - setPluginEntries(pluginEntries); - } - - public void setPluginEntries(List pluginEntries) { - this.onPause(false); - this.onDestroy(); - this.clearPluginObjects(); - entries.clear(); for (PluginEntry entry : pluginEntries) { addService(entry); } } - + public void setPluginIdWhitelist(Set pluginIdWhitelist) { this.pluginIdWhitelist = pluginIdWhitelist; }