Skip to content

Instantly share code, notes, and snippets.

@vitillo
Last active August 29, 2015 14:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vitillo/e2c13df2edbf07482192 to your computer and use it in GitHub Desktop.
Save vitillo/e2c13df2edbf07482192 to your computer and use it in GitHub Desktop.
# HG changeset patch
# Parent a4dcfbebcb588b9893900152eb512c3810880d68
# User Roberto A. Vitillo <rvitillo@mozilla.com>
Bug 559505 - localstore.rdf kills ponies; r=enn
diff --git a/b2g/installer/package-manifest.in b/b2g/installer/package-manifest.in
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -290,16 +290,17 @@
@BINPATH@/components/shellservice.xpt
@BINPATH@/components/shistory.xpt
@BINPATH@/components/spellchecker.xpt
@BINPATH@/components/storage.xpt
@BINPATH@/components/telemetry.xpt
@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
#endif
@BINPATH@/components/txtsvc.xpt
@BINPATH@/components/txmgr.xpt
#ifdef MOZ_USE_NATIVE_UCONV
@BINPATH@/components/ucnative.xpt
@@ -529,16 +530,18 @@
@BINPATH@/components/HealthReportService.js
#endif
#ifdef MOZ_CAPTIVEDETECT
@BINPATH@/components/CaptivePortalDetectComponents.manifest
@BINPATH@/components/captivedetect.js
#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
@BINPATH@/components/AppsService.manifest
@BINPATH@/components/Push.js
@BINPATH@/components/Push.manifest
@BINPATH@/components/PushServiceLauncher.js
diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -1339,118 +1339,107 @@ BrowserGlue.prototype = {
var notification = notifyBox.appendNotification(text, title, null,
notifyBox.PRIORITY_CRITICAL_MEDIUM,
buttons);
notification.persistence = -1; // Until user closes it
},
_migrateUI: function BG__migrateUI() {
const UI_VERSION = 22;
- 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");
} catch(ex) {}
- if (currentUIVersion >= UI_VERSION)
+ 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 &&
currentset.indexOf("stop-button") != -1 &&
currentset.indexOf("urlbar-container") != -1 &&
currentset.indexOf("urlbar-container,reload-button,stop-button") == -1) {
currentset = currentset.replace(/(^|,)reload-button($|,)/, "$1$2")
.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 &&
currentset.indexOf("bookmarks-menu-button-container") != -1) {
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");
function getToolbarFolderCount() {
let toolbarFolder =
PlacesUtils.getFolderContents(PlacesUtils.toolbarFolderId).root;
let toolbarChildCount = toolbarFolder.childCount;
toolbarFolder.containerOpen = false;
return toolbarChildCount;
}
if (toolbarIsCustomized || getToolbarFolderCount() > 3) {
- this._setPersist(toolbarResource, collapsedResource, "false");
+ xulStore.setValue(BROWSER_DOCURL, "PersonalToolbar", "collapsed", "false");
}
}
}
if (currentUIVersion < 8) {
// Reset homepage pref for users who have it set to google.com/firefox
let uri = Services.prefs.getComplexValue("browser.startup.homepage",
Ci.nsIPrefLocalizedString).data;
if (uri && /^https?:\/\/(www\.)?google(\.\w{2,3}){1,2}\/firefox\/?$/.test(uri)) {
Services.prefs.clearUserPref("browser.startup.homepage");
}
}
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
// previous UI version, and the button was not already placed on the
// toolbar manually.
if (currentset &&
currentset.indexOf("downloads-button") == -1) {
// The element is added either after the search bar or before the home
@@ -1461,17 +1450,17 @@ BrowserGlue.prototype = {
"$1search-container,downloads-button$2")
} else if (currentset.indexOf("home-button") != -1) {
currentset = currentset.replace(/(^|,)home-button($|,)/,
"$1downloads-button,home-button$2")
} else {
currentset = currentset.replace(/(^|,)window-controls($|,)/,
"$1downloads-button,window-controls$2")
}
- this._setPersist(toolbarResource, currentsetResource, currentset);
+ xulStore.setValue(BROWSER_DOCURL, "nav-bar", "currentset", currentset);
}
}
#ifdef XP_WIN
if (currentUIVersion < 10) {
// For Windows systems with display set to > 96dpi (i.e. systemDefaultScale
// will return a value > 1.0), we want to discard any saved full-zoom settings,
// as we'll now be scaling the content according to the system resolution
@@ -1491,25 +1480,23 @@ BrowserGlue.prototype = {
Services.prefs.clearUserPref("dom.event.contextmenu.enabled");
Services.prefs.clearUserPref("javascript.enabled");
Services.prefs.clearUserPref("permissions.default.image");
}
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);
}
}
}
if (currentUIVersion < 13) {
try {
if (Services.prefs.getBoolPref("plugins.hide_infobar_for_missing_plugin"))
Services.prefs.setBoolPref("plugins.notifyMissingFlash", false);
@@ -1520,62 +1507,54 @@ BrowserGlue.prototype = {
if (currentUIVersion < 14) {
// DOM Storage doesn't specially handle about: pages anymore.
let path = OS.Path.join(OS.Constants.Path.profileDir,
"chromeappsstore.sqlite");
OS.File.remove(path);
}
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")) {
// The button isn't in the nav-bar, so let's look for an appropriate
// place to put it.
if (currentset.contains("downloads-button")) {
currentset = currentset.replace(/(^|,)downloads-button($|,)/,
"$1bookmarks-menu-button,downloads-button$2");
} else if (currentset.contains("home-button")) {
currentset = currentset.replace(/(^|,)home-button($|,)/,
"$1bookmarks-menu-button,home-button$2");
} else {
// Just append.
currentset = currentset.replace(/(^|,)window-controls($|,)/,
"$1bookmarks-menu-button,window-controls$2")
}
- this._setPersist(toolbarResource, currentsetResource, currentset);
+ xulStore.setValue(BROWSER_DOCURL, "nav-bar", "currentset", currentset);
}
}
}
if (currentUIVersion < 18) {
// Remove iconsize and mode from all the toolbars
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);
}
}
}
if (currentUIVersion < 19) {
let detector = null;
try {
detector = Services.prefs.getComplexValue("intl.charset.detector",
@@ -1588,82 +1567,35 @@ BrowserGlue.prototype = {
// If the encoding detector pref value is not reachable from the UI,
// reset to default (varies by localization).
Services.prefs.clearUserPref("intl.charset.detector");
}
}
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) {
// Reset the Sync promobox count to promote the new FxAccount-based Sync.
Services.prefs.clearUserPref("browser.syncPromoViewsLeft");
Services.prefs.clearUserPref("browser.syncPromoViewsLeftMap");
}
- 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
// ------------------------------
sanitize: function BG_sanitize(aParentWindow) {
this._sanitizer.sanitize(aParentWindow);
},
diff --git a/browser/components/places/content/treeView.js b/browser/components/places/content/treeView.js
--- a/browser/components/places/content/treeView.js
+++ b/browser/components/places/content/treeView.js
@@ -18,16 +18,24 @@ function PlacesTreeView(aFlatList, aOnOp
this._flatList = aFlatList;
this._openContainerCallback = aOnOpenFlatContainer;
this._controller = 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) {
this.__dateService = Cc["@mozilla.org/intl/scriptabledateformat;1"].
getService(Ci.nsIScriptableDateFormat);
}
return this.__dateService;
},
@@ -302,21 +310,25 @@ PlacesTreeView.prototype = {
this._rows[row] = curChild;
rowsInserted++;
// Recursively do containers.
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)
rowsInserted += this._buildVisibleSection(curChild, row + 1, aToOpen);
}
}
return rowsInserted;
@@ -1104,21 +1116,26 @@ PlacesTreeView.prototype = {
try {
return this._getRowForNode(aNode, true);
}
catch(ex) { }
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
get rowCount() this._rows.length,
get selection() this._selection,
set selection(val) this._selection = val,
getRowProperties: function() { return ""; },
@@ -1488,25 +1505,26 @@ PlacesTreeView.prototype = {
let node = this._rows[aRow];
if (this._flatList && this._openContainerCallback) {
this._openContainerCallback(node);
return;
}
// 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");
+ }
}
}
node.containerOpen = !node.containerOpen;
},
cycleHeader: function PTV_cycleHeader(aColumn) {
if (!this._result)
diff --git a/browser/components/places/src/PlacesUIUtils.jsm b/browser/components/places/src/PlacesUIUtils.jsm
--- a/browser/components/places/src/PlacesUIUtils.jsm
+++ b/browser/components/places/src/PlacesUIUtils.jsm
@@ -1015,20 +1015,16 @@ this.PlacesUIUtils = {
Weave.Service.engineManager.get("tabs").enabled;
},
};
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;
});
XPCOMUtils.defineLazyGetter(PlacesUIUtils, "useAsyncTransactions", function() {
try {
return Services.prefs.getBoolPref("browser.places.useAsyncTransactions");
diff --git a/browser/components/places/tests/browser/browser_toolbar_migration.js b/browser/components/places/tests/browser/browser_toolbar_migration.js
--- a/browser/components/places/tests/browser/browser_toolbar_migration.js
+++ b/browser/components/places/tests/browser/browser_toolbar_migration.js
@@ -1,90 +1,53 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/**
* 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();
}
};
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()
{
info("A toolbar with added bookmarks should be uncollapsed.");
let ids = [];
ids.push(
PlacesUtils.bookmarks.insertSeparator(PlacesUtils.toolbarFolderId,
@@ -93,43 +56,47 @@ function test_many_bookmarks_toolbar()
ids.push(
PlacesUtils.bookmarks.insertSeparator(PlacesUtils.toolbarFolderId,
PlacesUtils.bookmarks.DEFAULT_INDEX)
);
ids.push(
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");
},
];
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) {
clean();
Services.prefs.setIntPref("browser.migration.version", 4);
gTests.shift().call();
}
}
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);
}
diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -297,16 +297,17 @@
#endif
@BINPATH@/browser/components/shellservice.xpt
@BINPATH@/components/shistory.xpt
@BINPATH@/components/spellchecker.xpt
@BINPATH@/components/storage.xpt
@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
#endif
@BINPATH@/components/txtsvc.xpt
@BINPATH@/components/txmgr.xpt
@BINPATH@/components/uconv.xpt
@BINPATH@/components/unicharutil.xpt
@@ -496,16 +497,18 @@
#ifdef MOZ_CAPTIVEDETECT
@BINPATH@/components/CaptivePortalDetectComponents.manifest
@BINPATH@/components/captivedetect.js
#endif
@BINPATH@/components/servicesComponents.manifest
@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
@BINPATH@/components/SettingsManager.manifest
@BINPATH@/components/Webapps.js
@BINPATH@/components/Webapps.manifest
@BINPATH@/components/AppsService.js
@BINPATH@/components/AppsService.manifest
diff --git a/content/xul/document/src/XULDocument.cpp b/content/xul/document/src/XULDocument.cpp
--- a/content/xul/document/src/XULDocument.cpp
+++ b/content/xul/document/src/XULDocument.cpp
@@ -28,32 +28,28 @@
#include "nsError.h"
#include "nsIBoxObject.h"
#include "nsIChromeRegistry.h"
#include "nsView.h"
#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"
#include "nsGkAtoms.h"
#include "nsXMLContentSink.h"
#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"
#include "nsXULCommandDispatcher.h"
#include "nsXULElement.h"
#include "prlog.h"
#include "rdf.h"
#include "nsIFrame.h"
@@ -131,21 +127,16 @@ const uint32_t kMaxAttributeLength = 409
//----------------------------------------------------------------------
//
// Statics
//
int32_t XULDocument::gRefCnt = 0;
-nsIRDFService* XULDocument::gRDFService;
-nsIRDFResource* XULDocument::kNC_persist;
-nsIRDFResource* XULDocument::kNC_attribute;
-nsIRDFResource* XULDocument::kNC_value;
-
PRLogModuleInfo* XULDocument::gXULLog;
//----------------------------------------------------------------------
struct BroadcasterMapEntry : public PLDHashEntryHdr {
Element* mBroadcaster; // [WEAK]
nsSmallVoidArray mListeners; // [OWNING] of BroadcastListener objects
};
@@ -224,36 +215,21 @@ XULDocument::~XULDocument()
// look for persisted data:
mPersistenceIds.Clear();
// Destroy our broadcaster map.
if (mBroadcasterMap) {
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);
}
}
} // namespace dom
} // namespace mozilla
@@ -343,16 +319,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(XULDocument, XMLDocument)
delete tmp->mTemplateBuilderTable;
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
NS_IMPL_ADDREF_INHERITED(XULDocument, XMLDocument)
NS_IMPL_RELEASE_INHERITED(XULDocument, XMLDocument)
// QueryInterface implementation for XULDocument
@@ -1326,126 +1303,60 @@ XULDocument::Persist(const nsAString& aI
}
tag = do_GetAtom(aAttr);
NS_ENSURE_TRUE(tag, NS_ERROR_OUT_OF_MEMORY);
nameSpaceID = kNameSpaceID_None;
}
- rv = Persist(element, nameSpaceID, tag);
- if (NS_FAILED(rv)) return rv;
-
- return NS_OK;
+ return Persist(element, nameSpaceID, tag);
}
nsresult
XULDocument::Persist(nsIContent* aElement, int32_t aNameSpaceID,
nsIAtom* aAttribute)
{
// For non-chrome documents, persistance is simply broken
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;
-
- // Turn the value into a literal
+ nsAutoString id;
+
+ aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::id, id);
+ nsAtomString attrstr(aAttribute);
+
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;
}
-
- // 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);
+ NS_ConvertUTF8toUTF16 uri(utf8uri);
+
+ bool hasAttr;
+ rv = mLocalStore->HasValue(uri, id, attrstr, &hasAttr);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
}
- 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 (hasAttr && valuestr.IsEmpty()) {
+ return mLocalStore->RemoveValue(uri, id, attrstr);
+ } else {
+ return mLocalStore->SetValue(uri, id, attrstr, valuestr);
}
-
- 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;
}
nsresult
XULDocument::GetViewportSize(int32_t* aWidth,
int32_t* aHeight)
{
*aWidth = *aHeight = 0;
@@ -1988,35 +1899,17 @@ XULDocument::Init()
{
nsresult rv = XMLDocument::Init();
NS_ENSURE_SUCCESS(rv, rv);
// Create our command dispatcher and hook it up.
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.
nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
if (!cache) {
NS_ERROR("Could not instantiate nsXULPrototypeCache");
return NS_ERROR_FAILURE;
}
@@ -2170,154 +2063,133 @@ nsresult
XULDocument::ApplyPersistentAttributes()
{
// For non-chrome documents, persistance is simply broken
if (!nsContentUtils::IsSystemPrincipal(NodePrincipal()))
return NS_ERROR_NOT_AVAILABLE;
// 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();
mApplyingPersistedAttrs = false;
// After we've applied persistence once, we should only reapply
// it to nodes created by overlays
mRestrictPersistence = true;
mPersistenceIds.Clear();
return NS_OK;
}
-nsresult
+nsresult
XULDocument::ApplyPersistentAttributesInternal()
{
nsCOMArray<nsIContent> elements;
- nsAutoCString docurl;
- mDocumentURI->GetSpec(docurl);
-
- nsCOMPtr<nsIRDFResource> doc;
- gRDFService->GetResource(docurl, getter_AddRefs(doc));
-
- nsCOMPtr<nsISimpleEnumerator> persisted;
- mLocalStore->GetTargets(doc, kNC_persist, true, getter_AddRefs(persisted));
+ nsAutoCString utf8uri;
+ nsresult rv = mDocumentURI->GetSpec(utf8uri);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ NS_ConvertUTF8toUTF16 uri(utf8uri);
+
+ // 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));
-
- nsCOMPtr<nsIRDFResource> resource = do_QueryInterface(isupports);
- if (! resource) {
- NS_WARNING("expected element to be a resource");
+ }
+
+ nsAutoString id;
+ ids->GetNext(id);
+
+ 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;
}
nsresult
-XULDocument::ApplyPersistentAttributesToElements(nsIRDFResource* aResource,
+XULDocument::ApplyPersistentAttributesToElements(const nsAString &aID,
nsCOMArray<nsIContent>& aElements)
{
- nsresult rv;
-
- nsCOMPtr<nsISimpleEnumerator> attrs;
- rv = mLocalStore->ArcLabelsOut(aResource, getter_AddRefs(attrs));
- if (NS_FAILED(rv)) return rv;
+ nsAutoCString utf8uri;
+ nsresult rv = mDocumentURI->GetSpec(utf8uri);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ NS_ConvertUTF8toUTF16 uri(utf8uri);
+
+ // 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;
-
- nsCOMPtr<nsIAtom> attr = do_GetAtom(attrname);
- if (! attr)
+ nsAutoString attrstr;
+ attrs->GetNext(attrstr);
+
+ 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;
-
- rv = element->SetAttr(/* XXX */ kNameSpaceID_None,
- attr,
- wrapper,
- true);
+ if (!element) {
+ continue;
+ }
+
+ rv = element->SetAttr(kNameSpaceID_None, attr, value, PR_TRUE);
}
}
return NS_OK;
}
void
XULDocument::TraceProtos(JSTracer* aTrc, uint32_t aGCNumber)
diff --git a/content/xul/document/src/XULDocument.h b/content/xul/document/src/XULDocument.h
--- a/content/xul/document/src/XULDocument.h
+++ b/content/xul/document/src/XULDocument.h
@@ -17,16 +17,17 @@
#include "nsIDOMXULCommandDispatcher.h"
#include "nsIDOMXULDocument.h"
#include "nsCOMArray.h"
#include "nsIURI.h"
#include "nsIXULDocument.h"
#include "nsScriptLoader.h"
#include "nsIStreamListener.h"
#include "nsICSSLoaderObserver.h"
+#include "nsIXULStore.h"
#include "mozilla/Attributes.h"
#include "js/TracingAPI.h"
#include "js/TypeDecls.h"
class nsIRDFResource;
class nsIRDFService;
@@ -254,17 +255,17 @@ protected:
nsIParser** aResult);
nsresult
LoadOverlayInternal(nsIURI* aURI, bool aIsDynamic, bool* aShouldReturn,
bool* aFailureFromContent);
nsresult ApplyPersistentAttributes();
nsresult ApplyPersistentAttributesInternal();
- nsresult ApplyPersistentAttributesToElements(nsIRDFResource* aResource,
+ nsresult ApplyPersistentAttributesToElements(const nsAString &aID,
nsCOMArray<nsIContent>& aElements);
nsresult
AddElementToDocumentPre(Element* aElement);
nsresult
AddElementToDocumentPost(Element* aElement);
@@ -309,20 +310,20 @@ protected:
// NOTE, THIS IS STILL IN PROGRESS, TALK TO PINK OR SCC BEFORE
// CHANGING
XULDocument* mNextSrcLoadWaiter; // [OWNER] but not COMPtr
// 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
* progress (waiting for an overlay to finish loading).
* mStillWalking prevents DoneLoading (and StartLayout) from being
* called in this situation.
*/
bool mStillWalking;
diff --git a/content/xul/templates/src/nsXULContentUtils.cpp b/content/xul/templates/src/nsXULContentUtils.cpp
--- a/content/xul/templates/src/nsXULContentUtils.cpp
+++ b/content/xul/templates/src/nsXULContentUtils.cpp
@@ -181,46 +181,16 @@ nsXULContentUtils::FindChildByTag(nsICon
}
}
*aResult = nullptr;
return NS_RDF_NO_VALUE; // not found
}
-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
*/
nsresult
nsXULContentUtils::GetTextForNode(nsIRDFNode* aNode, nsAString& aResult)
{
if (! aNode) {
@@ -283,88 +253,16 @@ nsXULContentUtils::GetTextForNode(nsIRDF
return NS_OK;
}
NS_ERROR("not a resource or a literal");
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)
{
// construct a fully-qualified URI from the namespace/tag pair.
NS_PRECONDITION(aAttribute != nullptr, "null ptr");
if (! aAttribute)
return NS_ERROR_NULL_POINTER;
return GetResource(aNameSpaceID, nsDependentAtomString(aAttribute),
diff --git a/content/xul/templates/src/nsXULContentUtils.h b/content/xul/templates/src/nsXULContentUtils.h
--- a/content/xul/templates/src/nsXULContentUtils.h
+++ b/content/xul/templates/src/nsXULContentUtils.h
@@ -111,40 +111,18 @@ public:
nsIContent **aResult);
static nsresult
FindChildByResource(nsIContent* aElement,
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);
static nsresult
GetResource(int32_t aNameSpaceID, const nsAString& aAttribute, nsIRDFResource** aResult);
static nsresult
SetCommandUpdater(nsIDocument* aDocument, nsIContent* aElement);
diff --git a/content/xul/templates/src/nsXULTreeBuilder.cpp b/content/xul/templates/src/nsXULTreeBuilder.cpp
--- a/content/xul/templates/src/nsXULTreeBuilder.cpp
+++ b/content/xul/templates/src/nsXULTreeBuilder.cpp
@@ -3,17 +3,16 @@
* 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 "nscore.h"
#include "nsError.h"
#include "nsIContent.h"
#include "mozilla/dom/NodeInfo.h"
#include "nsIDOMElement.h"
-#include "nsILocalStore.h"
#include "nsIBoxObject.h"
#include "nsITreeBoxObject.h"
#include "nsITreeSelection.h"
#include "nsITreeColumns.h"
#include "nsITreeView.h"
#include "nsTreeUtils.h"
#include "nsIServiceManager.h"
#include "nsReadableUtils.h"
@@ -26,16 +25,17 @@
#include "nsXULTemplateBuilder.h"
#include "nsIXULSortService.h"
#include "nsTArray.h"
#include "nsUnicharUtils.h"
#include "nsNameSpaceManager.h"
#include "nsDOMClassInfoID.h"
#include "nsWhitespaceTokenizer.h"
#include "nsTreeContentView.h"
+#include "nsIXULStore.h"
// For security check
#include "nsIDocument.h"
/**
* A XUL template builder that serves as an tree view, allowing
* (pretty much) arbitrary RDF to be presented in an tree.
*/
@@ -134,23 +134,20 @@ protected:
/**
* Remove the matches for the rows in a subtree
*/
nsresult
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().
*/
static int
Compare(const void* aLeft, const void* aRight, void* aClosure);
/**
@@ -237,16 +234,21 @@ protected:
* Sort hints (compare case, etc)
*/
uint32_t mSortHints;
/**
* The builder observers.
*/
nsCOMArray<nsIXULTreeBuilderObserver> mObservers;
+
+ /*
+ * XUL store for holding open container state
+ */
+ nsCOMPtr<nsIXULStore> mLocalStore;
};
//----------------------------------------------------------------------
nsresult
NS_NewXULTreeBuilder(nsISupports* aOuter, REFNSIID aIID, void** aResult)
{
*aResult = nullptr;
@@ -273,16 +275,17 @@ NS_NewXULTreeBuilder(nsISupports* aOuter
NS_IMPL_ADDREF_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder)
NS_IMPL_RELEASE_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder)
NS_IMPL_CYCLE_COLLECTION_INHERITED(nsXULTreeBuilder, nsXULTemplateBuilder,
mBoxObject,
mSelection,
mPersistStateStore,
+ mLocalStore,
mObservers)
DOMCI_DATA(XULTreeBuilder, nsXULTreeBuilder)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsXULTreeBuilder)
NS_INTERFACE_MAP_ENTRY(nsIXULTreeBuilder)
NS_INTERFACE_MAP_ENTRY(nsITreeView)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(XULTreeBuilder)
@@ -523,18 +526,17 @@ nsXULTreeBuilder::IsContainerOpen(int32_
{
NS_PRECONDITION(aIndex >= 0 && aIndex < mRows.Count(), "bad row");
if (aIndex < 0 || aIndex >= mRows.Count())
return NS_ERROR_INVALID_ARG;
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
: nsTreeRows::eContainerState_Closed;
}
*aOpen = (iter->mContainerState == nsTreeRows::eContainerState_Open);
return NS_OK;
@@ -752,52 +754,26 @@ nsXULTreeBuilder::SetTree(nsITreeBoxObje
// If this is teardown time, then we're done.
if (!mBoxObject) {
Uninit(false);
return NS_OK;
}
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();
if (mSortVariable)
SortSubtree(mRows.GetRoot());
return NS_OK;
}
@@ -825,44 +801,46 @@ nsXULTreeBuilder::ToggleOpenState(int32_
uint32_t count = mObservers.Count();
for (uint32_t i = 0; i < count; ++i) {
nsCOMPtr<nsIXULTreeBuilderObserver> observer = mObservers.SafeObjectAt(i);
if (observer)
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);
}
}
return NS_OK;
}
@@ -1220,20 +1198,19 @@ nsXULTreeBuilder::ReplaceMatch(nsIXULTem
if (result != mRootResult) {
// don't open containers if child processing isn't allowed
bool mayProcessChildren;
nsresult rv = result->GetMayProcessChildren(&mayProcessChildren);
if (NS_FAILED(rv) || ! mayProcessChildren) return NS_OK;
}
- bool open;
- IsContainerOpen(result, &open);
- if (open)
+ if (IsContainerOpen(result)) {
OpenContainer(iter.GetRowIndex(), result);
+ }
}
}
return NS_OK;
}
nsresult
nsXULTreeBuilder::SynchronizeResult(nsIXULTemplateResult* aResult)
@@ -1631,19 +1608,17 @@ nsXULTreeBuilder::OpenSubtreeForQuerySet
return rv;
}
// Remember that this match applied to this row
mRows.InsertRowAt(newmatch, aSubtree, count);
// 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;
}
++count;
}
if (mFlags & eLoggingEnabled)
@@ -1717,46 +1692,52 @@ nsXULTreeBuilder::RemoveMatchesFor(nsTre
if ((row.mContainerState == nsTreeRows::eContainerState_Open) && row.mSubtree)
RemoveMatchesFor(*(row.mSubtree));
}
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
nsXULTreeBuilder::Compare(const void* aLeft, const void* aRight, void* aClosure)
{
nsXULTreeBuilder* self = static_cast<nsXULTreeBuilder*>(aClosure);
nsTreeRows::Row* left = static_cast<nsTreeRows::Row*>
diff --git a/mobile/android/installer/package-manifest.in b/mobile/android/installer/package-manifest.in
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -244,16 +244,17 @@
@BINPATH@/components/shellservice.xpt
@BINPATH@/components/shistory.xpt
@BINPATH@/components/spellchecker.xpt
@BINPATH@/components/storage.xpt
@BINPATH@/components/telemetry.xpt
@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
#endif
@BINPATH@/components/txtsvc.xpt
@BINPATH@/components/txmgr.xpt
@BINPATH@/components/uconv.xpt
@BINPATH@/components/unicharutil.xpt
@@ -391,16 +392,18 @@
#endif
@BINPATH@/components/nsINIProcessor.manifest
@BINPATH@/components/nsINIProcessor.js
@BINPATH@/components/nsPrompter.manifest
@BINPATH@/components/nsPrompter.js
@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
@BINPATH@/components/AppsService.manifest
@BINPATH@/components/Push.js
@BINPATH@/components/Push.manifest
diff --git a/toolkit/components/moz.build b/toolkit/components/moz.build
--- a/toolkit/components/moz.build
+++ b/toolkit/components/moz.build
@@ -44,16 +44,17 @@ PARALLEL_DIRS += [
'telemetry',
'thumbnails',
'typeaheadfind',
'urlformatter',
'viewconfig',
'viewsource',
'workerloader',
'workerlz4',
+ 'xulstore'
]
if CONFIG['MOZ_CRASHREPORTER']:
PARALLEL_DIRS += ['crashes']
if CONFIG['MOZ_SOCIAL']:
PARALLEL_DIRS += ['social']
diff --git a/toolkit/components/places/nsNavHistory.cpp b/toolkit/components/places/nsNavHistory.cpp
--- a/toolkit/components/places/nsNavHistory.cpp
+++ b/toolkit/components/places/nsNavHistory.cpp
@@ -3644,17 +3644,17 @@ nsNavHistory::ResultsAsList(mozIStorageS
}
return NS_OK;
}
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)
{
nsAutoCString uri;
nsresult rv = aResultNode->GetUri(uri);
NS_ENSURE_SUCCESS(rv, rv);
diff --git a/toolkit/components/xulstore/XULStore.js b/toolkit/components/xulstore/XULStore.js
new file mode 100644
--- /dev/null
+++ b/toolkit/components/xulstore/XULStore.js
@@ -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]);
diff --git a/toolkit/components/xulstore/XULStore.manifest b/toolkit/components/xulstore/XULStore.manifest
new file mode 100644
--- /dev/null
+++ b/toolkit/components/xulstore/XULStore.manifest
@@ -0,0 +1,2 @@
+component {6f46b6f4-c8b1-4bd4-a4fa-9ebbed0753ea} XULStore.js
+contract @mozilla.org/xul/xulstore;1 {6f46b6f4-c8b1-4bd4-a4fa-9ebbed0753ea}
diff --git a/toolkit/components/xulstore/moz.build b/toolkit/components/xulstore/moz.build
new file mode 100644
--- /dev/null
+++ b/toolkit/components/xulstore/moz.build
@@ -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',
+]
diff --git a/toolkit/components/xulstore/nsIXULStore.idl b/toolkit/components/xulstore/nsIXULStore.idl
new file mode 100644
--- /dev/null
+++ b/toolkit/components/xulstore/nsIXULStore.idl
@@ -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);
+};
diff --git a/toolkit/components/xulstore/tests/chrome/animals.rdf b/toolkit/components/xulstore/tests/chrome/animals.rdf
new file mode 100644
--- /dev/null
+++ b/toolkit/components/xulstore/tests/chrome/animals.rdf
@@ -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>
diff --git a/toolkit/components/xulstore/tests/chrome/chrome.ini b/toolkit/components/xulstore/tests/chrome/chrome.ini
new file mode 100644
--- /dev/null
+++ b/toolkit/components/xulstore/tests/chrome/chrome.ini
@@ -0,0 +1,6 @@
+[DEFAULT]
+support-files =
+ window_persistence.xul
+ animals.rdf
+
+[test_persistence.xul]
diff --git a/toolkit/components/xulstore/tests/chrome/test_persistence.xul b/toolkit/components/xulstore/tests/chrome/test_persistence.xul
new file mode 100644
--- /dev/null
+++ b/toolkit/components/xulstore/tests/chrome/test_persistence.xul
@@ -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>
diff --git a/toolkit/components/xulstore/tests/chrome/window_persistence.xul b/toolkit/components/xulstore/tests/chrome/window_persistence.xul
new file mode 100644
--- /dev/null
+++ b/toolkit/components/xulstore/tests/chrome/window_persistence.xul
@@ -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>
diff --git a/toolkit/components/xulstore/tests/moz.build b/toolkit/components/xulstore/tests/moz.build
new file mode 100644
--- /dev/null
+++ b/toolkit/components/xulstore/tests/moz.build
@@ -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']
diff --git a/toolkit/components/xulstore/tests/xpcshell/localstore.rdf b/toolkit/components/xulstore/tests/xpcshell/localstore.rdf
new file mode 100644
--- /dev/null
+++ b/toolkit/components/xulstore/tests/xpcshell/localstore.rdf
@@ -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>
diff --git a/toolkit/components/xulstore/tests/xpcshell/test_XULStore.js b/toolkit/components/xulstore/tests/xpcshell/test_XULStore.js
new file mode 100644
--- /dev/null
+++ b/toolkit/components/xulstore/tests/xpcshell/test_XULStore.js
@@ -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));
+});
diff --git a/toolkit/components/xulstore/tests/xpcshell/xpcshell.ini b/toolkit/components/xulstore/tests/xpcshell/xpcshell.ini
new file mode 100644
--- /dev/null
+++ b/toolkit/components/xulstore/tests/xpcshell/xpcshell.ini
@@ -0,0 +1,6 @@
+[DEFAULT]
+
+support-files =
+ localstore.rdf
+
+[test_XULStore.js]
diff --git a/toolkit/mozapps/extensions/test/browser/browser_bug562797.js b/toolkit/mozapps/extensions/test/browser/browser_bug562797.js
--- a/toolkit/mozapps/extensions/test/browser/browser_bug562797.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_bug562797.js
@@ -471,27 +471,18 @@ add_test(function() {
});
});
// Tests that going back to search results works
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");
is_in_list(aManager, "addons://list/extension", false, false);
var search = aManager.document.getElementById("header-search");
search.focus();
search.value = "bar";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment