From f60d54eae4a6af0ab55fa8fa1323b5ebacf8db85 Mon Sep 17 00:00:00 2001 From: Ian Clelland Date: Mon, 18 Mar 2013 10:16:24 -0400 Subject: [PATCH 1/7] [CB-2305] Add InAppBrowser injectSriptCode command to support InAppBrowser.executeScript and InAppBrowser.insertCSS APIs --- .../src/org/apache/cordova/InAppBrowser.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/framework/src/org/apache/cordova/InAppBrowser.java b/framework/src/org/apache/cordova/InAppBrowser.java index 48e27c60..02aa002d 100644 --- a/framework/src/org/apache/cordova/InAppBrowser.java +++ b/framework/src/org/apache/cordova/InAppBrowser.java @@ -151,6 +151,21 @@ public class InAppBrowser extends CordovaPlugin { pluginResult.setKeepCallback(false); this.callbackContext.sendPluginResult(pluginResult); } + else if (action.equals("injectScriptCode")) { + String source = args.getString(0); + + org.json.JSONArray jsonEsc = new org.json.JSONArray(); + jsonEsc.put(source); + String jsonRepr = jsonEsc.toString(); + String jsonSourceString = jsonRepr.substring(1, jsonRepr.length()-1); + String scriptEnclosure = "(function(d){var c=d.createElement('script');c.type='text/javascript';c.innerText=" + + jsonSourceString + + ";d.getElementsByTagName('head')[0].appendChild(c);})(document)"; + this.inAppWebView.loadUrl("javascript:" + scriptEnclosure); + + PluginResult pluginResult = new PluginResult(PluginResult.Status.OK); + this.callbackContext.sendPluginResult(pluginResult); + } else { status = PluginResult.Status.INVALID_ACTION; } From 0c740909534b355cd5628380689544c4f2c2a8b8 Mon Sep 17 00:00:00 2001 From: Andrew Grieve Date: Sat, 23 Mar 2013 14:07:22 -0400 Subject: [PATCH 2/7] Log a message when exec() is made to an unregistered plugin. --- framework/src/org/apache/cordova/api/PluginManager.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/framework/src/org/apache/cordova/api/PluginManager.java b/framework/src/org/apache/cordova/api/PluginManager.java index 337ef129..774b21c3 100755 --- a/framework/src/org/apache/cordova/api/PluginManager.java +++ b/framework/src/org/apache/cordova/api/PluginManager.java @@ -30,6 +30,7 @@ import org.xmlpull.v1.XmlPullParserException; import android.content.Intent; import android.content.res.XmlResourceParser; +import android.util.Log; import android.webkit.WebResourceResponse; /** @@ -213,6 +214,7 @@ public class PluginManager { public boolean exec(String service, String action, String callbackId, String rawArgs) { CordovaPlugin plugin = this.getPlugin(service); if (plugin == null) { + Log.d(TAG, "exec() call to unknown plugin: " + service); PluginResult cr = new PluginResult(PluginResult.Status.CLASS_NOT_FOUND_EXCEPTION); app.sendPluginResult(cr, callbackId); return true; From 73c7994cd15264349f64fe82cb83e61558ebbd53 Mon Sep 17 00:00:00 2001 From: Andrew Grieve Date: Sat, 23 Mar 2013 14:07:57 -0400 Subject: [PATCH 3/7] Fix NPE in InAppBrowser. When cordova.getActivity().getIntent().getExtras() == null. --- framework/src/org/apache/cordova/InAppBrowser.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/src/org/apache/cordova/InAppBrowser.java b/framework/src/org/apache/cordova/InAppBrowser.java index 02aa002d..0d5d4965 100644 --- a/framework/src/org/apache/cordova/InAppBrowser.java +++ b/framework/src/org/apache/cordova/InAppBrowser.java @@ -460,7 +460,7 @@ public class InAppBrowser extends CordovaPlugin { //Toggle whether this is enabled or not! Bundle appSettings = cordova.getActivity().getIntent().getExtras(); - boolean enableDatabase = appSettings.getBoolean("InAppBrowserStorageEnabled", true); + boolean enableDatabase = appSettings == null ? true : appSettings.getBoolean("InAppBrowserStorageEnabled", true); if(enableDatabase) { String databasePath = cordova.getActivity().getApplicationContext().getDir("inAppBrowserDB", Context.MODE_PRIVATE).getPath(); From f4859444ddca5a94a1bcb8d7fcfef6faedc476d2 Mon Sep 17 00:00:00 2001 From: Shravan Narayan Date: Tue, 26 Mar 2013 00:10:26 -0400 Subject: [PATCH 4/7] Fixed protocol regex bug. Unknown protocol support Added whitelist support for unknown protocols --- framework/src/org/apache/cordova/Config.java | 24 ++++++++++++++++---- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/framework/src/org/apache/cordova/Config.java b/framework/src/org/apache/cordova/Config.java index f5de38db..594c2b27 100644 --- a/framework/src/org/apache/cordova/Config.java +++ b/framework/src/org/apache/cordova/Config.java @@ -171,7 +171,7 @@ public class Config { LOG.i("CordovaLog", "Found start page location: %s", src); if (src != null) { - Pattern schemeRegex = Pattern.compile("^[a-z]+://"); + Pattern schemeRegex = Pattern.compile("^[a-z-]+://"); Matcher matcher = schemeRegex.matcher(src); if (matcher.find()) { startUrl = src; @@ -220,19 +220,33 @@ public class Config { } else { // specific access // check if subdomains should be included // TODO: we should not add more domains if * has already been added + Pattern schemeRegex = Pattern.compile("^[a-z-]+://"); + Matcher matcher = schemeRegex.matcher(origin); if (subdomains) { - // XXX making it stupid friendly for people who forget to include protocol/SSL + // Check for http or https protocols if (origin.startsWith("http")) { this.whiteList.add(Pattern.compile(origin.replaceFirst("https?://", "^https?://(.*\\.)?"))); - } else { + } + // Check for other protocols + else if(matcher.find()){ + this.whiteList.add(Pattern.compile("^" + origin.replaceFirst("//", "//(.*\\.)?"))); + } + // XXX making it stupid friendly for people who forget to include protocol/SSL + else { this.whiteList.add(Pattern.compile("^https?://(.*\\.)?" + origin)); } LOG.d(TAG, "Origin to allow with subdomains: %s", origin); } else { - // XXX making it stupid friendly for people who forget to include protocol/SSL + // Check for http or https protocols if (origin.startsWith("http")) { this.whiteList.add(Pattern.compile(origin.replaceFirst("https?://", "^https?://"))); - } else { + } + // Check for other protocols + else if(matcher.find()){ + this.whiteList.add(Pattern.compile("^" + origin)); + } + // XXX making it stupid friendly for people who forget to include protocol/SSL + else { this.whiteList.add(Pattern.compile("^https?://" + origin)); } LOG.d(TAG, "Origin to allow: %s", origin); From 5ee7e81ff96476a3784ab2db9fab10574b4b95e7 Mon Sep 17 00:00:00 2001 From: JasonM23 Date: Fri, 22 Mar 2013 11:56:43 +1100 Subject: [PATCH 5/7] [CB-51] Added httpMethod for upload (defaults to POST) --- framework/src/org/apache/cordova/FileTransfer.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/framework/src/org/apache/cordova/FileTransfer.java b/framework/src/org/apache/cordova/FileTransfer.java index dba29aff..75bed2cc 100644 --- a/framework/src/org/apache/cordova/FileTransfer.java +++ b/framework/src/org/apache/cordova/FileTransfer.java @@ -204,6 +204,7 @@ public class FileTransfer extends CordovaPlugin { // Look for headers on the params map for backwards compatibility with older Cordova versions. final JSONObject headers = args.optJSONObject(8) == null ? params.optJSONObject("headers") : args.optJSONObject(8); final String objectId = args.getString(9); + final String httpMethod = getArgument(args, 10, "POST"); Log.d(LOG_TAG, "fileKey: " + fileKey); Log.d(LOG_TAG, "fileName: " + fileName); @@ -213,6 +214,7 @@ public class FileTransfer extends CordovaPlugin { Log.d(LOG_TAG, "chunkedMode: " + chunkedMode); Log.d(LOG_TAG, "headers: " + headers); Log.d(LOG_TAG, "objectId: " + objectId); + Log.d(LOG_TAG, "httpMethod: " + httpMethod); final URL url; try { @@ -280,7 +282,7 @@ public class FileTransfer extends CordovaPlugin { conn.setUseCaches(false); // Use a post method. - conn.setRequestMethod("POST"); + conn.setRequestMethod(httpMethod); conn.setRequestProperty("Content-Type", "multipart/form-data;boundary=" + BOUNDARY); // Set the cookies on the response From 36c33a58892f49067f4d3edf51b401b4a3c94259 Mon Sep 17 00:00:00 2001 From: James Jong Date: Fri, 15 Feb 2013 18:54:02 -0500 Subject: [PATCH 6/7] CB-1944: Better error messages for Create script - fixed to detect missing packages individually - added a specific message for each missing package - messages include how to correct and package download link --- bin/create.bat | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/bin/create.bat b/bin/create.bat index 35fdc3b6..cdbd6118 100644 --- a/bin/create.bat +++ b/bin/create.bat @@ -16,17 +16,37 @@ :: under the License. @ECHO OFF -IF NOT DEFINED JAVA_HOME GOTO MISSING +IF NOT DEFINED JAVA_HOME GOTO MISSING_JAVA_HOME + FOR %%X in (java.exe javac.exe ant.bat android.bat) do ( - SET FOUND=%%~$PATH:X - IF NOT DEFINED FOUND GOTO MISSING + IF [%%~$PATH:X]==[] ( + ECHO Cannot locate %%X using the PATH environment variable. + ECHO Retry after adding directory containing %%X to the PATH variable. + ECHO Remember to open a new command window after updating the PATH variable. + IF "%%X"=="java.exe" GOTO GET_JAVA + IF "%%X"=="javac.exe" GOTO GET_JAVA + IF "%%X"=="ant.bat" GOTO GET_ANT + IF "%%X"=="android.bat" GOTO GET_ANDROID + GOTO ERROR + ) ) cscript "%~dp0\create.js" %* GOTO END -:MISSING -ECHO Missing one of the following: -ECHO JDK: http://java.oracle.com -ECHO Android SDK: http://developer.android.com -ECHO Apache ant: http://ant.apache.org +:MISSING_JAVA_HOME + ECHO The JAVA_HOME environment variable is not set. + ECHO Set JAVA_HOME to an existing JRE directory. + ECHO Remember to also add JAVA_HOME to the PATH variable. + ECHO After updating system variables, open a new command window and retry. + GOTO ERROR +:GET_JAVA + ECHO Visit http://java.oracle.com if you need to install Java (JDK). + GOTO ERROR +:GET_ANT + ECHO Visit http://ant.apache.org if you need to install Apache Ant. + GOTO ERROR +:GET_ANDROID + ECHO Visit http://developer.android.com if you need to install the Android SDK. + GOTO ERROR +:ERROR EXIT /B 1 :END From 282367c6d59bf7a74d2455c7ff7b478f2157f0a1 Mon Sep 17 00:00:00 2001 From: Ian Clelland Date: Tue, 19 Mar 2013 22:39:11 -0400 Subject: [PATCH 7/7] [CB-1517] Properly report download progress for GZIP-encoded resources --- .../src/org/apache/cordova/FileTransfer.java | 101 ++++++++++++++++-- 1 file changed, 91 insertions(+), 10 deletions(-) diff --git a/framework/src/org/apache/cordova/FileTransfer.java b/framework/src/org/apache/cordova/FileTransfer.java index 75bed2cc..c1ca15cf 100644 --- a/framework/src/org/apache/cordova/FileTransfer.java +++ b/framework/src/org/apache/cordova/FileTransfer.java @@ -41,6 +41,8 @@ import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.HashMap; import java.util.Iterator; +import java.util.zip.GZIPInputStream; +import java.util.zip.Inflater; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; @@ -99,11 +101,84 @@ public class FileTransfer extends CordovaPlugin { } } + /** + * Adds an interface method to an InputStream to return the number of bytes + * read from the raw stream. This is used to track total progress against + * the HTTP Content-Length header value from the server. + */ + private static abstract class TrackingInputStream extends FilterInputStream { + public TrackingInputStream(final InputStream in) { + super(in); + } + public abstract long getTotalRawBytesRead(); + } + + private static class ExposedGZIPInputStream extends GZIPInputStream { + public ExposedGZIPInputStream(final InputStream in) throws IOException { + super(in); + } + public Inflater getInflater() { + return inf; + } + } + + /** + * Provides raw bytes-read tracking for a GZIP input stream. Reports the + * total number of compressed bytes read from the input, rather than the + * number of uncompressed bytes. + */ + private static class TrackingGZIPInputStream extends TrackingInputStream { + private ExposedGZIPInputStream gzin; + public TrackingGZIPInputStream(final ExposedGZIPInputStream gzin) throws IOException { + super(gzin); + this.gzin = gzin; + } + public long getTotalRawBytesRead() { + return gzin.getInflater().getBytesRead(); + } + } + + /** + * Provides simple total-bytes-read tracking for an existing InputStream + */ + private static class TrackingHTTPInputStream extends TrackingInputStream { + private long bytesRead = 0; + public TrackingHTTPInputStream(InputStream stream) { + super(stream); + } + + private int updateBytesRead(int newBytesRead) { + if (newBytesRead != -1) { + bytesRead += newBytesRead; + } + return newBytesRead; + } + + @Override + public int read() throws IOException { + return updateBytesRead(super.read()); + } + + @Override + public int read(byte[] buffer) throws IOException { + return updateBytesRead(super.read(buffer)); + } + + @Override + public int read(byte[] bytes, int offset, int count) throws IOException { + return updateBytesRead(super.read(bytes, offset, count)); + } + + public long getTotalRawBytesRead() { + return bytesRead; + } + } + /** * Works around a bug on Android 2.3. * http://code.google.com/p/android/issues/detail?id=14562 */ - private static final class DoneHandlerInputStream extends FilterInputStream { + private static final class DoneHandlerInputStream extends TrackingHTTPInputStream { private boolean done; public DoneHandlerInputStream(InputStream stream) { @@ -409,7 +484,7 @@ public class FileTransfer extends CordovaPlugin { int responseCode = conn.getResponseCode(); Log.d(LOG_TAG, "response code: " + responseCode); Log.d(LOG_TAG, "response headers: " + conn.getHeaderFields()); - InputStream inStream = null; + TrackingInputStream inStream = null; try { inStream = getInputStream(conn); synchronized (context) { @@ -485,11 +560,15 @@ public class FileTransfer extends CordovaPlugin { } } - private static InputStream getInputStream(URLConnection conn) throws IOException { + private static TrackingInputStream getInputStream(URLConnection conn) throws IOException { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { return new DoneHandlerInputStream(conn.getInputStream()); } - return conn.getInputStream(); + String encoding = conn.getContentEncoding(); + if (encoding != null && encoding.equalsIgnoreCase("gzip")) { + return new TrackingGZIPInputStream(new ExposedGZIPInputStream(conn.getInputStream())); + } + return new TrackingHTTPInputStream(conn.getInputStream()); } // always verify the host - don't check for certificate @@ -700,6 +779,9 @@ public class FileTransfer extends CordovaPlugin { { connection.setRequestProperty("cookie", cookie); } + + // This must be explicitly set for gzip progress tracking to work. + connection.setRequestProperty("Accept-Encoding", "gzip"); // Handle the other headers if (headers != null) { @@ -711,14 +793,15 @@ public class FileTransfer extends CordovaPlugin { Log.d(LOG_TAG, "Download file:" + url); FileProgressResult progress = new FileProgressResult(); - if (connection.getContentEncoding() == null) { - // Only trust content-length header if no gzip etc + if (connection.getContentEncoding() == null || connection.getContentEncoding().equalsIgnoreCase("gzip")) { + // Only trust content-length header if we understand + // the encoding -- identity or gzip progress.setLengthComputable(true); progress.setTotal(connection.getContentLength()); } FileOutputStream outputStream = null; - InputStream inputStream = null; + TrackingInputStream inputStream = null; try { inputStream = getInputStream(connection); @@ -733,12 +816,10 @@ public class FileTransfer extends CordovaPlugin { // write bytes to file byte[] buffer = new byte[MAX_BUFFER_SIZE]; int bytesRead = 0; - long totalBytes = 0; while ((bytesRead = inputStream.read(buffer)) > 0) { outputStream.write(buffer, 0, bytesRead); - totalBytes += bytesRead; // Send a progress event. - progress.setLoaded(totalBytes); + progress.setLoaded(inputStream.getTotalRawBytesRead()); PluginResult progressResult = new PluginResult(PluginResult.Status.OK, progress.toJSONObject()); progressResult.setKeepCallback(true); context.sendPluginResult(progressResult);