tor-browser

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

source.rs (10087B)


      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 
      5 use super::fetcher::{GeckoFileFetcher, MockFileFetcher};
      6 use crate::env::GeckoEnvironment;
      7 
      8 use fluent::FluentResource;
      9 use l10nregistry::source::{FileSource, FileSourceOptions, ResourceOption, ResourceStatus, RcResource};
     10 
     11 use nsstring::{nsACString, nsCString};
     12 use thin_vec::ThinVec;
     13 use unic_langid::LanguageIdentifier;
     14 
     15 use std::{borrow::Cow, mem, rc::Rc};
     16 use xpcom::RefPtr;
     17 
     18 #[repr(C)]
     19 pub enum L10nFileSourceStatus {
     20    None,
     21    EmptyName,
     22    EmptyPrePath,
     23    EmptyResId,
     24    InvalidLocaleCode,
     25 }
     26 
     27 // For historical reasons we maintain a locale in Firefox with a codename `ja-JP-mac`.
     28 // This string is an invalid BCP-47 language tag, so we don't store it in Gecko, which uses
     29 // valid BCP-47 tags only, but rather keep that quirk local to Gecko L10nRegistry file fetcher.
     30 //
     31 // Here, if we encounter `ja-JP-mac` (invalid BCP-47), we swap it for a valid equivalent: `ja-JP-macos`.
     32 //
     33 // See bug 1726586 for details and fetcher::get_locale_for_gecko.
     34 fn get_locale_from_gecko<'s>(input: Cow<'s, str>) -> Cow<'s, str> {
     35    if input == "ja-JP-mac" {
     36        "ja-JP-macos".into()
     37    } else {
     38        input
     39    }
     40 }
     41 
     42 #[no_mangle]
     43 pub extern "C" fn l10nfilesource_new(
     44    name: &nsACString,
     45    metasource: &nsACString,
     46    locales: &ThinVec<nsCString>,
     47    pre_path: &nsACString,
     48    allow_override: bool,
     49    status: &mut L10nFileSourceStatus,
     50 ) -> *const FileSource {
     51    if name.is_empty() {
     52        *status = L10nFileSourceStatus::EmptyName;
     53        return std::ptr::null();
     54    }
     55    if pre_path.is_empty() {
     56        *status = L10nFileSourceStatus::EmptyPrePath;
     57        return std::ptr::null();
     58    }
     59 
     60    let locales: Result<Vec<LanguageIdentifier>, _> = locales
     61        .iter()
     62        .map(|l| get_locale_from_gecko(l.to_utf8()).parse())
     63        .collect();
     64 
     65    let locales = match locales {
     66        Ok(locales) => locales,
     67        Err(..) => {
     68            *status = L10nFileSourceStatus::InvalidLocaleCode;
     69            return std::ptr::null();
     70        }
     71    };
     72 
     73    let mut source = FileSource::new(
     74        name.to_string(),
     75        Some(metasource.to_string()),
     76        locales,
     77        pre_path.to_string(),
     78        FileSourceOptions { allow_override },
     79        GeckoFileFetcher,
     80    );
     81    source.set_reporter(GeckoEnvironment::new(None));
     82 
     83    *status = L10nFileSourceStatus::None;
     84    Rc::into_raw(Rc::new(source))
     85 }
     86 
     87 #[no_mangle]
     88 pub unsafe extern "C" fn l10nfilesource_new_with_index(
     89    name: &nsACString,
     90    metasource: &nsACString,
     91    locales: &ThinVec<nsCString>,
     92    pre_path: &nsACString,
     93    index_elements: *const nsCString,
     94    index_length: usize,
     95    allow_override: bool,
     96    status: &mut L10nFileSourceStatus,
     97 ) -> *const FileSource {
     98    if name.is_empty() {
     99        *status = L10nFileSourceStatus::EmptyName;
    100        return std::ptr::null();
    101    }
    102    if pre_path.is_empty() {
    103        *status = L10nFileSourceStatus::EmptyPrePath;
    104        return std::ptr::null();
    105    }
    106 
    107    let locales: Result<Vec<LanguageIdentifier>, _> = locales
    108        .iter()
    109        .map(|l| get_locale_from_gecko(l.to_utf8()).parse())
    110        .collect();
    111 
    112    let index = if index_length > 0 {
    113        assert!(!index_elements.is_null());
    114        std::slice::from_raw_parts(index_elements, index_length)
    115    } else {
    116        &[]
    117    }
    118    .into_iter()
    119    .map(|s| s.to_string())
    120    .collect();
    121 
    122    let locales = match locales {
    123        Ok(locales) => locales,
    124        Err(..) => {
    125            *status = L10nFileSourceStatus::InvalidLocaleCode;
    126            return std::ptr::null();
    127        }
    128    };
    129 
    130    let mut source = FileSource::new_with_index(
    131        name.to_string(),
    132        Some(metasource.to_string()),
    133        locales,
    134        pre_path.to_string(),
    135        FileSourceOptions { allow_override },
    136        GeckoFileFetcher,
    137        index,
    138    );
    139    source.set_reporter(GeckoEnvironment::new(None));
    140 
    141    *status = L10nFileSourceStatus::None;
    142    Rc::into_raw(Rc::new(source))
    143 }
    144 
    145 #[repr(C)]
    146 pub struct L10nFileSourceMockFile {
    147    path: nsCString,
    148    source: nsCString,
    149 }
    150 
    151 #[no_mangle]
    152 pub extern "C" fn l10nfilesource_new_mock(
    153    name: &nsACString,
    154    metasource: &nsACString,
    155    locales: &ThinVec<nsCString>,
    156    pre_path: &nsACString,
    157    fs: &ThinVec<L10nFileSourceMockFile>,
    158    status: &mut L10nFileSourceStatus,
    159 ) -> *const FileSource {
    160    if name.is_empty() {
    161        *status = L10nFileSourceStatus::EmptyName;
    162        return std::ptr::null();
    163    }
    164    if pre_path.is_empty() {
    165        *status = L10nFileSourceStatus::EmptyPrePath;
    166        return std::ptr::null();
    167    }
    168 
    169    let locales: Result<Vec<LanguageIdentifier>, _> = locales
    170        .iter()
    171        .map(|l| get_locale_from_gecko(l.to_utf8()).parse())
    172        .collect();
    173 
    174    let locales = match locales {
    175        Ok(locales) => locales,
    176        Err(..) => {
    177            *status = L10nFileSourceStatus::InvalidLocaleCode;
    178            return std::ptr::null();
    179        }
    180    };
    181 
    182    let fs = fs
    183        .iter()
    184        .map(|mock| (mock.path.to_string(), mock.source.to_string()))
    185        .collect();
    186    let fetcher = MockFileFetcher::new(fs);
    187    let mut source = FileSource::new(
    188        name.to_string(),
    189        Some(metasource.to_string()),
    190        locales,
    191        pre_path.to_string(),
    192        Default::default(),
    193        fetcher,
    194    );
    195    source.set_reporter(GeckoEnvironment::new(None));
    196 
    197    *status = L10nFileSourceStatus::None;
    198    Rc::into_raw(Rc::new(source))
    199 }
    200 
    201 #[no_mangle]
    202 pub unsafe extern "C" fn l10nfilesource_addref(source: *const FileSource) {
    203    let raw = Rc::from_raw(source);
    204    mem::forget(Rc::clone(&raw));
    205    mem::forget(raw);
    206 }
    207 
    208 #[no_mangle]
    209 pub unsafe extern "C" fn l10nfilesource_release(source: *const FileSource) {
    210    let _ = Rc::from_raw(source);
    211 }
    212 
    213 #[no_mangle]
    214 pub extern "C" fn l10nfilesource_get_name(source: &FileSource, ret_val: &mut nsACString) {
    215    ret_val.assign(&source.name);
    216 }
    217 
    218 #[no_mangle]
    219 pub extern "C" fn l10nfilesource_get_metasource(source: &FileSource, ret_val: &mut nsACString) {
    220    ret_val.assign(&source.metasource);
    221 }
    222 
    223 #[no_mangle]
    224 pub extern "C" fn l10nfilesource_get_locales(
    225    source: &FileSource,
    226    ret_val: &mut ThinVec<nsCString>,
    227 ) {
    228    for locale in source.locales() {
    229        ret_val.push(locale.to_string().into());
    230    }
    231 }
    232 
    233 #[no_mangle]
    234 pub extern "C" fn l10nfilesource_get_prepath(source: &FileSource, ret_val: &mut nsACString) {
    235    ret_val.assign(&source.pre_path);
    236 }
    237 
    238 #[no_mangle]
    239 pub extern "C" fn l10nfilesource_get_index(
    240    source: &FileSource,
    241    ret_val: &mut ThinVec<nsCString>,
    242 ) -> bool {
    243    if let Some(index) = source.get_index() {
    244        for entry in index {
    245            ret_val.push(entry.to_string().into());
    246        }
    247        true
    248    } else {
    249        false
    250    }
    251 }
    252 
    253 #[no_mangle]
    254 pub extern "C" fn l10nfilesource_has_file(
    255    source: &FileSource,
    256    locale: &nsACString,
    257    path: &nsACString,
    258    status: &mut L10nFileSourceStatus,
    259    present: &mut bool,
    260 ) -> bool {
    261    if path.is_empty() {
    262        *status = L10nFileSourceStatus::EmptyResId;
    263        return false;
    264    }
    265 
    266    let locale = match locale.to_utf8().parse() {
    267        Ok(locale) => locale,
    268        Err(..) => {
    269            *status = L10nFileSourceStatus::InvalidLocaleCode;
    270            return false;
    271        }
    272    };
    273 
    274    *status = L10nFileSourceStatus::None;
    275    // To work around Option<bool> we return bool for the option,
    276    // and the `present` argument is the value of it.
    277    if let Some(val) = source.has_file(&locale, &path.to_utf8().into()) {
    278        *present = val;
    279        true
    280    } else {
    281        false
    282    }
    283 }
    284 
    285 #[no_mangle]
    286 pub extern "C" fn l10nfilesource_fetch_file_sync(
    287    source: &FileSource,
    288    locale: &nsACString,
    289    path: &nsACString,
    290    status: &mut L10nFileSourceStatus,
    291 ) -> *const FluentResource {
    292    if path.is_empty() {
    293        *status = L10nFileSourceStatus::EmptyResId;
    294        return std::ptr::null();
    295    }
    296 
    297    let locale = match locale.to_utf8().parse() {
    298        Ok(locale) => locale,
    299        Err(..) => {
    300            *status = L10nFileSourceStatus::InvalidLocaleCode;
    301            return std::ptr::null();
    302        }
    303    };
    304 
    305    *status = L10nFileSourceStatus::None;
    306    //XXX: Bug 1723191 - if we encounter a request for sync load while async load is in progress
    307    //                   we will discard the async load and force the sync load instead.
    308    //                   There may be a better option but we haven't had time to explore it.
    309    if let ResourceOption::Some(res) =
    310        source.fetch_file_sync(&locale, &path.to_utf8().into(), /* overload */ true)
    311    {
    312        Rc::into_raw(res)
    313    } else {
    314        std::ptr::null()
    315    }
    316 }
    317 
    318 #[no_mangle]
    319 pub unsafe extern "C" fn l10nfilesource_fetch_file(
    320    source: &FileSource,
    321    locale: &nsACString,
    322    path: &nsACString,
    323    promise: &xpcom::Promise,
    324    callback: extern "C" fn(&xpcom::Promise, Option<&FluentResource>),
    325    status: &mut L10nFileSourceStatus,
    326 ) {
    327    if path.is_empty() {
    328        *status = L10nFileSourceStatus::EmptyResId;
    329        return;
    330    }
    331 
    332    let locale = match locale.to_utf8().parse() {
    333        Ok(locale) => locale,
    334        Err(..) => {
    335            *status = L10nFileSourceStatus::InvalidLocaleCode;
    336            return;
    337        }
    338    };
    339 
    340    *status = L10nFileSourceStatus::None;
    341 
    342    let path = path.to_utf8().into();
    343 
    344    match source.fetch_file(&locale, &path) {
    345        ResourceStatus::MissingOptional => callback(promise, None),
    346        ResourceStatus::MissingRequired => callback(promise, None),
    347        ResourceStatus::Loaded(res) => callback(promise, Some(&res)),
    348        res @ ResourceStatus::Loading(_) => {
    349            let strong_promise = RefPtr::new(promise);
    350            moz_task::spawn_local("l10nfilesource_fetch_file", async move {
    351                callback(
    352                    &strong_promise,
    353                    Option::<RcResource>::from(res.await).as_ref().map(|r| &**r),
    354                );
    355            })
    356            .detach();
    357        }
    358    }
    359 }