1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use std::cmp::{max, min};
use std::ops::Range;
use std::path::PathBuf;
use std::time::SystemTime;

use embedder_traits::FilterPattern;
use ipc_channel::ipc::IpcSender;
use malloc_size_of_derive::MallocSizeOf;
use num_traits::ToPrimitive;
use serde::{Deserialize, Serialize};
use uuid::Uuid;

use crate::blob_url_store::{BlobBuf, BlobURLStoreError};

// HACK: Not really process-safe now, we should send Origin
//       directly instead of this in future, blocked on #11722
/// File manager store entry's origin
pub type FileOrigin = String;

/// A token modulating access to a file for a blob URL.
pub enum FileTokenCheck {
    /// Checking against a token not required,
    /// used for accessing a file
    /// that isn't linked to from a blob URL.
    NotRequired,
    /// Checking against token required.
    Required(Uuid),
    /// Request should always fail,
    /// used for cases when a check is required,
    /// but no token could be acquired.
    ShouldFail,
}

/// Relative slice positions of a sequence,
/// whose semantic should be consistent with (start, end) parameters in
/// <https://w3c.github.io/FileAPI/#dfn-slice>
#[derive(Clone, Debug, Deserialize, MallocSizeOf, Serialize)]
pub struct RelativePos {
    /// Relative to first byte if non-negative,
    /// relative to one past last byte if negative,
    pub start: i64,
    /// Relative offset from first byte if Some(non-negative),
    /// relative to one past last byte if Some(negative),
    /// None if one past last byte
    pub end: Option<i64>,
}

impl RelativePos {
    /// Full range from start to end
    pub fn full_range() -> RelativePos {
        RelativePos {
            start: 0,
            end: None,
        }
    }

    /// Instantiate optional slice position parameters
    pub fn from_opts(start: Option<i64>, end: Option<i64>) -> RelativePos {
        RelativePos {
            start: start.unwrap_or(0),
            end,
        }
    }

    /// Slice the inner sliced range by repositioning
    pub fn slice_inner(&self, rel_pos: &RelativePos) -> RelativePos {
        RelativePos {
            start: self.start + rel_pos.start,
            end: match (self.end, rel_pos.end) {
                (Some(old_end), Some(rel_end)) => Some(old_end + rel_end),
                (old, None) => old,
                (None, rel) => rel,
            },
        }
    }

    /// Compute absolute range by giving the total size
    /// <https://w3c.github.io/FileAPI/#slice-method-algo>
    pub fn to_abs_range(&self, size: usize) -> Range<usize> {
        let size = size as i64;

        let start = {
            if self.start < 0 {
                max(size + self.start, 0)
            } else {
                min(self.start, size)
            }
        };

        let end = match self.end {
            Some(rel_end) => {
                if rel_end < 0 {
                    max(size + rel_end, 0)
                } else {
                    min(rel_end, size)
                }
            },
            None => size,
        };

        let span: i64 = max(end - start, 0);

        Range {
            start: start.to_usize().unwrap(),
            end: (start + span).to_usize().unwrap(),
        }
    }
}

/// Response to file selection request
#[derive(Debug, Deserialize, Serialize)]
pub struct SelectedFile {
    pub id: Uuid,
    pub filename: PathBuf,
    pub modified: SystemTime,
    pub size: u64,
    // https://w3c.github.io/FileAPI/#dfn-type
    pub type_string: String,
}

#[derive(Debug, Deserialize, Serialize)]
pub enum FileManagerThreadMsg {
    /// Select a single file. Last field is pre-selected file path for testing
    SelectFile(
        Vec<FilterPattern>,
        IpcSender<FileManagerResult<SelectedFile>>,
        FileOrigin,
        Option<String>,
    ),

    /// Select multiple files. Last field is pre-selected file paths for testing
    SelectFiles(
        Vec<FilterPattern>,
        IpcSender<FileManagerResult<Vec<SelectedFile>>>,
        FileOrigin,
        Option<Vec<String>>,
    ),

    /// Read FileID-indexed file in chunks, optionally check URL validity based on boolean flag
    ReadFile(
        IpcSender<FileManagerResult<ReadFileProgress>>,
        Uuid,
        FileOrigin,
    ),

    /// Add an entry as promoted memory-based blob
    PromoteMemory(Uuid, BlobBuf, bool, FileOrigin),

    /// Add a sliced entry pointing to the parent FileID, and send back the associated FileID
    /// as part of a valid Blob URL
    AddSlicedURLEntry(
        Uuid,
        RelativePos,
        IpcSender<Result<Uuid, BlobURLStoreError>>,
        FileOrigin,
    ),

    /// Decrease reference count and send back the acknowledgement
    DecRef(Uuid, FileOrigin, IpcSender<Result<(), BlobURLStoreError>>),

    /// Activate an internal FileID so it becomes valid as part of a Blob URL
    ActivateBlobURL(Uuid, IpcSender<Result<(), BlobURLStoreError>>, FileOrigin),

    /// Revoke Blob URL and send back the acknowledgement
    RevokeBlobURL(Uuid, FileOrigin, IpcSender<Result<(), BlobURLStoreError>>),
}

#[derive(Debug, Deserialize, Serialize)]
pub enum ReadFileProgress {
    Meta(BlobBuf),
    Partial(Vec<u8>),
    EOF,
}

pub type FileManagerResult<T> = Result<T, FileManagerThreadError>;

#[derive(Debug, Deserialize, Serialize)]
pub enum FileManagerThreadError {
    /// The selection action is invalid due to exceptional reason
    InvalidSelection,
    /// The selection action is cancelled by user
    UserCancelled,
    /// Errors returned from file system request
    FileSystemError(String),
    /// Blob URL Store error
    BlobURLStoreError(BlobURLStoreError),
}