Skip to content

Commit 87b5e7c

Browse files
committed
Squashed 'libs/editor/' changes from 4832795..50ddd25
50ddd25 Merge pull request #289 from wordpress-mobile/issue/24-js-security 300ffea Added profiling markers for visual editor startup 8747fc5 Merge branch 'issue/24-js-security' of github.com:wordpress-mobile/WordPress-Editor-Android into issue/24-js-security da37d47 Drop NativeStateJsInterface, loading native localized Strings during ZSSEditor init instead e6b654e Drop NativeStateJsInterface, loading native localized Strings during ZSSEditor init instead 6914c39 Inject androidApiLevel variable when loading the android-editor HTML file 050de28 Use URL loading through an iframe for JS callbacks on API<17, instead of a JavascriptInterface 39534a2 escape single quotes when calling execJavaScriptFromString 2771c83 fix wordpress-mobile/WordPress-Editor-Android#285: markImage(Video)UploadFailed now called with 2 parameters b22fd31 Merge branch 'develop' into feature/visual-editor 4a8f94c Upgrade gradle plugin to 2.0.0-beta5 9bab5b7 Update gradle version in wrapper f358e5e fix MockEditorActivity for tests e2e4ec2 Merge commit '68c6f4cd95baefe3198dca9e8bd7d1ee3732f576' into issue/3610-media-tracking-fixes 30ffce9 Centralized tracking of added images/videos 2515856 Merge commit '097b51b9618f0fe23146c66b58918651e9c80bbc' into issue/3610-media-tracking-fixes b46fd23 Merge branch 'develop' into feature/visual-editor 1f0df17 Merge pull request #3742 from wordpress-mobile/issue/update-to-android-gradle-plugin-2.0 c0e7c30 update to android gradle plugin beta4 40fdc2d update to android gradle plugin 2.0.0-beta-2 - Instant Run 5b4eb42 Updating to latest versions of internal dependencies bd28005 Keeping internal dependencies always pointing to the latest version git-subtree-dir: libs/editor git-subtree-split: 50ddd25ce768b2caeae1634deaefe31c23d3a0e0
1 parent 68c6f4c commit 87b5e7c

File tree

11 files changed

+83
-72
lines changed

11 files changed

+83
-72
lines changed

WordPressEditor/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ buildscript {
33
jcenter()
44
}
55
dependencies {
6-
classpath 'com.android.tools.build:gradle:1.5.0'
6+
classpath 'com.android.tools.build:gradle:2.0.0-beta5'
77
}
88
}
99

