зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to b-i
This commit is contained in:
Коммит
95e52e8429
|
@ -681,6 +681,7 @@ static const EStateRule sWAIUnivStateMap[] = {
|
|||
eARIAExpanded, // Currently under spec review but precedent exists
|
||||
eARIAHasPopup, // Note this is technically a "property"
|
||||
eARIAInvalid,
|
||||
eARIAModal,
|
||||
eARIARequired, // XXX not global, Bug 553117
|
||||
eARIANone
|
||||
};
|
||||
|
@ -716,6 +717,7 @@ static const AttrCharacteristics gWAIUnivAttrMap[] = {
|
|||
{&nsGkAtoms::aria_labelledby, ATTR_BYPASSOBJ | ATTR_GLOBAL },
|
||||
{&nsGkAtoms::aria_level, ATTR_BYPASSOBJ }, /* handled via groupPosition */
|
||||
{&nsGkAtoms::aria_live, ATTR_VALTOKEN | ATTR_GLOBAL },
|
||||
{&nsGkAtoms::aria_modal, ATTR_BYPASSOBJ | ATTR_VALTOKEN | ATTR_GLOBAL },
|
||||
{&nsGkAtoms::aria_multiline, ATTR_BYPASSOBJ | ATTR_VALTOKEN },
|
||||
{&nsGkAtoms::aria_multiselectable, ATTR_BYPASSOBJ | ATTR_VALTOKEN },
|
||||
{&nsGkAtoms::aria_owns, ATTR_BYPASSOBJ | ATTR_GLOBAL },
|
||||
|
|
|
@ -228,15 +228,15 @@ uint64_t UniversalStatesFor(mozilla::dom::Element* aElement);
|
|||
uint8_t AttrCharacteristicsFor(nsIAtom* aAtom);
|
||||
|
||||
/**
|
||||
* Represents a simple enumerator for iterating through ARIA attributes
|
||||
* exposed as object attributes on a given accessible.
|
||||
* Represents a simple enumerator for iterating through ARIA attributes
|
||||
* exposed as object attributes on a given accessible.
|
||||
*/
|
||||
class AttrIterator
|
||||
{
|
||||
public:
|
||||
explicit AttrIterator(nsIContent* aContent) :
|
||||
mContent(aContent), mAttrIdx(0)
|
||||
{
|
||||
explicit AttrIterator(nsIContent* aContent) :
|
||||
mContent(aContent), mAttrIdx(0)
|
||||
{
|
||||
mAttrCount = mContent->GetAttrCount();
|
||||
}
|
||||
|
||||
|
|
|
@ -185,6 +185,16 @@ aria::MapToState(EStateRule aRule, dom::Element* aElement, uint64_t* aState)
|
|||
return true;
|
||||
}
|
||||
|
||||
case eARIAModal:
|
||||
{
|
||||
static const TokenTypeData data(
|
||||
nsGkAtoms::aria_modal, eBoolType,
|
||||
0, states::MODAL);
|
||||
|
||||
MapTokenType(aElement, aState, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
case eARIAMultiline:
|
||||
{
|
||||
static const TokenTypeData data(
|
||||
|
|
|
@ -33,6 +33,7 @@ enum EStateRule
|
|||
eARIAExpanded,
|
||||
eARIAHasPopup,
|
||||
eARIAInvalid,
|
||||
eARIAModal,
|
||||
eARIAMultiline,
|
||||
eARIAMultiSelectable,
|
||||
eARIAOrientation,
|
||||
|
|
|
@ -28,16 +28,18 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=558036
|
|||
testAbsentAttrs(getNode("atomic_false").firstChild, {"container-atomic" : "false"});
|
||||
|
||||
testAttrs("autocomplete", {"autocomplete" : "true"}, true);
|
||||
testAttrs("checkbox", {"checkable" : "true"}, true);
|
||||
testAttrs("checkedCheckbox", {"checkable" : "true"}, true);
|
||||
testAttrs("checkedMenuitem", {"checkable" : "true"}, true);
|
||||
testAttrs("checkedOption", {"checkable" : "true"}, true);
|
||||
testAttrs("checkedRadio", {"checkable" : "true"}, true);
|
||||
testAttrs("checkedTreeitem", {"checkable" : "true"}, true);
|
||||
testAttrs("checkbox", {"checkable" : "true"}, true);
|
||||
testAttrs("checkedCheckbox", {"checkable" : "true"}, true);
|
||||
testAttrs("checkedMenuitem", {"checkable" : "true"}, true);
|
||||
testAttrs("checkedOption", {"checkable" : "true"}, true);
|
||||
testAttrs("checkedRadio", {"checkable" : "true"}, true);
|
||||
testAttrs("checkedTreeitem", {"checkable" : "true"}, true);
|
||||
testAttrs("dropeffect", {"dropeffect" : "copy"}, true);
|
||||
testAttrs("grabbed", {"grabbed" : "true"}, true);
|
||||
testAbsentAttrs("haspopup", { "haspopup": "false" });
|
||||
testAttrs("hidden", {"hidden" : "true"}, true);
|
||||
testAbsentAttrs("hidden_false", { "hidden": "false" });
|
||||
testAbsentAttrs("modal", {"modal" : "true"});
|
||||
testAttrs("sortAscending", {"sort" : "ascending"}, true);
|
||||
testAttrs("sortDescending", {"sort" : "descending"}, true);
|
||||
testAttrs("sortNone", {"sort" : "none"}, true);
|
||||
|
@ -198,8 +200,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=558036
|
|||
<div id="checkedTreeitem" role="treeitem" aria-checked="true"></div>
|
||||
<div id="dropeffect" aria-dropeffect="copy"></div>
|
||||
<div id="grabbed" aria-grabbed="true"></div>
|
||||
<div id="haspopup" aria-haspopup="true"></div>
|
||||
<div id="hidden" aria-hidden="true"></div>
|
||||
<div id="hidden_false" aria-hidden="false"></div>
|
||||
<div id="modal" aria-modal="true"></div>
|
||||
<div id="sortAscending" role="columnheader" aria-sort="ascending"></div>
|
||||
<div id="sortDescending" role="columnheader" aria-sort="descending"></div>
|
||||
<div id="sortNone" role="columnheader" aria-sort="none"></div>
|
||||
|
|
|
@ -42,6 +42,7 @@ const EXT_STATE_EDITABLE = nsIAccessibleStates.EXT_STATE_EDITABLE;
|
|||
const EXT_STATE_ENABLED = nsIAccessibleStates.EXT_STATE_ENABLED;
|
||||
const EXT_STATE_EXPANDABLE = nsIAccessibleStates.EXT_STATE_EXPANDABLE;
|
||||
const EXT_STATE_HORIZONTAL = nsIAccessibleStates.EXT_STATE_HORIZONTAL;
|
||||
const EXT_STATE_MODAL = nsIAccessibleStates.EXT_STATE_MODAL;
|
||||
const EXT_STATE_MULTI_LINE = nsIAccessibleStates.EXT_STATE_MULTI_LINE;
|
||||
const EXT_STATE_PINNED = nsIAccessibleStates.EXT_STATE_PINNED;
|
||||
const EXT_STATE_SENSITIVE = nsIAccessibleStates.EXT_STATE_SENSITIVE;
|
||||
|
|
|
@ -97,6 +97,10 @@
|
|||
// disabled, too. See bug 429285.
|
||||
testAriaDisabledTree("group");
|
||||
|
||||
// aria-modal
|
||||
testStates("aria_modal", 0, EXT_STATE_MODAL);
|
||||
testStates("aria_modal_false", 0, 0, 0, EXT_STATE_MODAL);
|
||||
|
||||
// aria-multiline
|
||||
testStates("aria_multiline_textbox", 0, EXT_STATE_MULTI_LINE);
|
||||
|
||||
|
@ -379,6 +383,8 @@
|
|||
<div id="aria_mixed_checkbox" role="checkbox" aria-checked="mixed">
|
||||
I might agree
|
||||
</div>
|
||||
<div id="aria_modal" aria-modal="true">modal stuff</div>
|
||||
<div id="aria_modal_false" aria-modal="false">non modal stuff</div>div>
|
||||
<div id="aria_multiline_textbox" role="textbox" aria-multiline="true"></div>
|
||||
<div id="aria_multiselectable_listbox" role="listbox" aria-multiselectable="true"></div>
|
||||
<div id="aria_pressed_button" role="button" aria-pressed="true">Button</div>
|
||||
|
|
|
@ -36,10 +36,42 @@ let principal = Cc["@mozilla.org/scriptsecuritymanager;1"].
|
|||
getService(Ci.nsIScriptSecurityManager).
|
||||
getCodebasePrincipal(principaluri);
|
||||
|
||||
function toArray(args) {
|
||||
return Array.prototype.slice.call(args);
|
||||
}
|
||||
|
||||
function openInternal(args, forPrincipal, deleting) {
|
||||
if (forPrincipal) {
|
||||
args = toArray(args);
|
||||
} else {
|
||||
args = [principal].concat(toArray(args));
|
||||
}
|
||||
if (args.length == 2) {
|
||||
args.push({ storage: "persistent" });
|
||||
} else if (!deleting && args.length >= 3 && typeof args[2] === "number") {
|
||||
args[2] = { version: args[2], storage: "persistent" };
|
||||
}
|
||||
|
||||
if (deleting) {
|
||||
return indexedDB.deleteForPrincipal.apply(indexedDB, args);
|
||||
}
|
||||
|
||||
return indexedDB.openForPrincipal.apply(indexedDB, args);
|
||||
}
|
||||
|
||||
exports.indexedDB = Object.freeze({
|
||||
open: indexedDB.openForPrincipal.bind(indexedDB, principal),
|
||||
openForPrincipal: indexedDB.openForPrincipal.bind(indexedDB),
|
||||
deleteDatabase: indexedDB.deleteForPrincipal.bind(indexedDB, principal),
|
||||
open: function () {
|
||||
return openInternal(arguments, false, false);
|
||||
},
|
||||
deleteDatabase: function () {
|
||||
return openInternal(arguments, false, true);
|
||||
},
|
||||
openForPrincipal: function () {
|
||||
return openInternal(arguments, true, false);
|
||||
},
|
||||
deleteForPrincipal: function () {
|
||||
return openInternal(arguments, true, true);
|
||||
},
|
||||
cmp: indexedDB.cmp.bind(indexedDB)
|
||||
});
|
||||
|
||||
|
|
|
@ -28,16 +28,82 @@ exports["test db variables"] = function(assert) {
|
|||
}
|
||||
|
||||
exports["test open"] = function(assert, done) {
|
||||
let request = indexedDB.open("MyTestDatabase");
|
||||
testOpen(0, assert, done);
|
||||
}
|
||||
|
||||
function testOpen(step, assert, done) {
|
||||
const dbName = "MyTestDatabase";
|
||||
const openParams = [
|
||||
{ dbName: "MyTestDatabase", dbVersion: 10 },
|
||||
{ dbName: "MyTestDatabase" },
|
||||
{ dbName: "MyTestDatabase", dbOptions: { storage: "temporary" } },
|
||||
{ dbName: "MyTestDatabase", dbOptions: { version: 20, storage: "default" } }
|
||||
];
|
||||
|
||||
let params = openParams[step];
|
||||
|
||||
let request;
|
||||
let expectedStorage;
|
||||
let expectedVersion;
|
||||
let upgradeNeededCalled = false;
|
||||
if ("dbVersion" in params) {
|
||||
request = indexedDB.open(params.dbName, params.dbVersion);
|
||||
expectedVersion = params.dbVersion;
|
||||
expectedStorage = "persistent";
|
||||
} else if ("dbOptions" in params) {
|
||||
request = indexedDB.open(params.dbName, params.dbOptions);
|
||||
if ("version" in params.dbOptions) {
|
||||
expectedVersion = params.dbOptions.version;
|
||||
} else {
|
||||
expectedVersion = 1;
|
||||
}
|
||||
if ("storage" in params.dbOptions) {
|
||||
expectedStorage = params.dbOptions.storage;
|
||||
} else {
|
||||
expectedStorage = "persistent";
|
||||
}
|
||||
} else {
|
||||
request = indexedDB.open(params.dbName);
|
||||
expectedVersion = 1;
|
||||
expectedStorage = "persistent";
|
||||
}
|
||||
request.onerror = function(event) {
|
||||
assert.fail("Failed to open indexedDB")
|
||||
done();
|
||||
};
|
||||
}
|
||||
request.onupgradeneeded = function(event) {
|
||||
upgradeNeededCalled = true;
|
||||
assert.equal(event.oldVersion, 0, "Correct old version");
|
||||
}
|
||||
request.onsuccess = function(event) {
|
||||
assert.pass("IndexedDB was open");
|
||||
done();
|
||||
};
|
||||
};
|
||||
assert.equal(upgradeNeededCalled, true, "Upgrade needed called");
|
||||
let db = request.result;
|
||||
assert.equal(db.storage, expectedStorage, "Storage is correct");
|
||||
db.onversionchange = function(event) {
|
||||
assert.equal(event.oldVersion, expectedVersion, "Old version is correct");
|
||||
db.close();
|
||||
}
|
||||
if ("dbOptions" in params) {
|
||||
request = indexedDB.deleteDatabase(params.dbName, params.dbOptions);
|
||||
} else {
|
||||
request = indexedDB.deleteDatabase(params.dbName);
|
||||
}
|
||||
request.onerror = function(event) {
|
||||
assert.fail("Failed to delete indexedDB")
|
||||
done();
|
||||
}
|
||||
request.onsuccess = function(event) {
|
||||
assert.pass("IndexedDB was deleted");
|
||||
|
||||
if (++step == openParams.length) {
|
||||
done();
|
||||
} else {
|
||||
testOpen(step, assert, done);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exports["test dbname is unprefixed"] = function(assert, done) {
|
||||
// verify fixes in https://bugzilla.mozilla.org/show_bug.cgi?id=786688
|
||||
|
|
|
@ -329,7 +329,7 @@ var shell = {
|
|||
window.addEventListener('sizemodechange', this);
|
||||
window.addEventListener('unload', this);
|
||||
this.contentBrowser.addEventListener('mozbrowserloadstart', this, true);
|
||||
this.contentBrowser.addEventListener('mozbrowserselectionchange', this, true);
|
||||
this.contentBrowser.addEventListener('mozbrowserselectionstatechanged', this, true);
|
||||
this.contentBrowser.addEventListener('mozbrowserscrollviewchange', this, true);
|
||||
this.contentBrowser.addEventListener('mozbrowsertouchcarettap', this, true);
|
||||
|
||||
|
@ -357,7 +357,7 @@ var shell = {
|
|||
window.removeEventListener('mozfullscreenchange', this);
|
||||
window.removeEventListener('sizemodechange', this);
|
||||
this.contentBrowser.removeEventListener('mozbrowserloadstart', this, true);
|
||||
this.contentBrowser.removeEventListener('mozbrowserselectionchange', this, true);
|
||||
this.contentBrowser.removeEventListener('mozbrowserselectionstatechanged', this, true);
|
||||
this.contentBrowser.removeEventListener('mozbrowserscrollviewchange', this, true);
|
||||
this.contentBrowser.removeEventListener('mozbrowsertouchcarettap', this, true);
|
||||
ppmm.removeMessageListener("content-handler", this);
|
||||
|
@ -505,8 +505,8 @@ var shell = {
|
|||
detail: evt.detail,
|
||||
});
|
||||
break;
|
||||
case 'mozbrowserselectionchange':
|
||||
// The mozbrowserselectionchange event, may have crossed the chrome-content boundary.
|
||||
case 'mozbrowserselectionstatechanged':
|
||||
// The mozbrowserselectionstatechanged event, may have crossed the chrome-content boundary.
|
||||
// This event always dispatch to shell.js. But the offset we got from this event is
|
||||
// based on tab's coordinate. So get the actual offsets between shell and evt.target.
|
||||
let elt = evt.target;
|
||||
|
@ -524,7 +524,7 @@ var shell = {
|
|||
|
||||
DoCommandHelper.setEvent(evt);
|
||||
shell.sendChromeEvent({
|
||||
type: 'selectionchange',
|
||||
type: 'selectionstatechanged',
|
||||
detail: data,
|
||||
});
|
||||
break;
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
],
|
||||
"zip_files": [
|
||||
["{workdir}/out/target/product/flame/*.img", "out/target/product/flame/"],
|
||||
["{workdir}/out/target/product/flame/bootloader", "out/target/product/flame/"],
|
||||
["{workdir}/boot.img", "out/target/product/flame/"],
|
||||
"{workdir}/flash.sh",
|
||||
"{workdir}/load-config.sh",
|
||||
|
|
|
@ -1825,7 +1825,7 @@ pref("privacy.trackingprotection.ui.enabled", false);
|
|||
#endif
|
||||
|
||||
#ifdef NIGHTLY_BUILD
|
||||
pref("browser.tabs.remote.autostart.1", false);
|
||||
pref("browser.tabs.remote.autostart.1", true);
|
||||
#endif
|
||||
|
||||
// Temporary pref to allow printing in e10s windows on some platforms.
|
||||
|
|
|
@ -151,6 +151,7 @@ const SNIPPETS_UPDATE_INTERVAL_MS = 86400000; // 1 Day.
|
|||
// IndexedDB storage constants.
|
||||
const DATABASE_NAME = "abouthome";
|
||||
const DATABASE_VERSION = 1;
|
||||
const DATABASE_STORAGE = "persistent";
|
||||
const SNIPPETS_OBJECTSTORE_NAME = "snippets";
|
||||
|
||||
// This global tracks if the page has been set up before, to prevent double inits
|
||||
|
@ -224,7 +225,8 @@ function ensureSnippetsMapThen(aCallback)
|
|||
gSnippetsMapCallbacks.length = 0;
|
||||
}
|
||||
|
||||
let openRequest = indexedDB.open(DATABASE_NAME, DATABASE_VERSION);
|
||||
let openRequest = indexedDB.open(DATABASE_NAME, {version: DATABASE_VERSION,
|
||||
storage: DATABASE_STORAGE});
|
||||
|
||||
openRequest.onerror = function (event) {
|
||||
// Try to delete the old database so that we can start this process over
|
||||
|
|
|
@ -152,6 +152,9 @@ function onCheckboxClick(aPartId)
|
|||
var checkbox = document.getElementById(aPartId + "Def");
|
||||
if (checkbox.checked) {
|
||||
SitePermissions.remove(gPermURI, aPartId);
|
||||
if (aPartId == "indexedDB") {
|
||||
SitePermissions.remove(gPermURI, "indexedDB-unlimited");
|
||||
}
|
||||
command.setAttribute("disabled", "true");
|
||||
var perm = SitePermissions.getDefault(aPartId);
|
||||
setRadioState(aPartId, perm);
|
||||
|
@ -206,6 +209,7 @@ function onIndexedDBClear()
|
|||
.getService(nsIQuotaManager)
|
||||
.clearStoragesForURI(gPermURI);
|
||||
|
||||
SitePermissions.remove(gPermURI, "indexedDB");
|
||||
SitePermissions.remove(gPermURI, "indexedDB-unlimited");
|
||||
initIndexedDBRow();
|
||||
}
|
||||
|
|
|
@ -3310,7 +3310,11 @@
|
|||
|
||||
let timeoutPromise = new Promise((aResolve, aReject) => {
|
||||
timeoutId = setTimeout(() => {
|
||||
this._showBusySpinnerRemoteBrowser(toBrowser);
|
||||
if (toBrowser.isRemoteBrowser) {
|
||||
// The browser's remoteness could have changed since we
|
||||
// setTimeout so only show the spinner if it's still remote.
|
||||
this._showBusySpinnerRemoteBrowser(toBrowser);
|
||||
}
|
||||
attemptTabSwitch(aResolve, aReject);
|
||||
}, kTabSwitchTimeout);
|
||||
});
|
||||
|
|
|
@ -415,7 +415,7 @@ skip-if = buildapp == 'mulet' || e10s
|
|||
[browser_tabMatchesInAwesomebar.js]
|
||||
skip-if = e10s # Bug 1093206 - need to re-enable tests relying on swapFrameLoaders et al for e10s (test calls gBrowser.swapBrowsersAndCloseOther)
|
||||
[browser_tabMatchesInAwesomebar_perwindowpb.js]
|
||||
skip-if = e10s || true # Bug 1093373, Bug 1104755
|
||||
skip-if = e10s # Bug 1093373
|
||||
[browser_tab_drag_drop_perwindow.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_tab_dragdrop.js]
|
||||
|
|
|
@ -80,7 +80,8 @@ function test() {
|
|||
controller.startSearch(testURL);
|
||||
|
||||
// Wait for the Awesomebar popup to appear.
|
||||
promisePopupShown(aDestWindow.gURLBar.popup).then(() => {
|
||||
let popup = aDestWindow.gURLBar.popup;
|
||||
promisePopupShown(popup).then(() => {
|
||||
function searchIsComplete() {
|
||||
return controller.searchStatus ==
|
||||
Ci.nsIAutoCompleteController.STATUS_COMPLETE_MATCH;
|
||||
|
@ -106,8 +107,8 @@ function test() {
|
|||
}, true);
|
||||
}
|
||||
|
||||
// Select the second match, if any.
|
||||
if (controller.matchCount > 1) {
|
||||
// Make sure the last match is selected.
|
||||
while (popup.selectedIndex < controller.matchCount - 1) {
|
||||
controller.handleKeyNavigation(KeyEvent.DOM_VK_DOWN);
|
||||
}
|
||||
|
||||
|
|
|
@ -365,21 +365,31 @@ function injectLoopAPI(targetWindow) {
|
|||
/**
|
||||
* Displays a confirmation dialog using the specified strings.
|
||||
*
|
||||
* Callback parameters:
|
||||
* - err null on success, non-null on unexpected failure to show the prompt.
|
||||
* - {Boolean} True if the user chose the OK button.
|
||||
* @param {Object} options Confirm dialog options
|
||||
* @param {Function} callback Function that will be invoked once the operation
|
||||
* finished. The first argument passed will be an
|
||||
* `Error` object or `null`. The second argument
|
||||
* will be the result of the operation, TRUE if
|
||||
* the user chose the OK button.
|
||||
*/
|
||||
confirm: {
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: function(bodyMessage, okButtonMessage, cancelButtonMessage, callback) {
|
||||
try {
|
||||
let buttonFlags =
|
||||
value: function(options, callback) {
|
||||
let buttonFlags;
|
||||
if (options.okButton && options.cancelButton) {
|
||||
buttonFlags =
|
||||
(Ci.nsIPrompt.BUTTON_POS_0 * Ci.nsIPrompt.BUTTON_TITLE_IS_STRING) +
|
||||
(Ci.nsIPrompt.BUTTON_POS_1 * Ci.nsIPrompt.BUTTON_TITLE_IS_STRING);
|
||||
} else if (!options.okButton && !options.cancelButton) {
|
||||
buttonFlags = Services.prompt.STD_YES_NO_BUTTONS;
|
||||
} else {
|
||||
callback(cloneValueInto(new Error("confirm: missing button options"), targetWindow));
|
||||
}
|
||||
|
||||
try {
|
||||
let chosenButton = Services.prompt.confirmEx(null, "",
|
||||
bodyMessage, buttonFlags, okButtonMessage, cancelButtonMessage,
|
||||
options.message, buttonFlags, options.okButton, options.cancelButton,
|
||||
null, null, {});
|
||||
|
||||
callback(null, chosenButton == 0);
|
||||
|
@ -707,6 +717,24 @@ function injectLoopAPI(targetWindow) {
|
|||
|
||||
request.send();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Associates a session-id and a call-id with a window for debugging.
|
||||
*
|
||||
* @param {string} windowId The window id.
|
||||
* @param {string} sessionId OT session id.
|
||||
* @param {string} callId The callId on the server.
|
||||
*/
|
||||
addConversationContext: {
|
||||
enumerable: true,
|
||||
writable: true,
|
||||
value: function(windowId, sessionId, callid) {
|
||||
MozLoopService.addConversationContext(windowId, {
|
||||
sessionId: sessionId,
|
||||
callId: callid
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -120,6 +120,8 @@ let gConversationWindowData = new Map();
|
|||
* and register with the Loop server.
|
||||
*/
|
||||
let MozLoopServiceInternal = {
|
||||
conversationContexts: new Map(),
|
||||
|
||||
mocks: {
|
||||
pushHandler: undefined,
|
||||
webSocket: undefined,
|
||||
|
@ -817,6 +819,22 @@ let MozLoopServiceInternal = {
|
|||
if (winID != ourID) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Chat Window Id, this is different that the internal winId
|
||||
let windowId = window.location.hash.slice(1);
|
||||
var context = this.conversationContexts.get(windowId);
|
||||
var exists = pc.id.match(/session=(\S+)/);
|
||||
if (context && !exists) {
|
||||
// Not ideal but insert our data amidst existing data like this:
|
||||
// - 000 (id=00 url=http)
|
||||
// + 000 (session=000 call=000 id=00 url=http)
|
||||
var pair = pc.id.split("("); //)
|
||||
if (pair.length == 2) {
|
||||
pc.id = pair[0] + "(session=" + context.sessionId +
|
||||
(context.callId? " call=" + context.callId : "") + " " + pair[1]; //)
|
||||
}
|
||||
}
|
||||
|
||||
if (type == "iceconnectionstatechange") {
|
||||
switch(pc.iceConnectionState) {
|
||||
case "failed":
|
||||
|
@ -1485,5 +1503,13 @@ this.MozLoopService = {
|
|||
|
||||
log.error("Window data was already fetched before. Possible race condition!");
|
||||
return null;
|
||||
},
|
||||
|
||||
getConversationContext: function(winId) {
|
||||
return MozLoopServiceInternal.conversationContexts.get(winId);
|
||||
},
|
||||
|
||||
addConversationContext: function(windowId, context) {
|
||||
MozLoopServiceInternal.conversationContexts.set(windowId, context);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -403,25 +403,25 @@ loop.contacts = (function(_, mozL10n) {
|
|||
this.props.startForm("contacts_edit", contact);
|
||||
break;
|
||||
case "remove":
|
||||
navigator.mozLoop.confirm(
|
||||
mozL10n.get("confirm_delete_contact_alert"),
|
||||
mozL10n.get("confirm_delete_contact_remove_button"),
|
||||
mozL10n.get("confirm_delete_contact_cancel_button"),
|
||||
(err, result) => {
|
||||
navigator.mozLoop.confirm({
|
||||
message: mozL10n.get("confirm_delete_contact_alert"),
|
||||
okButton: mozL10n.get("confirm_delete_contact_remove_button"),
|
||||
cancelButton: mozL10n.get("confirm_delete_contact_cancel_button")
|
||||
}, (err, result) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
navigator.mozLoop.contacts.remove(contact._guid, err => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
navigator.mozLoop.contacts.remove(contact._guid, err => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
break;
|
||||
case "block":
|
||||
case "unblock":
|
||||
|
|
|
@ -403,25 +403,25 @@ loop.contacts = (function(_, mozL10n) {
|
|||
this.props.startForm("contacts_edit", contact);
|
||||
break;
|
||||
case "remove":
|
||||
navigator.mozLoop.confirm(
|
||||
mozL10n.get("confirm_delete_contact_alert"),
|
||||
mozL10n.get("confirm_delete_contact_remove_button"),
|
||||
mozL10n.get("confirm_delete_contact_cancel_button"),
|
||||
(err, result) => {
|
||||
navigator.mozLoop.confirm({
|
||||
message: mozL10n.get("confirm_delete_contact_alert"),
|
||||
okButton: mozL10n.get("confirm_delete_contact_remove_button"),
|
||||
cancelButton: mozL10n.get("confirm_delete_contact_cancel_button")
|
||||
}, (err, result) => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
navigator.mozLoop.contacts.remove(contact._guid, err => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
navigator.mozLoop.contacts.remove(contact._guid, err => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
break;
|
||||
case "block":
|
||||
case "unblock":
|
||||
|
|
|
@ -675,10 +675,10 @@ loop.conversation = (function(mozL10n) {
|
|||
|
||||
// XXX Old class creation for the incoming conversation view, whilst
|
||||
// we transition across (bug 1072323).
|
||||
var conversation = new sharedModels.ConversationModel(
|
||||
{}, // Model attributes
|
||||
{sdk: window.OT} // Model dependencies
|
||||
);
|
||||
var conversation = new sharedModels.ConversationModel({}, {
|
||||
sdk: window.OT,
|
||||
mozLoop: navigator.mozLoop
|
||||
});
|
||||
|
||||
// Obtain the windowId and pass it through
|
||||
var helper = new loop.shared.utils.Helper();
|
||||
|
|
|
@ -675,10 +675,10 @@ loop.conversation = (function(mozL10n) {
|
|||
|
||||
// XXX Old class creation for the incoming conversation view, whilst
|
||||
// we transition across (bug 1072323).
|
||||
var conversation = new sharedModels.ConversationModel(
|
||||
{}, // Model attributes
|
||||
{sdk: window.OT} // Model dependencies
|
||||
);
|
||||
var conversation = new sharedModels.ConversationModel({}, {
|
||||
sdk: window.OT,
|
||||
mozLoop: navigator.mozLoop
|
||||
});
|
||||
|
||||
// Obtain the windowId and pass it through
|
||||
var helper = new loop.shared.utils.Helper();
|
||||
|
|
|
@ -521,6 +521,65 @@ loop.panel = (function(_, mozL10n) {
|
|||
}
|
||||
});
|
||||
|
||||
var EditInPlace = React.createClass({displayName: 'EditInPlace',
|
||||
mixins: [React.addons.LinkedStateMixin],
|
||||
|
||||
propTypes: {
|
||||
onChange: React.PropTypes.func.isRequired,
|
||||
text: React.PropTypes.string,
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {text: ""};
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {edit: false, text: this.props.text};
|
||||
},
|
||||
|
||||
handleTextClick: function(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
this.setState({edit: true}, function() {
|
||||
this.getDOMNode().querySelector("input").select();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
handleInputClick: function(event) {
|
||||
event.stopPropagation();
|
||||
},
|
||||
|
||||
handleFormSubmit: function(event) {
|
||||
event.preventDefault();
|
||||
this.props.onChange(this.state.text);
|
||||
this.setState({edit: false});
|
||||
},
|
||||
|
||||
cancelEdit: function(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
this.setState({edit: false, text: this.props.text});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (!this.state.edit) {
|
||||
return (
|
||||
React.DOM.span({className: "edit-in-place", onClick: this.handleTextClick,
|
||||
title: mozL10n.get("rooms_name_this_room_tooltip2")},
|
||||
this.state.text
|
||||
)
|
||||
);
|
||||
}
|
||||
return (
|
||||
React.DOM.form({onSubmit: this.handleFormSubmit},
|
||||
React.DOM.input({type: "text", valueLink: this.linkState("text"),
|
||||
onClick: this.handleInputClick,
|
||||
onBlur: this.cancelEdit})
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Room list entry.
|
||||
*/
|
||||
|
@ -539,7 +598,7 @@ loop.panel = (function(_, mozL10n) {
|
|||
(nextState.urlCopied !== this.state.urlCopied);
|
||||
},
|
||||
|
||||
handleClickRoomUrl: function(event) {
|
||||
handleClickEntry: function(event) {
|
||||
event.preventDefault();
|
||||
this.props.dispatcher.dispatch(new sharedActions.OpenRoom({
|
||||
roomToken: this.props.room.roomToken
|
||||
|
@ -547,6 +606,7 @@ loop.panel = (function(_, mozL10n) {
|
|||
},
|
||||
|
||||
handleCopyButtonClick: function(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
this.props.dispatcher.dispatch(new sharedActions.CopyRoomUrl({
|
||||
roomUrl: this.props.room.roomUrl
|
||||
|
@ -555,10 +615,31 @@ loop.panel = (function(_, mozL10n) {
|
|||
},
|
||||
|
||||
handleDeleteButtonClick: function(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
// XXX We should prompt end user for confirmation; see bug 1092953.
|
||||
this.props.dispatcher.dispatch(new sharedActions.DeleteRoom({
|
||||
roomToken: this.props.room.roomToken
|
||||
navigator.mozLoop.confirm({
|
||||
message: mozL10n.get("rooms_list_deleteConfirmation_label"),
|
||||
okButton: null,
|
||||
cancelButton: null
|
||||
}, function(err, result) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.dispatcher.dispatch(new sharedActions.DeleteRoom({
|
||||
roomToken: this.props.room.roomToken
|
||||
}));
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
renameRoom: function(newRoomName) {
|
||||
this.props.dispatcher.dispatch(new sharedActions.RenameRoom({
|
||||
roomToken: this.props.room.roomToken,
|
||||
newRoomName: newRoomName
|
||||
}));
|
||||
},
|
||||
|
||||
|
@ -582,10 +663,11 @@ loop.panel = (function(_, mozL10n) {
|
|||
});
|
||||
|
||||
return (
|
||||
React.DOM.div({className: roomClasses, onMouseLeave: this.handleMouseLeave},
|
||||
React.DOM.div({className: roomClasses, onMouseLeave: this.handleMouseLeave,
|
||||
onClick: this.handleClickEntry},
|
||||
React.DOM.h2(null,
|
||||
React.DOM.span({className: "room-notification"}),
|
||||
room.roomName,
|
||||
EditInPlace({text: room.roomName, onChange: this.renameRoom}),
|
||||
React.DOM.button({className: copyButtonClasses,
|
||||
title: mozL10n.get("rooms_list_copy_url_tooltip"),
|
||||
onClick: this.handleCopyButtonClick}),
|
||||
|
@ -593,11 +675,7 @@ loop.panel = (function(_, mozL10n) {
|
|||
title: mozL10n.get("rooms_list_delete_tooltip"),
|
||||
onClick: this.handleDeleteButtonClick})
|
||||
),
|
||||
React.DOM.p(null,
|
||||
React.DOM.a({href: "#", onClick: this.handleClickRoomUrl},
|
||||
room.roomUrl
|
||||
)
|
||||
)
|
||||
React.DOM.p(null, React.DOM.a({href: "#"}, room.roomUrl))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -521,6 +521,65 @@ loop.panel = (function(_, mozL10n) {
|
|||
}
|
||||
});
|
||||
|
||||
var EditInPlace = React.createClass({
|
||||
mixins: [React.addons.LinkedStateMixin],
|
||||
|
||||
propTypes: {
|
||||
onChange: React.PropTypes.func.isRequired,
|
||||
text: React.PropTypes.string,
|
||||
},
|
||||
|
||||
getDefaultProps: function() {
|
||||
return {text: ""};
|
||||
},
|
||||
|
||||
getInitialState: function() {
|
||||
return {edit: false, text: this.props.text};
|
||||
},
|
||||
|
||||
handleTextClick: function(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
this.setState({edit: true}, function() {
|
||||
this.getDOMNode().querySelector("input").select();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
handleInputClick: function(event) {
|
||||
event.stopPropagation();
|
||||
},
|
||||
|
||||
handleFormSubmit: function(event) {
|
||||
event.preventDefault();
|
||||
this.props.onChange(this.state.text);
|
||||
this.setState({edit: false});
|
||||
},
|
||||
|
||||
cancelEdit: function(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
this.setState({edit: false, text: this.props.text});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
if (!this.state.edit) {
|
||||
return (
|
||||
<span className="edit-in-place" onClick={this.handleTextClick}
|
||||
title={mozL10n.get("rooms_name_this_room_tooltip2")}>
|
||||
{this.state.text}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<form onSubmit={this.handleFormSubmit}>
|
||||
<input type="text" valueLink={this.linkState("text")}
|
||||
onClick={this.handleInputClick}
|
||||
onBlur={this.cancelEdit} />
|
||||
</form>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Room list entry.
|
||||
*/
|
||||
|
@ -539,7 +598,7 @@ loop.panel = (function(_, mozL10n) {
|
|||
(nextState.urlCopied !== this.state.urlCopied);
|
||||
},
|
||||
|
||||
handleClickRoomUrl: function(event) {
|
||||
handleClickEntry: function(event) {
|
||||
event.preventDefault();
|
||||
this.props.dispatcher.dispatch(new sharedActions.OpenRoom({
|
||||
roomToken: this.props.room.roomToken
|
||||
|
@ -547,6 +606,7 @@ loop.panel = (function(_, mozL10n) {
|
|||
},
|
||||
|
||||
handleCopyButtonClick: function(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
this.props.dispatcher.dispatch(new sharedActions.CopyRoomUrl({
|
||||
roomUrl: this.props.room.roomUrl
|
||||
|
@ -555,10 +615,31 @@ loop.panel = (function(_, mozL10n) {
|
|||
},
|
||||
|
||||
handleDeleteButtonClick: function(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
// XXX We should prompt end user for confirmation; see bug 1092953.
|
||||
this.props.dispatcher.dispatch(new sharedActions.DeleteRoom({
|
||||
roomToken: this.props.room.roomToken
|
||||
navigator.mozLoop.confirm({
|
||||
message: mozL10n.get("rooms_list_deleteConfirmation_label"),
|
||||
okButton: null,
|
||||
cancelButton: null
|
||||
}, function(err, result) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.props.dispatcher.dispatch(new sharedActions.DeleteRoom({
|
||||
roomToken: this.props.room.roomToken
|
||||
}));
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
renameRoom: function(newRoomName) {
|
||||
this.props.dispatcher.dispatch(new sharedActions.RenameRoom({
|
||||
roomToken: this.props.room.roomToken,
|
||||
newRoomName: newRoomName
|
||||
}));
|
||||
},
|
||||
|
||||
|
@ -582,10 +663,11 @@ loop.panel = (function(_, mozL10n) {
|
|||
});
|
||||
|
||||
return (
|
||||
<div className={roomClasses} onMouseLeave={this.handleMouseLeave}>
|
||||
<div className={roomClasses} onMouseLeave={this.handleMouseLeave}
|
||||
onClick={this.handleClickEntry}>
|
||||
<h2>
|
||||
<span className="room-notification" />
|
||||
{room.roomName}
|
||||
<EditInPlace text={room.roomName} onChange={this.renameRoom} />
|
||||
<button className={copyButtonClasses}
|
||||
title={mozL10n.get("rooms_list_copy_url_tooltip")}
|
||||
onClick={this.handleCopyButtonClick} />
|
||||
|
@ -593,11 +675,7 @@ loop.panel = (function(_, mozL10n) {
|
|||
title={mozL10n.get("rooms_list_delete_tooltip")}
|
||||
onClick={this.handleDeleteButtonClick} />
|
||||
</h2>
|
||||
<p>
|
||||
<a href="#" onClick={this.handleClickRoomUrl}>
|
||||
{room.roomUrl}
|
||||
</a>
|
||||
</p>
|
||||
<p><a href="#">{room.roomUrl}</a></p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -204,9 +204,11 @@ body {
|
|||
|
||||
.room-list > .room-entry {
|
||||
padding: .5rem 1rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.room-list > .room-entry > h2 {
|
||||
display: inline-block;
|
||||
font-size: .85rem;
|
||||
color: #777;
|
||||
}
|
||||
|
@ -298,16 +300,33 @@ body {
|
|||
top: 0px;
|
||||
}
|
||||
|
||||
.room-list > .room-entry > h2 {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
/* keep the various room-entry row pieces aligned with each other */
|
||||
.room-list > .room-entry > h2 > button,
|
||||
.room-list > .room-entry > h2 > span {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* Edit in place */
|
||||
|
||||
.room-list > .room-entry > h2 > .edit-in-place {
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.room-list > .room-entry > h2 > .edit-in-place:hover {
|
||||
background: #fefbc4;
|
||||
}
|
||||
|
||||
.room-list > .room-entry > h2 > form {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.room-list > .room-entry > h2 > form > input {
|
||||
border: none;
|
||||
background: #fefbc4;
|
||||
font-size: 13.6px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
|
||||
.button-group {
|
||||
|
|
|
@ -161,7 +161,8 @@ loop.store.ActiveRoomStore = (function() {
|
|||
this._registerPostSetupActions();
|
||||
|
||||
this.setStoreState({
|
||||
roomState: ROOM_STATES.GATHER
|
||||
roomState: ROOM_STATES.GATHER,
|
||||
windowId: actionData.windowId
|
||||
});
|
||||
|
||||
// Get the window data from the mozLoop api.
|
||||
|
@ -307,6 +308,9 @@ loop.store.ActiveRoomStore = (function() {
|
|||
this._setRefreshTimeout(actionData.expires);
|
||||
this._sdkDriver.connectSession(actionData);
|
||||
|
||||
this._mozLoop.addConversationContext(this._storeState.windowId,
|
||||
actionData.sessionId, "");
|
||||
|
||||
// If we haven't got a room name yet, go and get one. We typically
|
||||
// need to do this in the case of the standalone window.
|
||||
// XXX When bug 1103331 lands this can be moved to earlier.
|
||||
|
|
|
@ -170,6 +170,9 @@ loop.store.ConversationStore = (function() {
|
|||
sessionId: this.get("sessionId"),
|
||||
sessionToken: this.get("sessionToken")
|
||||
});
|
||||
navigator.mozLoop.addConversationContext(this.get("windowId"),
|
||||
this.get("sessionId"),
|
||||
this.get("callId"));
|
||||
this.set({callState: CALL_STATES.ONGOING});
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -57,6 +57,7 @@ loop.shared.models = (function(l10n) {
|
|||
* Constructor.
|
||||
*
|
||||
* Options:
|
||||
* - {OT} mozLoop: browser mozLoop service object.
|
||||
*
|
||||
* Required:
|
||||
* - {OT} sdk: OT SDK object.
|
||||
|
@ -66,6 +67,7 @@ loop.shared.models = (function(l10n) {
|
|||
*/
|
||||
initialize: function(attributes, options) {
|
||||
options = options || {};
|
||||
this.mozLoop = options.mozLoop;
|
||||
if (!options.sdk) {
|
||||
throw new Error("missing required sdk");
|
||||
}
|
||||
|
@ -186,6 +188,13 @@ loop.shared.models = (function(l10n) {
|
|||
this._sessionDisconnected);
|
||||
this.session.connect(this.get("apiKey"), this.get("sessionToken"),
|
||||
this._onConnectCompletion.bind(this));
|
||||
|
||||
// We store the call credentials for debugging purposes.
|
||||
if (this.mozLoop) {
|
||||
this.mozLoop.addConversationContext(this.get("windowId"),
|
||||
this.get("sessionId"),
|
||||
this.get("callId"));
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -263,7 +263,11 @@ loop.StandaloneMozLoop = (function(mozL10n) {
|
|||
*/
|
||||
getLoopPref: function(prefName) {
|
||||
return localStorage.getItem(prefName);
|
||||
}
|
||||
},
|
||||
|
||||
// Dummy function to reflect those in the desktop mozLoop that we
|
||||
// don't currently use.
|
||||
addConversationContext: function() {}
|
||||
};
|
||||
|
||||
return StandaloneMozLoop;
|
||||
|
|
|
@ -54,7 +54,8 @@ describe("loop.panel", function() {
|
|||
callback(null, []);
|
||||
},
|
||||
on: sandbox.stub()
|
||||
}
|
||||
},
|
||||
confirm: sandbox.stub()
|
||||
};
|
||||
|
||||
document.mozL10n.initialize(navigator.mozLoop);
|
||||
|
@ -714,6 +715,42 @@ describe("loop.panel", function() {
|
|||
return TestUtils.renderIntoDocument(loop.panel.RoomEntry(props));
|
||||
}
|
||||
|
||||
describe("Edit room name", function() {
|
||||
var roomEntry, domNode;
|
||||
|
||||
beforeEach(function() {
|
||||
roomEntry = mountRoomEntry({
|
||||
dispatcher: dispatcher,
|
||||
deleteRoom: sandbox.stub(),
|
||||
room: new loop.store.Room(roomData)
|
||||
});
|
||||
domNode = roomEntry.getDOMNode();
|
||||
|
||||
TestUtils.Simulate.click(domNode.querySelector(".edit-in-place"));
|
||||
});
|
||||
|
||||
it("should render an edit form on room name click", function() {
|
||||
expect(domNode.querySelector("form")).not.eql(null);
|
||||
expect(domNode.querySelector("input").value).eql(roomData.roomName);
|
||||
});
|
||||
|
||||
it("should dispatch a RenameRoom action when submitting the form",
|
||||
function() {
|
||||
var dispatch = sandbox.stub(dispatcher, "dispatch");
|
||||
|
||||
TestUtils.Simulate.change(domNode.querySelector("input"), {
|
||||
target: {value: "New name"}
|
||||
});
|
||||
TestUtils.Simulate.submit(domNode.querySelector("form"));
|
||||
|
||||
sinon.assert.calledOnce(dispatch);
|
||||
sinon.assert.calledWithExactly(dispatch, new sharedActions.RenameRoom({
|
||||
roomToken: roomData.roomToken,
|
||||
newRoomName: "New name"
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
describe("Copy button", function() {
|
||||
var roomEntry, copyButton;
|
||||
|
||||
|
@ -779,15 +816,27 @@ describe("loop.panel", function() {
|
|||
expect(deleteButton).to.not.equal(null);
|
||||
});
|
||||
|
||||
it("should call the delete function when clicked", function() {
|
||||
it("should dispatch a delete action when confirmation is granted", function() {
|
||||
sandbox.stub(dispatcher, "dispatch");
|
||||
|
||||
navigator.mozLoop.confirm.callsArgWith(1, null, true);
|
||||
TestUtils.Simulate.click(deleteButton);
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.confirm);
|
||||
sinon.assert.calledOnce(dispatcher.dispatch);
|
||||
sinon.assert.calledWithExactly(dispatcher.dispatch,
|
||||
new sharedActions.DeleteRoom({roomToken: roomData.roomToken}));
|
||||
});
|
||||
|
||||
it("should not dispatch an action when the confirmation is cancelled", function() {
|
||||
sandbox.stub(dispatcher, "dispatch");
|
||||
|
||||
navigator.mozLoop.confirm.callsArgWith(1, null, false);
|
||||
TestUtils.Simulate.click(deleteButton);
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.confirm);
|
||||
sinon.assert.notCalled(dispatcher.dispatch);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Room URL click", function() {
|
||||
|
|
|
@ -21,6 +21,7 @@ describe("loop.store.ActiveRoomStore", function () {
|
|||
|
||||
fakeMozLoop = {
|
||||
setLoopPref: sandbox.stub(),
|
||||
addConversationContext: sandbox.stub(),
|
||||
rooms: {
|
||||
get: sinon.stub(),
|
||||
join: sinon.stub(),
|
||||
|
@ -398,6 +399,7 @@ describe("loop.store.ActiveRoomStore", function () {
|
|||
apiKey: "9876543210",
|
||||
sessionToken: "12563478",
|
||||
sessionId: "15263748",
|
||||
windowId: "42",
|
||||
expires: 20
|
||||
};
|
||||
|
||||
|
@ -431,6 +433,21 @@ describe("loop.store.ActiveRoomStore", function () {
|
|||
actionData);
|
||||
});
|
||||
|
||||
it("should call mozLoop.addConversationContext", function() {
|
||||
var actionData = new sharedActions.JoinedRoom(fakeJoinedData);
|
||||
|
||||
store.setupWindowData(new sharedActions.SetupWindowData({
|
||||
windowId: "42",
|
||||
type: "room",
|
||||
}));
|
||||
|
||||
store.joinedRoom(actionData);
|
||||
|
||||
sinon.assert.calledOnce(fakeMozLoop.addConversationContext);
|
||||
sinon.assert.calledWithExactly(fakeMozLoop.addConversationContext,
|
||||
"42", "15263748", "");
|
||||
});
|
||||
|
||||
it("should call mozLoop.rooms.get to get the room data if the roomName" +
|
||||
"is not known", function() {
|
||||
store.setStoreState({roomName: undefined});
|
||||
|
|
|
@ -38,6 +38,7 @@ describe("loop.store.ConversationStore", function () {
|
|||
|
||||
navigator.mozLoop = {
|
||||
getLoopPref: sandbox.stub(),
|
||||
addConversationContext: sandbox.stub(),
|
||||
calls: {
|
||||
setCallInProgress: sandbox.stub(),
|
||||
clearCallInProgress: sandbox.stub()
|
||||
|
@ -75,6 +76,7 @@ describe("loop.store.ConversationStore", function () {
|
|||
sessionId: "321456",
|
||||
sessionToken: "341256",
|
||||
websocketToken: "543216",
|
||||
windowId: "28",
|
||||
progressURL: "fakeURL"
|
||||
};
|
||||
|
||||
|
@ -222,6 +224,17 @@ describe("loop.store.ConversationStore", function () {
|
|||
sessionToken: "341256"
|
||||
});
|
||||
});
|
||||
|
||||
it("should call mozLoop.addConversationContext", function() {
|
||||
store.set(fakeSessionData);
|
||||
|
||||
store.connectionProgress(
|
||||
new sharedActions.ConnectionProgress({wsState: WS_STATES.CONNECTING}));
|
||||
|
||||
sinon.assert.calledOnce(navigator.mozLoop.addConversationContext);
|
||||
sinon.assert.calledWithExactly(navigator.mozLoop.addConversationContext,
|
||||
"28", "321456", "142536");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -9,8 +9,8 @@ var expect = chai.expect;
|
|||
describe("loop.shared.models", function() {
|
||||
"use strict";
|
||||
|
||||
var sharedModels = loop.shared.models,
|
||||
sandbox, fakeXHR, requests = [], fakeSDK, fakeSession, fakeSessionData;
|
||||
var sharedModels = loop.shared.models, sandbox, fakeXHR,
|
||||
requests = [], fakeSDK, fakeMozLoop, fakeSession, fakeSessionData;
|
||||
|
||||
beforeEach(function() {
|
||||
sandbox = sinon.sandbox.create();
|
||||
|
@ -42,6 +42,7 @@ describe("loop.shared.models", function() {
|
|||
initPublisher: sandbox.spy(),
|
||||
initSession: sandbox.stub().returns(fakeSession)
|
||||
};
|
||||
fakeMozLoop = {};
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
|
@ -63,6 +64,7 @@ describe("loop.shared.models", function() {
|
|||
beforeEach(function() {
|
||||
conversation = new sharedModels.ConversationModel({}, {
|
||||
sdk: fakeSDK
|
||||
mozLoop: fakeMozLoop
|
||||
});
|
||||
conversation.set("loopToken", "fakeToken");
|
||||
});
|
||||
|
@ -159,7 +161,8 @@ describe("loop.shared.models", function() {
|
|||
|
||||
beforeEach(function() {
|
||||
model = new sharedModels.ConversationModel(fakeSessionData, {
|
||||
sdk: fakeSDK
|
||||
sdk: fakeSDK,
|
||||
mozLoop: fakeMozLoop
|
||||
});
|
||||
model.set({
|
||||
publishedStream: true,
|
||||
|
@ -177,6 +180,21 @@ describe("loop.shared.models", function() {
|
|||
expect(model.get("subscribedStream")).eql(false);
|
||||
});
|
||||
|
||||
it("should call addConversationContext", function() {
|
||||
fakeMozLoop.addConversationContext = sandbox.stub();
|
||||
|
||||
model.set({
|
||||
windowId: "28",
|
||||
sessionId: "321456",
|
||||
callId: "142536",
|
||||
});
|
||||
model.startSession();
|
||||
|
||||
sinon.assert.calledOnce(fakeMozLoop.addConversationContext);
|
||||
sinon.assert.calledWithExactly(fakeMozLoop.addConversationContext,
|
||||
"28", "321456", "142536");
|
||||
});
|
||||
|
||||
it("should call connect", function() {
|
||||
fakeSession.connect = sandbox.stub();
|
||||
|
||||
|
|
|
@ -51,9 +51,12 @@ navigator.mozLoop = {
|
|||
ensureRegistered: function() {},
|
||||
getAudioBlob: function(){},
|
||||
getLoopPref: function(pref) {
|
||||
// Ensure UI for rooms is displayed in the showcase.
|
||||
if (pref === "rooms.enabled") {
|
||||
return true;
|
||||
switch(pref) {
|
||||
// Ensure UI for rooms is displayed in the showcase.
|
||||
case "rooms.enabled":
|
||||
// Ensure we skip FTE completely.
|
||||
case "gettingStarted.seen":
|
||||
return true;
|
||||
}
|
||||
},
|
||||
setLoopPref: function(){},
|
||||
|
|
|
@ -2469,7 +2469,7 @@ let DefaultBrowserCheck = {
|
|||
let E10SUINotification = {
|
||||
// Increase this number each time we want to roll out an
|
||||
// e10s testing period to Nightly users.
|
||||
CURRENT_NOTICE_COUNT: 2,
|
||||
CURRENT_NOTICE_COUNT: 3,
|
||||
CURRENT_PROMPT_PREF: "browser.displayedE10SPrompt.1",
|
||||
PREVIOUS_PROMPT_PREF: "browser.displayedE10SPrompt",
|
||||
|
||||
|
|
|
@ -123,8 +123,15 @@ StyleEditorUI.prototype = {
|
|||
|
||||
let hUtils = toolbox.highlighterUtils;
|
||||
if (hUtils.hasCustomHighlighter(SELECTOR_HIGHLIGHTER_TYPE)) {
|
||||
this._highlighter =
|
||||
yield hUtils.getHighlighterByType(SELECTOR_HIGHLIGHTER_TYPE);
|
||||
try {
|
||||
this._highlighter =
|
||||
yield hUtils.getHighlighterByType(SELECTOR_HIGHLIGHTER_TYPE);
|
||||
} catch (e) {
|
||||
// The selectorHighlighter can't always be instantiated, for example
|
||||
// it doesn't work with XUL windows (until bug 1094959 gets fixed).
|
||||
console.warn("The selectorHighlighter couldn't be instantiated, " +
|
||||
"elements matching hovered selectors will not be highlighted");
|
||||
}
|
||||
}
|
||||
}.bind(this)).then(() => {
|
||||
this.createUI();
|
||||
|
|
|
@ -73,3 +73,4 @@ skip-if = e10s # Bug 1055333 - style editor tests disabled with e10s
|
|||
[browser_styleeditor_sourcemap_watching.js]
|
||||
skip-if = e10s # Bug 1055333 - style editor tests disabled with e10s
|
||||
[browser_styleeditor_transition_rule.js]
|
||||
[browser_styleeditor_xul.js]
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that the style-editor initializes correctly for XUL windows.
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
const TEST_URL = "about:config";
|
||||
|
||||
let test = asyncTest(function*() {
|
||||
let tab = yield addTab(TEST_URL);
|
||||
let target = TargetFactory.forTab(tab);
|
||||
|
||||
let toolbox = yield gDevTools.showToolbox(target, "styleeditor");
|
||||
let panel = toolbox.getCurrentPanel();
|
||||
yield panel.UI.once("editor-added");
|
||||
|
||||
ok(panel, "The style-editor panel did initialize correctly for the XUL window");
|
||||
});
|
|
@ -265,6 +265,7 @@ skip-if = buildapp == 'mulet'
|
|||
[browser_webconsole_bug_846918_hsts_invalid-headers.js]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[browser_webconsole_bug_915141_toggle_response_logging_with_keyboard.js]
|
||||
[browser_webconsole_filter_buttons_contextmenu.js]
|
||||
[browser_webconsole_bug_1006027_message_timestamps_incorrect.js]
|
||||
[browser_webconsole_bug_1010953_cspro.js]
|
||||
[browser_webconsole_certificate_messages.js]
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests that the filter button context menu logic works correctly.
|
||||
|
||||
const TEST_URI = "http://example.com/";
|
||||
|
||||
function test() {
|
||||
addTab(TEST_URI);
|
||||
browser.addEventListener("load", function onLoad() {
|
||||
browser.removeEventListener("load", onLoad, true);
|
||||
openConsole(null, testFilterButtons);
|
||||
}, true);
|
||||
}
|
||||
|
||||
function testFilterButtons(aHud) {
|
||||
let hudBox = aHud.ui.rootElement;
|
||||
|
||||
testRightClick("net", hudBox, aHud)
|
||||
.then(() => testRightClick("css", hudBox, aHud))
|
||||
.then(() => testRightClick("js", hudBox, aHud))
|
||||
.then(() => testRightClick("logging", hudBox, aHud))
|
||||
.then(() => testRightClick("security", hudBox, aHud))
|
||||
.then(finishTest);
|
||||
}
|
||||
|
||||
function testRightClick(aCategory, hudBox, aHud) {
|
||||
let deferred = promise.defer();
|
||||
let selector = ".webconsole-filter-button[category=\"" + aCategory + "\"]";
|
||||
let button = hudBox.querySelector(selector);
|
||||
let mainButton = getMainButton(button, aHud);
|
||||
let origCheckedState = button.getAttribute("aria-pressed");
|
||||
let contextMenu = aHud.iframeWindow.document.getElementById(aCategory + "-contextmenu");
|
||||
|
||||
function verifyContextMenuIsClosed() {
|
||||
info("verify the context menu is closed");
|
||||
is(button.getAttribute("open"), false, "The context menu for the \"" + aCategory +
|
||||
"\" button is closed");
|
||||
}
|
||||
|
||||
function verifyOriginalCheckedState() {
|
||||
info("verify the button has the original checked state");
|
||||
is(button.getAttribute("aria-pressed"), origCheckedState,
|
||||
"The button state should not have changed");
|
||||
};
|
||||
|
||||
function verifyNewCheckedState() {
|
||||
info("verify the button's checked state has changed");
|
||||
isnot(button.getAttribute("aria-pressed"), origCheckedState,
|
||||
"The button state should have changed");
|
||||
};
|
||||
|
||||
function leftClickToClose() {
|
||||
info("left click the button to close the contextMenu");
|
||||
EventUtils.sendMouseEvent({type: "click"}, button);
|
||||
executeSoon(() => {
|
||||
verifyContextMenuIsClosed();
|
||||
verifyOriginalCheckedState();
|
||||
leftClickToChangeCheckedState();
|
||||
});
|
||||
}
|
||||
|
||||
function leftClickToChangeCheckedState() {
|
||||
info("left click the mainbutton to change checked state");
|
||||
EventUtils.sendMouseEvent({type: "click"}, mainButton);
|
||||
executeSoon(() => {
|
||||
verifyContextMenuIsClosed();
|
||||
verifyNewCheckedState();
|
||||
deferred.resolve();
|
||||
});
|
||||
}
|
||||
|
||||
verifyContextMenuIsClosed();
|
||||
info("right click the button to open the context menu");
|
||||
waitForContextMenu(contextMenu, mainButton, verifyOriginalCheckedState,
|
||||
leftClickToClose);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function getMainButton(aTargetButton, aHud) {
|
||||
let anonymousNodes = aHud.ui.document.getAnonymousNodes(aTargetButton);
|
||||
let subbutton;
|
||||
|
||||
for (let i = 0; i < anonymousNodes.length; i++) {
|
||||
let node = anonymousNodes[i];
|
||||
if (node.classList.contains("toolbarbutton-menubutton-button")) {
|
||||
subbutton = node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return subbutton;
|
||||
}
|
|
@ -690,6 +690,9 @@ WebConsoleFrame.prototype = {
|
|||
let categories = this.document
|
||||
.querySelectorAll(".webconsole-filter-button[category]");
|
||||
Array.forEach(categories, function(aButton) {
|
||||
aButton.addEventListener("contextmenu", (aEvent) => {
|
||||
aButton.open = true;
|
||||
}, false);
|
||||
aButton.addEventListener("click", this._toggleFilter, false);
|
||||
|
||||
let someChecked = false;
|
||||
|
@ -811,7 +814,9 @@ WebConsoleFrame.prototype = {
|
|||
{
|
||||
let target = aEvent.target;
|
||||
let tagName = target.tagName;
|
||||
if (tagName != aEvent.currentTarget.tagName) {
|
||||
// Prevent toggle if generated from a contextmenu event (right click)
|
||||
let isRightClick = (aEvent.button === 2); // right click is button 2;
|
||||
if (tagName != aEvent.currentTarget.tagName || isRightClick) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -5395,4 +5400,3 @@ ConsoleContextMenu.prototype = {
|
|||
this.lastClickedMessage = null;
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -93,7 +93,7 @@ function goUpdateConsoleCommands() {
|
|||
accesskeyMacOSX="&btnPageNet.accesskeyMacOSX;"
|
||||
accesskey="&btnPageNet.accesskey;"
|
||||
tabindex="3">
|
||||
<menupopup>
|
||||
<menupopup id="net-contextmenu">
|
||||
<menuitem label="&btnConsoleErrors;" type="checkbox" autocheck="false"
|
||||
prefKey="network"/>
|
||||
<menuitem label="&btnConsoleWarnings;" type="checkbox" autocheck="false"
|
||||
|
@ -110,7 +110,7 @@ function goUpdateConsoleCommands() {
|
|||
tooltiptext="&btnPageCSS.tooltip2;"
|
||||
accesskey="&btnPageCSS.accesskey;"
|
||||
tabindex="4">
|
||||
<menupopup>
|
||||
<menupopup id="css-contextmenu">
|
||||
<menuitem label="&btnConsoleErrors;" type="checkbox" autocheck="false"
|
||||
prefKey="csserror"/>
|
||||
<menuitem label="&btnConsoleWarnings;" type="checkbox"
|
||||
|
@ -124,7 +124,7 @@ function goUpdateConsoleCommands() {
|
|||
tooltiptext="&btnPageJS.tooltip;"
|
||||
accesskey="&btnPageJS.accesskey;"
|
||||
tabindex="5">
|
||||
<menupopup>
|
||||
<menupopup id="js-contextmenu">
|
||||
<menuitem label="&btnConsoleErrors;" type="checkbox"
|
||||
autocheck="false" prefKey="exception"/>
|
||||
<menuitem label="&btnConsoleWarnings;" type="checkbox"
|
||||
|
@ -138,7 +138,7 @@ function goUpdateConsoleCommands() {
|
|||
tooltiptext="&btnPageSecurity.tooltip;"
|
||||
accesskey="&btnPageSecurity.accesskey;"
|
||||
tabindex="6">
|
||||
<menupopup>
|
||||
<menupopup id="security-contextmenu">
|
||||
<menuitem label="&btnConsoleErrors;" type="checkbox"
|
||||
autocheck="false" prefKey="secerror"/>
|
||||
<menuitem label="&btnConsoleWarnings;" type="checkbox"
|
||||
|
@ -150,7 +150,7 @@ function goUpdateConsoleCommands() {
|
|||
tooltiptext="&btnPageLogging.tooltip;"
|
||||
accesskey="&btnPageLogging.accesskey3;"
|
||||
tabindex="7">
|
||||
<menupopup>
|
||||
<menupopup id="logging-contextmenu">
|
||||
<menuitem label="&btnConsoleErrors;" type="checkbox"
|
||||
autocheck="false" prefKey="error"/>
|
||||
<menuitem label="&btnConsoleWarnings;" type="checkbox"
|
||||
|
|
|
@ -115,13 +115,6 @@ this.SitePermissions = {
|
|||
* a UI for managing permissions.
|
||||
*/
|
||||
getStateLabel: function (aPermissionID, aState) {
|
||||
if (aPermissionID in gPermissionObject &&
|
||||
gPermissionObject[aPermissionID].getStateLabel) {
|
||||
let label = gPermissionObject[aPermissionID].getStateLabel(aState);
|
||||
if (label)
|
||||
return label;
|
||||
}
|
||||
|
||||
switch (aState) {
|
||||
case this.UNKNOWN:
|
||||
return gStringBundle.GetStringFromName("alwaysAsk");
|
||||
|
@ -151,11 +144,6 @@ let gPermissionObject = {
|
|||
* Defaults to UNKNOWN, indicating that the user will be asked each time
|
||||
* a page asks for that permissions.
|
||||
*
|
||||
* - getStateLabel
|
||||
* Called to get the localized label for the given permission state, to be
|
||||
* used in a UI for managing permissions. May return null for states that
|
||||
* should use their default label.
|
||||
*
|
||||
* - onChange
|
||||
* Called when a permission state changes.
|
||||
*
|
||||
|
@ -208,21 +196,8 @@ let gPermissionObject = {
|
|||
},
|
||||
|
||||
"indexedDB": {
|
||||
states: [ SitePermissions.ALLOW, SitePermissions.UNKNOWN, SitePermissions.BLOCK ],
|
||||
getStateLabel: function (aState) {
|
||||
// indexedDB redefines nsIPermissionManager.UNKNOWN_ACTION (the default)
|
||||
// as "allow" and nsIPermissionManager.ALLOW_ACTION as "ask the user."
|
||||
switch (aState) {
|
||||
case SitePermissions.UNKNOWN:
|
||||
return gStringBundle.GetStringFromName("allow");
|
||||
case SitePermissions.ALLOW:
|
||||
return gStringBundle.GetStringFromName("alwaysAsk");
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
},
|
||||
onChange: function (aURI, aState) {
|
||||
if (aState == SitePermissions.ALLOW || aState == SitePermissions.BLOCK)
|
||||
if (aState == SitePermissions.BLOCK)
|
||||
Services.perms.remove(aURI.host, "indexedDB-unlimited");
|
||||
}
|
||||
},
|
||||
|
|
|
@ -65,6 +65,8 @@ this.PermissionsInstaller = {
|
|||
newPermNames = newPermNames.concat(expandedPermNames);
|
||||
}
|
||||
|
||||
newPermNames.push("indexedDB");
|
||||
|
||||
// Add the appcache related permissions.
|
||||
if (newManifest.appcache_path) {
|
||||
newPermNames = newPermNames.concat(["offline-app", "pin-app"]);
|
||||
|
@ -116,6 +118,8 @@ this.PermissionsInstaller = {
|
|||
break;
|
||||
}
|
||||
|
||||
this._setPermission("indexedDB", "allow", aApp);
|
||||
|
||||
// Add the appcache related permissions. We allow it for all kinds of
|
||||
// apps.
|
||||
if (newManifest.appcache_path) {
|
||||
|
|
|
@ -306,8 +306,7 @@ this.PermissionsTable = { geolocation: {
|
|||
privileged: ALLOW_ACTION,
|
||||
certified: ALLOW_ACTION,
|
||||
substitute: [
|
||||
"indexedDB-unlimited",
|
||||
"default-persistent-storage"
|
||||
"indexedDB-unlimited"
|
||||
]
|
||||
},
|
||||
"background-sensors": {
|
||||
|
@ -645,7 +644,7 @@ this.AllPossiblePermissions = [];
|
|||
}
|
||||
}
|
||||
AllPossiblePermissions =
|
||||
AllPossiblePermissions.concat(["offline-app", "pin-app"]);
|
||||
AllPossiblePermissions.concat(["indexedDB", "offline-app", "pin-app"]);
|
||||
})();
|
||||
|
||||
this.isExplicitInPermissionsTable = function(aPermName, aIntStatus) {
|
||||
|
|
|
@ -735,25 +735,12 @@ MainProcessRunnable::InitOnMainThread()
|
|||
NS_ENSURE_STATE(qm);
|
||||
|
||||
nsresult rv =
|
||||
QuotaManager::GetInfoFromPrincipal(mPrincipal,
|
||||
quota::PERSISTENCE_TYPE_INVALID,
|
||||
&mGroup, &mOrigin, &mIsApp,
|
||||
QuotaManager::GetInfoFromPrincipal(mPrincipal, &mGroup, &mOrigin, &mIsApp,
|
||||
&mHasUnlimStoragePerm);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// XXX Don't use mGroup yet! We might need to update it right after we
|
||||
// initialize persistence type.
|
||||
|
||||
InitPersistenceType();
|
||||
|
||||
// XXX Since we couldn't pass persistence type to GetInfoFromPrincipal(),
|
||||
// we need to do this manually.
|
||||
// This hack is only temporary, it will go away once we have regular
|
||||
// metadata files for persistent storge.
|
||||
if (mPersistence == quota::PERSISTENCE_TYPE_PERSISTENT) {
|
||||
mGroup = mOrigin;
|
||||
}
|
||||
|
||||
mEnforcingQuota =
|
||||
QuotaManager::IsQuotaEnforced(mPersistence, mOrigin, mIsApp,
|
||||
mHasUnlimStoragePerm);
|
||||
|
@ -1843,7 +1830,7 @@ public:
|
|||
|
||||
virtual void
|
||||
OnOriginClearCompleted(PersistenceType aPersistenceType,
|
||||
const OriginOrPatternString& aOriginOrPattern)
|
||||
const nsACString& aOrigin)
|
||||
MOZ_OVERRIDE
|
||||
{ }
|
||||
|
||||
|
|
|
@ -3119,6 +3119,12 @@ nsDOMWindowUtils::GetFileReferences(const nsAString& aDatabaseName, int64_t aId,
|
|||
nsCOMPtr<nsPIDOMWindow> window = do_QueryReferent(mWindow);
|
||||
NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
|
||||
|
||||
nsCString origin;
|
||||
nsresult rv =
|
||||
quota::QuotaManager::GetInfoFromWindow(window, nullptr, &origin, nullptr,
|
||||
nullptr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
IDBOpenDBOptions options;
|
||||
JS::Rooted<JS::Value> optionsVal(aCx, aOptions);
|
||||
if (!options.Init(aCx, optionsVal)) {
|
||||
|
@ -3128,12 +3134,6 @@ nsDOMWindowUtils::GetFileReferences(const nsAString& aDatabaseName, int64_t aId,
|
|||
quota::PersistenceType persistenceType =
|
||||
quota::PersistenceTypeFromStorage(options.mStorage);
|
||||
|
||||
nsCString origin;
|
||||
nsresult rv =
|
||||
quota::QuotaManager::GetInfoFromWindow(window, persistenceType, nullptr,
|
||||
&origin, nullptr, nullptr);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsRefPtr<indexedDB::IndexedDatabaseManager> mgr =
|
||||
indexedDB::IndexedDatabaseManager::Get();
|
||||
|
||||
|
|
|
@ -1612,7 +1612,7 @@ nsFocusManager::Blur(nsPIDOMWindow* aWindowToClear,
|
|||
|
||||
nsRefPtr<SelectionCarets> selectionCarets = presShell->GetSelectionCarets();
|
||||
if (selectionCarets) {
|
||||
selectionCarets->SetVisibility(false);
|
||||
selectionCarets->NotifyBlur();
|
||||
}
|
||||
|
||||
bool clearFirstBlurEvent = false;
|
||||
|
|
|
@ -2235,6 +2235,7 @@ GK_ATOM(aria_label, "aria-label")
|
|||
GK_ATOM(aria_labelledby, "aria-labelledby")
|
||||
GK_ATOM(aria_level, "aria-level")
|
||||
GK_ATOM(aria_live, "aria-live")
|
||||
GK_ATOM(aria_modal, "aria-modal")
|
||||
GK_ATOM(aria_multiline, "aria-multiline")
|
||||
GK_ATOM(aria_multiselectable, "aria-multiselectable")
|
||||
GK_ATOM(aria_orientation, "aria-orientation")
|
||||
|
|
|
@ -40,8 +40,6 @@
|
|||
#include "nsWindowMemoryReporter.h"
|
||||
#include "WindowNamedPropertiesHandler.h"
|
||||
#include "nsFrameSelection.h"
|
||||
#include "nsISelectionListener.h"
|
||||
#include "nsCaret.h"
|
||||
|
||||
// Helper Classes
|
||||
#include "nsJSUtils.h"
|
||||
|
@ -194,8 +192,6 @@
|
|||
|
||||
#include "nsRefreshDriver.h"
|
||||
|
||||
#include "mozilla/dom/SelectionChangeEvent.h"
|
||||
|
||||
#include "mozilla/AddonPathService.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
|
@ -275,7 +271,6 @@ static PopupControlState gPopupControlState = openAbused;
|
|||
static int32_t gRunningTimeoutDepth = 0;
|
||||
static bool gMouseDown = false;
|
||||
static bool gDragServiceDisabled = false;
|
||||
static bool gSelectionCaretPrefEnabled = false;
|
||||
static FILE *gDumpFile = nullptr;
|
||||
static uint64_t gNextWindowID = 0;
|
||||
static uint32_t gSerialCounter = 0;
|
||||
|
@ -1191,9 +1186,6 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow)
|
|||
Preferences::AddBoolVarCache(&sIdleObserversAPIFuzzTimeDisabled,
|
||||
"dom.idle-observers-api.fuzz_time.disabled",
|
||||
false);
|
||||
Preferences::AddBoolVarCache(&gSelectionCaretPrefEnabled,
|
||||
"selectioncaret.enabled",
|
||||
false);
|
||||
}
|
||||
|
||||
if (gDumpFile == nullptr) {
|
||||
|
@ -9293,61 +9285,6 @@ public:
|
|||
nsString mAction;
|
||||
};
|
||||
|
||||
static bool
|
||||
CheckReason(int16_t aReason, SelectionChangeReason aReasonType)
|
||||
{
|
||||
switch (aReasonType) {
|
||||
case SelectionChangeReason::Drag:
|
||||
return aReason & nsISelectionListener::DRAG_REASON;
|
||||
case SelectionChangeReason::Mousedown:
|
||||
return aReason & nsISelectionListener::MOUSEDOWN_REASON;
|
||||
case SelectionChangeReason::Mouseup:
|
||||
return aReason & nsISelectionListener::MOUSEUP_REASON;
|
||||
case SelectionChangeReason::Keypress:
|
||||
return aReason & nsISelectionListener::KEYPRESS_REASON;
|
||||
case SelectionChangeReason::Selectall:
|
||||
return aReason & nsISelectionListener::SELECTALL_REASON;
|
||||
case SelectionChangeReason::Collapsetostart:
|
||||
return aReason & nsISelectionListener::COLLAPSETOSTART_REASON;
|
||||
case SelectionChangeReason::Collapsetoend:
|
||||
return aReason & nsISelectionListener::COLLAPSETOEND_REASON;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static nsRect
|
||||
GetSelectionBoundingRect(Selection* aSel, nsIPresShell* aShell)
|
||||
{
|
||||
nsRect res;
|
||||
// Bounding client rect may be empty after calling GetBoundingClientRect
|
||||
// when range is collapsed. So we get caret's rect when range is
|
||||
// collapsed.
|
||||
if (aSel->IsCollapsed()) {
|
||||
aShell->FlushPendingNotifications(Flush_Layout);
|
||||
nsIFrame* frame = nsCaret::GetGeometry(aSel, &res);
|
||||
if (frame) {
|
||||
nsIFrame* relativeTo =
|
||||
nsLayoutUtils::GetContainingBlockForClientRect(frame);
|
||||
res = nsLayoutUtils::TransformFrameRectToAncestor(frame, res, relativeTo);
|
||||
}
|
||||
} else {
|
||||
int32_t rangeCount = aSel->GetRangeCount();
|
||||
nsLayoutUtils::RectAccumulator accumulator;
|
||||
for (int32_t idx = 0; idx < rangeCount; ++idx) {
|
||||
nsRange* range = aSel->GetRangeAt(idx);
|
||||
nsRange::CollectClientRects(&accumulator, range,
|
||||
range->GetStartParent(), range->StartOffset(),
|
||||
range->GetEndParent(), range->EndOffset(),
|
||||
true, false);
|
||||
}
|
||||
res = accumulator.mResultRect.IsEmpty() ? accumulator.mFirstRect :
|
||||
accumulator.mResultRect;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsGlobalWindow::UpdateCommands(const nsAString& anAction, nsISelection* aSel, int16_t aReason)
|
||||
{
|
||||
|
@ -9370,55 +9307,6 @@ nsGlobalWindow::UpdateCommands(const nsAString& anAction, nsISelection* aSel, in
|
|||
}
|
||||
}
|
||||
|
||||
if (gSelectionCaretPrefEnabled && mDoc && anAction.EqualsLiteral("selectionchange")) {
|
||||
SelectionChangeEventInit init;
|
||||
init.mBubbles = true;
|
||||
if (aSel) {
|
||||
bool isTouchCaretVisible = false;
|
||||
bool isCollapsed = aSel->Collapsed();
|
||||
|
||||
nsIPresShell *shell = mDoc->GetShell();
|
||||
if (shell) {
|
||||
nsRefPtr<TouchCaret> touchCaret = shell->GetTouchCaret();
|
||||
if (touchCaret) {
|
||||
isTouchCaretVisible = touchCaret->GetVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
// Dispatch selection change events when touch caret is visible even if selection
|
||||
// is collapsed because it could be the shortcut mode, otherwise ignore this
|
||||
// UpdateCommands
|
||||
if (isCollapsed && !isTouchCaretVisible) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
Selection* selection = static_cast<Selection*>(aSel);
|
||||
nsRect rect = GetSelectionBoundingRect(selection, mDoc->GetShell());
|
||||
nsRefPtr<DOMRect> domRect = new DOMRect(ToSupports(this));
|
||||
domRect->SetLayoutRect(rect);
|
||||
init.mBoundingClientRect = domRect;
|
||||
|
||||
selection->Stringify(init.mSelectedText);
|
||||
for (uint32_t reasonType = 0;
|
||||
reasonType < static_cast<uint32_t>(SelectionChangeReason::EndGuard_);
|
||||
++reasonType) {
|
||||
SelectionChangeReason strongReasonType =
|
||||
static_cast<SelectionChangeReason>(reasonType);
|
||||
if (CheckReason(aReason, strongReasonType)) {
|
||||
init.mReasons.AppendElement(strongReasonType);
|
||||
}
|
||||
}
|
||||
|
||||
nsRefPtr<SelectionChangeEvent> event =
|
||||
SelectionChangeEvent::Constructor(mDoc, NS_LITERAL_STRING("mozselectionchange"), init);
|
||||
|
||||
event->SetTrusted(true);
|
||||
event->GetInternalNSEvent()->mFlags.mOnlyChromeDispatch = true;
|
||||
bool ret;
|
||||
mDoc->DispatchEvent(event, &ret);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -94,6 +94,7 @@ function BrowserElementChild() {
|
|||
|
||||
this._isContentWindowCreated = false;
|
||||
this._pendingSetInputMethodActive = [];
|
||||
this._forceDispatchSelectionStateChanged = false;
|
||||
|
||||
this._init();
|
||||
};
|
||||
|
@ -161,8 +162,8 @@ BrowserElementChild.prototype = {
|
|||
/* useCapture = */ true,
|
||||
/* wantsUntrusted = */ false);
|
||||
|
||||
addEventListener('mozselectionchange',
|
||||
this._selectionChangeHandler.bind(this),
|
||||
addEventListener('mozselectionstatechanged',
|
||||
this._selectionStateChangedHandler.bind(this),
|
||||
/* useCapture = */ false,
|
||||
/* wantsUntrusted = */ false);
|
||||
|
||||
|
@ -591,23 +592,61 @@ BrowserElementChild.prototype = {
|
|||
sendAsyncMsg('scrollviewchange', detail);
|
||||
},
|
||||
|
||||
_selectionChangeHandler: function(e) {
|
||||
_selectionStateChangedHandler: function(e) {
|
||||
e.stopPropagation();
|
||||
let boundingClientRect = e.boundingClientRect;
|
||||
if (!boundingClientRect) {
|
||||
return;
|
||||
|
||||
let isCollapsed = (e.selectedText.length == 0);
|
||||
let isMouseUp = (e.states.indexOf('mouseup') == 0);
|
||||
let canPaste = this._isCommandEnabled("paste");
|
||||
|
||||
if (!this._forceDispatchSelectionStateChanged) {
|
||||
// SelectionStateChanged events with the following states are not
|
||||
// necessary to trigger the text dialog, bypass these events
|
||||
// by default.
|
||||
//
|
||||
if(e.states.length == 0 ||
|
||||
e.states.indexOf('drag') == 0 ||
|
||||
e.states.indexOf('keypress') == 0 ||
|
||||
e.states.indexOf('mousedown') == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The collapsed SelectionStateChanged event is unnecessary to dispatch,
|
||||
// bypass this event by default. But there is one exception to support
|
||||
// the shortcut mode which can paste previous copied content easily
|
||||
if (isCollapsed) {
|
||||
if (isMouseUp && canPaste) {
|
||||
//Dispatch this selection change event to support shortcut mode
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For every touch on the screen, there are always two mouse events received,
|
||||
// mousedown/mouseup, no matter touch or long tap on the screen. When there is
|
||||
// is a non-collapsed selection change event which comes with mouseup reason,
|
||||
// it implies some texts are selected. In order to hide the text dialog during next
|
||||
// touch, here sets the forceDispatchSelectionStateChanged flag as true to dispatch the
|
||||
// next SelecitonChange event(with the mousedown) so that the parent side can
|
||||
// hide the text dialog.
|
||||
if (isMouseUp && !isCollapsed) {
|
||||
this._forceDispatchSelectionStateChanged = true;
|
||||
} else {
|
||||
this._forceDispatchSelectionStateChanged = false;
|
||||
}
|
||||
|
||||
let zoomFactor = content.screen.width / content.innerWidth;
|
||||
|
||||
let detail = {
|
||||
rect: {
|
||||
width: boundingClientRect.width,
|
||||
height: boundingClientRect.height,
|
||||
top: boundingClientRect.top,
|
||||
bottom: boundingClientRect.bottom,
|
||||
left: boundingClientRect.left,
|
||||
right: boundingClientRect.right,
|
||||
width: boundingClientRect ? boundingClientRect.width : 0,
|
||||
height: boundingClientRect ? boundingClientRect.height : 0,
|
||||
top: boundingClientRect ? boundingClientRect.top : 0,
|
||||
bottom: boundingClientRect ? boundingClientRect.bottom : 0,
|
||||
left: boundingClientRect ? boundingClientRect.left : 0,
|
||||
right: boundingClientRect ? boundingClientRect.right : 0,
|
||||
},
|
||||
commands: {
|
||||
canSelectAll: this._isCommandEnabled("selectall"),
|
||||
|
@ -616,7 +655,7 @@ BrowserElementChild.prototype = {
|
|||
canPaste: this._isCommandEnabled("paste"),
|
||||
},
|
||||
zoomFactor: zoomFactor,
|
||||
reasons: e.reasons,
|
||||
states: e.states,
|
||||
isCollapsed: (e.selectedText.length == 0),
|
||||
};
|
||||
|
||||
|
@ -631,7 +670,7 @@ BrowserElementChild.prototype = {
|
|||
currentWindow = currentWindow.parent;
|
||||
}
|
||||
|
||||
sendAsyncMsg('selectionchange', detail);
|
||||
sendAsyncMsg('selectionstatechanged', detail);
|
||||
},
|
||||
|
||||
_themeColorChangedHandler: function(eventType, target) {
|
||||
|
|
|
@ -197,7 +197,7 @@ BrowserElementParent.prototype = {
|
|||
"got-visible": this._gotDOMRequestResult,
|
||||
"visibilitychange": this._childVisibilityChange,
|
||||
"got-set-input-method-active": this._gotDOMRequestResult,
|
||||
"selectionchange": this._handleSelectionChange,
|
||||
"selectionstatechanged": this._handleSelectionStateChanged,
|
||||
"scrollviewchange": this._handleScrollViewChange,
|
||||
"touchcarettap": this._handleTouchCaretTap
|
||||
};
|
||||
|
@ -435,8 +435,8 @@ BrowserElementParent.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
_handleSelectionChange: function(data) {
|
||||
let evt = this._createEvent('selectionchange', data.json,
|
||||
_handleSelectionStateChanged: function(data) {
|
||||
let evt = this._createEvent('selectionstatechanged', data.json,
|
||||
/* cancelable = */ false);
|
||||
this._frameElement.dispatchEvent(evt);
|
||||
},
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/* Any copyright is dedicated to the public domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that "cut, copy, paste, selectall" and selectionchange event works from inside an <iframe mozbrowser>.
|
||||
// Test that "cut, copy, paste, selectall" and selectionstatechanged event works from inside an <iframe mozbrowser>.
|
||||
"use strict";
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
@ -142,9 +142,9 @@ function dispatchTest(e) {
|
|||
}
|
||||
|
||||
function testSelectAll(e) {
|
||||
iframe.addEventListener("mozbrowserselectionchange", function selectchangeforselectall(e) {
|
||||
iframe.removeEventListener("mozbrowserselectionchange", selectchangeforselectall, true);
|
||||
ok(true, "got mozbrowserselectionchange event." + stateMeaning);
|
||||
iframe.addEventListener("mozbrowserselectionstatechanged", function selectchangeforselectall(e) {
|
||||
iframe.removeEventListener("mozbrowserselectionstatechanged", selectchangeforselectall, true);
|
||||
ok(true, "got mozbrowserselectionstatechanged event." + stateMeaning);
|
||||
ok(e.detail, "event.detail is not null." + stateMeaning);
|
||||
ok(e.detail.width != 0, "event.detail.width is not zero" + stateMeaning);
|
||||
ok(e.detail.height != 0, "event.detail.height is not zero" + stateMeaning);
|
||||
|
@ -153,7 +153,7 @@ function testSelectAll(e) {
|
|||
|
||||
mm.addMessageListener('content-focus', function messageforfocus(msg) {
|
||||
mm.removeMessageListener('content-focus', messageforfocus);
|
||||
// test selectall command, after calling this the selectionchange event should be fired.
|
||||
// test selectall command, after calling this the selectionstatechanged event should be fired.
|
||||
doCommand('selectall');
|
||||
});
|
||||
|
||||
|
|
|
@ -223,25 +223,6 @@ WebGLFramebuffer::Attachment::RectangleObject() const
|
|||
// The following IsValidFBOTextureXXX functions check the internal format that
|
||||
// is used by GL or GL ES texture formats. This corresponds to the state that
|
||||
// is stored in WebGLTexture::ImageInfo::InternalFormat()
|
||||
static inline bool
|
||||
IsValidFBOTextureColorFormat(TexInternalFormat internalformat)
|
||||
{
|
||||
/* These formats are internal formats for each texture -- the actual low
|
||||
* level format, which we might have to do conversions for when running
|
||||
* against desktop GL (e.g. GL_RGBA + GL_FLOAT -> GL_RGBA32F).
|
||||
*
|
||||
* This function just handles all of them whether desktop GL or ES.
|
||||
*/
|
||||
|
||||
TexInternalFormat unsizedformat = UnsizedInternalFormatFromInternalFormat(internalformat);
|
||||
return unsizedformat == LOCAL_GL_ALPHA ||
|
||||
unsizedformat == LOCAL_GL_LUMINANCE ||
|
||||
unsizedformat == LOCAL_GL_LUMINANCE_ALPHA ||
|
||||
unsizedformat == LOCAL_GL_RGB ||
|
||||
unsizedformat == LOCAL_GL_RGBA ||
|
||||
unsizedformat == LOCAL_GL_SRGB ||
|
||||
unsizedformat == LOCAL_GL_SRGB_ALPHA;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
IsValidFBOTextureDepthFormat(GLenum internalformat)
|
||||
|
@ -258,14 +239,6 @@ IsValidFBOTextureDepthStencilFormat(GLenum internalformat)
|
|||
// The following IsValidFBORenderbufferXXX functions check the internal format
|
||||
// that is stored by WebGLRenderbuffer::InternalFormat(). Valid values can be
|
||||
// found in WebGLContext::RenderbufferStorage.
|
||||
static inline bool
|
||||
IsValidFBORenderbufferColorFormat(GLenum internalFormat)
|
||||
{
|
||||
return internalFormat == LOCAL_GL_RGB565 ||
|
||||
internalFormat == LOCAL_GL_RGB5_A1 ||
|
||||
internalFormat == LOCAL_GL_RGBA4 ||
|
||||
internalFormat == LOCAL_GL_SRGB8_ALPHA8_EXT;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
IsValidFBORenderbufferDepthFormat(GLenum internalFormat)
|
||||
|
@ -702,7 +675,6 @@ WebGLFramebuffer::HasDefinedAttachments() const
|
|||
return hasAttachments;
|
||||
}
|
||||
|
||||
|
||||
static bool
|
||||
IsIncomplete(const WebGLFramebuffer::Attachment& cur)
|
||||
{
|
||||
|
@ -782,7 +754,6 @@ WebGLFramebuffer::AllImageRectsMatch() const
|
|||
return imageRectsMatch;
|
||||
}
|
||||
|
||||
|
||||
const WebGLRectangleObject&
|
||||
WebGLFramebuffer::RectangleObject() const
|
||||
{
|
||||
|
|
|
@ -2903,6 +2903,14 @@ void HTMLMediaElement::MetadataLoaded(const MediaInfo* aInfo,
|
|||
mDecoder->SetFragmentEndTime(mFragmentEnd);
|
||||
}
|
||||
|
||||
// Tracks just got known, pass the info along to the output streams
|
||||
uint8_t hints = (mHasAudio ? DOMMediaStream::HINT_CONTENTS_AUDIO : 0) |
|
||||
(mHasVideo ? DOMMediaStream::HINT_CONTENTS_VIDEO : 0);
|
||||
for (uint32_t i = 0; i < mOutputStreams.Length(); ++i) {
|
||||
OutputMediaStream* out = &mOutputStreams[i];
|
||||
out->mStream->SetHintContents(hints);
|
||||
}
|
||||
|
||||
// If this element had a video track, but consists only of an audio track now,
|
||||
// delete the VideoFrameContainer. This happens when the src is changed to an
|
||||
// audio only file.
|
||||
|
|
|
@ -793,8 +793,6 @@ nsTextInputListener::NotifySelectionChanged(nsIDOMDocument* aDoc, nsISelection*
|
|||
}
|
||||
}
|
||||
|
||||
UpdateTextInputCommands(NS_LITERAL_STRING("selectionchange"), aSel, aReason);
|
||||
|
||||
// if the collapsed state did not change, don't fire notifications
|
||||
if (collapsed == mSelectionWasCollapsed)
|
||||
return NS_OK;
|
||||
|
|
|
@ -5130,7 +5130,7 @@ public:
|
|||
|
||||
virtual void
|
||||
OnOriginClearCompleted(PersistenceType aPersistenceType,
|
||||
const OriginOrPatternString& aOriginOrPattern)
|
||||
const nsACString& aOrigin)
|
||||
MOZ_OVERRIDE;
|
||||
|
||||
virtual void
|
||||
|
@ -5941,7 +5941,8 @@ Factory::AllocPBackgroundIDBFactoryRequestParent(
|
|||
|
||||
const DatabaseMetadata& metadata = commonParams->metadata();
|
||||
if (NS_WARN_IF(metadata.persistenceType() != PERSISTENCE_TYPE_PERSISTENT &&
|
||||
metadata.persistenceType() != PERSISTENCE_TYPE_TEMPORARY)) {
|
||||
metadata.persistenceType() != PERSISTENCE_TYPE_TEMPORARY &&
|
||||
metadata.persistenceType() != PERSISTENCE_TYPE_DEFAULT)) {
|
||||
ASSERT_UNLESS_FUZZING();
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -9429,14 +9430,13 @@ QuotaClient::GetUsageForOrigin(PersistenceType aPersistenceType,
|
|||
}
|
||||
|
||||
void
|
||||
QuotaClient::OnOriginClearCompleted(
|
||||
PersistenceType aPersistenceType,
|
||||
const OriginOrPatternString& aOriginOrPattern)
|
||||
QuotaClient::OnOriginClearCompleted(PersistenceType aPersistenceType,
|
||||
const nsACString& aOrigin)
|
||||
{
|
||||
AssertIsOnIOThread();
|
||||
|
||||
if (IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get()) {
|
||||
mgr->InvalidateFileManagers(aPersistenceType, aOriginOrPattern);
|
||||
mgr->InvalidateFileManagers(aPersistenceType, aOrigin);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10753,6 +10753,9 @@ FactoryOp::CheckPermission(ContentParent* aContentParent,
|
|||
QuotaManager::GetInfoForChrome(&mGroup, &mOrigin, &mIsApp,
|
||||
&mHasUnlimStoragePerm);
|
||||
|
||||
MOZ_ASSERT(!QuotaManager::IsFirstPromptRequired(persistenceType, mOrigin,
|
||||
mIsApp));
|
||||
|
||||
mEnforcingQuota =
|
||||
QuotaManager::IsQuotaEnforced(persistenceType, mOrigin, mIsApp,
|
||||
mHasUnlimStoragePerm);
|
||||
|
@ -10771,14 +10774,19 @@ FactoryOp::CheckPermission(ContentParent* aContentParent,
|
|||
return rv;
|
||||
}
|
||||
|
||||
nsCString group;
|
||||
nsCString origin;
|
||||
bool isApp;
|
||||
bool hasUnlimStoragePerm;
|
||||
rv = QuotaManager::GetInfoFromPrincipal(principal, &group, &origin,
|
||||
&isApp, &hasUnlimStoragePerm);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
PermissionRequestBase::PermissionValue permission;
|
||||
|
||||
if (persistenceType == PERSISTENCE_TYPE_TEMPORARY) {
|
||||
// Temporary storage doesn't need to check the permission.
|
||||
permission = PermissionRequestBase::kPermissionAllowed;
|
||||
} else {
|
||||
MOZ_ASSERT(persistenceType == PERSISTENCE_TYPE_PERSISTENT);
|
||||
|
||||
if (QuotaManager::IsFirstPromptRequired(persistenceType, origin, isApp)) {
|
||||
#ifdef MOZ_CHILD_PERMISSIONS
|
||||
if (aContentParent) {
|
||||
if (NS_WARN_IF(!AssertAppPrincipal(aContentParent, principal))) {
|
||||
|
@ -10799,16 +10807,16 @@ FactoryOp::CheckPermission(ContentParent* aContentParent,
|
|||
return rv;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
permission = PermissionRequestBase::kPermissionAllowed;
|
||||
}
|
||||
|
||||
if (permission != PermissionRequestBase::kPermissionDenied &&
|
||||
State_Initial == mState) {
|
||||
rv = QuotaManager::GetInfoFromPrincipal(principal, persistenceType, &mGroup,
|
||||
&mOrigin, &mIsApp,
|
||||
&mHasUnlimStoragePerm);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
mGroup = group;
|
||||
mOrigin = origin;
|
||||
mIsApp = isApp;
|
||||
mHasUnlimStoragePerm = hasUnlimStoragePerm;
|
||||
|
||||
mEnforcingQuota =
|
||||
QuotaManager::IsQuotaEnforced(persistenceType, mOrigin, mIsApp,
|
||||
|
@ -10971,22 +10979,14 @@ FactoryOp::FinishOpen()
|
|||
MOZ_ASSERT(!mContentParent);
|
||||
MOZ_ASSERT(!QuotaClient::IsShuttingDownOnMainThread());
|
||||
|
||||
PersistenceType persistenceType = mCommonParams.metadata().persistenceType();
|
||||
|
||||
// XXX This is temporary, but we don't currently support the explicit
|
||||
// 'persistent' storage type.
|
||||
if (persistenceType == PERSISTENCE_TYPE_PERSISTENT &&
|
||||
mCommonParams.metadata().persistenceTypeIsExplicit()) {
|
||||
IDB_REPORT_INTERNAL_ERR();
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
QuotaManager* quotaManager = QuotaManager::GetOrCreate();
|
||||
if (NS_WARN_IF(!quotaManager)) {
|
||||
IDB_REPORT_INTERNAL_ERR();
|
||||
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
|
||||
}
|
||||
|
||||
PersistenceType persistenceType = mCommonParams.metadata().persistenceType();
|
||||
|
||||
nsresult rv =
|
||||
quotaManager->
|
||||
WaitForOpenAllowed(OriginOrPatternString::FromOrigin(mOrigin),
|
||||
|
|
|
@ -998,10 +998,8 @@ IDBDatabase::GetQuotaInfo(nsACString& aOrigin,
|
|||
MOZ_ASSERT(IndexedDatabaseManager::IsMainProcess());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
PersistenceType persistenceType = mSpec->metadata().persistenceType();
|
||||
|
||||
if (aPersistenceType) {
|
||||
*aPersistenceType = persistenceType;
|
||||
*aPersistenceType = mSpec->metadata().persistenceType();
|
||||
MOZ_ASSERT(*aPersistenceType != PERSISTENCE_TYPE_INVALID);
|
||||
}
|
||||
|
||||
|
@ -1025,7 +1023,6 @@ IDBDatabase::GetQuotaInfo(nsACString& aOrigin,
|
|||
}
|
||||
|
||||
rv = QuotaManager::GetInfoFromPrincipal(principal,
|
||||
persistenceType,
|
||||
nullptr,
|
||||
&aOrigin,
|
||||
nullptr,
|
||||
|
|
|
@ -479,24 +479,18 @@ IDBFactory::OpenInternal(nsIPrincipal* aPrincipal,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
// XXX We need a bug to switch to temporary storage by default.
|
||||
|
||||
PersistenceType persistenceType;
|
||||
bool persistenceTypeIsExplicit;
|
||||
|
||||
if (principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
|
||||
// Chrome privilege always gets persistent storage.
|
||||
persistenceType = PERSISTENCE_TYPE_PERSISTENT;
|
||||
persistenceTypeIsExplicit = false;
|
||||
} else {
|
||||
persistenceType = PersistenceTypeFromStorage(aStorageType);
|
||||
persistenceTypeIsExplicit = aStorageType.WasPassed();
|
||||
}
|
||||
|
||||
DatabaseMetadata& metadata = commonParams.metadata();
|
||||
metadata.name() = aName;
|
||||
metadata.persistenceType() = persistenceType;
|
||||
metadata.persistenceTypeIsExplicit() = persistenceTypeIsExplicit;
|
||||
|
||||
FactoryRequestParams params;
|
||||
if (aDeleting) {
|
||||
|
|
|
@ -157,15 +157,9 @@ IDBMutableFile::Create(IDBDatabase* aDatabase,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
const DatabaseSpec* spec = aDatabase->Spec();
|
||||
MOZ_ASSERT(spec);
|
||||
|
||||
PersistenceType persistenceType = spec->metadata().persistenceType();
|
||||
|
||||
nsCString group;
|
||||
nsCString origin;
|
||||
if (NS_WARN_IF(NS_FAILED(QuotaManager::GetInfoFromPrincipal(principal,
|
||||
persistenceType,
|
||||
&group,
|
||||
&origin,
|
||||
nullptr,
|
||||
|
@ -173,6 +167,11 @@ IDBMutableFile::Create(IDBDatabase* aDatabase,
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
const DatabaseSpec* spec = aDatabase->Spec();
|
||||
MOZ_ASSERT(spec);
|
||||
|
||||
PersistenceType persistenceType = spec->metadata().persistenceType();
|
||||
|
||||
nsCString storageId;
|
||||
QuotaManager::GetStorageId(persistenceType,
|
||||
origin,
|
||||
|
|
|
@ -80,7 +80,8 @@ public:
|
|||
AssertIsOnIOThread();
|
||||
|
||||
return !mPersistentStorageFileManagers.IsEmpty() ||
|
||||
!mTemporaryStorageFileManagers.IsEmpty();
|
||||
!mTemporaryStorageFileManagers.IsEmpty() ||
|
||||
!mDefaultStorageFileManagers.IsEmpty();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -105,6 +106,7 @@ private:
|
|||
|
||||
nsTArray<nsRefPtr<FileManager> > mPersistentStorageFileManagers;
|
||||
nsTArray<nsRefPtr<FileManager> > mTemporaryStorageFileManagers;
|
||||
nsTArray<nsRefPtr<FileManager> > mDefaultStorageFileManagers;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
@ -178,16 +180,6 @@ private:
|
|||
bool mWaiting;
|
||||
};
|
||||
|
||||
struct MOZ_STACK_CLASS InvalidateInfo
|
||||
{
|
||||
InvalidateInfo(PersistenceType aPersistenceType, const nsACString& aPattern)
|
||||
: persistenceType(aPersistenceType), pattern(aPattern)
|
||||
{ }
|
||||
|
||||
PersistenceType persistenceType;
|
||||
const nsACString& pattern;
|
||||
};
|
||||
|
||||
void
|
||||
TestingPrefChangedCallback(const char* aPrefName, void* aClosure)
|
||||
{
|
||||
|
@ -554,66 +546,49 @@ IndexedDatabaseManager::AddFileManager(FileManager* aFileManager)
|
|||
info->AddFileManager(aFileManager);
|
||||
}
|
||||
|
||||
// static
|
||||
PLDHashOperator
|
||||
IndexedDatabaseManager::InvalidateAndRemoveFileManagers(
|
||||
const nsACString& aKey,
|
||||
nsAutoPtr<FileManagerInfo>& aValue,
|
||||
void* aUserArg)
|
||||
{
|
||||
AssertIsOnIOThread();
|
||||
NS_ASSERTION(!aKey.IsEmpty(), "Empty key!");
|
||||
NS_ASSERTION(aValue, "Null pointer!");
|
||||
|
||||
if (!aUserArg) {
|
||||
aValue->InvalidateAllFileManagers();
|
||||
return PL_DHASH_REMOVE;
|
||||
}
|
||||
|
||||
InvalidateInfo* info = static_cast<InvalidateInfo*>(aUserArg);
|
||||
|
||||
if (PatternMatchesOrigin(info->pattern, aKey)) {
|
||||
aValue->InvalidateAndRemoveFileManagers(info->persistenceType);
|
||||
|
||||
if (!aValue->HasFileManagers()) {
|
||||
return PL_DHASH_REMOVE;
|
||||
}
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
IndexedDatabaseManager::InvalidateAllFileManagers()
|
||||
{
|
||||
AssertIsOnIOThread();
|
||||
|
||||
mFileManagerInfos.Enumerate(InvalidateAndRemoveFileManagers, nullptr);
|
||||
class MOZ_STACK_CLASS Helper MOZ_FINAL
|
||||
{
|
||||
public:
|
||||
static PLDHashOperator
|
||||
Enumerate(const nsACString& aKey,
|
||||
FileManagerInfo* aValue,
|
||||
void* aUserArg)
|
||||
{
|
||||
AssertIsOnIOThread();
|
||||
MOZ_ASSERT(!aKey.IsEmpty());
|
||||
MOZ_ASSERT(aValue);
|
||||
|
||||
aValue->InvalidateAllFileManagers();
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
};
|
||||
|
||||
mFileManagerInfos.EnumerateRead(Helper::Enumerate, nullptr);
|
||||
mFileManagerInfos.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
IndexedDatabaseManager::InvalidateFileManagers(
|
||||
PersistenceType aPersistenceType,
|
||||
const OriginOrPatternString& aOriginOrPattern)
|
||||
const nsACString& aOrigin)
|
||||
{
|
||||
AssertIsOnIOThread();
|
||||
NS_ASSERTION(!aOriginOrPattern.IsEmpty(), "Empty pattern!");
|
||||
MOZ_ASSERT(!aOrigin.IsEmpty());
|
||||
|
||||
if (aOriginOrPattern.IsOrigin()) {
|
||||
FileManagerInfo* info;
|
||||
if (!mFileManagerInfos.Get(aOriginOrPattern, &info)) {
|
||||
return;
|
||||
}
|
||||
|
||||
info->InvalidateAndRemoveFileManagers(aPersistenceType);
|
||||
|
||||
if (!info->HasFileManagers()) {
|
||||
mFileManagerInfos.Remove(aOriginOrPattern);
|
||||
}
|
||||
FileManagerInfo* info;
|
||||
if (!mFileManagerInfos.Get(aOrigin, &info)) {
|
||||
return;
|
||||
}
|
||||
else {
|
||||
InvalidateInfo info(aPersistenceType, aOriginOrPattern);
|
||||
mFileManagerInfos.Enumerate(InvalidateAndRemoveFileManagers, &info);
|
||||
|
||||
info->InvalidateAndRemoveFileManagers(aPersistenceType);
|
||||
|
||||
if (!info->HasFileManagers()) {
|
||||
mFileManagerInfos.Remove(aOrigin);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -792,6 +767,10 @@ FileManagerInfo::InvalidateAllFileManagers() const
|
|||
for (i = 0; i < mTemporaryStorageFileManagers.Length(); i++) {
|
||||
mTemporaryStorageFileManagers[i]->Invalidate();
|
||||
}
|
||||
|
||||
for (i = 0; i < mDefaultStorageFileManagers.Length(); i++) {
|
||||
mDefaultStorageFileManagers[i]->Invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -836,11 +815,12 @@ FileManagerInfo::GetArray(PersistenceType aPersistenceType)
|
|||
return mPersistentStorageFileManagers;
|
||||
case PERSISTENCE_TYPE_TEMPORARY:
|
||||
return mTemporaryStorageFileManagers;
|
||||
case PERSISTENCE_TYPE_DEFAULT:
|
||||
return mDefaultStorageFileManagers;
|
||||
|
||||
case PERSISTENCE_TYPE_INVALID:
|
||||
default:
|
||||
MOZ_CRASH("Bad storage type value!");
|
||||
return mPersistentStorageFileManagers;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,12 +26,6 @@ namespace dom {
|
|||
|
||||
class TabContext;
|
||||
|
||||
namespace quota {
|
||||
|
||||
class OriginOrPatternString;
|
||||
|
||||
} // namespace quota
|
||||
|
||||
namespace indexedDB {
|
||||
|
||||
class FileManager;
|
||||
|
@ -39,7 +33,6 @@ class FileManagerInfo;
|
|||
|
||||
class IndexedDatabaseManager MOZ_FINAL : public nsIObserver
|
||||
{
|
||||
typedef mozilla::dom::quota::OriginOrPatternString OriginOrPatternString;
|
||||
typedef mozilla::dom::quota::PersistenceType PersistenceType;
|
||||
|
||||
public:
|
||||
|
@ -96,7 +89,7 @@ public:
|
|||
|
||||
void
|
||||
InvalidateFileManagers(PersistenceType aPersistenceType,
|
||||
const OriginOrPatternString& aOriginOrPattern);
|
||||
const nsACString& aOrigin);
|
||||
|
||||
void
|
||||
InvalidateFileManager(PersistenceType aPersistenceType,
|
||||
|
@ -150,11 +143,6 @@ private:
|
|||
void
|
||||
Destroy();
|
||||
|
||||
static PLDHashOperator
|
||||
InvalidateAndRemoveFileManagers(const nsACString& aKey,
|
||||
nsAutoPtr<FileManagerInfo>& aValue,
|
||||
void* aUserArg);
|
||||
|
||||
// Maintains a list of all file managers per origin. This list isn't
|
||||
// protected by any mutex but it is only ever touched on the IO thread.
|
||||
nsClassHashtable<nsCStringHashKey, FileManagerInfo> mFileManagerInfos;
|
||||
|
|
|
@ -67,7 +67,6 @@ struct DatabaseMetadata
|
|||
nsString name;
|
||||
uint64_t version;
|
||||
PersistenceType persistenceType;
|
||||
bool persistenceTypeIsExplicit;
|
||||
};
|
||||
|
||||
struct ObjectStoreMetadata
|
||||
|
|
|
@ -102,13 +102,11 @@ PermissionRequestBase::PermissionValueForIntPermission(uint32_t aIntPermission)
|
|||
{
|
||||
AssertSanity();
|
||||
|
||||
// The 'indexedDB' permission is unusual in that the default action is to
|
||||
// allow access. Switch that here to make the logic clearer.
|
||||
switch (aIntPermission) {
|
||||
case kPermissionDefault:
|
||||
return kPermissionAllowed;
|
||||
case kPermissionAllowed:
|
||||
return kPermissionPrompt;
|
||||
case kPermissionAllowed:
|
||||
return kPermissionAllowed;
|
||||
case kPermissionDenied:
|
||||
return kPermissionDenied;
|
||||
default:
|
||||
|
@ -180,13 +178,11 @@ PermissionRequestBase::SetExplicitPermission(nsIPrincipal* aPrincipal,
|
|||
return;
|
||||
}
|
||||
|
||||
nsresult rv = aIntPermission == kPermissionAllowed ?
|
||||
permMan->RemoveFromPrincipal(aPrincipal, kPermissionString) :
|
||||
permMan->AddFromPrincipal(aPrincipal,
|
||||
kPermissionString,
|
||||
aIntPermission,
|
||||
nsIPermissionManager::EXPIRE_NEVER,
|
||||
/* aExpireTime */ 0);
|
||||
nsresult rv = permMan->AddFromPrincipal(aPrincipal,
|
||||
kPermissionString,
|
||||
aIntPermission,
|
||||
nsIPermissionManager::EXPIRE_NEVER,
|
||||
/* aExpireTime */ 0);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -23,8 +23,8 @@ function test()
|
|||
requestLongerTimeout(2);
|
||||
waitForExplicitFinish();
|
||||
// Avoids the prompt
|
||||
setPermission(testPageURL1, "indexedDB", "unknown");
|
||||
setPermission(testPageURL2, "indexedDB", "unknown");
|
||||
setPermission(testPageURL1, "indexedDB");
|
||||
setPermission(testPageURL2, "indexedDB");
|
||||
executeSoon(test1);
|
||||
}
|
||||
|
||||
|
@ -68,7 +68,7 @@ function test3()
|
|||
{
|
||||
// Remove database from domain 2
|
||||
ForgetAboutSite.removeDataFromDomain(domains[1]);
|
||||
setPermission(testPageURL4, "indexedDB", "unknown");
|
||||
setPermission(testPageURL4, "indexedDB");
|
||||
executeSoon(test4);
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,8 @@
|
|||
{
|
||||
const name = window.location.pathname;
|
||||
|
||||
let request = indexedDB.open(name, 1);
|
||||
let request = indexedDB.open(name, { version: 1,
|
||||
storage: "persistent" });
|
||||
request.onerror = grabEventAndContinueHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
let event = yield undefined;
|
||||
|
|
|
@ -14,7 +14,7 @@ function test()
|
|||
PopupNotifications.transitionsEnabled = false;
|
||||
|
||||
// We want a prompt.
|
||||
setPermission(testPageURL, "indexedDB", "allow");
|
||||
removePermission(testPageURL, "indexedDB");
|
||||
executeSoon(test1);
|
||||
}
|
||||
|
||||
|
@ -31,7 +31,7 @@ function test1()
|
|||
"First database creation was successful");
|
||||
ok(!exception, "No exception");
|
||||
is(getPermission(testPageURL, "indexedDB"),
|
||||
Components.interfaces.nsIPermissionManager.UNKNOWN_ACTION,
|
||||
Components.interfaces.nsIPermissionManager.ALLOW_ACTION,
|
||||
"Correct permission set");
|
||||
gBrowser.removeCurrentTab();
|
||||
executeSoon(test2);
|
||||
|
@ -67,7 +67,7 @@ function test2()
|
|||
"First database creation was successful");
|
||||
ok(!exception, "No exception");
|
||||
is(getPermission(testPageURL, "indexedDB"),
|
||||
Components.interfaces.nsIPermissionManager.UNKNOWN_ACTION,
|
||||
Components.interfaces.nsIPermissionManager.ALLOW_ACTION,
|
||||
"Correct permission set");
|
||||
gBrowser.removeCurrentTab();
|
||||
unregisterAllPopupEventHandlers();
|
||||
|
|
|
@ -12,7 +12,7 @@ function test()
|
|||
waitForExplicitFinish();
|
||||
// We want the prompt.
|
||||
PopupNotifications.transitionsEnabled = false;
|
||||
setPermission(testPageURL, "indexedDB", "allow");
|
||||
removePermission(testPageURL, "indexedDB");
|
||||
executeSoon(test1);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,6 +10,8 @@ const notificationID = "indexedDB-permissions-prompt";
|
|||
function test()
|
||||
{
|
||||
waitForExplicitFinish();
|
||||
// Avoids the actual prompt
|
||||
setPermission(testPageURL, "indexedDB");
|
||||
executeSoon(test1);
|
||||
}
|
||||
|
||||
|
|
|
@ -35,7 +35,9 @@
|
|||
window.removeEventListener("indexedDB-addMore", onAddMore, true);
|
||||
window.removeEventListener("indexedDB-done", onDone, true);
|
||||
|
||||
let request = indexedDB.open(window.location.pathname, version++);
|
||||
let request =
|
||||
indexedDB.open(window.location.pathname, { version: version++,
|
||||
storage: "persistent" });
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = function(event) {
|
||||
db.deleteObjectStore("foo");
|
||||
|
@ -55,7 +57,8 @@
|
|||
window.addEventListener("indexedDB-addMore", onAddMore, true);
|
||||
window.addEventListener("indexedDB-done", onDone, true);
|
||||
|
||||
let request = indexedDB.open(name, version++);
|
||||
let request = indexedDB.open(name, { version: version++,
|
||||
storage: "persistent" });
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||
let event = yield undefined;
|
||||
|
|
|
@ -13,6 +13,7 @@ function test()
|
|||
waitForExplicitFinish();
|
||||
requestLongerTimeout(10);
|
||||
PopupNotifications.transitionsEnabled = false;
|
||||
setPermission(testPageURL, "indexedDB");
|
||||
removePermission(testPageURL, "indexedDB-unlimited");
|
||||
Services.prefs.setIntPref("dom.indexedDB.warningQuota", 2);
|
||||
executeSoon(test1);
|
||||
|
@ -43,7 +44,7 @@ function test1()
|
|||
"Correct permission set");
|
||||
gBrowser.removeCurrentTab();
|
||||
unregisterAllPopupEventHandlers();
|
||||
executeSoon(finish);
|
||||
executeSoon(test2);
|
||||
});
|
||||
executeSoon(function() { dispatchEvent("indexedDB-done"); });
|
||||
}
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
function onAddMore() {
|
||||
const name = window.location.pathname + i++;
|
||||
|
||||
let request = indexedDB.open(name, 1);
|
||||
let request = indexedDB.open(name, { version: 1,
|
||||
storage: "persistent" });
|
||||
request.onerror = errorHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ function test()
|
|||
waitForExplicitFinish();
|
||||
requestLongerTimeout(10);
|
||||
PopupNotifications.transitionsEnabled = false;
|
||||
setPermission(testPageURL, "indexedDB");
|
||||
removePermission(testPageURL, "indexedDB-unlimited");
|
||||
Services.prefs.setIntPref("dom.indexedDB.warningQuota", 2);
|
||||
executeSoon(test1);
|
||||
|
|
|
@ -49,7 +49,8 @@
|
|||
deleteRequest.onerror = errorHandler;
|
||||
deleteRequest.onsuccess = function () {
|
||||
// It is imperative that we open a different database this time.
|
||||
let request = indexedDB.open("take2", 1);
|
||||
let request = indexedDB.open("take2", { version: 1,
|
||||
storage: "persistent" });
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = function(event) {
|
||||
db = event.target.result;
|
||||
|
@ -67,7 +68,8 @@
|
|||
window.addEventListener("indexedDB-reset", onReset, true);
|
||||
window.addEventListener("indexedDB-done", onDone, true);
|
||||
|
||||
let request = indexedDB.open(name, 1);
|
||||
let request = indexedDB.open(name, { version: 1,
|
||||
storage: "persistent" });
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||
let event = yield undefined;
|
||||
|
|
|
@ -13,6 +13,7 @@ function test()
|
|||
waitForExplicitFinish();
|
||||
requestLongerTimeout(10);
|
||||
PopupNotifications.transitionsEnabled = false;
|
||||
setPermission(testPageURL, "indexedDB");
|
||||
removePermission(testPageURL, "indexedDB-unlimited");
|
||||
Services.prefs.setIntPref("dom.indexedDB.warningQuota", 2);
|
||||
executeSoon(test1);
|
||||
|
|
|
@ -13,6 +13,7 @@ function test()
|
|||
waitForExplicitFinish();
|
||||
requestLongerTimeout(10);
|
||||
PopupNotifications.transitionsEnabled = false;
|
||||
setPermission(testPageURL, "indexedDB");
|
||||
removePermission(testPageURL, "indexedDB-unlimited");
|
||||
Services.prefs.setIntPref("dom.indexedDB.warningQuota", 2);
|
||||
executeSoon(test1);
|
||||
|
|
|
@ -49,6 +49,8 @@
|
|||
};
|
||||
|
||||
function testSteps() {
|
||||
window.parent.SpecialPowers.addPermission("indexedDB", true, document);
|
||||
|
||||
let request = indexedDB.open(window.location.pathname, 1);
|
||||
request.onsuccess = unexpectedSuccessHandler;
|
||||
request.onerror = grabEventAndContinueHandler;
|
||||
|
|
|
@ -87,6 +87,8 @@
|
|||
}
|
||||
|
||||
function testSteps() {
|
||||
window.parent.SpecialPowers.addPermission("indexedDB", true, document);
|
||||
|
||||
let request = indexedDB.open(window.location.pathname, 1);
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||
|
|
|
@ -51,6 +51,8 @@
|
|||
};
|
||||
|
||||
function testSteps() {
|
||||
window.parent.SpecialPowers.addPermission("indexedDB", true, document);
|
||||
|
||||
// Test 1: Throwing an exception in an upgradeneeded handler should
|
||||
// abort the versionchange transaction and fire an error at the request.
|
||||
let request = indexedDB.open(window.location.pathname, 1);
|
||||
|
|
|
@ -109,24 +109,10 @@ function dispatchEvent(eventName)
|
|||
gBrowser.selectedBrowser.contentWindow.dispatchEvent(event);
|
||||
}
|
||||
|
||||
function setPermission(url, permission, value)
|
||||
function setPermission(url, permission)
|
||||
{
|
||||
const nsIPermissionManager = Components.interfaces.nsIPermissionManager;
|
||||
|
||||
switch (value) {
|
||||
case "allow":
|
||||
value = nsIPermissionManager.ALLOW_ACTION;
|
||||
break;
|
||||
case "deny":
|
||||
value = nsIPermissionManager.DENY_ACTION;
|
||||
break;
|
||||
case "unknown":
|
||||
value = nsIPermissionManager.UNKNOWN_ACTION;
|
||||
break;
|
||||
default:
|
||||
throw new Error("No idea what to set here!");
|
||||
}
|
||||
|
||||
let uri = Components.classes["@mozilla.org/network/io-service;1"]
|
||||
.getService(Components.interfaces.nsIIOService)
|
||||
.newURI(url, null, null);
|
||||
|
@ -135,8 +121,9 @@ function setPermission(url, permission, value)
|
|||
.getNoAppCodebasePrincipal(uri);
|
||||
|
||||
Components.classes["@mozilla.org/permissionmanager;1"]
|
||||
.getService(Components.interfaces.nsIPermissionManager)
|
||||
.addFromPrincipal(principal, permission, value);
|
||||
.getService(nsIPermissionManager)
|
||||
.addFromPrincipal(principal, permission,
|
||||
nsIPermissionManager.ALLOW_ACTION);
|
||||
}
|
||||
|
||||
function removePermission(url, permission)
|
||||
|
|
|
@ -49,6 +49,7 @@ if (!window.runTest) {
|
|||
{
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
allowIndexedDB();
|
||||
if (limitedQuota) {
|
||||
denyUnlimitedQuota();
|
||||
}
|
||||
|
@ -66,10 +67,11 @@ if (!window.runTest) {
|
|||
|
||||
function finishTest()
|
||||
{
|
||||
resetUnlimitedQuota();
|
||||
resetExperimental();
|
||||
resetArchiveReader();
|
||||
resetExperimental();
|
||||
resetTesting();
|
||||
resetUnlimitedQuota();
|
||||
resetIndexedDB();
|
||||
SpecialPowers.notifyObserversInParentProcess(null, "disk-space-watcher",
|
||||
"free");
|
||||
|
||||
|
@ -197,6 +199,16 @@ function removePermission(type, url)
|
|||
SpecialPowers.removePermission(type, url);
|
||||
}
|
||||
|
||||
function allowIndexedDB(url)
|
||||
{
|
||||
addPermission("indexedDB", true, url);
|
||||
}
|
||||
|
||||
function resetIndexedDB(url)
|
||||
{
|
||||
removePermission("indexedDB", url);
|
||||
}
|
||||
|
||||
function allowUnlimitedQuota(url)
|
||||
{
|
||||
addPermission("indexedDB-unlimited", true, url);
|
||||
|
|
|
@ -151,9 +151,7 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
|
|||
[test_deleteDatabase_interactions.html]
|
||||
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
|
||||
[test_disabled_quota_prompt.html]
|
||||
# Test temporarily disabled.
|
||||
skip-if = true
|
||||
# skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
|
||||
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
|
||||
[test_error_events_abort_transactions.html]
|
||||
skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
|
||||
[test_event_propagation.html]
|
||||
|
|
|
@ -40,6 +40,7 @@
|
|||
{
|
||||
const databaseName = window.location.pathname;
|
||||
const databaseVersion = 1;
|
||||
const databaseStorage = "persistent";
|
||||
const objectStoreName = "foo";
|
||||
|
||||
info("setting quota pref");
|
||||
|
@ -50,7 +51,8 @@
|
|||
|
||||
info("opening database");
|
||||
|
||||
let request = indexedDB.open(databaseName, databaseVersion);
|
||||
let request = indexedDB.open(databaseName, {version: databaseVersion,
|
||||
storage: databaseStorage});
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||
request.onsuccess = unexpectedSuccessHandler;
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
{
|
||||
const name = window.location.pathname;
|
||||
const version = 1;
|
||||
const storages = ["persistent", "temporary", "default"];
|
||||
|
||||
const objectStoreName = "Foo";
|
||||
const data = { key: 1, value: "bar" };
|
||||
|
@ -27,84 +28,44 @@
|
|||
is(e.name, "TypeError", "Good error name.");
|
||||
}
|
||||
|
||||
let request = indexedDB.open(name, { version: version,
|
||||
storage: "persistent" });
|
||||
request.onerror = grabEventAndContinueHandler;
|
||||
request.onupgradeneeded = unexpectedSuccessHandler;
|
||||
request.onsuccess = unexpectedSuccessHandler;
|
||||
let event = yield undefined;
|
||||
for (let storage of storages) {
|
||||
let request = indexedDB.open(name, { version: version,
|
||||
storage: storage });
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
let event = yield undefined;
|
||||
|
||||
is(event.type, "error", "Got error event");
|
||||
is(event.target, request, "Got correct target");
|
||||
is(event.target.error.name, "UnknownError", "Got correct error name");
|
||||
event.preventDefault();
|
||||
is(event.type, "upgradeneeded", "Got correct event type");
|
||||
|
||||
request = indexedDB.open(name, { version: version });
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield undefined;
|
||||
let db = event.target.result;
|
||||
db.onerror = errorHandler;
|
||||
|
||||
is(event.type, "upgradeneeded", "Got correct event type");
|
||||
let objectStore = db.createObjectStore(objectStoreName, { });
|
||||
|
||||
let db = event.target.result;
|
||||
db.onerror = errorHandler;
|
||||
event = yield undefined;
|
||||
|
||||
let objectStore = db.createObjectStore(objectStoreName, { });
|
||||
is(event.type, "success", "Got correct event type");
|
||||
|
||||
event = yield undefined;
|
||||
is(db.name, name, "Correct name");
|
||||
is(db.version, version, "Correct version");
|
||||
is(db.storage, storage, "Correct persistence type");
|
||||
|
||||
is(event.type, "success", "Got correct event type");
|
||||
objectStore = db.transaction([objectStoreName], "readwrite")
|
||||
.objectStore(objectStoreName);
|
||||
|
||||
is(db.name, name, "Correct name");
|
||||
is(db.version, version, "Correct version");
|
||||
is(db.storage, "persistent", "Correct persistence type");
|
||||
request = objectStore.get(data.key);
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield undefined;
|
||||
|
||||
objectStore = db.transaction([objectStoreName], "readwrite")
|
||||
.objectStore(objectStoreName);
|
||||
is(event.target.result, null, "Got no data");
|
||||
|
||||
request = objectStore.get(data.key);
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield undefined;
|
||||
request = objectStore.add(data.value, data.key);
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield undefined;
|
||||
|
||||
is(event.target.result, null, "Got no data");
|
||||
|
||||
request = objectStore.add(data.value, data.key);
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield undefined;
|
||||
|
||||
is(event.target.result, data.key, "Got correct key");
|
||||
|
||||
request = indexedDB.open(name, { version: version,
|
||||
storage: "temporary" });
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = grabEventAndContinueHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield undefined;
|
||||
|
||||
is(event.type, "upgradeneeded", "Got correct event type");
|
||||
|
||||
db = event.target.result;
|
||||
db.onerror = errorHandler;
|
||||
|
||||
objectStore = db.createObjectStore(objectStoreName, { });
|
||||
|
||||
event = yield undefined;
|
||||
|
||||
is(event.type, "success", "Got correct event type");
|
||||
|
||||
is(db.name, name, "Correct name");
|
||||
is(db.version, version, "Correct version");
|
||||
is(db.storage, "temporary", "Correct persistence type");
|
||||
|
||||
objectStore = db.transaction([objectStoreName])
|
||||
.objectStore(objectStoreName);
|
||||
|
||||
request = objectStore.get(data.key);
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
event = yield undefined;
|
||||
|
||||
is(event.target.result, null, "Got no data");
|
||||
is(event.target.result, data.key, "Got correct key");
|
||||
}
|
||||
|
||||
finishTest();
|
||||
yield undefined;
|
||||
|
|
|
@ -62,6 +62,9 @@
|
|||
|
||||
function runTest() {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
SpecialPowers.addPermission("indexedDB", true, document);
|
||||
|
||||
window.addEventListener("message", messageListener, false);
|
||||
setiframe();
|
||||
}
|
||||
|
|
Двоичный файл не отображается.
|
@ -14,7 +14,7 @@ function testSteps()
|
|||
clearAllDatabases(continueToNextStepSync);
|
||||
yield undefined;
|
||||
|
||||
installPackagedProfile("bug1056939");
|
||||
installPackagedProfile("bug1056939_profile");
|
||||
|
||||
let request = indexedDB.open(dbName1, 1);
|
||||
request.onerror = errorHandler;
|
||||
|
@ -71,61 +71,3 @@ function testSteps()
|
|||
finishTest();
|
||||
yield undefined;
|
||||
}
|
||||
|
||||
function installPackagedProfile(packageName)
|
||||
{
|
||||
let directoryService = Cc["@mozilla.org/file/directory_service;1"]
|
||||
.getService(Ci.nsIProperties);
|
||||
|
||||
let profileDir = directoryService.get("ProfD", Ci.nsIFile);
|
||||
|
||||
let currentDir = directoryService.get("CurWorkD", Ci.nsIFile);
|
||||
|
||||
let packageFile = currentDir.clone();
|
||||
packageFile.append(packageName + ".zip");
|
||||
|
||||
let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"]
|
||||
.createInstance(Ci.nsIZipReader);
|
||||
zipReader.open(packageFile);
|
||||
|
||||
let entryNames = [];
|
||||
let entries = zipReader.findEntries(null);
|
||||
while (entries.hasMore()) {
|
||||
let entry = entries.getNext();
|
||||
if (entry != "create_db.html") {
|
||||
entryNames.push(entry);
|
||||
}
|
||||
}
|
||||
entryNames.sort();
|
||||
|
||||
for (let entryName of entryNames) {
|
||||
let zipentry = zipReader.getEntry(entryName);
|
||||
|
||||
let file = profileDir.clone();
|
||||
let split = entryName.split("/");
|
||||
for(let i = 0; i < split.length; i++) {
|
||||
file.append(split[i]);
|
||||
}
|
||||
|
||||
if (zipentry.isDirectory) {
|
||||
file.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0755", 8));
|
||||
} else {
|
||||
let istream = zipReader.getInputStream(entryName);
|
||||
|
||||
var ostream = Cc["@mozilla.org/network/file-output-stream;1"]
|
||||
.createInstance(Ci.nsIFileOutputStream);
|
||||
ostream.init(file, -1, parseInt("0644", 8), 0);
|
||||
|
||||
let bostream = Cc['@mozilla.org/network/buffered-output-stream;1']
|
||||
.createInstance(Ci.nsIBufferedOutputStream);
|
||||
bostream.init(ostream, 32768);
|
||||
|
||||
bostream.writeFrom(istream, istream.available());
|
||||
|
||||
istream.close();
|
||||
bostream.close();
|
||||
}
|
||||
}
|
||||
|
||||
zipReader.close();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
/**
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
var testGenerator = testSteps();
|
||||
|
||||
function testSteps()
|
||||
{
|
||||
const openParams = [
|
||||
// This one lives in storage/default/http+++localhost
|
||||
{ url: "http://localhost", dbName: "dbA", dbVersion: 1 },
|
||||
|
||||
// This one lives in storage/default/http+++www.mozilla.org
|
||||
{ url: "http://www.mozilla.org", dbName: "dbB", dbVersion: 1 },
|
||||
|
||||
// This one lives in storage/default/http+++www.mozilla.org+8080
|
||||
{ url: "http://www.mozilla.org:8080", dbName: "dbC", dbVersion: 1 },
|
||||
|
||||
// This one lives in storage/default/https+++www.mozilla.org
|
||||
{ url: "https://www.mozilla.org", dbName: "dbD", dbVersion: 1 },
|
||||
|
||||
// This one lives in storage/default/https+++www.mozilla.org+8080
|
||||
{ url: "https://www.mozilla.org:8080", dbName: "dbE", dbVersion: 1 },
|
||||
|
||||
// This one lives in storage/permanent/indexeddb+++fx-devtools
|
||||
{ url: "indexeddb://fx-devtools", dbName: "dbF",
|
||||
dbOptions: { version: 1, storage: "persistent" } },
|
||||
|
||||
// This one lives in storage/permanent/moz-safe-about+home
|
||||
{ url: "moz-safe-about:home", dbName: "dbG",
|
||||
dbOptions: { version: 1, storage: "persistent" } },
|
||||
|
||||
// This one lives in storage/default/file++++Users+joe+
|
||||
{ url: "file:///Users/joe/", dbName: "dbH", dbVersion: 1 },
|
||||
|
||||
// This one lives in storage/default/file++++Users+joe+index.html
|
||||
{ url: "file:///Users/joe/index.html", dbName: "dbI", dbVersion: 1 },
|
||||
|
||||
// This one lives in storage/default/file++++c++Users+joe+
|
||||
{ url: "file:///c:/Users/joe/", dbName: "dbJ", dbVersion: 1 },
|
||||
|
||||
// This one lives in storage/default/file++++c++Users+joe+index.html
|
||||
{ url: "file:///c:/Users/joe/index.html", dbName: "dbK", dbVersion: 1 },
|
||||
|
||||
// This one lives in storage/permanent/chrome
|
||||
{ dbName: "dbL", dbVersion: 1 },
|
||||
|
||||
// This one lives in storage/default/1007+f+app+++system.gaiamobile.org
|
||||
{ appId: 1007, inMozBrowser: false, url: "app://system.gaiamobile.org",
|
||||
dbName: "dbM", dbVersion: 1 },
|
||||
|
||||
// This one lives in storage/default/1007+t+https+++developer.cdn.mozilla.net
|
||||
{ appId: 1007, inMozBrowser: true, url: "https://developer.cdn.mozilla.net",
|
||||
dbName: "dbN", dbVersion: 1 },
|
||||
|
||||
// This one lives in storage/default/http+++127.0.0.1
|
||||
{ url: "http://127.0.0.1", dbName: "dbO", dbVersion: 1 },
|
||||
|
||||
// This one lives in storage/default/file++++
|
||||
{ url: "file:///", dbName: "dbP", dbVersion: 1 },
|
||||
|
||||
// This one lives in storage/default/file++++c++
|
||||
{ url: "file:///c:/", dbName: "dbQ", dbVersion: 1 },
|
||||
|
||||
// This one lives in storage/temporary/http+++localhost
|
||||
{ url: "http://localhost", dbName: "dbZ",
|
||||
dbOptions: { version: 1, storage: "temporary" } }
|
||||
];
|
||||
|
||||
let ios = SpecialPowers.Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(SpecialPowers.Ci.nsIIOService);
|
||||
|
||||
let ssm = SpecialPowers.Cc["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(SpecialPowers.Ci.nsIScriptSecurityManager);
|
||||
|
||||
function openDatabase(params) {
|
||||
let request;
|
||||
if ("url" in params) {
|
||||
let uri = ios.newURI(params.url, null, null);
|
||||
let principal;
|
||||
if ("appId" in params) {
|
||||
principal = ssm.getAppCodebasePrincipal(uri, params.appId,
|
||||
params.inMozBrowser);
|
||||
} else {
|
||||
principal = ssm.getNoAppCodebasePrincipal(uri);
|
||||
}
|
||||
if ("dbVersion" in params) {
|
||||
request = indexedDB.openForPrincipal(principal, params.dbName,
|
||||
params.dbVersion);
|
||||
} else {
|
||||
request = indexedDB.openForPrincipal(principal, params.dbName,
|
||||
params.dbOptions);
|
||||
}
|
||||
} else {
|
||||
if ("dbVersion" in params) {
|
||||
request = indexedDB.open(params.dbName, params.dbVersion);
|
||||
} else {
|
||||
request = indexedDB.open(params.dbName, params.dbOptions);
|
||||
}
|
||||
}
|
||||
return request;
|
||||
}
|
||||
|
||||
clearAllDatabases(continueToNextStepSync);
|
||||
yield undefined;
|
||||
|
||||
installPackagedProfile("defaultStorageUpgrade_profile");
|
||||
|
||||
for (let params of openParams) {
|
||||
let request = openDatabase(params);
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = unexpectedSuccessHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
let event = yield undefined;
|
||||
|
||||
is(event.type, "success", "Correct event type");
|
||||
}
|
||||
|
||||
resetAllDatabases(continueToNextStepSync);
|
||||
yield undefined;
|
||||
|
||||
for (let params of openParams) {
|
||||
let request = openDatabase(params);
|
||||
request.onerror = errorHandler;
|
||||
request.onupgradeneeded = unexpectedSuccessHandler;
|
||||
request.onsuccess = grabEventAndContinueHandler;
|
||||
let event = yield undefined;
|
||||
|
||||
is(event.type, "success", "Correct event type");
|
||||
}
|
||||
|
||||
finishTest();
|
||||
yield undefined;
|
||||
}
|
|
@ -278,6 +278,64 @@ function clearAllDatabases(callback) {
|
|||
resetOrClearAllDatabases(callback, true);
|
||||
}
|
||||
|
||||
function installPackagedProfile(packageName)
|
||||
{
|
||||
let directoryService = Cc["@mozilla.org/file/directory_service;1"]
|
||||
.getService(Ci.nsIProperties);
|
||||
|
||||
let profileDir = directoryService.get("ProfD", Ci.nsIFile);
|
||||
|
||||
let currentDir = directoryService.get("CurWorkD", Ci.nsIFile);
|
||||
|
||||
let packageFile = currentDir.clone();
|
||||
packageFile.append(packageName + ".zip");
|
||||
|
||||
let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"]
|
||||
.createInstance(Ci.nsIZipReader);
|
||||
zipReader.open(packageFile);
|
||||
|
||||
let entryNames = [];
|
||||
let entries = zipReader.findEntries(null);
|
||||
while (entries.hasMore()) {
|
||||
let entry = entries.getNext();
|
||||
if (entry != "create_db.html") {
|
||||
entryNames.push(entry);
|
||||
}
|
||||
}
|
||||
entryNames.sort();
|
||||
|
||||
for (let entryName of entryNames) {
|
||||
let zipentry = zipReader.getEntry(entryName);
|
||||
|
||||
let file = profileDir.clone();
|
||||
let split = entryName.split("/");
|
||||
for(let i = 0; i < split.length; i++) {
|
||||
file.append(split[i]);
|
||||
}
|
||||
|
||||
if (zipentry.isDirectory) {
|
||||
file.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0755", 8));
|
||||
} else {
|
||||
let istream = zipReader.getInputStream(entryName);
|
||||
|
||||
var ostream = Cc["@mozilla.org/network/file-output-stream;1"]
|
||||
.createInstance(Ci.nsIFileOutputStream);
|
||||
ostream.init(file, -1, parseInt("0644", 8), 0);
|
||||
|
||||
let bostream = Cc['@mozilla.org/network/buffered-output-stream;1']
|
||||
.createInstance(Ci.nsIBufferedOutputStream);
|
||||
bostream.init(ostream, 32768);
|
||||
|
||||
bostream.writeFrom(istream, istream.available());
|
||||
|
||||
istream.close();
|
||||
bostream.close();
|
||||
}
|
||||
}
|
||||
|
||||
zipReader.close();
|
||||
}
|
||||
|
||||
var SpecialPowers = {
|
||||
isMainProcess: function() {
|
||||
return Components.classes["@mozilla.org/xre/app-info;1"]
|
||||
|
|
|
@ -8,7 +8,8 @@ head = xpcshell-head-parent-process.js
|
|||
tail =
|
||||
skip-if = toolkit == 'gonk'
|
||||
support-files =
|
||||
bug1056939.zip
|
||||
bug1056939_profile.zip
|
||||
defaultStorageUpgrade_profile.zip
|
||||
GlobalObjectsChild.js
|
||||
GlobalObjectsComponent.js
|
||||
GlobalObjectsComponent.manifest
|
||||
|
@ -20,6 +21,7 @@ support-files =
|
|||
|
||||
[test_blob_file_backed.js]
|
||||
[test_bug1056939.js]
|
||||
[test_defaultStorageUpgrade.js]
|
||||
[test_globalObjects_ipc.js]
|
||||
skip-if = toolkit == 'android'
|
||||
[test_invalidate.js]
|
||||
|
|
|
@ -120,6 +120,8 @@ function start()
|
|||
SpecialPowers.addPermission("browser", true, { manifestURL: manifestURL,
|
||||
isInBrowserElement: false });
|
||||
SpecialPowers.addPermission("embed-apps", true, document);
|
||||
SpecialPowers.addPermission("indexedDB", true, { manifestURL: manifestURL,
|
||||
isInBrowserElement: false });
|
||||
|
||||
SpecialPowers.setAllAppsLaunchable(true);
|
||||
|
||||
|
@ -131,6 +133,8 @@ function start()
|
|||
SpecialPowers.removePermission("browser",
|
||||
location.protocol + "//" + appDomain);
|
||||
SpecialPowers.removePermission("embed-apps", location.href);
|
||||
SpecialPowers.removePermission("indexedDB",
|
||||
location.protocol + "//" + appDomain);
|
||||
}
|
||||
}, false);
|
||||
|
||||
|
|
|
@ -4278,7 +4278,8 @@ ContentParent::RecvGetFileReferences(const PersistenceType& aPersistenceType,
|
|||
MOZ_ASSERT(aResult);
|
||||
|
||||
if (NS_WARN_IF(aPersistenceType != quota::PERSISTENCE_TYPE_PERSISTENT &&
|
||||
aPersistenceType != quota::PERSISTENCE_TYPE_TEMPORARY)) {
|
||||
aPersistenceType != quota::PERSISTENCE_TYPE_TEMPORARY &&
|
||||
aPersistenceType != quota::PERSISTENCE_TYPE_DEFAULT)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,7 +15,6 @@
|
|||
#include "MediaStreamGraph.h"
|
||||
#include "AudioStreamTrack.h"
|
||||
#include "VideoStreamTrack.h"
|
||||
#include "MediaEngine.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
|
@ -54,8 +53,8 @@ public:
|
|||
if (!track) {
|
||||
stream->CreateDOMTrack(mID, mType);
|
||||
track = stream->BindDOMTrack(mID, mType);
|
||||
stream->NotifyMediaStreamTrackCreated(track);
|
||||
}
|
||||
stream->NotifyMediaStreamTrackCreated(track);
|
||||
} else {
|
||||
track = stream->GetDOMTrackFor(mID);
|
||||
}
|
||||
|
@ -325,13 +324,16 @@ DOMMediaStream::RemovePrincipalChangeObserver(PrincipalChangeObserver* aObserver
|
|||
void
|
||||
DOMMediaStream::SetHintContents(TrackTypeHints aHintContents)
|
||||
{
|
||||
mHintContents = aHintContents;
|
||||
if (aHintContents & HINT_CONTENTS_AUDIO) {
|
||||
CreateDOMTrack(kAudioTrack, MediaSegment::AUDIO);
|
||||
}
|
||||
if (aHintContents & HINT_CONTENTS_VIDEO) {
|
||||
TrackTypeHints oldHintContents = mHintContents;
|
||||
mHintContents |= aHintContents;
|
||||
if (aHintContents & HINT_CONTENTS_VIDEO &&
|
||||
!(oldHintContents & HINT_CONTENTS_VIDEO)) {
|
||||
CreateDOMTrack(kVideoTrack, MediaSegment::VIDEO);
|
||||
}
|
||||
if (aHintContents & HINT_CONTENTS_AUDIO &&
|
||||
!(oldHintContents & HINT_CONTENTS_AUDIO)) {
|
||||
CreateDOMTrack(kAudioTrack, MediaSegment::AUDIO);
|
||||
}
|
||||
}
|
||||
|
||||
MediaStreamTrack*
|
||||
|
@ -340,9 +342,11 @@ DOMMediaStream::CreateDOMTrack(TrackID aTrackID, MediaSegment::Type aType)
|
|||
MediaStreamTrack* track;
|
||||
switch (aType) {
|
||||
case MediaSegment::AUDIO:
|
||||
mHintContents |= HINT_CONTENTS_AUDIO;
|
||||
track = new AudioStreamTrack(this, aTrackID);
|
||||
break;
|
||||
case MediaSegment::VIDEO:
|
||||
mHintContents |= HINT_CONTENTS_VIDEO;
|
||||
track = new VideoStreamTrack(this, aTrackID);
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -48,6 +48,12 @@ class MediaTrackListListener;
|
|||
|
||||
class MediaStreamDirectListener;
|
||||
|
||||
// For classes that need fixed track IDs
|
||||
enum {
|
||||
kVideoTrack = 1,
|
||||
kAudioTrack = 2
|
||||
};
|
||||
|
||||
#define NS_DOMMEDIASTREAM_IID \
|
||||
{ 0x8cb65468, 0x66c0, 0x444e, \
|
||||
{ 0x89, 0x9f, 0x89, 0x1d, 0x9e, 0xd2, 0xbe, 0x7c } }
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "mozilla/Preferences.h"
|
||||
#include "gfx2DGlue.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "DOMMediaStream.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
@ -356,9 +357,6 @@ static void WriteVideoToMediaStream(MediaStream* aStream,
|
|||
aOutput->AppendFrame(image.forget(), duration, aIntrinsicSize);
|
||||
}
|
||||
|
||||
static const TrackID TRACK_AUDIO = 1;
|
||||
static const TrackID TRACK_VIDEO = 2;
|
||||
|
||||
void MediaDecoderStateMachine::SendStreamData()
|
||||
{
|
||||
NS_ASSERTION(OnDecodeThread() || OnStateMachineThread(),
|
||||
|
@ -394,14 +392,14 @@ void MediaDecoderStateMachine::SendStreamData()
|
|||
if (!stream->mStreamInitialized) {
|
||||
if (mInfo.HasAudio()) {
|
||||
AudioSegment* audio = new AudioSegment();
|
||||
mediaStream->AddAudioTrack(TRACK_AUDIO, mInfo.mAudio.mRate, 0, audio);
|
||||
stream->mStream->DispatchWhenNotEnoughBuffered(TRACK_AUDIO,
|
||||
mediaStream->AddAudioTrack(kAudioTrack, mInfo.mAudio.mRate, 0, audio);
|
||||
stream->mStream->DispatchWhenNotEnoughBuffered(kAudioTrack,
|
||||
GetStateMachineThread(), GetWakeDecoderRunnable());
|
||||
}
|
||||
if (mInfo.HasVideo()) {
|
||||
VideoSegment* video = new VideoSegment();
|
||||
mediaStream->AddTrack(TRACK_VIDEO, 0, video);
|
||||
stream->mStream->DispatchWhenNotEnoughBuffered(TRACK_VIDEO,
|
||||
mediaStream->AddTrack(kVideoTrack, 0, video);
|
||||
stream->mStream->DispatchWhenNotEnoughBuffered(kVideoTrack,
|
||||
GetStateMachineThread(), GetWakeDecoderRunnable());
|
||||
}
|
||||
stream->mStreamInitialized = true;
|
||||
|
@ -417,10 +415,10 @@ void MediaDecoderStateMachine::SendStreamData()
|
|||
SendStreamAudio(audio[i], stream, &output);
|
||||
}
|
||||
if (output.GetDuration() > 0) {
|
||||
mediaStream->AppendToTrack(TRACK_AUDIO, &output);
|
||||
mediaStream->AppendToTrack(kAudioTrack, &output);
|
||||
}
|
||||
if (AudioQueue().IsFinished() && !stream->mHaveSentFinishAudio) {
|
||||
mediaStream->EndTrack(TRACK_AUDIO);
|
||||
mediaStream->EndTrack(kAudioTrack);
|
||||
stream->mHaveSentFinishAudio = true;
|
||||
}
|
||||
minLastAudioPacketTime = std::min(minLastAudioPacketTime, stream->mLastAudioPacketTime);
|
||||
|
@ -462,10 +460,10 @@ void MediaDecoderStateMachine::SendStreamData()
|
|||
}
|
||||
}
|
||||
if (output.GetDuration() > 0) {
|
||||
mediaStream->AppendToTrack(TRACK_VIDEO, &output);
|
||||
mediaStream->AppendToTrack(kVideoTrack, &output);
|
||||
}
|
||||
if (VideoQueue().IsFinished() && !stream->mHaveSentFinishVideo) {
|
||||
mediaStream->EndTrack(TRACK_VIDEO);
|
||||
mediaStream->EndTrack(kVideoTrack);
|
||||
stream->mHaveSentFinishVideo = true;
|
||||
}
|
||||
endPosition = std::max(endPosition,
|
||||
|
@ -531,10 +529,10 @@ bool MediaDecoderStateMachine::HaveEnoughDecodedAudio(int64_t aAmpleAudioUSecs)
|
|||
|
||||
DecodedStreamData* stream = mDecoder->GetDecodedStream();
|
||||
if (stream && stream->mStreamInitialized && !stream->mHaveSentFinishAudio) {
|
||||
if (!stream->mStream->HaveEnoughBuffered(TRACK_AUDIO)) {
|
||||
if (!stream->mStream->HaveEnoughBuffered(kAudioTrack)) {
|
||||
return false;
|
||||
}
|
||||
stream->mStream->DispatchWhenNotEnoughBuffered(TRACK_AUDIO,
|
||||
stream->mStream->DispatchWhenNotEnoughBuffered(kAudioTrack,
|
||||
GetStateMachineThread(), GetWakeDecoderRunnable());
|
||||
}
|
||||
|
||||
|
@ -551,10 +549,10 @@ bool MediaDecoderStateMachine::HaveEnoughDecodedVideo()
|
|||
|
||||
DecodedStreamData* stream = mDecoder->GetDecodedStream();
|
||||
if (stream && stream->mStreamInitialized && !stream->mHaveSentFinishVideo) {
|
||||
if (!stream->mStream->HaveEnoughBuffered(TRACK_VIDEO)) {
|
||||
if (!stream->mStream->HaveEnoughBuffered(kVideoTrack)) {
|
||||
return false;
|
||||
}
|
||||
stream->mStream->DispatchWhenNotEnoughBuffered(TRACK_VIDEO,
|
||||
stream->mStream->DispatchWhenNotEnoughBuffered(kVideoTrack,
|
||||
GetStateMachineThread(), GetWakeDecoderRunnable());
|
||||
}
|
||||
|
||||
|
|
|
@ -984,6 +984,7 @@ RTCPeerConnection.prototype = {
|
|||
|
||||
get peerIdentity() { return this._peerIdentity; },
|
||||
get id() { return this._impl.id; },
|
||||
set id(s) { this._impl.id = s; },
|
||||
get iceGatheringState() { return this._iceGatheringState; },
|
||||
get iceConnectionState() { return this._iceConnectionState; },
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "AppleUtils.h"
|
||||
#include "MP4Reader.h"
|
||||
#include "MP4Decoder.h"
|
||||
#include "mp4_demuxer/Adts.h"
|
||||
#include "mp4_demuxer/DecoderData.h"
|
||||
#include "AppleATDecoder.h"
|
||||
#include "prlog.h"
|
||||
|
@ -17,6 +18,7 @@ PRLogModuleInfo* GetAppleMediaLog();
|
|||
#else
|
||||
#define LOG(...)
|
||||
#endif
|
||||
#define FourCC2Str(n) ((char[5]){(char)(n >> 24), (char)(n >> 16), (char)(n >> 8), (char)(n), 0})
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
@ -24,9 +26,11 @@ AppleATDecoder::AppleATDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
|
|||
MediaTaskQueue* aAudioTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback)
|
||||
: mConfig(aConfig)
|
||||
, mFileStreamError(false)
|
||||
, mTaskQueue(aAudioTaskQueue)
|
||||
, mCallback(aCallback)
|
||||
, mConverter(nullptr)
|
||||
, mStream(nullptr)
|
||||
{
|
||||
MOZ_COUNT_CTOR(AppleATDecoder);
|
||||
LOG("Creating Apple AudioToolbox decoder");
|
||||
|
@ -58,38 +62,6 @@ AppleATDecoder::Init()
|
|||
NS_ERROR("Non recognised format");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
LOG("Initializing Apple AudioToolbox decoder");
|
||||
AudioStreamBasicDescription inputFormat;
|
||||
PodZero(&inputFormat);
|
||||
|
||||
if (NS_FAILED(GetInputAudioDescription(inputFormat))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
// Fill in the output format manually.
|
||||
PodZero(&mOutputFormat);
|
||||
mOutputFormat.mFormatID = kAudioFormatLinearPCM;
|
||||
mOutputFormat.mSampleRate = inputFormat.mSampleRate;
|
||||
mOutputFormat.mChannelsPerFrame = inputFormat.mChannelsPerFrame;
|
||||
#if defined(MOZ_SAMPLE_TYPE_FLOAT32)
|
||||
mOutputFormat.mBitsPerChannel = 32;
|
||||
mOutputFormat.mFormatFlags =
|
||||
kLinearPCMFormatFlagIsFloat |
|
||||
0;
|
||||
#else
|
||||
# error Unknown audio sample type
|
||||
#endif
|
||||
// Set up the decoder so it gives us one sample per frame
|
||||
mOutputFormat.mFramesPerPacket = 1;
|
||||
mOutputFormat.mBytesPerPacket = mOutputFormat.mBytesPerFrame
|
||||
= mOutputFormat.mChannelsPerFrame * mOutputFormat.mBitsPerChannel / 8;
|
||||
|
||||
OSStatus rv = AudioConverterNew(&inputFormat, &mOutputFormat, &mConverter);
|
||||
if (rv) {
|
||||
LOG("Error %d constructing AudioConverter", rv);
|
||||
mConverter = nullptr;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -118,6 +90,7 @@ AppleATDecoder::Flush()
|
|||
{
|
||||
LOG("Flushing AudioToolbox AAC decoder");
|
||||
mTaskQueue->Flush();
|
||||
mQueuedSamples.Clear();
|
||||
OSStatus rv = AudioConverterReset(mConverter);
|
||||
if (rv) {
|
||||
LOG("Error %d resetting AudioConverter", rv);
|
||||
|
@ -139,12 +112,22 @@ nsresult
|
|||
AppleATDecoder::Shutdown()
|
||||
{
|
||||
LOG("Shutdown: Apple AudioToolbox AAC decoder");
|
||||
mQueuedSamples.Clear();
|
||||
OSStatus rv = AudioConverterDispose(mConverter);
|
||||
if (rv) {
|
||||
LOG("error %d disposing of AudioConverter", rv);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mConverter = nullptr;
|
||||
|
||||
if (mStream) {
|
||||
rv = AudioFileStreamClose(mStream);
|
||||
if (rv) {
|
||||
LOG("error %d disposing of AudioFileStream", rv);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mStream = nullptr;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -193,6 +176,36 @@ _PassthroughInputDataCallback(AudioConverterRef aAudioConverter,
|
|||
|
||||
void
|
||||
AppleATDecoder::SubmitSample(nsAutoPtr<mp4_demuxer::MP4Sample> aSample)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
if (!mConverter) {
|
||||
rv = SetupDecoder(aSample);
|
||||
if (rv != NS_OK && rv != NS_ERROR_NOT_INITIALIZED) {
|
||||
mCallback->Error();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
mQueuedSamples.AppendElement(aSample);
|
||||
|
||||
if (rv == NS_OK) {
|
||||
for (size_t i = 0; i < mQueuedSamples.Length(); i++) {
|
||||
if (NS_FAILED(DecodeSample(mQueuedSamples[i]))) {
|
||||
mQueuedSamples.Clear();
|
||||
mCallback->Error();
|
||||
return;
|
||||
}
|
||||
}
|
||||
mQueuedSamples.Clear();
|
||||
}
|
||||
|
||||
if (mTaskQueue->IsEmpty()) {
|
||||
mCallback->InputExhausted();
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
AppleATDecoder::DecodeSample(mp4_demuxer::MP4Sample* aSample)
|
||||
{
|
||||
// Array containing the queued decoded audio frames, about to be output.
|
||||
nsTArray<AudioDataValue> outputData;
|
||||
|
@ -235,8 +248,7 @@ AppleATDecoder::SubmitSample(nsAutoPtr<mp4_demuxer::MP4Sample> aSample)
|
|||
|
||||
if (rv && rv != kNoMoreDataErr) {
|
||||
LOG("Error decoding audio stream: %d\n", rv);
|
||||
mCallback->Error();
|
||||
return;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (numFrames) {
|
||||
|
@ -250,40 +262,38 @@ AppleATDecoder::SubmitSample(nsAutoPtr<mp4_demuxer::MP4Sample> aSample)
|
|||
}
|
||||
} while (true);
|
||||
|
||||
if (!outputData.IsEmpty()) {
|
||||
size_t numFrames = outputData.Length() / channels;
|
||||
int rate = mOutputFormat.mSampleRate;
|
||||
CheckedInt<Microseconds> duration = FramesToUsecs(numFrames, rate);
|
||||
if (!duration.isValid()) {
|
||||
NS_WARNING("Invalid count of accumulated audio samples");
|
||||
mCallback->Error();
|
||||
return;
|
||||
}
|
||||
|
||||
LOG("pushed audio at time %lfs; duration %lfs\n",
|
||||
(double)aSample->composition_timestamp / USECS_PER_S,
|
||||
(double)duration.value() / USECS_PER_S);
|
||||
|
||||
nsAutoArrayPtr<AudioDataValue>
|
||||
data(new AudioDataValue[outputData.Length()]);
|
||||
PodCopy(data.get(), &outputData[0], outputData.Length());
|
||||
nsRefPtr<AudioData> audio = new AudioData(aSample->byte_offset,
|
||||
aSample->composition_timestamp,
|
||||
duration.value(),
|
||||
numFrames,
|
||||
data.forget(),
|
||||
channels,
|
||||
rate);
|
||||
mCallback->Output(audio);
|
||||
if (outputData.IsEmpty()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (mTaskQueue->IsEmpty()) {
|
||||
mCallback->InputExhausted();
|
||||
size_t numFrames = outputData.Length() / channels;
|
||||
int rate = mOutputFormat.mSampleRate;
|
||||
CheckedInt<Microseconds> duration = FramesToUsecs(numFrames, rate);
|
||||
if (!duration.isValid()) {
|
||||
NS_WARNING("Invalid count of accumulated audio samples");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
LOG("pushed audio at time %lfs; duration %lfs\n",
|
||||
(double)aSample->composition_timestamp / USECS_PER_S,
|
||||
(double)duration.value() / USECS_PER_S);
|
||||
|
||||
nsAutoArrayPtr<AudioDataValue> data(new AudioDataValue[outputData.Length()]);
|
||||
PodCopy(data.get(), &outputData[0], outputData.Length());
|
||||
nsRefPtr<AudioData> audio = new AudioData(aSample->byte_offset,
|
||||
aSample->composition_timestamp,
|
||||
duration.value(),
|
||||
numFrames,
|
||||
data.forget(),
|
||||
channels,
|
||||
rate);
|
||||
mCallback->Output(audio);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
AppleATDecoder::GetInputAudioDescription(AudioStreamBasicDescription& aDesc)
|
||||
AppleATDecoder::GetInputAudioDescription(AudioStreamBasicDescription& aDesc,
|
||||
const mozilla::Vector<uint8_t>& aExtraData)
|
||||
{
|
||||
// Request the properties from CoreAudio using the codec magic cookie
|
||||
AudioFormatInfo formatInfo;
|
||||
|
@ -292,8 +302,8 @@ AppleATDecoder::GetInputAudioDescription(AudioStreamBasicDescription& aDesc)
|
|||
if (mFormatID == kAudioFormatMPEG4AAC) {
|
||||
formatInfo.mASBD.mFormatFlags = mConfig.extended_profile;
|
||||
}
|
||||
formatInfo.mMagicCookieSize = mConfig.extra_data.length();
|
||||
formatInfo.mMagicCookie = mConfig.extra_data.begin();
|
||||
formatInfo.mMagicCookieSize = aExtraData.length();
|
||||
formatInfo.mMagicCookie = aExtraData.begin();
|
||||
|
||||
UInt32 formatListSize;
|
||||
// Attempt to retrieve the default format using
|
||||
|
@ -355,4 +365,143 @@ AppleATDecoder::GetInputAudioDescription(AudioStreamBasicDescription& aDesc)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
AppleATDecoder::SetupDecoder(mp4_demuxer::MP4Sample* aSample)
|
||||
{
|
||||
if (mFormatID == kAudioFormatMPEG4AAC &&
|
||||
mConfig.extended_profile == 2) {
|
||||
// Check for implicit SBR signalling if stream is AAC-LC
|
||||
// This will provide us with an updated magic cookie for use with
|
||||
// GetInputAudioDescription.
|
||||
if (NS_SUCCEEDED(GetImplicitAACMagicCookie(aSample)) &&
|
||||
!mMagicCookie.length()) {
|
||||
// nothing found yet, will try again later
|
||||
return NS_ERROR_NOT_INITIALIZED;
|
||||
}
|
||||
// An error occurred, fallback to using default stream description
|
||||
}
|
||||
|
||||
LOG("Initializing Apple AudioToolbox decoder");
|
||||
|
||||
AudioStreamBasicDescription inputFormat;
|
||||
PodZero(&inputFormat);
|
||||
nsresult rv =
|
||||
GetInputAudioDescription(inputFormat,
|
||||
mMagicCookie.length() ?
|
||||
mMagicCookie : mConfig.extra_data);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
// Fill in the output format manually.
|
||||
PodZero(&mOutputFormat);
|
||||
mOutputFormat.mFormatID = kAudioFormatLinearPCM;
|
||||
mOutputFormat.mSampleRate = inputFormat.mSampleRate;
|
||||
mOutputFormat.mChannelsPerFrame = inputFormat.mChannelsPerFrame;
|
||||
#if defined(MOZ_SAMPLE_TYPE_FLOAT32)
|
||||
mOutputFormat.mBitsPerChannel = 32;
|
||||
mOutputFormat.mFormatFlags =
|
||||
kLinearPCMFormatFlagIsFloat |
|
||||
0;
|
||||
#else
|
||||
# error Unknown audio sample type
|
||||
#endif
|
||||
// Set up the decoder so it gives us one sample per frame
|
||||
mOutputFormat.mFramesPerPacket = 1;
|
||||
mOutputFormat.mBytesPerPacket = mOutputFormat.mBytesPerFrame
|
||||
= mOutputFormat.mChannelsPerFrame * mOutputFormat.mBitsPerChannel / 8;
|
||||
|
||||
OSStatus status = AudioConverterNew(&inputFormat, &mOutputFormat, &mConverter);
|
||||
if (status) {
|
||||
LOG("Error %d constructing AudioConverter", status);
|
||||
mConverter = nullptr;
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static void
|
||||
_MetadataCallback(void* aAppleATDecoder,
|
||||
AudioFileStreamID aStream,
|
||||
AudioFileStreamPropertyID aProperty,
|
||||
UInt32* aFlags)
|
||||
{
|
||||
AppleATDecoder* decoder = static_cast<AppleATDecoder*>(aAppleATDecoder);
|
||||
LOG("MetadataCallback receiving: '%s'", FourCC2Str(aProperty));
|
||||
if (aProperty == kAudioFileStreamProperty_MagicCookieData) {
|
||||
UInt32 size;
|
||||
Boolean writeable;
|
||||
OSStatus rv = AudioFileStreamGetPropertyInfo(aStream,
|
||||
aProperty,
|
||||
&size,
|
||||
&writeable);
|
||||
if (rv) {
|
||||
LOG("Couldn't get property info for '%s' (%s)",
|
||||
FourCC2Str(aProperty), FourCC2Str(rv));
|
||||
decoder->mFileStreamError = true;
|
||||
return;
|
||||
}
|
||||
nsAutoArrayPtr<uint8_t> data(new uint8_t[size]);
|
||||
rv = AudioFileStreamGetProperty(aStream, aProperty,
|
||||
&size, data);
|
||||
if (rv) {
|
||||
LOG("Couldn't get property '%s' (%s)",
|
||||
FourCC2Str(aProperty), FourCC2Str(rv));
|
||||
decoder->mFileStreamError = true;
|
||||
return;
|
||||
}
|
||||
decoder->mMagicCookie.append(data.get(), size);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
_SampleCallback(void* aSBR,
|
||||
UInt32 aNumBytes,
|
||||
UInt32 aNumPackets,
|
||||
const void* aData,
|
||||
AudioStreamPacketDescription* aPackets)
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
AppleATDecoder::GetImplicitAACMagicCookie(const mp4_demuxer::MP4Sample* aSample)
|
||||
{
|
||||
// Prepend ADTS header to AAC audio.
|
||||
mp4_demuxer::MP4Sample adtssample(*aSample);
|
||||
bool rv = mp4_demuxer::Adts::ConvertSample(mConfig.channel_count,
|
||||
mConfig.frequency_index,
|
||||
mConfig.aac_profile,
|
||||
&adtssample);
|
||||
if (!rv) {
|
||||
NS_WARNING("Failed to apply ADTS header");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (!mStream) {
|
||||
OSStatus rv = AudioFileStreamOpen(this,
|
||||
_MetadataCallback,
|
||||
_SampleCallback,
|
||||
kAudioFileAAC_ADTSType,
|
||||
&mStream);
|
||||
if (rv) {
|
||||
NS_WARNING("Couldn't open AudioFileStream");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
OSStatus status = AudioFileStreamParseBytes(mStream,
|
||||
adtssample.size,
|
||||
adtssample.data,
|
||||
0 /* discontinuity */);
|
||||
if (status) {
|
||||
NS_WARNING("Couldn't parse sample");
|
||||
}
|
||||
|
||||
if (status || mFileStreamError || mMagicCookie.length()) {
|
||||
// We have decoded a magic cookie or an error occurred as such
|
||||
// we won't need the stream any longer.
|
||||
AudioFileStreamClose(mStream);
|
||||
mStream = nullptr;
|
||||
}
|
||||
|
||||
return (mFileStreamError || status) ? NS_ERROR_FAILURE : NS_OK;
|
||||
}
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "PlatformDecoderModule.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
#include "mozilla/Vector.h"
|
||||
#include "nsIThread.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -34,15 +35,29 @@ public:
|
|||
// Callbacks also need access to the config.
|
||||
const mp4_demuxer::AudioDecoderConfig& mConfig;
|
||||
|
||||
// Use to extract magic cookie for HE-AAC detection.
|
||||
mozilla::Vector<uint8_t> mMagicCookie;
|
||||
// Will be set to true should an error occurred while attempting to retrieve
|
||||
// the magic cookie property.
|
||||
bool mFileStreamError;
|
||||
|
||||
private:
|
||||
RefPtr<MediaTaskQueue> mTaskQueue;
|
||||
MediaDataDecoderCallback* mCallback;
|
||||
AudioConverterRef mConverter;
|
||||
AudioStreamBasicDescription mOutputFormat;
|
||||
UInt32 mFormatID;
|
||||
AudioFileStreamID mStream;
|
||||
nsTArray<nsAutoPtr<mp4_demuxer::MP4Sample>> mQueuedSamples;
|
||||
|
||||
void SubmitSample(nsAutoPtr<mp4_demuxer::MP4Sample> aSample);
|
||||
nsresult GetInputAudioDescription(AudioStreamBasicDescription& aDesc);
|
||||
nsresult DecodeSample(mp4_demuxer::MP4Sample* aSample);
|
||||
nsresult GetInputAudioDescription(AudioStreamBasicDescription& aDesc,
|
||||
const mozilla::Vector<uint8_t>& aExtraData);
|
||||
// Setup AudioConverter once all information required has been gathered.
|
||||
// Will return NS_ERROR_NOT_INITIALIZED if more data is required.
|
||||
nsresult SetupDecoder(mp4_demuxer::MP4Sample* aSample);
|
||||
nsresult GetImplicitAACMagicCookie(const mp4_demuxer::MP4Sample* aSample);
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче