From bf078814cc0944ece8199d136e298efa0126a6fb Mon Sep 17 00:00:00 2001 From: Fabio Alessandrelli Date: Wed, 19 May 2021 15:51:36 +0200 Subject: [PATCH] [HTML5] Add easy to use download API. New `JavaScript.download_buffer` method to create a prompt that let the user download a file. --- doc/classes/JavaScript.xml | 16 ++++++ platform/javascript/SCsub | 2 - platform/javascript/api/api.cpp | 5 ++ .../javascript/api/javascript_singleton.h | 1 + .../api/javascript_tools_editor_plugin.cpp | 9 ++- platform/javascript/javascript_singleton.cpp | 5 ++ .../js/libs/library_godot_editor_tools.js | 57 ------------------- .../javascript/js/libs/library_godot_os.js | 17 ++++++ 8 files changed, 51 insertions(+), 61 deletions(-) delete mode 100644 platform/javascript/js/libs/library_godot_editor_tools.js diff --git a/doc/classes/JavaScript.xml b/doc/classes/JavaScript.xml index ae49610d2a55..6a515c0d72cf 100644 --- a/doc/classes/JavaScript.xml +++ b/doc/classes/JavaScript.xml @@ -31,6 +31,22 @@ Creates a new JavaScript object using the [code]new[/code] constructor. The [code]object[/code] must a valid property of the JavaScript [code]window[/code]. See [JavaScriptObject] for usage. + + + + + + + + + + + Prompts the user to download a file containing the specified [code]buffer[/code]. The file will have the given [code]name[/code] and [code]mime[/code] type. + [b]Note:[/b] The browser may override the [url=https://en.wikipedia.org/wiki/Media_type]MIME type[/url] provided based on the file [code]name[/code]'s extension. + [b]Note:[/b] Browsers might block the download if [method download_buffer] is not being called from a user interaction (e.g. button click). + [b]Note:[/b] Browsers might ask the user for permission or block the download if multiple download requests are made in a quick succession. + + diff --git a/platform/javascript/SCsub b/platform/javascript/SCsub index 752f9f81e865..4403a1ebd9e4 100644 --- a/platform/javascript/SCsub +++ b/platform/javascript/SCsub @@ -22,8 +22,6 @@ sys_env.AddJSLibraries( ] ) -if env["tools"]: - sys_env.AddJSLibraries(["js/libs/library_godot_editor_tools.js"]) if env["javascript_eval"]: sys_env.AddJSLibraries(["js/libs/library_godot_javascript_singleton.js"]) diff --git a/platform/javascript/api/api.cpp b/platform/javascript/api/api.cpp index 5db96ab8fa3f..b82ddda65cd8 100644 --- a/platform/javascript/api/api.cpp +++ b/platform/javascript/api/api.cpp @@ -70,6 +70,7 @@ void JavaScript::_bind_methods() { mi.arguments.push_back(PropertyInfo(Variant::STRING, "object")); ClassDB::bind_vararg_method(METHOD_FLAGS_DEFAULT, "create_object", &JavaScript::_create_object_bind, mi); } + ClassDB::bind_method(D_METHOD("download_buffer", "buffer", "name", "mime"), &JavaScript::download_buffer, DEFVAL("application/octet-stream")); } #if !defined(JAVASCRIPT_ENABLED) || !defined(JAVASCRIPT_EVAL_ENABLED) @@ -100,3 +101,7 @@ Variant JavaScript::_create_object_bind(const Variant **p_args, int p_argcount, return Ref(); } #endif +#if !defined(JAVASCRIPT_ENABLED) +void JavaScript::download_buffer(Vector p_arr, const String &p_name, const String &p_mime) { +} +#endif diff --git a/platform/javascript/api/javascript_singleton.h b/platform/javascript/api/javascript_singleton.h index 9f383ec9681c..551c7d597c97 100644 --- a/platform/javascript/api/javascript_singleton.h +++ b/platform/javascript/api/javascript_singleton.h @@ -58,6 +58,7 @@ class JavaScript : public Object { Ref get_interface(const String &p_interface); Ref create_callback(Object *p_ref, const StringName &p_method); Variant _create_object_bind(const Variant **p_args, int p_argcount, Variant::CallError &r_error); + void download_buffer(Vector p_arr, const String &p_name, const String &p_mime = "application/octet-stream"); static JavaScript *get_singleton(); JavaScript(); diff --git a/platform/javascript/api/javascript_tools_editor_plugin.cpp b/platform/javascript/api/javascript_tools_editor_plugin.cpp index ba34628b91ff..1284c0559917 100644 --- a/platform/javascript/api/javascript_tools_editor_plugin.cpp +++ b/platform/javascript/api/javascript_tools_editor_plugin.cpp @@ -41,7 +41,7 @@ // JavaScript functions defined in library_godot_editor_tools.js extern "C" { -extern void godot_js_editor_download_file(const char *p_path, const char *p_name, const char *p_mime); +extern int godot_js_os_download_buffer(const uint8_t *p_buf, int p_buf_size, const char *p_name, const char *p_mime); } static void _javascript_editor_init_callback() { @@ -70,7 +70,12 @@ void JavaScriptToolsEditorPlugin::_download_zip(Variant p_v) { String base_path = resource_path.substr(0, resource_path.rfind("/")) + "/"; _zip_recursive(resource_path, base_path, zip); zipClose(zip, NULL); - godot_js_editor_download_file("/tmp/project.zip", "project.zip", "application/zip"); + FileAccess *f = FileAccess::open("/tmp/project.zip", FileAccess::READ); + ERR_FAIL_COND_MSG(!f, "Unable to create zip file"); + Vector buf; + buf.resize(f->get_len()); + f->get_buffer(buf.ptrw(), buf.size()); + godot_js_os_download_buffer(buf.ptr(), buf.size(), "project.zip", "application/zip"); } void JavaScriptToolsEditorPlugin::_bind_methods() { diff --git a/platform/javascript/javascript_singleton.cpp b/platform/javascript/javascript_singleton.cpp index 3897c6d62c80..3bf14128f06b 100644 --- a/platform/javascript/javascript_singleton.cpp +++ b/platform/javascript/javascript_singleton.cpp @@ -302,6 +302,7 @@ union js_eval_ret { }; extern int godot_js_eval(const char *p_js, int p_use_global_ctx, union js_eval_ret *p_union_ptr, void *p_byte_arr, void *p_byte_arr_write, void *(*p_callback)(void *p_ptr, void *p_ptr2, int p_len)); +extern int godot_js_os_download_buffer(const uint8_t *p_buf, int p_buf_size, const char *p_name, const char *p_mime); } void *resize_poolbytearray_and_open_write(void *p_arr, void *r_write, int p_len) { @@ -338,3 +339,7 @@ Variant JavaScript::eval(const String &p_code, bool p_use_global_exec_context) { } #endif // JAVASCRIPT_EVAL_ENABLED + +void JavaScript::download_buffer(Vector p_arr, const String &p_name, const String &p_mime) { + godot_js_os_download_buffer(p_arr.ptr(), p_arr.size(), p_name.utf8().get_data(), p_mime.utf8().get_data()); +} diff --git a/platform/javascript/js/libs/library_godot_editor_tools.js b/platform/javascript/js/libs/library_godot_editor_tools.js deleted file mode 100644 index d7f1ad5ea101..000000000000 --- a/platform/javascript/js/libs/library_godot_editor_tools.js +++ /dev/null @@ -1,57 +0,0 @@ -/*************************************************************************/ -/* library_godot_editor_tools.js */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ - -const GodotEditorTools = { - godot_js_editor_download_file__deps: ['$FS'], - godot_js_editor_download_file__sig: 'viii', - godot_js_editor_download_file: function (p_path, p_name, p_mime) { - const path = GodotRuntime.parseString(p_path); - const name = GodotRuntime.parseString(p_name); - const mime = GodotRuntime.parseString(p_mime); - const size = FS.stat(path)['size']; - const buf = new Uint8Array(size); - const fd = FS.open(path, 'r'); - FS.read(fd, buf, 0, size); - FS.close(fd); - FS.unlink(path); - const blob = new Blob([buf], { type: mime }); - const url = window.URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = name; - a.style.display = 'none'; - document.body.appendChild(a); - a.click(); - a.remove(); - window.URL.revokeObjectURL(url); - }, -}; - -mergeInto(LibraryManager.library, GodotEditorTools); diff --git a/platform/javascript/js/libs/library_godot_os.js b/platform/javascript/js/libs/library_godot_os.js index 1d9f889bce4c..7414b8cc476a 100644 --- a/platform/javascript/js/libs/library_godot_os.js +++ b/platform/javascript/js/libs/library_godot_os.js @@ -304,6 +304,23 @@ const GodotOS = { godot_js_os_hw_concurrency_get: function () { return navigator.hardwareConcurrency || 1; }, + + godot_js_os_download_buffer__sig: 'viiii', + godot_js_os_download_buffer: function (p_ptr, p_size, p_name, p_mime) { + const buf = GodotRuntime.heapSlice(HEAP8, p_ptr, p_size); + const name = GodotRuntime.parseString(p_name); + const mime = GodotRuntime.parseString(p_mime); + const blob = new Blob([buf], { type: mime }); + const url = window.URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = name; + a.style.display = 'none'; + document.body.appendChild(a); + a.click(); + a.remove(); + window.URL.revokeObjectURL(url); + }, }; autoAddDeps(GodotOS, '$GodotOS');