diff --git a/WKWebViewRTC/Classes/WKWebViewRTC.swift b/WKWebViewRTC/Classes/WKWebViewRTC.swift index 4c77151..dc9f246 100644 --- a/WKWebViewRTC/Classes/WKWebViewRTC.swift +++ b/WKWebViewRTC/Classes/WKWebViewRTC.swift @@ -626,7 +626,17 @@ public class WKWebViewRTC : NSObject { let pcId = command.argument(at: 0) as! Int let dcId = command.argument(at: 1) as! Int - let data = command.argument(at: 2) as! Data + let dataArg = command.argument(at: 2); + var data: Data? + if let base64 = dataArg as? String { + data = Data(base64Encoded: base64) + } else if let rawData = dataArg as? Data { + data = rawData; + } + guard let data = data else { + NSLog("WKWebViewRTC#RTCPeerConnection_RTCDataChannel_sendBinary() | ERROR: data argument is invalid") + return; + } let pluginRTCPeerConnection = self.pluginRTCPeerConnections[pcId] if pluginRTCPeerConnection == nil { diff --git a/WKWebViewRTC/Js/jsWKWebViewRTC.js b/WKWebViewRTC/Js/jsWKWebViewRTC.js index 5520f0d..95fbf6f 100644 --- a/WKWebViewRTC/Js/jsWKWebViewRTC.js +++ b/WKWebViewRTC/Js/jsWKWebViewRTC.js @@ -308,7 +308,7 @@ function setup(env) { /** * Selects a color for a debug namespace - * @param {String} namespace The namespace string for the for the debug instance to be colored + * @param {String} namespace The namespace string for the debug instance to be colored * @return {Number|String} An ANSI color code for the given namespace * @api private */ @@ -334,6 +334,8 @@ function setup(env) { function createDebug(namespace) { let prevTime; let enableOverride = null; + let namespacesCache; + let enabledCache; function debug(...args) { // Disabled? @@ -394,7 +396,17 @@ function setup(env) { Object.defineProperty(debug, 'enabled', { enumerable: true, configurable: false, - get: () => enableOverride === null ? createDebug.enabled(namespace) : enableOverride, + get: () => { + if (enableOverride !== null) { + return enableOverride; + } + if (namespacesCache !== createDebug.namespaces) { + namespacesCache = createDebug.namespaces; + enabledCache = createDebug.enabled(namespace); + } + + return enabledCache; + }, set: v => { enableOverride = v; } @@ -423,6 +435,7 @@ function setup(env) { */ function enable(namespaces) { createDebug.save(namespaces); + createDebug.namespaces = namespaces; createDebug.names = []; createDebug.skips = []; @@ -2743,6 +2756,15 @@ var debugerror.log = console.warn.bind(console); +function str2ab(base64) { + const binaryString = window.atob(base64); + const len = binaryString.length; + const bytes = new Uint8Array(len); + for (var i = 0; i < len; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + return bytes.buffer; +} function RTCDataChannel(peerConnection, label, options, dataFromEvent) { var self = this; @@ -2815,16 +2837,25 @@ function RTCDataChannel(peerConnection, label, options, dataFromEvent) { exec.execNative(onResultOK, null, 'WKWebViewRTC', 'RTCPeerConnection_RTCDataChannel_setListener', [this.peerConnection.pcId, this.dcId]); } - function onResultOK(data) { + function dataToEvent(data) { if (data.type) { - onEvent.call(self, data); - // Special handler for received binary mesage. - } else { - onEvent.call(self, { + return data; + } + if (data.Type === 'ArrayBuffer') { + return { type: 'message', - message: data - }); + message: str2ab(data.Data) + } } + return { + type: 'message', + message + } + } + + function onResultOK(data) { + const event = dataToEvent(data); + onEvent.call(self, event); } } @@ -2843,22 +2874,20 @@ Object.defineProperty(RTCDataChannel.prototype, 'binaryType', { } }); - -RTCDataChannel.prototype.send = function (data) { - if (isClosed.call(this) || this.readyState !== 'open') { - return; - } - - debug('send() | [data:%o]', data); - - if (!data) { - return; +function ab2str(arrayBuffer) { + let binary_string = '' + bytes = new Uint8Array(arrayBuffer); + for (let i = 0; i < bytes.byteLength; i++) { + binary_string += String.fromCharCode(bytes[i]); } + const base64String = window.btoa(binary_string); + return base64String; +} - if (typeof data === 'string' || data instanceof String) { - exec.execNative(null, null, 'WKWebViewRTC', 'RTCPeerConnection_RTCDataChannel_sendString', [this.peerConnection.pcId, this.dcId, data]); - } else if (window.ArrayBuffer && data instanceof window.ArrayBuffer) { - exec.execNative(null, null, 'WKWebViewRTC', 'RTCPeerConnection_RTCDataChannel_sendBinary', [this.peerConnection.pcId, this.dcId, data]); +function getStringRepresentation(data) { + let buffer; + if (window.ArrayBuffer && data instanceof window.ArrayBuffer) { + buffer = data; } else if ( (window.Int8Array && data instanceof window.Int8Array) || (window.Uint8Array && data instanceof window.Uint8Array) || @@ -2871,9 +2900,29 @@ RTCDataChannel.prototype.send = function (data) { (window.Float64Array && data instanceof window.Float64Array) || (window.DataView && data instanceof window.DataView) ) { - exec.execNative(null, null, 'WKWebViewRTC', 'RTCPeerConnection_RTCDataChannel_sendBinary', [this.peerConnection.pcId, this.dcId, data.buffer]); + buffer = data.buffer; + } + if (!buffer) throw new Error('Invalid data type'); + + return ab2str(buffer); +} + +RTCDataChannel.prototype.send = function (data) { + if (isClosed.call(this) || this.readyState !== 'open') { + return; + } + + debug('send() | [data:%o]', data); + + if (!data) { + return; + } + + if (typeof data === 'string' || data instanceof String) { + exec.execNative(null, null, 'WKWebViewRTC', 'RTCPeerConnection_RTCDataChannel_sendString', [this.peerConnection.pcId, this.dcId, data]); } else { - throw new Error('invalid data type'); + const stringifiedData = getStringRepresentation(data); + exec.execNative(null, null, 'WKWebViewRTC', 'RTCPeerConnection_RTCDataChannel_sendBinary', [this.peerConnection.pcId, this.dcId, stringifiedData]); } }; diff --git a/WKWebViewRTC/Js/src/RTCDataChannel.js b/WKWebViewRTC/Js/src/RTCDataChannel.js index 5eb348a..91d4a68 100644 --- a/WKWebViewRTC/Js/src/RTCDataChannel.js +++ b/WKWebViewRTC/Js/src/RTCDataChannel.js @@ -26,6 +26,15 @@ var debugerror.log = console.warn.bind(console); +function str2ab(base64) { + const binaryString = window.atob(base64); + const len = binaryString.length; + const bytes = new Uint8Array(len); + for (var i = 0; i < len; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + return bytes.buffer; +} function RTCDataChannel(peerConnection, label, options, dataFromEvent) { var self = this; @@ -98,17 +107,26 @@ function RTCDataChannel(peerConnection, label, options, dataFromEvent) { exec.execNative(onResultOK, null, 'WKWebViewRTC', 'RTCPeerConnection_RTCDataChannel_setListener', [this.peerConnection.pcId, this.dcId]); } - function onResultOK(data) { + function dataToEvent(data) { if (data.type) { - onEvent.call(self, data); - // Special handler for received binary mesage. - } else { - onEvent.call(self, { + return data; + } + if (data.Type === 'ArrayBuffer') { + return { type: 'message', - message: data - }); + message: str2ab(data.Data) + } + } + return { + type: 'message', + message } } + + function onResultOK(data) { + const event = dataToEvent(data); + onEvent.call(self, event); + } } RTCDataChannel.prototype = Object.create(EventTarget.prototype); @@ -126,22 +144,20 @@ Object.defineProperty(RTCDataChannel.prototype, 'binaryType', { } }); - -RTCDataChannel.prototype.send = function (data) { - if (isClosed.call(this) || this.readyState !== 'open') { - return; - } - - debug('send() | [data:%o]', data); - - if (!data) { - return; +function ab2str(arrayBuffer) { + let binary_string = '' + bytes = new Uint8Array(arrayBuffer); + for (let i = 0; i < bytes.byteLength; i++) { + binary_string += String.fromCharCode(bytes[i]); } + const base64String = window.btoa(binary_string); + return base64String; +} - if (typeof data === 'string' || data instanceof String) { - exec.execNative(null, null, 'WKWebViewRTC', 'RTCPeerConnection_RTCDataChannel_sendString', [this.peerConnection.pcId, this.dcId, data]); - } else if (window.ArrayBuffer && data instanceof window.ArrayBuffer) { - exec.execNative(null, null, 'WKWebViewRTC', 'RTCPeerConnection_RTCDataChannel_sendBinary', [this.peerConnection.pcId, this.dcId, data]); +function getStringRepresentation(data) { + let buffer; + if (window.ArrayBuffer && data instanceof window.ArrayBuffer) { + buffer = data; } else if ( (window.Int8Array && data instanceof window.Int8Array) || (window.Uint8Array && data instanceof window.Uint8Array) || @@ -154,9 +170,29 @@ RTCDataChannel.prototype.send = function (data) { (window.Float64Array && data instanceof window.Float64Array) || (window.DataView && data instanceof window.DataView) ) { - exec.execNative(null, null, 'WKWebViewRTC', 'RTCPeerConnection_RTCDataChannel_sendBinary', [this.peerConnection.pcId, this.dcId, data.buffer]); + buffer = data.buffer; + } + if (!buffer) throw new Error('Invalid data type'); + + return ab2str(buffer); +} + +RTCDataChannel.prototype.send = function (data) { + if (isClosed.call(this) || this.readyState !== 'open') { + return; + } + + debug('send() | [data:%o]', data); + + if (!data) { + return; + } + + if (typeof data === 'string' || data instanceof String) { + exec.execNative(null, null, 'WKWebViewRTC', 'RTCPeerConnection_RTCDataChannel_sendString', [this.peerConnection.pcId, this.dcId, data]); } else { - throw new Error('invalid data type'); + const stringifiedData = getStringRepresentation(data); + exec.execNative(null, null, 'WKWebViewRTC', 'RTCPeerConnection_RTCDataChannel_sendBinary', [this.peerConnection.pcId, this.dcId, stringifiedData]); } };