Bug 1620621 - Add XPCOM FFI for rust_cascade r=emilio,vporof

Differential Revision: https://phabricator.services.mozilla.com/D66166
This commit is contained in:
Rob Wu 2020-04-29 23:26:48 +00:00
Родитель 6b57ac9c48
Коммит 4294b3bca0
13 изменённых файлов: 270 добавлений и 0 удалений

13
Cargo.lock сгенерированный
Просмотреть файл

@ -485,6 +485,18 @@ dependencies = [
"ppv-lite86",
]
[[package]]
name = "cascade_bloom_filter"
version = "0.1.0"
dependencies = [
"nserror",
"nsstring",
"rental",
"rust_cascade",
"thin-vec",
"xpcom",
]
[[package]]
name = "cast"
version = "0.2.2"
@ -1812,6 +1824,7 @@ dependencies = [
"authenticator",
"bitsdownload",
"bookmark_sync",
"cascade_bloom_filter",
"cert_storage",
"chardetng_c",
"cose-c",

Просмотреть файл

@ -0,0 +1,12 @@
[package]
name = "cascade_bloom_filter"
version = "0.1.0"
authors = ["Rob Wu <rob@robwu.nl>"]
[dependencies]
nserror = { path = "../../../xpcom/rust/nserror" }
nsstring = { path = "../../../xpcom/rust/nsstring" }
rental = "0.5.5"
rust_cascade = "0.6.0"
thin-vec = { version = "0.1.0", features = ["gecko-ffi"] }
xpcom = { path = "../../../xpcom/rust/xpcom" }

Просмотреть файл

@ -0,0 +1,25 @@
/* 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 "nsCOMPtr.h"
#include "CascadeFilter.h"
namespace {
extern "C" {
// Implemented in Rust.
void cascade_filter_construct(nsICascadeFilter** aResult);
}
} // namespace
namespace mozilla {
already_AddRefed<nsICascadeFilter> ConstructCascadeFilter() {
nsCOMPtr<nsICascadeFilter> filter;
cascade_filter_construct(getter_AddRefs(filter));
return filter.forget();
}
} // namespace mozilla

Просмотреть файл

@ -0,0 +1,17 @@
/* 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 CASCADE_BLOOM_FILTER_CASCADE_FILTER_H_
#define CASCADE_BLOOM_FILTER_CASCADE_FILTER_H_
#include "nsICascadeFilter.h"
#include "nsCOMPtr.h"
namespace mozilla {
already_AddRefed<nsICascadeFilter> ConstructCascadeFilter();
} // namespace mozilla
#endif // CASCADE_BLOOM_FILTER_CASCADE_FILTER_H_

Просмотреть файл

@ -0,0 +1,15 @@
# -*- 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/.
Classes = [
{
'cid': '{c8d0b0b3-17f8-458b-9264-7b67b288fe79}',
'contract_ids': ['@mozilla.org/cascade-filter;1'],
'type': 'nsICascadeFilter',
'headers': ['mozilla/CascadeFilter.h'],
'constructor': 'mozilla::ConstructCascadeFilter',
},
]

Просмотреть файл

@ -0,0 +1,30 @@
# -*- 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/.
with Files('**'):
BUG_COMPONENT = ('Toolkit', 'Blocklist Implementation')
XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell/xpcshell.ini']
XPIDL_SOURCES += [
'nsICascadeFilter.idl',
]
XPIDL_MODULE = 'cascade_bindings'
EXPORTS.mozilla += [
"CascadeFilter.h"
]
SOURCES += [
"CascadeFilter.cpp"
]
XPCOM_MANIFESTS += [
'components.conf',
]
FINAL_LIBRARY = 'xul'

Просмотреть файл

@ -0,0 +1,28 @@
/* 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 "nsISupports.idl"
/**
* A consumer of a filter cascade, i.e. a cascaded bloom filter as generated by
* https://github.com/mozilla/filter-cascade
*/
[scriptable, uuid(c8d0b0b3-17f8-458b-9264-7b67b288fe79)]
interface nsICascadeFilter : nsISupports {
/**
* Initialize with the data that represents the filter cascade.
* This method can be called repeatedly.
*
* @throws NS_ERROR_INVALID_ARG if the input is malformed.
*/
void setFilterData(in Array<octet> data);
/**
* Check whether a given key is a member of the filter cascade.
* The result can only be relied upon if the key was known at the time of the
* filter generation. If the key is unknown, the method may incorrectly
* return true (due to the probabilistic nature of bloom filters).
*/
boolean has(in ACString key);
};

Просмотреть файл

@ -0,0 +1,75 @@
/* 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/. */
extern crate nserror;
extern crate nsstring;
#[macro_use]
extern crate rental;
extern crate rust_cascade;
extern crate thin_vec;
#[macro_use]
extern crate xpcom;
use nserror::{nsresult, NS_ERROR_INVALID_ARG, NS_ERROR_NOT_INITIALIZED, NS_OK};
use nsstring::nsACString;
use rust_cascade::Cascade;
use std::cell::RefCell;
use thin_vec::ThinVec;
use xpcom::interfaces::nsICascadeFilter;
use xpcom::{xpcom_method, RefPtr};
// Cascade does not take ownership of the data, so we must own the data in order to pass its
// reference to Cascade.
rental! {
mod rentals {
use super::Cascade;
#[rental]
pub struct CascadeWithOwnedData {
owndata: Box<[u8]>,
cascade: Box<Cascade<'owndata>>,
}
}
}
#[derive(xpcom)]
#[xpimplements(nsICascadeFilter)]
#[refcnt = "nonatomic"]
pub struct InitCascadeFilter {
filter: RefCell<Option<rentals::CascadeWithOwnedData>>,
}
impl CascadeFilter {
fn new() -> RefPtr<CascadeFilter> {
CascadeFilter::allocate(InitCascadeFilter {
filter: RefCell::new(None),
})
}
xpcom_method!(set_filter_data => SetFilterData(data: *const ThinVec<u8>));
fn set_filter_data(&self, data: &ThinVec<u8>) -> Result<(), nsresult> {
let filter = rentals::CascadeWithOwnedData::try_new_or_drop(
Vec::from(data.as_slice()).into_boxed_slice(),
|data| Cascade::from_bytes(data).unwrap_or(None).ok_or(NS_ERROR_INVALID_ARG)
)?;
self.filter.borrow_mut().replace(filter);
Ok(())
}
xpcom_method!(has => Has(key: *const nsACString) -> bool);
fn has(&self, key: &nsACString) -> Result<bool, nsresult> {
match self.filter.borrow().as_ref() {
None => Err(NS_ERROR_NOT_INITIALIZED),
Some(filter) => Ok(filter.rent(|cascade| cascade.has(&*key))),
}
}
}
#[no_mangle]
pub unsafe extern "C" fn cascade_filter_construct(result: &mut *const nsICascadeFilter) {
let inst: RefPtr<CascadeFilter> = CascadeFilter::new();
*result = inst.coerce::<nsICascadeFilter>();
std::mem::forget(inst);
}

Просмотреть файл

@ -0,0 +1,51 @@
"use strict";
const CASCADE_CID = "@mozilla.org/cascade-filter;1";
const CASCADE_IID = Ci.nsICascadeFilter;
const CascadeFilter = Components.Constructor(CASCADE_CID, CASCADE_IID);
add_task(function CascadeFilter_uninitialized() {
let filter = new CascadeFilter();
Assert.throws(
() => filter.has(""),
e => e.result === Cr.NS_ERROR_NOT_INITIALIZED,
"Cannot use has() if the filter is not initialized"
);
});
add_task(function CascadeFilter_with_setFilterData() {
let filter = new CascadeFilter();
Assert.throws(
() => filter.setFilterData(),
e => e.result === Cr.NS_ERROR_XPC_NOT_ENOUGH_ARGS,
"setFilterData without parameters should throw"
);
Assert.throws(
() => filter.setFilterData(null),
e => e.result === Cr.NS_ERROR_XPC_CANT_CONVERT_PRIMITIVE_TO_ARRAY,
"setFilterData with null parameter is invalid"
);
Assert.throws(
() => filter.setFilterData(new Uint8Array()),
e => e.result === Cr.NS_ERROR_INVALID_ARG,
"setFilterData with empty array is invalid"
);
// Test data based on rust_cascade's unit tests (bloom_v1_test_from_bytes),
// with two bytes in front to have a valid format.
const TEST_DATA = [1, 0, 1, 9, 0, 0, 0, 1, 0, 0, 0, 1, 0x41, 0];
Assert.throws(
() => filter.setFilterData(new Uint8Array(TEST_DATA.slice(1))),
e => e.result === Cr.NS_ERROR_INVALID_ARG,
"setFilterData with invalid data (missing head) is invalid"
);
Assert.throws(
() => filter.setFilterData(new Uint8Array(TEST_DATA.slice(0, -1))),
e => e.result === Cr.NS_ERROR_INVALID_ARG,
"setFilterData with invalid data (missing tail) is invalid"
);
filter.setFilterData(new Uint8Array(TEST_DATA));
Assert.equal(filter.has("this"), true, "has(this) should be true");
Assert.equal(filter.has("that"), true, "has(that) should be true");
Assert.equal(filter.has("other"), false, "has(other) should be false");
});

Просмотреть файл

@ -0,0 +1 @@
[test_cascade_bindings.js]

Просмотреть файл

@ -23,6 +23,7 @@ DIRS += [
'backgroundhangmonitor',
'bitsdownload',
'browser',
'cascade_bloom_filter',
'certviewer',
'cleardata',
'clearsitedata',

Просмотреть файл

@ -34,6 +34,7 @@ log = {version = "0.4", features = ["release_max_level_info"]}
env_logger = {version = "0.6", default-features = false} # disable `regex` to reduce code size
cose-c = { version = "0.1.5" }
jsrust_shared = { path = "../../../../js/src/rust/shared" }
cascade_bloom_filter = { path = "../../../components/cascade_bloom_filter" }
cert_storage = { path = "../../../../security/manager/ssl/cert_storage", optional = true }
bitsdownload = { path = "../../../components/bitsdownload", optional = true }
storage = { path = "../../../../storage/rust" }

Просмотреть файл

@ -15,6 +15,7 @@ extern crate authenticator;
extern crate bitsdownload;
#[cfg(feature = "moz_places")]
extern crate bookmark_sync;
extern crate cascade_bloom_filter;
#[cfg(feature = "new_cert_storage")]
extern crate cert_storage;
extern crate chardetng_c;