@@ -48,7 +48,7 @@ android {
4848
dependencies {
4949
compile 'com.android.support:appcompat-v7:23.1.1'
5050
compile 'com.android.support:support-v4:23.1.1'
51-
compile 'org.wordpress:utils:1.7.0'
51+
compile 'org.wordpress:utils:1.9.0'
5252

5353
// Test libraries
5454
testCompile 'junit:junit:4.11'

WordPressEditor/src/androidTest/java/org.wordpress.android.editor/MockEditorActivity.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import android.support.v7.app.AppCompatActivity;
77
import android.widget.LinearLayout;
88

9+
import org.wordpress.android.editor.EditorFragmentAbstract.TrackableEvent;
910
import org.wordpress.android.util.helpers.MediaFile;
1011

1112
public class MockEditorActivity extends AppCompatActivity implements EditorFragmentAbstract.EditorFragmentListener {
@@ -75,5 +76,10 @@ public String onAuthHeaderRequested(String url) {
7576
public void saveMediaFile(MediaFile mediaFile) {
7677

7778
}
79+
80+
@Override
81+
public void onTrackableEvent(TrackableEvent event) {
82+
83+
}
7884
}
7985

WordPressEditor/src/main/java/org/wordpress/android/editor/EditorFragment.java

Lines changed: 30 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import android.view.View;
2525
import android.view.ViewGroup;
2626
import android.view.inputmethod.InputMethodManager;
27-
import android.webkit.JavascriptInterface;
2827
import android.webkit.URLUtil;
2928
import android.webkit.WebView;
3029
import android.widget.ToggleButton;
@@ -36,6 +35,7 @@
3635
import org.wordpress.android.util.AppLog;
3736
import org.wordpress.android.util.AppLog.T;
3837
import org.wordpress.android.util.JSONUtils;
38+
import org.wordpress.android.util.ProfilingUtils;
3939
import org.wordpress.android.util.StringUtils;
4040
import org.wordpress.android.util.ToastUtils;
4141
import org.wordpress.android.util.UrlUtils;
@@ -60,7 +60,6 @@ public class EditorFragment extends EditorFragmentAbstract implements View.OnCli
6060
private static final String ARG_PARAM_CONTENT = "param_content";
6161

6262
private static final String JS_CALLBACK_HANDLER = "nativeCallbackHandler";
63-
private static final String JS_STATE_INTERFACE = "nativeState";
6463

6564
private static final String KEY_TITLE = "title";
6665
private static final String KEY_CONTENT = "content";
@@ -121,6 +120,9 @@ public EditorFragment() {
121120
@Override
122121
public void onCreate(Bundle savedInstanceState) {
123122
super.onCreate(savedInstanceState);
123+
124+
ProfilingUtils.start("Visual Editor Startup");
125+
ProfilingUtils.split("EditorFragment.onCreate");
124126
}
125127

126128
@Override
@@ -372,14 +374,26 @@ protected void initJsEditor() {
372374
return;
373375
}
374376

377+
ProfilingUtils.split("EditorFragment.initJsEditor");
378+
375379
String htmlEditor = Utils.getHtmlFromFile(getActivity(), "android-editor.html");
376380
if (htmlEditor != null) {
377381
htmlEditor = htmlEditor.replace("%%TITLE%%", getString(R.string.visual_editor));
382+
htmlEditor = htmlEditor.replace("%%ANDROID_API_LEVEL%%", String.valueOf(Build.VERSION.SDK_INT));
383+
htmlEditor = htmlEditor.replace("%%LOCALIZED_STRING_INIT%%",
384+
"nativeState.localizedStringEdit = '" + getString(R.string.edit) + "';\n" +
385+
"nativeState.localizedStringUploading = '" + getString(R.string.uploading) + "';\n" +
386+
"nativeState.localizedStringUploadingGallery = '" + getString(R.string.uploading_gallery_placeholder) + "';\n");
378387
}
379388

380-
mWebView.addJavascriptInterface(new JsCallbackReceiver(this), JS_CALLBACK_HANDLER);
381-
mWebView.addJavascriptInterface(new NativeStateJsInterface(getActivity().getApplicationContext()),
382-
JS_STATE_INTERFACE);
389+
// To avoid reflection security issues with JavascriptInterface on API<17, we use an iframe to make URL requests
390+
// for callbacks from JS instead. These are received by WebViewClient.shouldOverrideUrlLoading() and then
391+
// passed on to the JsCallbackReceiver
392+
if (Build.VERSION.SDK_INT < 17) {
393+
mWebView.setJsCallbackReceiver(new JsCallbackReceiver(this));
394+
} else {
395+
mWebView.addJavascriptInterface(new JsCallbackReceiver(this), JS_CALLBACK_HANDLER);
396+
}
383397

384398
mWebView.loadDataWithBaseURL("file:///android_asset/", htmlEditor, "text/html", "utf-8", "");
385399

@@ -745,13 +759,11 @@ public void run() {
745759
if (URLUtil.isNetworkUrl(mediaUrl)) {
746760
String mediaId = mediaFile.getMediaId();
747761
mWebView.execJavaScriptFromString("ZSSEditor.insertImage('" + mediaUrl + "', '" + mediaId + "');");
748-
mEditorFragmentListener.onTrackableEvent(TrackableEvent.NETWORK_IMAGE_ADDED);
749762
} else {
750763
String id = mediaFile.getMediaId();
751764
mWebView.execJavaScriptFromString("ZSSEditor.insertLocalImage(" + id + ", '" + mediaUrl + "');");
752765
mWebView.execJavaScriptFromString("ZSSEditor.setProgressOnImage(" + id + ", " + 0 + ");");
753766
mUploadingMediaIds.add(id);
754-
mEditorFragmentListener.onTrackableEvent(TrackableEvent.LOCAL_IMAGE_ADDED);
755767
}
756768
}
757769
});
@@ -835,12 +847,12 @@ public void run() {
835847
}
836848

837849
@Override
838-
public void onMediaUploadFailed(final String mediaId) {
850+
public void onMediaUploadFailed(final String mediaId, final String errorMessage) {
839851
mWebView.post(new Runnable() {
840852
@Override
841853
public void run() {
842-
mEditorFragmentListener.onTrackableEvent(TrackableEvent.UPLOAD_IMAGE_FAILED);
843-
mWebView.execJavaScriptFromString("ZSSEditor.markImageUploadFailed(" + mediaId + ");");
854+
mWebView.execJavaScriptFromString("ZSSEditor.markImageUploadFailed(" + mediaId + ", '"
855+
+ errorMessage.replace("'", "\\'").replace("\"", "\\\"") + "');");
844856
mFailedMediaIds.add(mediaId);
845857
mUploadingMediaIds.remove(mediaId);
846858
}
@@ -869,6 +881,8 @@ public void run() {
869881
}
870882

871883
public void onDomLoaded() {
884+
ProfilingUtils.split("EditorFragment.onDomLoaded");
885+
872886
mWebView.post(new Runnable() {
873887
public void run() {
874888
mDomHasLoaded = true;
@@ -886,7 +900,8 @@ public void run() {
886900

887901
// If there are images that are still in progress (because the editor exited before they completed),
888902
// set them to failed, so the user can restart them (otherwise they will stay stuck in 'uploading' mode)
889-
mWebView.execJavaScriptFromString("ZSSEditor.markAllUploadingImagesAsFailed();");
903+
mWebView.execJavaScriptFromString("ZSSEditor.markAllUploadingImagesAsFailed('"
904+
+ getString(R.string.tap_to_try_again) + "');");
890905

891906
// Update the list of failed media uploads
892907
mWebView.execJavaScriptFromString("ZSSEditor.getFailedImages();");
@@ -924,6 +939,10 @@ public void run() {
924939

925940
mWaitingGalleries.clear();
926941
}
942+
943+
ProfilingUtils.split("EditorFragment.onDomLoaded completed");
944+
ProfilingUtils.dump();
945+
ProfilingUtils.stop();
927946
}
928947
});
929948
}
@@ -998,7 +1017,6 @@ public void onClick(DialogInterface dialog, int id) {
9981017
mWebView.post(new Runnable() {
9991018
@Override
10001019
public void run() {
1001-
mEditorFragmentListener.onTrackableEvent(TrackableEvent.UPLOAD_IMAGE_RETRIED);
10021020
mWebView.execJavaScriptFromString("ZSSEditor.unmarkImageUploadFailed(" + mediaId + ");");
10031021
mWebView.execJavaScriptFromString("ZSSEditor.setProgressOnImage(" + mediaId + ", " + 0 + ");");
10041022
mFailedMediaIds.remove(mediaId);
@@ -1268,37 +1286,4 @@ private void applyFormattingHtmlMode(ToggleButton toggleButton, String tag) {
12681286
mSourceViewContent.setSelection(selectionEnd + endTag.length());
12691287
}
12701288
}
1271-
1272-
private class NativeStateJsInterface {
1273-
Context mContext;
1274-
1275-
NativeStateJsInterface(Context context) {
1276-
mContext = context;
1277-
}
1278-
1279-
@JavascriptInterface
1280-
public String getStringEdit() {
1281-
return mContext.getString(R.string.edit);
1282-
}
1283-
1284-
@JavascriptInterface
1285-
public String getStringTapToRetry() {
1286-
return mContext.getString(R.string.tap_to_try_again);
1287-
}
1288-
1289-
@JavascriptInterface
1290-
public String getStringUploading() {
1291-
return mContext.getString(R.string.uploading);
1292-
}
1293-
1294-
@JavascriptInterface
1295-
public String getStringUploadingGallery() {
1296-
return mContext.getString(R.string.uploading_gallery_placeholder);
1297-
}
1298-
1299-
@JavascriptInterface
1300-
public int getAPILevel() {
1301-
return Build.VERSION.SDK_INT;
1302-
}
1303-
}
13041289
}

WordPressEditor/src/main/java/org/wordpress/android/editor/EditorFragmentAbstract.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,10 +137,6 @@ public enum TrackableEvent {
137137
UNLINK_BUTTON_TAPPED,
138138
LINK_BUTTON_TAPPED,
139139
MEDIA_BUTTON_TAPPED,
140-
NETWORK_IMAGE_ADDED,
141-
LOCAL_IMAGE_ADDED,
142-
UPLOAD_IMAGE_FAILED,
143-
UPLOAD_IMAGE_RETRIED,
144140
IMAGE_EDITED,
145141
BOLD_BUTTON_TAPPED,
146142
ITALIC_BUTTON_TAPPED,

WordPressEditor/src/main/java/org/wordpress/android/editor/EditorMediaUploadListener.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
public interface EditorMediaUploadListener {
44
void onMediaUploadSucceeded(String localId, String remoteId, String remoteUrl);
55
void onMediaUploadProgress(String localId, float progress);
6-
void onMediaUploadFailed(String localId);
6+
void onMediaUploadFailed(String localId, String errorMessage);
77
void onGalleryMediaUploadSucceeded(long galleryId, String remoteId, int remaining);
88
}

WordPressEditor/src/main/java/org/wordpress/android/editor/EditorWebViewAbstract.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public abstract class EditorWebViewAbstract extends WebView {
3636

3737
private OnImeBackListener mOnImeBackListener;
3838
private AuthHeaderRequestListener mAuthHeaderRequestListener;
39+
private JsCallbackReceiver mJsCallbackReceiver;
3940

4041
private Map<String, String> mHeaderMap = new HashMap<>();
4142

@@ -53,6 +54,12 @@ private void configureWebView() {
5354
this.setWebViewClient(new WebViewClient() {
5455
@Override
5556
public boolean shouldOverrideUrlLoading(WebView view, String url) {
57+
if (url != null && url.startsWith("callback") && mJsCallbackReceiver != null) {
58+
String[] split = url.split(":", 2);
59+
String callbackId = split[0];
60+
String params = (split.length > 1 ? split[1] : "");
61+
mJsCallbackReceiver.executeCallback(callbackId, params);
62+
}
5663
return true;
5764
}
5865

@@ -178,6 +185,14 @@ public void setAuthHeaderRequestListener(AuthHeaderRequestListener listener) {
178185
mAuthHeaderRequestListener = listener;
179186
}
180187

188+
/**
189+
* Used on API<17 to handle callbacks as a safe alternative to JavascriptInterface (which has security risks
190+
* at those API levels).
191+
*/
192+
public void setJsCallbackReceiver(JsCallbackReceiver jsCallbackReceiver) {
193+
mJsCallbackReceiver = jsCallbackReceiver;
194+
}
195+
181196
@Override
182197
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
183198
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {

example/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ buildscript {
33
jcenter()
44
}
55
dependencies {
6-
classpath 'com.android.tools.build:gradle:1.5.0'
6+
classpath 'com.android.tools.build:gradle:2.0.0-beta5'
77
}
88
}
99

example/src/main/java/org/wordpress/example/EditorExampleActivity.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,8 @@ public void run() {
246246
count += 0.1;
247247
}
248248

249-
((EditorMediaUploadListener) mEditorFragment).onMediaUploadFailed(mediaId);
249+
((EditorMediaUploadListener) mEditorFragment).onMediaUploadFailed(mediaId,
250+
getString(R.string.tap_to_try_again));
250251

251252
mFailedUploads.put(mediaId, mediaUrl);
252253
} catch (InterruptedException e) {

gradle/wrapper/gradle-wrapper.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
33
distributionPath=wrapper/dists
44
zipStoreBase=GRADLE_USER_HOME
55
zipStorePath=wrapper/dists
6-
distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
6+
distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip

0 commit comments

Comments
 (0)