mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 04:41:11 +00:00
Bug 1922351 - Move ffi serde serialization from Servo into somewhere usable by other crates in the tree. r=nika
Differential Revision: https://phabricator.services.mozilla.com/D224394
This commit is contained in:
parent
e04c8b5dbf
commit
4f1b4c38f6
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -2273,11 +2273,11 @@ name = "geckoservo"
|
||||
version = "0.0.1"
|
||||
dependencies = [
|
||||
"atomic_refcell",
|
||||
"bincode",
|
||||
"cssparser",
|
||||
"cstr",
|
||||
"dom",
|
||||
"gecko-profiler",
|
||||
"ipdl_utils",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"log",
|
||||
@ -2402,6 +2402,7 @@ dependencies = [
|
||||
"gkrust_utils",
|
||||
"http_sfv",
|
||||
"idna_glue",
|
||||
"ipdl_utils",
|
||||
"jog",
|
||||
"jsrust_shared",
|
||||
"kvstore",
|
||||
@ -3191,6 +3192,13 @@ dependencies = [
|
||||
"sha2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ipdl_utils"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bincode",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.10.5"
|
||||
|
@ -45,6 +45,7 @@ exclude = [
|
||||
"tools/fuzzing/rust",
|
||||
"dom/base/rust",
|
||||
"dom/origin-trials/ffi",
|
||||
"ipc/rust/ipdl_utils",
|
||||
|
||||
# Excluded because we don't want to vendor their dependencies.
|
||||
"intl/l10n/rust/l10nregistry-tests",
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "mozilla/dom/WebGLIpdl.h"
|
||||
#include "mozilla/ipc/ByteBuf.h"
|
||||
#include "mozilla/ipc/ProtocolMessageUtils.h"
|
||||
#include "mozilla/ipc/RustMessageUtils.h"
|
||||
#include "mozilla/layers/APZInputBridge.h"
|
||||
#include "mozilla/layers/AsyncDragMetrics.h"
|
||||
#include "mozilla/layers/CompositorOptions.h"
|
||||
@ -1159,44 +1160,23 @@ struct ParamTraits<mozilla::layers::DoubleTapToZoomMetrics> {
|
||||
}
|
||||
};
|
||||
|
||||
#define IMPL_PARAMTRAITS_BY_SERDE(type_) \
|
||||
template <> \
|
||||
struct ParamTraits<mozilla::type_> { \
|
||||
typedef mozilla::type_ paramType; \
|
||||
static void Write(MessageWriter* aWriter, const paramType& aParam) { \
|
||||
mozilla::ipc::ByteBuf v; \
|
||||
mozilla::DebugOnly<bool> rv = Servo_##type_##_Serialize(&aParam, &v); \
|
||||
MOZ_ASSERT(rv, "Serialize ##type_## failed"); \
|
||||
WriteParam(aWriter, std::move(v)); \
|
||||
} \
|
||||
static ReadResult<paramType> Read(MessageReader* aReader) { \
|
||||
mozilla::ipc::ByteBuf in; \
|
||||
ReadResult<paramType> result; \
|
||||
if (!ReadParam(aReader, &in) || !in.mData) { \
|
||||
return result; \
|
||||
} \
|
||||
/* TODO: Should be able to initialize `result` in-place instead */ \
|
||||
mozilla::AlignedStorage2<paramType> value; \
|
||||
if (!Servo_##type_##_Deserialize(&in, value.addr())) { \
|
||||
return result; \
|
||||
} \
|
||||
result = std::move(*value.addr()); \
|
||||
value.addr()->~paramType(); \
|
||||
return result; \
|
||||
} \
|
||||
};
|
||||
|
||||
IMPL_PARAMTRAITS_BY_SERDE(LengthPercentage)
|
||||
IMPL_PARAMTRAITS_BY_SERDE(StyleOffsetPath)
|
||||
IMPL_PARAMTRAITS_BY_SERDE(StyleOffsetRotate)
|
||||
IMPL_PARAMTRAITS_BY_SERDE(StylePositionOrAuto)
|
||||
IMPL_PARAMTRAITS_BY_SERDE(StyleOffsetPosition)
|
||||
IMPL_PARAMTRAITS_BY_SERDE(StyleRotate)
|
||||
IMPL_PARAMTRAITS_BY_SERDE(StyleScale)
|
||||
IMPL_PARAMTRAITS_BY_SERDE(StyleTranslate)
|
||||
IMPL_PARAMTRAITS_BY_SERDE(StyleTransform)
|
||||
IMPL_PARAMTRAITS_BY_SERDE(StyleComputedTimingFunction)
|
||||
|
||||
} /* namespace IPC */
|
||||
|
||||
#define DEFINE_SERVO_PARAMTRAITS(ty_) \
|
||||
MOZ_DEFINE_RUST_PARAMTRAITS(mozilla::ty_, Servo_##ty_##_Serialize, \
|
||||
Servo_##ty_##_Deserialize)
|
||||
|
||||
DEFINE_SERVO_PARAMTRAITS(LengthPercentage)
|
||||
DEFINE_SERVO_PARAMTRAITS(StyleOffsetPath)
|
||||
DEFINE_SERVO_PARAMTRAITS(StyleOffsetRotate)
|
||||
DEFINE_SERVO_PARAMTRAITS(StylePositionOrAuto)
|
||||
DEFINE_SERVO_PARAMTRAITS(StyleOffsetPosition)
|
||||
DEFINE_SERVO_PARAMTRAITS(StyleRotate)
|
||||
DEFINE_SERVO_PARAMTRAITS(StyleScale)
|
||||
DEFINE_SERVO_PARAMTRAITS(StyleTranslate)
|
||||
DEFINE_SERVO_PARAMTRAITS(StyleTransform)
|
||||
DEFINE_SERVO_PARAMTRAITS(StyleComputedTimingFunction)
|
||||
|
||||
#undef DEFINE_SERVO_PARAMTRAITS
|
||||
|
||||
#endif /* mozilla_layers_LayersMessageUtils */
|
||||
|
42
ipc/glue/RustMessageUtils.h
Normal file
42
ipc/glue/RustMessageUtils.h
Normal file
@ -0,0 +1,42 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* 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/. */
|
||||
|
||||
#ifndef mozilla_ipc_RustMessageUtils_h
|
||||
#define mozilla_ipc_RustMessageUtils_h
|
||||
|
||||
#include "chrome/common/ipc_message_utils.h"
|
||||
|
||||
// Some macros for rust integrations. See ipc/rust/ipdl_utils
|
||||
#define MOZ_DEFINE_RUST_PARAMTRAITS(type_, serializer_, deserializer_) \
|
||||
extern "C" uint8_t* serializer_(const type_*, size_t* len, size_t* cap); \
|
||||
extern "C" bool deserializer_(const uint8_t*, size_t len, type_*); \
|
||||
\
|
||||
template <> \
|
||||
struct IPC::ParamTraits<type_> { \
|
||||
using paramType = type_; \
|
||||
static void Write(IPC::MessageWriter* aWriter, const paramType& aParam) { \
|
||||
size_t len, cap; \
|
||||
uint8_t* buf = serializer_(&aParam, &cap, &len); \
|
||||
MOZ_DIAGNOSTIC_ASSERT(buf, #type_ " serialization failed"); \
|
||||
WriteParam(aWriter, mozilla::ipc::ByteBuf(buf, len, cap)); \
|
||||
} \
|
||||
static IPC::ReadResult<paramType> Read(IPC::MessageReader* aReader) { \
|
||||
mozilla::ipc::ByteBuf in; \
|
||||
IPC::ReadResult<paramType> result; \
|
||||
if (!ReadParam(aReader, &in) || !in.mData) { \
|
||||
return result; \
|
||||
} \
|
||||
/* TODO: Should be able to initialize `result` in-place instead */ \
|
||||
mozilla::AlignedStorage2<paramType> value; \
|
||||
if (!deserializer_(in.mData, in.mLen, value.addr())) { \
|
||||
return result; \
|
||||
} \
|
||||
result = std::move(*value.addr()); \
|
||||
value.addr()->~paramType(); \
|
||||
return result; \
|
||||
} \
|
||||
};
|
||||
|
||||
#endif
|
@ -54,6 +54,7 @@ EXPORTS.mozilla.ipc += [
|
||||
"ProtocolUtils.h",
|
||||
"RandomAccessStreamUtils.h",
|
||||
"RawShmem.h",
|
||||
"RustMessageUtils.h",
|
||||
"ScopedPort.h",
|
||||
"SerializedStructuredCloneBuffer.h",
|
||||
"SharedMemory.h",
|
||||
|
14
ipc/rust/ipdl_utils/Cargo.toml
Normal file
14
ipc/rust/ipdl_utils/Cargo.toml
Normal file
@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "ipdl_utils"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
authors = [
|
||||
"Emilio Cobos Álvarez <emilio@crisal.io>",
|
||||
]
|
||||
license = "MPL-2.0"
|
||||
|
||||
[lib]
|
||||
path = "lib.rs"
|
||||
|
||||
[dependencies]
|
||||
bincode = "1"
|
47
ipc/rust/ipdl_utils/lib.rs
Normal file
47
ipc/rust/ipdl_utils/lib.rs
Normal file
@ -0,0 +1,47 @@
|
||||
/* 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/. */
|
||||
|
||||
#[doc(hidden)]
|
||||
pub use bincode as _bincode;
|
||||
|
||||
/// Takes a type, a serializer, and a deserializer name. The type must implement serde::Serialize.
|
||||
/// Defines a pair of ffi functions that go along with the MOZ_DEFINE_RUST_PARAMTRAITS macro.
|
||||
#[macro_export]
|
||||
macro_rules! define_ffi_serializer {
|
||||
($ty:ty, $serializer:ident, $deserializer:ident) => {
|
||||
#[no_mangle]
|
||||
pub extern "C" fn $serializer(
|
||||
v: &$ty,
|
||||
out_len: &mut usize,
|
||||
out_cap: &mut usize,
|
||||
) -> *mut u8 {
|
||||
*out_len = 0;
|
||||
*out_cap = 0;
|
||||
let mut buf = match $crate::_bincode::serialize(v) {
|
||||
Ok(buf) => buf,
|
||||
Err(..) => return std::ptr::null_mut(),
|
||||
};
|
||||
*out_len = buf.len();
|
||||
*out_cap = buf.capacity();
|
||||
let ptr = buf.as_mut_ptr();
|
||||
std::mem::forget(buf);
|
||||
ptr
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn $deserializer(
|
||||
input: *const u8,
|
||||
len: usize,
|
||||
out: *mut $ty,
|
||||
) -> bool {
|
||||
let slice = std::slice::from_raw_parts(input, len);
|
||||
let element = match $crate::_bincode::deserialize(slice) {
|
||||
Ok(buf) => buf,
|
||||
Err(..) => return false,
|
||||
};
|
||||
std::ptr::write(out, element);
|
||||
true
|
||||
}
|
||||
};
|
||||
}
|
@ -103,23 +103,6 @@ BASIC_RULE_FUNCS_LOCKED(NestedDeclarations)
|
||||
#undef BASIC_RULE_FUNCS_WITHOUT_GETTER_UNLOCKED
|
||||
#undef BASIC_RULE_FUNCS_WITHOUT_GETTER_WITH_PREFIX
|
||||
|
||||
#define BASIC_SERDE_FUNCS(type_) \
|
||||
bool Servo_##type_##_Deserialize(mozilla::ipc::ByteBuf* input, type_* v); \
|
||||
bool Servo_##type_##_Serialize(const type_* v, mozilla::ipc::ByteBuf* output);
|
||||
|
||||
BASIC_SERDE_FUNCS(LengthPercentage)
|
||||
BASIC_SERDE_FUNCS(StyleRotate)
|
||||
BASIC_SERDE_FUNCS(StyleScale)
|
||||
BASIC_SERDE_FUNCS(StyleTranslate)
|
||||
BASIC_SERDE_FUNCS(StyleTransform)
|
||||
BASIC_SERDE_FUNCS(StyleOffsetPath)
|
||||
BASIC_SERDE_FUNCS(StyleOffsetRotate)
|
||||
BASIC_SERDE_FUNCS(StylePositionOrAuto)
|
||||
BASIC_SERDE_FUNCS(StyleOffsetPosition)
|
||||
BASIC_SERDE_FUNCS(StyleComputedTimingFunction)
|
||||
|
||||
#undef BASIC_SERDE_FUNCS
|
||||
|
||||
void Servo_CounterStyleRule_GetDescriptorCssText(
|
||||
const StyleLockedCounterStyleRule* rule, nsCSSCounterDesc desc,
|
||||
nsACString* result);
|
||||
|
@ -12,7 +12,6 @@ headers = [
|
||||
"mozilla/dom/MediaList.h",
|
||||
"mozilla/dom/ShadowRoot.h",
|
||||
"mozilla/gfx/FontFeature.h",
|
||||
"mozilla/ipc/ByteBuf.h",
|
||||
"mozilla/AnimationPropertySegment.h",
|
||||
"mozilla/ComputedTiming.h",
|
||||
"mozilla/CORSMode.h",
|
||||
@ -209,7 +208,6 @@ allowlist-types = [
|
||||
"mozilla::dom::IterationCompositeOperation",
|
||||
"mozilla::dom::StyleChildrenIterator",
|
||||
"mozilla::HalfCorner",
|
||||
"mozilla::ipc::ByteBuf",
|
||||
"mozilla::MallocSizeOf",
|
||||
"mozilla::OriginFlags",
|
||||
"mozilla::PropertyStyleAnimationValuePair",
|
||||
|
@ -125,10 +125,6 @@ class ImageTracker;
|
||||
|
||||
} // namespace dom
|
||||
|
||||
namespace ipc {
|
||||
class ByteBuf;
|
||||
} // namespace ipc
|
||||
|
||||
// Replacement for a Rust Box<T> for a non-dynamically-sized-type.
|
||||
//
|
||||
// TODO(emilio): If this was some sort of nullable box then this could be made
|
||||
|
@ -3,6 +3,7 @@ name = "geckoservo"
|
||||
version = "0.0.1"
|
||||
authors = ["The Servo Project Developers"]
|
||||
license = "MPL-2.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
name = "geckoservo"
|
||||
@ -14,10 +15,10 @@ gecko_refcount_logging = ["style/gecko_refcount_logging", "servo_arc/gecko_refco
|
||||
|
||||
[dependencies]
|
||||
atomic_refcell = "0.1"
|
||||
bincode = "1.0"
|
||||
cssparser = "0.34"
|
||||
cstr = "0.2"
|
||||
dom = { path = "../../../dom/base/rust" }
|
||||
ipdl_utils = { path = "../../../ipc/rust/ipdl_utils" }
|
||||
gecko-profiler = { path = "../../../tools/profiler/rust-api" }
|
||||
libc = "0.2"
|
||||
log = {version = "0.4", features = ["release_max_level_info"]}
|
||||
|
@ -4,7 +4,6 @@
|
||||
|
||||
use super::error_reporter::ErrorReporter;
|
||||
use super::stylesheet_loader::{AsyncStylesheetParser, StylesheetLoader};
|
||||
use bincode::{deserialize, serialize};
|
||||
use cssparser::ToCss as ParserToCss;
|
||||
use cssparser::{
|
||||
BasicParseError, ParseError as CssParseError, Parser, ParserInput, ParserState, SourceLocation,
|
||||
@ -60,7 +59,6 @@ use style::gecko_bindings::bindings::Gecko_HaveSeenPtr;
|
||||
use style::gecko_bindings::structs;
|
||||
use style::gecko_bindings::structs::gfx::FontPaletteValueSet;
|
||||
use style::gecko_bindings::structs::gfxFontFeatureValueSet;
|
||||
use style::gecko_bindings::structs::ipc::ByteBuf;
|
||||
use style::gecko_bindings::structs::nsAtom;
|
||||
use style::gecko_bindings::structs::nsCSSCounterDesc;
|
||||
use style::gecko_bindings::structs::nsCSSFontDesc;
|
||||
@ -994,110 +992,64 @@ pub extern "C" fn Servo_AnimationValue_Uncompute(
|
||||
.into()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn create_byte_buf_from_vec(mut v: Vec<u8>) -> ByteBuf {
|
||||
let w = ByteBuf {
|
||||
mData: v.as_mut_ptr(),
|
||||
mLen: v.len(),
|
||||
mCapacity: v.capacity(),
|
||||
};
|
||||
std::mem::forget(v);
|
||||
w
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn view_byte_buf(b: &ByteBuf) -> &[u8] {
|
||||
if b.mData.is_null() {
|
||||
debug_assert_eq!(b.mCapacity, 0);
|
||||
return &[];
|
||||
}
|
||||
unsafe { std::slice::from_raw_parts(b.mData, b.mLen) }
|
||||
}
|
||||
|
||||
macro_rules! impl_basic_serde_funcs {
|
||||
($ser_name:ident, $de_name:ident, $computed_type:ty) => {
|
||||
#[no_mangle]
|
||||
pub extern "C" fn $ser_name(v: &$computed_type, output: &mut ByteBuf) -> bool {
|
||||
let buf = match serialize(v) {
|
||||
Ok(buf) => buf,
|
||||
Err(..) => return false,
|
||||
};
|
||||
|
||||
*output = create_byte_buf_from_vec(buf);
|
||||
true
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn $de_name(input: &ByteBuf, v: *mut $computed_type) -> bool {
|
||||
let buf = match deserialize(view_byte_buf(input)) {
|
||||
Ok(buf) => buf,
|
||||
Err(..) => return false,
|
||||
};
|
||||
|
||||
std::ptr::write(v, buf);
|
||||
true
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_basic_serde_funcs!(
|
||||
ipdl_utils::define_ffi_serializer!(
|
||||
computed::LengthPercentage,
|
||||
Servo_LengthPercentage_Serialize,
|
||||
Servo_LengthPercentage_Deserialize,
|
||||
computed::LengthPercentage
|
||||
Servo_LengthPercentage_Deserialize
|
||||
);
|
||||
|
||||
impl_basic_serde_funcs!(
|
||||
ipdl_utils::define_ffi_serializer!(
|
||||
computed::transform::Rotate,
|
||||
Servo_StyleRotate_Serialize,
|
||||
Servo_StyleRotate_Deserialize,
|
||||
computed::transform::Rotate
|
||||
Servo_StyleRotate_Deserialize
|
||||
);
|
||||
|
||||
impl_basic_serde_funcs!(
|
||||
ipdl_utils::define_ffi_serializer!(
|
||||
computed::transform::Scale,
|
||||
Servo_StyleScale_Serialize,
|
||||
Servo_StyleScale_Deserialize,
|
||||
computed::transform::Scale
|
||||
Servo_StyleScale_Deserialize
|
||||
);
|
||||
|
||||
impl_basic_serde_funcs!(
|
||||
ipdl_utils::define_ffi_serializer!(
|
||||
computed::transform::Translate,
|
||||
Servo_StyleTranslate_Serialize,
|
||||
Servo_StyleTranslate_Deserialize,
|
||||
computed::transform::Translate
|
||||
Servo_StyleTranslate_Deserialize
|
||||
);
|
||||
|
||||
impl_basic_serde_funcs!(
|
||||
ipdl_utils::define_ffi_serializer!(
|
||||
computed::transform::Transform,
|
||||
Servo_StyleTransform_Serialize,
|
||||
Servo_StyleTransform_Deserialize,
|
||||
computed::transform::Transform
|
||||
Servo_StyleTransform_Deserialize
|
||||
);
|
||||
|
||||
impl_basic_serde_funcs!(
|
||||
ipdl_utils::define_ffi_serializer!(
|
||||
computed::motion::OffsetPath,
|
||||
Servo_StyleOffsetPath_Serialize,
|
||||
Servo_StyleOffsetPath_Deserialize,
|
||||
computed::motion::OffsetPath
|
||||
Servo_StyleOffsetPath_Deserialize
|
||||
);
|
||||
|
||||
impl_basic_serde_funcs!(
|
||||
ipdl_utils::define_ffi_serializer!(
|
||||
computed::motion::OffsetRotate,
|
||||
Servo_StyleOffsetRotate_Serialize,
|
||||
Servo_StyleOffsetRotate_Deserialize,
|
||||
computed::motion::OffsetRotate
|
||||
Servo_StyleOffsetRotate_Deserialize
|
||||
);
|
||||
|
||||
impl_basic_serde_funcs!(
|
||||
ipdl_utils::define_ffi_serializer!(
|
||||
computed::position::PositionOrAuto,
|
||||
Servo_StylePositionOrAuto_Serialize,
|
||||
Servo_StylePositionOrAuto_Deserialize,
|
||||
computed::position::PositionOrAuto
|
||||
Servo_StylePositionOrAuto_Deserialize
|
||||
);
|
||||
|
||||
impl_basic_serde_funcs!(
|
||||
ipdl_utils::define_ffi_serializer!(
|
||||
computed::motion::OffsetPosition,
|
||||
Servo_StyleOffsetPosition_Serialize,
|
||||
Servo_StyleOffsetPosition_Deserialize,
|
||||
computed::motion::OffsetPosition
|
||||
Servo_StyleOffsetPosition_Deserialize
|
||||
);
|
||||
|
||||
impl_basic_serde_funcs!(
|
||||
ipdl_utils::define_ffi_serializer!(
|
||||
ComputedTimingFunction,
|
||||
Servo_StyleComputedTimingFunction_Serialize,
|
||||
Servo_StyleComputedTimingFunction_Deserialize,
|
||||
ComputedTimingFunction
|
||||
Servo_StyleComputedTimingFunction_Deserialize
|
||||
);
|
||||
|
||||
// Return the ComputedValues by a base ComputedValues and the rules.
|
||||
|
@ -2,28 +2,14 @@
|
||||
* 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/. */
|
||||
|
||||
extern crate bincode;
|
||||
extern crate cssparser;
|
||||
#[macro_use]
|
||||
extern crate cstr;
|
||||
extern crate dom;
|
||||
#[macro_use]
|
||||
extern crate gecko_profiler;
|
||||
extern crate libc;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate malloc_size_of;
|
||||
extern crate nsstring;
|
||||
extern crate num_traits;
|
||||
extern crate selectors;
|
||||
extern crate servo_arc;
|
||||
extern crate smallvec;
|
||||
#[macro_use]
|
||||
extern crate style;
|
||||
extern crate style_traits;
|
||||
extern crate thin_vec;
|
||||
extern crate to_shmem;
|
||||
extern crate lazy_static;
|
||||
|
||||
mod error_reporter;
|
||||
#[allow(non_snake_case)]
|
||||
|
@ -55,6 +55,7 @@ fog_control = { path = "../../../components/glean" }
|
||||
app_services_logger = { path = "../../../../services/common/app_services_logger" }
|
||||
http_sfv = { path = "../../../../netwerk/base/http-sfv" }
|
||||
idna_glue = { path = "../../../../netwerk/base/idna_glue" }
|
||||
ipdl_utils = { path = "../../../../ipc/rust/ipdl_utils" }
|
||||
unic-langid = { version = "0.9", features = ["likelysubtags"] }
|
||||
unic-langid-ffi = { path = "../../../../intl/locale/rust/unic-langid-ffi" }
|
||||
fluent-langneg = { version = "0.13", features = ["cldr"] }
|
||||
|
@ -32,6 +32,7 @@ extern crate gecko_profiler;
|
||||
extern crate gkrust_utils;
|
||||
extern crate http_sfv;
|
||||
extern crate idna_glue;
|
||||
extern crate ipdl_utils;
|
||||
extern crate jog;
|
||||
extern crate jsrust_shared;
|
||||
extern crate kvstore;
|
||||
|
Loading…
Reference in New Issue
Block a user