summaryrefslogtreecommitdiffstats
path: root/intl/l10n/rust/l10nregistry-ffi/src/env.rs
blob: 7a77af21767a6b4744982816e9cec5b63720d369 (plain)
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
/* 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 http://mozilla.org/MPL/2.0/. */

use crate::xpcom_utils::get_app_locales;
use cstr::cstr;
use fluent_fallback::env::LocalesProvider;
use l10nregistry::{
    env::ErrorReporter,
    errors::{L10nRegistryError, L10nRegistrySetupError},
};
use log::warn;
use nserror::{nsresult, NS_ERROR_NOT_AVAILABLE};
use nsstring::{nsCStr, nsString};
use std::fmt::{self, Write};
use unic_langid::LanguageIdentifier;
use xpcom::interfaces;

#[derive(Clone)]
pub struct GeckoEnvironment {
    custom_locales: Option<Vec<LanguageIdentifier>>,
}

impl GeckoEnvironment {
    pub fn new(custom_locales: Option<Vec<LanguageIdentifier>>) -> Self {
        Self { custom_locales }
    }

    pub fn report_l10nregistry_setup_error(error: &L10nRegistrySetupError) {
        warn!("L10nRegistry setup error: {}", error);
        let result = log_simple_console_error(
            &error.to_string(),
            false,
            true,
            None,
            (0, 0),
            interfaces::nsIScriptError::errorFlag as u32,
        );
        if let Err(err) = result {
            warn!("Error while reporting an error: {}", err);
        }
    }
}

impl ErrorReporter for GeckoEnvironment {
    fn report_errors(&self, errors: Vec<L10nRegistryError>) {
        for error in errors {
            warn!("L10nRegistry error: {}", error);
            let result = match error {
                L10nRegistryError::FluentError {
                    resource_id,
                    loc,
                    error,
                } => log_simple_console_error(
                    &error.to_string(),
                    false,
                    true,
                    Some(nsString::from(&resource_id.value)),
                    loc.map_or((0, 0), |(l, c)| (l as u32, c as u32)),
                    interfaces::nsIScriptError::errorFlag as u32,
                ),
                L10nRegistryError::MissingResource { .. } => log_simple_console_error(
                    &error.to_string(),
                    false,
                    true,
                    None,
                    (0, 0),
                    interfaces::nsIScriptError::warningFlag as u32,
                ),
            };
            if let Err(err) = result {
                warn!("Error while reporting an error: {}", err);
            }
        }
    }
}

impl LocalesProvider for GeckoEnvironment {
    type Iter = std::vec::IntoIter<unic_langid::LanguageIdentifier>;
    fn locales(&self) -> Self::Iter {
        if let Some(custom_locales) = &self.custom_locales {
            custom_locales.clone().into_iter()
        } else {
            let result = get_app_locales()
                .expect("Failed to retrieve app locales")
                .into_iter()
                .map(|s| LanguageIdentifier::from_bytes(&s).expect("Failed to parse a locale"))
                .collect::<Vec<_>>();
            result.into_iter()
        }
    }
}

fn log_simple_console_error(
    error: &impl fmt::Display,
    from_private_window: bool,
    from_chrome_context: bool,
    path: Option<nsString>,
    pos: (u32, u32),
    error_flags: u32,
) -> Result<(), nsresult> {
    // Format whatever error argument into a wide string with `Display`.
    let mut error_str = nsString::new();
    write!(&mut error_str, "{}", error).expect("nsString has an infallible Write impl");

    // Get the relevant services, and create the script error object.
    let console_service =
        xpcom::get_service::<interfaces::nsIConsoleService>(cstr!("@mozilla.org/consoleservice;1"))
            .ok_or(NS_ERROR_NOT_AVAILABLE)?;
    let script_error =
        xpcom::create_instance::<interfaces::nsIScriptError>(cstr!("@mozilla.org/scripterror;1"))
            .ok_or(NS_ERROR_NOT_AVAILABLE)?;
    let category = nsCStr::from("l10n");
    unsafe {
        script_error
            .Init(
                &*error_str,
                &*path.unwrap_or(nsString::new()), /* aSourceName */
                &*nsString::new(),                 /* aSourceLine */
                pos.0,                             /* aLineNumber */
                pos.1,                             /* aColNumber */
                error_flags,
                &*category,
                from_private_window,
                from_chrome_context,
            )
            .to_result()?;

        console_service.LogMessage(&**script_error).to_result()?;
    }
    Ok(())
}