From 890514a0570a415ca810051596874df1cbd7b6c3 Mon Sep 17 00:00:00 2001 From: Kenneth Myhra Date: Sun, 24 Jul 2022 21:32:18 +0200 Subject: [PATCH] LibWeb: Introduce the File interface from the FileAPI spec --- .../LibWeb/Bindings/WindowObjectHelper.h | 3 + Userland/Libraries/LibWeb/CMakeLists.txt | 1 + Userland/Libraries/LibWeb/FileAPI/Blob.cpp | 5 ++ Userland/Libraries/LibWeb/FileAPI/Blob.h | 3 + Userland/Libraries/LibWeb/FileAPI/File.cpp | 73 +++++++++++++++++++ Userland/Libraries/LibWeb/FileAPI/File.h | 38 ++++++++++ Userland/Libraries/LibWeb/FileAPI/File.idl | 13 ++++ Userland/Libraries/LibWeb/Forward.h | 2 + Userland/Libraries/LibWeb/idl_files.cmake | 1 + 9 files changed, 139 insertions(+) create mode 100644 Userland/Libraries/LibWeb/FileAPI/File.cpp create mode 100644 Userland/Libraries/LibWeb/FileAPI/File.h create mode 100644 Userland/Libraries/LibWeb/FileAPI/File.idl diff --git a/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h b/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h index de38e1c2a6..515c8dbbb1 100644 --- a/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h +++ b/Userland/Libraries/LibWeb/Bindings/WindowObjectHelper.h @@ -87,6 +87,8 @@ #include #include #include +#include +#include #include #include #include @@ -422,6 +424,7 @@ ADD_WINDOW_OBJECT_INTERFACE(ErrorEvent) \ ADD_WINDOW_OBJECT_INTERFACE(Event) \ ADD_WINDOW_OBJECT_INTERFACE(EventTarget) \ + ADD_WINDOW_OBJECT_INTERFACE(File) \ ADD_WINDOW_OBJECT_INTERFACE(Headers) \ ADD_WINDOW_OBJECT_INTERFACE(History) \ ADD_WINDOW_OBJECT_INTERFACE(HTMLAnchorElement) \ diff --git a/Userland/Libraries/LibWeb/CMakeLists.txt b/Userland/Libraries/LibWeb/CMakeLists.txt index aaba562ed3..689d1d40aa 100644 --- a/Userland/Libraries/LibWeb/CMakeLists.txt +++ b/Userland/Libraries/LibWeb/CMakeLists.txt @@ -128,6 +128,7 @@ set(SOURCES Fetch/Infrastructure/HTTP/Responses.cpp Fetch/Infrastructure/HTTP/Statuses.cpp FileAPI/Blob.cpp + FileAPI/File.cpp FontCache.cpp Geometry/DOMRectList.cpp HTML/AttributeNames.cpp diff --git a/Userland/Libraries/LibWeb/FileAPI/Blob.cpp b/Userland/Libraries/LibWeb/FileAPI/Blob.cpp index b473f78e09..bf5ced8dfd 100644 --- a/Userland/Libraries/LibWeb/FileAPI/Blob.cpp +++ b/Userland/Libraries/LibWeb/FileAPI/Blob.cpp @@ -52,6 +52,11 @@ Blob::Blob(ByteBuffer byte_buffer, String type) { } +Blob::Blob(ByteBuffer byte_buffer) + : m_byte_buffer(move(byte_buffer)) +{ +} + // https://w3c.github.io/FileAPI/#ref-for-dom-blob-blob DOM::ExceptionOr> Blob::create(Optional> const& blob_parts, Optional const& options) { diff --git a/Userland/Libraries/LibWeb/FileAPI/Blob.h b/Userland/Libraries/LibWeb/FileAPI/Blob.h index 064e75275d..8ea740b924 100644 --- a/Userland/Libraries/LibWeb/FileAPI/Blob.h +++ b/Userland/Libraries/LibWeb/FileAPI/Blob.h @@ -54,6 +54,9 @@ public: ReadonlyBytes bytes() const { return m_byte_buffer.bytes(); } +protected: + Blob(ByteBuffer byte_buffer); + private: Blob() = default; diff --git a/Userland/Libraries/LibWeb/FileAPI/File.cpp b/Userland/Libraries/LibWeb/FileAPI/File.cpp new file mode 100644 index 0000000000..68d9aebb5f --- /dev/null +++ b/Userland/Libraries/LibWeb/FileAPI/File.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2022, Kenneth Myhra + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include + +namespace Web::FileAPI { + +static bool is_basic_latin(StringView view) +{ + for (auto code_point : view) { + if (code_point < 0x0020 || code_point > 0x007E) + return false; + } + return true; +} + +File::File(ByteBuffer byte_buffer, String file_name, String type, i64 last_modified) + : Blob(move(byte_buffer), move(type)) + , m_name(move(file_name)) + , m_last_modified(last_modified) +{ +} + +// https://w3c.github.io/FileAPI/#ref-for-dom-file-file +DOM::ExceptionOr> File::create(Vector const& file_bits, String const& file_name, Optional const& options) +{ + // 1. Let bytes be the result of processing blob parts given fileBits and options. + auto bytes = TRY_OR_RETURN_OOM(process_blob_parts(file_bits)); + + // 2. Let n be the fileName argument to the constructor. + // NOTE: Underlying OS filesystems use differing conventions for file name; with constructed files, mandating UTF-16 lessens ambiquity when file names are converted to byte sequences. + auto name = file_name; + + auto type = String::empty(); + i64 last_modified = 0; + // 3. Process FilePropertyBag dictionary argument by running the following substeps: + if (options.has_value()) { + // 1. If the type member is provided and is not the empty string, let t be set to the type dictionary member. + // If t contains any characters outside the range U+0020 to U+007E, then set t to the empty string and return from these substeps. + // NOTE: t is set to empty string at declaration. + if (!options->type.is_empty()) { + if (is_basic_latin(options->type)) + type = options->type; + } + + // 2. Convert every character in t to ASCII lowercase. + if (!type.is_empty()) + type = options->type.to_lowercase(); + + // 3. If the lastModified member is provided, let d be set to the lastModified dictionary member. If it is not provided, set d to the current date and time represented as the number of milliseconds since the Unix Epoch (which is the equivalent of Date.now() [ECMA-262]). + // Note: Since ECMA-262 Date objects convert to long long values representing the number of milliseconds since the Unix Epoch, the lastModified member could be a Date object [ECMA-262]. + last_modified = options->last_modified.has_value() ? options->last_modified.value() : Time::now_realtime().to_milliseconds(); + } + + // 4. Return a new File object F such that: + // 2. F refers to the bytes byte sequence. + // NOTE: Spec started at 2 therefore keeping the same number sequence here. + // 3. F.size is set to the number of total bytes in bytes. + // 4. F.name is set to n. + // 5. F.type is set to t. + // 6. F.lastModified is set to d. + return adopt_ref(*new File(move(bytes), move(name), move(type), last_modified)); +} + +DOM::ExceptionOr> File::create_with_global_object(Bindings::WindowObject&, Vector const& file_bits, String const& file_name, Optional const& options) +{ + return create(file_bits, file_name, options); +} + +} diff --git a/Userland/Libraries/LibWeb/FileAPI/File.h b/Userland/Libraries/LibWeb/FileAPI/File.h new file mode 100644 index 0000000000..636a620b32 --- /dev/null +++ b/Userland/Libraries/LibWeb/FileAPI/File.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2022, Kenneth Myhra + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace Web::FileAPI { + +struct FilePropertyBag : BlobPropertyBag { + Optional last_modified; +}; + +class File : public Blob { + +public: + using WrapperType = Bindings::FileWrapper; + + static DOM::ExceptionOr> create(Vector const& file_bits, String const& file_name, Optional const& options = {}); + static DOM::ExceptionOr> create_with_global_object(Bindings::WindowObject&, Vector const& file_bits, String const& file_name, Optional const& options = {}); + + // https://w3c.github.io/FileAPI/#dfn-name + String const& name() const { return m_name; } + // https://w3c.github.io/FileAPI/#dfn-lastModified + i64 last_modified() const { return m_last_modified; } + +private: + File(ByteBuffer byte_buffer, String file_name, String type, i64 last_modified); + + String m_name; + i64 m_last_modified { 0 }; +}; + +} diff --git a/Userland/Libraries/LibWeb/FileAPI/File.idl b/Userland/Libraries/LibWeb/FileAPI/File.idl new file mode 100644 index 0000000000..4f5712fb9e --- /dev/null +++ b/Userland/Libraries/LibWeb/FileAPI/File.idl @@ -0,0 +1,13 @@ +#import + +[Exposed=(Window,Worker), Serializable] +interface File : Blob { + constructor(sequence fileBits, USVString fileName, optional FilePropertyBag options = {}); + + readonly attribute DOMString name; + readonly attribute long long lastModified; +}; + +dictionary FilePropertyBag : BlobPropertyBag { + long long lastModified; +}; diff --git a/Userland/Libraries/LibWeb/Forward.h b/Userland/Libraries/LibWeb/Forward.h index 3decec6049..6836e00911 100644 --- a/Userland/Libraries/LibWeb/Forward.h +++ b/Userland/Libraries/LibWeb/Forward.h @@ -185,6 +185,7 @@ class Response; namespace Web::FileAPI { class Blob; +class File; } namespace Web::Geometry { @@ -474,6 +475,7 @@ class ErrorEventWrapper; class EventListenerWrapper; class EventTargetWrapper; class EventWrapper; +class FileWrapper; class FocusEventWrapper; class HeadersWrapper; class HeadersIteratorWrapper; diff --git a/Userland/Libraries/LibWeb/idl_files.cmake b/Userland/Libraries/LibWeb/idl_files.cmake index 8b0117ef5f..4a5b25691b 100644 --- a/Userland/Libraries/LibWeb/idl_files.cmake +++ b/Userland/Libraries/LibWeb/idl_files.cmake @@ -55,6 +55,7 @@ libweb_js_wrapper(Encoding/TextDecoder) libweb_js_wrapper(Encoding/TextEncoder) libweb_js_wrapper(Fetch/Headers ITERABLE) libweb_js_wrapper(FileAPI/Blob) +libweb_js_wrapper(FileAPI/File) libweb_js_wrapper(Geometry/DOMPoint) libweb_js_wrapper(Geometry/DOMPointReadOnly) libweb_js_wrapper(Geometry/DOMRect)