mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-07 18:04:46 +00:00
Bug 1660393 - P3: Implement load_sync and load_async functions for L10n. r=zbraniecki,froydnj
Differential Revision: https://phabricator.services.mozilla.com/D89695
This commit is contained in:
parent
0b93a75baa
commit
f5c6d9e6a5
20
Cargo.lock
generated
20
Cargo.lock
generated
@ -1950,6 +1950,7 @@ dependencies = [
|
||||
"fog-gtest",
|
||||
"gecko-fuzz-targets",
|
||||
"gkrust-shared",
|
||||
"l10nregistry-ffi-gtest",
|
||||
"moz_task-gtest",
|
||||
"mozglue-static",
|
||||
"mp4parse-gtest",
|
||||
@ -1989,6 +1990,7 @@ dependencies = [
|
||||
"http_sfv",
|
||||
"jsrust_shared",
|
||||
"kvstore",
|
||||
"l10nregistry-ffi",
|
||||
"lmdb-rkv-sys",
|
||||
"log",
|
||||
"mapped_hyph",
|
||||
@ -2606,6 +2608,24 @@ dependencies = [
|
||||
"xpcom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "l10nregistry-ffi"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"libc",
|
||||
"nserror",
|
||||
"nsstring",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "l10nregistry-ffi-gtest"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"l10nregistry-ffi",
|
||||
"moz_task",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
|
134
intl/l10n/L10nRegistry.cpp
Normal file
134
intl/l10n/L10nRegistry.cpp
Normal file
@ -0,0 +1,134 @@
|
||||
/* 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/. */
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/URLPreloader.h"
|
||||
#include "nsIChannel.h"
|
||||
#include "nsILoadInfo.h"
|
||||
#include "nsIStreamLoader.h"
|
||||
#include "nsString.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace intl {
|
||||
|
||||
class ResourceLoader final : public nsIStreamLoaderObserver {
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISTREAMLOADEROBSERVER
|
||||
|
||||
typedef nsresult (*Callback)(void* aClosure, nsACString* aString,
|
||||
nsresult aSuccess);
|
||||
|
||||
ResourceLoader(Callback aCallback, void* aClosure)
|
||||
: mCallback(aCallback), mClosure(aClosure) {}
|
||||
|
||||
protected:
|
||||
~ResourceLoader() = default;
|
||||
|
||||
private:
|
||||
Callback mCallback;
|
||||
void* mClosure;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(ResourceLoader, nsIStreamLoaderObserver)
|
||||
|
||||
// nsIStreamLoaderObserver
|
||||
NS_IMETHODIMP
|
||||
ResourceLoader::OnStreamComplete(nsIStreamLoader* aLoader,
|
||||
nsISupports* aContext, nsresult aStatus,
|
||||
uint32_t aStringLen, const uint8_t* aString) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (NS_FAILED(aStatus)) {
|
||||
mCallback(mClosure, nullptr, aStatus);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCString data;
|
||||
data.Adopt(reinterpret_cast<char*>(const_cast<uint8_t*>(aString)),
|
||||
aStringLen);
|
||||
mCallback(mClosure, &data, NS_OK);
|
||||
|
||||
return NS_SUCCESS_ADOPTED_DATA;
|
||||
}
|
||||
|
||||
class L10nRegistry {
|
||||
public:
|
||||
static nsresult Load(const nsACString& aPath,
|
||||
ResourceLoader::Callback aCallback, void* aClosure) {
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(uri), aPath);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_TRUE(uri, NS_ERROR_INVALID_ARG);
|
||||
|
||||
RefPtr<ResourceLoader> listener =
|
||||
MakeRefPtr<ResourceLoader>(aCallback, aClosure);
|
||||
|
||||
// TODO: What is the lifetime requirement for loader?
|
||||
RefPtr<nsIStreamLoader> loader;
|
||||
rv = NS_NewStreamLoader(
|
||||
getter_AddRefs(loader), uri, listener,
|
||||
nsContentUtils::GetSystemPrincipal(),
|
||||
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
|
||||
nsIContentPolicy::TYPE_OTHER);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static nsresult LoadSync(const nsACString& aPath, nsACString& aRetVal) {
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(uri), aPath);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
NS_ENSURE_TRUE(uri, NS_ERROR_INVALID_ARG);
|
||||
|
||||
auto result = URLPreloader::ReadURI(uri);
|
||||
if (result.isOk()) {
|
||||
aRetVal = result.unwrap();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
auto err = result.unwrapErr();
|
||||
if (err != NS_ERROR_INVALID_ARG && err != NS_ERROR_NOT_INITIALIZED) {
|
||||
return err;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
rv = NS_NewChannel(
|
||||
getter_AddRefs(channel), uri, nsContentUtils::GetSystemPrincipal(),
|
||||
nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_SEC_CONTEXT_IS_NULL,
|
||||
nsIContentPolicy::TYPE_OTHER, nullptr, /* nsICookieJarSettings */
|
||||
nullptr, /* aPerformanceStorage */
|
||||
nullptr, /* aLoadGroup */
|
||||
nullptr, /* aCallbacks */
|
||||
nsIRequest::LOAD_BACKGROUND);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIInputStream> input;
|
||||
rv = channel->Open(getter_AddRefs(input));
|
||||
NS_ENSURE_SUCCESS(rv, NS_ERROR_INVALID_ARG);
|
||||
|
||||
return NS_ReadInputStreamToString(input, aRetVal, -1);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace intl
|
||||
} // namespace mozilla
|
||||
|
||||
extern "C" {
|
||||
nsresult L10nRegistryLoad(const nsACString* aPath,
|
||||
mozilla::intl::ResourceLoader::Callback aCallback,
|
||||
void* aClosure) {
|
||||
if (!aPath || !aCallback) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
return mozilla::intl::L10nRegistry::Load(*aPath, aCallback, aClosure);
|
||||
}
|
||||
|
||||
nsresult L10nRegistryLoadSync(const nsACString* aPath, nsACString* aRetVal) {
|
||||
return mozilla::intl::L10nRegistry::LoadSync(*aPath, *aRetVal);
|
||||
}
|
||||
}
|
@ -14,6 +14,7 @@ EXPORTS.mozilla.intl += [
|
||||
UNIFIED_SOURCES += [
|
||||
'FluentBundle.cpp',
|
||||
'FluentResource.cpp',
|
||||
'L10nRegistry.cpp',
|
||||
'Localization.cpp',
|
||||
]
|
||||
|
||||
@ -26,6 +27,10 @@ TESTING_JS_MODULES += [
|
||||
'FluentSyntax.jsm',
|
||||
]
|
||||
|
||||
TEST_DIRS += [
|
||||
'rust/gtest',
|
||||
]
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'mozILocalization.idl',
|
||||
]
|
||||
|
14
intl/l10n/rust/gtest/Cargo.toml
Normal file
14
intl/l10n/rust/gtest/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "l10nregistry-ffi-gtest"
|
||||
version = "0.1.0"
|
||||
authors = ["nobody@mozilla.org"]
|
||||
license = "MPL-2.0"
|
||||
description = "Tests for rust bindings to l10nRegistry"
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
l10nregistry-ffi = { path = "../l10nregistry-ffi" }
|
||||
moz_task = { path = "../../../../xpcom/rust/moz_task" }
|
||||
|
||||
[lib]
|
||||
path = "test.rs"
|
23
intl/l10n/rust/gtest/Test.cpp
Normal file
23
intl/l10n/rust/gtest/Test.cpp
Normal file
@ -0,0 +1,23 @@
|
||||
/* 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/. */
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
extern "C" void Rust_L10NLoadAsync(bool* aItWorked);
|
||||
|
||||
TEST(RustL10N, LoadAsync)
|
||||
{
|
||||
bool itWorked = false;
|
||||
Rust_L10NLoadAsync(&itWorked);
|
||||
EXPECT_TRUE(itWorked);
|
||||
}
|
||||
|
||||
extern "C" void Rust_L10NLoadSync(bool* aItWorked);
|
||||
|
||||
TEST(RustL10N, LoadSync)
|
||||
{
|
||||
bool itWorked = false;
|
||||
Rust_L10NLoadSync(&itWorked);
|
||||
EXPECT_TRUE(itWorked);
|
||||
}
|
11
intl/l10n/rust/gtest/moz.build
Normal file
11
intl/l10n/rust/gtest/moz.build
Normal file
@ -0,0 +1,11 @@
|
||||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'Test.cpp',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul-gtest'
|
55
intl/l10n/rust/gtest/test.rs
Normal file
55
intl/l10n/rust/gtest/test.rs
Normal file
@ -0,0 +1,55 @@
|
||||
/* 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 l10nregistry_ffi::{load_async, load_sync};
|
||||
use moz_task;
|
||||
use std::{
|
||||
sync::atomic::{AtomicBool, Ordering::Relaxed},
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn Rust_L10NLoadAsync(it_worked: *mut bool) {
|
||||
let done = Arc::new(AtomicBool::new(false));
|
||||
let done2 = done.clone();
|
||||
|
||||
moz_task::spawn_current_thread(async move {
|
||||
match load_async("resource://gre/localization/en-US/toolkit/about/aboutAbout.ftl").await {
|
||||
Ok(res) => {
|
||||
assert_eq!(res.len(), 460);
|
||||
assert!(res.starts_with(
|
||||
b"# This Source Code Form is subject to the terms of the Mozilla Public"
|
||||
));
|
||||
unsafe {
|
||||
*it_worked = true;
|
||||
}
|
||||
}
|
||||
Err(err) => println!("{:?}", err),
|
||||
}
|
||||
|
||||
done.store(true, Relaxed);
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
unsafe {
|
||||
moz_task::gtest_only::spin_event_loop_until(move || done2.load(Relaxed)).unwrap();
|
||||
*it_worked = true;
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn Rust_L10NLoadSync(it_worked: *mut bool) {
|
||||
match load_sync("resource://gre/localization/en-US/toolkit/about/aboutAbout.ftl") {
|
||||
Ok(res) => {
|
||||
assert_eq!(res.len(), 460);
|
||||
assert!(res.starts_with(
|
||||
b"# This Source Code Form is subject to the terms of the Mozilla Public"
|
||||
));
|
||||
unsafe {
|
||||
*it_worked = true;
|
||||
}
|
||||
}
|
||||
Err(err) => println!("{:?}", err),
|
||||
}
|
||||
}
|
11
intl/l10n/rust/l10nregistry-ffi/Cargo.toml
Normal file
11
intl/l10n/rust/l10nregistry-ffi/Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "l10nregistry-ffi"
|
||||
version = "0.1.0"
|
||||
authors = ["nobody@mozilla.org"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
futures-channel = "0.3"
|
||||
libc = "0.2"
|
||||
nserror = { path = "../../../../xpcom/rust/nserror" }
|
||||
nsstring = { path = "../../../../xpcom/rust/nsstring" }
|
108
intl/l10n/rust/l10nregistry-ffi/src/lib.rs
Normal file
108
intl/l10n/rust/l10nregistry-ffi/src/lib.rs
Normal file
@ -0,0 +1,108 @@
|
||||
/* 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 futures_channel::oneshot::channel;
|
||||
use libc::c_void;
|
||||
use nserror::nsresult;
|
||||
use nsstring::{nsACString, nsCString, nsCStringLike};
|
||||
use std::io::{self, Error, ErrorKind};
|
||||
|
||||
trait ToIoResult {
|
||||
fn to_io_result(self) -> io::Result<()>;
|
||||
}
|
||||
|
||||
impl ToIoResult for nsresult {
|
||||
fn to_io_result(self) -> io::Result<()> {
|
||||
if self.failed() {
|
||||
return Err(Error::new(ErrorKind::Other, self));
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod ffi {
|
||||
use super::*;
|
||||
|
||||
pub fn load(
|
||||
path: &nsACString,
|
||||
callback: Option<extern "C" fn(*mut c_void, *mut nsACString, nsresult)>,
|
||||
closure: *mut c_void,
|
||||
) -> io::Result<()> {
|
||||
extern "C" {
|
||||
fn L10nRegistryLoad(
|
||||
aPath: *const nsACString,
|
||||
callback: Option<extern "C" fn(*mut c_void, *mut nsACString, nsresult)>,
|
||||
closure: *mut c_void,
|
||||
) -> nsresult;
|
||||
}
|
||||
unsafe { L10nRegistryLoad(path, callback, closure) }.to_io_result()
|
||||
}
|
||||
|
||||
pub fn load_sync(path: &nsACString, result: &mut nsACString) -> io::Result<()> {
|
||||
extern "C" {
|
||||
fn L10nRegistryLoadSync(aPath: *const nsACString, aRetVal: *mut nsACString) -> nsresult;
|
||||
}
|
||||
unsafe { L10nRegistryLoadSync(path, result) }.to_io_result()
|
||||
}
|
||||
|
||||
/// Swap the memory to take ownership of the string data
|
||||
#[allow(non_snake_case)]
|
||||
pub fn take_nsACString(s: &mut nsACString) -> nsCString {
|
||||
let mut result = nsCString::new();
|
||||
result.take_from(s);
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
struct CallbackClosure {
|
||||
cb: Box<dyn FnOnce(io::Result<&mut nsACString>)>,
|
||||
}
|
||||
|
||||
impl CallbackClosure {
|
||||
fn new<CB>(cb: CB) -> *mut Self
|
||||
where
|
||||
CB: FnOnce(io::Result<&mut nsACString>) + 'static,
|
||||
{
|
||||
let boxed = Box::new(Self {
|
||||
cb: Box::new(cb) as Box<dyn FnOnce(io::Result<&mut nsACString>)>,
|
||||
});
|
||||
Box::into_raw(boxed)
|
||||
}
|
||||
|
||||
fn call(self: Box<Self>, s: io::Result<&mut nsACString>) {
|
||||
(self.cb)(s)
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" fn load_async_cb(closure: *mut c_void, string: *mut nsACString, success: nsresult) {
|
||||
let result = success.to_io_result().map(|_| unsafe { &mut *string });
|
||||
let closure = unsafe {
|
||||
debug_assert_ne!(closure, std::ptr::null_mut());
|
||||
Box::from_raw(closure as *mut CallbackClosure)
|
||||
};
|
||||
closure.call(result);
|
||||
}
|
||||
|
||||
pub async fn load_async(path: impl nsCStringLike) -> io::Result<nsCString> {
|
||||
let (sender, receiver) = channel::<io::Result<nsCString>>();
|
||||
|
||||
let closure = CallbackClosure::new(move |result| {
|
||||
let result = result.map(ffi::take_nsACString);
|
||||
sender.send(result).expect("Failed to send result");
|
||||
});
|
||||
|
||||
ffi::load(&*path.adapt(), Some(load_async_cb), closure as *mut c_void)?;
|
||||
|
||||
let result = receiver
|
||||
.await
|
||||
.map_err(|_| Error::new(ErrorKind::Interrupted, "canceled"))?;
|
||||
result
|
||||
}
|
||||
|
||||
pub fn load_sync(path: impl nsCStringLike) -> io::Result<nsCString> {
|
||||
let mut result = nsCString::new();
|
||||
ffi::load_sync(&*path.adapt(), &mut result)?;
|
||||
Ok(result)
|
||||
}
|
@ -38,6 +38,7 @@ with_dbus = ["gkrust-shared/with_dbus"]
|
||||
|
||||
[dependencies]
|
||||
bench-collections-gtest = { path = "../../../../xpcom/rust/gtest/bench-collections" }
|
||||
l10nregistry-ffi-gtest = { path = "../../../../intl/l10n/rust/gtest" }
|
||||
moz_task-gtest = { path = "../../../../xpcom/rust/gtest/moz_task" }
|
||||
mp4parse-gtest = { path = "../../../../dom/media/gtest" }
|
||||
nsstring-gtest = { path = "../../../../xpcom/rust/gtest/nsstring" }
|
||||
|
@ -8,6 +8,7 @@ extern crate fog_gtest;
|
||||
#[cfg(feature = "libfuzzer")]
|
||||
extern crate gecko_fuzz_targets;
|
||||
extern crate gkrust_shared;
|
||||
extern crate l10nregistry_ffi_gtest;
|
||||
extern crate moz_task_gtest;
|
||||
extern crate mp4parse_gtest;
|
||||
extern crate nsstring_gtest;
|
||||
|
@ -65,6 +65,7 @@ rusqlite = { version = "0.24.1", features = ["modern_sqlite", "in_gecko"] }
|
||||
fluent = { version = "0.13.1", features = ["fluent-pseudo"] }
|
||||
fluent-ffi = { path = "../../../../intl/l10n/rust/fluent-ffi" }
|
||||
firefox-accounts-bridge = { path = "../../../../services/fxaccounts/rust-bridge/firefox-accounts-bridge", optional=true }
|
||||
l10nregistry-ffi = { path = "../../../../intl/l10n/rust/l10nregistry-ffi" }
|
||||
|
||||
processtools = { path = "../../../components/processtools" }
|
||||
qcms = { path = "../../../../gfx/qcms" }
|
||||
|
@ -34,6 +34,7 @@ extern crate gkrust_utils;
|
||||
extern crate http_sfv;
|
||||
extern crate jsrust_shared;
|
||||
extern crate kvstore;
|
||||
extern crate l10nregistry_ffi;
|
||||
extern crate mapped_hyph;
|
||||
extern crate mozurl;
|
||||
extern crate mp4parse_capi;
|
||||
|
Loading…
Reference in New Issue
Block a user