diff --git a/.eslintrc.js b/.eslintrc.js index f11ecc92d0..227bb68b4a 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -74,6 +74,8 @@ module.exports = { "$": false, "brackets": false, "Phoenix": false, + "iconv": false, + "Buffer": true, "clearTimeout": false, "console": false, "define": false, diff --git a/docs/generatedApiDocs/NodeConnector-API.md b/docs/generatedApiDocs/NodeConnector-API.md index a1a7f34683..73b9b6fd96 100644 --- a/docs/generatedApiDocs/NodeConnector-API.md +++ b/docs/generatedApiDocs/NodeConnector-API.md @@ -167,7 +167,7 @@ Returns **[boolean][5]** Returns true if Node.js Engine is available. Terminate the PhNodeEngine node if it is available. Else does nothing. -Returns **void** +Returns **[Promise][6]** promise that resolves when node process is terminated and exits. ## setInspectEnabled @@ -187,7 +187,7 @@ Returns **[boolean][5]** True if inspect mode is enabled, false otherwise. Retrieves the node inspector port for the Phoenix Node.js engine. -Returns **[number][6]** The inspection port number. +Returns **[number][7]** The inspection port number. [1]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String @@ -199,4 +199,6 @@ Returns **[number][6]** The inspection port number. [5]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean -[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number +[6]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise + +[7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number diff --git a/src-node/index.js b/src-node/index.js index 04bcdddebc..6aca6df6f9 100644 --- a/src-node/index.js +++ b/src-node/index.js @@ -65,6 +65,7 @@ const net = require('net'); const PhoenixFS = require('@phcode/fs/dist/phoenix-fs'); const NodeConnector = require("./node-connector"); require("./test-connection"); +require("./utils"); function randomNonce(byteLength) { const randomBuffer = new Uint8Array(byteLength); crypto.getRandomValues(randomBuffer); diff --git a/src-node/test-connection.js b/src-node/test-connection.js index dbb71ad04d..b79302ef0b 100644 --- a/src-node/test-connection.js +++ b/src-node/test-connection.js @@ -185,7 +185,7 @@ function awaits(waitTimeMs){ }); } -exports.testDelayedNodeConnectorCreateExec = async function (connectorName) { +exports.testDelayedNodeConnectorCreateExec = async function () { const newNodeConnName = "node_exec_Q_test"; const newExport = {}; const newNodeConn = NodeConnector.createNodeConnector(newNodeConnName, newExport); @@ -201,7 +201,7 @@ exports.testDelayedNodeConnectorCreateExec = async function (connectorName) { expectEqual(result, expectedResult); }; -exports.testDelayedNodeConnectorCreateEvent = async function (connectorName) { +exports.testDelayedNodeConnectorCreateEvent = async function () { const newNodeConnName = "node_event_Q_test"; const newExport = {}; const newNodeConn = NodeConnector.createNodeConnector(newNodeConnName, newExport); diff --git a/src-node/utils.js b/src-node/utils.js new file mode 100644 index 0000000000..51d927ff92 --- /dev/null +++ b/src-node/utils.js @@ -0,0 +1,23 @@ +const NodeConnector = require("./node-connector"); + +const UTILS_NODE_CONNECTOR = "ph_utils"; +NodeConnector.createNodeConnector(UTILS_NODE_CONNECTOR, exports); + +async function getURLContent({url, options}) { + options = options || { + redirect: "follow", + headers: { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/98.0.4758.102 Safari/537.36", + Host: "github.com", + Referer: "https://github.com/", + "Cache-Control": "no-cache" + } + }; + const fetchResponse = await fetch(url, options); + const bufferContents = await fetchResponse.arrayBuffer(); + return { + buffer: bufferContents + }; +} + +exports.getURLContent = getURLContent; diff --git a/src/brackets.js b/src/brackets.js index a3594ad321..6060ce166f 100644 --- a/src/brackets.js +++ b/src/brackets.js @@ -123,6 +123,7 @@ define(function (require, exports, module) { require("LiveDevelopment/main"); require("utils/NodeConnection"); require("utils/NodeDomain"); + require("utils/NodeUtils"); require("utils/ColorUtils"); require("view/ThemeManager"); require("thirdparty/lodash"); diff --git a/src/filesystem/RemoteFile.js b/src/filesystem/RemoteFile.js index 7dbeeb1264..31ebc4ac66 100644 --- a/src/filesystem/RemoteFile.js +++ b/src/filesystem/RemoteFile.js @@ -22,10 +22,11 @@ define(function (require, exports, module) { - var FileSystemError = require("filesystem/FileSystemError"), - FileSystemStats = require("filesystem/FileSystemStats"); + const FileSystemError = require("filesystem/FileSystemError"), + FileSystemStats = require("filesystem/FileSystemStats"), + NodeUtils = require("utils/NodeUtils"); - var SESSION_START_TIME = new Date(); + const SESSION_START_TIME = new Date(); /** * Create a new file stat. See the FileSystemStats class for more details. @@ -164,19 +165,41 @@ define(function (require, exports, module) { // no-op }; + function _nodeConnectorRead(url, encoding, successCB, errorCB) { + NodeUtils.fetchURLText(url, encoding) + .then(successCB) + .catch(err=>{ + console.error("failed fetch url: ", url, err); + errorCB(err); + }); + } + + function isTauriResource(url) { + const startingURLs = [ + "phtauri://", "https://phtauri.localhost", "asset://", "https://asset.localhost", + "tauri://", "https://tauri.localhost" + ]; + for(let start of startingURLs){ + if(url.startsWith(start)){ + return true; + } + } + return false; + } + function _remoteRead(url, encoding, successCB, errorCB) { + if(Phoenix.browser.isTauri && !isTauriResource(url)) { + _nodeConnectorRead(url, encoding, successCB, errorCB); + return; + } let xmlhttp = new XMLHttpRequest(); xmlhttp.open("GET", url, true); xmlhttp.responseType = "arraybuffer"; xmlhttp.onload = function(oEvent) { - var arrayBuffer = xmlhttp.response; - - // if you want to access the bytes: - var byteArray = new Uint8Array(arrayBuffer); - + const arrayBuffer = xmlhttp.response; try { - successCB(new TextDecoder(encoding).decode(byteArray)); + successCB(iconv.decode(Buffer.from(arrayBuffer), encoding)); } catch (err) { errorCB(err); } diff --git a/src/utils/Global.js b/src/utils/Global.js index edd1bb1597..704da936cc 100644 --- a/src/utils/Global.js +++ b/src/utils/Global.js @@ -63,15 +63,6 @@ define(function (require, exports, module) { console.log(err); } - // Uncomment the following line to force all low level file i/o routines to complete - // asynchronously. This should only be done for testing/debugging. - // NOTE: Make sure this line is commented out again before committing! - //brackets.forceAsyncCallbacks = true; - - // Load native shell when brackets is run in a native shell rather than the browser - // TODO: (issue #266) load conditionally - global.brackets.shellAPI = require("utils/ShellAPI"); - global.brackets.nativeMenus = false; // Locale-related APIs diff --git a/src/utils/NodeUtils.js b/src/utils/NodeUtils.js new file mode 100644 index 0000000000..15dff9133f --- /dev/null +++ b/src/utils/NodeUtils.js @@ -0,0 +1,41 @@ +/* + * GNU AGPL-3.0 License + * + * Copyright (c) 2021 - present core.ai . All rights reserved. + * Original work Copyright (c) 2012 - 2021 Adobe Systems Incorporated. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License + * for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see https://opensource.org/licenses/AGPL-3.0. + * + */ + +/** + * Generic node util APIs connector. see `src-node/utils.js` for node peer + */ + +define(function (require, exports, module) { + if(!Phoenix.browser.isTauri) { + // node not available in browser builds, return + return; + } + const UTILS_NODE_CONNECTOR = "ph_utils"; + const NodeConnector = require('NodeConnector'); + const utilsConnector = NodeConnector.createNodeConnector(UTILS_NODE_CONNECTOR, exports); + + async function fetchURLText(url, encoding) { + const {buffer} = await utilsConnector.execPeer("getURLContent", {url}); + return iconv.decode(Buffer.from(buffer), encoding); + } + + exports.fetchURLText = fetchURLText; +}); diff --git a/src/utils/ShellAPI.js b/src/utils/ShellAPI.js deleted file mode 100644 index c7f7f51fac..0000000000 --- a/src/utils/ShellAPI.js +++ /dev/null @@ -1,74 +0,0 @@ -/* - * GNU AGPL-3.0 License - * - * Copyright (c) 2021 - present core.ai . All rights reserved. - * Original work Copyright (c) 2012 - 2021 Adobe Systems Incorporated. All rights reserved. - * - * This program is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License - * for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see https://opensource.org/licenses/AGPL-3.0. - * - */ - - /** - * This is JavaScript API exposed to the native shell when Brackets is run in a native shell rather than a browser. - */ -define(function (require, exports, module) { - - - // Load dependent modules - var AppInit = require("utils/AppInit"), - CommandManager = require("command/CommandManager"), - Commands = require("command/Commands"); - - var appReady = false; // Set to true after app is fully initialized - - /** - * The native function BracketsShellAPI::DispatchBracketsJSCommand calls this function in order to enable - * calling Brackets commands from the native shell. - */ - function executeCommand(eventName) { - // Temporary fix for #2616 - don't execute the command if a modal dialog is open. - // This should really be fixed with proper menu enabling. - if ($(".modal.instance").length || !appReady) { - // Another hack to fix issue #3219 so that all test windows are closed - // as before the fix for #3152 has been introduced. isBracketsTestWindow - // property is explicitly set in createTestWindowAndRun() in SpecRunnerUtils.js. - if (window.isBracketsTestWindow) { - return false; - } - // Return false for all commands except file.close_window command for - // which we have to return true (issue #3152). - return (eventName === Commands.FILE_CLOSE_WINDOW); - } - - // Use E for Error so that uglify doesn't change this to simply Error() - var promise, E = Error, e = new E(), stackDepth = e.stack.split("\n").length; - - // This function should *only* be called as a top-level function. If the current - // stack depth is > 2, it is most likely because we are at a breakpoint. - if (stackDepth < 3) { - promise = CommandManager.execute(eventName); - } else { - console.error("Skipping command " + eventName + " because it looks like you are " + - "at a breakpoint. If you are NOT at a breakpoint, please " + - "file a bug and mention this comment. Stack depth = " + stackDepth + "."); - } - return (promise && promise.state() === "rejected") ? false : true; - } - - AppInit.appReady(function () { - appReady = true; - }); - - exports.executeCommand = executeCommand; -}); diff --git a/test/SpecRunner.js b/test/SpecRunner.js index f3b363d366..fa6721c43f 100644 --- a/test/SpecRunner.js +++ b/test/SpecRunner.js @@ -199,6 +199,7 @@ define(function (require, exports, module) { require("utils/Global"); require("command/Menus"); require("utils/NodeDomain"); + require("utils/NodeUtils"); require("utils/ColorUtils"); require("preferences/PreferencesBase"); require("JSUtils/Session");