diff --git a/src/js/protocols/WebSerial.js b/src/js/protocols/WebSerial.js index 3fd076f7..48354ec6 100644 --- a/src/js/protocols/WebSerial.js +++ b/src/js/protocols/WebSerial.js @@ -334,18 +334,26 @@ class WebSerial extends EventTarget { // AT32 on macOS requires smaller chunks (63 bytes) to work correctly due to // USB buffer size limitations in the macOS implementation const batchWriteSize = 63; - let remainingData = data; - while (remainingData.byteLength > batchWriteSize) { - const sliceData = remainingData.slice(0, batchWriteSize); - remainingData = remainingData.slice(batchWriteSize); + + // Ensure data is a Uint8Array for proper slicing + const dataArray = data instanceof Uint8Array ? data : new Uint8Array(data); + + let offset = 0; + while (offset + batchWriteSize < dataArray.byteLength) { + const chunk = dataArray.slice(offset, offset + batchWriteSize); + offset += batchWriteSize; try { - await this.writer.write(sliceData); + await this.writer.write(chunk); } catch (error) { console.error(`${logHead} Error writing batch chunk:`, error); throw error; // Re-throw to be caught by the send method } } - await this.writer.write(remainingData); + + // Write the remaining data + if (offset < dataArray.byteLength) { + await this.writer.write(dataArray.slice(offset)); + } } async send(data, callback) { @@ -358,14 +366,17 @@ class WebSerial extends EventTarget { } try { - if (this.isNeedBatchWrite) { - await this.batchWrite(data); - } else { - await this.writer.write(data); - } - this.bytesSent += data.byteLength; + // Create a buffer from the data + const buffer = data instanceof ArrayBuffer ? data : new Uint8Array(data).buffer; - const result = { bytesSent: data.byteLength }; + if (this.isNeedBatchWrite) { + await this.batchWrite(buffer); + } else { + await this.writer.write(new Uint8Array(buffer)); + } + this.bytesSent += buffer.byteLength; + + const result = { bytesSent: buffer.byteLength }; if (callback) { callback(result); } diff --git a/src/js/tabs/setup.js b/src/js/tabs/setup.js index ed962351..aec4ccf2 100644 --- a/src/js/tabs/setup.js +++ b/src/js/tabs/setup.js @@ -14,6 +14,7 @@ import $ from "jquery"; import { ispConnected } from "../utils/connection"; import { sensorTypes } from "../sensor_types"; import { addArrayElementsAfter, replaceArrayElement } from "../utils/array"; +import { isFirefoxBrowser } from "../utils/checkBrowserCompatibility"; const setup = { yaw_fix: 0.0, @@ -387,17 +388,19 @@ setup.initialize = function (callback) { const buildConfig = ` - ${i18n.getMessage( - "initialSetupInfoBuildConfig", -)}`; + + ${i18n.getMessage("initialSetupInfoBuildConfig")} + + `; // Creates the "Log" button const buildLog = ` - ${i18n.getMessage( - "initialSetupInfoBuildLog", -)}`; + + ${i18n.getMessage("initialSetupInfoBuildLog")} + + `; // Shows the "Config" and "Log" buttons build_info_e.html(`${buildConfig} ${buildLog}`); @@ -429,19 +432,19 @@ setup.initialize = function (callback) { // Creates the "Options" button (if possible) const buildOptions = buildOptionsValid ? ` - ${i18n.getMessage( - "initialSetupInfoBuildOptions", - )}` + + ${i18n.getMessage("initialSetupInfoBuildOptions")} + + ` : ""; // Creates the "Download" button (if possible) const buildDownload = buildKeyValid - ? ` - ${i18n.getMessage( - "initialSetupInfoBuildDownload", -)}` + ? ` + + ${i18n.getMessage("initialSetupInfoBuildDownload")} + + ` : ""; // Shows the "Options" and/or "Download" buttons @@ -493,13 +496,14 @@ setup.initialize = function (callback) { } function showNetworkStatus() { + const isFirefox = isFirefoxBrowser(); const networkStatus = ispConnected(); let statusText = ""; - const type = navigator.connection.effectiveType; - const downlink = navigator.connection.downlink; - const rtt = navigator.connection.rtt; + const type = isFirefox ? "NA" : navigator.connection.effectiveType; + const downlink = isFirefox ? "NA" : navigator.connection.downlink; + const rtt = isFirefox ? "NA" : navigator.connection.rtt; if (!networkStatus || !navigator.onLine || type === "none") { statusText = i18n.getMessage("initialSetupNetworkInfoStatusOffline"); @@ -510,9 +514,9 @@ setup.initialize = function (callback) { } $(".network-status").text(statusText); - $(".network-type").text(navigator.connection.effectiveType); - $(".network-downlink").text(`${navigator.connection.downlink} Mbps`); - $(".network-rtt").text(navigator.connection.rtt); + $(".network-type").text(type); + $(".network-downlink").text(`${downlink} Mbps`); + $(".network-rtt").text(rtt); } prepareDisarmFlags(); diff --git a/src/js/utils/checkBrowserCompatibility.js b/src/js/utils/checkBrowserCompatibility.js index a1a34411..5a49eacd 100644 --- a/src/js/utils/checkBrowserCompatibility.js +++ b/src/js/utils/checkBrowserCompatibility.js @@ -28,15 +28,26 @@ export function getOS() { } export function isChromiumBrowser() { - if (navigator.userAgentData) { - return navigator.userAgentData.brands.some((brand) => { - return brand.brand == "Chromium"; - }); + // https://developer.mozilla.org/en-US/docs/Web/HTTP/Browser_detection_using_the_user_agent + if (!navigator.userAgentData) { + // Fallback to traditional userAgent string check + return /Chrome/.test(navigator.userAgent) && /Google Inc/.test(navigator.vendor); } - // Fallback for older browsers/Android - const ua = navigator.userAgent.toLowerCase(); - return ua.includes("chrom") || ua.includes("edg"); + // https://learn.microsoft.com/en-us/microsoft-edge/web-platform/user-agent-guidance + return navigator.userAgentData.brands.some((brand) => { + return brand.brand == "Chromium"; + }); +} + +export function isFirefoxBrowser() { + if (navigator.userAgentData) { + return navigator.userAgentData.brands.some((brand) => { + return brand.brand == "Firefox"; + }); + } + // Fallback to traditional userAgent string check + return navigator.userAgent.includes("Firefox"); } export function isAndroid() { @@ -65,15 +76,18 @@ export function checkBrowserCompatibility() { const isWebBluetooth = checkWebBluetoothSupport(); const isWebUSB = checkWebUSBSupport(); const isChromium = isChromiumBrowser(); - + const isFirefox = isFirefoxBrowser(); const isNative = Capacitor.isNativePlatform(); - const compatible = isNative || (isChromium && (isWebSerial || isWebBluetooth || isWebUSB)); + const compatible = isNative || ((isChromium || isFirefox) && (isWebSerial || isWebBluetooth || isWebUSB)); console.log("User Agent: ", navigator.userAgentData); console.log("Native: ", isNative); console.log("Chromium: ", isChromium); + console.log("Firefox: ", isFirefox); console.log("Web Serial: ", isWebSerial); + console.log("Web Bluetooth: ", isWebBluetooth); + console.log("Web USB: ", isWebUSB); console.log("OS: ", getOS()); console.log("Android: ", isAndroid()); @@ -86,7 +100,8 @@ export function checkBrowserCompatibility() { let errorMessage = ""; if (!isChromium) { - errorMessage = "Betaflight app requires a Chromium based browser (Chrome, Chromium, Edge).
"; + errorMessage = + "Betaflight app requires a Chromium based browser (Chrome, Chromium, Edge),
or Firefox based browser running the WebSerial extension.
"; } if (!isWebBluetooth) {