android: compat webviewexecutor to support arraybuffer
This commit is contained in:
		| @@ -17,6 +17,7 @@ package pub.doric.engine; | ||||
|  | ||||
| import com.github.pengfeizhou.jscore.ArchiveException; | ||||
| import com.github.pengfeizhou.jscore.JSArray; | ||||
| import com.github.pengfeizhou.jscore.JSArrayBuffer; | ||||
| import com.github.pengfeizhou.jscore.JSBoolean; | ||||
| import com.github.pengfeizhou.jscore.JSDecoder; | ||||
| import com.github.pengfeizhou.jscore.JSNull; | ||||
| @@ -127,6 +128,9 @@ public class DoricJSDecoder extends JSDecoder { | ||||
|             } | ||||
|             return jsArray; | ||||
|         } | ||||
|         if (this.value instanceof byte[]) { | ||||
|             return new JSArrayBuffer((byte[]) this.value); | ||||
|         } | ||||
|         return JS_NULL; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -109,14 +109,14 @@ public class DoricJSEngine implements Handler.Callback, DoricTimerExtension.Time | ||||
|             e.printStackTrace(new PrintWriter(stringWriter)); | ||||
|             mDoricRegistry.onLog(Log.ERROR, stringWriter.toString()); | ||||
|             //In case some unexpected errors happened | ||||
|             if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { | ||||
|                 mDoricRegistry.onLog(Log.WARN, "Use DoricWebViewJSExecutor"); | ||||
|                 mDoricJSE = new DoricWebViewJSExecutor(Doric.application()); | ||||
|                 loadBuiltinJS("doric-web.js"); | ||||
|             } else { | ||||
| //            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { | ||||
| //                mDoricRegistry.onLog(Log.WARN, "Use DoricWebViewJSExecutor"); | ||||
| //                mDoricJSE = new DoricWebViewJSExecutor(Doric.application()); | ||||
| //                loadBuiltinJS("doric-web.js"); | ||||
| //            } else { | ||||
|                 mDoricRegistry.onLog(Log.WARN, "Use DoricWebShellJSExecutor"); | ||||
|                 mDoricJSE = new DoricWebShellJSExecutor(Doric.application()); | ||||
|             } | ||||
| //            } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -21,6 +21,7 @@ import android.os.Build; | ||||
| import android.os.Handler; | ||||
| import android.os.HandlerThread; | ||||
| import android.text.TextUtils; | ||||
| import android.util.Base64; | ||||
| import android.webkit.ConsoleMessage; | ||||
| import android.webkit.JavascriptInterface; | ||||
| import android.webkit.JsResult; | ||||
| @@ -43,6 +44,7 @@ import org.json.JSONObject; | ||||
|  | ||||
| import java.io.ByteArrayInputStream; | ||||
| import java.io.InputStream; | ||||
| import java.lang.ref.SoftReference; | ||||
| import java.util.HashMap; | ||||
| import java.util.HashSet; | ||||
| import java.util.Map; | ||||
| @@ -66,6 +68,8 @@ public class DoricWebShellJSExecutor implements IDoricJSE { | ||||
|     private WebView webView; | ||||
|     private final Map<String, JavaFunction> globalFunctions = new HashMap<>(); | ||||
|     private final Handler handler; | ||||
|     private static final Map<String, SoftReference<byte[]>> arrayBuffers = new HashMap<>(); | ||||
|     private static final AtomicInteger arrayBufferId = new AtomicInteger(); | ||||
|  | ||||
|     private static Object unwrapJSObject(JSONObject jsonObject) { | ||||
|         String type = jsonObject.optString("type"); | ||||
| @@ -90,6 +94,9 @@ public class DoricWebShellJSExecutor implements IDoricJSE { | ||||
|                     e.printStackTrace(); | ||||
|                     return JSONObject.NULL; | ||||
|                 } | ||||
|             case "arrayBuffer": | ||||
|                 String base64 = jsonObject.optString("value"); | ||||
|                 return Base64.decode(base64, Base64.NO_WRAP); | ||||
|             default: | ||||
|                 return JSONObject.NULL; | ||||
|         } | ||||
| @@ -127,6 +134,12 @@ public class DoricWebShellJSExecutor implements IDoricJSE { | ||||
|                 e.printStackTrace(); | ||||
|             } | ||||
|         } | ||||
|         if (javaValue.getType() == 6) { | ||||
|             byte[] data = javaValue.getByteData(); | ||||
|             String id = String.valueOf(arrayBufferId.incrementAndGet()); | ||||
|             arrayBuffers.put(id, new SoftReference<>(data)); | ||||
|             return new JSONBuilder().put("type", "arrayBuffer").put("value", id).toJSONObject(); | ||||
|         } | ||||
|         return WRAPPED_NULL; | ||||
|     } | ||||
|  | ||||
| @@ -142,6 +155,16 @@ public class DoricWebShellJSExecutor implements IDoricJSE { | ||||
|             readyFuture.set(true); | ||||
|         } | ||||
|  | ||||
|         @JavascriptInterface | ||||
|         public String fetchArrayBuffer(String arrayBufferId) { | ||||
|             SoftReference<byte[]> ref = arrayBuffers.remove(arrayBufferId); | ||||
|             if (ref != null && ref.get() != null) { | ||||
|                 byte[] data = ref.get(); | ||||
|                 return Base64.encodeToString(data, Base64.NO_WRAP); | ||||
|             } | ||||
|             return ""; | ||||
|         } | ||||
|  | ||||
|         @JavascriptInterface | ||||
|         public void log(String message) { | ||||
|             DoricLog.d(message); | ||||
|   | ||||
| @@ -22,6 +22,7 @@ import android.os.Build; | ||||
| import android.os.Handler; | ||||
| import android.os.HandlerThread; | ||||
| import android.text.TextUtils; | ||||
| import android.util.Base64; | ||||
| import android.webkit.ConsoleMessage; | ||||
| import android.webkit.JavascriptInterface; | ||||
| import android.webkit.JsResult; | ||||
| @@ -39,8 +40,10 @@ import org.json.JSONArray; | ||||
| import org.json.JSONException; | ||||
| import org.json.JSONObject; | ||||
|  | ||||
| import java.lang.ref.SoftReference; | ||||
| import java.util.HashMap; | ||||
| import java.util.Map; | ||||
| import java.util.concurrent.atomic.AtomicInteger; | ||||
|  | ||||
| import pub.doric.BuildConfig; | ||||
| import pub.doric.async.SettableFuture; | ||||
| @@ -57,6 +60,8 @@ public class DoricWebViewJSExecutor implements IDoricJSE { | ||||
|     private WebView webView; | ||||
|     private final Map<String, JavaFunction> globalFunctions = new HashMap<>(); | ||||
|     private final Handler handler; | ||||
|     private static final Map<String, SoftReference<byte[]>> arrayBuffers = new HashMap<>(); | ||||
|     private static final AtomicInteger arrayBufferId = new AtomicInteger(); | ||||
|  | ||||
|     private static Object unwrapJSObject(JSONObject jsonObject) { | ||||
|         String type = jsonObject.optString("type"); | ||||
| @@ -81,6 +86,9 @@ public class DoricWebViewJSExecutor implements IDoricJSE { | ||||
|                     e.printStackTrace(); | ||||
|                     return JSONObject.NULL; | ||||
|                 } | ||||
|             case "arrayBuffer": | ||||
|                 String base64 = jsonObject.optString("value"); | ||||
|                 return Base64.decode(base64, Base64.NO_WRAP); | ||||
|             default: | ||||
|                 return JSONObject.NULL; | ||||
|         } | ||||
| @@ -118,6 +126,12 @@ public class DoricWebViewJSExecutor implements IDoricJSE { | ||||
|                 e.printStackTrace(); | ||||
|             } | ||||
|         } | ||||
|         if (javaValue.getType() == 6) { | ||||
|             byte[] data = javaValue.getByteData(); | ||||
|             String id = String.valueOf(arrayBufferId.incrementAndGet()); | ||||
|             arrayBuffers.put(id, new SoftReference<>(data)); | ||||
|             return new JSONBuilder().put("type", "arrayBuffer").put("value", id).toJSONObject(); | ||||
|         } | ||||
|         return WRAPPED_NULL; | ||||
|     } | ||||
|  | ||||
| @@ -132,9 +146,19 @@ public class DoricWebViewJSExecutor implements IDoricJSE { | ||||
|             DoricLog.d(message); | ||||
|         } | ||||
|  | ||||
|         @JavascriptInterface | ||||
|         public String fetchArrayBuffer(String arrayBufferId) { | ||||
|             SoftReference<byte[]> ref = arrayBuffers.remove(arrayBufferId); | ||||
|             if (ref != null && ref.get() != null) { | ||||
|                 byte[] data = ref.get(); | ||||
|                 return Base64.encodeToString(data, Base64.NO_WRAP); | ||||
|             } | ||||
|             return ""; | ||||
|         } | ||||
|  | ||||
|         @JavascriptInterface | ||||
|         public void returnNative(String result) { | ||||
|             DoricLog.d("return Native" + result); | ||||
|             DoricLog.d("return Native " + result); | ||||
|             if (returnFuture != null) { | ||||
|                 returnFuture.set(result); | ||||
|             } | ||||
|   | ||||
| @@ -52,6 +52,8 @@ import com.bumptech.glide.request.target.DrawableImageViewTarget; | ||||
| import com.bumptech.glide.request.target.SizeReadyCallback; | ||||
| import com.bumptech.glide.request.target.Target; | ||||
| import com.facebook.yoga.YogaNode; | ||||
| import com.github.pengfeizhou.jscore.ArchiveException; | ||||
| import com.github.pengfeizhou.jscore.JSDecoder; | ||||
| import com.github.pengfeizhou.jscore.JSONBuilder; | ||||
| import com.github.pengfeizhou.jscore.JSObject; | ||||
| import com.github.pengfeizhou.jscore.JSValue; | ||||
| @@ -394,7 +396,7 @@ public class ImageNode extends ViewNode<ImageView> { | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void blend(ImageView view, String name, JSValue prop) { | ||||
|     protected void blend(final ImageView view, String name, JSValue prop) { | ||||
|         switch (name) { | ||||
|             case "image": | ||||
|                 if (!prop.isObject()) { | ||||
| @@ -522,13 +524,46 @@ public class ImageNode extends ViewNode<ImageView> { | ||||
|                 if (!prop.isObject()) { | ||||
|                     return; | ||||
|                 } | ||||
|                 int width = prop.asObject().getProperty("width").asNumber().toInt(); | ||||
|                 int height = prop.asObject().getProperty("height").asNumber().toInt(); | ||||
|                 final int width = prop.asObject().getProperty("width").asNumber().toInt(); | ||||
|                 final int height = prop.asObject().getProperty("height").asNumber().toInt(); | ||||
|                 JSValue pixelsValue = prop.asObject().getProperty("pixels"); | ||||
|                 if (pixelsValue.isArrayBuffer()) { | ||||
|                     byte[] pixels = prop.asObject().getProperty("pixels").asArrayBuffer().value(); | ||||
|                     Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); | ||||
|                     ByteBuffer byteBuffer = ByteBuffer.wrap(pixels); | ||||
|                     bitmap.copyPixelsFromBuffer(byteBuffer); | ||||
|                     view.setImageBitmap(bitmap); | ||||
|                 } else if (pixelsValue.isString()) { | ||||
|                     String pixelsCallbackId = pixelsValue.asString().value(); | ||||
|                     callJSResponse(pixelsCallbackId).setCallback(new AsyncResult.Callback<JSDecoder>() { | ||||
|                         @Override | ||||
|                         public void onResult(JSDecoder result) { | ||||
|                             try { | ||||
|                                 JSValue value = result.decode(); | ||||
|                                 if (value.isArrayBuffer()) { | ||||
|                                     byte[] pixels = value.asArrayBuffer().value(); | ||||
|                                     Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); | ||||
|                                     ByteBuffer byteBuffer = ByteBuffer.wrap(pixels); | ||||
|                                     bitmap.copyPixelsFromBuffer(byteBuffer); | ||||
|                                     view.setImageBitmap(bitmap); | ||||
|                                 } | ||||
|                             } catch (ArchiveException e) { | ||||
|                                 e.printStackTrace(); | ||||
|                             } | ||||
|  | ||||
|                         } | ||||
|  | ||||
|                         @Override | ||||
|                         public void onError(Throwable t) { | ||||
|  | ||||
|                         } | ||||
|  | ||||
|                         @Override | ||||
|                         public void onFinish() { | ||||
|  | ||||
|                         } | ||||
|                     }); | ||||
|                 } | ||||
|                 break; | ||||
|             default: | ||||
|                 super.blend(view, name, prop); | ||||
|   | ||||
| @@ -0,0 +1,23 @@ | ||||
| package com.github.pengfeizhou.jscore; | ||||
|  | ||||
| public class JSArrayBuffer extends JSValue { | ||||
|     private final byte[] mVal; | ||||
|  | ||||
|     public JSArrayBuffer(byte[] val) { | ||||
|         this.mVal = val; | ||||
|     } | ||||
|  | ||||
|     public int getByteLength() { | ||||
|         return mVal.length; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public JSType getJSType() { | ||||
|         return JSType.ArrayBuffer; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public byte[] value() { | ||||
|         return mVal; | ||||
|     } | ||||
| } | ||||
| @@ -25,6 +25,7 @@ public abstract class JSValue { | ||||
|         String, | ||||
|         Object, | ||||
|         Array, | ||||
|         ArrayBuffer, | ||||
|     } | ||||
|  | ||||
|     public abstract JSType getJSType(); | ||||
| @@ -55,6 +56,10 @@ public abstract class JSValue { | ||||
|         return getJSType() == JSType.Array; | ||||
|     } | ||||
|  | ||||
|     public boolean isArrayBuffer() { | ||||
|         return getJSType() == JSType.ArrayBuffer; | ||||
|     } | ||||
|  | ||||
|     public JSNull asNull() { | ||||
|         return (JSNull) this; | ||||
|     } | ||||
| @@ -79,6 +84,10 @@ public abstract class JSValue { | ||||
|         return (JSArray) this; | ||||
|     } | ||||
|  | ||||
|     public JSArrayBuffer asArrayBuffer() { | ||||
|         return (JSArrayBuffer) this; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public String toString() { | ||||
|         return String.valueOf(value()); | ||||
|   | ||||
| @@ -25,10 +25,14 @@ public class JavaValue { | ||||
|     protected static final int TYPE_STRING = 3; | ||||
|     protected static final int TYPE_OBJECT = 4; | ||||
|     protected static final int TYPE_ARRAY = 5; | ||||
|     protected static final int TYPE_ARRAYBUFFER = 6; | ||||
|  | ||||
|     protected JavaFunction[] functions = null; | ||||
|     protected String[] functionNames = null; | ||||
|     protected int type; | ||||
|     protected String value = ""; | ||||
|     protected byte[] data = null; | ||||
|     protected MemoryReleaser memoryReleaser = null; | ||||
|  | ||||
|     public JavaValue() { | ||||
|         this.type = TYPE_NULL; | ||||
| @@ -75,6 +79,17 @@ public class JavaValue { | ||||
|         this.value = jsonArray.toString(); | ||||
|     } | ||||
|  | ||||
|     public JavaValue(byte[] data) { | ||||
|         this.type = TYPE_ARRAYBUFFER; | ||||
|         this.data = data; | ||||
|     } | ||||
|  | ||||
|     public JavaValue(byte[] data, MemoryReleaser memoryReleaser) { | ||||
|         this.type = TYPE_ARRAYBUFFER; | ||||
|         this.data = data; | ||||
|         this.memoryReleaser = memoryReleaser; | ||||
|     } | ||||
|  | ||||
|     public int getType() { | ||||
|         return type; | ||||
|     } | ||||
| @@ -82,4 +97,24 @@ public class JavaValue { | ||||
|     public String getValue() { | ||||
|         return value; | ||||
|     } | ||||
|  | ||||
|     public byte[] getByteData() { | ||||
|         return data; | ||||
|     } | ||||
|  | ||||
|     public interface MemoryReleaser { | ||||
|         /** | ||||
|          * Called when JS deallocated the arraybuffer | ||||
|          */ | ||||
|         void deallocate(byte[] data); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Called by JNI | ||||
|      */ | ||||
|     private void onDeallocated() { | ||||
|         if (this.memoryReleaser != null) { | ||||
|             this.memoryReleaser.deallocate(this.data); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -2314,13 +2314,11 @@ var Image = /** @class */ (function (_super) { | ||||
|     }; | ||||
|     Image.prototype.toModel = function () { | ||||
|         var ret = _super.prototype.toModel.call(this); | ||||
|         if (Environment.platform === 'iOS') { | ||||
|         if (Reflect.has(ret.props, "imagePixels")) { | ||||
|             var imagePixels = Reflect.get(ret.props, "imagePixels"); | ||||
|             var pixels_1 = imagePixels.pixels; | ||||
|             imagePixels.pixels = this.callback2Id(function () { return pixels_1; }); | ||||
|         } | ||||
|         } | ||||
|         return ret; | ||||
|     }; | ||||
|     __decorate$b([ | ||||
|   | ||||
| @@ -1728,13 +1728,11 @@ class Image extends View { | ||||
|     } | ||||
|     toModel() { | ||||
|         const ret = super.toModel(); | ||||
|         if (Environment.platform === 'iOS') { | ||||
|         if (Reflect.has(ret.props, "imagePixels")) { | ||||
|             const imagePixels = Reflect.get(ret.props, "imagePixels"); | ||||
|             const pixels = imagePixels.pixels; | ||||
|             imagePixels.pixels = this.callback2Id(() => pixels); | ||||
|         } | ||||
|         } | ||||
|         return ret; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -3256,13 +3256,11 @@ class Image extends View { | ||||
|     } | ||||
|     toModel() { | ||||
|         const ret = super.toModel(); | ||||
|         if (Environment.platform === 'iOS') { | ||||
|         if (Reflect.has(ret.props, "imagePixels")) { | ||||
|             const imagePixels = Reflect.get(ret.props, "imagePixels"); | ||||
|             const pixels = imagePixels.pixels; | ||||
|             imagePixels.pixels = this.callback2Id(() => pixels); | ||||
|         } | ||||
|         } | ||||
|         return ret; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -1,42 +1,53 @@ | ||||
| 'use strict'; | ||||
|  | ||||
| "use strict"; | ||||
| function _binaryValue(v) { | ||||
|     switch (typeof v) { | ||||
|         case "number": | ||||
|             return { | ||||
|                 type: "number", | ||||
|                 value: v | ||||
|             }; | ||||
|         case "string": | ||||
|             return { | ||||
|                 type: "string", | ||||
|                 value: v | ||||
|             }; | ||||
|         case "boolean": | ||||
|             return { | ||||
|                 type: "boolean", | ||||
|                 value: v | ||||
|             }; | ||||
|         case "object": | ||||
|             if (v instanceof Array) { | ||||
|                 return { | ||||
|                     type: "array", | ||||
|                     value: JSON.stringify(v) | ||||
|                 }; | ||||
| function _arrayBufferToBase64(arrayBuffer) { | ||||
|     let base64 = ''; | ||||
|     const encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; | ||||
|     const bytes = new Uint8Array(arrayBuffer); | ||||
|     const byteLength = bytes.byteLength; | ||||
|     const byteRemainder = byteLength % 3; | ||||
|     const mainLength = byteLength - byteRemainder; | ||||
|     let a, b, c, d; | ||||
|     let chunk; | ||||
|     // Main loop deals with bytes in chunks of 3 | ||||
|     for (var i = 0; i < mainLength; i = i + 3) { | ||||
|         // Combine the three bytes into a single integer | ||||
|         chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]; | ||||
|         // Use bitmasks to extract 6-bit segments from the triplet | ||||
|         a = (chunk & 16515072) >> 18; // 16515072 = (2^6 - 1) << 18 | ||||
|         b = (chunk & 258048) >> 12; // 258048   = (2^6 - 1) << 12 | ||||
|         c = (chunk & 4032) >> 6; // 4032     = (2^6 - 1) << 6 | ||||
|         d = chunk & 63; // 63       = 2^6 - 1 | ||||
|         // Convert the raw binary segments to the appropriate ASCII encoding | ||||
|         base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d]; | ||||
|     } | ||||
|             else { | ||||
|                 return { | ||||
|                     type: "object", | ||||
|                     value: JSON.stringify(v) | ||||
|                 }; | ||||
|     // Deal with the remaining bytes and padding | ||||
|     if (byteRemainder == 1) { | ||||
|         chunk = bytes[mainLength]; | ||||
|         a = (chunk & 252) >> 2; // 252 = (2^6 - 1) << 2 | ||||
|         // Set the 4 least significant bits to zero | ||||
|         b = (chunk & 3) << 4; // 3   = 2^2 - 1 | ||||
|         base64 += encodings[a] + encodings[b] + '=='; | ||||
|     } | ||||
|         default: | ||||
|             return { | ||||
|                 type: "null", | ||||
|                 value: undefined | ||||
|             }; | ||||
|     else if (byteRemainder == 2) { | ||||
|         chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1]; | ||||
|         a = (chunk & 64512) >> 10; // 64512 = (2^6 - 1) << 10 | ||||
|         b = (chunk & 1008) >> 4; // 1008  = (2^6 - 1) << 4 | ||||
|         // Set the 2 least significant bits to zero | ||||
|         c = (chunk & 15) << 2; // 15    = 2^4 - 1 | ||||
|         base64 += encodings[a] + encodings[b] + encodings[c] + '='; | ||||
|     } | ||||
|     return base64; | ||||
| } | ||||
| function _base64ToArrayBuffer(v) { | ||||
|     const binary_string = window.atob(v); | ||||
|     const len = binary_string.length; | ||||
|     const bytes = new Uint8Array(len); | ||||
|     for (let i = 0; i < len; i++) { | ||||
|         bytes[i] = binary_string.charCodeAt(i); | ||||
|     } | ||||
|     return bytes.buffer; | ||||
| } | ||||
| function _wrappedValue(v) { | ||||
|     switch (typeof v) { | ||||
| @@ -56,7 +67,13 @@ function _wrappedValue(v) { | ||||
|                 value: v | ||||
|             }; | ||||
|         case "object": | ||||
|             if (v instanceof Array) { | ||||
|             if (v instanceof ArrayBuffer) { | ||||
|                 return { | ||||
|                     type: "arrayBuffer", | ||||
|                     value: _arrayBufferToBase64(v) | ||||
|                 }; | ||||
|             } | ||||
|             else if (v instanceof Array) { | ||||
|                 return { | ||||
|                     type: "array", | ||||
|                     value: JSON.stringify(v) | ||||
| @@ -89,6 +106,10 @@ function _rawValue(v) { | ||||
|                 return JSON.parse(v.value); | ||||
|             } | ||||
|             return v.value; | ||||
|         case "arrayBuffer": | ||||
|             const data = NativeClient.fetchArrayBuffer(v.value); | ||||
|             return _base64ToArrayBuffer(data); | ||||
|             ; | ||||
|         default: | ||||
|             return undefined; | ||||
|     } | ||||
|   | ||||
| @@ -2,51 +2,74 @@ declare module NativeClient { | ||||
|     function log(message: string): void | ||||
|     function returnNative(ret: string): void | ||||
|     function callNative(name: string, args: string): string | ||||
|     function fetchArrayBuffer(id: string): string | ||||
| } | ||||
|  | ||||
| type RawValue = number | string | boolean | object | undefined | ||||
| type RawValue = number | string | boolean | object | undefined | ArrayBuffer | ||||
|  | ||||
| type WrappedValue = { | ||||
|     type: "number" | "string" | "boolean" | "object" | "array" | "null", | ||||
|     type: "number" | "string" | "boolean" | "object" | "array" | "null" | "arrayBuffer", | ||||
|     value: RawValue, | ||||
| } | ||||
| function _binaryValue(v: RawValue) { | ||||
|     switch (typeof v) { | ||||
|         case "number": | ||||
|             return { | ||||
|                 type: "number", | ||||
|                 value: v | ||||
|             }; | ||||
|         case "string": | ||||
|             return { | ||||
|                 type: "string", | ||||
|                 value: v | ||||
|             }; | ||||
|         case "boolean": | ||||
|             return { | ||||
|                 type: "boolean", | ||||
|                 value: v | ||||
|             }; | ||||
|         case "object": | ||||
|             if (v instanceof Array) { | ||||
|                 return { | ||||
|                     type: "array", | ||||
|                     value: JSON.stringify(v) | ||||
|                 }; | ||||
|             } else { | ||||
|                 return { | ||||
|                     type: "object", | ||||
|                     value: JSON.stringify(v) | ||||
|                 }; | ||||
|             } | ||||
|         default: | ||||
|             return { | ||||
|                 type: "null", | ||||
|                 value: undefined | ||||
|             }; | ||||
|  | ||||
| function _arrayBufferToBase64(arrayBuffer: ArrayBuffer) { | ||||
|     let base64 = '' | ||||
|     const encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' | ||||
|  | ||||
|     const bytes = new Uint8Array(arrayBuffer) | ||||
|     const byteLength = bytes.byteLength | ||||
|     const byteRemainder = byteLength % 3 | ||||
|     const mainLength = byteLength - byteRemainder | ||||
|  | ||||
|     let a, b, c, d | ||||
|     let chunk | ||||
|  | ||||
|     // Main loop deals with bytes in chunks of 3 | ||||
|     for (var i = 0; i < mainLength; i = i + 3) { | ||||
|         // Combine the three bytes into a single integer | ||||
|         chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2] | ||||
|         // Use bitmasks to extract 6-bit segments from the triplet | ||||
|         a = (chunk & 16515072) >> 18 // 16515072 = (2^6 - 1) << 18 | ||||
|         b = (chunk & 258048) >> 12 // 258048   = (2^6 - 1) << 12 | ||||
|         c = (chunk & 4032) >> 6 // 4032     = (2^6 - 1) << 6 | ||||
|         d = chunk & 63               // 63       = 2^6 - 1 | ||||
|         // Convert the raw binary segments to the appropriate ASCII encoding | ||||
|         base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d] | ||||
|     } | ||||
|  | ||||
|     // Deal with the remaining bytes and padding | ||||
|     if (byteRemainder == 1) { | ||||
|         chunk = bytes[mainLength] | ||||
|  | ||||
|         a = (chunk & 252) >> 2 // 252 = (2^6 - 1) << 2 | ||||
|  | ||||
|         // Set the 4 least significant bits to zero | ||||
|         b = (chunk & 3) << 4 // 3   = 2^2 - 1 | ||||
|  | ||||
|         base64 += encodings[a] + encodings[b] + '==' | ||||
|     } else if (byteRemainder == 2) { | ||||
|         chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1] | ||||
|  | ||||
|         a = (chunk & 64512) >> 10 // 64512 = (2^6 - 1) << 10 | ||||
|         b = (chunk & 1008) >> 4 // 1008  = (2^6 - 1) << 4 | ||||
|  | ||||
|         // Set the 2 least significant bits to zero | ||||
|         c = (chunk & 15) << 2 // 15    = 2^4 - 1 | ||||
|  | ||||
|         base64 += encodings[a] + encodings[b] + encodings[c] + '=' | ||||
|     } | ||||
|     return base64 | ||||
| } | ||||
| function _base64ToArrayBuffer(v: string) { | ||||
|     const binary_string = window.atob(v); | ||||
|     const len = binary_string.length; | ||||
|     const bytes = new Uint8Array(len); | ||||
|     for (let i = 0; i < len; i++) { | ||||
|         bytes[i] = binary_string.charCodeAt(i); | ||||
|     } | ||||
|     return bytes.buffer; | ||||
| } | ||||
|  | ||||
| function _wrappedValue(v: RawValue): WrappedValue { | ||||
|     switch (typeof v) { | ||||
|         case "number": | ||||
| @@ -65,7 +88,12 @@ function _wrappedValue(v: RawValue): WrappedValue { | ||||
|                 value: v | ||||
|             }; | ||||
|         case "object": | ||||
|             if (v instanceof Array) { | ||||
|             if (v instanceof ArrayBuffer) { | ||||
|                 return { | ||||
|                     type: "arrayBuffer", | ||||
|                     value: _arrayBufferToBase64(v) | ||||
|                 }; | ||||
|             } else if (v instanceof Array) { | ||||
|                 return { | ||||
|                     type: "array", | ||||
|                     value: JSON.stringify(v) | ||||
| @@ -98,6 +126,9 @@ function _rawValue(v: WrappedValue): RawValue { | ||||
|                 return JSON.parse(v.value) | ||||
|             } | ||||
|             return v.value; | ||||
|         case "arrayBuffer": | ||||
|             const data = NativeClient.fetchArrayBuffer(v.value as string); | ||||
|             return _base64ToArrayBuffer(data);; | ||||
|         default: | ||||
|             return undefined; | ||||
|     } | ||||
|   | ||||
							
								
								
									
										20
									
								
								doric-js/lib/index.web.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										20
									
								
								doric-js/lib/index.web.d.ts
									
									
									
									
										vendored
									
									
								
							| @@ -2,25 +2,15 @@ declare module NativeClient { | ||||
|     function log(message: string): void; | ||||
|     function returnNative(ret: string): void; | ||||
|     function callNative(name: string, args: string): string; | ||||
|     function fetchArrayBuffer(id: string): string; | ||||
| } | ||||
| declare type RawValue = number | string | boolean | object | undefined; | ||||
| declare type RawValue = number | string | boolean | object | undefined | ArrayBuffer; | ||||
| declare type WrappedValue = { | ||||
|     type: "number" | "string" | "boolean" | "object" | "array" | "null"; | ||||
|     type: "number" | "string" | "boolean" | "object" | "array" | "null" | "arrayBuffer"; | ||||
|     value: RawValue; | ||||
| }; | ||||
| declare function _binaryValue(v: RawValue): { | ||||
|     type: string; | ||||
|     value: number; | ||||
| } | { | ||||
|     type: string; | ||||
|     value: string; | ||||
| } | { | ||||
|     type: string; | ||||
|     value: boolean; | ||||
| } | { | ||||
|     type: string; | ||||
|     value: undefined; | ||||
| }; | ||||
| declare function _arrayBufferToBase64(arrayBuffer: ArrayBuffer): string; | ||||
| declare function _base64ToArrayBuffer(v: string): ArrayBufferLike; | ||||
| declare function _wrappedValue(v: RawValue): WrappedValue; | ||||
| declare function _rawValue(v: WrappedValue): RawValue; | ||||
| declare function __injectGlobalObject(name: string, args: string): void; | ||||
|   | ||||
| @@ -1,40 +1,51 @@ | ||||
| "use strict"; | ||||
| function _binaryValue(v) { | ||||
|     switch (typeof v) { | ||||
|         case "number": | ||||
|             return { | ||||
|                 type: "number", | ||||
|                 value: v | ||||
|             }; | ||||
|         case "string": | ||||
|             return { | ||||
|                 type: "string", | ||||
|                 value: v | ||||
|             }; | ||||
|         case "boolean": | ||||
|             return { | ||||
|                 type: "boolean", | ||||
|                 value: v | ||||
|             }; | ||||
|         case "object": | ||||
|             if (v instanceof Array) { | ||||
|                 return { | ||||
|                     type: "array", | ||||
|                     value: JSON.stringify(v) | ||||
|                 }; | ||||
| function _arrayBufferToBase64(arrayBuffer) { | ||||
|     let base64 = ''; | ||||
|     const encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; | ||||
|     const bytes = new Uint8Array(arrayBuffer); | ||||
|     const byteLength = bytes.byteLength; | ||||
|     const byteRemainder = byteLength % 3; | ||||
|     const mainLength = byteLength - byteRemainder; | ||||
|     let a, b, c, d; | ||||
|     let chunk; | ||||
|     // Main loop deals with bytes in chunks of 3 | ||||
|     for (var i = 0; i < mainLength; i = i + 3) { | ||||
|         // Combine the three bytes into a single integer | ||||
|         chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2]; | ||||
|         // Use bitmasks to extract 6-bit segments from the triplet | ||||
|         a = (chunk & 16515072) >> 18; // 16515072 = (2^6 - 1) << 18 | ||||
|         b = (chunk & 258048) >> 12; // 258048   = (2^6 - 1) << 12 | ||||
|         c = (chunk & 4032) >> 6; // 4032     = (2^6 - 1) << 6 | ||||
|         d = chunk & 63; // 63       = 2^6 - 1 | ||||
|         // Convert the raw binary segments to the appropriate ASCII encoding | ||||
|         base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d]; | ||||
|     } | ||||
|             else { | ||||
|                 return { | ||||
|                     type: "object", | ||||
|                     value: JSON.stringify(v) | ||||
|                 }; | ||||
|     // Deal with the remaining bytes and padding | ||||
|     if (byteRemainder == 1) { | ||||
|         chunk = bytes[mainLength]; | ||||
|         a = (chunk & 252) >> 2; // 252 = (2^6 - 1) << 2 | ||||
|         // Set the 4 least significant bits to zero | ||||
|         b = (chunk & 3) << 4; // 3   = 2^2 - 1 | ||||
|         base64 += encodings[a] + encodings[b] + '=='; | ||||
|     } | ||||
|         default: | ||||
|             return { | ||||
|                 type: "null", | ||||
|                 value: undefined | ||||
|             }; | ||||
|     else if (byteRemainder == 2) { | ||||
|         chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1]; | ||||
|         a = (chunk & 64512) >> 10; // 64512 = (2^6 - 1) << 10 | ||||
|         b = (chunk & 1008) >> 4; // 1008  = (2^6 - 1) << 4 | ||||
|         // Set the 2 least significant bits to zero | ||||
|         c = (chunk & 15) << 2; // 15    = 2^4 - 1 | ||||
|         base64 += encodings[a] + encodings[b] + encodings[c] + '='; | ||||
|     } | ||||
|     return base64; | ||||
| } | ||||
| function _base64ToArrayBuffer(v) { | ||||
|     const binary_string = window.atob(v); | ||||
|     const len = binary_string.length; | ||||
|     const bytes = new Uint8Array(len); | ||||
|     for (let i = 0; i < len; i++) { | ||||
|         bytes[i] = binary_string.charCodeAt(i); | ||||
|     } | ||||
|     return bytes.buffer; | ||||
| } | ||||
| function _wrappedValue(v) { | ||||
|     switch (typeof v) { | ||||
| @@ -54,7 +65,13 @@ function _wrappedValue(v) { | ||||
|                 value: v | ||||
|             }; | ||||
|         case "object": | ||||
|             if (v instanceof Array) { | ||||
|             if (v instanceof ArrayBuffer) { | ||||
|                 return { | ||||
|                     type: "arrayBuffer", | ||||
|                     value: _arrayBufferToBase64(v) | ||||
|                 }; | ||||
|             } | ||||
|             else if (v instanceof Array) { | ||||
|                 return { | ||||
|                     type: "array", | ||||
|                     value: JSON.stringify(v) | ||||
| @@ -87,6 +104,10 @@ function _rawValue(v) { | ||||
|                 return JSON.parse(v.value); | ||||
|             } | ||||
|             return v.value; | ||||
|         case "arrayBuffer": | ||||
|             const data = NativeClient.fetchArrayBuffer(v.value); | ||||
|             return _base64ToArrayBuffer(data); | ||||
|             ; | ||||
|         default: | ||||
|             return undefined; | ||||
|     } | ||||
|   | ||||
| @@ -50,13 +50,11 @@ export class Image extends View { | ||||
|     } | ||||
|     toModel() { | ||||
|         const ret = super.toModel(); | ||||
|         if (Environment.platform === 'iOS') { | ||||
|         if (Reflect.has(ret.props, "imagePixels")) { | ||||
|             const imagePixels = Reflect.get(ret.props, "imagePixels"); | ||||
|             const pixels = imagePixels.pixels; | ||||
|             imagePixels.pixels = this.callback2Id(() => pixels); | ||||
|         } | ||||
|         } | ||||
|         return ret; | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -150,15 +150,13 @@ export class Image extends View { | ||||
|         return this.nativeChannel(context, "getImagePixels")() | ||||
|     } | ||||
|  | ||||
|     toModel() { | ||||
|     toModel(): NativeViewModel { | ||||
|         const ret = super.toModel() | ||||
|         if (Environment.platform === 'iOS') { | ||||
|         if (Reflect.has(ret.props, "imagePixels")) { | ||||
|             const imagePixels = Reflect.get(ret.props, "imagePixels") | ||||
|             const pixels = imagePixels.pixels | ||||
|             imagePixels.pixels = this.callback2Id(() => pixels) | ||||
|         } | ||||
|         } | ||||
|         return ret | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										2
									
								
								doric-web/dist/index.js
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								doric-web/dist/index.js
									
									
									
									
										vendored
									
									
								
							| @@ -3330,13 +3330,11 @@ class Image extends View { | ||||
|     } | ||||
|     toModel() { | ||||
|         const ret = super.toModel(); | ||||
|         if (Environment.platform === 'iOS') { | ||||
|         if (Reflect.has(ret.props, "imagePixels")) { | ||||
|             const imagePixels = Reflect.get(ret.props, "imagePixels"); | ||||
|             const pixels = imagePixels.pixels; | ||||
|             imagePixels.pixels = this.callback2Id(() => pixels); | ||||
|         } | ||||
|         } | ||||
|         return ret; | ||||
|     } | ||||
| } | ||||
|   | ||||
							
								
								
									
										2
									
								
								doric-web/dist/index.js.map
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								doric-web/dist/index.js.map
									
									
									
									
										vendored
									
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
		Reference in New Issue
	
	Block a user