Bug 559505 - Deprecate localstore.rdf and replace it with xulstore. r=enn, sr=bsmedberg

CLOSED TREE
This commit is contained in:
Roberto A. Vitillo 2014-08-19 10:31:00 -04:00
parent f66c4d46e0
commit 4bafda674f
27 changed files with 1195 additions and 618 deletions

View File

@ -296,6 +296,7 @@
@BINPATH@/components/toolkit_finalizationwitness.xpt
@BINPATH@/components/toolkit_formautofill.xpt
@BINPATH@/components/toolkit_osfile.xpt
@BINPATH@/components/toolkit_xulstore.xpt
@BINPATH@/components/toolkitprofile.xpt
#ifdef MOZ_ENABLE_XREMOTE
@BINPATH@/components/toolkitremote.xpt
@ -540,6 +541,8 @@
#endif
@BINPATH@/components/TelemetryStartup.js
@BINPATH@/components/TelemetryStartup.manifest
@BINPATH@/components/XULStore.js
@BINPATH@/components/XULStore.manifest
@BINPATH@/components/Webapps.js
@BINPATH@/components/Webapps.manifest
@BINPATH@/components/AppsService.js

View File

@ -1320,7 +1320,7 @@ BrowserGlue.prototype = {
_migrateUI: function BG__migrateUI() {
const UI_VERSION = 23;
const BROWSER_DOCURL = "chrome://browser/content/browser.xul#";
const BROWSER_DOCURL = "chrome://browser/content/browser.xul";
let currentUIVersion = 0;
try {
currentUIVersion = Services.prefs.getIntPref("browser.migration.version");
@ -1328,28 +1328,22 @@ BrowserGlue.prototype = {
if (currentUIVersion >= UI_VERSION)
return;
this._rdf = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService);
this._dataSource = this._rdf.GetDataSource("rdf:local-store");
this._dirty = false;
let xulStore = Cc["@mozilla.org/xul/xulstore;1"].getService(Ci.nsIXULStore);
if (currentUIVersion < 2) {
// This code adds the customizable bookmarks button.
let currentsetResource = this._rdf.GetResource("currentset");
let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar");
let currentset = this._getPersist(toolbarResource, currentsetResource);
let currentset = xulStore.getValue(BROWSER_DOCURL, "nav-bar", "currentset");
// Need to migrate only if toolbar is customized and the element is not found.
if (currentset &&
currentset.indexOf("bookmarks-menu-button-container") == -1) {
currentset += ",bookmarks-menu-button-container";
this._setPersist(toolbarResource, currentsetResource, currentset);
xulStore.setValue(BROWSER_DOCURL, "nav-bar", "currentset", currentset);
}
}
if (currentUIVersion < 3) {
// This code merges the reload/stop/go button into the url bar.
let currentsetResource = this._rdf.GetResource("currentset");
let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar");
let currentset = this._getPersist(toolbarResource, currentsetResource);
let currentset = xulStore.getValue(BROWSER_DOCURL, "nav-bar", "currentset");
// Need to migrate only if toolbar is customized and all 3 elements are found.
if (currentset &&
currentset.indexOf("reload-button") != -1 &&
@ -1360,15 +1354,13 @@ BrowserGlue.prototype = {
.replace(/(^|,)stop-button($|,)/, "$1$2")
.replace(/(^|,)urlbar-container($|,)/,
"$1urlbar-container,reload-button,stop-button$2");
this._setPersist(toolbarResource, currentsetResource, currentset);
xulStore.setValue(BROWSER_DOCURL, "nav-bar", "currentset", currentset);
}
}
if (currentUIVersion < 4) {
// This code moves the home button to the immediate left of the bookmarks menu button.
let currentsetResource = this._rdf.GetResource("currentset");
let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar");
let currentset = this._getPersist(toolbarResource, currentsetResource);
let currentset = xulStore.getValue(BROWSER_DOCURL, "nav-bar", "currentset");
// Need to migrate only if toolbar is customized and the elements are found.
if (currentset &&
currentset.indexOf("home-button") != -1 &&
@ -1376,24 +1368,21 @@ BrowserGlue.prototype = {
currentset = currentset.replace(/(^|,)home-button($|,)/, "$1$2")
.replace(/(^|,)bookmarks-menu-button-container($|,)/,
"$1home-button,bookmarks-menu-button-container$2");
this._setPersist(toolbarResource, currentsetResource, currentset);
xulStore.setValue(BROWSER_DOCURL, "nav-bar", "currentset", currentset);
}
}
if (currentUIVersion < 5) {
// This code uncollapses PersonalToolbar if its collapsed status is not
// persisted, and user customized it or changed default bookmarks.
let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "PersonalToolbar");
let collapsedResource = this._rdf.GetResource("collapsed");
let collapsed = this._getPersist(toolbarResource, collapsedResource);
//
// If the user does not have a persisted value for the toolbar's
// "collapsed" attribute, try to determine whether it's customized.
if (collapsed === null) {
if (!xulStore.hasValue(BROWSER_DOCURL, "PersonalToolbar", "collapsed")) {
// We consider the toolbar customized if it has more than
// 3 children, or if it has a persisted currentset value.
let currentsetResource = this._rdf.GetResource("currentset");
let toolbarIsCustomized = !!this._getPersist(toolbarResource,
currentsetResource);
let toolbarIsCustomized = xulStore.hasValue(BROWSER_DOCURL,
"PersonalToolbar", "currentset");
let getToolbarFolderCount = function () {
let toolbarFolder =
PlacesUtils.getFolderContents(PlacesUtils.toolbarFolderId).root;
@ -1403,7 +1392,7 @@ BrowserGlue.prototype = {
};
if (toolbarIsCustomized || getToolbarFolderCount() > 3) {
this._setPersist(toolbarResource, collapsedResource, "false");
xulStore.setValue(BROWSER_DOCURL, "PersonalToolbar", "collapsed", "false");
}
}
}
@ -1419,9 +1408,7 @@ BrowserGlue.prototype = {
if (currentUIVersion < 9) {
// This code adds the customizable downloads buttons.
let currentsetResource = this._rdf.GetResource("currentset");
let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar");
let currentset = this._getPersist(toolbarResource, currentsetResource);
let currentset = xulStore.getValue(BROWSER_DOCURL, "nav-bar", "currentset");
// Since the Downloads button is located in the navigation bar by default,
// migration needs to happen only if the toolbar was customized using a
@ -1442,7 +1429,7 @@ BrowserGlue.prototype = {
currentset = currentset.replace(/(^|,)window-controls($|,)/,
"$1downloads-button,window-controls$2")
}
this._setPersist(toolbarResource, currentsetResource, currentset);
xulStore.setValue(BROWSER_DOCURL, "nav-bar", "currentset", currentset);
}
}
@ -1472,15 +1459,13 @@ BrowserGlue.prototype = {
if (currentUIVersion < 12) {
// Remove bookmarks-menu-button-container, then place
// bookmarks-menu-button into its position.
let currentsetResource = this._rdf.GetResource("currentset");
let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar");
let currentset = this._getPersist(toolbarResource, currentsetResource);
let currentset = xulStore.getValue(BROWSER_DOCURL, "nav-bar", "currentset");
// Need to migrate only if toolbar is customized.
if (currentset) {
if (currentset.contains("bookmarks-menu-button-container")) {
currentset = currentset.replace(/(^|,)bookmarks-menu-button-container($|,)/,
"$1bookmarks-menu-button$2");
this._setPersist(toolbarResource, currentsetResource, currentset);
xulStore.setValue(BROWSER_DOCURL, "nav-bar", "currentset", currentset);
}
}
}
@ -1501,20 +1486,16 @@ BrowserGlue.prototype = {
}
if (currentUIVersion < 16) {
let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar");
let collapsedResource = this._rdf.GetResource("collapsed");
let isCollapsed = this._getPersist(toolbarResource, collapsedResource);
let isCollapsed = xulStore.getValue(BROWSER_DOCURL, "nav-bar", "collapsed");
if (isCollapsed == "true") {
this._setPersist(toolbarResource, collapsedResource, "false");
xulStore.setValue(BROWSER_DOCURL, "nav-bar", "collapsed", "false");
}
}
// Insert the bookmarks-menu-button into the nav-bar if it isn't already
// there.
if (currentUIVersion < 17) {
let currentsetResource = this._rdf.GetResource("currentset");
let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar");
let currentset = this._getPersist(toolbarResource, currentsetResource);
let currentset = xulStore.getValue(BROWSER_DOCURL, "nav-bar", "currentset");
// Need to migrate only if toolbar is customized.
if (currentset) {
if (!currentset.contains("bookmarks-menu-button")) {
@ -1531,7 +1512,7 @@ BrowserGlue.prototype = {
currentset = currentset.replace(/(^|,)window-controls($|,)/,
"$1bookmarks-menu-button,window-controls$2")
}
this._setPersist(toolbarResource, currentsetResource, currentset);
xulStore.setValue(BROWSER_DOCURL, "nav-bar", "currentset", currentset);
}
}
}
@ -1541,12 +1522,8 @@ BrowserGlue.prototype = {
let toolbars = ["navigator-toolbox", "nav-bar", "PersonalToolbar",
"addon-bar", "TabsToolbar", "toolbar-menubar"];
for (let resourceName of ["mode", "iconsize"]) {
let resource = this._rdf.GetResource(resourceName);
for (let toolbarId of toolbars) {
let toolbar = this._rdf.GetResource(BROWSER_DOCURL + toolbarId);
if (this._getPersist(toolbar, resource)) {
this._setPersist(toolbar, resource);
}
xulStore.removeValue(BROWSER_DOCURL, toolbarId, resourceName);
}
}
}
@ -1569,21 +1546,13 @@ BrowserGlue.prototype = {
if (currentUIVersion < 20) {
// Remove persisted collapsed state from TabsToolbar.
let resource = this._rdf.GetResource("collapsed");
let toolbar = this._rdf.GetResource(BROWSER_DOCURL + "TabsToolbar");
if (this._getPersist(toolbar, resource)) {
this._setPersist(toolbar, resource);
}
xulStore.removeValue(BROWSER_DOCURL, "TabsToolbar", "collapsed");
}
if (currentUIVersion < 21) {
// Make sure the 'toolbarbutton-1' class will always be present from here
// on out.
let button = this._rdf.GetResource(BROWSER_DOCURL + "bookmarks-menu-button");
let classResource = this._rdf.GetResource("class");
if (this._getPersist(button, classResource)) {
this._setPersist(button, classResource);
}
xulStore.removeValue(BROWSER_DOCURL, "bookmarks-menu-button", "class");
}
if (currentUIVersion < 22) {
@ -1603,49 +1572,10 @@ BrowserGlue.prototype = {
}
}
if (this._dirty)
this._dataSource.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush();
delete this._rdf;
delete this._dataSource;
// Update the migration version.
Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
},
_getPersist: function BG__getPersist(aSource, aProperty) {
var target = this._dataSource.GetTarget(aSource, aProperty, true);
if (target instanceof Ci.nsIRDFLiteral)
return target.Value;
return null;
},
_setPersist: function BG__setPersist(aSource, aProperty, aTarget) {
this._dirty = true;
try {
var oldTarget = this._dataSource.GetTarget(aSource, aProperty, true);
if (oldTarget) {
if (aTarget)
this._dataSource.Change(aSource, aProperty, oldTarget, this._rdf.GetLiteral(aTarget));
else
this._dataSource.Unassert(aSource, aProperty, oldTarget);
}
else {
this._dataSource.Assert(aSource, aProperty, this._rdf.GetLiteral(aTarget), true);
}
// Add the entry to the persisted set for this document if it's not there.
// This code is mostly borrowed from XULDocument::Persist.
let docURL = aSource.ValueUTF8.split("#")[0];
let docResource = this._rdf.GetResource(docURL);
let persistResource = this._rdf.GetResource("http://home.netscape.com/NC-rdf#persist");
if (!this._dataSource.HasAssertion(docResource, persistResource, aSource, true)) {
this._dataSource.Assert(docResource, persistResource, aSource, true);
}
}
catch(ex) {}
},
// ------------------------------
// public nsIBrowserGlue members
// ------------------------------

View File

@ -1020,10 +1020,6 @@ XPCOMUtils.defineLazyServiceGetter(PlacesUIUtils, "RDF",
"@mozilla.org/rdf/rdf-service;1",
"nsIRDFService");
XPCOMUtils.defineLazyGetter(PlacesUIUtils, "localStore", function() {
return PlacesUIUtils.RDF.GetDataSource("rdf:local-store");
});
XPCOMUtils.defineLazyGetter(PlacesUIUtils, "ellipsis", function() {
return Services.prefs.getComplexValue("intl.ellipsis",
Ci.nsIPrefLocalizedString).data;

View File

@ -23,6 +23,14 @@ function PlacesTreeView(aFlatList, aOnOpenFlatContainer, aController) {
PlacesTreeView.prototype = {
get wrappedJSObject() this,
__xulStore: null,
get _xulStore() {
if (!this.__xulStore) {
this.__xulStore = Cc["@mozilla.org/xul/xulstore;1"].getService(Ci.nsIXULStore);
}
return this.__xulStore;
},
__dateService: null,
get _dateService() {
if (!this.__dateService) {
@ -307,11 +315,15 @@ PlacesTreeView.prototype = {
if (!this._flatList &&
curChild instanceof Ci.nsINavHistoryContainerResultNode &&
!this._controller.hasCachedLivemarkInfo(curChild)) {
let resource = this._getResourceForNode(curChild);
let isopen = resource != null &&
PlacesUIUtils.localStore.HasAssertion(resource,
openLiteral,
trueLiteral, true);
let uri = curChild.uri;
let isopen = false;
if (uri) {
let docURI = this._getDocumentURI();
let val = this._xulStore.getValue(docURI, uri, "open");
isopen = (val == "true");
}
if (isopen != curChild.containerOpen)
aToOpen.push(curChild);
else if (curChild.containerOpen && curChild.childCount > 0)
@ -1109,11 +1121,16 @@ PlacesTreeView.prototype = {
return Ci.nsINavHistoryResultTreeViewer.INDEX_INVISIBLE;
},
_getResourceForNode: function PTV_getResourceForNode(aNode)
// Retrieves an nsIURI for the document
_documentURI: null,
_getDocumentURI: function()
{
let uri = aNode.uri;
NS_ASSERT(uri, "if there is no uri, we can't persist the open state");
return uri ? PlacesUIUtils.RDF.GetResource(uri) : null;
if (!this._documentURI) {
let ioService = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
this._documentURI = ioService.newURI(document.URL, null, null);
}
return this._documentURI;
},
// nsITreeView
@ -1497,15 +1514,16 @@ PlacesTreeView.prototype = {
// Persist containers open status, but never persist livemarks.
if (!this._controller.hasCachedLivemarkInfo(node)) {
let resource = this._getResourceForNode(node);
if (resource) {
const openLiteral = PlacesUIUtils.RDF.GetResource("http://home.netscape.com/NC-rdf#open");
const trueLiteral = PlacesUIUtils.RDF.GetLiteral("true");
let uri = node.uri;
if (node.containerOpen)
PlacesUIUtils.localStore.Unassert(resource, openLiteral, trueLiteral);
else
PlacesUIUtils.localStore.Assert(resource, openLiteral, trueLiteral, true);
if (uri) {
let docURI = this._getDocumentURI();
if (node.containerOpen) {
this._xulStore.removeValue(docURI, uri, "open");
} else {
this._xulStore.setValue(docURI, uri, "open", "true");
}
}
}

View File

@ -5,62 +5,25 @@
/**
* Tests PersonalToolbar migration path.
*/
let bg = Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsIObserver);
let gOriginalMigrationVersion;
const BROWSER_URL = getBrowserURL();
let localStore = {
get RDF() Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService),
get store() this.RDF.GetDataSource("rdf:local-store"),
get xulStore() Cc["@mozilla.org/xul/xulstore;1"].getService(Ci.nsIXULStore),
get toolbar()
getValue: function getValue(aProperty)
{
delete this.toolbar;
let toolbar = this.RDF.GetResource(BROWSER_URL + "#PersonalToolbar");
// Add the entry to the persisted set for this document if it's not there.
// See XULDocument::Persist.
let doc = this.RDF.GetResource(BROWSER_URL);
let persist = this.RDF.GetResource("http://home.netscape.com/NC-rdf#persist");
if (!this.store.HasAssertion(doc, persist, toolbar, true)) {
this.store.Assert(doc, persist, toolbar, true);
}
return this.toolbar = toolbar;
return this.xulStore.getValue(BROWSER_URL, "PersonalToolbar", aProperty);
},
getPersist: function getPersist(aProperty)
setValue: function setValue(aProperty, aValue)
{
let property = this.RDF.GetResource(aProperty);
let target = this.store.GetTarget(this.toolbar, property, true);
if (target instanceof Ci.nsIRDFLiteral)
return target.Value;
return null;
},
setPersist: function setPersist(aProperty, aValue)
{
let property = this.RDF.GetResource(aProperty);
let value = aValue ? this.RDF.GetLiteral(aValue) : null;
try {
let oldTarget = this.store.GetTarget(this.toolbar, property, true);
if (oldTarget && value) {
this.store.Change(this.toolbar, property, oldTarget, value);
}
else if (value) {
this.store.Assert(this.toolbar, property, value, true);
}
else if (oldTarget) {
this.store.Unassert(this.toolbar, property, oldTarget);
}
else {
return;
}
if (aValue) {
this.xulStore.setValue(BROWSER_URL, "PersonalToolbar", aProperty, aValue);
} else {
this.xulStore.removeValue(BROWSER_URL, "PersonalToolbar", aProperty);
}
catch(ex) {
return;
}
this.store.QueryInterface(Ci.nsIRDFRemoteDataSource).Flush();
}
};
@ -69,17 +32,17 @@ let gTests = [
function test_explicitly_collapsed_toolbar()
{
info("An explicitly collapsed toolbar should not be uncollapsed.");
localStore.setPersist("collapsed", "true");
localStore.setValue("collapsed", "true");
bg.observe(null, "browser-glue-test", "force-ui-migration");
is(localStore.getPersist("collapsed"), "true", "Toolbar is collapsed");
is(localStore.getValue("collapsed"), "true", "Toolbar is collapsed");
},
function test_customized_toolbar()
{
info("A customized toolbar should be uncollapsed.");
localStore.setPersist("currentset", "splitter");
localStore.setValue("currentset", "splitter");
bg.observe(null, "browser-glue-test", "force-ui-migration");
is(localStore.getPersist("collapsed"), "false", "Toolbar has been uncollapsed");
is(localStore.getValue("collapsed"), "false", "Toolbar has been uncollapsed");
},
function test_many_bookmarks_toolbar()
@ -98,8 +61,12 @@ function test_many_bookmarks_toolbar()
PlacesUtils.bookmarks.insertSeparator(PlacesUtils.toolbarFolderId,
PlacesUtils.bookmarks.DEFAULT_INDEX)
);
ids.push(
PlacesUtils.bookmarks.insertSeparator(PlacesUtils.toolbarFolderId,
PlacesUtils.bookmarks.DEFAULT_INDEX)
);
bg.observe(null, "browser-glue-test", "force-ui-migration");
is(localStore.getPersist("collapsed"), "false", "Toolbar has been uncollapsed");
is(localStore.getValue("collapsed"), "false", "Toolbar has been uncollapsed");
},
];
@ -109,14 +76,14 @@ function test()
gOriginalMigrationVersion = Services.prefs.getIntPref("browser.migration.version");
registerCleanupFunction(clean);
if (localStore.getPersist("currentset") !== null) {
if (localStore.getValue("currentset") !== null) {
info("Toolbar currentset was persisted by a previous test, fixing it.");
localStore.setPersist("currentset", null);
localStore.setValue("currentset", null);
}
if (localStore.getPersist("collapsed") !== null) {
if (localStore.getValue("collapsed") !== null) {
info("Toolbar collapsed status was persisted by a previous test, fixing it.");
localStore.setPersist("collapsed", null);
localStore.setValue("collapsed", null);
}
while (gTests.length) {
@ -129,7 +96,7 @@ function test()
function clean()
{
Services.prefs.setIntPref("browser.migration.version", gOriginalMigrationVersion);
localStore.setPersist("currentset", null);
localStore.setPersist("collapsed", null);
localStore.setValue("currentset", null);
localStore.setValue("collapsed", null);
}

View File

@ -303,6 +303,7 @@
@BINPATH@/components/toolkit_finalizationwitness.xpt
@BINPATH@/components/toolkit_formautofill.xpt
@BINPATH@/components/toolkit_osfile.xpt
@BINPATH@/components/toolkit_xulstore.xpt
@BINPATH@/components/toolkitprofile.xpt
#ifdef MOZ_ENABLE_XREMOTE
@BINPATH@/components/toolkitremote.xpt
@ -503,6 +504,8 @@
@BINPATH@/components/cryptoComponents.manifest
@BINPATH@/components/TelemetryStartup.js
@BINPATH@/components/TelemetryStartup.manifest
@BINPATH@/components/XULStore.js
@BINPATH@/components/XULStore.manifest
@BINPATH@/components/messageWakeupService.js
@BINPATH@/components/messageWakeupService.manifest
@BINPATH@/components/SettingsManager.js

View File

@ -33,9 +33,6 @@
#include "nsViewManager.h"
#include "nsIContentViewer.h"
#include "nsIDOMXULElement.h"
#include "nsIRDFNode.h"
#include "nsIRDFRemoteDataSource.h"
#include "nsIRDFService.h"
#include "nsIStreamListener.h"
#include "nsITimer.h"
#include "nsDocShell.h"
@ -44,11 +41,10 @@
#include "nsXULContentSink.h"
#include "nsXULContentUtils.h"
#include "nsIXULOverlayProvider.h"
#include "nsIStringEnumerator.h"
#include "nsNetUtil.h"
#include "nsParserCIID.h"
#include "nsPIBoxObject.h"
#include "nsRDFCID.h"
#include "nsILocalStore.h"
#include "nsXPIDLString.h"
#include "nsPIDOMWindow.h"
#include "nsPIWindowRoot.h"
@ -137,11 +133,6 @@ const uint32_t kMaxAttributeLength = 4096;
int32_t XULDocument::gRefCnt = 0;
nsIRDFService* XULDocument::gRDFService;
nsIRDFResource* XULDocument::kNC_persist;
nsIRDFResource* XULDocument::kNC_attribute;
nsIRDFResource* XULDocument::kNC_value;
PRLogModuleInfo* XULDocument::gXULLog;
//----------------------------------------------------------------------
@ -230,26 +221,11 @@ XULDocument::~XULDocument()
PL_DHashTableDestroy(mBroadcasterMap);
}
if (mLocalStore) {
nsCOMPtr<nsIRDFRemoteDataSource> remote =
do_QueryInterface(mLocalStore);
if (remote)
remote->Flush();
}
delete mTemplateBuilderTable;
Preferences::UnregisterCallback(XULDocument::DirectionChanged,
"intl.uidirection.", this);
if (--gRefCnt == 0) {
NS_IF_RELEASE(gRDFService);
NS_IF_RELEASE(kNC_persist);
NS_IF_RELEASE(kNC_attribute);
NS_IF_RELEASE(kNC_value);
}
if (mOffThreadCompileStringBuf) {
js_free(mOffThreadCompileStringBuf);
}
@ -349,6 +325,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XULDocument, XMLDocument)
tmp->mTemplateBuilderTable = nullptr;
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCommandDispatcher)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocalStore)
//XXX We should probably unlink all the objects we traverse.
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
@ -1331,10 +1308,7 @@ XULDocument::Persist(const nsAString& aID,
nameSpaceID = kNameSpaceID_None;
}
rv = Persist(element, nameSpaceID, tag);
if (NS_FAILED(rv)) return rv;
return NS_OK;
return Persist(element, nameSpaceID, tag);
}
nsresult
@ -1345,102 +1319,39 @@ XULDocument::Persist(nsIContent* aElement, int32_t aNameSpaceID,
if (!nsContentUtils::IsSystemPrincipal(NodePrincipal()))
return NS_ERROR_NOT_AVAILABLE;
// First make sure we _have_ a local store to stuff the persisted
// information into. (We might not have one if profile information
// hasn't been loaded yet...)
if (!mLocalStore)
return NS_OK;
nsresult rv;
nsCOMPtr<nsIRDFResource> element;
rv = nsXULContentUtils::GetElementResource(aElement, getter_AddRefs(element));
if (NS_FAILED(rv)) return rv;
// No ID, so nothing to persist.
if (! element)
return NS_OK;
// Ick. Construct a property from the attribute. Punt on
// namespaces for now.
// Don't bother with unreasonable attributes. We clamp long values,
// but truncating attribute names turns it into a different attribute
// so there's no point in persisting anything at all
nsAtomCString attrstr(aAttribute);
if (attrstr.Length() > kMaxAttrNameLength) {
NS_WARNING("Can't persist, Attribute name too long");
return NS_ERROR_ILLEGAL_VALUE;
if (!mLocalStore) {
mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
if (NS_WARN_IF(!mLocalStore)) {
return NS_ERROR_NOT_INITIALIZED;
}
}
nsCOMPtr<nsIRDFResource> attr;
rv = gRDFService->GetResource(attrstr,
getter_AddRefs(attr));
if (NS_FAILED(rv)) return rv;
nsAutoString id;
aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id);
nsAtomString attrstr(aAttribute);
// Turn the value into a literal
nsAutoString valuestr;
aElement->GetAttr(kNameSpaceID_None, aAttribute, valuestr);
// prevent over-long attributes that choke the parser (bug 319846)
// (can't simply Truncate without testing, it's implemented
// using SetLength and will grow a short string)
if (valuestr.Length() > kMaxAttributeLength) {
NS_WARNING("Truncating persisted attribute value");
valuestr.Truncate(kMaxAttributeLength);
nsAutoCString utf8uri;
nsresult rv = mDocumentURI->GetSpec(utf8uri);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
NS_ConvertUTF8toUTF16 uri(utf8uri);
bool hasAttr;
rv = mLocalStore->HasValue(uri, id, attrstr, &hasAttr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
// See if there was an old value...
nsCOMPtr<nsIRDFNode> oldvalue;
rv = mLocalStore->GetTarget(element, attr, true, getter_AddRefs(oldvalue));
if (NS_FAILED(rv)) return rv;
if (oldvalue && valuestr.IsEmpty()) {
// ...there was an oldvalue, and they've removed it. XXXThis
// handling isn't quite right...
rv = mLocalStore->Unassert(element, attr, oldvalue);
if (hasAttr && valuestr.IsEmpty()) {
return mLocalStore->RemoveValue(uri, id, attrstr);
} else {
return mLocalStore->SetValue(uri, id, attrstr, valuestr);
}
else {
// Now either 'change' or 'assert' based on whether there was
// an old value.
nsCOMPtr<nsIRDFLiteral> newvalue;
rv = gRDFService->GetLiteral(valuestr.get(), getter_AddRefs(newvalue));
if (NS_FAILED(rv)) return rv;
if (oldvalue) {
if (oldvalue != newvalue)
rv = mLocalStore->Change(element, attr, oldvalue, newvalue);
else
rv = NS_OK;
}
else {
rv = mLocalStore->Assert(element, attr, newvalue, true);
}
}
if (NS_FAILED(rv)) return rv;
// Add it to the persisted set for this document (if it's not
// there already).
{
nsAutoCString docurl;
rv = mDocumentURI->GetSpec(docurl);
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIRDFResource> doc;
rv = gRDFService->GetResource(docurl, getter_AddRefs(doc));
if (NS_FAILED(rv)) return rv;
bool hasAssertion;
rv = mLocalStore->HasAssertion(doc, kNC_persist, element, true, &hasAssertion);
if (NS_FAILED(rv)) return rv;
if (! hasAssertion) {
rv = mLocalStore->Assert(doc, kNC_persist, element, true);
if (NS_FAILED(rv)) return rv;
}
}
return NS_OK;
}
@ -1993,25 +1904,7 @@ XULDocument::Init()
mCommandDispatcher = new nsXULCommandDispatcher(this);
NS_ENSURE_TRUE(mCommandDispatcher, NS_ERROR_OUT_OF_MEMORY);
// this _could_ fail; e.g., if we've tried to grab the local store
// before profiles have initialized. If so, no big deal; nothing
// will persist.
mLocalStore = do_GetService(NS_LOCALSTORE_CONTRACTID);
if (gRefCnt++ == 0) {
// Keep the RDF service cached in a member variable to make using
// it a bit less painful
rv = CallGetService("@mozilla.org/rdf/rdf-service;1", &gRDFService);
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to get RDF Service");
if (NS_FAILED(rv)) return rv;
gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "persist"),
&kNC_persist);
gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "attribute"),
&kNC_attribute);
gRDFService->GetResource(NS_LITERAL_CSTRING(NC_NAMESPACE_URI "value"),
&kNC_value);
// ensure that the XUL prototype cache is instantiated successfully,
// so that we can use nsXULPrototypeCache::GetInstance() without
// null-checks in the rest of the class.
@ -2175,8 +2068,12 @@ XULDocument::ApplyPersistentAttributes()
// Add all of the 'persisted' attributes into the content
// model.
if (!mLocalStore)
return NS_OK;
if (!mLocalStore) {
mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
if (NS_WARN_IF(!mLocalStore)) {
return NS_ERROR_NOT_INITIALIZED;
}
}
mApplyingPersistedAttrs = true;
ApplyPersistentAttributesInternal();
@ -2191,56 +2088,49 @@ XULDocument::ApplyPersistentAttributes()
}
nsresult
nsresult
XULDocument::ApplyPersistentAttributesInternal()
{
nsCOMArray<nsIContent> elements;
nsAutoCString docurl;
mDocumentURI->GetSpec(docurl);
nsAutoCString utf8uri;
nsresult rv = mDocumentURI->GetSpec(utf8uri);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
NS_ConvertUTF8toUTF16 uri(utf8uri);
nsCOMPtr<nsIRDFResource> doc;
gRDFService->GetResource(docurl, getter_AddRefs(doc));
nsCOMPtr<nsISimpleEnumerator> persisted;
mLocalStore->GetTargets(doc, kNC_persist, true, getter_AddRefs(persisted));
// Get a list of element IDs for which persisted values are available
nsCOMPtr<nsIStringEnumerator> ids;
rv = mLocalStore->GetIDsEnumerator(uri, getter_AddRefs(ids));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
while (1) {
bool hasmore = false;
persisted->HasMoreElements(&hasmore);
if (! hasmore)
ids->HasMore(&hasmore);
if (!hasmore) {
break;
}
nsCOMPtr<nsISupports> isupports;
persisted->GetNext(getter_AddRefs(isupports));
nsAutoString id;
ids->GetNext(id);
nsCOMPtr<nsIRDFResource> resource = do_QueryInterface(isupports);
if (! resource) {
NS_WARNING("expected element to be a resource");
if (mRestrictPersistence && !mPersistenceIds.Contains(id)) {
continue;
}
const char *uri;
resource->GetValueConst(&uri);
if (! uri)
continue;
nsAutoString id;
nsXULContentUtils::MakeElementID(this, nsDependentCString(uri), id);
if (id.IsEmpty())
continue;
if (mRestrictPersistence && !mPersistenceIds.Contains(id))
continue;
// This will clear the array if there are no elements.
GetElementsForID(id, elements);
if (!elements.Count())
if (!elements.Count()) {
continue;
}
ApplyPersistentAttributesToElements(resource, elements);
rv = ApplyPersistentAttributesToElements(id, elements);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
return NS_OK;
@ -2248,71 +2138,53 @@ XULDocument::ApplyPersistentAttributesInternal()
nsresult
XULDocument::ApplyPersistentAttributesToElements(nsIRDFResource* aResource,
XULDocument::ApplyPersistentAttributesToElements(const nsAString &aID,
nsCOMArray<nsIContent>& aElements)
{
nsresult rv;
nsAutoCString utf8uri;
nsresult rv = mDocumentURI->GetSpec(utf8uri);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
NS_ConvertUTF8toUTF16 uri(utf8uri);
nsCOMPtr<nsISimpleEnumerator> attrs;
rv = mLocalStore->ArcLabelsOut(aResource, getter_AddRefs(attrs));
if (NS_FAILED(rv)) return rv;
// Get a list of attributes for which persisted values are available
nsCOMPtr<nsIStringEnumerator> attrs;
rv = mLocalStore->GetAttributeEnumerator(uri, aID, getter_AddRefs(attrs));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
while (1) {
bool hasmore;
rv = attrs->HasMoreElements(&hasmore);
if (NS_FAILED(rv)) return rv;
if (! hasmore)
bool hasmore = PR_FALSE;
attrs->HasMore(&hasmore);
if (!hasmore) {
break;
nsCOMPtr<nsISupports> isupports;
rv = attrs->GetNext(getter_AddRefs(isupports));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIRDFResource> property = do_QueryInterface(isupports);
if (! property) {
NS_WARNING("expected a resource");
continue;
}
const char* attrname;
rv = property->GetValueConst(&attrname);
if (NS_FAILED(rv)) return rv;
nsAutoString attrstr;
attrs->GetNext(attrstr);
nsCOMPtr<nsIAtom> attr = do_GetAtom(attrname);
if (! attr)
nsAutoString value;
rv = mLocalStore->GetValue(uri, aID, attrstr, value);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCOMPtr<nsIAtom> attr = do_GetAtom(attrstr);
if (NS_WARN_IF(!attr)) {
return NS_ERROR_OUT_OF_MEMORY;
// XXX could hang namespace off here, as well...
nsCOMPtr<nsIRDFNode> node;
rv = mLocalStore->GetTarget(aResource, property, true,
getter_AddRefs(node));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIRDFLiteral> literal = do_QueryInterface(node);
if (! literal) {
NS_WARNING("expected a literal");
continue;
}
const char16_t* value;
rv = literal->GetValueConst(&value);
if (NS_FAILED(rv)) return rv;
nsDependentString wrapper(value);
uint32_t cnt = aElements.Count();
for (int32_t i = int32_t(cnt) - 1; i >= 0; --i) {
nsCOMPtr<nsIContent> element = aElements.SafeObjectAt(i);
if (!element)
continue;
if (!element) {
continue;
}
rv = element->SetAttr(/* XXX */ kNameSpaceID_None,
attr,
wrapper,
true);
rv = element->SetAttr(kNameSpaceID_None, attr, value, PR_TRUE);
}
}

View File

@ -22,6 +22,7 @@
#include "nsScriptLoader.h"
#include "nsIStreamListener.h"
#include "nsICSSLoaderObserver.h"
#include "nsIXULStore.h"
#include "mozilla/Attributes.h"
@ -259,7 +260,7 @@ protected:
nsresult ApplyPersistentAttributes();
nsresult ApplyPersistentAttributesInternal();
nsresult ApplyPersistentAttributesToElements(nsIRDFResource* aResource,
nsresult ApplyPersistentAttributesToElements(const nsAString &aID,
nsCOMArray<nsIContent>& aElements);
nsresult
@ -314,10 +315,10 @@ protected:
// Tracks elements with a 'ref' attribute, or an 'id' attribute where
// the element's namespace has no registered ID attribute name.
nsTHashtable<nsRefMapEntry> mRefMap;
nsCOMPtr<nsIRDFDataSource> mLocalStore;
bool mApplyingPersistedAttrs;
bool mIsWritingFastLoad;
bool mDocumentLoaded;
nsCOMPtr<nsIXULStore> mLocalStore;
bool mApplyingPersistedAttrs;
bool mIsWritingFastLoad;
bool mDocumentLoaded;
/**
* Since ResumeWalk is interruptible, it's possible that last
* stylesheet finishes loading while the PD walk is still in

View File

@ -186,36 +186,6 @@ nsXULContentUtils::FindChildByTag(nsIContent* aElement,
}
nsresult
nsXULContentUtils::GetElementResource(nsIContent* aElement, nsIRDFResource** aResult)
{
// Perform a reverse mapping from an element in the content model
// to an RDF resource.
nsresult rv;
char16_t buf[128];
nsFixedString id(buf, ArrayLength(buf), 0);
// Whoa. Why the "id" attribute? What if it's not even a XUL
// element? This is totally bogus!
aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id);
if (id.IsEmpty())
return NS_ERROR_FAILURE;
// Since the element will store its ID attribute as a document-relative value,
// we may need to qualify it first...
nsCOMPtr<nsIDocument> doc = aElement->GetDocument();
NS_ASSERTION(doc, "element is not in any document");
if (! doc)
return NS_ERROR_FAILURE;
rv = nsXULContentUtils::MakeElementResource(doc, id, aResult);
if (NS_FAILED(rv)) return rv;
return NS_OK;
}
/*
Note: this routine is similar, yet distinctly different from, nsBookmarksService::GetTextForNode
*/
@ -287,78 +257,6 @@ nsXULContentUtils::GetTextForNode(nsIRDFNode* aNode, nsAString& aResult)
return NS_ERROR_UNEXPECTED;
}
nsresult
nsXULContentUtils::MakeElementURI(nsIDocument* aDocument,
const nsAString& aElementID,
nsCString& aURI)
{
// Convert an element's ID to a URI that can be used to refer to
// the element in the XUL graph.
nsIURI *docURI = aDocument->GetDocumentURI();
NS_ENSURE_TRUE(docURI, NS_ERROR_UNEXPECTED);
nsRefPtr<nsIURI> docURIClone;
nsresult rv = docURI->Clone(getter_AddRefs(docURIClone));
NS_ENSURE_SUCCESS(rv, rv);
rv = docURIClone->SetRef(NS_ConvertUTF16toUTF8(aElementID));
if (NS_SUCCEEDED(rv)) {
return docURIClone->GetSpec(aURI);
}
// docURIClone is apparently immutable. Fine - we can append ref manually.
rv = docURI->GetSpec(aURI);
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString ref;
NS_EscapeURL(NS_ConvertUTF16toUTF8(aElementID), esc_FilePath | esc_AlwaysCopy, ref);
aURI.Append('#');
aURI.Append(ref);
return NS_OK;
}
nsresult
nsXULContentUtils::MakeElementResource(nsIDocument* aDocument, const nsAString& aID, nsIRDFResource** aResult)
{
nsresult rv;
char buf[256];
nsFixedCString uri(buf, sizeof(buf), 0);
rv = MakeElementURI(aDocument, aID, uri);
if (NS_FAILED(rv)) return rv;
rv = gRDF->GetResource(uri, aResult);
NS_ASSERTION(NS_SUCCEEDED(rv), "unable to create resource");
if (NS_FAILED(rv)) return rv;
return NS_OK;
}
nsresult
nsXULContentUtils::MakeElementID(nsIDocument* aDocument,
const nsACString& aURI,
nsAString& aElementID)
{
// Convert a URI into an element ID that can be accessed from the
// DOM APIs.
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), aURI,
aDocument->GetDocumentCharacterSet().get());
NS_ENSURE_SUCCESS(rv, rv);
nsAutoCString ref;
uri->GetRef(ref);
CopyUTF8toUTF16(ref, aElementID);
return NS_OK;
}
nsresult
nsXULContentUtils::GetResource(int32_t aNameSpaceID, nsIAtom* aAttribute, nsIRDFResource** aResult)
{

View File

@ -115,31 +115,9 @@ public:
nsIRDFResource* aResource,
nsIContent** aResult);
static nsresult
GetElementResource(nsIContent* aElement, nsIRDFResource** aResult);
static nsresult
GetTextForNode(nsIRDFNode* aNode, nsAString& aResult);
/**
* Construct a URI from the element ID given. This uses aElement as the
* ref and aDocument's document URI as the base. If aDocument's document
* URI does not support refs, this will throw NS_ERROR_NOT_AVAILABLE.
*/
static nsresult
MakeElementURI(nsIDocument* aDocument, const nsAString& aElementID, nsCString& aURI);
static nsresult
MakeElementResource(nsIDocument* aDocument, const nsAString& aElementID, nsIRDFResource** aResult);
/**
* Extract the element ID from aURI. Note that aURI must be an absolute
* URI string in UTF8; the element ID is the ref from the URI. If the
* scheme does not support refs, then the ID will be empty.
*/
static nsresult
MakeElementID(nsIDocument* aDocument, const nsACString& aURI, nsAString& aElementID);
static nsresult
GetResource(int32_t aNameSpaceID, nsIAtom* aAttribute, nsIRDFResource** aResult);

View File

@ -8,7 +8,6 @@
#include "nsIContent.h"
#include "mozilla/dom/NodeInfo.h"
#include "nsIDOMElement.h"
#include "nsILocalStore.h"
#include "nsIBoxObject.h"
#include "nsITreeBoxObject.h"
#include "nsITreeSelection.h"
@ -31,6 +30,7 @@
#include "nsDOMClassInfoID.h"
#include "nsWhitespaceTokenizer.h"
#include "nsTreeContentView.h"
#include "nsIXULStore.h"
// For security check
#include "nsIDocument.h"
@ -139,13 +139,10 @@ protected:
RemoveMatchesFor(nsTreeRows::Subtree& subtree);
/**
* Helper methods that determine if the specified container is open.
* Helper method that determines if the specified container is open.
*/
nsresult
IsContainerOpen(nsIXULTemplateResult *aResult, bool* aOpen);
nsresult
IsContainerOpen(nsIRDFResource* aResource, bool* aOpen);
bool
IsContainerOpen(nsIXULTemplateResult* aResource);
/**
* A sorting callback for NS_QuickSort().
@ -242,6 +239,11 @@ protected:
* The builder observers.
*/
nsCOMArray<nsIXULTreeBuilderObserver> mObservers;
/*
* XUL store for holding open container state
*/
nsCOMPtr<nsIXULStore> mLocalStore;
};
//----------------------------------------------------------------------
@ -278,6 +280,7 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder,
mBoxObject,
mSelection,
mPersistStateStore,
mLocalStore,
mObservers)
DOMCI_DATA(XULTreeBuilder, nsXULTreeBuilder)
@ -528,8 +531,7 @@ nsXULTreeBuilder::IsContainerOpen(int32_t aIndex, bool* aOpen)
nsTreeRows::iterator iter = mRows[aIndex];
if (iter->mContainerState == nsTreeRows::eContainerState_Unknown) {
bool isOpen;
IsContainerOpen(iter->mMatch->mResult, &isOpen);
bool isOpen = IsContainerOpen(iter->mMatch->mResult);
iter->mContainerState = isOpen
? nsTreeRows::eContainerState_Open
@ -757,42 +759,16 @@ nsXULTreeBuilder::SetTree(nsITreeBoxObject* aTree)
}
NS_ENSURE_TRUE(mRoot, NS_ERROR_NOT_INITIALIZED);
// Is our root's principal trusted?
// Only use the XUL store if the root's principal is trusted.
bool isTrusted = false;
nsresult rv = IsSystemPrincipal(mRoot->NodePrincipal(), &isTrusted);
if (NS_SUCCEEDED(rv) && isTrusted) {
// Get the datasource we intend to use to remember open state.
nsAutoString datasourceStr;
mRoot->GetAttr(kNameSpaceID_None, nsGkAtoms::statedatasource, datasourceStr);
// since we are trusted, use the user specified datasource
// if non specified, use localstore, which gives us
// persistence across sessions
if (! datasourceStr.IsEmpty()) {
gRDFService->GetDataSource(NS_ConvertUTF16toUTF8(datasourceStr).get(),
getter_AddRefs(mPersistStateStore));
}
else {
gRDFService->GetDataSource("rdf:local-store",
getter_AddRefs(mPersistStateStore));
mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
if(NS_WARN_IF(!mLocalStore)){
return NS_ERROR_NOT_INITIALIZED;
}
}
// Either no specific datasource was specified, or we failed
// to get one because we are not trusted.
//
// XXX if it were possible to ``write an arbitrary datasource
// back'', then we could also allow an untrusted document to
// use a statedatasource from the same codebase.
if (! mPersistStateStore) {
mPersistStateStore =
do_CreateInstance("@mozilla.org/rdf/datasource;1?name=in-memory-datasource");
}
NS_ASSERTION(mPersistStateStore, "failed to get a persistent state store");
if (! mPersistStateStore)
return NS_ERROR_FAILURE;
Rebuild();
EnsureSortVariables();
@ -830,34 +806,36 @@ nsXULTreeBuilder::ToggleOpenState(int32_t aIndex)
observer->OnToggleOpenState(aIndex);
}
if (mPersistStateStore) {
if (mLocalStore && mRoot) {
bool isOpen;
IsContainerOpen(aIndex, &isOpen);
nsCOMPtr<nsIRDFResource> container;
GetResourceFor(aIndex, getter_AddRefs(container));
if (! container)
nsIDocument* doc = mRoot->GetDocument();
if (!doc) {
return NS_ERROR_FAILURE;
}
bool hasProperty;
IsContainerOpen(container, &hasProperty);
nsIURI* docURI = doc->GetDocumentURI();
nsTreeRows::Row& row = *(mRows[aIndex]);
nsAutoString nodeid;
nsresult rv = row.mMatch->mResult->GetId(nodeid);
if (NS_FAILED(rv)) {
return rv;
}
nsAutoCString utf8uri;
rv = docURI->GetSpec(utf8uri);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
NS_ConvertUTF8toUTF16 uri(utf8uri);
if (isOpen) {
if (hasProperty) {
mPersistStateStore->Unassert(container,
nsXULContentUtils::NC_open,
nsXULContentUtils::true_);
}
mLocalStore->RemoveValue(uri, nodeid, NS_LITERAL_STRING("open"));
CloseContainer(aIndex);
}
else {
if (! hasProperty) {
mPersistStateStore->Assert(container,
nsXULContentUtils::NC_open,
nsXULContentUtils::true_,
true);
}
} else {
mLocalStore->SetValue(uri, nodeid, NS_LITERAL_STRING("open"),
NS_LITERAL_STRING("true"));
OpenContainer(aIndex, result);
}
@ -1225,10 +1203,9 @@ nsXULTreeBuilder::ReplaceMatch(nsIXULTemplateResult* aOldResult,
if (NS_FAILED(rv) || ! mayProcessChildren) return NS_OK;
}
bool open;
IsContainerOpen(result, &open);
if (open)
if (IsContainerOpen(result)) {
OpenContainer(iter.GetRowIndex(), result);
}
}
}
@ -1636,9 +1613,7 @@ nsXULTreeBuilder::OpenSubtreeForQuerySet(nsTreeRows::Subtree* aSubtree,
// If this is open, then remember it so we can recursively add
// *its* rows to the tree.
bool isOpen = false;
IsContainerOpen(nextresult, &isOpen);
if (isOpen) {
if (IsContainerOpen(nextresult)) {
if (open.AppendElement(count) == nullptr)
return NS_ERROR_OUT_OF_MEMORY;
}
@ -1722,36 +1697,42 @@ nsXULTreeBuilder::RemoveMatchesFor(nsTreeRows::Subtree& subtree)
return NS_OK;
}
nsresult
nsXULTreeBuilder::IsContainerOpen(nsIXULTemplateResult *aResult, bool* aOpen)
bool
nsXULTreeBuilder::IsContainerOpen(nsIXULTemplateResult *aResult)
{
// items are never open if recursion is disabled
if ((mFlags & eDontRecurse) && aResult != mRootResult) {
*aOpen = false;
return NS_OK;
}
// items are never open if recursion is disabled
if ((mFlags & eDontRecurse) && aResult != mRootResult) {
return false;
}
nsCOMPtr<nsIRDFResource> id;
nsresult rv = GetResultResource(aResult, getter_AddRefs(id));
if (NS_FAILED(rv))
return rv;
if (!mLocalStore) {
return false;
}
return IsContainerOpen(id, aOpen);
}
nsIDocument* doc = mRoot->GetDocument();
if (!doc) {
return false;
}
nsresult
nsXULTreeBuilder::IsContainerOpen(nsIRDFResource* aResource, bool* aOpen)
{
if (mPersistStateStore)
mPersistStateStore->HasAssertion(aResource,
nsXULContentUtils::NC_open,
nsXULContentUtils::true_,
true,
aOpen);
else
*aOpen = false;
nsIURI* docURI = doc->GetDocumentURI();
return NS_OK;
nsAutoString nodeid;
nsresult rv = aResult->GetId(nodeid);
if (NS_FAILED(rv)) {
return false;
}
nsAutoCString utf8uri;
rv = docURI->GetSpec(utf8uri);
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
NS_ConvertUTF8toUTF16 uri(utf8uri);
nsAutoString val;
mLocalStore->GetValue(uri, nodeid, NS_LITERAL_STRING("open"), val);
return val.EqualsLiteral("true");
}
int

View File

@ -249,6 +249,7 @@
@BINPATH@/components/toolkit_finalizationwitness.xpt
@BINPATH@/components/toolkit_formautofill.xpt
@BINPATH@/components/toolkit_osfile.xpt
@BINPATH@/components/toolkit_xulstore.xpt
@BINPATH@/components/toolkitprofile.xpt
#ifdef MOZ_ENABLE_XREMOTE
@BINPATH@/components/toolkitremote.xpt
@ -395,6 +396,8 @@
@BINPATH@/components/servicesComponents.manifest
@BINPATH@/components/TelemetryStartup.js
@BINPATH@/components/TelemetryStartup.manifest
@BINPATH@/components/XULStore.js
@BINPATH@/components/XULStore.manifest
@BINPATH@/components/Webapps.js
@BINPATH@/components/Webapps.manifest
@BINPATH@/components/AppsService.js

View File

@ -49,6 +49,7 @@ DIRS += [
'viewsource',
'workerloader',
'workerlz4',
'xulstore'
]
if CONFIG['MOZ_CRASHREPORTER']:

View File

@ -3584,7 +3584,7 @@ const int64_t UNDEFINED_URN_VALUE = -1;
// Create a urn (like
// urn:places-persist:place:group=0&group=1&sort=1&type=1,,%28local%20files%29)
// to be used to persist the open state of this container in localstore.rdf
// to be used to persist the open state of this container
nsresult
CreatePlacesPersistURN(nsNavHistoryQueryResultNode *aResultNode,
int64_t aValue, const nsCString& aTitle, nsCString& aURN)

View File

@ -0,0 +1,336 @@
/* 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/. */
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
const Cu = Components.utils;
// Enables logging and shorter save intervals.
const debugMode = false;
// 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_CONTRACTID = "@mozilla.org/xul/xulstore;1";
const XULSTORE_CID = Components.ID("{6f46b6f4-c8b1-4bd4-a4fa-9ebbed0753ea}");
const STOREDB_FILENAME = "xulstore.json";
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm");
function XULStore() {
this.init();
}
XULStore.prototype = {
classID: XULSTORE_CID,
classInfo: XPCOMUtils.generateCI({classID: XULSTORE_CID,
contractID: XULSTORE_CONTRACTID,
classDescription: "XULStore",
interfaces: [Ci.nsIXULStore]}),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsIXULStore,
Ci.nsISupportsWeakReference]),
_xpcom_factory: XPCOMUtils.generateSingletonFactory(XULStore),
/* ---------- 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" },
* },
*
* "chrome://foopy/b.xul" : { ... },
* ...
* }
*/
_data: {},
_storeFile: null,
_needsSaving: false,
_saveAllowed: true,
_writeTimer: Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer),
_writeTimerInitialized: false,
init: function () {
Services.obs.addObserver(this, "profile-before-change", true);
this._storeFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
this._storeFile.append(STOREDB_FILENAME);
if (!this._storeFile.exists()) {
this.import();
} else {
this.readFile();
}
},
observe: function(subject, topic, data) {
if (topic == "profile-before-change") {
this.writeFile();
this._saveAllowed = false;
}
},
/*
* Internal function for logging debug messages to the Error Console window
*/
log: function (message) {
if (!debugMode)
return;
dump("XULStore: " + message + "\n");
Services.console.logStringMessage("XULStore: " + message);
},
import: function() {
let localStoreFile = Services.dirsvc.get("ProfD", Ci.nsIFile);
localStoreFile.append("localstore.rdf");
if (!localStoreFile.exists()) {
return;
}
const RDF = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService);
const persistKey = RDF.GetResource("http://home.netscape.com/NC-rdf#persist");
this.log("Import localstore from " + localStoreFile.path);
let localStoreURI = Services.io.newFileURI(localStoreFile).spec;
let localStore = RDF.GetDataSourceBlocking(localStoreURI);
let resources = localStore.GetAllResources();
while (resources.hasMoreElements()) {
let resource = resources.getNext().QueryInterface(Ci.nsIRDFResource);
let uri;
try {
uri = NetUtil.newURI(resource.ValueUTF8);
} catch(ex) {
continue; // skip invalid uris
}
// If this has a ref, then this is an attribute reference. Otherwise,
// this is a document reference.
if (!uri.hasRef)
continue;
// Verify that there the persist key is connected up.
let docURI = uri.specIgnoringRef;
if (!localStore.HasAssertion(RDF.GetResource(docURI), persistKey, resource, true))
continue;
let id = uri.ref;
let attrs = localStore.ArcLabelsOut(resource);
while (attrs.hasMoreElements()) {
let attr = attrs.getNext().QueryInterface(Ci.nsIRDFResource);
let value = localStore.GetTarget(resource, attr, true);
if (value instanceof Ci.nsIRDFLiteral) {
this.setValue(docURI, id, attr.ValueUTF8, value.Value);
}
}
}
},
readFile: function() {
const MODE_RDONLY = 0x01;
const FILE_PERMS = 0600;
let stream = Cc["@mozilla.org/network/file-input-stream;1"].
createInstance(Ci.nsIFileInputStream);
let json = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON);
try {
stream.init(this._storeFile, MODE_RDONLY, FILE_PERMS, 0);
this._data = json.decodeFromStream(stream, stream.available());
} catch(e) {
this.log("Error reading JSON: " + e);
// Ignore problem, we'll just continue on with an empty dataset.
} finally {
stream.close();
}
},
writeFile: Task.async(function* () {
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);
yield 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: function() {
this._needsSaving = true;
if (this._writeTimerInitialized)
return;
let callback = () => {
this._writeTimerInitialized = false;
this.writeFile();
};
// Don't write the file more than once every 30 seconds.
this._writeTimerInitialized = true;
this._writeTimer.initWithCallback(callback, WRITE_DELAY_MS, Ci.nsITimer.TYPE_ONE_SHOT);
},
/* ---------- interface implementation ---------- */
setValue: function (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 > 1024 || attr.length > 1024 || value.length > 1024)
throw Components.Exception("id, attribute, or value too long", Cr.NS_ERROR_ILLEGAL_VALUE);
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: function (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: function (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: function (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();
}
}
},
getIDsEnumerator: function (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: function (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);
}
};
function nsStringEnumerator(items) {
this._items = items;
}
nsStringEnumerator.prototype = {
QueryInterface : XPCOMUtils.generateQI([Ci.nsIStringEnumerator]),
_nextIndex : 0,
hasMore: function() {
return this._nextIndex < this._items.length;
},
getNext : function() {
if (!this.hasMore())
throw Cr.NS_ERROR_NOT_AVAILABLE;
return this._items[this._nextIndex++];
},
};
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([XULStore]);

View File

@ -0,0 +1,2 @@
component {6f46b6f4-c8b1-4bd4-a4fa-9ebbed0753ea} XULStore.js
contract @mozilla.org/xul/xulstore;1 {6f46b6f4-c8b1-4bd4-a4fa-9ebbed0753ea}

View File

@ -0,0 +1,18 @@
# -*- Mode: python; c-basic-offset: 4; 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/.
TEST_DIRS += ['tests']
XPIDL_SOURCES += [
'nsIXULStore.idl',
]
XPIDL_MODULE = 'toolkit_xulstore'
EXTRA_COMPONENTS += [
'XULStore.js',
'XULStore.manifest',
]

View File

@ -0,0 +1,75 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
interface nsIStringEnumerator;
/**
* 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.
*
* The data is serialized to [profile directory]/xulstore.json
*/
[scriptable, uuid(987c4b35-c426-4dd7-ad49-3c9fa4c65d20)]
interface nsIXULStore: nsISupports
{
/**
* Sets a value in the store.
*
* @param doc - document URI
* @param id - identifier of the node
* @param attr - attribute to store
* @param value - value of the attribute
*/
void setValue(in AString doc, in AString id, in AString attr, in AString value);
/**
* Returns true if the store contains a value for attr.
*
* @param doc - URI of the document
* @param id - identifier of the node
* @param attr - attribute
*/
bool hasValue(in AString doc, in AString id, in AString attr);
/**
* Retrieves a value in the store, or an empty string if it does not exist.
*
* @param doc - document URI
* @param id - identifier of the node
* @param attr - attribute to retrieve
*
* @returns the value of the attribute
*/
AString getValue(in AString doc, in AString id, in AString attr);
/**
* Removes a value in the store.
*
* @param doc - document URI
* @param id - identifier of the node
* @param attr - attribute to remove
*/
void removeValue(in AString doc, in AString id, in AString attr);
/**
* Iterates over all of the ids associated with a given document uri that
* have stored data.
*
* @param doc - document URI
*/
nsIStringEnumerator getIDsEnumerator(in AString doc);
/**
* Iterates over all of the attributes associated with a given document uri
* and id that have stored data.
*
* @param doc - document URI
* @param id - identifier of the node
*/
nsIStringEnumerator getAttributeEnumerator(in AString doc, in AString id);
};

View File

@ -0,0 +1,142 @@
<?xml version="1.0"?>
<RDF:RDF xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:NC="http://home.netscape.com/NC-rdf#"
xmlns:ANIMALS="http://www.some-fictitious-zoo.com/rdf#">
<ANIMALS:Class RDF:about="http://www.some-fictitious-zoo.com/arachnids">
<ANIMALS:name>Arachnids</ANIMALS:name>
</ANIMALS:Class>
<RDF:Description RDF:about="http://www.some-fictitious-zoo.com/arachnids/tarantula">
<ANIMALS:name>Tarantula</ANIMALS:name>
</RDF:Description>
<ANIMALS:Class RDF:about="http://www.some-fictitious-zoo.com/birds">
<ANIMALS:name>Birds</ANIMALS:name>
</ANIMALS:Class>
<RDF:Description RDF:about="http://www.some-fictitious-zoo.com/birds/emu">
<ANIMALS:name>Emu</ANIMALS:name>
</RDF:Description>
<RDF:Description RDF:about="http://www.some-fictitious-zoo.com/birds/barnowl">
<ANIMALS:name>Barn Owl</ANIMALS:name>
</RDF:Description>
<RDF:Description RDF:about="http://www.some-fictitious-zoo.com/birds/raven">
<ANIMALS:name>Raven</ANIMALS:name>
</RDF:Description>
<ANIMALS:Class RDF:about="http://www.some-fictitious-zoo.com/crustaceans">
<ANIMALS:name>Crustaceans</ANIMALS:name>
</ANIMALS:Class>
<ANIMALS:Class RDF:about="http://www.some-fictitious-zoo.com/fish">
<ANIMALS:name>Fish</ANIMALS:name>
</ANIMALS:Class>
<RDF:Description RDF:about="http://www.some-fictitious-zoo.com/fish/cod">
<ANIMALS:name>Cod</ANIMALS:name>
</RDF:Description>
<RDF:Description RDF:about="http://www.some-fictitious-zoo.com/fish/swordfish">
<ANIMALS:name>Swordfish</ANIMALS:name>
</RDF:Description>
<ANIMALS:Class RDF:about="http://www.some-fictitious-zoo.com/mammals">
<ANIMALS:name>Mammals</ANIMALS:name>
</ANIMALS:Class>
<RDF:Description RDF:about="http://www.some-fictitious-zoo.com/mammals/lion">
<ANIMALS:name>Lion</ANIMALS:name>
</RDF:Description>
<RDF:Description RDF:about="http://www.some-fictitious-zoo.com/mammals/hippopotamus">
<ANIMALS:name>HIPPOPOTAMUS</ANIMALS:name>
</RDF:Description>
<RDF:Description RDF:about="http://www.some-fictitious-zoo.com/mammals/africanelephant">
<ANIMALS:name>African Elephant</ANIMALS:name>
</RDF:Description>
<RDF:Description RDF:about="http://www.some-fictitious-zoo.com/mammals/llama">
<ANIMALS:name>LLAMA</ANIMALS:name>
</RDF:Description>
<RDF:Description RDF:about="http://www.some-fictitious-zoo.com/mammals/polarbear">
<ANIMALS:name>Polar Bear</ANIMALS:name>
</RDF:Description>
<RDF:Description RDF:about="http://www.some-fictitious-zoo.com/mammals/aardvark">
<ANIMALS:name>aardvark</ANIMALS:name>
</RDF:Description>
<RDF:Description RDF:about="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo">
<ANIMALS:name>Nine-banded Armadillo</ANIMALS:name>
</RDF:Description>
<RDF:Description RDF:about="http://www.some-fictitious-zoo.com/mammals/gorilla">
<ANIMALS:name>Gorilla</ANIMALS:name>
</RDF:Description>
<ANIMALS:Class RDF:about="http://www.some-fictitious-zoo.com/reptiles">
<ANIMALS:name>Reptiles</ANIMALS:name>
</ANIMALS:Class>
<RDF:Description RDF:about="http://www.some-fictitious-zoo.com/reptiles/anaconda">
<ANIMALS:name>Anaconda</ANIMALS:name>
</RDF:Description>
<RDF:Description RDF:about="http://www.some-fictitious-zoo.com/reptiles/chameleon">
<ANIMALS:name>Chameleon</ANIMALS:name>
</RDF:Description>
<RDF:Seq RDF:about="http://www.some-fictitious-zoo.com/some-animals" ANIMALS:name="Zoo Animals">
<RDF:li RDF:resource="http://www.some-fictitious-zoo.com/arachnids"/>
<RDF:li RDF:resource="http://www.some-fictitious-zoo.com/birds"/>
</RDF:Seq>
<RDF:Seq RDF:about="http://www.some-fictitious-zoo.com/all-animals" ANIMALS:name="Zoo Animals">
<RDF:li>
<RDF:Seq RDF:about="http://www.some-fictitious-zoo.com/arachnids">
<RDF:li RDF:resource="http://www.some-fictitious-zoo.com/arachnids/tarantula"/>
</RDF:Seq>
</RDF:li>
<RDF:li>
<RDF:Seq RDF:about="http://www.some-fictitious-zoo.com/birds">
<RDF:li RDF:resource="http://www.some-fictitious-zoo.com/birds/emu"/>
<RDF:li RDF:resource="http://www.some-fictitious-zoo.com/birds/barnowl"/>
<RDF:li RDF:resource="http://www.some-fictitious-zoo.com/birds/raven"/>
</RDF:Seq>
</RDF:li>
<RDF:li>
<RDF:Seq RDF:about="http://www.some-fictitious-zoo.com/crustaceans"/>
</RDF:li>
<RDF:li>
<RDF:Seq RDF:about="http://www.some-fictitious-zoo.com/fish">
<RDF:li RDF:resource="http://www.some-fictitious-zoo.com/fish/cod"/>
<RDF:li RDF:resource="http://www.some-fictitious-zoo.com/fish/swordfish"/>
</RDF:Seq>
</RDF:li>
<RDF:li>
<RDF:Seq RDF:about="http://www.some-fictitious-zoo.com/mammals">
<RDF:li RDF:resource="http://www.some-fictitious-zoo.com/mammals/lion"/>
<RDF:li RDF:resource="http://www.some-fictitious-zoo.com/mammals/hippopotamus"/>
<RDF:li RDF:resource="http://www.some-fictitious-zoo.com/mammals/africanelephant"/>
<RDF:li RDF:resource="http://www.some-fictitious-zoo.com/mammals/llama"/>
<RDF:li RDF:resource="http://www.some-fictitious-zoo.com/mammals/polarbear"/>
<RDF:li RDF:resource="http://www.some-fictitious-zoo.com/mammals/aardvark"/>
<RDF:li RDF:resource="http://www.some-fictitious-zoo.com/mammals/ninebandedarmadillo"/>
<RDF:li RDF:resource="http://www.some-fictitious-zoo.com/mammals/gorilla"/>
</RDF:Seq>
</RDF:li>
<RDF:li>
<RDF:Seq RDF:about="http://www.some-fictitious-zoo.com/reptiles">
<RDF:li RDF:resource="http://www.some-fictitious-zoo.com/reptiles/anaconda"/>
<RDF:li RDF:resource="http://www.some-fictitious-zoo.com/reptiles/chameleon"/>
</RDF:Seq>
</RDF:li>
</RDF:Seq>
</RDF:RDF>

View File

@ -0,0 +1,6 @@
[DEFAULT]
support-files =
window_persistence.xul
animals.rdf
[test_persistence.xul]

View File

@ -0,0 +1,31 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
<window title="Persistence Tests"
onload="runTest()"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
<script>
SimpleTest.waitForExplicitFinish();
function runTest() {
window.openDialog("window_persistence.xul", "_blank", "chrome", true);
}
function windowOpened() {
window.openDialog("window_persistence.xul", "_blank", "chrome", false);
}
function testDone() {
SimpleTest.finish();
}
</script>
<body xmlns="http://www.w3.org/1999/xhtml">
<p id="display"/>
</body>
</window>

View File

@ -0,0 +1,98 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
<window title="Persistence Tests"
onload="opened()"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
persist="screenX screenY width height">
<button id="button1" label="Button1" persist="value"/>
<button id="button2" label="Button2" value="Normal" persist="value"/>
<tree id="tree" datasources="animals.rdf" ref="http://www.some-fictitious-zoo.com/all-animals"
flags="dont-build-content" width="200" height="200">
<treecols orient="horizontal" id="treecols">
<treecol id="treecol" primary="true" label="Name" flex="1"/>
</treecols>
<template id="template">
<treechildren>
<treeitem uri="rdf:*">
<treerow>
<treecell label="rdf:http://www.some-fictitious-zoo.com/rdf#name"/>
<treecell/>
</treerow>
</treeitem>
</treechildren>
</template>
</tree>
<script>
<![CDATA[
const Cc = Components.classes;
const Ci = Components.interfaces;
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()
{
// If the data in the tree has not been loaded yet, wait a bit and try again.
var treeView = document.getElementById("tree").view;
if (treeView.rowCount != 6 && treeView.rowCount != 17) {
setTimeout(opened, 50);
return;
}
runTest(treeView);
}
function runTest(treeView)
{
var firstRun = window.arguments[0];
if (firstRun) {
document.getElementById("button1").setAttribute("value", "Pressed");
document.getElementById("button2").removeAttribute("value");
document.getElementById("button2").setAttribute("foo", "bar");
document.persist("button2", "foo");
is(XULStore.getValue(URI, "button2", "foo"), "bar", "attribute persisted")
document.getElementById("button2").removeAttribute("foo");
document.persist("button2", "foo");
is(XULStore.hasValue(URI, "button2", "foo"), false, "attribute removed")
is(treeView.rowCount, 6, "tree rows are closed");
treeView.toggleOpenState(1);
treeView.toggleOpenState(7);
window.close();
window.opener.windowOpened();
}
else {
is(document.getElementById("button1").getAttribute("value"), "Pressed",
"Attribute set");
is(document.getElementById("button2").hasAttribute("value"), true,
"Attribute cleared");
is(document.getElementById("button2").getAttribute("value"), "",
"Attribute cleared");
is(document.getElementById("button2").hasAttribute("foo"), false,
"Attribute cleared");
is(document.getElementById("button2").getAttribute("foo"), "",
"Attribute cleared");
is(treeView.rowCount, 17, "tree rows are open");
is(treeView.isContainerOpen(0), false, "first closed row");
is(treeView.isContainerOpen(1), true, "first open row");
is(treeView.isContainerOpen(7), true, "second open row");
window.close();
window.opener.testDone();
}
}
function is(l, r, n) { window.opener.wrappedJSObject.SimpleTest.is(l,r,n); }
]]></script>
</window>

View File

@ -0,0 +1,6 @@
# 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/.
MOCHITEST_CHROME_MANIFESTS += ['chrome/chrome.ini']
XPCSHELL_TESTS_MANIFESTS += ['xpcshell/xpcshell.ini']

View File

@ -0,0 +1,31 @@
<?xml version="1.0"?>
<RDF:RDF xmlns:NC="http://home.netscape.com/NC-rdf#"
xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
<RDF:Description RDF:about="chrome://browser/content/browser.xul#sidebar-title"
value="" />
<RDF:Description RDF:about="about:config#prefCol"
ordinal="1"
sortDirection="ascending" />
<RDF:Description RDF:about="chrome://browser/content/browser.xul#addon-bar"
collapsed="true" />
<RDF:Description RDF:about="about:config">
<NC:persist RDF:resource="about:config#prefCol"/>
<NC:persist RDF:resource="about:config#lockCol"/>
<NC:persist RDF:resource="about:config#typeCol"/>
<NC:persist RDF:resource="about:config#valueCol"/>
</RDF:Description>
<RDF:Description RDF:about="about:config#lockCol"
ordinal="3" />
<RDF:Description RDF:about="chrome://browser/content/browser.xul">
<NC:persist RDF:resource="chrome://browser/content/browser.xul#main-window"/>
<NC:persist RDF:resource="chrome://browser/content/browser.xul#addon-bar"/>
<NC:persist RDF:resource="chrome://browser/content/browser.xul#sidebar-box"/>
<NC:persist RDF:resource="chrome://browser/content/browser.xul#sidebar-title"/>
</RDF:Description>
<RDF:Description RDF:about="chrome://browser/content/browser.xul#main-window"
width="994"
height="768"
screenX="4"
screenY="22"
sizemode="normal" />
</RDF:RDF>

View File

@ -0,0 +1,184 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/◦
*/
"use strict"
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const Cr = Components.results;
Cu.import("resource://gre/modules/osfile.jsm")
let XULStore = null;
let browserURI = "chrome://browser/content/browser.xul";
let aboutURI = "about:config";
function run_test() {
do_get_profile();
run_next_test();
}
function checkValue(uri, id, attr, reference) {
let value = XULStore.getValue(uri, id, attr);
do_check_true(value === reference);
}
function checkValueExists(uri, id, attr, exists) {
do_check_eq(XULStore.hasValue(uri, id, attr), exists);
}
function getIDs(uri) {
let it = XULStore.getIDsEnumerator(uri);
let result = [];
while (it.hasMore()) {
let value = it.getNext();
result.push(value);
}
result.sort();
return result;
}
function getAttributes(uri, id) {
let it = XULStore.getAttributeEnumerator(uri, id);
let result = [];
while (it.hasMore()) {
let value = it.getNext();
result.push(value);
}
result.sort();
return result;
}
function checkArrays(a, b) {
a.sort();
b.sort();
do_check_true(a.toString() == b.toString());
}
function checkOldStore() {
checkArrays(['addon-bar', 'main-window', 'sidebar-title'], getIDs(browserURI));
checkArrays(['collapsed'], getAttributes(browserURI, 'addon-bar'));
checkArrays(['height', 'screenX', 'screenY', 'sizemode', 'width'],
getAttributes(browserURI, 'main-window'));
checkArrays(['value'], getAttributes(browserURI, 'sidebar-title'));
checkValue(browserURI, "addon-bar", "collapsed", "true");
checkValue(browserURI, "main-window", "width", "994");
checkValue(browserURI, "main-window", "height", "768");
checkValue(browserURI, "main-window", "screenX", "4");
checkValue(browserURI, "main-window", "screenY", "22");
checkValue(browserURI, "main-window", "sizemode", "normal");
checkValue(browserURI, "sidebar-title", "value", "");
checkArrays(['lockCol', 'prefCol'], getIDs(aboutURI));
checkArrays(['ordinal'], getAttributes(aboutURI, 'lockCol'));
checkArrays(['ordinal', 'sortDirection'], getAttributes(aboutURI, 'prefCol'));
checkValue(aboutURI, "prefCol", "ordinal", "1");
checkValue(aboutURI, "prefCol", "sortDirection", "ascending");
checkValue(aboutURI, "lockCol", "ordinal", "3");
}
add_task(function* testImport(){
let src = "localstore.rdf";
let dst = OS.Path.join(OS.Constants.Path.profileDir, src);
yield OS.File.copy(src, dst);
// Importing relies on XULStore not yet being loaded before this point.
XULStore = Cc["@mozilla.org/xul/xulstore;1"].getService(Ci.nsIXULStore);
checkOldStore();
});
add_task(function* testGetValue() {
// Get non-existing property
checkValue(browserURI, "side-window", "height", "");
// Get existing property
checkValue(browserURI, "main-window", "width", "994");
});
add_task(function* testHasValue() {
// Check non-existing property
checkValueExists(browserURI, "side-window", "height", false);
// Check existing property
checkValueExists(browserURI, "main-window", "width", true);
});
add_task(function* testSetValue() {
// Set new attribute
checkValue(browserURI, "side-bar", "width", "");
XULStore.setValue(browserURI, "side-bar", "width", "1000");
checkValue(browserURI, "side-bar", "width", "1000");
checkArrays(["addon-bar", "main-window", "side-bar", "sidebar-title"], getIDs(browserURI));
checkArrays(["width"], getAttributes(browserURI, 'side-bar'));
// Modify existing property
checkValue(browserURI, "side-bar", "width", "1000");
XULStore.setValue(browserURI, "side-bar", "width", "1024");
checkValue(browserURI, "side-bar", "width", "1024");
checkArrays(["addon-bar", "main-window", "side-bar", "sidebar-title"], getIDs(browserURI));
checkArrays(["width"], getAttributes(browserURI, 'side-bar'));
// Add another attribute
checkValue(browserURI, "side-bar", "height", "");
XULStore.setValue(browserURI, "side-bar", "height", "1000");
checkValue(browserURI, "side-bar", "height", "1000");
checkArrays(["addon-bar", "main-window", "side-bar", "sidebar-title"], getIDs(browserURI));
checkArrays(["width", "height"], getAttributes(browserURI, 'side-bar'));
});
add_task(function* testRemoveValue() {
// Remove first attribute
checkValue(browserURI, "side-bar", "width", "1024");
XULStore.removeValue(browserURI, "side-bar", "width");
checkValue(browserURI, "side-bar", "width", "");
checkValueExists(browserURI, "side-bar", "width", false);
checkArrays(["addon-bar", "main-window", "side-bar", "sidebar-title"], getIDs(browserURI));
checkArrays(["height"], getAttributes(browserURI, 'side-bar'));
// Remove second attribute
checkValue(browserURI, "side-bar", "height", "1000");
XULStore.removeValue(browserURI, "side-bar", "height");
checkValue(browserURI, "side-bar", "height", "");
checkArrays(["addon-bar", "main-window", "sidebar-title"], getIDs(browserURI));
// Removing an attribute that doesn't exists shouldn't fail
XULStore.removeValue(browserURI, "main-window", "bar");
// Removing from an id that doesn't exists shouldn't fail
XULStore.removeValue(browserURI, "foo", "bar");
// Removing from a document that doesn't exists shouldn't fail
let nonDocURI = "chrome://example/content/other.xul";
XULStore.removeValue(nonDocURI, "foo", "bar");
// Remove all attributes in browserURI
XULStore.removeValue(browserURI, "addon-bar", "collapsed");
checkArrays([], getAttributes(browserURI, "addon-bar"));
XULStore.removeValue(browserURI, "main-window", "width");
XULStore.removeValue(browserURI, "main-window", "height");
XULStore.removeValue(browserURI, "main-window", "screenX");
XULStore.removeValue(browserURI, "main-window", "screenY");
XULStore.removeValue(browserURI, "main-window", "sizemode");
checkArrays([], getAttributes(browserURI, "main-window"));
XULStore.removeValue(browserURI, "sidebar-title", "value");
checkArrays([], getAttributes(browserURI, "sidebar-title"));
checkArrays([], getIDs(browserURI));
// Remove all attributes in aboutURI
XULStore.removeValue(aboutURI, "prefCol", "ordinal");
XULStore.removeValue(aboutURI, "prefCol", "sortDirection");
checkArrays([], getAttributes(aboutURI, "prefCol"));
XULStore.removeValue(aboutURI, "lockCol", "ordinal");
checkArrays([], getAttributes(aboutURI, "lockCol"));
checkArrays([], getIDs(aboutURI));
});

View File

@ -0,0 +1,6 @@
[DEFAULT]
support-files =
localstore.rdf
[test_XULStore.js]

View File

@ -476,17 +476,8 @@ add_test(function() {
// Before we open the add-ons manager, we should make sure that no filter
// has been set. If one is set, we remove it.
// This is for the check below, from bug 611459.
let RDF = Cc["@mozilla.org/rdf/rdf-service;1"].getService(Ci.nsIRDFService);
let store = RDF.GetDataSource("rdf:local-store");
let filterResource = RDF.GetResource("about:addons#search-filter-radiogroup");
let filterProperty = RDF.GetResource("value");
let filterTarget = store.GetTarget(filterResource, filterProperty, true);
if (filterTarget) {
is(filterTarget instanceof Ci.nsIRDFLiteral, true,
"Filter should be a value");
store.Unassert(filterResource, filterProperty, filterTarget);
}
let store = Cc["@mozilla.org/xul/xulstore;1"].getService(Ci.nsIXULStore);
store.removeValue("about:addons", "search-filter-radiogroup", "value");
open_manager("addons://list/extension", function(aManager) {
info("Part 1");