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;
6use std::rc::Rc;
7
8use dom_struct::dom_struct;
9use js::context::JSContext;
10use script_bindings::cell::DomRefCell;
11use script_bindings::reflector::{Reflector, reflect_dom_object_with_cx};
12
13use crate::dom::bindings::callback::ExceptionHandling;
14use crate::dom::bindings::codegen::Bindings::FileSystemDirectoryReaderBinding::{
15    FileSystemDirectoryReaderMethods, FileSystemEntriesCallback,
16};
17use crate::dom::bindings::codegen::Bindings::FileSystemEntryBinding::ErrorCallback;
18use crate::dom::bindings::refcounted::Trusted;
19use crate::dom::bindings::reflector::DomGlobal;
20use crate::dom::bindings::root::{Dom, DomRoot};
21use crate::dom::filesystemdirectoryentry::FileSystemDirectoryEntry;
22use crate::dom::globalscope::GlobalScope;
23
24#[dom_struct]
25pub(crate) struct FileSystemDirectoryReader {
26    reflector_: Reflector,
27    dir: Dom<FileSystemDirectoryEntry>,
28    idx: Cell<usize>,
29    reading_flag: Cell<bool>,
30    done_flag: Cell<bool>,
31    pending_callbacks: DomRefCell<Vec<PendingEntriesCallback>>,
32    next_callback: Cell<usize>,
33}
34
35#[derive(JSTraceable, MallocSizeOf)]
36struct PendingEntriesCallback {
37    id: usize,
38    #[conditional_malloc_size_of]
39    callback: Rc<FileSystemEntriesCallback>,
40}
41
42impl FileSystemDirectoryReader {
43    fn new_inherited(dir: &FileSystemDirectoryEntry) -> FileSystemDirectoryReader {
44        FileSystemDirectoryReader {
45            reflector_: Reflector::new(),
46            dir: Dom::from_ref(dir),
47            idx: Cell::new(0),
48            reading_flag: Cell::new(false),
49            done_flag: Cell::new(false),
50            pending_callbacks: Default::default(),
51            next_callback: Cell::new(0),
52        }
53    }
54
55    pub(crate) fn new(
56        cx: &mut JSContext,
57        global: &GlobalScope,
58        dir: &FileSystemDirectoryEntry,
59    ) -> DomRoot<FileSystemDirectoryReader> {
60        reflect_dom_object_with_cx(
61            Box::new(FileSystemDirectoryReader::new_inherited(dir)),
62            global,
63            cx,
64        )
65    }
66}
67
68impl FileSystemDirectoryReaderMethods<crate::DomTypeHolder> for FileSystemDirectoryReader {
69    /// <https://wicg.github.io/entries-api/#dom-filesystemdirectoryreader-readentries>
70    fn ReadEntries(
71        &self,
72        success_callback: Rc<FileSystemEntriesCallback>,
73        _error_callback: Option<Rc<ErrorCallback>>,
74    ) {
75        // Per spec ยง7.3: queue a task to invoke successCallback with the
76        // directory's children that have not yet been produced. The first
77        // call returns all children; subsequent calls return an empty list
78        // (done flag set).
79        let id = self.next_callback.get();
80        let pending_callback = PendingEntriesCallback {
81            id,
82            callback: success_callback,
83        };
84        self.pending_callbacks.borrow_mut().push(pending_callback);
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                    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}