tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

mod.rs (12824B)


      1 mod asynchronous;
      2 mod synchronous;
      3 
      4 use crate::{
      5    env::ErrorReporter,
      6    errors::L10nRegistrySetupError,
      7    fluent::FluentBundle,
      8    source::{FileSource, ResourceId},
      9 };
     10 use fluent_bundle::FluentResource;
     11 use fluent_fallback::generator::BundleGenerator;
     12 use rustc_hash::FxHashSet;
     13 use std::{
     14    cell::{Ref, RefCell, RefMut},
     15    collections::HashSet,
     16    rc::Rc,
     17 };
     18 use unic_langid::LanguageIdentifier;
     19 
     20 pub use asynchronous::GenerateBundles;
     21 pub use synchronous::GenerateBundlesSync;
     22 
     23 pub type FluentResourceSet = Vec<Rc<FluentResource>>;
     24 
     25 /// The shared information that makes up the configuration the L10nRegistry. It is
     26 /// broken out into a separate struct so that it can be shared via an Rc pointer.
     27 #[derive(Default)]
     28 struct Shared<P, B> {
     29    metasources: RefCell<MetaSources>,
     30    provider: P,
     31    bundle_adapter: Option<B>,
     32 }
     33 
     34 /// [FileSources](FileSource) represent a single directory location to look for .ftl
     35 /// files. These are Stored in a [Vec]. For instance, in a built version of Firefox with
     36 /// the en-US locale, each [FileSource] may represent a different folder with many
     37 /// different files.
     38 ///
     39 /// Firefox supports other *meta sources* for localization files in the form of language
     40 /// packs which can be downloaded from the addon store. These language packs then would
     41 /// be a separate metasource than the app' language. This [MetaSources] adds another [Vec]
     42 /// over the [Vec] of [FileSources](FileSource) in order to provide a unified way to
     43 /// iterate over all possible [FileSource] locations to finally obtain the final bundle.
     44 ///
     45 /// This structure uses an [Rc] to point to the [FileSource] so that a shallow copy
     46 /// of these [FileSources](FileSource) can be obtained for iteration. This makes
     47 /// it quick to copy the list of [MetaSources] for iteration, and guards against
     48 /// invalidating that async nature of iteration when the underlying data mutates.
     49 ///
     50 /// Note that the async iteration of bundles is still only happening in one thread,
     51 /// and is not multi-threaded. The processing is just split over time.
     52 ///
     53 /// The [MetaSources] are ultimately owned by the [Shared] in a [RefCell] so that the
     54 /// source of truth can be mutated, and shallow copies of the [MetaSources] used for
     55 /// iteration will be unaffected.
     56 ///
     57 /// Deriving [Clone] here is a relatively cheap operation, since the [Rc] will be cloned,
     58 /// and point to the original [FileSource].
     59 #[derive(Default, Clone)]
     60 pub struct MetaSources(Vec<Vec<Rc<FileSource>>>);
     61 
     62 impl MetaSources {
     63    /// Iterate over all FileSources in all MetaSources.
     64    pub fn filesources(&self) -> impl Iterator<Item = &Rc<FileSource>> {
     65        self.0.iter().flatten()
     66    }
     67 
     68    /// Iterate over all FileSources in all MetaSources.
     69    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Vec<Rc<FileSource>>> {
     70        self.0.iter_mut()
     71    }
     72 
     73    /// The number of metasources.
     74    pub fn len(&self) -> usize {
     75        self.0.len()
     76    }
     77 
     78    /// If there are no metasources.
     79    pub fn is_empty(&self) -> bool {
     80        self.0.is_empty()
     81    }
     82 
     83    /// Clears out all metasources.
     84    pub fn clear(&mut self) {
     85        self.0.clear();
     86    }
     87 
     88    /// Clears out only empty metasources.
     89    pub fn clear_empty_metasources(&mut self) {
     90        self.0.retain(|metasource| !metasource.is_empty());
     91    }
     92 
     93    /// Adds a [FileSource] to its appropriate metasource.
     94    pub fn add_filesource(&mut self, new_source: FileSource) {
     95        if let Some(metasource) = self
     96            .0
     97            .iter_mut()
     98            .find(|source| source[0].metasource == new_source.metasource)
     99        {
    100            // A metasource was found, add to the existing one.
    101            metasource.push(Rc::new(new_source));
    102        } else {
    103            // Create a new metasource.
    104            self.0.push(vec![Rc::new(new_source)]);
    105        }
    106    }
    107 
    108    /// Adds a [FileSource] to its appropriate metasource.
    109    pub fn update_filesource(&mut self, new_source: &FileSource) -> bool {
    110        if let Some(metasource) = self
    111            .0
    112            .iter_mut()
    113            .find(|source| source[0].metasource == new_source.metasource)
    114        {
    115            if let Some(idx) = metasource.iter().position(|source| **source == *new_source) {
    116                *metasource.get_mut(idx).unwrap() = Rc::new(new_source.clone());
    117                return true;
    118            }
    119        }
    120        false
    121    }
    122 
    123    /// Get a metasource by index, but provide a nice error message if the index
    124    /// is out of bounds.
    125    pub fn get(&self, metasource_idx: usize) -> &Vec<Rc<FileSource>> {
    126        if let Some(metasource) = self.0.get(metasource_idx) {
    127            return &metasource;
    128        }
    129        panic!(
    130            "Metasource index of {} is out of range of the list of {} meta sources.",
    131            metasource_idx,
    132            self.0.len()
    133        );
    134    }
    135 
    136    /// Get a [FileSource] from a metasource, but provide a nice error message if the
    137    /// index is out of bounds.
    138    pub fn filesource(&self, metasource_idx: usize, filesource_idx: usize) -> &FileSource {
    139        let metasource = self.get(metasource_idx);
    140        let reversed_idx = metasource.len() - 1 - filesource_idx;
    141        if let Some(file_source) = metasource.get(reversed_idx) {
    142            return file_source;
    143        }
    144        panic!(
    145            "File source index of {} is out of range of the list of {} file sources.",
    146            filesource_idx,
    147            metasource.len()
    148        );
    149    }
    150 
    151    /// Get a [FileSource] by name from a metasource. This is useful for testing.
    152    #[cfg(feature = "test-fluent")]
    153    pub fn file_source_by_name(&self, metasource_idx: usize, name: &str) -> Option<&FileSource> {
    154        use std::borrow::Borrow;
    155        self.get(metasource_idx)
    156            .iter()
    157            .find(|&source| source.name == name)
    158            .map(|source| source.borrow())
    159    }
    160 
    161    /// Get an iterator for the [FileSources](FileSource) that match the [LanguageIdentifier]
    162    /// and [ResourceId].
    163    #[cfg(feature = "test-fluent")]
    164    pub fn get_sources_for_resource<'l>(
    165        &'l self,
    166        metasource_idx: usize,
    167        langid: &'l LanguageIdentifier,
    168        resource_id: &'l ResourceId,
    169    ) -> impl Iterator<Item = &FileSource> {
    170        use std::borrow::Borrow;
    171        self.get(metasource_idx)
    172            .iter()
    173            .filter(move |source| source.has_file(langid, resource_id) != Some(false))
    174            .map(|source| source.borrow())
    175    }
    176 }
    177 
    178 /// The [BundleAdapter] can adapt the bundle to the environment with such actions as
    179 /// setting the platform, and hooking up functions such as Fluent's DATETIME and
    180 /// NUMBER formatting functions.
    181 pub trait BundleAdapter {
    182    fn adapt_bundle(&self, bundle: &mut FluentBundle);
    183 }
    184 
    185 /// The L10nRegistry is the main struct for owning the registry information.
    186 ///
    187 /// `P` - A provider
    188 /// `B` - A bundle adapter
    189 #[derive(Clone)]
    190 pub struct L10nRegistry<P, B> {
    191    shared: Rc<Shared<P, B>>,
    192 }
    193 
    194 impl<P, B> L10nRegistry<P, B> {
    195    /// Create a new [L10nRegistry] from a provider.
    196    pub fn with_provider(provider: P) -> Self {
    197        Self {
    198            shared: Rc::new(Shared {
    199                metasources: Default::default(),
    200                provider,
    201                bundle_adapter: None,
    202            }),
    203        }
    204    }
    205 
    206    /// Set the bundle adapter. See [BundleAdapter] for more information.
    207    pub fn set_bundle_adapter(&mut self, bundle_adapter: B) -> Result<(), L10nRegistrySetupError>
    208    where
    209        B: BundleAdapter,
    210    {
    211        let shared = Rc::get_mut(&mut self.shared).ok_or(L10nRegistrySetupError::RegistryLocked)?;
    212        shared.bundle_adapter = Some(bundle_adapter);
    213        Ok(())
    214    }
    215 
    216    pub fn try_borrow_metasources(&self) -> Result<Ref<'_, MetaSources>, L10nRegistrySetupError> {
    217        self.shared
    218            .metasources
    219            .try_borrow()
    220            .map_err(|_| L10nRegistrySetupError::RegistryLocked)
    221    }
    222 
    223    pub fn try_borrow_metasources_mut(
    224        &self,
    225    ) -> Result<RefMut<'_, MetaSources>, L10nRegistrySetupError> {
    226        self.shared
    227            .metasources
    228            .try_borrow_mut()
    229            .map_err(|_| L10nRegistrySetupError::RegistryLocked)
    230    }
    231 
    232    /// Adds a new [FileSource] to the registry and to its appropriate metasource. If the
    233    /// metasource for this [FileSource] does not exist, then it is created.
    234    pub fn register_sources(
    235        &self,
    236        new_sources: Vec<FileSource>,
    237    ) -> Result<(), L10nRegistrySetupError> {
    238        for new_source in new_sources {
    239            self.try_borrow_metasources_mut()?
    240                .add_filesource(new_source);
    241        }
    242        Ok(())
    243    }
    244 
    245    /// Update the information about sources already stored in the registry. Each
    246    /// [FileSource] provided must exist, or else a [L10nRegistrySetupError] will
    247    /// be returned.
    248    pub fn update_sources(
    249        &self,
    250        new_sources: Vec<FileSource>,
    251    ) -> Result<(), L10nRegistrySetupError> {
    252        for new_source in new_sources {
    253            if !self
    254                .try_borrow_metasources_mut()?
    255                .update_filesource(&new_source)
    256            {
    257                return Err(L10nRegistrySetupError::MissingSource {
    258                    name: new_source.name,
    259                });
    260            }
    261        }
    262        Ok(())
    263    }
    264 
    265    /// Remove the provided sources. If a metasource becomes empty after this operation,
    266    /// the metasource is also removed.
    267    pub fn remove_sources<S>(&self, del_sources: Vec<S>) -> Result<(), L10nRegistrySetupError>
    268    where
    269        S: ToString,
    270    {
    271        let del_sources: Vec<String> = del_sources.into_iter().map(|s| s.to_string()).collect();
    272 
    273        for metasource in self.try_borrow_metasources_mut()?.iter_mut() {
    274            metasource.retain(|source| !del_sources.contains(&source.name));
    275        }
    276 
    277        self.try_borrow_metasources_mut()?.clear_empty_metasources();
    278 
    279        Ok(())
    280    }
    281 
    282    /// Clears out all metasources and sources.
    283    pub fn clear_sources(&self) -> Result<(), L10nRegistrySetupError> {
    284        self.try_borrow_metasources_mut()?.clear();
    285        Ok(())
    286    }
    287 
    288    /// Flattens out all metasources and returns the complete list of source names.
    289    pub fn get_source_names(&self) -> Result<Vec<String>, L10nRegistrySetupError> {
    290        Ok(self
    291            .try_borrow_metasources()?
    292            .filesources()
    293            .map(|s| s.name.clone())
    294            .collect())
    295    }
    296 
    297    /// Checks if any metasources has a source, by the name.
    298    pub fn has_source(&self, name: &str) -> Result<bool, L10nRegistrySetupError> {
    299        Ok(self
    300            .try_borrow_metasources()?
    301            .filesources()
    302            .any(|source| source.name == name))
    303    }
    304 
    305    /// Get a [FileSource] by name by searching through all meta sources.
    306    pub fn file_source_by_name(
    307        &self,
    308        name: &str,
    309    ) -> Result<Option<FileSource>, L10nRegistrySetupError> {
    310        Ok(self
    311            .try_borrow_metasources()?
    312            .filesources()
    313            .find(|source| source.name == name)
    314            .map(|source| (**source).clone()))
    315    }
    316 
    317    /// Returns a unique list of locale names from all sources.
    318    pub fn get_available_locales(&self) -> Result<Vec<LanguageIdentifier>, L10nRegistrySetupError> {
    319        let mut result = HashSet::new();
    320        let metasources = self.try_borrow_metasources()?;
    321        for source in metasources.filesources() {
    322            for locale in source.locales() {
    323                result.insert(locale);
    324            }
    325        }
    326        Ok(result.into_iter().map(|l| l.to_owned()).collect())
    327    }
    328 }
    329 
    330 /// Defines how to generate bundles synchronously and asynchronously.
    331 impl<P, B> BundleGenerator for L10nRegistry<P, B>
    332 where
    333    P: ErrorReporter + Clone,
    334    B: BundleAdapter + Clone,
    335 {
    336    type Resource = Rc<FluentResource>;
    337    type Iter = GenerateBundlesSync<P, B>;
    338    type Stream = GenerateBundles<P, B>;
    339    type LocalesIter = std::vec::IntoIter<LanguageIdentifier>;
    340 
    341    /// The synchronous version of the bundle generator. This is hooked into Gecko
    342    /// code via the `l10nregistry_generate_bundles_sync` function.
    343    fn bundles_iter(
    344        &self,
    345        locales: Self::LocalesIter,
    346        resource_ids: FxHashSet<ResourceId>,
    347    ) -> Self::Iter {
    348        let resource_ids = resource_ids.into_iter().collect();
    349        self.generate_bundles_sync(locales, resource_ids)
    350    }
    351 
    352    /// The asynchronous version of the bundle generator. This is hooked into Gecko
    353    /// code via the `l10nregistry_generate_bundles` function.
    354    fn bundles_stream(
    355        &self,
    356        locales: Self::LocalesIter,
    357        resource_ids: FxHashSet<ResourceId>,
    358    ) -> Self::Stream {
    359        let resource_ids = resource_ids.into_iter().collect();
    360        self.generate_bundles(locales, resource_ids)
    361            .expect("Unable to get the MetaSources.")
    362    }
    363 }