Skip to main content

script/dom/filesystem/
filesystemdirectoryreader.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::cell::Cell;
6
7use dom_struct::dom_struct;
8use js::context::JSContext;
9use script_bindings::cell::DomRefCell;
10use script_bindings::reflector::{Reflector, reflect_dom_object};
11
12use crate::dom::bindings::callback::{ExceptionHandling, RootedCallback, TracedCallback};
13use crate::dom::bindings::codegen::Bindings::FileSystemDirectoryReaderBinding::{
14    FileSystemDirectoryReaderMethods, FileSystemEntriesCallback,
15};
16use crate::dom::bindings::codegen::Bindings::FileSystemEntryBinding::ErrorCallback;
17use crate::dom::bindings::refcounted::Trusted;
18use crate::dom::bindings::reflector::DomGlobal;
19use crate::dom::bindings::root::{Dom, DomRoot};
20use crate::dom::filesystemdirectoryentry::FileSystemDirectoryEntry;
21use crate::dom::globalscope::GlobalScope;
22
23#[dom_struct]
24pub(crate) struct FileSystemDirectoryReader {
25    reflector_: Reflector,
26    dir: Dom<FileSystemDirectoryEntry>,
27    idx: Cell<usize>,
28    reading_flag: Cell<bool>,
29    done_flag: Cell<bool>,
30    pending_callbacks: DomRefCell<Vec<PendingEntriesCallback>>,
31    next_callback: Cell<usize>,
32}
33
34#[derive(JSTraceable, MallocSizeOf)]
35#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
36struct PendingEntriesCallback {
37    id: usize,
38    callback: TracedCallback<FileSystemEntriesCallback>,
39}
40
41impl FileSystemDirectoryReader {
42    fn new_inherited(dir: &FileSystemDirectoryEntry) -> FileSystemDirectoryReader {
43        FileSystemDirectoryReader {
44            reflector_: Reflector::new(),
45            dir: Dom::from_ref(dir),
46            idx: Cell::new(0),
47            reading_flag: Cell::new(false),
48            done_flag: Cell::new(false),
49            pending_callbacks: Default::default(),
50            next_callback: Cell::new(0),
51        }
52    }
53
54    pub(crate) fn new(
55        cx: &mut JSContext,
56        global: &GlobalScope,
57        dir: &FileSystemDirectoryEntry,
58    ) -> DomRoot<FileSystemDirectoryReader> {
59        reflect_dom_object(
60            cx,
61            Box::new(FileSystemDirectoryReader::new_inherited(dir)),
62            global,
63        )
64    }
65}
66
67impl FileSystemDirectoryReaderMethods<crate::DomTypeHolder> for FileSystemDirectoryReader {
68    /// <https://wicg.github.io/entries-api/#dom-filesystemdirectoryreader-readentries>
69    fn ReadEntries(
70        &self,
71        success_callback: RootedCallback<FileSystemEntriesCallback>,
72        _error_callback: Option<RootedCallback<ErrorCallback>>,
73    ) {
74        // Per spec ยง7.3: queue a task to invoke successCallback with the
75        // directory's children that have not yet been produced. The first
76        // call returns all children; subsequent calls return an empty list
77        // (done flag set).
78        let id = self.next_callback.get();
79        self.pending_callbacks
80            .borrow_mut()
81            .push(PendingEntriesCallback {
82                id,
83                callback: success_callback.to_traced(),
84            });
85        self.next_callback.set(id + 1);
86
87        let this = Trusted::new(self);
88        self.global()
89            .task_manager()
90            .dom_manipulation_task_source()
91            .queue(task!(invoke_read_entries: move |cx| {
92                let this = this.root();
93                let maybe_index = this
94                    .pending_callbacks
95                    .borrow()
96                    .iter()
97                    .position(|val| val.id == id);
98                if let Some(index) = maybe_index {
99                    rooted!(&in(cx) let callback = this
100                        .pending_callbacks
101                        .borrow_mut()
102                        .swap_remove(index)
103                        .callback);
104                    let entries = if this.done_flag.get() {
105                        Vec::new()
106                    } else {
107                        this.done_flag.set(true);
108                        this.dir.children()
109                    };
110                    let _ = callback.Call__(cx, entries, ExceptionHandling::Report);
111                }
112            }));
113    }
114}