Backed out changeset 2f8f0e53a7db (bug 1460811) for leakcheck perma failures. CLOSED TREE

This commit is contained in:
Razvan Maries 2019-04-19 00:16:32 +03:00
parent b8b5ed3ca4
commit 1f6c35708a
34 changed files with 360 additions and 1774 deletions

19
Cargo.lock generated
View File

@ -1248,7 +1248,6 @@ dependencies = [
"u2fhid 0.2.3",
"webrender_bindings 0.1.0",
"xpcom 0.1.0",
"xulstore 0.1.0",
]
[[package]]
@ -3433,24 +3432,6 @@ dependencies = [
"syn 0.13.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "xulstore"
version = "0.1.0"
dependencies = [
"crossbeam-utils 0.6.3 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)",
"lmdb-rkv 0.11.2 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"moz_task 0.1.0",
"nserror 0.1.0",
"nsstring 0.1.0",
"rkv 0.9.4 (registry+https://github.com/rust-lang/crates.io-index)",
"serde_json 1.0.26 (registry+https://github.com/rust-lang/crates.io-index)",
"xpcom 0.1.0",
]
[[package]]
name = "yaml-rust"
version = "0.4.2"

View File

@ -5,7 +5,8 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
#include "XULPersist.h"
#include "mozilla/XULStore.h"
#include "nsIXULStore.h"
namespace mozilla {
namespace dom {
@ -79,6 +80,13 @@ void XULPersist::Persist(Element* aElement, int32_t aNameSpaceID,
return;
}
if (!mLocalStore) {
mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
if (NS_WARN_IF(!mLocalStore)) {
return;
}
}
nsAutoString id;
aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id);
@ -95,14 +103,13 @@ void XULPersist::Persist(Element* aElement, int32_t aNameSpaceID,
NS_ConvertUTF8toUTF16 uri(utf8uri);
bool hasAttr;
rv = XULStore::HasValue(uri, id, attrstr, hasAttr);
rv = mLocalStore->HasValue(uri, id, attrstr, &hasAttr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
if (hasAttr && valuestr.IsEmpty()) {
rv = XULStore::RemoveValue(uri, id, attrstr);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "value removed");
mLocalStore->RemoveValue(uri, id, attrstr);
return;
}
@ -114,8 +121,7 @@ void XULPersist::Persist(Element* aElement, int32_t aNameSpaceID,
}
}
rv = XULStore::SetValue(uri, id, attrstr, valuestr);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "value set");
mLocalStore->SetValue(uri, id, attrstr, valuestr);
}
nsresult XULPersist::ApplyPersistentAttributes() {
@ -129,6 +135,13 @@ nsresult XULPersist::ApplyPersistentAttributes() {
// Add all of the 'persisted' attributes into the content
// model.
if (!mLocalStore) {
mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
if (NS_WARN_IF(!mLocalStore)) {
return NS_ERROR_NOT_INITIALIZED;
}
}
ApplyPersistentAttributesInternal();
return NS_OK;
@ -145,19 +158,22 @@ nsresult XULPersist::ApplyPersistentAttributesInternal() {
NS_ConvertUTF8toUTF16 uri(utf8uri);
// Get a list of element IDs for which persisted values are available
UniquePtr<XULStoreIterator> ids;
rv = XULStore::GetIDs(uri, ids);
nsCOMPtr<nsIStringEnumerator> ids;
rv = mLocalStore->GetIDsEnumerator(uri, getter_AddRefs(ids));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
while (ids->HasMore()) {
nsAutoString id;
rv = ids->GetNext(&id);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
while (1) {
bool hasmore = false;
ids->HasMore(&hasmore);
if (!hasmore) {
break;
}
nsAutoString id;
ids->GetNext(id);
// We want to hold strong refs to the elements while applying
// persistent attributes, just in case.
const nsTArray<Element*>* allElements = mDocument->GetAllElementsForId(id);
@ -189,21 +205,24 @@ nsresult XULPersist::ApplyPersistentAttributesToElements(
NS_ConvertUTF8toUTF16 uri(utf8uri);
// Get a list of attributes for which persisted values are available
UniquePtr<XULStoreIterator> attrs;
rv = XULStore::GetAttrs(uri, aID, attrs);
nsCOMPtr<nsIStringEnumerator> attrs;
rv = mLocalStore->GetAttributeEnumerator(uri, aID, getter_AddRefs(attrs));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
while (attrs->HasMore()) {
nsAutoString attrstr;
rv = attrs->GetNext(&attrstr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
while (1) {
bool hasmore = PR_FALSE;
attrs->HasMore(&hasmore);
if (!hasmore) {
break;
}
nsAutoString attrstr;
attrs->GetNext(attrstr);
nsAutoString value;
rv = XULStore::GetValue(uri, aID, attrstr, value);
rv = mLocalStore->GetValue(uri, aID, attrstr, value);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}

View File

@ -7,6 +7,8 @@
#ifndef mozilla_dom_XULPersist_h
#define mozilla_dom_XULPersist_h
class nsIXULStore;
namespace mozilla {
namespace dom {
@ -31,6 +33,7 @@ class XULPersist final : public nsStubDocumentObserver {
nsresult ApplyPersistentAttributesToElements(const nsAString& aID,
nsCOMArray<Element>& aElements);
nsCOMPtr<nsIXULStore> mLocalStore;
// A weak pointer to our document. Nulled out by DropDocumentReference.
Document* MOZ_NON_OWNING_REF mDocument;
};

View File

@ -566,16 +566,16 @@
"minbytes": 6000,
"maxbytes": 6000
},
"{profile}\\xulstore.json": {
"mincount": 0,
"maxcount": 0,
"minbytes": 0,
"maxbytes": 702
},
"{talos}\\talos\\tests\\{tp5n_files}": {
"mincount": 0,
"maxcount": 2,
"minbytes": 0,
"maxbytes": 16384
},
"{profile}\\xulstore\\data.mdb": {
"mincount": 0,
"maxcount": 4,
"minbytes": 0,
"maxbytes": 608
}
}

View File

@ -80,7 +80,7 @@ DIRS += [
'windowcreator',
'windowwatcher',
'workerloader',
'xulstore',
'xulstore'
]
if CONFIG['MOZ_BUILD_APP'] != 'mobile/android':

View File

@ -1,25 +0,0 @@
[package]
name = "xulstore"
version = "0.1.0"
authors = ["nobody@mozilla.org"]
license = "MPL-2.0"
[dependencies]
crossbeam-utils = "0.6.3"
lazy_static = "1.0"
libc = "0.2"
lmdb-rkv = "0.11.2"
log = "0.4"
moz_task = { path = "../../../xpcom/rust/moz_task" }
nsstring = { path = "../../../xpcom/rust/nsstring" }
nserror = { path = "../../../xpcom/rust/nserror" }
rkv = "0.9.3"
serde_json = "1"
xpcom = { path = "../../../xpcom/rust/xpcom" }
# Get rid of failure's dependency on backtrace. Eventually
# backtrace will move into Rust core, but we don't need it here.
[dependencies.failure]
version = "0.1"
default_features = false
features = ["derive"]

View File

@ -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 https://mozilla.org/MPL/2.0/. */
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/XULStore.h"
#include "nsCOMPtr.h"
#include "nsIXULStore.h"
namespace mozilla {
// The XULStore API is implemented in Rust and exposed to C++ via a set of
// C functions with the "xulstore_" prefix. We declare them in this anonymous
// namespace to prevent C++ code outside this file from accessing them,
// as they are an internal implementation detail, and C++ code should use
// the mozilla::XULStore::* functions and mozilla::XULStoreIterator class
// declared in XULStore.h.
namespace {
extern "C" {
void xulstore_new_service(nsIXULStore** result);
nsresult xulstore_set_value(const nsAString* doc, const nsAString* id,
const nsAString* attr, const nsAString* value);
nsresult xulstore_has_value(const nsAString* doc, const nsAString* id,
const nsAString* attr, bool* has_value);
nsresult xulstore_get_value(const nsAString* doc, const nsAString* id,
const nsAString* attr, nsAString* value);
nsresult xulstore_remove_value(const nsAString* doc, const nsAString* id,
const nsAString* attr);
XULStoreIterator* xulstore_get_ids(const nsAString* doc, nsresult* result);
XULStoreIterator* xulstore_get_attrs(const nsAString* doc, const nsAString* id,
nsresult* result);
bool xulstore_iter_has_more(const XULStoreIterator*);
nsresult xulstore_iter_get_next(XULStoreIterator*, nsAString* value);
void xulstore_iter_free(XULStoreIterator* iterator);
}
// A static reference to the nsIXULStore singleton that JS uses to access
// the store. Retrieved via mozilla::XULStore::GetService().
static StaticRefPtr<nsIXULStore> sXULStore;
} // namespace
bool XULStoreIterator::HasMore() const { return xulstore_iter_has_more(this); }
nsresult XULStoreIterator::GetNext(nsAString* item) {
return xulstore_iter_get_next(this, item);
}
void DefaultDelete<XULStoreIterator>::operator()(XULStoreIterator* ptr) const {
xulstore_iter_free(ptr);
}
namespace XULStore {
already_AddRefed<nsIXULStore> GetService() {
nsCOMPtr<nsIXULStore> xulStore;
if (sXULStore) {
xulStore = sXULStore;
} else {
xulstore_new_service(getter_AddRefs(xulStore));
sXULStore = xulStore;
mozilla::ClearOnShutdown(&sXULStore);
}
return xulStore.forget();
}
nsresult SetValue(const nsAString& doc, const nsAString& id,
const nsAString& attr, const nsAString& value) {
return xulstore_set_value(&doc, &id, &attr, &value);
}
nsresult HasValue(const nsAString& doc, const nsAString& id,
const nsAString& attr, bool& has_value) {
return xulstore_has_value(&doc, &id, &attr, &has_value);
}
nsresult GetValue(const nsAString& doc, const nsAString& id,
const nsAString& attr, nsAString& value) {
return xulstore_get_value(&doc, &id, &attr, &value);
}
nsresult RemoveValue(const nsAString& doc, const nsAString& id,
const nsAString& attr) {
return xulstore_remove_value(&doc, &id, &attr);
}
nsresult GetIDs(const nsAString& doc, UniquePtr<XULStoreIterator>& iter) {
// We assign the value of the iter here in C++ via a return value
// rather than in the Rust function via an out parameter in order
// to ensure that any old value is deleted, since the UniquePtr's
// assignment operator won't delete the old value if the assignment
// happens in Rust.
nsresult result;
iter.reset(xulstore_get_ids(&doc, &result));
return result;
}
nsresult GetAttrs(const nsAString& doc, const nsAString& id,
UniquePtr<XULStoreIterator>& iter) {
// We assign the value of the iter here in C++ via a return value
// rather than in the Rust function via an out parameter in order
// to ensure that any old value is deleted, since the UniquePtr's
// assignment operator won't delete the old value if the assignment
// happens in Rust.
nsresult result;
iter.reset(xulstore_get_attrs(&doc, &id, &result));
return result;
}
}; // namespace XULStore
}; // namespace mozilla

View File

@ -1,55 +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 https://mozilla.org/MPL/2.0/. */
/*
* This file declares the XULStore API for C++ via the mozilla::XULStore
* namespace and the mozilla::XULStoreIterator class. It also declares
* the mozilla::XULStore::GetService() function that the component manager
* uses to instantiate and retrieve the nsIXULStore singleton.
*/
#ifndef mozilla_XULStore_h
#define mozilla_XULStore_h
#include "nsIXULStore.h"
namespace mozilla {
class XULStoreIterator final {
public:
bool HasMore() const;
nsresult GetNext(nsAString* item);
private:
XULStoreIterator() = delete;
XULStoreIterator(const XULStoreIterator&) = delete;
XULStoreIterator& operator=(const XULStoreIterator&) = delete;
~XULStoreIterator() = delete;
};
template <>
class DefaultDelete<XULStoreIterator> {
public:
void operator()(XULStoreIterator* ptr) const;
};
namespace XULStore {
// Instantiates and retrieves the nsIXULStore singleton that JS uses to access
// the store. C++ code should use the mozilla::XULStore::* functions instead.
already_AddRefed<nsIXULStore> GetService();
nsresult SetValue(const nsAString& doc, const nsAString& id,
const nsAString& attr, const nsAString& value);
nsresult HasValue(const nsAString& doc, const nsAString& id,
const nsAString& attr, bool& has_value);
nsresult GetValue(const nsAString& doc, const nsAString& id,
const nsAString& attr, nsAString& value);
nsresult RemoveValue(const nsAString& doc, const nsAString& id,
const nsAString& attr);
nsresult GetIDs(const nsAString& doc, UniquePtr<XULStoreIterator>& iter);
nsresult GetAttrs(const nsAString& doc, const nsAString& id,
UniquePtr<XULStoreIterator>& iter);
}; // namespace XULStore
}; // namespace mozilla
#endif // mozilla_XULStore_h

View File

@ -2,45 +2,127 @@
* 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"];
const xulStore = Cc["@mozilla.org/xul/xulstore;1"].getService(Ci.nsIXULStore);
// Enables logging.
// Enables logging and shorter save intervals.
const debugMode = false;
// Internal function for logging debug messages to the Error Console window
function log(message) {
if (!debugMode)
return;
console.log("XULStore: " + message);
// Delay when a change is made to when the file is saved.
// 30 seconds normally, or 3 seconds for testing
const WRITE_DELAY_MS = (debugMode ? 3 : 30) * 1000;
const XULSTORE_CID = Components.ID("{6f46b6f4-c8b1-4bd4-a4fa-9ebbed0753ea}");
const STOREDB_FILENAME = "xulstore.json";
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
ChromeUtils.defineModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
function XULStore() {
if (!Services.appinfo.inSafeMode)
this.load();
}
const XULStore = {
setValue: xulStore.setValue,
hasValue: xulStore.hasValue,
getValue: xulStore.getValue,
removeValue: xulStore.removeValue,
removeDocument: xulStore.removeDocument,
XULStore.prototype = {
classID: XULSTORE_CID,
QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver, Ci.nsIXULStore,
Ci.nsISupportsWeakReference]),
_xpcom_factory: XPCOMUtils.generateSingletonFactory(XULStore),
/**
* Sets a value for a specified node's attribute, except in
* the case below (following the original XULDocument::persist):
* 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).
/* ---------- private members ---------- */
/*
* The format of _data is _data[docuri][elementid][attribute]. For example:
* {
* "chrome://blah/foo.xul" : {
* "main-window" : { aaa : 1, bbb : "c" },
* "barColumn" : { ddd : 9, eee : "f" },
* },
*
* @param node - DOM node
* @param attr - attribute to store
* "chrome://foopy/b.xul" : { ... },
* ...
* }
*/
_data: {},
_storeFile: null,
_needsSaving: false,
_saveAllowed: true,
_writeTimer: Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer),
load() {
Services.obs.addObserver(this, "profile-before-change", true);
try {
this._storeFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
} catch (ex) {
try {
this._storeFile = Services.dirsvc.get("ProfDS", Ci.nsIFile);
} catch (ex) {
throw new Error("Can't find profile directory.");
}
}
this._storeFile.append(STOREDB_FILENAME);
this.readFile();
},
observe(subject, topic, data) {
this.writeFile();
if (topic == "profile-before-change") {
this._saveAllowed = false;
}
},
/*
* Internal function for logging debug messages to the Error Console window
*/
log(message) {
if (!debugMode)
return;
console.log("XULStore: " + message);
},
readFile() {
try {
this._data = JSON.parse(Cu.readUTF8File(this._storeFile));
} catch (e) {
this.log("Error reading JSON: " + e);
// This exception could mean that the file didn't exist.
// We'll just ignore the error and start with a blank slate.
}
},
async writeFile() {
if (!this._needsSaving)
return;
this._needsSaving = false;
this.log("Writing to xulstore.json");
try {
let data = JSON.stringify(this._data);
let encoder = new TextEncoder();
data = encoder.encode(data);
await OS.File.writeAtomic(this._storeFile.path, data,
{ tmpPath: this._storeFile.path + ".tmp" });
} catch (e) {
this.log("Failed to write xulstore.json: " + e);
throw e;
}
},
markAsChanged() {
if (this._needsSaving || !this._storeFile)
return;
// Don't write the file more than once every 30 seconds.
this._needsSaving = true;
this._writeTimer.init(this, WRITE_DELAY_MS, Ci.nsITimer.TYPE_ONE_SHOT);
},
/* ---------- interface implementation ---------- */
persist(node, attr) {
if (!node.id) {
throw new Error("Node without ID passed into persist()");
@ -50,7 +132,7 @@ const XULStore = {
const value = node.getAttribute(attr);
if (node.localName == "window") {
log("Persisting attributes to windows is handled by nsXULWindow.");
this.log("Persisting attributes to windows is handled by nsXULWindow.");
return;
}
@ -58,38 +140,169 @@ const XULStore = {
// 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);
if (!value && this.hasValue(uri, node.id, attr)) {
this.removeValue(uri, node.id, attr);
} else {
xulStore.setValue(uri, node.id, attr, value);
this.setValue(uri, node.id, attr, value);
}
},
setValue(docURI, id, attr, value) {
this.log("Saving " + attr + "=" + value + " for id=" + id + ", doc=" + docURI);
if (!this._saveAllowed) {
Services.console.logStringMessage("XULStore: Changes after profile-before-change are ignored!");
return;
}
// bug 319846 -- don't save really long attributes or values.
if (id.length > 512 || attr.length > 512) {
throw Components.Exception("id or attribute name too long", Cr.NS_ERROR_ILLEGAL_VALUE);
}
if (value.length > 4096) {
Services.console.logStringMessage("XULStore: Warning, truncating long attribute value");
value = value.substr(0, 4096);
}
let obj = this._data;
if (!(docURI in obj)) {
obj[docURI] = {};
}
obj = obj[docURI];
if (!(id in obj)) {
obj[id] = {};
}
obj = obj[id];
// Don't set the value if it is already set to avoid saving the file.
if (attr in obj && obj[attr] == value)
return;
obj[attr] = value; // IE, this._data[docURI][id][attr] = value;
this.markAsChanged();
},
hasValue(docURI, id, attr) {
this.log("has store value for id=" + id + ", attr=" + attr + ", doc=" + docURI);
let ids = this._data[docURI];
if (ids) {
let attrs = ids[id];
if (attrs) {
return attr in attrs;
}
}
return false;
},
getValue(docURI, id, attr) {
this.log("get store value for id=" + id + ", attr=" + attr + ", doc=" + docURI);
let ids = this._data[docURI];
if (ids) {
let attrs = ids[id];
if (attrs) {
return attrs[attr] || "";
}
}
return "";
},
removeValue(docURI, id, attr) {
this.log("remove store value for id=" + id + ", attr=" + attr + ", doc=" + docURI);
if (!this._saveAllowed) {
Services.console.logStringMessage("XULStore: Changes after profile-before-change are ignored!");
return;
}
let ids = this._data[docURI];
if (ids) {
let attrs = ids[id];
if (attrs && attr in attrs) {
delete attrs[attr];
if (Object.getOwnPropertyNames(attrs).length == 0) {
delete ids[id];
if (Object.getOwnPropertyNames(ids).length == 0) {
delete this._data[docURI];
}
}
this.markAsChanged();
}
}
},
removeDocument(docURI) {
this.log("remove store values for doc=" + docURI);
if (!this._saveAllowed) {
Services.console.logStringMessage("XULStore: Changes after profile-before-change are ignored!");
return;
}
if (this._data[docURI]) {
delete this._data[docURI];
this.markAsChanged();
}
},
getIDsEnumerator(docURI) {
return new XULStoreEnumerator(xulStore.getIDsEnumerator(docURI));
this.log("Getting ID enumerator for doc=" + docURI);
if (!(docURI in this._data))
return new nsStringEnumerator([]);
let result = [];
let ids = this._data[docURI];
if (ids) {
for (let id in this._data[docURI]) {
result.push(id);
}
}
return new nsStringEnumerator(result);
},
getAttributeEnumerator(docURI, id) {
return new XULStoreEnumerator(xulStore.getAttributeEnumerator(docURI, id));
this.log("Getting attribute enumerator for id=" + id + ", doc=" + docURI);
if (!(docURI in this._data) || !(id in this._data[docURI]))
return new nsStringEnumerator([]);
let attrs = [];
for (let attr in this._data[docURI][id]) {
attrs.push(attr);
}
return new nsStringEnumerator(attrs);
},
};
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());
}
}
function nsStringEnumerator(items) {
this._items = items;
}
nsStringEnumerator.prototype = {
QueryInterface: ChromeUtils.generateQI([Ci.nsIStringEnumerator]),
_nextIndex: 0,
[Symbol.iterator]() {
return this._items.values();
},
hasMore() {
return this._nextIndex < this._items.length;
},
getNext() {
if (!this.hasMore())
throw Cr.NS_ERROR_NOT_AVAILABLE;
return this._items[this._nextIndex++];
},
};
var EXPORTED_SYMBOLS = ["XULStore"];

View File

@ -6,11 +6,9 @@
Classes = [
{
'cid': '{be70bf11-0c28-4a02-a38c-0148538d42cf}',
'cid': '{6f46b6f4-c8b1-4bd4-a4fa-9ebbed0753ea}',
'contract_ids': ['@mozilla.org/xul/xulstore;1'],
'type': 'nsIXULStore',
'headers': ['mozilla/XULStore.h'],
'singleton': True,
'constructor': 'mozilla::XULStore::GetService',
'jsm': 'resource://gre/modules/XULStore.jsm',
'constructor': 'XULStore',
},
]

View File

@ -14,26 +14,12 @@ XPIDL_SOURCES += [
'nsIXULStore.idl',
]
TEST_DIRS += [
'tests/gtest',
]
EXPORTS.mozilla += [
'XULStore.h',
]
XPIDL_MODULE = 'toolkit_xulstore'
EXTRA_JS_MODULES += [
'XULStore.jsm',
]
XPIDL_MODULE = 'xulstore'
XPCOM_MANIFESTS += [
'components.conf',
]
UNIFIED_SOURCES += [
'XULStore.cpp',
]
FINAL_LIBRARY = 'xul'

View File

@ -5,19 +5,31 @@
#include "nsISupports.idl"
interface nsIStringEnumerator;
webidl Node;
/**
* The XUL store is used to store information related to a XUL document/application.
* 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.
*
* 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.
* The data is serialized to [profile directory]/xulstore.json
*/
[scriptable, uuid(987c4b35-c426-4dd7-ad49-3c9fa4c65d20)]
interface nsIXULStore: nsISupports
{
/**
* Sets a value for a specified node's attribute, except in
* the case below (following the original XULDocument::persist):
* 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
*/
void persist(in Node aNode, in AString attr);
/**
* Sets a value in the store.
*

View File

@ -1,114 +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::StoreError as RkvStoreError;
use serde_json::Error as SerdeJsonError;
use std::{
io::Error as IoError, str::Utf8Error, string::FromUtf16Error, sync::PoisonError,
};
pub(crate) type XULStoreResult<T> = Result<T, XULStoreError>;
#[derive(Debug, Fail)]
pub(crate) enum XULStoreError {
#[fail(display = "error converting bytes: {:?}", _0)]
ConvertBytes(Utf8Error),
#[fail(display = "error converting string: {:?}", _0)]
ConvertString(FromUtf16Error),
#[fail(display = "I/O error: {:?}", _0)]
IoError(IoError),
#[fail(display = "iteration is finished")]
IterationFinished,
#[fail(display = "JSON error: {}", _0)]
JsonError(SerdeJsonError),
#[fail(display = "error result {}", _0)]
NsResult(nsresult),
#[fail(display = "poison error getting read/write lock")]
PoisonError,
#[fail(display = "store error: {:?}", _0)]
RkvStoreError(RkvStoreError),
#[fail(display = "id or attribute name too long")]
IdAttrNameTooLong,
#[fail(display = "unavailable")]
Unavailable,
#[fail(display = "unexpected key: {:?}", _0)]
UnexpectedKey(String),
#[fail(display = "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::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 From<FromUtf16Error> for XULStoreError {
fn from(err: FromUtf16Error) -> XULStoreError {
XULStoreError::ConvertString(err)
}
}
impl From<nsresult> for XULStoreError {
fn from(result: nsresult) -> XULStoreError {
XULStoreError::NsResult(result)
}
}
impl<T> From<PoisonError<T>> for XULStoreError {
fn from(_: PoisonError<T>) -> XULStoreError {
XULStoreError::PoisonError
}
}
impl From<RkvStoreError> for XULStoreError {
fn from(err: RkvStoreError) -> XULStoreError {
XULStoreError::RkvStoreError(err)
}
}
impl From<Utf8Error> for XULStoreError {
fn from(err: Utf8Error) -> XULStoreError {
XULStoreError::ConvertBytes(err)
}
}
impl From<IoError> for XULStoreError {
fn from(err: IoError) -> XULStoreError {
XULStoreError::IoError(err)
}
}
impl From<SerdeJsonError> for XULStoreError {
fn from(err: SerdeJsonError) -> XULStoreError {
XULStoreError::JsonError(err)
}
}

View File

@ -1,318 +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;
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);
}
#[derive(xpcom)]
#[xpimplements(nsIXULStore)]
#[refcnt = "atomic"]
pub struct InitXULStoreService {}
impl XULStoreService {
fn new() -> RefPtr<XULStoreService> {
XULStoreService::allocate(InitXULStoreService {})
}
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()),
}
}
}
#[derive(xpcom)]
#[xpimplements(nsIStringEnumerator)]
#[refcnt = "nonatomic"]
pub(crate) struct InitStringEnumerator {
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()),
}
}
}
#[derive(xpcom)]
#[xpimplements(nsIObserver)]
#[refcnt = "nonatomic"]
pub(crate) struct InitProfileChangeObserver {}
impl ProfileChangeObserver {
#[allow(non_snake_case)]
unsafe fn Observe(
&self,
_subject: *const nsISupports,
_topic: *const c_char,
_data: *const i16,
) -> 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));
}

View File

@ -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)?)
}
}

View File

@ -1,219 +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 failure;
#[macro_use]
extern crate lazy_static;
extern crate libc;
extern crate lmdb;
#[macro_use]
extern crate log;
extern crate moz_task;
extern crate nserror;
extern crate nsstring;
extern crate rkv;
extern crate serde_json;
#[macro_use]
extern crate xpcom;
mod error;
mod ffi;
mod iter;
mod persist;
mod statics;
use crate::{
error::{XULStoreError, XULStoreResult},
iter::XULStoreIterator,
persist::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 mut ids: Vec<String> = ids.keys().map(|id| id.clone()).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 mut attrs: Vec<String> = attrs.keys().map(|attr| attr.clone()).collect();
Ok(XULStoreIterator::new(attrs.into_iter()))
}
None => Ok(XULStoreIterator::new(vec![].into_iter())),
},
None => Ok(XULStoreIterator::new(vec![].into_iter())),
}
}

View File

@ -1,160 +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},
statics::get_database,
};
use crossbeam_utils::atomic::AtomicCell;
use lmdb::Error as LmdbError;
use moz_task::{create_thread, Task, TaskRunnable};
use nserror::nsresult;
use rkv::{StoreError as RkvStoreError, Value};
use std::{collections::HashMap, sync::Mutex, thread::sleep, time::Duration};
use xpcom::{interfaces::nsIThread, RefPtr, ThreadBoundRefPtr};
/// 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.
lazy_static! {
/// 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 ref CHANGES: Mutex<Option<HashMap<String, Option<String>>>> = { 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 ref PERSIST: Mutex<()> = { Mutex::new(()) };
static ref THREAD: Option<ThreadBoundRefPtr<nsIThread>> = {
let thread: RefPtr<nsIThread> = match create_thread("XULStore") {
Ok(thread) => thread,
Err(err) => {
error!("error creating XULStore thread: {}", err);
return None;
}
};
Some(ThreadBoundRefPtr::new(thread))
};
}
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());
let thread = THREAD
.as_ref()
.ok_or(XULStoreError::Unavailable)?
.get_ref()
.ok_or(XULStoreError::Unavailable)?;
TaskRunnable::new("XULStore::Persist", task)?.dispatch(thread)?;
}
// 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()?;
let db = get_database()?;
let mut writer = db.env.write()?;
// 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();
// The Option should be a Some(HashMap) (otherwise the task
// shouldn't have been scheduled in the first place). If it's None,
// unexpectedly, then we return an error early.
let writes = writes.ok_or(XULStoreError::Unavailable)?;
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::LmdbError(LmdbError::NotFound)) => {
warn!("tried to remove key that isn't in the store");
}
Err(err) => return Err(err.into()),
}
}
}
}
writer.commit()?;
Ok(())
}()));
}
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(())
}
}

View File

@ -1,245 +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 rkv::{Rkv, SingleStore, StoreOptions, Value};
use std::{
collections::BTreeMap,
fs::{create_dir_all, remove_file, File},
path::PathBuf,
str,
sync::Mutex,
};
use xpcom::{interfaces::nsIFile, XpCom};
type XULStoreCache = BTreeMap<String, BTreeMap<String, BTreeMap<String, String>>>;
pub struct Database {
pub env: Rkv,
pub store: SingleStore,
}
impl Database {
fn new(env: Rkv, store: SingleStore) -> Database {
Database { env, store }
}
}
lazy_static! {
static ref PROFILE_DIR: Mutex<Option<PathBuf>> = {
observe_profile_change();
Mutex::new(get_profile_dir().ok())
};
pub(crate) static ref DATA_CACHE: Mutex<Option<XULStoreCache>> =
{ Mutex::new(cache_data().ok()) };
}
pub(crate) fn get_database() -> XULStoreResult<Database> {
let xulstore_dir = get_xulstore_dir()?;
let env = Rkv::new(xulstore_dir.as_path())?;
let store = env.open_single("db", StoreOptions::create())?;
Ok(Database::new(env, 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 = xpcom::services::get_DirectoryService().ok_or(XULStoreError::Unavailable)?;
let mut profile_dir = xpcom::GetterAddrefs::<nsIFile>::new();
unsafe {
dir_svc
.Get(
c_str!("ProfD").as_ptr(),
&nsIFile::IID,
profile_dir.void_ptr(),
)
.to_result()
.or_else(|_| {
dir_svc
.Get(
c_str!("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 = xpcom::services::get_ObserverService().ok_or(XULStoreError::Unavailable)?;
let observer = ProfileChangeObserver::new();
unsafe {
obs_svc
.AddObserver(observer.coerce(), c_str!("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 app_info_svc = xpcom::services::get_AppInfoService().ok_or(XULStoreError::Unavailable)?;
let mut in_safe_mode = false;
unsafe {
app_info_svc.GetInSafeMode(&mut in_safe_mode).to_result()?;
}
Ok(in_safe_mode)
}
fn cache_data() -> XULStoreResult<XULStoreCache> {
if in_safe_mode()? {
return Ok(BTreeMap::new());
}
let db = get_database()?;
maybe_migrate_data(&db.env, db.store);
let reader = db.env.read()?;
let mut all = XULStoreCache::default();
let iterator = db.store.iter_start(&reader)?;
for result in iterator {
let (key, value): (&str, String) = match result {
Ok((key, value)) => {
assert!(value.is_some(), "iterated key has 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(env: &Rkv, 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 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: &Option<Value>) -> XULStoreResult<String> {
match value {
Some(Value::Str(val)) => Ok(val.to_string()),
// Per the XULStore API, return an empty string if the value
// isn't found.
None => Ok(String::new()),
// 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).
Some(_) => Err(XULStoreError::UnexpectedValue),
}
}

View File

@ -13,7 +13,7 @@
<script>
<![CDATA[
const {XULStore} = ChromeUtils.import("resource://gre/modules/XULStore.jsm");
let XULStore = Cc["@mozilla.org/xul/xulstore;1"].getService(Ci.nsIXULStore);
let URI = "chrome://mochitests/content/chrome/toolkit/components/xulstore/tests/chrome/window_persistence.xul";
function opened()

View File

@ -1,7 +0,0 @@
[package]
name = "xulstore-gtest"
version = "0.1.0"
authors = ["nobody@mozilla.org"]
[lib]
path = "test.rs"

View File

@ -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(NS_LITERAL_STRING("SetGetValue"));
nsAutoString id(NS_LITERAL_STRING("foo"));
nsAutoString attr(NS_LITERAL_STRING("bar"));
nsAutoString value;
EXPECT_EQ(GetValue(doc, id, attr, value), NS_OK);
EXPECT_TRUE(value.EqualsASCII(""));
{
nsAutoString value(NS_LITERAL_STRING("baz"));
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(NS_LITERAL_STRING("HasValue"));
nsAutoString id(NS_LITERAL_STRING("foo"));
nsAutoString attr(NS_LITERAL_STRING("bar"));
bool hasValue = true;
EXPECT_EQ(HasValue(doc, id, attr, hasValue), NS_OK);
EXPECT_FALSE(hasValue);
nsAutoString value(NS_LITERAL_STRING("baz"));
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(NS_LITERAL_STRING("RemoveValue"));
nsAutoString id(NS_LITERAL_STRING("foo"));
nsAutoString attr(NS_LITERAL_STRING("bar"));
nsAutoString value(NS_LITERAL_STRING("baz"));
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(NS_LITERAL_STRING("idIterDoc"));
nsAutoString id1(NS_LITERAL_STRING("id1"));
nsAutoString id2(NS_LITERAL_STRING("id2"));
nsAutoString id3(NS_LITERAL_STRING("id3"));
nsAutoString attr(NS_LITERAL_STRING("attr"));
nsAutoString value(NS_LITERAL_STRING("value"));
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(NS_LITERAL_STRING("otherDoc"));
nsAutoString otherID(NS_LITERAL_STRING("otherID"));
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(NS_LITERAL_STRING("attrIterDoc"));
nsAutoString id(NS_LITERAL_STRING("id"));
nsAutoString attr1(NS_LITERAL_STRING("attr1"));
nsAutoString attr2(NS_LITERAL_STRING("attr2"));
nsAutoString attr3(NS_LITERAL_STRING("attr3"));
nsAutoString value(NS_LITERAL_STRING("value"));
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(NS_LITERAL_STRING("otherID"));
nsAutoString otherAttr(NS_LITERAL_STRING("otherAttr"));
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());
}

View File

@ -1,14 +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'
if CONFIG['CC_TYPE'] in ('clang', 'gcc'):
CXXFLAGS += ['-Wno-error=shadow']

View File

@ -1,71 +0,0 @@
"use strict";
const {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm");
function run_test() {
do_get_profile();
run_next_test();
}
add_task(async function test_create_old_datastore() {
const path = OS.Path.join(OS.Constants.Path.profileDir, "xulstore.json");
const xulstoreJSON = {
doc1: {
id1: {
attr1: "value1",
},
},
doc2: {
id1: {
attr2: "value2",
},
id2: {
attr1: "value1",
attr2: "value2",
attr3: "value3",
},
id3: {},
},
doc3: {},
};
await OS.File.writeAtomic(path, JSON.stringify(xulstoreJSON));
});
add_task(async function test_get_values() {
// We wait until now to import XULStore.jsm 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"), "");
});

View File

@ -1,43 +0,0 @@
"use strict";
const {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm");
function run_test() {
do_get_profile();
run_next_test();
}
add_task(async function test_create_old_datastore() {
const path = OS.Path.join(OS.Constants.Path.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 OS.File.writeAtomic(path, JSON.stringify(xulstoreJSON));
});
add_task(async function test_get_values() {
// We wait until now to import XULStore.jsm 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"), "");
});

View File

@ -1,28 +0,0 @@
"use strict";
const {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm");
function run_test() {
do_get_profile();
run_next_test();
}
add_task(async function test_create_old_datastore() {
const path = OS.Path.join(OS.Constants.Path.profileDir, "xulstore.json");
// Invalid JSON: it's missing the final closing brace.
const xulstoreJSON = '{ doc: { id: { attr: "value" } }';
await OS.File.writeAtomic(path, xulstoreJSON);
});
add_task(async function test_get_value() {
// We wait until now to import XULStore.jsm 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"), "");
});

View File

@ -1,43 +0,0 @@
"use strict";
const {OS} = ChromeUtils.import("resource://gre/modules/osfile.jsm");
const {FileUtils} = ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
add_task(async function test_get_values() {
// Import XULStore.jsm 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 OS.File.
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");
});

View File

@ -2,7 +2,3 @@
skip-if = toolkit == 'android'
[test_XULStore.js]
[test_XULStore_migration.js]
[test_XULStore_migration_fail_invalid_json.js]
[test_XULStore_migration_fail_invalid_data.js]
[test_XULStore_migration_profile_change.js]

View File

@ -27,7 +27,6 @@ audioipc-server = { path = "../../../../media/audioipc/server", optional = true
u2fhid = { path = "../../../../dom/webauthn/u2f-hid-rs" }
gkrust_utils = { path = "../../../../xpcom/rust/gkrust_utils" }
rsdparsa_capi = { path = "../../../../media/webrtc/signaling/src/sdp/rsdparsa_capi" }
xulstore = { path = "../../../components/xulstore" }
# We have these to enforce common feature sets for said crates.
log = {version = "0.4", features = ["release_max_level_info"]}
env_logger = {version = "0.5", default-features = false} # disable `regex` to reduce code size

View File

@ -34,7 +34,6 @@ extern crate log;
extern crate cert_storage;
extern crate cosec;
extern crate rsdparsa_capi;
extern crate xulstore;
#[cfg(feature = "spidermonkey_rust")]
extern crate jsrust_shared;
#[cfg(feature = "bitsdownload")]

View File

@ -51,11 +51,6 @@ if (AppConstants.MOZ_CRASHREPORTER) {
});
}
XPCOMUtils.defineLazyGetter(Services, "xulStore", () => {
const {XULStore} = ChromeUtils.import("resource://gre/modules/XULStore.jsm");
return XULStore;
});
XPCOMUtils.defineLazyGetter(Services, "io", () => {
return Cc["@mozilla.org/network/io-service;1"]
.getService(Ci.nsIIOService)
@ -104,6 +99,7 @@ var initTable = {
netUtils: ["@mozilla.org/network/util;1", "nsINetUtil"],
loadContextInfo: ["@mozilla.org/load-context-info-factory;1", "nsILoadContextInfoFactory"],
qms: ["@mozilla.org/dom/quota-manager-service;1", "nsIQuotaManagerService"],
xulStore: ["@mozilla.org/xul/xulstore;1", "nsIXULStore"],
};
if (AppConstants.platform == "android") {

View File

@ -52,9 +52,6 @@ service('URIFixup', 'nsIURIFixup',
"@mozilla.org/docshell/urifixup;1")
service('Bits', 'nsIBits',
"@mozilla.org/bits;1")
# NB: this should also expose nsIXULAppInfo, as does Services.jsm.
service('AppInfoService', 'nsIXULRuntime',
"@mozilla.org/xre/app-info;1")
# The definition file needs access to the definitions of the particular
# interfaces. If you add a new interface here, make sure the necessary includes
@ -88,7 +85,6 @@ CPP_INCLUDES = """
#include "nsIGfxInfo.h"
#include "nsIURIFixup.h"
#include "nsIBits.h"
#include "nsIXULRuntime.h"
"""
#####

View File

@ -52,18 +52,6 @@ impl fmt::Debug for nsresult {
}
}
impl<T, E> From<Result<T, E>> for nsresult
where
E: Into<nsresult>,
{
fn from(result: Result<T, E>) -> nsresult {
match result {
Ok(_) => NS_OK,
Err(e) => e.into(),
}
}
}
impl Error for nsresult {}
extern "C" {

View File

@ -55,7 +55,6 @@
#include "mozilla/AutoRestore.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
#include "mozilla/XULStore.h"
#include "mozilla/dom/BarProps.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/Event.h"
@ -1604,8 +1603,15 @@ nsresult nsXULWindow::GetPersistentValue(const nsAtom* aAttr,
NS_ENSURE_SUCCESS(rv, rv);
NS_ConvertUTF8toUTF16 uri(utf8uri);
nsDependentAtomString attrString(aAttr);
rv = XULStore::GetValue(uri, windowElementId, attrString, aValue);
if (!mLocalStore) {
mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
if (NS_WARN_IF(!mLocalStore)) {
return NS_ERROR_NOT_INITIALIZED;
}
}
rv = mLocalStore->GetValue(uri, windowElementId, nsDependentAtomString(aAttr),
aValue);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -1655,9 +1661,15 @@ nsresult nsXULWindow::SetPersistentValue(const nsAtom* aAttr,
maybeConvertedValue);
}
nsDependentAtomString attrString(aAttr);
return XULStore::SetValue(uri, windowElementId, attrString,
maybeConvertedValue);
if (!mLocalStore) {
mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
if (NS_WARN_IF(!mLocalStore)) {
return NS_ERROR_NOT_INITIALIZED;
}
}
return mLocalStore->SetValue(
uri, windowElementId, nsDependentAtomString(aAttr), maybeConvertedValue);
}
NS_IMETHODIMP nsXULWindow::SavePersistentAttributes() {

View File

@ -33,6 +33,7 @@
#include "nsIXULBrowserWindow.h"
#include "nsIWidgetListener.h"
#include "nsITabParent.h"
#include "nsIXULStore.h"
namespace mozilla {
namespace dom {
@ -194,6 +195,7 @@ class nsXULWindow : public nsIBaseWindow,
GetPrimaryTabParentSize(int32_t* aWidth, int32_t* aHeight);
nsresult GetPrimaryContentShellSize(int32_t* aWidth, int32_t* aHeight);
nsresult SetPrimaryTabParentSize(int32_t aWidth, int32_t aHeight);
nsCOMPtr<nsIXULStore> mLocalStore;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsXULWindow, NS_XULWINDOW_IMPL_CID)