/* * Copyright (c) 2024, Andrew Kaster * Copyright (c) 2024, Jamie Mansfield * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include #include namespace Web::CSS { JS_DEFINE_ALLOCATOR(FontFaceSet); // https://drafts.csswg.org/css-font-loading/#dom-fontfaceset-fontfaceset JS::NonnullGCPtr FontFaceSet::construct_impl(JS::Realm& realm, Vector> const& initial_faces) { auto ready_promise = WebIDL::create_promise(realm); auto set_entries = JS::Set::create(realm); // The FontFaceSet constructor, when called, must iterate its initialFaces argument and add each value to its set entries. for (auto const& face : initial_faces) set_entries->set_add(face); if (set_entries->set_size() == 0) WebIDL::resolve_promise(realm, *ready_promise); return realm.heap().allocate(realm, realm, ready_promise, set_entries); } JS::NonnullGCPtr FontFaceSet::create(JS::Realm& realm) { return construct_impl(realm, {}); } FontFaceSet::FontFaceSet(JS::Realm& realm, JS::NonnullGCPtr ready_promise, JS::NonnullGCPtr set_entries) : DOM::EventTarget(realm) , m_set_entries(set_entries) , m_ready_promise(ready_promise) { bool const is_ready = ready()->state() == JS::Promise::State::Fulfilled; m_status = is_ready ? Bindings::FontFaceSetLoadStatus::Loaded : Bindings::FontFaceSetLoadStatus::Loading; } void FontFaceSet::initialize(JS::Realm& realm) { Base::initialize(realm); WEB_SET_PROTOTYPE_FOR_INTERFACE(FontFaceSet); } void FontFaceSet::visit_edges(Cell::Visitor& visitor) { Base::visit_edges(visitor); visitor.visit(m_set_entries); visitor.visit(m_ready_promise); visitor.visit(m_loading_fonts); visitor.visit(m_loaded_fonts); visitor.visit(m_failed_fonts); } // https://drafts.csswg.org/css-font-loading/#dom-fontfaceset-add WebIDL::ExceptionOr> FontFaceSet::add(JS::Handle face) { // 1. If font is already in the FontFaceSet’s set entries, skip to the last step of this algorithm immediately. if (m_set_entries->set_has(face)) return JS::NonnullGCPtr(*this); // 2. If font is CSS-connected, throw an InvalidModificationError exception and exit this algorithm immediately. if (face->is_css_connected()) { return WebIDL::InvalidModificationError::create(realm(), "Cannot add a CSS-connected FontFace to a FontFaceSet"_fly_string); } // 3. Add the font argument to the FontFaceSet’s set entries. m_set_entries->set_add(face); // 4. If font’s status attribute is "loading" if (face->status() == Bindings::FontFaceLoadStatus::Loading) { // 1. If the FontFaceSet’s [[LoadingFonts]] list is empty, switch the FontFaceSet to loading. if (m_loading_fonts.is_empty()) { m_status = Bindings::FontFaceSetLoadStatus::Loading; } // 2. Append font to the FontFaceSet’s [[LoadingFonts]] list. m_loading_fonts.append(*face); } // 5. Return the FontFaceSet. return JS::NonnullGCPtr(*this); } // https://drafts.csswg.org/css-font-loading/#dom-fontfaceset-delete bool FontFaceSet::delete_(JS::Handle face) { // FIXME: Do the actual spec steps return m_set_entries->set_remove(face); } // https://drafts.csswg.org/css-font-loading/#dom-fontfaceset-clear void FontFaceSet::clear() { // FIXME: Do the actual spec steps m_set_entries->set_clear(); } // https://drafts.csswg.org/css-font-loading/#dom-fontfaceset-onloading void FontFaceSet::set_onloading(WebIDL::CallbackType* event_handler) { set_event_handler_attribute(HTML::EventNames::loading, event_handler); } // https://drafts.csswg.org/css-font-loading/#dom-fontfaceset-onloading WebIDL::CallbackType* FontFaceSet::onloading() { return event_handler_attribute(HTML::EventNames::loading); } // https://drafts.csswg.org/css-font-loading/#dom-fontfaceset-onloadingdone void FontFaceSet::set_onloadingdone(WebIDL::CallbackType* event_handler) { set_event_handler_attribute(HTML::EventNames::loadingdone, event_handler); } // https://drafts.csswg.org/css-font-loading/#dom-fontfaceset-onloadingdone WebIDL::CallbackType* FontFaceSet::onloadingdone() { return event_handler_attribute(HTML::EventNames::loadingdone); } // https://drafts.csswg.org/css-font-loading/#dom-fontfaceset-onloadingerror void FontFaceSet::set_onloadingerror(WebIDL::CallbackType* event_handler) { set_event_handler_attribute(HTML::EventNames::loadingerror, event_handler); } // https://drafts.csswg.org/css-font-loading/#dom-fontfaceset-onloadingerror WebIDL::CallbackType* FontFaceSet::onloadingerror() { return event_handler_attribute(HTML::EventNames::loadingerror); } // https://drafts.csswg.org/css-font-loading/#dom-fontfaceset-load JS::ThrowCompletionOr> FontFaceSet::load(String const&, String const&) { // FIXME: Do the steps auto promise = WebIDL::create_rejected_promise(realm(), WebIDL::NotSupportedError::create(realm(), "FontFaceSet::load is not yet implemented"_fly_string)); return verify_cast(*promise->promise()); } // https://drafts.csswg.org/css-font-loading/#font-face-set-ready JS::NonnullGCPtr FontFaceSet::ready() const { return verify_cast(*m_ready_promise->promise()); } }