mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 871445 - patch 9 - DataStore: child<->parent communication, r=ehsan
--HG-- rename : dom/datastore/tests/test_basic.html => dom/datastore/tests/test_oop.html
This commit is contained in:
parent
bc8f1080be
commit
263e781a73
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
'use strict'
|
'use strict'
|
||||||
|
|
||||||
this.EXPORTED_SYMBOLS = ["DataStore", "DataStoreAccess"];
|
this.EXPORTED_SYMBOLS = ["DataStore"];
|
||||||
|
|
||||||
function debug(s) {
|
function debug(s) {
|
||||||
// dump('DEBUG DataStore: ' + s + '\n');
|
// dump('DEBUG DataStore: ' + s + '\n');
|
||||||
@ -67,13 +67,13 @@ function parseIds(aId) {
|
|||||||
return aId;
|
return aId;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Exposed DataStore object */
|
/* DataStore object */
|
||||||
function ExposedDataStore(aWindow, aDataStore, aReadOnly) {
|
this.DataStore = function(aWindow, aName, aOwner, aReadOnly) {
|
||||||
debug("ExposedDataStore created");
|
debug("DataStore created");
|
||||||
this.init(aWindow, aDataStore, aReadOnly);
|
this.init(aWindow, aName, aOwner, aReadOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
ExposedDataStore.prototype = {
|
this.DataStore.prototype = {
|
||||||
classDescription: "DataStore XPCOM Component",
|
classDescription: "DataStore XPCOM Component",
|
||||||
classID: Components.ID("{db5c9602-030f-4bff-a3de-881a8de370f2}"),
|
classID: Components.ID("{db5c9602-030f-4bff-a3de-881a8de370f2}"),
|
||||||
contractID: "@mozilla.org/dom/datastore;1",
|
contractID: "@mozilla.org/dom/datastore;1",
|
||||||
@ -81,286 +81,46 @@ ExposedDataStore.prototype = {
|
|||||||
|
|
||||||
callbacks: [],
|
callbacks: [],
|
||||||
|
|
||||||
init: function(aWindow, aDataStore, aReadOnly) {
|
_window: null,
|
||||||
debug("ExposedDataStore init");
|
_name: null,
|
||||||
|
_owner: null,
|
||||||
|
_readOnly: null,
|
||||||
|
_revisionId: null,
|
||||||
|
|
||||||
this.window = aWindow;
|
init: function(aWindow, aName, aOwner, aReadOnly) {
|
||||||
this.dataStore = aDataStore;
|
debug("DataStore init");
|
||||||
this.isReadOnly = aReadOnly;
|
|
||||||
},
|
|
||||||
|
|
||||||
receiveMessage: function(aMessage) {
|
this._window = aWindow;
|
||||||
debug("receiveMessage");
|
this._name = aName;
|
||||||
|
this._owner = aOwner;
|
||||||
|
this._readOnly = aReadOnly;
|
||||||
|
|
||||||
if (aMessage.name != "DataStore:Changed:Return:OK") {
|
this._db = new DataStoreDB();
|
||||||
debug("Wrong message: " + aMessage.name);
|
this._db.init(aOwner, aName);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let self = this;
|
let self = this;
|
||||||
|
Services.obs.addObserver(function(aSubject, aTopic, aData) {
|
||||||
this.dataStore.retrieveRevisionId(
|
let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
|
||||||
function() {
|
if (wId == self._innerWindowID) {
|
||||||
let event = new self.window.DataStoreChangeEvent('change', aMessage.data);
|
cpmm.removeMessageListener("DataStore:Changed:Return:OK", self);
|
||||||
self.__DOM_IMPL__.dispatchEvent(event);
|
self._db.delete();
|
||||||
},
|
|
||||||
// Forcing the reading of the revisionId
|
|
||||||
true
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
// Public interface :
|
|
||||||
|
|
||||||
get name() {
|
|
||||||
return this.dataStore.name;
|
|
||||||
},
|
|
||||||
|
|
||||||
get owner() {
|
|
||||||
return this.dataStore.owner;
|
|
||||||
},
|
|
||||||
|
|
||||||
get readOnly() {
|
|
||||||
return this.isReadOnly;
|
|
||||||
},
|
|
||||||
|
|
||||||
get: function(aId) {
|
|
||||||
aId = parseIds(aId);
|
|
||||||
if (aId === null) {
|
|
||||||
return throwInvalidArg(this.window);
|
|
||||||
}
|
}
|
||||||
|
}, "inner-window-destroyed", false);
|
||||||
|
|
||||||
|
let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||||
|
.getInterface(Ci.nsIDOMWindowUtils);
|
||||||
|
this._innerWindowID = util.currentInnerWindowID;
|
||||||
|
|
||||||
|
cpmm.addMessageListener("DataStore:Changed:Return:OK", this);
|
||||||
|
cpmm.sendAsyncMessage("DataStore:RegisterForMessages",
|
||||||
|
{ store: this._name, owner: this._owner });
|
||||||
|
},
|
||||||
|
|
||||||
|
newDBPromise: function(aTxnType, aFunction) {
|
||||||
let self = this;
|
let self = this;
|
||||||
|
return new this._window.Promise(function(aResolve, aReject) {
|
||||||
// Promise<Object>
|
|
||||||
return this.dataStore.newDBPromise(this.window, "readonly",
|
|
||||||
function(aResolve, aReject, aTxn, aStore, aRevisionStore) {
|
|
||||||
self.dataStore.getInternal(aStore,
|
|
||||||
Array.isArray(aId) ? aId : [ aId ],
|
|
||||||
function(aResults) {
|
|
||||||
aResolve(Array.isArray(aId) ? aResults : aResults[0]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
update: function(aId, aObj) {
|
|
||||||
aId = parseInt(aId);
|
|
||||||
if (isNaN(aId) || aId <= 0) {
|
|
||||||
return throwInvalidArg(this.window);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isReadOnly) {
|
|
||||||
return throwReadOnly(this.window);
|
|
||||||
}
|
|
||||||
|
|
||||||
let self = this;
|
|
||||||
|
|
||||||
// Promise<void>
|
|
||||||
return this.dataStore.newDBPromise(this.window, "readwrite",
|
|
||||||
function(aResolve, aReject, aTxn, aStore, aRevisionStore) {
|
|
||||||
self.dataStore.updateInternal(aResolve, aStore, aRevisionStore, aId, aObj);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
add: function(aObj) {
|
|
||||||
if (this.isReadOnly) {
|
|
||||||
return throwReadOnly(this.window);
|
|
||||||
}
|
|
||||||
|
|
||||||
let self = this;
|
|
||||||
|
|
||||||
// Promise<int>
|
|
||||||
return this.dataStore.newDBPromise(this.window, "readwrite",
|
|
||||||
function(aResolve, aReject, aTxn, aStore, aRevisionStore) {
|
|
||||||
self.dataStore.addInternal(aResolve, aStore, aRevisionStore, aObj);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
remove: function(aId) {
|
|
||||||
aId = parseInt(aId);
|
|
||||||
if (isNaN(aId) || aId <= 0) {
|
|
||||||
return throwInvalidArg(this.window);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.isReadOnly) {
|
|
||||||
return throwReadOnly(this.window);
|
|
||||||
}
|
|
||||||
|
|
||||||
let self = this;
|
|
||||||
|
|
||||||
// Promise<void>
|
|
||||||
return this.dataStore.newDBPromise(this.window, "readwrite",
|
|
||||||
function(aResolve, aReject, aTxn, aStore, aRevisionStore) {
|
|
||||||
self.dataStore.removeInternal(aResolve, aStore, aRevisionStore, aId);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
clear: function() {
|
|
||||||
if (this.isReadOnly) {
|
|
||||||
return throwReadOnly(this.window);
|
|
||||||
}
|
|
||||||
|
|
||||||
let self = this;
|
|
||||||
|
|
||||||
// Promise<void>
|
|
||||||
return this.dataStore.newDBPromise(this.window, "readwrite",
|
|
||||||
function(aResolve, aReject, aTxn, aStore, aRevisionStore) {
|
|
||||||
self.dataStore.clearInternal(aResolve, aStore, aRevisionStore);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
get revisionId() {
|
|
||||||
return this.dataStore.revisionId;
|
|
||||||
},
|
|
||||||
|
|
||||||
getChanges: function(aRevisionId) {
|
|
||||||
debug("GetChanges: " + aRevisionId);
|
|
||||||
|
|
||||||
if (aRevisionId === null || aRevisionId === undefined) {
|
|
||||||
return this.window.Promise.reject(
|
|
||||||
new this.window.DOMError("SyntaxError", "Invalid revisionId"));
|
|
||||||
}
|
|
||||||
|
|
||||||
let self = this;
|
|
||||||
|
|
||||||
// Promise<DataStoreChanges>
|
|
||||||
return new this.window.Promise(function(aResolve, aReject) {
|
|
||||||
debug("GetChanges promise started");
|
|
||||||
self.dataStore.db.revisionTxn(
|
|
||||||
'readonly',
|
|
||||||
function(aTxn, aStore) {
|
|
||||||
debug("GetChanges transaction success");
|
|
||||||
|
|
||||||
let request = self.dataStore.db.getInternalRevisionId(
|
|
||||||
aRevisionId,
|
|
||||||
aStore,
|
|
||||||
function(aInternalRevisionId) {
|
|
||||||
if (aInternalRevisionId == undefined) {
|
|
||||||
aResolve(undefined);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This object is the return value of this promise.
|
|
||||||
// Initially we use maps, and then we convert them in array.
|
|
||||||
let changes = {
|
|
||||||
revisionId: '',
|
|
||||||
addedIds: {},
|
|
||||||
updatedIds: {},
|
|
||||||
removedIds: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
let request = aStore.mozGetAll(self.window.IDBKeyRange.lowerBound(aInternalRevisionId, true));
|
|
||||||
request.onsuccess = function(aEvent) {
|
|
||||||
for (let i = 0; i < aEvent.target.result.length; ++i) {
|
|
||||||
let data = aEvent.target.result[i];
|
|
||||||
|
|
||||||
switch (data.operation) {
|
|
||||||
case REVISION_ADDED:
|
|
||||||
changes.addedIds[data.objectId] = true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REVISION_UPDATED:
|
|
||||||
// We don't consider an update if this object has been added
|
|
||||||
// or if it has been already modified by a previous
|
|
||||||
// operation.
|
|
||||||
if (!(data.objectId in changes.addedIds) &&
|
|
||||||
!(data.objectId in changes.updatedIds)) {
|
|
||||||
changes.updatedIds[data.objectId] = true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case REVISION_REMOVED:
|
|
||||||
let id = data.objectId;
|
|
||||||
|
|
||||||
// If the object has been added in this range of revisions
|
|
||||||
// we can ignore it and remove it from the list.
|
|
||||||
if (id in changes.addedIds) {
|
|
||||||
delete changes.addedIds[id];
|
|
||||||
} else {
|
|
||||||
changes.removedIds[id] = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (id in changes.updatedIds) {
|
|
||||||
delete changes.updatedIds[id];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// The last revisionId.
|
|
||||||
if (aEvent.target.result.length) {
|
|
||||||
changes.revisionId = aEvent.target.result[aEvent.target.result.length - 1].revisionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
// From maps to arrays.
|
|
||||||
changes.addedIds = Object.keys(changes.addedIds).map(function(aKey) { return parseInt(aKey, 10); });
|
|
||||||
changes.updatedIds = Object.keys(changes.updatedIds).map(function(aKey) { return parseInt(aKey, 10); });
|
|
||||||
changes.removedIds = Object.keys(changes.removedIds).map(function(aKey) { return parseInt(aKey, 10); });
|
|
||||||
|
|
||||||
let wrappedObject = ObjectWrapper.wrap(changes, self.window);
|
|
||||||
aResolve(wrappedObject);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
function(aEvent) {
|
|
||||||
debug("GetChanges transaction failed");
|
|
||||||
aReject(createDOMError(self.window, aEvent));
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
getLength: function() {
|
|
||||||
let self = this;
|
|
||||||
|
|
||||||
// Promise<int>
|
|
||||||
return this.dataStore.newDBPromise(this.window, "readonly",
|
|
||||||
function(aResolve, aReject, aTxn, aStore, aRevisionStore) {
|
|
||||||
self.dataStore.getLengthInternal(aResolve, aStore);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
set onchange(aCallback) {
|
|
||||||
debug("Set OnChange");
|
|
||||||
this.__DOM_IMPL__.setEventHandler("onchange", aCallback);
|
|
||||||
},
|
|
||||||
|
|
||||||
get onchange() {
|
|
||||||
debug("Get OnChange");
|
|
||||||
return this.__DOM_IMPL__.getEventHandler("onchange");
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* DataStore object */
|
|
||||||
|
|
||||||
function DataStore(aAppId, aName, aOwner, aReadOnly, aGlobalScope) {
|
|
||||||
this.appId = aAppId;
|
|
||||||
this.name = aName;
|
|
||||||
this.owner = aOwner;
|
|
||||||
this.readOnly = aReadOnly;
|
|
||||||
|
|
||||||
this.db = new DataStoreDB();
|
|
||||||
this.db.init(aOwner, aName, aGlobalScope);
|
|
||||||
}
|
|
||||||
|
|
||||||
DataStore.prototype = {
|
|
||||||
appId: null,
|
|
||||||
name: null,
|
|
||||||
owner: null,
|
|
||||||
readOnly: null,
|
|
||||||
revisionId: null,
|
|
||||||
|
|
||||||
newDBPromise: function(aWindow, aTxnType, aFunction) {
|
|
||||||
let db = this.db;
|
|
||||||
return new aWindow.Promise(function(aResolve, aReject) {
|
|
||||||
debug("DBPromise started");
|
debug("DBPromise started");
|
||||||
db.txn(
|
self._db.txn(
|
||||||
aTxnType,
|
aTxnType,
|
||||||
function(aTxn, aStore, aRevisionStore) {
|
function(aTxn, aStore, aRevisionStore) {
|
||||||
debug("DBPromise success");
|
debug("DBPromise success");
|
||||||
@ -368,7 +128,7 @@ DataStore.prototype = {
|
|||||||
},
|
},
|
||||||
function(aEvent) {
|
function(aEvent) {
|
||||||
debug("DBPromise error");
|
debug("DBPromise error");
|
||||||
aReject(createDOMError(aWindow, aEvent));
|
aReject(createDOMError(self._window, aEvent));
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -478,7 +238,7 @@ DataStore.prototype = {
|
|||||||
let request = aStore.clear();
|
let request = aStore.clear();
|
||||||
request.onsuccess = function() {
|
request.onsuccess = function() {
|
||||||
debug("ClearInternal success");
|
debug("ClearInternal success");
|
||||||
self.db.clearRevisions(aRevisionStore,
|
self._db.clearRevisions(aRevisionStore,
|
||||||
function() {
|
function() {
|
||||||
debug("Revisions cleared");
|
debug("Revisions cleared");
|
||||||
|
|
||||||
@ -506,23 +266,18 @@ DataStore.prototype = {
|
|||||||
|
|
||||||
addRevision: function(aRevisionStore, aId, aType, aSuccessCb) {
|
addRevision: function(aRevisionStore, aId, aType, aSuccessCb) {
|
||||||
let self = this;
|
let self = this;
|
||||||
this.db.addRevision(aRevisionStore, aId, aType,
|
this._db.addRevision(aRevisionStore, aId, aType,
|
||||||
function(aRevisionId) {
|
function(aRevisionId) {
|
||||||
self.revisionId = aRevisionId;
|
self._revisionId = aRevisionId;
|
||||||
self.sendNotification(aId, aType, aRevisionId);
|
self.sendNotification(aId, aType, aRevisionId);
|
||||||
aSuccessCb();
|
aSuccessCb();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
retrieveRevisionId: function(aSuccessCb, aForced) {
|
retrieveRevisionId: function(aSuccessCb) {
|
||||||
if (this.revisionId != null && !aForced) {
|
|
||||||
aSuccessCb();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let self = this;
|
let self = this;
|
||||||
this.db.revisionTxn(
|
this._db.revisionTxn(
|
||||||
'readwrite',
|
'readwrite',
|
||||||
function(aTxn, aRevisionStore) {
|
function(aTxn, aRevisionStore) {
|
||||||
debug("RetrieveRevisionId transaction success");
|
debug("RetrieveRevisionId transaction success");
|
||||||
@ -534,48 +289,20 @@ DataStore.prototype = {
|
|||||||
// If the revision doesn't exist, let's create the first one.
|
// If the revision doesn't exist, let's create the first one.
|
||||||
self.addRevision(aRevisionStore, 0, REVISION_VOID,
|
self.addRevision(aRevisionStore, 0, REVISION_VOID,
|
||||||
function(aRevisionId) {
|
function(aRevisionId) {
|
||||||
self.revisionId = aRevisionId;
|
self._revisionId = aRevisionId;
|
||||||
aSuccessCb();
|
aSuccessCb();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.revisionId = cursor.value.revisionId;
|
self._revisionId = cursor.value.revisionId;
|
||||||
aSuccessCb();
|
aSuccessCb();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
exposeObject: function(aWindow, aReadOnly) {
|
|
||||||
debug("Exposing Object");
|
|
||||||
|
|
||||||
let exposedObject = new ExposedDataStore(aWindow, this, aReadOnly);
|
|
||||||
let object = aWindow.DataStore._create(aWindow, exposedObject);
|
|
||||||
|
|
||||||
Services.obs.addObserver(function(aSubject, aTopic, aData) {
|
|
||||||
let wId = aSubject.QueryInterface(Ci.nsISupportsPRUint64).data;
|
|
||||||
if (wId == exposedObject.innerWindowID) {
|
|
||||||
cpmm.removeMessageListener("DataStore:Changed:Return:OK", exposedObject);
|
|
||||||
}
|
|
||||||
}, "inner-window-destroyed", false);
|
|
||||||
|
|
||||||
let util = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
||||||
.getInterface(Ci.nsIDOMWindowUtils);
|
|
||||||
exposedObject.innerWindowID = util.currentInnerWindowID;
|
|
||||||
|
|
||||||
cpmm.addMessageListener("DataStore:Changed:Return:OK", exposedObject);
|
|
||||||
cpmm.sendAsyncMessage("DataStore:RegisterForMessages",
|
|
||||||
{ store: this.name, owner: this.owner });
|
|
||||||
|
|
||||||
return object;
|
|
||||||
},
|
|
||||||
|
|
||||||
delete: function() {
|
|
||||||
this.db.delete();
|
|
||||||
},
|
|
||||||
|
|
||||||
sendNotification: function(aId, aOperation, aRevisionId) {
|
sendNotification: function(aId, aOperation, aRevisionId) {
|
||||||
debug("SendNotification");
|
debug("SendNotification");
|
||||||
if (aOperation != REVISION_VOID) {
|
if (aOperation != REVISION_VOID) {
|
||||||
@ -584,16 +311,250 @@ DataStore.prototype = {
|
|||||||
message: { revisionId: aRevisionId, id: aId,
|
message: { revisionId: aRevisionId, id: aId,
|
||||||
operation: aOperation } } );
|
operation: aOperation } } );
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
receiveMessage: function(aMessage) {
|
||||||
|
debug("receiveMessage");
|
||||||
|
|
||||||
|
if (aMessage.name != "DataStore:Changed:Return:OK") {
|
||||||
|
debug("Wrong message: " + aMessage.name);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let self = this;
|
||||||
|
|
||||||
|
this.retrieveRevisionId(
|
||||||
|
function() {
|
||||||
|
let event = new self._window.DataStoreChangeEvent('change', aMessage.data);
|
||||||
|
self.__DOM_IMPL__.dispatchEvent(event);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Public interface :
|
||||||
|
|
||||||
|
get name() {
|
||||||
|
return this._name;
|
||||||
|
},
|
||||||
|
|
||||||
|
get owner() {
|
||||||
|
return this._owner;
|
||||||
|
},
|
||||||
|
|
||||||
|
get readOnly() {
|
||||||
|
return this._readOnly;
|
||||||
|
},
|
||||||
|
|
||||||
|
get: function(aId) {
|
||||||
|
aId = parseIds(aId);
|
||||||
|
if (aId === null) {
|
||||||
|
return throwInvalidArg(this._window);
|
||||||
|
}
|
||||||
|
|
||||||
|
let self = this;
|
||||||
|
|
||||||
|
// Promise<Object>
|
||||||
|
return this.newDBPromise("readonly",
|
||||||
|
function(aResolve, aReject, aTxn, aStore, aRevisionStore) {
|
||||||
|
self.getInternal(aStore,
|
||||||
|
Array.isArray(aId) ? aId : [ aId ],
|
||||||
|
function(aResults) {
|
||||||
|
aResolve(Array.isArray(aId) ? aResults : aResults[0]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
update: function(aId, aObj) {
|
||||||
|
aId = parseInt(aId);
|
||||||
|
if (isNaN(aId) || aId <= 0) {
|
||||||
|
return throwInvalidArg(this._window);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._readOnly) {
|
||||||
|
return throwReadOnly(this._window);
|
||||||
|
}
|
||||||
|
|
||||||
|
let self = this;
|
||||||
|
|
||||||
|
// Promise<void>
|
||||||
|
return this.newDBPromise("readwrite",
|
||||||
|
function(aResolve, aReject, aTxn, aStore, aRevisionStore) {
|
||||||
|
self.updateInternal(aResolve, aStore, aRevisionStore, aId, aObj);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
add: function(aObj) {
|
||||||
|
if (this._readOnly) {
|
||||||
|
return throwReadOnly(this._window);
|
||||||
|
}
|
||||||
|
|
||||||
|
let self = this;
|
||||||
|
|
||||||
|
// Promise<int>
|
||||||
|
return this.newDBPromise("readwrite",
|
||||||
|
function(aResolve, aReject, aTxn, aStore, aRevisionStore) {
|
||||||
|
self.addInternal(aResolve, aStore, aRevisionStore, aObj);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
remove: function(aId) {
|
||||||
|
aId = parseInt(aId);
|
||||||
|
if (isNaN(aId) || aId <= 0) {
|
||||||
|
return throwInvalidArg(this._window);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this._readOnly) {
|
||||||
|
return throwReadOnly(this._window);
|
||||||
|
}
|
||||||
|
|
||||||
|
let self = this;
|
||||||
|
|
||||||
|
// Promise<void>
|
||||||
|
return this.newDBPromise("readwrite",
|
||||||
|
function(aResolve, aReject, aTxn, aStore, aRevisionStore) {
|
||||||
|
self.removeInternal(aResolve, aStore, aRevisionStore, aId);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
clear: function() {
|
||||||
|
if (this._readOnly) {
|
||||||
|
return throwReadOnly(this._window);
|
||||||
|
}
|
||||||
|
|
||||||
|
let self = this;
|
||||||
|
|
||||||
|
// Promise<void>
|
||||||
|
return this.newDBPromise("readwrite",
|
||||||
|
function(aResolve, aReject, aTxn, aStore, aRevisionStore) {
|
||||||
|
self.clearInternal(aResolve, aStore, aRevisionStore);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
get revisionId() {
|
||||||
|
return this._revisionId;
|
||||||
|
},
|
||||||
|
|
||||||
|
getChanges: function(aRevisionId) {
|
||||||
|
debug("GetChanges: " + aRevisionId);
|
||||||
|
|
||||||
|
if (aRevisionId === null || aRevisionId === undefined) {
|
||||||
|
return this._window.Promise.reject(
|
||||||
|
new this._window.DOMError("SyntaxError", "Invalid revisionId"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let self = this;
|
||||||
|
|
||||||
|
// Promise<DataStoreChanges>
|
||||||
|
return new this._window.Promise(function(aResolve, aReject) {
|
||||||
|
debug("GetChanges promise started");
|
||||||
|
self._db.revisionTxn(
|
||||||
|
'readonly',
|
||||||
|
function(aTxn, aStore) {
|
||||||
|
debug("GetChanges transaction success");
|
||||||
|
|
||||||
|
let request = self._db.getInternalRevisionId(
|
||||||
|
aRevisionId,
|
||||||
|
aStore,
|
||||||
|
function(aInternalRevisionId) {
|
||||||
|
if (aInternalRevisionId == undefined) {
|
||||||
|
aResolve(undefined);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This object is the return value of this promise.
|
||||||
|
// Initially we use maps, and then we convert them in array.
|
||||||
|
let changes = {
|
||||||
|
revisionId: '',
|
||||||
|
addedIds: {},
|
||||||
|
updatedIds: {},
|
||||||
|
removedIds: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
/* DataStoreAccess */
|
let request = aStore.mozGetAll(self._window.IDBKeyRange.lowerBound(aInternalRevisionId, true));
|
||||||
|
request.onsuccess = function(aEvent) {
|
||||||
|
for (let i = 0; i < aEvent.target.result.length; ++i) {
|
||||||
|
let data = aEvent.target.result[i];
|
||||||
|
|
||||||
function DataStoreAccess(aAppId, aName, aOrigin, aReadOnly) {
|
switch (data.operation) {
|
||||||
this.appId = aAppId;
|
case REVISION_ADDED:
|
||||||
this.name = aName;
|
changes.addedIds[data.objectId] = true;
|
||||||
this.origin = aOrigin;
|
break;
|
||||||
this.readOnly = aReadOnly;
|
|
||||||
|
case REVISION_UPDATED:
|
||||||
|
// We don't consider an update if this object has been added
|
||||||
|
// or if it has been already modified by a previous
|
||||||
|
// operation.
|
||||||
|
if (!(data.objectId in changes.addedIds) &&
|
||||||
|
!(data.objectId in changes.updatedIds)) {
|
||||||
|
changes.updatedIds[data.objectId] = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case REVISION_REMOVED:
|
||||||
|
let id = data.objectId;
|
||||||
|
|
||||||
|
// If the object has been added in this range of revisions
|
||||||
|
// we can ignore it and remove it from the list.
|
||||||
|
if (id in changes.addedIds) {
|
||||||
|
delete changes.addedIds[id];
|
||||||
|
} else {
|
||||||
|
changes.removedIds[id] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
DataStoreAccess.prototype = {};
|
if (id in changes.updatedIds) {
|
||||||
|
delete changes.updatedIds[id];
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The last revisionId.
|
||||||
|
if (aEvent.target.result.length) {
|
||||||
|
changes.revisionId = aEvent.target.result[aEvent.target.result.length - 1].revisionId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// From maps to arrays.
|
||||||
|
changes.addedIds = Object.keys(changes.addedIds).map(function(aKey) { return parseInt(aKey, 10); });
|
||||||
|
changes.updatedIds = Object.keys(changes.updatedIds).map(function(aKey) { return parseInt(aKey, 10); });
|
||||||
|
changes.removedIds = Object.keys(changes.removedIds).map(function(aKey) { return parseInt(aKey, 10); });
|
||||||
|
|
||||||
|
let wrappedObject = ObjectWrapper.wrap(changes, self._window);
|
||||||
|
aResolve(wrappedObject);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
function(aEvent) {
|
||||||
|
debug("GetChanges transaction failed");
|
||||||
|
aReject(createDOMError(self._window, aEvent));
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
getLength: function() {
|
||||||
|
let self = this;
|
||||||
|
|
||||||
|
// Promise<int>
|
||||||
|
return this.newDBPromise("readonly",
|
||||||
|
function(aResolve, aReject, aTxn, aStore, aRevisionStore) {
|
||||||
|
self.getLengthInternal(aResolve, aStore);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
set onchange(aCallback) {
|
||||||
|
debug("Set OnChange");
|
||||||
|
this.__DOM_IMPL__.setEventHandler("onchange", aCallback);
|
||||||
|
},
|
||||||
|
|
||||||
|
get onchange() {
|
||||||
|
debug("Get OnChange");
|
||||||
|
return this.__DOM_IMPL__.getEventHandler("onchange");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
@ -15,8 +15,6 @@ function debug(s) {
|
|||||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
Cu.import("resource://gre/modules/Services.jsm");
|
Cu.import("resource://gre/modules/Services.jsm");
|
||||||
|
|
||||||
const kFromDataStoreChangeNotifier = "fromDataStoreChangeNotifier";
|
|
||||||
|
|
||||||
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
|
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
|
||||||
"@mozilla.org/parentprocessmessagemanager;1",
|
"@mozilla.org/parentprocessmessagemanager;1",
|
||||||
"nsIMessageBroadcaster");
|
"nsIMessageBroadcaster");
|
||||||
|
@ -17,6 +17,15 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
|||||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||||
Cu.import('resource://gre/modules/Services.jsm');
|
Cu.import('resource://gre/modules/Services.jsm');
|
||||||
Cu.import('resource://gre/modules/DataStore.jsm');
|
Cu.import('resource://gre/modules/DataStore.jsm');
|
||||||
|
Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
||||||
|
"@mozilla.org/childprocessmessagemanager;1",
|
||||||
|
"nsIMessageSender");
|
||||||
|
|
||||||
|
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
|
||||||
|
"@mozilla.org/parentprocessmessagemanager;1",
|
||||||
|
"nsIMessageBroadcaster");
|
||||||
|
|
||||||
/* DataStoreService */
|
/* DataStoreService */
|
||||||
|
|
||||||
@ -32,6 +41,12 @@ function DataStoreService() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
obs.addObserver(this, 'webapps-clear-data', false);
|
obs.addObserver(this, 'webapps-clear-data', false);
|
||||||
|
|
||||||
|
let inParent = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime)
|
||||||
|
.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
|
||||||
|
if (inParent) {
|
||||||
|
ppmm.addMessageListener("DataStore:Get", this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DataStoreService.prototype = {
|
DataStoreService.prototype = {
|
||||||
@ -49,13 +64,11 @@ DataStoreService.prototype = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let store = new DataStore(aAppId, aName, aOwner, aReadOnly);
|
|
||||||
|
|
||||||
if (!(aName in this.stores)) {
|
if (!(aName in this.stores)) {
|
||||||
this.stores[aName] = {};
|
this.stores[aName] = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
this.stores[aName][aAppId] = store;
|
this.stores[aName][aAppId] = { owner: aOwner, readOnly: aReadOnly };
|
||||||
},
|
},
|
||||||
|
|
||||||
installAccessDataStore: function(aAppId, aName, aOwner, aReadOnly) {
|
installAccessDataStore: function(aAppId, aName, aOwner, aReadOnly) {
|
||||||
@ -68,80 +81,69 @@ DataStoreService.prototype = {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let accessStore = new DataStoreAccess(aAppId, aName, aOwner, aReadOnly);
|
|
||||||
|
|
||||||
if (!(aName in this.accessStores)) {
|
if (!(aName in this.accessStores)) {
|
||||||
this.accessStores[aName] = {};
|
this.accessStores[aName] = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
this.accessStores[aName][aAppId] = accessStore;
|
this.accessStores[aName][aAppId] = { owner: aOwner, readOnly: aReadOnly };
|
||||||
},
|
},
|
||||||
|
|
||||||
getDataStores: function(aWindow, aName) {
|
getDataStores: function(aWindow, aName) {
|
||||||
debug('getDataStores - aName: ' + aName);
|
debug('getDataStores - aName: ' + aName);
|
||||||
let appId = aWindow.document.nodePrincipal.appId;
|
|
||||||
|
|
||||||
let self = this;
|
// This method can be called in the child so we need to send a request to
|
||||||
|
// the parent and create DataStore object here.
|
||||||
|
|
||||||
return new aWindow.Promise(function(resolve, reject) {
|
return new aWindow.Promise(function(resolve, reject) {
|
||||||
let matchingStores = [];
|
new DataStoreServiceChild(aWindow, aName, resolve);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
if (aName in self.stores) {
|
receiveMessage: function(aMessage) {
|
||||||
if (appId in self.stores[aName]) {
|
if (aMessage.name != 'DataStore:Get') {
|
||||||
matchingStores.push({ store: self.stores[aName][appId],
|
return;
|
||||||
readonly: false });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i in self.stores[aName]) {
|
let msg = aMessage.data;
|
||||||
|
|
||||||
|
// This is a security issue and it will be fixed by Bug 916091
|
||||||
|
let appId = msg.appId;
|
||||||
|
|
||||||
|
let results = [];
|
||||||
|
|
||||||
|
if (msg.name in this.stores) {
|
||||||
|
if (appId in this.stores[msg.name]) {
|
||||||
|
results.push({ store: this.stores[msg.name][appId],
|
||||||
|
readOnly: false });
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i in this.stores[msg.name]) {
|
||||||
if (i == appId) {
|
if (i == appId) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let access = self.getDataStoreAccess(self.stores[aName][i], appId);
|
let access = this.getDataStoreAccess(msg.name, appId);
|
||||||
if (!access) {
|
if (!access) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let readOnly = self.stores[aName][i].readOnly || access.readOnly;
|
let readOnly = this.stores[msg.name][i].readOnly || access.readOnly;
|
||||||
matchingStores.push({ store: self.stores[aName][i],
|
results.push({ store: this.stores[msg.name][i],
|
||||||
readonly: readOnly });
|
readOnly: readOnly });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let callbackPending = matchingStores.length;
|
msg.stores = results;
|
||||||
let results = [];
|
aMessage.target.sendAsyncMessage("DataStore:Get:Return", msg);
|
||||||
|
|
||||||
if (!callbackPending) {
|
|
||||||
resolve(results);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < matchingStores.length; ++i) {
|
|
||||||
let obj = matchingStores[i].store.exposeObject(aWindow,
|
|
||||||
matchingStores[i].readonly);
|
|
||||||
results.push(obj);
|
|
||||||
|
|
||||||
matchingStores[i].store.retrieveRevisionId(
|
|
||||||
function() {
|
|
||||||
--callbackPending;
|
|
||||||
if (!callbackPending) {
|
|
||||||
resolve(results);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// if the revision is already known, we don't need to retrieve it
|
|
||||||
// again.
|
|
||||||
false
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
getDataStoreAccess: function(aStore, aAppId) {
|
getDataStoreAccess: function(aName, aAppId) {
|
||||||
if (!(aStore.name in this.accessStores) ||
|
if (!(aName in this.accessStores) ||
|
||||||
!(aAppId in this.accessStores[aStore.name])) {
|
!(aAppId in this.accessStores[aName])) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.accessStores[aStore.name][aAppId];
|
return this.accessStores[aName][aAppId];
|
||||||
},
|
},
|
||||||
|
|
||||||
observe: function observe(aSubject, aTopic, aData) {
|
observe: function observe(aSubject, aTopic, aData) {
|
||||||
@ -160,7 +162,6 @@ DataStoreService.prototype = {
|
|||||||
|
|
||||||
for (let key in this.stores) {
|
for (let key in this.stores) {
|
||||||
if (params.appId in this.stores[key]) {
|
if (params.appId in this.stores[key]) {
|
||||||
this.stores[key][params.appId].delete();
|
|
||||||
delete this.stores[key][params.appId];
|
delete this.stores[key][params.appId];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,4 +182,59 @@ DataStoreService.prototype = {
|
|||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* DataStoreServiceChild */
|
||||||
|
|
||||||
|
function DataStoreServiceChild(aWindow, aName, aResolve) {
|
||||||
|
debug("DataStoreServiceChild created");
|
||||||
|
this.init(aWindow, aName, aResolve);
|
||||||
|
}
|
||||||
|
|
||||||
|
DataStoreServiceChild.prototype = {
|
||||||
|
__proto__: DOMRequestIpcHelper.prototype,
|
||||||
|
|
||||||
|
init: function(aWindow, aName, aResolve) {
|
||||||
|
this._window = aWindow;
|
||||||
|
this._name = aName;
|
||||||
|
this._resolve = aResolve;
|
||||||
|
|
||||||
|
this.initDOMRequestHelper(aWindow, [ "DataStore:Get:Return" ]);
|
||||||
|
|
||||||
|
// This is a security issue and it will be fixed by Bug 916091
|
||||||
|
cpmm.sendAsyncMessage("DataStore:Get",
|
||||||
|
{ name: aName, appId: aWindow.document.nodePrincipal.appId });
|
||||||
|
},
|
||||||
|
|
||||||
|
receiveMessage: function(aMessage) {
|
||||||
|
if (aMessage.name != 'DataStore:Get:Return') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let msg = aMessage.data;
|
||||||
|
let self = this;
|
||||||
|
|
||||||
|
let callbackPending = msg.stores.length;
|
||||||
|
let results = [];
|
||||||
|
|
||||||
|
if (!callbackPending) {
|
||||||
|
this._resolve(results);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < msg.stores.length; ++i) {
|
||||||
|
let obj = new DataStore(this._window, this._name,
|
||||||
|
msg.stores[i].owner, msg.stores[i].readOnly);
|
||||||
|
let exposedObj = this._window.DataStore._create(this._window, obj);
|
||||||
|
results.push(exposedObj);
|
||||||
|
|
||||||
|
obj.retrieveRevisionId(
|
||||||
|
function() {
|
||||||
|
--callbackPending;
|
||||||
|
if (!callbackPending) {
|
||||||
|
self._resolve(results);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DataStoreService]);
|
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([DataStoreService]);
|
||||||
|
@ -28,6 +28,7 @@ MOCHITEST_FILES = \
|
|||||||
file_app2.template.webapp \
|
file_app2.template.webapp \
|
||||||
test_arrays.html \
|
test_arrays.html \
|
||||||
file_arrays.html \
|
file_arrays.html \
|
||||||
|
test_oop.html \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
include $(topsrcdir)/config/rules.mk
|
include $(topsrcdir)/config/rules.mk
|
||||||
|
129
dom/datastore/tests/test_oop.html
Normal file
129
dom/datastore/tests/test_oop.html
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Test for DataStore - basic operation on a readonly db</title>
|
||||||
|
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="container"></div>
|
||||||
|
<script type="application/javascript;version=1.7">
|
||||||
|
|
||||||
|
var gHostedManifestURL = 'http://test/tests/dom/datastore/tests/file_app.sjs?testToken=file_basic.html';
|
||||||
|
var gApp;
|
||||||
|
|
||||||
|
function cbError() {
|
||||||
|
ok(false, "Error callback invoked");
|
||||||
|
finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
function installApp() {
|
||||||
|
var request = navigator.mozApps.install(gHostedManifestURL);
|
||||||
|
request.onerror = cbError;
|
||||||
|
request.onsuccess = function() {
|
||||||
|
gApp = request.result;
|
||||||
|
runTest();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function uninstallApp() {
|
||||||
|
// Uninstall the app.
|
||||||
|
var request = navigator.mozApps.mgmt.uninstall(gApp);
|
||||||
|
request.onerror = cbError;
|
||||||
|
request.onsuccess = function() {
|
||||||
|
// All done.
|
||||||
|
info("All done");
|
||||||
|
runTest();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function testApp() {
|
||||||
|
var ifr = document.createElement('iframe');
|
||||||
|
ifr.setAttribute('mozbrowser', 'true');
|
||||||
|
ifr.setAttribute('mozapp', gApp.manifestURL);
|
||||||
|
ifr.setAttribute('src', gApp.manifest.launch_path);
|
||||||
|
var domParent = document.getElementById('container');
|
||||||
|
|
||||||
|
// Set us up to listen for messages from the app.
|
||||||
|
var listener = function(e) {
|
||||||
|
var message = e.detail.message;
|
||||||
|
if (/^OK/.exec(message)) {
|
||||||
|
ok(true, "Message from app: " + message);
|
||||||
|
} else if (/KO/.exec(message)) {
|
||||||
|
ok(false, "Message from app: " + message);
|
||||||
|
} else if (/DONE/.exec(message)) {
|
||||||
|
ok(true, "Messaging from app complete");
|
||||||
|
ifr.removeEventListener('mozbrowsershowmodalprompt', listener);
|
||||||
|
domParent.removeChild(ifr);
|
||||||
|
runTest();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This event is triggered when the app calls "alert".
|
||||||
|
ifr.addEventListener('mozbrowsershowmodalprompt', listener, false);
|
||||||
|
domParent.appendChild(ifr);
|
||||||
|
}
|
||||||
|
|
||||||
|
var tests = [
|
||||||
|
// Permissions
|
||||||
|
function() {
|
||||||
|
SpecialPowers.pushPermissions(
|
||||||
|
[{ "type": "browser", "allow": 1, "context": document },
|
||||||
|
{ "type": "embed-apps", "allow": 1, "context": document },
|
||||||
|
{ "type": "webapps-manage", "allow": 1, "context": document }], runTest);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Preferences
|
||||||
|
function() {
|
||||||
|
SpecialPowers.pushPrefEnv({"set": [["dom.promise.enabled", true]]}, runTest);
|
||||||
|
},
|
||||||
|
|
||||||
|
function() {
|
||||||
|
SpecialPowers.pushPrefEnv({"set": [["dom.datastore.enabled", true]]}, runTest);
|
||||||
|
},
|
||||||
|
|
||||||
|
function() {
|
||||||
|
SpecialPowers.pushPrefEnv({"set": [["dom.ipc.browser_frames.oop_by_default", true]]}, runTest);
|
||||||
|
},
|
||||||
|
|
||||||
|
function() {
|
||||||
|
SpecialPowers.setBoolPref("dom.mozBrowserFramesEnabled", true);
|
||||||
|
runTest();
|
||||||
|
},
|
||||||
|
|
||||||
|
// No confirmation needed when an app is installed
|
||||||
|
function() {
|
||||||
|
SpecialPowers.autoConfirmAppInstall(runTest);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Installing the app
|
||||||
|
installApp,
|
||||||
|
|
||||||
|
// Run tests in app
|
||||||
|
testApp,
|
||||||
|
|
||||||
|
// Uninstall the app
|
||||||
|
uninstallApp
|
||||||
|
];
|
||||||
|
|
||||||
|
function runTest() {
|
||||||
|
if (!tests.length) {
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var test = tests.shift();
|
||||||
|
test();
|
||||||
|
}
|
||||||
|
|
||||||
|
function finish() {
|
||||||
|
SimpleTest.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
SpecialPowers.Cu.import("resource://gre/modules/DataStoreChangeNotifier.jsm");
|
||||||
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
runTest();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in New Issue
Block a user