forked from mirrors/gecko-dev
Bug 1777973: Remove MOZ_NEW_XULSTORE implementation. r=NeilDeakin
Differential Revision: https://phabricator.services.mozilla.com/D166786
This commit is contained in:
parent
ead5ce5973
commit
1135edff9e
31 changed files with 21 additions and 1807 deletions
20
Cargo.lock
generated
20
Cargo.lock
generated
|
|
@ -2271,7 +2271,6 @@ dependencies = [
|
|||
"wgpu_bindings",
|
||||
"wpf-gpu-raster",
|
||||
"xpcom",
|
||||
"xulstore",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
@ -6585,25 +6584,6 @@ dependencies = [
|
|||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xulstore"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"crossbeam-utils 0.8.14",
|
||||
"cstr",
|
||||
"libc",
|
||||
"log",
|
||||
"moz_task",
|
||||
"nserror",
|
||||
"nsstring",
|
||||
"once_cell",
|
||||
"rkv",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
"thiserror",
|
||||
"xpcom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "yaml-rust"
|
||||
version = "0.4.5"
|
||||
|
|
|
|||
|
|
@ -6,13 +6,9 @@
|
|||
|
||||
#include "XULPersist.h"
|
||||
|
||||
#ifdef MOZ_NEW_XULSTORE
|
||||
# include "mozilla/XULStore.h"
|
||||
#else
|
||||
# include "nsIXULStore.h"
|
||||
# include "nsIStringEnumerator.h"
|
||||
# include "nsServiceManagerUtils.h"
|
||||
#endif
|
||||
#include "nsIXULStore.h"
|
||||
#include "nsIStringEnumerator.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "mozilla/BasePrincipal.h"
|
||||
#include "mozilla/dom/Document.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
|
|
@ -98,14 +94,12 @@ void XULPersist::Persist(Element* aElement, nsAtom* aAttribute) {
|
|||
return;
|
||||
}
|
||||
|
||||
#ifndef MOZ_NEW_XULSTORE
|
||||
if (!mLocalStore) {
|
||||
mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
|
||||
if (NS_WARN_IF(!mLocalStore)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
nsAutoString id;
|
||||
|
||||
|
|
@ -132,11 +126,7 @@ void XULPersist::Persist(Element* aElement, nsAtom* aAttribute) {
|
|||
valuestr = kMissingAttributeToken;
|
||||
}
|
||||
|
||||
#ifdef MOZ_NEW_XULSTORE
|
||||
rv = XULStore::SetValue(uri, id, attrstr, valuestr);
|
||||
#else
|
||||
mLocalStore->SetValue(uri, id, attrstr, valuestr);
|
||||
#endif
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "value set");
|
||||
}
|
||||
|
||||
|
|
@ -151,14 +141,12 @@ nsresult XULPersist::ApplyPersistentAttributes() {
|
|||
|
||||
// Add all of the 'persisted' attributes into the content
|
||||
// model.
|
||||
#ifndef MOZ_NEW_XULSTORE
|
||||
if (!mLocalStore) {
|
||||
mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
|
||||
if (NS_WARN_IF(!mLocalStore)) {
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
nsCOMArray<Element> elements;
|
||||
|
||||
|
|
@ -170,35 +158,16 @@ nsresult XULPersist::ApplyPersistentAttributes() {
|
|||
NS_ConvertUTF8toUTF16 uri(utf8uri);
|
||||
|
||||
// Get a list of element IDs for which persisted values are available
|
||||
#ifdef MOZ_NEW_XULSTORE
|
||||
UniquePtr<XULStoreIterator> ids;
|
||||
rv = XULStore::GetIDs(uri, ids);
|
||||
#else
|
||||
nsCOMPtr<nsIStringEnumerator> ids;
|
||||
rv = mLocalStore->GetIDsEnumerator(uri, getter_AddRefs(ids));
|
||||
#endif
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
#ifdef MOZ_NEW_XULSTORE
|
||||
while (ids->HasMore()) {
|
||||
nsAutoString id;
|
||||
rv = ids->GetNext(&id);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
#else
|
||||
while (1) {
|
||||
bool hasmore = false;
|
||||
ids->HasMore(&hasmore);
|
||||
if (!hasmore) {
|
||||
break;
|
||||
}
|
||||
|
||||
bool hasmore;
|
||||
while (NS_SUCCEEDED(ids->HasMore(&hasmore)) && hasmore) {
|
||||
nsAutoString id;
|
||||
ids->GetNext(id);
|
||||
#endif
|
||||
|
||||
// We want to hold strong refs to the elements while applying
|
||||
// persistent attributes, just in case.
|
||||
|
|
@ -226,41 +195,19 @@ nsresult XULPersist::ApplyPersistentAttributesToElements(
|
|||
nsCOMArray<Element>& aElements) {
|
||||
nsresult rv = NS_OK;
|
||||
// Get a list of attributes for which persisted values are available
|
||||
#ifdef MOZ_NEW_XULSTORE
|
||||
UniquePtr<XULStoreIterator> attrs;
|
||||
rv = XULStore::GetAttrs(aDocURI, aID, attrs);
|
||||
#else
|
||||
nsCOMPtr<nsIStringEnumerator> attrs;
|
||||
rv = mLocalStore->GetAttributeEnumerator(aDocURI, aID, getter_AddRefs(attrs));
|
||||
#endif
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
#ifdef MOZ_NEW_XULSTORE
|
||||
while (attrs->HasMore()) {
|
||||
nsAutoString attrstr;
|
||||
rv = attrs->GetNext(&attrstr);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsAutoString value;
|
||||
rv = XULStore::GetValue(aDocURI, aID, attrstr, value);
|
||||
#else
|
||||
while (1) {
|
||||
bool hasmore = PR_FALSE;
|
||||
attrs->HasMore(&hasmore);
|
||||
if (!hasmore) {
|
||||
break;
|
||||
}
|
||||
|
||||
bool hasmore;
|
||||
while (NS_SUCCEEDED(attrs->HasMore(&hasmore)) && hasmore) {
|
||||
nsAutoString attrstr;
|
||||
attrs->GetNext(attrstr);
|
||||
|
||||
nsAutoString value;
|
||||
rv = mLocalStore->GetValue(aDocURI, aID, attrstr, value);
|
||||
#endif
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,9 +39,7 @@ class XULPersist final : public nsStubDocumentObserver {
|
|||
const nsAString& aDocURI,
|
||||
nsCOMArray<Element>& aElements);
|
||||
|
||||
#ifndef MOZ_NEW_XULSTORE
|
||||
nsCOMPtr<nsIXULStore> mLocalStore;
|
||||
#endif
|
||||
|
||||
// A weak pointer to our document. Nulled out by DropDocumentReference.
|
||||
Document* MOZ_NON_OWNING_REF mDocument;
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
[package]
|
||||
name = "xulstore"
|
||||
version = "0.1.0"
|
||||
authors = ["The Mozilla Project Developers"]
|
||||
license = "MPL-2.0"
|
||||
|
||||
[dependencies]
|
||||
crossbeam-utils = "0.8"
|
||||
cstr = "0.2"
|
||||
libc = "0.2"
|
||||
log = "0.4"
|
||||
moz_task = { path = "../../../xpcom/rust/moz_task" }
|
||||
nsstring = { path = "../../../xpcom/rust/nsstring" }
|
||||
nserror = { path = "../../../xpcom/rust/nserror" }
|
||||
once_cell = "1"
|
||||
rkv = { version = "0.18", default-features = false, features=["no-canonicalize-path"] }
|
||||
serde_json = "1"
|
||||
tempfile = "3"
|
||||
thiserror = "1"
|
||||
xpcom = { path = "../../../xpcom/rust/xpcom" }
|
||||
|
|
@ -4,32 +4,13 @@
|
|||
# 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/.
|
||||
|
||||
if defined('MOZ_NEW_XULSTORE'):
|
||||
Classes = [
|
||||
{
|
||||
'cid': '{be70bf11-0c28-4a02-a38c-0148538d42cf}',
|
||||
'contract_ids': ['@mozilla.org/xul/xulstore;1'],
|
||||
'type': 'nsIXULStore',
|
||||
'headers': ['mozilla/XULStore.h'],
|
||||
'singleton': True,
|
||||
'constructor': 'mozilla::XULStore::GetService',
|
||||
},
|
||||
{
|
||||
'js_name': 'xulStore',
|
||||
'cid': '{e8e12dba-b942-4c0d-aa21-2843cfc64529}',
|
||||
'contract_ids': ['@mozilla.org/xul/js-xulstore;1'],
|
||||
'jsm': 'resource://gre/modules/XULStore.jsm',
|
||||
'constructor': 'getXULStore',
|
||||
},
|
||||
]
|
||||
else:
|
||||
Classes = [
|
||||
{
|
||||
'js_name': 'xulStore',
|
||||
'cid': '{6f46b6f4-c8b1-4bd4-a4fa-9ebbed0753ea}',
|
||||
'contract_ids': ['@mozilla.org/xul/xulstore;1'],
|
||||
'interfaces': ['nsIXULStore'],
|
||||
'jsm': 'resource://gre/modules/XULStore.jsm',
|
||||
'constructor': 'XULStore',
|
||||
},
|
||||
]
|
||||
Classes = [
|
||||
{
|
||||
'js_name': 'xulStore',
|
||||
'cid': '{6f46b6f4-c8b1-4bd4-a4fa-9ebbed0753ea}',
|
||||
'contract_ids': ['@mozilla.org/xul/xulstore;1'],
|
||||
'interfaces': ['nsIXULStore'],
|
||||
'jsm': 'resource://gre/modules/XULStore.jsm',
|
||||
'constructor': 'XULStore',
|
||||
},
|
||||
]
|
||||
|
|
|
|||
|
|
@ -20,25 +20,6 @@ XPCOM_MANIFESTS += [
|
|||
"components.conf",
|
||||
]
|
||||
|
||||
if CONFIG["MOZ_NEW_XULSTORE"]:
|
||||
EXTRA_JS_MODULES += [
|
||||
"new/XULStore.jsm",
|
||||
]
|
||||
|
||||
TEST_DIRS += [
|
||||
"tests/gtest",
|
||||
]
|
||||
|
||||
EXPORTS.mozilla += [
|
||||
"XULStore.h",
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
"XULStore.cpp",
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = "xul"
|
||||
else:
|
||||
EXTRA_JS_MODULES += [
|
||||
"old/XULStore.jsm",
|
||||
]
|
||||
EXTRA_JS_MODULES += [
|
||||
"XULStore.jsm",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -1,107 +0,0 @@
|
|||
/* 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 strict";
|
||||
|
||||
// This JS module wraps the nsIXULStore XPCOM service with useful abstractions.
|
||||
// In particular, it wraps the enumerators returned by getIDsEnumerator()
|
||||
// and getAttributeEnumerator() in JS objects that implement the iterable
|
||||
// protocol. It also implements the persist() method. JS consumers should use
|
||||
// this module rather than accessing nsIXULStore directly.
|
||||
|
||||
const EXPORTED_SYMBOLS = ["XULStore", "getXULStore"];
|
||||
|
||||
// Services.xulStore loads this module and returns its `XULStore` symbol
|
||||
// when this implementation of XULStore is enabled, so using it here
|
||||
// would loop infinitely. But the mozilla/use-services rule is a good
|
||||
// requiremnt for every other consumer of XULStore.
|
||||
// eslint-disable-next-line mozilla/use-services
|
||||
const xulStore = Cc["@mozilla.org/xul/xulstore;1"].getService(Ci.nsIXULStore);
|
||||
|
||||
// Enables logging.
|
||||
const debugMode = false;
|
||||
|
||||
// Internal function for logging debug messages to the Error Console window
|
||||
function log(message) {
|
||||
if (!debugMode) {
|
||||
return;
|
||||
}
|
||||
console.log("XULStore: " + message);
|
||||
}
|
||||
|
||||
const XULStore = {
|
||||
setValue: xulStore.setValue,
|
||||
hasValue: xulStore.hasValue,
|
||||
getValue: xulStore.getValue,
|
||||
removeValue: xulStore.removeValue,
|
||||
removeDocument: xulStore.removeDocument,
|
||||
|
||||
/**
|
||||
* Sets a value for a specified node's attribute, except in
|
||||
* the case below:
|
||||
* If the value is empty and if calling `hasValue` with the node's
|
||||
* document and ID and `attr` would return true, then the
|
||||
* value instead gets removed from the store (see Bug 1476680).
|
||||
*
|
||||
* @param node - DOM node
|
||||
* @param attr - attribute to store
|
||||
*/
|
||||
persist(node, attr) {
|
||||
if (!node.id) {
|
||||
throw new Error("Node without ID passed into persist()");
|
||||
}
|
||||
|
||||
const uri = node.ownerDocument.documentURI;
|
||||
const value = node.getAttribute(attr);
|
||||
|
||||
if (node.localName == "window") {
|
||||
log("Persisting attributes to windows is handled by AppWindow.");
|
||||
return;
|
||||
}
|
||||
|
||||
// See Bug 1476680 - we could drop the `hasValue` check so that
|
||||
// any time there's an empty attribute it gets removed from the
|
||||
// store. Since this is copying behavior from document.persist,
|
||||
// callers would need to be updated with that change.
|
||||
if (!value && xulStore.hasValue(uri, node.id, attr)) {
|
||||
xulStore.removeValue(uri, node.id, attr);
|
||||
} else {
|
||||
xulStore.setValue(uri, node.id, attr, value);
|
||||
}
|
||||
},
|
||||
|
||||
getIDsEnumerator(docURI) {
|
||||
return new XULStoreEnumerator(xulStore.getIDsEnumerator(docURI));
|
||||
},
|
||||
|
||||
getAttributeEnumerator(docURI, id) {
|
||||
return new XULStoreEnumerator(xulStore.getAttributeEnumerator(docURI, id));
|
||||
},
|
||||
};
|
||||
|
||||
class XULStoreEnumerator {
|
||||
constructor(enumerator) {
|
||||
this.enumerator = enumerator;
|
||||
}
|
||||
|
||||
hasMore() {
|
||||
return this.enumerator.hasMore();
|
||||
}
|
||||
|
||||
getNext() {
|
||||
return this.enumerator.getNext();
|
||||
}
|
||||
|
||||
*[Symbol.iterator]() {
|
||||
while (this.enumerator.hasMore()) {
|
||||
yield this.enumerator.getNext();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Only here for the sake of component registration, which requires a
|
||||
// callable function.
|
||||
function getXULStore() {
|
||||
return XULStore;
|
||||
}
|
||||
|
|
@ -12,12 +12,6 @@ webidl Node;
|
|||
* Typically it is used to store the persisted state for the document, such as
|
||||
* window location, toolbars that are open and nodes that are open and closed in a tree.
|
||||
*
|
||||
* If MOZ_NEW_XULSTORE is enabled:
|
||||
* XULStore.jsm wraps this API in useful abstractions for JS consumers.
|
||||
* XULStore.h provides a more idiomatic API for C++ consumers.
|
||||
* You should use those APIs unless you have good reasons to use this one.
|
||||
*
|
||||
* If MOZ_NEW_XULSTORE is disabled:
|
||||
* The data is serialized to [profile directory]/xulstore.json
|
||||
*/
|
||||
[scriptable, uuid(987c4b35-c426-4dd7-ad49-3c9fa4c65d20)]
|
||||
|
|
|
|||
|
|
@ -1,81 +0,0 @@
|
|||
/* 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 nserror::{
|
||||
nsresult, NS_ERROR_FAILURE, NS_ERROR_ILLEGAL_VALUE, NS_ERROR_NOT_AVAILABLE, NS_ERROR_UNEXPECTED,
|
||||
};
|
||||
use rkv::{MigrateError as RkvMigrateError, StoreError as RkvStoreError};
|
||||
use serde_json::Error as SerdeJsonError;
|
||||
use std::{io::Error as IoError, str::Utf8Error, string::FromUtf16Error, sync::PoisonError};
|
||||
use thiserror::Error;
|
||||
|
||||
pub(crate) type XULStoreResult<T> = Result<T, XULStoreError>;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub(crate) enum XULStoreError {
|
||||
#[error("error converting bytes: {0:?}")]
|
||||
ConvertBytes(#[from] Utf8Error),
|
||||
|
||||
#[error("error converting string: {0:?}")]
|
||||
ConvertString(#[from] FromUtf16Error),
|
||||
|
||||
#[error("I/O error: {0:?}")]
|
||||
IoError(#[from] IoError),
|
||||
|
||||
#[error("iteration is finished")]
|
||||
IterationFinished,
|
||||
|
||||
#[error("JSON error: {0}")]
|
||||
JsonError(#[from] SerdeJsonError),
|
||||
|
||||
#[error("error result {0}")]
|
||||
NsResult(#[from] nsresult),
|
||||
|
||||
#[error("poison error getting read/write lock")]
|
||||
PoisonError,
|
||||
|
||||
#[error("migrate error: {0:?}")]
|
||||
RkvMigrateError(#[from] RkvMigrateError),
|
||||
|
||||
#[error("store error: {0:?}")]
|
||||
RkvStoreError(#[from] RkvStoreError),
|
||||
|
||||
#[error("id or attribute name too long")]
|
||||
IdAttrNameTooLong,
|
||||
|
||||
#[error("unavailable")]
|
||||
Unavailable,
|
||||
|
||||
#[error("unexpected key: {0:?}")]
|
||||
UnexpectedKey(String),
|
||||
|
||||
#[error("unexpected value")]
|
||||
UnexpectedValue,
|
||||
}
|
||||
|
||||
impl From<XULStoreError> for nsresult {
|
||||
fn from(err: XULStoreError) -> nsresult {
|
||||
match err {
|
||||
XULStoreError::ConvertBytes(_) => NS_ERROR_FAILURE,
|
||||
XULStoreError::ConvertString(_) => NS_ERROR_FAILURE,
|
||||
XULStoreError::IoError(_) => NS_ERROR_FAILURE,
|
||||
XULStoreError::IterationFinished => NS_ERROR_FAILURE,
|
||||
XULStoreError::JsonError(_) => NS_ERROR_FAILURE,
|
||||
XULStoreError::NsResult(result) => result,
|
||||
XULStoreError::PoisonError => NS_ERROR_UNEXPECTED,
|
||||
XULStoreError::RkvMigrateError(_) => NS_ERROR_FAILURE,
|
||||
XULStoreError::RkvStoreError(_) => NS_ERROR_FAILURE,
|
||||
XULStoreError::IdAttrNameTooLong => NS_ERROR_ILLEGAL_VALUE,
|
||||
XULStoreError::Unavailable => NS_ERROR_NOT_AVAILABLE,
|
||||
XULStoreError::UnexpectedKey(_) => NS_ERROR_UNEXPECTED,
|
||||
XULStoreError::UnexpectedValue => NS_ERROR_UNEXPECTED,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> From<PoisonError<T>> for XULStoreError {
|
||||
fn from(_: PoisonError<T>) -> XULStoreError {
|
||||
XULStoreError::PoisonError
|
||||
}
|
||||
}
|
||||
|
|
@ -1,325 +0,0 @@
|
|||
/* 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 as XULStore;
|
||||
use crate::{iter::XULStoreIterator, statics::update_profile_dir};
|
||||
use libc::{c_char, c_void};
|
||||
use nserror::{nsresult, NS_ERROR_NOT_IMPLEMENTED, NS_OK};
|
||||
use nsstring::{nsAString, nsString};
|
||||
use std::cell::RefCell;
|
||||
use std::ptr;
|
||||
use xpcom::{
|
||||
interfaces::{nsIJSEnumerator, nsIStringEnumerator, nsISupports, nsIXULStore},
|
||||
RefPtr,
|
||||
};
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn xulstore_new_service(result: *mut *const nsIXULStore) {
|
||||
let xul_store_service = XULStoreService::new();
|
||||
RefPtr::new(xul_store_service.coerce::<nsIXULStore>()).forget(&mut *result);
|
||||
}
|
||||
|
||||
#[xpcom(implement(nsIXULStore), atomic)]
|
||||
pub struct XULStoreService {}
|
||||
|
||||
impl XULStoreService {
|
||||
fn new() -> RefPtr<XULStoreService> {
|
||||
XULStoreService::allocate(InitXULStoreService {})
|
||||
}
|
||||
|
||||
#[allow(non_snake_case)]
|
||||
fn Persist(&self, _node: *const c_void, _attr: *const nsAString) -> nsresult {
|
||||
NS_ERROR_NOT_IMPLEMENTED
|
||||
}
|
||||
|
||||
xpcom_method!(
|
||||
set_value => SetValue(
|
||||
doc: *const nsAString,
|
||||
id: *const nsAString,
|
||||
attr: *const nsAString,
|
||||
value: *const nsAString
|
||||
)
|
||||
);
|
||||
|
||||
fn set_value(
|
||||
&self,
|
||||
doc: &nsAString,
|
||||
id: &nsAString,
|
||||
attr: &nsAString,
|
||||
value: &nsAString,
|
||||
) -> Result<(), nsresult> {
|
||||
XULStore::set_value(doc, id, attr, value).map_err(|err| err.into())
|
||||
}
|
||||
|
||||
xpcom_method!(
|
||||
has_value => HasValue(
|
||||
doc: *const nsAString,
|
||||
id: *const nsAString,
|
||||
attr: *const nsAString
|
||||
) -> bool
|
||||
);
|
||||
|
||||
fn has_value(
|
||||
&self,
|
||||
doc: &nsAString,
|
||||
id: &nsAString,
|
||||
attr: &nsAString,
|
||||
) -> Result<bool, nsresult> {
|
||||
XULStore::has_value(doc, id, attr).map_err(|err| err.into())
|
||||
}
|
||||
|
||||
xpcom_method!(
|
||||
get_value => GetValue(
|
||||
doc: *const nsAString,
|
||||
id: *const nsAString,
|
||||
attr: *const nsAString
|
||||
) -> nsAString
|
||||
);
|
||||
|
||||
fn get_value(
|
||||
&self,
|
||||
doc: &nsAString,
|
||||
id: &nsAString,
|
||||
attr: &nsAString,
|
||||
) -> Result<nsString, nsresult> {
|
||||
match XULStore::get_value(doc, id, attr) {
|
||||
Ok(val) => Ok(nsString::from(&val)),
|
||||
Err(err) => Err(err.into()),
|
||||
}
|
||||
}
|
||||
|
||||
xpcom_method!(
|
||||
remove_value => RemoveValue(
|
||||
doc: *const nsAString,
|
||||
id: *const nsAString,
|
||||
attr: *const nsAString
|
||||
)
|
||||
);
|
||||
|
||||
fn remove_value(
|
||||
&self,
|
||||
doc: &nsAString,
|
||||
id: &nsAString,
|
||||
attr: &nsAString,
|
||||
) -> Result<(), nsresult> {
|
||||
XULStore::remove_value(doc, id, attr).map_err(|err| err.into())
|
||||
}
|
||||
|
||||
xpcom_method!(
|
||||
remove_document => RemoveDocument(doc: *const nsAString)
|
||||
);
|
||||
|
||||
fn remove_document(&self, doc: &nsAString) -> Result<(), nsresult> {
|
||||
XULStore::remove_document(doc).map_err(|err| err.into())
|
||||
}
|
||||
|
||||
xpcom_method!(
|
||||
get_ids_enumerator => GetIDsEnumerator(
|
||||
doc: *const nsAString
|
||||
) -> * const nsIStringEnumerator
|
||||
);
|
||||
|
||||
fn get_ids_enumerator(&self, doc: &nsAString) -> Result<RefPtr<nsIStringEnumerator>, nsresult> {
|
||||
match XULStore::get_ids(doc) {
|
||||
Ok(val) => {
|
||||
let enumerator = StringEnumerator::new(val);
|
||||
Ok(RefPtr::new(enumerator.coerce::<nsIStringEnumerator>()))
|
||||
}
|
||||
Err(err) => Err(err.into()),
|
||||
}
|
||||
}
|
||||
|
||||
xpcom_method!(
|
||||
get_attribute_enumerator => GetAttributeEnumerator(
|
||||
doc: *const nsAString,
|
||||
id: *const nsAString
|
||||
) -> * const nsIStringEnumerator
|
||||
);
|
||||
|
||||
fn get_attribute_enumerator(
|
||||
&self,
|
||||
doc: &nsAString,
|
||||
id: &nsAString,
|
||||
) -> Result<RefPtr<nsIStringEnumerator>, nsresult> {
|
||||
match XULStore::get_attrs(doc, id) {
|
||||
Ok(val) => {
|
||||
let enumerator = StringEnumerator::new(val);
|
||||
Ok(RefPtr::new(enumerator.coerce::<nsIStringEnumerator>()))
|
||||
}
|
||||
Err(err) => Err(err.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[xpcom(implement(nsIStringEnumerator), nonatomic)]
|
||||
pub(crate) struct StringEnumerator {
|
||||
iter: RefCell<XULStoreIterator>,
|
||||
}
|
||||
impl StringEnumerator {
|
||||
pub(crate) fn new(iter: XULStoreIterator) -> RefPtr<StringEnumerator> {
|
||||
StringEnumerator::allocate(InitStringEnumerator {
|
||||
iter: RefCell::new(iter),
|
||||
})
|
||||
}
|
||||
|
||||
xpcom_method!(string_iterator => StringIterator() -> *const nsIJSEnumerator);
|
||||
|
||||
fn string_iterator(&self) -> Result<RefPtr<nsIJSEnumerator>, nsresult> {
|
||||
Err(NS_ERROR_NOT_IMPLEMENTED)
|
||||
}
|
||||
|
||||
xpcom_method!(has_more => HasMore() -> bool);
|
||||
|
||||
fn has_more(&self) -> Result<bool, nsresult> {
|
||||
let iter = self.iter.borrow();
|
||||
Ok(iter.has_more())
|
||||
}
|
||||
|
||||
xpcom_method!(get_next => GetNext() -> nsAString);
|
||||
|
||||
fn get_next(&self) -> Result<nsString, nsresult> {
|
||||
let mut iter = self.iter.borrow_mut();
|
||||
match iter.get_next() {
|
||||
Ok(value) => Ok(nsString::from(&value)),
|
||||
Err(err) => Err(err.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[xpcom(implement(nsIObserver), nonatomic)]
|
||||
pub(crate) struct ProfileChangeObserver {}
|
||||
impl ProfileChangeObserver {
|
||||
#[allow(non_snake_case)]
|
||||
unsafe fn Observe(
|
||||
&self,
|
||||
_subject: *const nsISupports,
|
||||
_topic: *const c_char,
|
||||
_data: *const u16,
|
||||
) -> nsresult {
|
||||
update_profile_dir();
|
||||
NS_OK
|
||||
}
|
||||
|
||||
pub(crate) fn new() -> RefPtr<ProfileChangeObserver> {
|
||||
ProfileChangeObserver::allocate(InitProfileChangeObserver {})
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn xulstore_set_value(
|
||||
doc: &nsAString,
|
||||
id: &nsAString,
|
||||
attr: &nsAString,
|
||||
value: &nsAString,
|
||||
) -> nsresult {
|
||||
XULStore::set_value(doc, id, attr, value).into()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn xulstore_has_value(
|
||||
doc: &nsAString,
|
||||
id: &nsAString,
|
||||
attr: &nsAString,
|
||||
has_value: *mut bool,
|
||||
) -> nsresult {
|
||||
match XULStore::has_value(doc, id, attr) {
|
||||
Ok(val) => {
|
||||
*has_value = val;
|
||||
NS_OK
|
||||
}
|
||||
Err(err) => err.into(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn xulstore_get_value(
|
||||
doc: &nsAString,
|
||||
id: &nsAString,
|
||||
attr: &nsAString,
|
||||
value: *mut nsAString,
|
||||
) -> nsresult {
|
||||
match XULStore::get_value(doc, id, attr) {
|
||||
Ok(val) => {
|
||||
(*value).assign(&nsString::from(&val));
|
||||
NS_OK
|
||||
}
|
||||
Err(err) => err.into(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn xulstore_remove_value(
|
||||
doc: &nsAString,
|
||||
id: &nsAString,
|
||||
attr: &nsAString,
|
||||
) -> nsresult {
|
||||
XULStore::remove_value(doc, id, attr).into()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn xulstore_get_ids(
|
||||
doc: &nsAString,
|
||||
result: *mut nsresult,
|
||||
) -> *mut XULStoreIterator {
|
||||
match XULStore::get_ids(doc) {
|
||||
Ok(iter) => {
|
||||
*result = NS_OK;
|
||||
Box::into_raw(Box::new(iter))
|
||||
}
|
||||
Err(err) => {
|
||||
*result = err.into();
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn xulstore_get_attrs(
|
||||
doc: &nsAString,
|
||||
id: &nsAString,
|
||||
result: *mut nsresult,
|
||||
) -> *mut XULStoreIterator {
|
||||
match XULStore::get_attrs(doc, id) {
|
||||
Ok(iter) => {
|
||||
*result = NS_OK;
|
||||
Box::into_raw(Box::new(iter))
|
||||
}
|
||||
Err(err) => {
|
||||
*result = err.into();
|
||||
ptr::null_mut()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn xulstore_iter_has_more(iter: &XULStoreIterator) -> bool {
|
||||
iter.has_more()
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn xulstore_iter_get_next(
|
||||
iter: &mut XULStoreIterator,
|
||||
value: *mut nsAString,
|
||||
) -> nsresult {
|
||||
match iter.get_next() {
|
||||
Ok(val) => {
|
||||
(*value).assign(&nsString::from(&val));
|
||||
NS_OK
|
||||
}
|
||||
Err(err) => err.into(),
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn xulstore_iter_free(iter: *mut XULStoreIterator) {
|
||||
drop(Box::from_raw(iter));
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn xulstore_shutdown() -> nsresult {
|
||||
match XULStore::shutdown() {
|
||||
Ok(()) => NS_OK,
|
||||
Err(err) => err.into(),
|
||||
}
|
||||
}
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
/* 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::error::{XULStoreError, XULStoreResult};
|
||||
use std::vec::IntoIter;
|
||||
|
||||
pub struct XULStoreIterator {
|
||||
values: IntoIter<String>,
|
||||
}
|
||||
|
||||
impl XULStoreIterator {
|
||||
pub(crate) fn new(values: IntoIter<String>) -> Self {
|
||||
Self { values }
|
||||
}
|
||||
|
||||
pub(crate) fn has_more(&self) -> bool {
|
||||
!self.values.as_slice().is_empty()
|
||||
}
|
||||
|
||||
pub(crate) fn get_next(&mut self) -> XULStoreResult<String> {
|
||||
Ok(self.values.next().ok_or(XULStoreError::IterationFinished)?)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,223 +0,0 @@
|
|||
/* 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 crossbeam_utils;
|
||||
#[macro_use]
|
||||
extern crate cstr;
|
||||
extern crate libc;
|
||||
#[macro_use]
|
||||
extern crate log;
|
||||
extern crate moz_task;
|
||||
extern crate nserror;
|
||||
extern crate nsstring;
|
||||
extern crate once_cell;
|
||||
extern crate rkv;
|
||||
extern crate serde_json;
|
||||
extern crate tempfile;
|
||||
extern crate thiserror;
|
||||
#[macro_use]
|
||||
extern crate xpcom;
|
||||
|
||||
mod error;
|
||||
mod ffi;
|
||||
mod iter;
|
||||
mod persist;
|
||||
mod statics;
|
||||
|
||||
use crate::{
|
||||
error::{XULStoreError, XULStoreResult},
|
||||
iter::XULStoreIterator,
|
||||
persist::{flush_writes, persist},
|
||||
statics::DATA_CACHE,
|
||||
};
|
||||
use nsstring::nsAString;
|
||||
use std::collections::btree_map::Entry;
|
||||
use std::fmt::Display;
|
||||
|
||||
const SEPARATOR: char = '\u{0009}';
|
||||
|
||||
pub(crate) fn make_key(doc: &impl Display, id: &impl Display, attr: &impl Display) -> String {
|
||||
format!("{}{}{}{}{}", doc, SEPARATOR, id, SEPARATOR, attr)
|
||||
}
|
||||
|
||||
pub(crate) fn set_value(
|
||||
doc: &nsAString,
|
||||
id: &nsAString,
|
||||
attr: &nsAString,
|
||||
value: &nsAString,
|
||||
) -> XULStoreResult<()> {
|
||||
debug!("XULStore set value: {} {} {} {}", doc, id, attr, value);
|
||||
|
||||
// bug 319846 -- don't save really long attributes or values.
|
||||
if id.len() > 512 || attr.len() > 512 {
|
||||
return Err(XULStoreError::IdAttrNameTooLong);
|
||||
}
|
||||
|
||||
let value = if value.len() > 4096 {
|
||||
warn!("XULStore: truncating long attribute value");
|
||||
String::from_utf16(&value[0..4096])?
|
||||
} else {
|
||||
String::from_utf16(value)?
|
||||
};
|
||||
|
||||
let mut cache_guard = DATA_CACHE.lock()?;
|
||||
let data = match cache_guard.as_mut() {
|
||||
Some(data) => data,
|
||||
None => return Ok(()),
|
||||
};
|
||||
data.entry(doc.to_string())
|
||||
.or_default()
|
||||
.entry(id.to_string())
|
||||
.or_default()
|
||||
.insert(attr.to_string(), value.clone());
|
||||
|
||||
persist(make_key(doc, id, attr), Some(value))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn has_value(doc: &nsAString, id: &nsAString, attr: &nsAString) -> XULStoreResult<bool> {
|
||||
debug!("XULStore has value: {} {} {}", doc, id, attr);
|
||||
|
||||
let cache_guard = DATA_CACHE.lock()?;
|
||||
let data = match cache_guard.as_ref() {
|
||||
Some(data) => data,
|
||||
None => return Ok(false),
|
||||
};
|
||||
|
||||
match data.get(&doc.to_string()) {
|
||||
Some(ids) => match ids.get(&id.to_string()) {
|
||||
Some(attrs) => Ok(attrs.contains_key(&attr.to_string())),
|
||||
None => Ok(false),
|
||||
},
|
||||
None => Ok(false),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_value(
|
||||
doc: &nsAString,
|
||||
id: &nsAString,
|
||||
attr: &nsAString,
|
||||
) -> XULStoreResult<String> {
|
||||
debug!("XULStore get value {} {} {}", doc, id, attr);
|
||||
|
||||
let cache_guard = DATA_CACHE.lock()?;
|
||||
let data = match cache_guard.as_ref() {
|
||||
Some(data) => data,
|
||||
None => return Ok(String::new()),
|
||||
};
|
||||
|
||||
match data.get(&doc.to_string()) {
|
||||
Some(ids) => match ids.get(&id.to_string()) {
|
||||
Some(attrs) => match attrs.get(&attr.to_string()) {
|
||||
Some(value) => Ok(value.clone()),
|
||||
None => Ok(String::new()),
|
||||
},
|
||||
None => Ok(String::new()),
|
||||
},
|
||||
None => Ok(String::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn remove_value(
|
||||
doc: &nsAString,
|
||||
id: &nsAString,
|
||||
attr: &nsAString,
|
||||
) -> XULStoreResult<()> {
|
||||
debug!("XULStore remove value {} {} {}", doc, id, attr);
|
||||
|
||||
let mut cache_guard = DATA_CACHE.lock()?;
|
||||
let data = match cache_guard.as_mut() {
|
||||
Some(data) => data,
|
||||
None => return Ok(()),
|
||||
};
|
||||
|
||||
let mut ids_empty = false;
|
||||
if let Some(ids) = data.get_mut(&doc.to_string()) {
|
||||
let mut attrs_empty = false;
|
||||
if let Some(attrs) = ids.get_mut(&id.to_string()) {
|
||||
attrs.remove(&attr.to_string());
|
||||
if attrs.is_empty() {
|
||||
attrs_empty = true;
|
||||
}
|
||||
}
|
||||
if attrs_empty {
|
||||
ids.remove(&id.to_string());
|
||||
if ids.is_empty() {
|
||||
ids_empty = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
if ids_empty {
|
||||
data.remove(&doc.to_string());
|
||||
}
|
||||
|
||||
persist(make_key(doc, id, attr), None)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn remove_document(doc: &nsAString) -> XULStoreResult<()> {
|
||||
debug!("XULStore remove document {}", doc);
|
||||
|
||||
let mut cache_guard = DATA_CACHE.lock()?;
|
||||
let data = match cache_guard.as_mut() {
|
||||
Some(data) => data,
|
||||
None => return Ok(()),
|
||||
};
|
||||
|
||||
if let Entry::Occupied(entry) = data.entry(doc.to_string()) {
|
||||
for (id, attrs) in entry.get() {
|
||||
for attr in attrs.keys() {
|
||||
persist(make_key(entry.key(), id, attr), None)?;
|
||||
}
|
||||
}
|
||||
entry.remove_entry();
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn get_ids(doc: &nsAString) -> XULStoreResult<XULStoreIterator> {
|
||||
debug!("XULStore get IDs for {}", doc);
|
||||
|
||||
let cache_guard = DATA_CACHE.lock()?;
|
||||
let data = match cache_guard.as_ref() {
|
||||
Some(data) => data,
|
||||
None => return Ok(XULStoreIterator::new(vec![].into_iter())),
|
||||
};
|
||||
|
||||
match data.get(&doc.to_string()) {
|
||||
Some(ids) => {
|
||||
let ids: Vec<String> = ids.keys().cloned().collect();
|
||||
Ok(XULStoreIterator::new(ids.into_iter()))
|
||||
}
|
||||
None => Ok(XULStoreIterator::new(vec![].into_iter())),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_attrs(doc: &nsAString, id: &nsAString) -> XULStoreResult<XULStoreIterator> {
|
||||
debug!("XULStore get attrs for doc, ID: {} {}", doc, id);
|
||||
|
||||
let cache_guard = DATA_CACHE.lock()?;
|
||||
let data = match cache_guard.as_ref() {
|
||||
Some(data) => data,
|
||||
None => return Ok(XULStoreIterator::new(vec![].into_iter())),
|
||||
};
|
||||
|
||||
match data.get(&doc.to_string()) {
|
||||
Some(ids) => match ids.get(&id.to_string()) {
|
||||
Some(attrs) => {
|
||||
let attrs: Vec<String> = attrs.keys().cloned().collect();
|
||||
Ok(XULStoreIterator::new(attrs.into_iter()))
|
||||
}
|
||||
None => Ok(XULStoreIterator::new(vec![].into_iter())),
|
||||
},
|
||||
None => Ok(XULStoreIterator::new(vec![].into_iter())),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn shutdown() -> XULStoreResult<()> {
|
||||
flush_writes()
|
||||
}
|
||||
|
|
@ -1,179 +0,0 @@
|
|||
/* 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/. */
|
||||
|
||||
//! The XULStore API is synchronous for both C++ and JS consumers and accessed
|
||||
//! on the main thread, so we persist its data to disk on a background thread
|
||||
//! to avoid janking the UI.
|
||||
//!
|
||||
//! We also re-open the database each time we write to it in order to conserve
|
||||
//! heap memory, since holding a database connection open would consume at least
|
||||
//! 3MB of heap memory in perpetuity.
|
||||
//!
|
||||
//! Since re-opening the database repeatedly to write individual changes can be
|
||||
//! expensive when there are many of them in quick succession, we batch changes
|
||||
//! and write them in batches.
|
||||
|
||||
use crate::{
|
||||
error::{XULStoreError, XULStoreResult},
|
||||
statics::get_database,
|
||||
};
|
||||
use crossbeam_utils::atomic::AtomicCell;
|
||||
use moz_task::{DispatchOptions, Task, TaskRunnable};
|
||||
use nserror::nsresult;
|
||||
use once_cell::sync::Lazy;
|
||||
use rkv::{StoreError as RkvStoreError, Value};
|
||||
use std::{collections::HashMap, sync::Mutex, thread::sleep, time::Duration};
|
||||
|
||||
/// A map of key/value pairs to persist. Values are Options so we can
|
||||
/// use the same structure for both puts and deletes, with a `None` value
|
||||
/// identifying a key that should be deleted from the database.
|
||||
///
|
||||
/// This is a map rather than a sequence in order to merge consecutive
|
||||
/// changes to the same key, i.e. when a consumer sets *foo* to `bar`
|
||||
/// and then sets it again to `baz` before we persist the first change.
|
||||
///
|
||||
/// In that case, there's no point in setting *foo* to `bar` before we set
|
||||
/// it to `baz`, and the map ensures we only ever persist the latest value
|
||||
/// for any given key.
|
||||
static CHANGES: Lazy<Mutex<Option<HashMap<String, Option<String>>>>> =
|
||||
Lazy::new(|| Mutex::new(None));
|
||||
|
||||
/// A Mutex that prevents two PersistTasks from running at the same time,
|
||||
/// since each task opens the database, and we need to ensure there is only
|
||||
/// one open database handle for the database at any given time.
|
||||
static PERSIST: Lazy<Mutex<()>> = Lazy::new(|| Mutex::new(()));
|
||||
|
||||
/// Synchronously persists changes recorded in memory to disk. Typically
|
||||
/// called from a background thread, however this can be called from the main
|
||||
/// thread in Gecko during shutdown (via flush_writes).
|
||||
fn sync_persist() -> XULStoreResult<()> {
|
||||
// Get the map of key/value pairs from the mutex, replacing it
|
||||
// with None. To avoid janking the main thread (if it decides
|
||||
// to makes more changes while we're persisting to disk), we only
|
||||
// lock the map long enough to move it out of the Mutex.
|
||||
let writes = CHANGES.lock()?.take();
|
||||
|
||||
// Return an error early if there's nothing to actually write
|
||||
let writes = writes.ok_or(XULStoreError::Unavailable)?;
|
||||
|
||||
let db = get_database()?;
|
||||
let env = db.rkv.read()?;
|
||||
let mut writer = env.write()?;
|
||||
|
||||
for (key, value) in writes.iter() {
|
||||
match value {
|
||||
Some(val) => db.store.put(&mut writer, &key, &Value::Str(val))?,
|
||||
None => {
|
||||
match db.store.delete(&mut writer, &key) {
|
||||
Ok(_) => (),
|
||||
|
||||
// The XULStore API doesn't care if a consumer tries
|
||||
// to remove a value that doesn't exist in the store,
|
||||
// so we ignore the error (although in this case the key
|
||||
// should exist, since it was in the cache!).
|
||||
Err(RkvStoreError::KeyValuePairNotFound) => {
|
||||
warn!("tried to remove key that isn't in the store");
|
||||
}
|
||||
|
||||
Err(err) => return Err(err.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
writer.commit()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn flush_writes() -> XULStoreResult<()> {
|
||||
// One of three things will happen here (barring unexpected errors):
|
||||
// - There are no writes queued and the background thread is idle. In which
|
||||
// case, we will get the lock, see that there's nothing to write, and
|
||||
// return (with data in memory and on disk in sync).
|
||||
// - There are no writes queued because the background thread is writing
|
||||
// them. In this case, we will block waiting for the lock held by the
|
||||
// writing thread (which will ensure that the changes are flushed), then
|
||||
// discover there are no more to write, and return.
|
||||
// - The background thread is busy writing changes, and another thread has
|
||||
// in the mean time added some. In this case, we will block waiting for
|
||||
// the lock held by the writing thread, discover that there are more
|
||||
// changes left, flush them ourselves, and return.
|
||||
//
|
||||
// This is not airtight, if changes are being added on a different thread
|
||||
// than the one calling this. However it should be a reasonably strong
|
||||
// guarantee even so.
|
||||
let _lock = PERSIST.lock()?;
|
||||
match sync_persist() {
|
||||
Ok(_) => (),
|
||||
|
||||
// It's no problem (in fact it's generally expected) that there's just
|
||||
// nothing to write.
|
||||
Err(XULStoreError::Unavailable) => {
|
||||
info!("Unable to persist xulstore");
|
||||
}
|
||||
|
||||
Err(err) => return Err(err.into()),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn persist(key: String, value: Option<String>) -> XULStoreResult<()> {
|
||||
let mut changes = CHANGES.lock()?;
|
||||
|
||||
if changes.is_none() {
|
||||
*changes = Some(HashMap::new());
|
||||
|
||||
// If *changes* was `None`, then this is the first change since
|
||||
// the last time we persisted, so dispatch a new PersistTask.
|
||||
let task = Box::new(PersistTask::new());
|
||||
TaskRunnable::new("XULStore::Persist", task)?
|
||||
.dispatch_background_task_with_options(DispatchOptions::default().may_block(true))?;
|
||||
}
|
||||
|
||||
// Now insert the key/value pair into the map. The unwrap() call here
|
||||
// should never panic, since the code above sets `writes` to a Some(HashMap)
|
||||
// if it's None.
|
||||
changes.as_mut().unwrap().insert(key, value);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub struct PersistTask {
|
||||
result: AtomicCell<Option<Result<(), XULStoreError>>>,
|
||||
}
|
||||
|
||||
impl PersistTask {
|
||||
pub fn new() -> PersistTask {
|
||||
PersistTask {
|
||||
result: AtomicCell::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Task for PersistTask {
|
||||
fn run(&self) {
|
||||
self.result.store(Some(|| -> Result<(), XULStoreError> {
|
||||
// Avoid persisting too often. We might want to adjust this value
|
||||
// in the future to trade durability for performance.
|
||||
sleep(Duration::from_millis(200));
|
||||
|
||||
// Prevent another PersistTask from running until this one finishes.
|
||||
// We do this before getting the database to ensure that there is
|
||||
// only ever one open database handle at a given time.
|
||||
let _lock = PERSIST.lock()?;
|
||||
sync_persist()
|
||||
}()));
|
||||
}
|
||||
|
||||
fn done(&self) -> Result<(), nsresult> {
|
||||
match self.result.swap(None) {
|
||||
Some(Ok(())) => (),
|
||||
Some(Err(err)) => error!("removeDocument error: {}", err),
|
||||
None => error!("removeDocument error: unexpected result"),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -1,255 +0,0 @@
|
|||
/* 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::{
|
||||
error::{XULStoreError, XULStoreResult},
|
||||
ffi::ProfileChangeObserver,
|
||||
make_key, SEPARATOR,
|
||||
};
|
||||
use moz_task::is_main_thread;
|
||||
use nsstring::nsString;
|
||||
use once_cell::sync::Lazy;
|
||||
use rkv::backend::{SafeMode, SafeModeDatabase, SafeModeEnvironment};
|
||||
use rkv::{StoreOptions, Value};
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
fs::{create_dir_all, remove_file, File},
|
||||
path::PathBuf,
|
||||
str,
|
||||
sync::{Arc, Mutex, RwLock},
|
||||
};
|
||||
use xpcom::{
|
||||
interfaces::{nsIFile, nsIObserverService, nsIProperties, nsIXULRuntime},
|
||||
RefPtr, XpCom,
|
||||
};
|
||||
|
||||
type Manager = rkv::Manager<SafeModeEnvironment>;
|
||||
type Rkv = rkv::Rkv<SafeModeEnvironment>;
|
||||
type SingleStore = rkv::SingleStore<SafeModeDatabase>;
|
||||
type XULStoreCache = BTreeMap<String, BTreeMap<String, BTreeMap<String, String>>>;
|
||||
|
||||
pub struct Database {
|
||||
pub rkv: Arc<RwLock<Rkv>>,
|
||||
pub store: SingleStore,
|
||||
}
|
||||
|
||||
impl Database {
|
||||
fn new(rkv: Arc<RwLock<Rkv>>, store: SingleStore) -> Database {
|
||||
Database { rkv, store }
|
||||
}
|
||||
}
|
||||
|
||||
static PROFILE_DIR: Lazy<Mutex<Option<PathBuf>>> = Lazy::new(|| {
|
||||
observe_profile_change();
|
||||
Mutex::new(get_profile_dir().ok())
|
||||
});
|
||||
|
||||
pub(crate) static DATA_CACHE: Lazy<Mutex<Option<XULStoreCache>>> =
|
||||
Lazy::new(|| Mutex::new(cache_data().ok()));
|
||||
|
||||
pub(crate) fn get_database() -> XULStoreResult<Database> {
|
||||
let mut manager = Manager::singleton().write()?;
|
||||
let xulstore_dir = get_xulstore_dir()?;
|
||||
let xulstore_path = xulstore_dir.as_path();
|
||||
let rkv = manager.get_or_create(xulstore_path, Rkv::new::<SafeMode>)?;
|
||||
let store = rkv.read()?.open_single("db", StoreOptions::create())?;
|
||||
Ok(Database::new(rkv, store))
|
||||
}
|
||||
|
||||
pub(crate) fn update_profile_dir() {
|
||||
// Failure to update the dir isn't fatal (although it means that we won't
|
||||
// persist XULStore data for this session), so we don't return a result.
|
||||
// But we use a closure returning a result to enable use of the ? operator.
|
||||
(|| -> XULStoreResult<()> {
|
||||
{
|
||||
let mut profile_dir_guard = PROFILE_DIR.lock()?;
|
||||
*profile_dir_guard = get_profile_dir().ok();
|
||||
}
|
||||
|
||||
let mut cache_guard = DATA_CACHE.lock()?;
|
||||
*cache_guard = cache_data().ok();
|
||||
|
||||
Ok(())
|
||||
})()
|
||||
.unwrap_or_else(|err| error!("error updating profile dir: {}", err));
|
||||
}
|
||||
|
||||
fn get_profile_dir() -> XULStoreResult<PathBuf> {
|
||||
// We can't use getter_addrefs() here because get_DirectoryService()
|
||||
// returns its nsIProperties interface, and its Get() method returns
|
||||
// a directory via its nsQIResult out param, which gets translated to
|
||||
// a `*mut *mut libc::c_void` in Rust, whereas getter_addrefs() expects
|
||||
// a closure with a `*mut *const T` parameter.
|
||||
|
||||
let dir_svc: RefPtr<nsIProperties> =
|
||||
xpcom::components::Directory::service().map_err(|_| XULStoreError::Unavailable)?;
|
||||
let mut profile_dir = xpcom::GetterAddrefs::<nsIFile>::new();
|
||||
unsafe {
|
||||
dir_svc
|
||||
.Get(
|
||||
cstr!("ProfD").as_ptr(),
|
||||
&nsIFile::IID,
|
||||
profile_dir.void_ptr(),
|
||||
)
|
||||
.to_result()
|
||||
.or_else(|_| {
|
||||
dir_svc
|
||||
.Get(
|
||||
cstr!("ProfDS").as_ptr(),
|
||||
&nsIFile::IID,
|
||||
profile_dir.void_ptr(),
|
||||
)
|
||||
.to_result()
|
||||
})?;
|
||||
}
|
||||
let profile_dir = profile_dir.refptr().ok_or(XULStoreError::Unavailable)?;
|
||||
|
||||
let mut profile_path = nsString::new();
|
||||
unsafe {
|
||||
profile_dir.GetPath(&mut *profile_path).to_result()?;
|
||||
}
|
||||
|
||||
let path = String::from_utf16(&profile_path[..])?;
|
||||
Ok(PathBuf::from(&path))
|
||||
}
|
||||
|
||||
fn get_xulstore_dir() -> XULStoreResult<PathBuf> {
|
||||
let mut xulstore_dir = PROFILE_DIR
|
||||
.lock()?
|
||||
.clone()
|
||||
.ok_or(XULStoreError::Unavailable)?;
|
||||
xulstore_dir.push("xulstore");
|
||||
|
||||
create_dir_all(xulstore_dir.clone())?;
|
||||
|
||||
Ok(xulstore_dir)
|
||||
}
|
||||
|
||||
fn observe_profile_change() {
|
||||
assert!(is_main_thread());
|
||||
|
||||
// Failure to observe the change isn't fatal (although it means we won't
|
||||
// persist XULStore data for this session), so we don't return a result.
|
||||
// But we use a closure returning a result to enable use of the ? operator.
|
||||
(|| -> XULStoreResult<()> {
|
||||
// Observe profile changes so we can update this directory accordingly.
|
||||
let obs_svc: RefPtr<nsIObserverService> =
|
||||
xpcom::components::Observer::service().map_err(|_| XULStoreError::Unavailable)?;
|
||||
let observer = ProfileChangeObserver::new();
|
||||
unsafe {
|
||||
obs_svc
|
||||
.AddObserver(
|
||||
observer.coerce(),
|
||||
cstr!("profile-after-change").as_ptr(),
|
||||
false,
|
||||
)
|
||||
.to_result()?
|
||||
};
|
||||
Ok(())
|
||||
})()
|
||||
.unwrap_or_else(|err| error!("error observing profile change: {}", err));
|
||||
}
|
||||
|
||||
fn in_safe_mode() -> XULStoreResult<bool> {
|
||||
let xul_runtime: RefPtr<nsIXULRuntime> =
|
||||
xpcom::components::XULRuntime::service().map_err(|_| XULStoreError::Unavailable)?;
|
||||
let mut in_safe_mode = false;
|
||||
unsafe {
|
||||
xul_runtime.GetInSafeMode(&mut in_safe_mode).to_result()?;
|
||||
}
|
||||
Ok(in_safe_mode)
|
||||
}
|
||||
|
||||
fn cache_data() -> XULStoreResult<XULStoreCache> {
|
||||
let db = get_database()?;
|
||||
maybe_migrate_data(&db, db.store);
|
||||
|
||||
let mut all = XULStoreCache::default();
|
||||
if in_safe_mode()? {
|
||||
return Ok(all);
|
||||
}
|
||||
|
||||
let env = db.rkv.read()?;
|
||||
let reader = env.read()?;
|
||||
let iterator = db.store.iter_start(&reader)?;
|
||||
|
||||
for result in iterator {
|
||||
let (key, value): (&str, String) = match result {
|
||||
Ok((key, value)) => match (str::from_utf8(&key), unwrap_value(&value)) {
|
||||
(Ok(key), Ok(value)) => (key, value),
|
||||
(Err(err), _) => return Err(err.into()),
|
||||
(_, Err(err)) => return Err(err),
|
||||
},
|
||||
Err(err) => return Err(err.into()),
|
||||
};
|
||||
|
||||
let parts = key.split(SEPARATOR).collect::<Vec<&str>>();
|
||||
if parts.len() != 3 {
|
||||
return Err(XULStoreError::UnexpectedKey(key.to_string()));
|
||||
}
|
||||
let (doc, id, attr) = (
|
||||
parts[0].to_string(),
|
||||
parts[1].to_string(),
|
||||
parts[2].to_string(),
|
||||
);
|
||||
|
||||
all.entry(doc)
|
||||
.or_default()
|
||||
.entry(id)
|
||||
.or_default()
|
||||
.entry(attr)
|
||||
.or_insert(value);
|
||||
}
|
||||
|
||||
Ok(all)
|
||||
}
|
||||
|
||||
fn maybe_migrate_data(db: &Database, store: SingleStore) {
|
||||
// Failure to migrate data isn't fatal, so we don't return a result.
|
||||
// But we use a closure returning a result to enable use of the ? operator.
|
||||
(|| -> XULStoreResult<()> {
|
||||
let mut old_datastore = PROFILE_DIR
|
||||
.lock()?
|
||||
.clone()
|
||||
.ok_or(XULStoreError::Unavailable)?;
|
||||
old_datastore.push("xulstore.json");
|
||||
if !old_datastore.exists() {
|
||||
debug!("old datastore doesn't exist: {:?}", old_datastore);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let file = File::open(old_datastore.clone())?;
|
||||
let json: XULStoreCache = serde_json::from_reader(file)?;
|
||||
|
||||
let env = db.rkv.read()?;
|
||||
let mut writer = env.write()?;
|
||||
|
||||
for (doc, ids) in json {
|
||||
for (id, attrs) in ids {
|
||||
for (attr, value) in attrs {
|
||||
let key = make_key(&doc, &id, &attr);
|
||||
store.put(&mut writer, &key, &Value::Str(&value))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
writer.commit()?;
|
||||
|
||||
remove_file(old_datastore)?;
|
||||
|
||||
Ok(())
|
||||
})()
|
||||
.unwrap_or_else(|err| error!("error migrating data: {}", err));
|
||||
}
|
||||
|
||||
fn unwrap_value(value: &Value) -> XULStoreResult<String> {
|
||||
match value {
|
||||
Value::Str(val) => Ok(val.to_string()),
|
||||
|
||||
// This should never happen, but it could happen in theory
|
||||
// if someone writes a different kind of value into the store
|
||||
// using a more general API (kvstore, rkv, LMDB).
|
||||
_ => Err(XULStoreError::UnexpectedValue),
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
[package]
|
||||
name = "xulstore-gtest"
|
||||
version = "0.1.0"
|
||||
authors = ["The Mozilla Project Developers"]
|
||||
|
||||
[lib]
|
||||
path = "test.rs"
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
#include <stdint.h>
|
||||
#include "gtest/gtest.h"
|
||||
#include "mozilla/XULStore.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsString.h"
|
||||
|
||||
using mozilla::XULStoreIterator;
|
||||
using mozilla::XULStore::GetAttrs;
|
||||
using mozilla::XULStore::GetIDs;
|
||||
using mozilla::XULStore::GetValue;
|
||||
using mozilla::XULStore::HasValue;
|
||||
using mozilla::XULStore::RemoveValue;
|
||||
using mozilla::XULStore::SetValue;
|
||||
|
||||
TEST(XULStore, SetGetValue)
|
||||
{
|
||||
nsAutoString doc(u"SetGetValue"_ns);
|
||||
nsAutoString id(u"foo"_ns);
|
||||
nsAutoString attr(u"bar"_ns);
|
||||
nsAutoString value;
|
||||
|
||||
EXPECT_EQ(GetValue(doc, id, attr, value), NS_OK);
|
||||
EXPECT_TRUE(value.EqualsASCII(""));
|
||||
|
||||
{
|
||||
nsAutoString value(u"baz"_ns);
|
||||
EXPECT_EQ(SetValue(doc, id, attr, value), NS_OK);
|
||||
}
|
||||
|
||||
EXPECT_EQ(GetValue(doc, id, attr, value), NS_OK);
|
||||
EXPECT_TRUE(value.EqualsASCII("baz"));
|
||||
}
|
||||
|
||||
TEST(XULStore, HasValue)
|
||||
{
|
||||
nsAutoString doc(u"HasValue"_ns);
|
||||
nsAutoString id(u"foo"_ns);
|
||||
nsAutoString attr(u"bar"_ns);
|
||||
bool hasValue = true;
|
||||
EXPECT_EQ(HasValue(doc, id, attr, hasValue), NS_OK);
|
||||
EXPECT_FALSE(hasValue);
|
||||
nsAutoString value(u"baz"_ns);
|
||||
EXPECT_EQ(SetValue(doc, id, attr, value), NS_OK);
|
||||
EXPECT_EQ(HasValue(doc, id, attr, hasValue), NS_OK);
|
||||
EXPECT_TRUE(hasValue);
|
||||
}
|
||||
|
||||
TEST(XULStore, RemoveValue)
|
||||
{
|
||||
nsAutoString doc(u"RemoveValue"_ns);
|
||||
nsAutoString id(u"foo"_ns);
|
||||
nsAutoString attr(u"bar"_ns);
|
||||
nsAutoString value(u"baz"_ns);
|
||||
EXPECT_EQ(SetValue(doc, id, attr, value), NS_OK);
|
||||
EXPECT_EQ(GetValue(doc, id, attr, value), NS_OK);
|
||||
EXPECT_TRUE(value.EqualsASCII("baz"));
|
||||
EXPECT_EQ(RemoveValue(doc, id, attr), NS_OK);
|
||||
EXPECT_EQ(GetValue(doc, id, attr, value), NS_OK);
|
||||
EXPECT_TRUE(value.EqualsASCII(""));
|
||||
}
|
||||
|
||||
TEST(XULStore, GetIDsIterator)
|
||||
{
|
||||
nsAutoString doc(u"idIterDoc"_ns);
|
||||
nsAutoString id1(u"id1"_ns);
|
||||
nsAutoString id2(u"id2"_ns);
|
||||
nsAutoString id3(u"id3"_ns);
|
||||
nsAutoString attr(u"attr"_ns);
|
||||
nsAutoString value(u"value"_ns);
|
||||
nsAutoString id;
|
||||
|
||||
// Confirm that the store doesn't have any IDs yet.
|
||||
mozilla::UniquePtr<XULStoreIterator> iter;
|
||||
EXPECT_EQ(GetIDs(doc, iter), NS_OK);
|
||||
EXPECT_FALSE(iter->HasMore());
|
||||
// EXPECT_EQ(iter->GetNext(&id), NS_ERROR_FAILURE);
|
||||
|
||||
// Insert with IDs in non-alphanumeric order to confirm
|
||||
// that store will order them when iterating them.
|
||||
EXPECT_EQ(SetValue(doc, id3, attr, value), NS_OK);
|
||||
EXPECT_EQ(SetValue(doc, id1, attr, value), NS_OK);
|
||||
EXPECT_EQ(SetValue(doc, id2, attr, value), NS_OK);
|
||||
|
||||
// Insert different ID for another doc to confirm that store
|
||||
// won't return it when iterating IDs for our doc.
|
||||
nsAutoString otherDoc(u"otherDoc"_ns);
|
||||
nsAutoString otherID(u"otherID"_ns);
|
||||
EXPECT_EQ(SetValue(otherDoc, otherID, attr, value), NS_OK);
|
||||
|
||||
EXPECT_EQ(GetIDs(doc, iter), NS_OK);
|
||||
EXPECT_TRUE(iter->HasMore());
|
||||
EXPECT_EQ(iter->GetNext(&id), NS_OK);
|
||||
EXPECT_TRUE(id.EqualsASCII("id1"));
|
||||
EXPECT_TRUE(iter->HasMore());
|
||||
EXPECT_EQ(iter->GetNext(&id), NS_OK);
|
||||
EXPECT_TRUE(id.EqualsASCII("id2"));
|
||||
EXPECT_TRUE(iter->HasMore());
|
||||
EXPECT_EQ(iter->GetNext(&id), NS_OK);
|
||||
EXPECT_TRUE(id.EqualsASCII("id3"));
|
||||
EXPECT_FALSE(iter->HasMore());
|
||||
}
|
||||
|
||||
TEST(XULStore, GetAttributeIterator)
|
||||
{
|
||||
nsAutoString doc(u"attrIterDoc"_ns);
|
||||
nsAutoString id(u"id"_ns);
|
||||
nsAutoString attr1(u"attr1"_ns);
|
||||
nsAutoString attr2(u"attr2"_ns);
|
||||
nsAutoString attr3(u"attr3"_ns);
|
||||
nsAutoString value(u"value"_ns);
|
||||
nsAutoString attr;
|
||||
|
||||
mozilla::UniquePtr<XULStoreIterator> iter;
|
||||
EXPECT_EQ(GetAttrs(doc, id, iter), NS_OK);
|
||||
EXPECT_FALSE(iter->HasMore());
|
||||
// EXPECT_EQ(iter->GetNext(&attr), NS_ERROR_FAILURE);
|
||||
|
||||
// Insert with attributes in non-alphanumeric order to confirm
|
||||
// that store will order them when iterating them.
|
||||
EXPECT_EQ(SetValue(doc, id, attr3, value), NS_OK);
|
||||
EXPECT_EQ(SetValue(doc, id, attr1, value), NS_OK);
|
||||
EXPECT_EQ(SetValue(doc, id, attr2, value), NS_OK);
|
||||
|
||||
// Insert different attribute for another ID to confirm that store
|
||||
// won't return it when iterating attributes for our ID.
|
||||
nsAutoString otherID(u"otherID"_ns);
|
||||
nsAutoString otherAttr(u"otherAttr"_ns);
|
||||
EXPECT_EQ(SetValue(doc, otherID, otherAttr, value), NS_OK);
|
||||
|
||||
EXPECT_EQ(GetAttrs(doc, id, iter), NS_OK);
|
||||
EXPECT_TRUE(iter->HasMore());
|
||||
EXPECT_EQ(iter->GetNext(&attr), NS_OK);
|
||||
EXPECT_TRUE(attr.EqualsASCII("attr1"));
|
||||
EXPECT_TRUE(iter->HasMore());
|
||||
EXPECT_EQ(iter->GetNext(&attr), NS_OK);
|
||||
EXPECT_TRUE(attr.EqualsASCII("attr2"));
|
||||
EXPECT_TRUE(iter->HasMore());
|
||||
EXPECT_EQ(iter->GetNext(&attr), NS_OK);
|
||||
EXPECT_TRUE(attr.EqualsASCII("attr3"));
|
||||
EXPECT_FALSE(iter->HasMore());
|
||||
}
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
# -*- 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 += [
|
||||
"TestXULStore.cpp",
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = "xul-gtest"
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
const { AppConstants } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/AppConstants.sys.mjs"
|
||||
);
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(
|
||||
{
|
||||
skip_if: () => !AppConstants.MOZ_NEW_XULSTORE,
|
||||
},
|
||||
async function test_create_old_datastore() {
|
||||
const path = PathUtils.join(PathUtils.profileDir, "xulstore.json");
|
||||
|
||||
const xulstoreJSON = {
|
||||
doc1: {
|
||||
id1: {
|
||||
attr1: "value1",
|
||||
},
|
||||
},
|
||||
doc2: {
|
||||
id1: {
|
||||
attr2: "value2",
|
||||
},
|
||||
id2: {
|
||||
attr1: "value1",
|
||||
attr2: "value2",
|
||||
attr3: "value3",
|
||||
},
|
||||
id3: {},
|
||||
},
|
||||
doc3: {},
|
||||
};
|
||||
|
||||
await IOUtils.writeJSON(path, xulstoreJSON);
|
||||
}
|
||||
);
|
||||
|
||||
add_task(
|
||||
{
|
||||
skip_if: () => !AppConstants.MOZ_NEW_XULSTORE,
|
||||
},
|
||||
async function test_get_values() {
|
||||
// We wait until now to import XULStore to ensure we've created
|
||||
// the old datastore, as importing that module will initiate the attempt
|
||||
// to migrate the old datastore to the new one.
|
||||
const { XULStore } = ChromeUtils.import(
|
||||
"resource://gre/modules/XULStore.jsm"
|
||||
);
|
||||
|
||||
Assert.equal(await XULStore.getValue("doc1", "id1", "attr1"), "value1");
|
||||
Assert.equal(await XULStore.getValue("doc1", "id1", "attr2"), "");
|
||||
Assert.equal(await XULStore.getValue("doc1", "id1", "attr3"), "");
|
||||
Assert.equal(await XULStore.getValue("doc1", "id2", "attr1"), "");
|
||||
Assert.equal(await XULStore.getValue("doc1", "id2", "attr2"), "");
|
||||
Assert.equal(await XULStore.getValue("doc1", "id2", "attr3"), "");
|
||||
Assert.equal(await XULStore.getValue("doc1", "id3", "attr1"), "");
|
||||
Assert.equal(await XULStore.getValue("doc1", "id3", "attr2"), "");
|
||||
Assert.equal(await XULStore.getValue("doc1", "id3", "attr3"), "");
|
||||
|
||||
Assert.equal(await XULStore.getValue("doc2", "id1", "attr1"), "");
|
||||
Assert.equal(await XULStore.getValue("doc2", "id1", "attr2"), "value2");
|
||||
Assert.equal(await XULStore.getValue("doc2", "id1", "attr3"), "");
|
||||
Assert.equal(await XULStore.getValue("doc2", "id2", "attr1"), "value1");
|
||||
Assert.equal(await XULStore.getValue("doc2", "id2", "attr2"), "value2");
|
||||
Assert.equal(await XULStore.getValue("doc2", "id2", "attr3"), "value3");
|
||||
Assert.equal(await XULStore.getValue("doc2", "id3", "attr1"), "");
|
||||
Assert.equal(await XULStore.getValue("doc2", "id3", "attr2"), "");
|
||||
Assert.equal(await XULStore.getValue("doc2", "id3", "attr3"), "");
|
||||
|
||||
Assert.equal(await XULStore.getValue("doc3", "id1", "attr1"), "");
|
||||
Assert.equal(await XULStore.getValue("doc3", "id1", "attr2"), "");
|
||||
Assert.equal(await XULStore.getValue("doc3", "id1", "attr3"), "");
|
||||
Assert.equal(await XULStore.getValue("doc3", "id2", "attr1"), "");
|
||||
Assert.equal(await XULStore.getValue("doc3", "id2", "attr2"), "");
|
||||
Assert.equal(await XULStore.getValue("doc3", "id2", "attr3"), "");
|
||||
Assert.equal(await XULStore.getValue("doc3", "id3", "attr1"), "");
|
||||
Assert.equal(await XULStore.getValue("doc3", "id3", "attr2"), "");
|
||||
Assert.equal(await XULStore.getValue("doc3", "id3", "attr3"), "");
|
||||
}
|
||||
);
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
const { AppConstants } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/AppConstants.sys.mjs"
|
||||
);
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(
|
||||
{
|
||||
skip_if: () => !AppConstants.MOZ_NEW_XULSTORE,
|
||||
},
|
||||
async function test_create_old_datastore() {
|
||||
const path = PathUtils.join(PathUtils.profileDir, "xulstore.json");
|
||||
|
||||
// Valid JSON, but invalid data: attr1's value is a number, not a string.
|
||||
const xulstoreJSON = {
|
||||
doc1: {
|
||||
id1: {
|
||||
attr1: 1,
|
||||
},
|
||||
},
|
||||
doc2: {
|
||||
id2: {
|
||||
attr2: "value2",
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
await IOUtils.writeJSON(path, xulstoreJSON);
|
||||
}
|
||||
);
|
||||
|
||||
add_task(
|
||||
{
|
||||
skip_if: () => !AppConstants.MOZ_NEW_XULSTORE,
|
||||
},
|
||||
async function test_get_values() {
|
||||
// We wait until now to import XULStore to ensure we've created
|
||||
// the old store, as importing that module will initiate the attempt
|
||||
// to migrate the old store to the new one.
|
||||
const { XULStore } = ChromeUtils.import(
|
||||
"resource://gre/modules/XULStore.jsm"
|
||||
);
|
||||
|
||||
// XULStore should *not* have migrated the values from the old store,
|
||||
// so it should return empty strings when we try to retrieve them.
|
||||
// That's true for both values, even though one of them is valid,
|
||||
// because the migrator uses a typed parser that requires the entire
|
||||
// JSON file to conform to the XULStore format.
|
||||
Assert.equal(await XULStore.getValue("doc1", "id1", "attr1"), "");
|
||||
Assert.equal(await XULStore.getValue("doc2", "id2", "attr2"), "");
|
||||
}
|
||||
);
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
const { AppConstants } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/AppConstants.sys.mjs"
|
||||
);
|
||||
|
||||
function run_test() {
|
||||
do_get_profile();
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(
|
||||
{
|
||||
skip_if: () => !AppConstants.MOZ_NEW_XULSTORE,
|
||||
},
|
||||
async function test_create_old_datastore() {
|
||||
const path = PathUtils.join(PathUtils.profileDir, "xulstore.json");
|
||||
|
||||
// Invalid JSON: it's missing the final closing brace.
|
||||
const xulstoreJSON = '{ doc: { id: { attr: "value" } }';
|
||||
|
||||
await IOUtils.writeUTF8(path, xulstoreJSON);
|
||||
}
|
||||
);
|
||||
|
||||
add_task(
|
||||
{
|
||||
skip_if: () => !AppConstants.MOZ_NEW_XULSTORE,
|
||||
},
|
||||
async function test_get_value() {
|
||||
// We wait until now to import XULStore to ensure we've created
|
||||
// the old store, as importing that module will initiate the attempt
|
||||
// to migrate the old store to the new one.
|
||||
const { XULStore } = ChromeUtils.import(
|
||||
"resource://gre/modules/XULStore.jsm"
|
||||
);
|
||||
|
||||
// XULStore should *not* have migrated the value from the old store,
|
||||
// so it should return an empty string when we try to retrieve it.
|
||||
Assert.equal(await XULStore.getValue("doc", "id", "attr"), "");
|
||||
}
|
||||
);
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
"use strict";
|
||||
|
||||
const { AppConstants } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/AppConstants.sys.mjs"
|
||||
);
|
||||
const { FileUtils } = ChromeUtils.importESModule(
|
||||
"resource://gre/modules/FileUtils.sys.mjs"
|
||||
);
|
||||
|
||||
add_task(
|
||||
{
|
||||
skip_if: () => !AppConstants.MOZ_NEW_XULSTORE,
|
||||
},
|
||||
async function test_get_values() {
|
||||
// Import XULStore before getting the profile to ensure that the new store
|
||||
// is initialized, as the purpose of this test is to confirm that the old
|
||||
// store data gets migrated if the profile change happens post-initialization.
|
||||
const { XULStore } = ChromeUtils.import(
|
||||
"resource://gre/modules/XULStore.jsm"
|
||||
);
|
||||
|
||||
// We haven't migrated any data yet (nor even changed to a profile), so there
|
||||
// shouldn't be a value in the store.
|
||||
Assert.equal(XULStore.getValue("doc1", "id1", "attr1"), "");
|
||||
|
||||
// Register an observer before the XULStore service registers its observer,
|
||||
// so we can observe the profile-after-change notification first and create
|
||||
// an old store for it to migrate. We need to write synchronously to avoid
|
||||
// racing XULStore, so we use FileUtils instead of IOUtils.
|
||||
Services.obs.addObserver(
|
||||
{
|
||||
observe() {
|
||||
const file = FileUtils.getFile("ProfD", ["xulstore.json"]);
|
||||
const xulstoreJSON = JSON.stringify({
|
||||
doc1: {
|
||||
id1: {
|
||||
attr1: "value1",
|
||||
},
|
||||
},
|
||||
});
|
||||
let stream = FileUtils.openAtomicFileOutputStream(file);
|
||||
stream.write(xulstoreJSON, xulstoreJSON.length);
|
||||
FileUtils.closeAtomicFileOutputStream(stream);
|
||||
},
|
||||
},
|
||||
"profile-after-change"
|
||||
);
|
||||
|
||||
// This creates a profile and changes to it, triggering first our
|
||||
// profile-after-change observer above and then XULStore's equivalent.
|
||||
do_get_profile(true);
|
||||
|
||||
// XULStore should now have migrated the value from the old store.
|
||||
Assert.equal(XULStore.getValue("doc1", "id1", "attr1"), "value1");
|
||||
}
|
||||
);
|
||||
|
|
@ -2,12 +2,3 @@
|
|||
skip-if = toolkit == 'android'
|
||||
|
||||
[test_XULStore.js]
|
||||
|
||||
# These tests only run on the new implementation of XULStore, since they
|
||||
# test migration of data from the old implementation to the new one.
|
||||
# But there isn't a skip-if condition we can add here to disable them,
|
||||
# so we disable them within each test file using add_task() properties.
|
||||
[test_XULStore_migration.js]
|
||||
[test_XULStore_migration_fail_invalid_json.js]
|
||||
[test_XULStore_migration_fail_invalid_data.js]
|
||||
[test_XULStore_migration_profile_change.js]
|
||||
|
|
|
|||
|
|
@ -50,9 +50,6 @@ if CONFIG['MOZ_GECKO_PROFILER_PARSE_ELF']:
|
|||
if CONFIG['MOZ_BITS_DOWNLOAD']:
|
||||
gkrust_features += ['bitsdownload']
|
||||
|
||||
if CONFIG['MOZ_NEW_XULSTORE']:
|
||||
gkrust_features += ['new_xulstore']
|
||||
|
||||
if CONFIG['LIBFUZZER']:
|
||||
gkrust_features += ['libfuzzer']
|
||||
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@ authenticator = { version = "0.4.0-alpha.6", features = ["gecko"] }
|
|||
gkrust_utils = { path = "../../../../xpcom/rust/gkrust_utils" }
|
||||
gecko_logger = { path = "../../../../xpcom/rust/gecko_logger" }
|
||||
rsdparsa_capi = { path = "../../../../dom/media/webrtc/sdp/rsdparsa_capi" }
|
||||
xulstore = { path = "../../../components/xulstore", optional = true }
|
||||
# We have these to enforce common feature sets for said crates.
|
||||
log = {version = "0.4", features = ["release_max_level_info"]}
|
||||
cose-c = { version = "0.1.5" }
|
||||
|
|
@ -136,7 +135,6 @@ spidermonkey_rust = []
|
|||
smoosh = ["jsrust_shared/smoosh"]
|
||||
gecko_profiler = ["gecko-profiler/enabled", "profiler_helper"]
|
||||
gecko_profiler_parse_elf = ["profiler_helper/parse_elf"]
|
||||
new_xulstore = ["xulstore"]
|
||||
libfuzzer = ["neqo_glue/fuzzing"]
|
||||
webrtc = ["mdns_service"]
|
||||
glean_disable_upload = ["fog_control/disable_upload"]
|
||||
|
|
|
|||
|
|
@ -49,8 +49,6 @@ extern crate static_prefs;
|
|||
extern crate storage;
|
||||
extern crate webrender_bindings;
|
||||
extern crate xpcom;
|
||||
#[cfg(feature = "new_xulstore")]
|
||||
extern crate xulstore;
|
||||
|
||||
extern crate audio_thread_priority;
|
||||
|
||||
|
|
|
|||
|
|
@ -393,13 +393,6 @@ export var AppConstants = Object.freeze({
|
|||
|
||||
TELEMETRY_PING_FORMAT_VERSION: @TELEMETRY_PING_FORMAT_VERSION@,
|
||||
|
||||
MOZ_NEW_XULSTORE:
|
||||
#ifdef MOZ_NEW_XULSTORE
|
||||
true,
|
||||
#else
|
||||
false,
|
||||
#endif
|
||||
|
||||
MOZ_NEW_NOTIFICATION_STORE:
|
||||
#ifdef MOZ_NEW_NOTIFICATION_STORE
|
||||
true,
|
||||
|
|
|
|||
|
|
@ -35,7 +35,6 @@ clippy:
|
|||
- third_party/rust/mp4parse_capi/
|
||||
- toolkit/components/kvstore/
|
||||
- toolkit/components/glean/
|
||||
- toolkit/components/xulstore/tests/gtest/
|
||||
- toolkit/library/rust/
|
||||
- tools/fuzzing/rust/
|
||||
- tools/profiler/rust-api/
|
||||
|
|
@ -69,7 +68,6 @@ clippy:
|
|||
- storage/rust/
|
||||
- storage/variant/
|
||||
# nsstring
|
||||
- toolkit/components/xulstore/
|
||||
- servo/ports/geckolib/tests/
|
||||
- xpcom/rust/xpcom/
|
||||
- xpcom/rust/nsstring/
|
||||
|
|
|
|||
|
|
@ -37,10 +37,6 @@
|
|||
#endif
|
||||
#include "prenv.h"
|
||||
|
||||
#ifdef MOZ_NEW_XULSTORE
|
||||
# include "mozilla/XULStore.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_BACKGROUNDTASKS
|
||||
# include "mozilla/BackgroundTasks.h"
|
||||
#endif
|
||||
|
|
@ -236,11 +232,6 @@ void AppShutdown::MaybeFastShutdown(ShutdownPhase aPhase) {
|
|||
}
|
||||
|
||||
nsresult rv;
|
||||
#ifdef MOZ_NEW_XULSTORE
|
||||
rv = XULStore::Shutdown();
|
||||
NS_ASSERTION(NS_SUCCEEDED(rv), "XULStore::Shutdown() failed.");
|
||||
#endif
|
||||
|
||||
nsCOMPtr<nsICertStorage> certStorage =
|
||||
do_GetService("@mozilla.org/security/certstorage;1", &rv);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
|
|
|
|||
|
|
@ -76,10 +76,6 @@
|
|||
# include "nsIWindowsUIUtils.h"
|
||||
#endif
|
||||
|
||||
#ifdef MOZ_NEW_XULSTORE
|
||||
# include "mozilla/XULStore.h"
|
||||
#endif
|
||||
|
||||
#include "mozilla/dom/DocumentL10n.h"
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
|
|
@ -1710,10 +1706,6 @@ nsresult AppWindow::GetPersistentValue(const nsAtom* aAttr, nsAString& aValue) {
|
|||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ConvertUTF8toUTF16 uri(utf8uri);
|
||||
|
||||
#ifdef MOZ_NEW_XULSTORE
|
||||
nsDependentAtomString attrString(aAttr);
|
||||
rv = XULStore::GetValue(uri, windowElementId, attrString, aValue);
|
||||
#else
|
||||
if (!mLocalStore) {
|
||||
mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
|
||||
if (NS_WARN_IF(!mLocalStore)) {
|
||||
|
|
@ -1723,7 +1715,6 @@ nsresult AppWindow::GetPersistentValue(const nsAtom* aAttr, nsAString& aValue) {
|
|||
|
||||
rv = mLocalStore->GetValue(uri, windowElementId, nsDependentAtomString(aAttr),
|
||||
aValue);
|
||||
#endif
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
|
@ -1946,11 +1937,6 @@ nsresult AppWindow::SetPersistentValue(const nsAtom* aAttr,
|
|||
maybeConvertedValue);
|
||||
}
|
||||
|
||||
#ifdef MOZ_NEW_XULSTORE
|
||||
nsDependentAtomString attrString(aAttr);
|
||||
return XULStore::SetValue(uri, windowElementId, attrString,
|
||||
maybeConvertedValue);
|
||||
#else
|
||||
if (!mLocalStore) {
|
||||
mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
|
||||
if (NS_WARN_IF(!mLocalStore)) {
|
||||
|
|
@ -1960,7 +1946,6 @@ nsresult AppWindow::SetPersistentValue(const nsAtom* aAttr,
|
|||
|
||||
return mLocalStore->SetValue(
|
||||
uri, windowElementId, nsDependentAtomString(aAttr), maybeConvertedValue);
|
||||
#endif
|
||||
}
|
||||
|
||||
void AppWindow::MaybeSavePersistentPositionAndSize(
|
||||
|
|
|
|||
|
|
@ -35,10 +35,7 @@
|
|||
#include "nsIRemoteTab.h"
|
||||
#include "nsIWebProgressListener.h"
|
||||
#include "nsITimer.h"
|
||||
|
||||
#ifndef MOZ_NEW_XULSTORE
|
||||
# include "nsIXULStore.h"
|
||||
#endif
|
||||
#include "nsIXULStore.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
|
@ -387,9 +384,7 @@ class AppWindow final : public nsIBaseWindow,
|
|||
nsresult SetPrimaryRemoteTabSize(int32_t aWidth, int32_t aHeight);
|
||||
void SizeShellToWithLimit(int32_t aDesiredWidth, int32_t aDesiredHeight,
|
||||
int32_t shellItemWidth, int32_t shellItemHeight);
|
||||
#ifndef MOZ_NEW_XULSTORE
|
||||
nsCOMPtr<nsIXULStore> mLocalStore;
|
||||
#endif
|
||||
};
|
||||
|
||||
NS_DEFINE_STATIC_IID_ACCESSOR(AppWindow, NS_APPWINDOW_IMPL_CID)
|
||||
|
|
|
|||
Loading…
Reference in a new issue