This commit is contained in:
Phil Ringnalda 2014-11-28 15:15:30 -08:00
Родитель 28017df3c0 ed3d451991
Коммит 95e52e8429
339 изменённых файлов: 17501 добавлений и 5914 удалений

Просмотреть файл

@ -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();
}

Двоичные данные
dom/indexedDB/test/unit/defaultStorageUpgrade_profile.zip Normal file

Двоичный файл не отображается.

Просмотреть файл

@ -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

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше