Merge m-c to b2g-inbound.

This commit is contained in:
Ryan VanderMeulen 2013-08-21 17:18:26 -04:00
commit 6bfa72099d
132 changed files with 3349 additions and 1690 deletions

View File

@ -163,6 +163,9 @@ this.AccessFu = {
Services.obs.removeObserver(this, 'Accessibility:LongPress');
Services.obs.removeObserver(this, 'Accessibility:MoveByGranularity');
delete this._quicknavModesPref;
delete this._notifyOutputPref;
if (this.doneCallback) {
this.doneCallback();
delete this.doneCallback;
@ -171,6 +174,9 @@ this.AccessFu = {
_enableOrDisable: function _enableOrDisable() {
try {
if (!this._activatePref) {
return;
}
let activatePref = this._activatePref.value;
if (activatePref == ACCESSFU_ENABLE ||
this._systemPref && activatePref == ACCESSFU_AUTO)

View File

@ -14,10 +14,15 @@ const EVENT_TEXT_CARET_MOVED = Ci.nsIAccessibleEvent.EVENT_TEXT_CARET_MOVED;
const EVENT_TEXT_INSERTED = Ci.nsIAccessibleEvent.EVENT_TEXT_INSERTED;
const EVENT_TEXT_REMOVED = Ci.nsIAccessibleEvent.EVENT_TEXT_REMOVED;
const EVENT_FOCUS = Ci.nsIAccessibleEvent.EVENT_FOCUS;
const EVENT_SHOW = Ci.nsIAccessibleEvent.EVENT_SHOW;
const EVENT_HIDE = Ci.nsIAccessibleEvent.EVENT_HIDE;
const ROLE_INTERNAL_FRAME = Ci.nsIAccessibleRole.ROLE_INTERNAL_FRAME;
const ROLE_DOCUMENT = Ci.nsIAccessibleRole.ROLE_DOCUMENT;
const ROLE_CHROME_WINDOW = Ci.nsIAccessibleRole.ROLE_CHROME_WINDOW;
const ROLE_TEXT_LEAF = Ci.nsIAccessibleRole.ROLE_TEXT_LEAF;
const TEXT_NODE = 3;
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Services',
@ -140,7 +145,11 @@ this.EventManager.prototype = {
// Don't bother with non-content events in firefox.
if (Utils.MozBuildApp == 'browser' &&
aEvent.eventType != EVENT_VIRTUALCURSOR_CHANGED &&
aEvent.accessibleDocument.docType == 'window') {
// XXX Bug 442005 results in DocAccessible::getDocType returning
// NS_ERROR_FAILURE. Checking for aEvent.accessibleDocument.docType ==
// 'window' does not currently work.
(aEvent.accessibleDocument.DOMDocument.doctype &&
aEvent.accessibleDocument.DOMDocument.doctype.name === 'window')) {
return;
}
@ -219,28 +228,47 @@ this.EventManager.prototype = {
this.editState = editState;
break;
}
case EVENT_SHOW:
{
let {liveRegion, isPolite} = this._handleLiveRegion(aEvent,
['additions', 'all']);
// Only handle show if it is a relevant live region.
if (!liveRegion) {
break;
}
// Show for text is handled by the EVENT_TEXT_INSERTED handler.
if (aEvent.accessible.role === ROLE_TEXT_LEAF) {
break;
}
this._dequeueLiveEvent(EVENT_HIDE, liveRegion);
this.present(Presentation.liveRegion(liveRegion, isPolite, false));
break;
}
case EVENT_HIDE:
{
let {liveRegion, isPolite} = this._handleLiveRegion(
aEvent.QueryInterface(Ci.nsIAccessibleHideEvent),
['removals', 'all']);
// Only handle hide if it is a relevant live region.
if (!liveRegion) {
break;
}
// Hide for text is handled by the EVENT_TEXT_REMOVED handler.
if (aEvent.accessible.role === ROLE_TEXT_LEAF) {
break;
}
this._queueLiveEvent(EVENT_HIDE, liveRegion, isPolite);
break;
}
case EVENT_TEXT_INSERTED:
case EVENT_TEXT_REMOVED:
{
if (aEvent.isFromUserInput) {
// XXX support live regions as well.
let event = aEvent.QueryInterface(Ci.nsIAccessibleTextChangeEvent);
let isInserted = event.isInserted;
let txtIface = aEvent.accessible.QueryInterface(Ci.nsIAccessibleText);
let text = '';
try {
text = txtIface.
getText(0, Ci.nsIAccessibleText.TEXT_OFFSET_END_OF_TEXT);
} catch (x) {
// XXX we might have gotten an exception with of a
// zero-length text. If we did, ignore it (bug #749810).
if (txtIface.characterCount)
throw x;
}
this.present(Presentation.textChanged(
isInserted, event.start, event.length,
text, event.modifiedText));
let {liveRegion, isPolite} = this._handleLiveRegion(aEvent,
['text', 'all']);
if (aEvent.isFromUserInput || liveRegion) {
// Handle all text mutations coming from the user or if they happen
// on a live region.
this._handleText(aEvent, liveRegion, isPolite);
}
break;
}
@ -258,6 +286,130 @@ this.EventManager.prototype = {
}
},
_handleText: function _handleText(aEvent, aLiveRegion, aIsPolite) {
let event = aEvent.QueryInterface(Ci.nsIAccessibleTextChangeEvent);
let isInserted = event.isInserted;
let txtIface = aEvent.accessible.QueryInterface(Ci.nsIAccessibleText);
let text = '';
try {
text = txtIface.getText(0, Ci.nsIAccessibleText.TEXT_OFFSET_END_OF_TEXT);
} catch (x) {
// XXX we might have gotten an exception with of a
// zero-length text. If we did, ignore it (bug #749810).
if (txtIface.characterCount) {
throw x;
}
}
// If there are embedded objects in the text, ignore them.
// Assuming changes to the descendants would already be handled by the
// show/hide event.
let modifiedText = event.modifiedText.replace(/\uFFFC/g, '').trim();
if (!modifiedText) {
return;
}
if (aLiveRegion) {
if (aEvent.eventType === EVENT_TEXT_REMOVED) {
this._queueLiveEvent(EVENT_TEXT_REMOVED, aLiveRegion, aIsPolite,
modifiedText);
} else {
this._dequeueLiveEvent(EVENT_TEXT_REMOVED, aLiveRegion);
this.present(Presentation.liveRegion(aLiveRegion, aIsPolite, false,
modifiedText));
}
} else {
this.present(Presentation.textChanged(isInserted, event.start,
event.length, text, modifiedText));
}
},
_handleLiveRegion: function _handleLiveRegion(aEvent, aRelevant) {
if (aEvent.isFromUserInput) {
return {};
}
let parseLiveAttrs = function parseLiveAttrs(aAccessible) {
let attrs = Utils.getAttributes(aAccessible);
if (attrs['container-live']) {
return {
live: attrs['container-live'],
relevant: attrs['container-relevant'] || 'additions text',
busy: attrs['container-busy'],
atomic: attrs['container-atomic'],
memberOf: attrs['member-of']
};
}
return null;
};
// XXX live attributes are not set for hidden accessibles yet. Need to
// climb up the tree to check for them.
let getLiveAttributes = function getLiveAttributes(aEvent) {
let liveAttrs = parseLiveAttrs(aEvent.accessible);
if (liveAttrs) {
return liveAttrs;
}
let parent = aEvent.targetParent;
while (parent) {
liveAttrs = parseLiveAttrs(parent);
if (liveAttrs) {
return liveAttrs;
}
parent = parent.parent
}
return {};
};
let {live, relevant, busy, atomic, memberOf} = getLiveAttributes(aEvent);
// If container-live is not present or is set to |off| ignore the event.
if (!live || live === 'off') {
return {};
}
// XXX: support busy and atomic.
// Determine if the type of the mutation is relevant. Default is additions
// and text.
let isRelevant = Utils.matchAttributeValue(relevant, aRelevant);
if (!isRelevant) {
return {};
}
return {
liveRegion: aEvent.accessible,
isPolite: live === 'polite'
};
},
_dequeueLiveEvent: function _dequeueLiveEvent(aEventType, aLiveRegion) {
let domNode = aLiveRegion.DOMNode;
if (this._liveEventQueue && this._liveEventQueue.has(domNode)) {
let queue = this._liveEventQueue.get(domNode);
let nextEvent = queue[0];
if (nextEvent.eventType === aEventType) {
Utils.win.clearTimeout(nextEvent.timeoutID);
queue.shift();
if (queue.length === 0) {
this._liveEventQueue.delete(domNode)
}
}
}
},
_queueLiveEvent: function _queueLiveEvent(aEventType, aLiveRegion, aIsPolite, aModifiedText) {
if (!this._liveEventQueue) {
this._liveEventQueue = new WeakMap();
}
let eventHandler = {
eventType: aEventType,
timeoutID: Utils.win.setTimeout(this.present.bind(this),
20, // Wait for a possible EVENT_SHOW or EVENT_TEXT_INSERTED event.
Presentation.liveRegion(aLiveRegion, aIsPolite, true, aModifiedText))
};
let domNode = aLiveRegion.DOMNode;
if (this._liveEventQueue.has(domNode)) {
this._liveEventQueue.get(domNode).push(eventHandler);
} else {
this._liveEventQueue.set(domNode, [eventHandler]);
}
},
present: function present(aPresentationData) {
this.sendMsgFunc("AccessFu:Present", aPresentationData);
},

View File

@ -116,7 +116,6 @@ this.OutputGenerator = {
let extState = {};
aAccessible.getState(state, extState);
let states = {base: state.value, ext: extState.value};
return func.apply(this, [aAccessible, roleString, states, flags, aContext]);
},
@ -418,6 +417,15 @@ this.UtteranceGenerator = {
return [gStringBundle.GetStringFromName(this.gActionMap[aActionName])];
},
genForLiveRegion: function genForLiveRegion(aContext, aIsHide, aModifiedText) {
let utterance = [];
if (aIsHide) {
utterance.push(gStringBundle.GetStringFromName('hidden'));
}
return utterance.concat(
aModifiedText || this.genForContext(aContext).output);
},
genForAnnouncement: function genForAnnouncement(aAnnouncement) {
try {
return [gStringBundle.GetStringFromName(aAnnouncement)];

View File

@ -102,7 +102,19 @@ Presenter.prototype = {
/**
* Announce something. Typically an app state change.
*/
announce: function announce(aAnnouncement) {}
announce: function announce(aAnnouncement) {},
/**
* Announce a live region.
* @param {PivotContext} aContext context object for an accessible.
* @param {boolean} aIsPolite A politeness level for a live region.
* @param {boolean} aIsHide An indicator of hide/remove event.
* @param {string} aModifiedText Optional modified text.
*/
liveRegion: function liveRegionShown(aContext, aIsPolite, aIsHide,
aModifiedText) {}
};
/**
@ -409,6 +421,13 @@ AndroidPresenter.prototype = {
fromIndex: 0
}]
};
},
liveRegion: function AndroidPresenter_liveRegion(aContext, aIsPolite,
aIsHide, aModifiedText) {
return this.announce(
UtteranceGenerator.genForLiveRegion(aContext, aIsHide,
aModifiedText).join(' '));
}
};
@ -451,6 +470,21 @@ SpeechPresenter.prototype = {
]
}
};
},
liveRegion: function SpeechPresenter_liveRegion(aContext, aIsPolite, aIsHide,
aModifiedText) {
return {
type: this.type,
details: {
actions: [{
method: 'speak',
data: UtteranceGenerator.genForLiveRegion(aContext, aIsHide,
aModifiedText).join(' '),
options: {enqueue: aIsPolite}
}]
}
};
}
};
@ -570,5 +604,16 @@ this.Presentation = {
// but there really isn't a point here.
return [p.announce(UtteranceGenerator.genForAnnouncement(aAnnouncement)[0])
for each (p in this.presenters)];
},
liveRegion: function Presentation_liveRegion(aAccessible, aIsPolite, aIsHide,
aModifiedText) {
let context;
if (!aModifiedText) {
context = new PivotContext(aAccessible, null, -1, -1, true,
aIsHide ? true : false);
}
return [p.liveRegion(context, aIsPolite, aIsHide, aModifiedText) for (
p of this.presenters)];
}
};

View File

@ -266,6 +266,15 @@ this.Utils = {
return true;
},
matchAttributeValue: function matchAttributeValue(aAttributeValue, values) {
let attrSet = new Set(aAttributeValue.split(' '));
for (let value of values) {
if (attrSet.has(value)) {
return value;
}
}
},
getLandmarkName: function getLandmarkName(aAccessible) {
const landmarks = [
'banner',
@ -281,11 +290,7 @@ this.Utils = {
}
// Looking up a role that would match a landmark.
for (let landmark of landmarks) {
if (roles.indexOf(landmark) > -1) {
return landmark;
}
}
return this.matchAttributeValue(roles, landmarks);
}
};
@ -422,12 +427,15 @@ this.Logger = {
* for a given accessible and its relationship with another accessible.
*/
this.PivotContext = function PivotContext(aAccessible, aOldAccessible,
aStartOffset, aEndOffset) {
aStartOffset, aEndOffset, aIgnoreAncestry = false,
aIncludeInvisible = false) {
this._accessible = aAccessible;
this._oldAccessible =
this._isDefunct(aOldAccessible) ? null : aOldAccessible;
this.startOffset = aStartOffset;
this.endOffset = aEndOffset;
this._ignoreAncestry = aIgnoreAncestry;
this._includeInvisible = aIncludeInvisible;
}
PivotContext.prototype = {
@ -497,7 +505,7 @@ PivotContext.prototype = {
*/
get oldAncestry() {
if (!this._oldAncestry) {
if (!this._oldAccessible) {
if (!this._oldAccessible || this._ignoreAncestry) {
this._oldAncestry = [];
} else {
this._oldAncestry = this._getAncestry(this._oldAccessible);
@ -512,7 +520,8 @@ PivotContext.prototype = {
*/
get currentAncestry() {
if (!this._currentAncestry) {
this._currentAncestry = this._getAncestry(this._accessible);
this._currentAncestry = this._ignoreAncestry ? [] :
this._getAncestry(this._accessible);
}
return this._currentAncestry;
},
@ -524,7 +533,7 @@ PivotContext.prototype = {
*/
get newAncestry() {
if (!this._newAncestry) {
this._newAncestry = [currentAncestor for (
this._newAncestry = this._ignoreAncestry ? [] : [currentAncestor for (
[index, currentAncestor] of Iterator(this.currentAncestry)) if (
currentAncestor !== this.oldAncestry[index])];
}
@ -543,9 +552,14 @@ PivotContext.prototype = {
}
let child = aAccessible.firstChild;
while (child) {
let state = {};
child.getState(state, {});
if (!(state.value & Ci.nsIAccessibleStates.STATE_INVISIBLE)) {
let include;
if (this._includeInvisible) {
include = true;
} else {
let [state,] = Utils.getStates(child);
include = !(state.value & Ci.nsIAccessibleStates.STATE_INVISIBLE);
}
if (include) {
if (aPreorder) {
yield child;
[yield node for (node of this._traverse(child, aPreorder, aStop))];
@ -703,7 +717,6 @@ PrefCache.prototype = {
if (!this.type) {
this.type = aBranch.getPrefType(this.name);
}
switch (this.type) {
case Ci.nsIPrefBranch.PREF_STRING:
return aBranch.getCharPref(this.name);

View File

@ -18,6 +18,7 @@ test_alive.html \
test_braille.html \
test_explicit_names.html \
test_landmarks.html \
test_live_regions.html \
test_tables.html \
test_utterance_order.html \
$(NULL)

View File

@ -21,6 +21,68 @@ var AccessFuTest = {
}
},
_registerListener: function AccessFuTest__registerListener(aWaitForMessage, aListenerFunc) {
var listener = {
observe: function observe(aMessage) {
// Ignore unexpected messages.
if (!(aMessage instanceof Components.interfaces.nsIConsoleMessage)) {
return;
}
if (aMessage.message.indexOf(aWaitForMessage) < 0) {
return;
}
aListenerFunc.apply(listener);
}
};
Services.console.registerListener(listener);
return listener;
},
on_log: function AccessFuTest_on_log(aWaitForMessage, aListenerFunc) {
return this._registerListener(aWaitForMessage, aListenerFunc);
},
off_log: function AccessFuTest_off_log(aListener) {
Services.console.unregisterListener(aListener);
},
once_log: function AccessFuTest_once_log(aWaitForMessage, aListenerFunc) {
return this._registerListener(aWaitForMessage,
function listenAndUnregister() {
Services.console.unregisterListener(this);
aListenerFunc();
});
},
_addObserver: function AccessFuTest__addObserver(aWaitForData, aListener) {
var listener = function listener(aSubject, aTopic, aData) {
var data = JSON.parse(aData)[1];
// Ignore non-relevant outputs.
if (!data) {
return;
}
isDeeply(data.details.actions, aWaitForData, "Data is correct");
aListener.apply(listener);
};
Services.obs.addObserver(listener, 'accessfu-output', false);
return listener;
},
on: function AccessFuTest_on(aWaitForData, aListener) {
return this._addObserver(aWaitForData, aListener);
},
off: function AccessFuTest_off(aListener) {
Services.obs.removeObserver(aListener, 'accessfu-output');
},
once: function AccessFuTest_once(aWaitForData, aListener) {
return this._addObserver(aWaitForData, function observerAndRemove() {
Services.obs.removeObserver(this, 'accessfu-output');
aListener();
});
},
_waitForExplicitFinish: false,
waitForExplicitFinish: function AccessFuTest_waitForExplicitFinish() {
@ -38,6 +100,7 @@ var AccessFuTest = {
SimpleTest.finish();
};
// Tear down accessibility and make AccessFu stop.
SpecialPowers.setIntPref("accessibility.accessfu.notify_output", 0);
SpecialPowers.setIntPref("accessibility.accessfu.activate", 0);
},
@ -87,5 +150,6 @@ var AccessFuTest = {
// Invoke the whole thing.
SpecialPowers.setIntPref("accessibility.accessfu.activate", 1);
SpecialPowers.setIntPref("accessibility.accessfu.notify_output", 1);
}
};

View File

@ -18,52 +18,38 @@
AccessFuTest.nextTest();
}
function makeEventManagerListener(waitForMessage, callback) {
return {
observe: function observe(aMessage) {
// Ignore unexpected messages.
if (!(aMessage instanceof Components.interfaces.nsIConsoleMessage)) {
return;
}
if (aMessage.message.indexOf(waitForMessage) < 0) {
return;
}
Services.console.unregisterListener(this);
callback();
}
};
// Listen for 'EventManager.stop' and enable AccessFu again.
function onStop() {
ok(true, "EventManager was stopped.");
isnot(AccessFu._enabled, true, "AccessFu was disabled.");
AccessFuTest.once_log("EventManager.start", AccessFuTest.nextTest);
AccessFu._enable();
}
function testEventManagerStartStop() {
// Firs listen for initial 'EventManager.start' and disable AccessFu.
var initialStartListener = makeEventManagerListener("EventManager.start",
function () {
ok(true, "EventManager was started.");
Services.console.registerListener(stopListener);
AccessFu._disable();
});
// Listen for 'EventManager.stop' and enable AccessFu again.
var stopListener = makeEventManagerListener("EventManager.stop",
function () {
ok(true, "EventManager was stopped.");
isnot(AccessFu._enabled, true, "AccessFu was disabled.");
Services.console.registerListener(finalStartListener);
AccessFu._enable();
});
// Make sure EventManager is started again.
var finalStartListener = makeEventManagerListener("EventManager.start",
function () {
ok(true, "EventManager was started again.");
ok(AccessFu._enabled, "AccessFu was enabled again.");
AccessFuTest.finish();
});
// Make sure EventManager is started again.
function onFinalStart() {
ok(true, "EventManager was started again.");
ok(AccessFu._enabled, "AccessFu was enabled again.");
AccessFuTest.finish();
}
Services.console.registerListener(initialStartListener);
// Listen for initial 'EventManager.start' and disable AccessFu.
function onInitialStart() {
ok(true, "EventManager was started.");
AccessFuTest.once_log("EventManager.stop", AccessFuTest.nextTest);
AccessFu._disable();
}
function init() {
AccessFuTest.once_log("EventManager.start", AccessFuTest.nextTest);
}
function doTest() {
AccessFuTest.addFunc(confirmAccessFuStart);
AccessFuTest.addFunc(testEventManagerStartStop);
AccessFuTest.addFunc(init);
AccessFuTest.addFunc(onInitialStart);
AccessFuTest.addFunc(onStop);
AccessFuTest.addFunc(onFinalStart);
AccessFuTest.waitForExplicitFinish();
AccessFuTest.runTests(); // Will call SimpleTest.finish();
}

View File

@ -0,0 +1,342 @@
<html>
<head>
<title>AccessFu tests for live regions support</title>
<link rel="stylesheet" type="text/css"
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="../common.js"></script>
<script type="application/javascript"
src="./jsatcommon.js"></script>
<script type="application/javascript">
function EventManagerStarted() {
AccessFuTest.once_log("EventManager.start", AccessFuTest.nextTest);
}
function hide(id) {
var element = document.getElementById(id);
element.style.display = "none";
}
function show(id) {
var element = document.getElementById(id);
element.style.display = "block";
}
function udpate(id, text, property) {
var element = document.getElementById(id);
element[property] = text;
}
function updateText(id, text) {
udpate(id, text, "textContent");
}
function updateHTML(id, text) {
udpate(id, text, "innerHTML");
}
var tests = [{
expected: [{
"method": "speak",
"data": "hidden I will be hidden",
"options": {
"enqueue": true
}
}],
action: function action() {
[hide(id) for (id of ["to_hide1", "to_hide2", "to_hide3", "to_hide4"])];
}
}, {
expected: [{
"method": "speak",
"data": "hidden I will be hidden",
"options": {
"enqueue": true
}
}],
action: function action() {
[hide(id) for (id of ["to_hide_descendant1", "to_hide_descendant2",
"to_hide_descendant3", "to_hide_descendant4"])];
}
}, {
expected: [{
"method": "speak",
"data": "I will be shown",
"options": {
"enqueue": true
}
}],
action: function action() {
[show(id) for (id of ["to_show1", "to_show2", "to_show3", "to_show4"])];
}
}, {
expected: [{
"method": "speak",
"data": "I will be shown",
"options": {
"enqueue": true
}
}],
action: function action() {
[show(id) for (id of ["to_show_descendant1", "to_show_descendant2",
"to_show_descendant3", "to_show_descendant4"])];
}
}, {
expected: [{
"method": "speak",
"data": "hidden I will be hidden",
"options": {
"enqueue": false
}
}],
action: function action() {
hide("to_hide_live_assertive");
}
}, {
expected: [{
"method": "speak",
"data": "I will be shown",
"options": {
"enqueue": false
}
}],
action: function action() {
[show(id) for (id of ["to_show_live_off", "to_show_live_assertive"])];
}
}, {
expected: [{
"method": "speak",
"data": "Text Added",
"options": {
"enqueue": false
}
}],
action: function action() {
updateText("text_add", "Text Added");
}
}, {
expected: [{
"method": "speak",
"data": "Text Added",
"options": {
"enqueue": false
}
}],
action: function action() {
updateHTML("text_add", "Text Added");
}
}, {
expected: [{
"method": "speak",
"data": "hidden Text Removed",
"options": {
"enqueue": true
}
}],
action: function action() {
updateText("text_remove", "");
}
}, {
expected: [{
"method": "speak",
"data": "Descendant Text Added",
"options": {
"enqueue": false
}
}],
action: function action() {
updateText("text_add_descendant", "Descendant Text Added");
}
}, {
expected: [{
"method": "speak",
"data": "Descendant Text Added",
"options": {
"enqueue": false
}
}],
action: function action() {
updateHTML("text_add_descendant", "Descendant Text Added");
}
}, {
expected: [{
"method": "speak",
"data": "hidden Descendant Text Removed",
"options": {
"enqueue": true
}
}],
action: function action() {
updateText("text_remove_descendant", "");
}
}, {
expected: [{
"method": "speak",
"data": "Descendant Text Added",
"options": {
"enqueue": false
}
}],
action: function action() {
updateText("text_add_descendant2", "Descendant Text Added");
}
}, {
expected: [{
"method": "speak",
"data": "Descendant Text Added",
"options": {
"enqueue": false
}
}],
action: function action() {
updateHTML("text_add_descendant2", "Descendant Text Added");
}
}, {
expected: [{
"method": "speak",
"data": "hidden Descendant Text Removed",
"options": {
"enqueue": true
}
}],
action: function action() {
updateText("text_remove_descendant2", "");
}
}, {
expected: [{
"method": "speak",
"data": "I am replaced main",
"options": {
"enqueue": true
}
}],
action: function action() {
var region = document.getElementById("to_replace_region");
var child = document.getElementById("to_replace");
child.setAttribute("role", "main");
}
}, {
expected: [{
"method": "speak",
"data": "I am a replaced text",
"options": {
"enqueue": false
}
}],
action: function action() {
updateText("to_replace_text", "I am a replaced text");
}
}, {
expected: [{
"method": "speak",
"data": "I am a replaced text",
"options": {
"enqueue": false
}
}],
action: function action() {
updateHTML("to_replace_text", "I am a replaced text");
}
}];
function doTest() {
AccessFuTest.addFunc(EventManagerStarted);
tests.forEach(function addTest(test) {
AccessFuTest.addFunc(function () {
AccessFuTest.once(test.expected, AccessFuTest.nextTest);
test.action();
});
});
AccessFuTest.addFunc(AccessFuTest.finish);
AccessFuTest.waitForExplicitFinish();
AccessFuTest.runTests();
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTest);
</script>
</head>
<body>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=795957"
title="[AccessFu] Support live regions">
Mozilla Bug 795957
</a>
<div id="root">
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test"></pre>
<p id="to_hide1">I should not be announced 1</p>
<p id="to_hide2" aria-live="polite">I should not be announced 2</p>
<p id="to_hide3" aria-live="assertive" aria-relevant="text">I should not be announced 3</p>
<p id="to_hide4" aria-live="polite" aria-relevant="all">I will be hidden</p>
<div>
<p id="to_hide_descendant1">I should not be announced 1</p>
</div>
<div aria-live="polite">
<p id="to_hide_descendant2">I should not be announced 2</p>
</div>
<div aria-live="assertive" aria-relevant="text">
<p id="to_hide_descendant3">I should not be announced 3</p>
</div>
<div aria-live="polite" aria-relevant="all">
<p id="to_hide_descendant4">I will be hidden</p>
</div>
<p id="to_show1" style="display: none">I should not be announced 1</p>
<p id="to_show2" aria-live="assertive" aria-relevant="text" style="display: none">I should not be announced 2</p>
<p id="to_show3" aria-live="polite" aria-relevant="removals" style="display: none">I should not be announced 3</p>
<p id="to_show4" aria-live="polite" aria-relevant="all" style="display: none">I will be shown</p>
<div>
<p id="to_show_descendant1" style="display: none">I should not be announced 1</p>
</div>
<div aria-live="polite" aria-relevant="removals">
<p id="to_show_descendant2" style="display: none">I should not be announced 2</p>
</div>
<div aria-live="assertive" aria-relevant="text">
<p id="to_show_descendant3" style="display: none">I should not be announced 3</p>
</div>
<div aria-live="polite" aria-relevant="all">
<p id="to_show_descendant4" style="display: none">I will be shown</p>
</div>
<div aria-live="assertive" aria-relevant="all">
<p id="to_hide_live_assertive">I will be hidden</p>
</div>
<p id="to_show_live_assertive" aria-live="assertive" style="display: none">I will be shown</p>
<p id="to_show_live_off" aria-live="off" style="display: none">I will not be shown</p>
<div id="to_replace_region" aria-live="polite" aria-relevant="all">
<p id="to_replace">I am replaced</p>
</div>
<p id="to_replace_text" aria-live="assertive" aria-relevant="text">I am going to be replaced</p>
<p id="text_add" aria-live="assertive" aria-relevant="text"></p>
<p id="text_remove" aria-live="polite" aria-relevant="all">Text Removed</p>
<div aria-live="assertive" aria-relevant="all">
<p id="text_add_descendant"></p>
</div>
<div aria-live="polite" aria-relevant="text">
<p id="text_remove_descendant">Descendant Text Removed</p>
</div>
<div aria-live="assertive" aria-relevant="text">
<p id="text_add_descendant2"></p>
</div>
<div aria-live="polite" aria-relevant="all">
<p id="text_remove_descendant2">Descendant Text Removed</p>
</div>
</div>
</body>
</html>

View File

@ -78,9 +78,9 @@ static void Output(const char *fmt, ... )
#if MOZ_WINCONSOLE
fwprintf_s(stderr, wide_msg);
#else
MessageBoxW(NULL, wide_msg, L"Firefox", MB_OK
| MB_ICONERROR
| MB_SETFOREGROUND);
MessageBoxW(nullptr, wide_msg, L"Firefox", MB_OK
| MB_ICONERROR
| MB_SETFOREGROUND);
#endif
#endif
@ -327,16 +327,16 @@ static int do_main(int argc, char* argv[], nsIFile *xreDirectory)
// Check for a metro test harness command line args file
HANDLE hTestFile = CreateFileA(path.get(),
GENERIC_READ,
0, NULL, OPEN_EXISTING,
0, nullptr, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
nullptr);
if (hTestFile != INVALID_HANDLE_VALUE) {
// Typical test harness command line args string is around 100 bytes.
char buffer[1024];
memset(buffer, 0, sizeof(buffer));
DWORD bytesRead = 0;
if (!ReadFile(hTestFile, (VOID*)buffer, sizeof(buffer)-1,
&bytesRead, NULL) || !bytesRead) {
&bytesRead, nullptr) || !bytesRead) {
CloseHandle(hTestFile);
printf("failed to read test file '%s'", testFile);
return -1;
@ -351,7 +351,7 @@ static int do_main(int argc, char* argv[], nsIFile *xreDirectory)
char* ptr = buffer;
newArgv[0] = ptr;
while (*ptr != NULL &&
while (*ptr != '\0' &&
(ptr - buffer) < sizeof(buffer) &&
newArgc < ARRAYSIZE(newArgv)) {
if (isspace(*ptr)) {
@ -513,13 +513,13 @@ InitXPCOMGlue(const char *argv0, nsIFile **xreDirectory)
}
if (absfwurl) {
CFURLRef xulurl =
CFURLCreateCopyAppendingPathComponent(NULL, absfwurl,
CFURLCreateCopyAppendingPathComponent(nullptr, absfwurl,
CFSTR("XUL.framework"),
true);
if (xulurl) {
CFURLRef xpcomurl =
CFURLCreateCopyAppendingPathComponent(NULL, xulurl,
CFURLCreateCopyAppendingPathComponent(nullptr, xulurl,
CFSTR("libxpcom.dylib"),
false);

View File

@ -336,7 +336,7 @@ pref("browser.download.manager.scanWhenDone", true);
pref("browser.download.manager.resumeOnWakeDelay", 10000);
// Enables the asynchronous Downloads API in the Downloads Panel.
pref("browser.download.useJSTransfer", false);
pref("browser.download.useJSTransfer", true);
// This allows disabling the Downloads Panel in favor of the old interface.
pref("browser.download.useToolkitUI", false);

View File

@ -112,10 +112,7 @@
}
function reloadProvider() {
Social.enabled = false;
Services.tm.mainThread.dispatch(function() {
Social.enabled = true;
}, Components.interfaces.nsIThread.DISPATCH_NORMAL);
Social.provider.reload();
}
parseQueryString();

View File

@ -195,21 +195,7 @@ let gGestureSupport = {
aEvent.allowedDirections |= isLTR ? aEvent.DIRECTION_RIGHT :
aEvent.DIRECTION_LEFT;
let isVerticalSwipe = false;
if (gHistorySwipeAnimation.active) {
if (aEvent.direction == aEvent.DIRECTION_UP) {
isVerticalSwipe = true;
// Force a synchronous scroll to the top of the page.
content.scrollTo(content.scrollX, 0);
}
else if (aEvent.direction == aEvent.DIRECTION_DOWN) {
isVerticalSwipe = true;
// Force a synchronous scroll to the bottom of the page.
content.scrollTo(content.scrollX, content.scrollMaxY);
}
}
gHistorySwipeAnimation.startAnimation(isVerticalSwipe);
gHistorySwipeAnimation.startAnimation();
this._doUpdate = function GS__doUpdate(aEvent) {
gHistorySwipeAnimation.updateAnimation(aEvent.delta);
@ -563,13 +549,10 @@ let gHistorySwipeAnimation = {
this.isLTR = document.documentElement.mozMatchesSelector(
":-moz-locale-dir(ltr)");
this._trackedSnapshots = [];
this._startingIndex = -1;
this._historyIndex = -1;
this._boxWidth = -1;
this._boxHeight = -1;
this._maxSnapshots = this._getMaxSnapshots();
this._lastSwipeDir = "";
this._isVerticalSwipe = false;
// We only want to activate history swipe animations if we store snapshots.
// If we don't store any, we handle horizontal swipes without animations.
@ -578,7 +561,6 @@ let gHistorySwipeAnimation = {
gBrowser.addEventListener("pagehide", this, false);
gBrowser.addEventListener("pageshow", this, false);
gBrowser.addEventListener("popstate", this, false);
gBrowser.addEventListener("DOMModalDialogClosed", this, false);
gBrowser.tabContainer.addEventListener("TabClose", this, false);
}
},
@ -590,7 +572,6 @@ let gHistorySwipeAnimation = {
gBrowser.removeEventListener("pagehide", this, false);
gBrowser.removeEventListener("pageshow", this, false);
gBrowser.removeEventListener("popstate", this, false);
gBrowser.removeEventListener("DOMModalDialogClosed", this, false);
gBrowser.tabContainer.removeEventListener("TabClose", this, false);
this.active = false;
@ -600,32 +581,17 @@ let gHistorySwipeAnimation = {
/**
* Starts the swipe animation and handles fast swiping (i.e. a swipe animation
* is already in progress when a new one is initiated).
*
* @param aIsVerticalSwipe
* Whether we're dealing with a vertical swipe or not.
*/
startAnimation: function HSA_startAnimation(aIsVerticalSwipe) {
this._isVerticalSwipe = aIsVerticalSwipe;
startAnimation: function HSA_startAnimation() {
if (this.isAnimationRunning()) {
// If this is a horizontal scroll, or if this is a vertical scroll that
// was started while a horizontal scroll was still running, handle it as
// as a fast swipe. In the case of the latter scenario, this allows us to
// start the vertical animation without first loading the final page, or
// taking another snapshot. If vertical scrolls are initiated repeatedly
// without prior horizontal scroll we skip this and restart the animation
// from 0.
if (!this._isVerticalSwipe || this._lastSwipeDir != "") {
gBrowser.stop();
this._lastSwipeDir = "RELOAD"; // just ensure that != ""
this._canGoBack = this.canGoBack();
this._canGoForward = this.canGoForward();
this._handleFastSwiping();
}
gBrowser.stop();
this._lastSwipeDir = "RELOAD"; // just ensure that != ""
this._canGoBack = this.canGoBack();
this._canGoForward = this.canGoForward();
this._handleFastSwiping();
}
else {
this._startingIndex = gBrowser.webNavigation.sessionHistory.index;
this._historyIndex = this._startingIndex;
this._historyIndex = gBrowser.webNavigation.sessionHistory.index;
this._canGoBack = this.canGoBack();
this._canGoForward = this.canGoForward();
if (this.active) {
@ -656,29 +622,20 @@ let gHistorySwipeAnimation = {
if (!this.isAnimationRunning())
return;
// We use the following value to decrease the bounce effect when scrolling
// to the top or bottom of the page, or when swiping back/forward past the
// browsing history. This value was determined experimentally.
let dampValue = 4;
if (this._isVerticalSwipe) {
this._prevBox.collapsed = true;
this._nextBox.collapsed = true;
this._positionBox(this._curBox, -1 * aVal / dampValue);
}
else if ((aVal >= 0 && this.isLTR) ||
(aVal <= 0 && !this.isLTR)) {
let tempDampValue = 1;
if ((aVal >= 0 && this.isLTR) ||
(aVal <= 0 && !this.isLTR)) {
if (aVal > 1)
aVal = 1; // Cap value to avoid sliding the page further than allowed.
if (this._canGoBack)
this._prevBox.collapsed = false;
else {
tempDampValue = dampValue;
else
this._prevBox.collapsed = true;
}
// The current page is pushed to the right (LTR) or left (RTL),
// the intention is to go back.
// If there is a page to go back to, it should show in the background.
this._positionBox(this._curBox, aVal / tempDampValue);
this._positionBox(this._curBox, aVal);
// The forward page should be pushed offscreen all the way to the right.
this._positionBox(this._nextBox, 1);
@ -694,14 +651,13 @@ let gHistorySwipeAnimation = {
// For the backdrop to be visible in that case, the previous page needs
// to be hidden (if it exists).
if (this._canGoForward) {
this._nextBox.collapsed = false;
let offset = this.isLTR ? 1 : -1;
this._positionBox(this._curBox, 0);
this._positionBox(this._nextBox, offset + aVal);
this._positionBox(this._nextBox, offset + aVal); // aVal is negative
}
else {
this._prevBox.collapsed = true;
this._positionBox(this._curBox, aVal / dampValue);
this._positionBox(this._curBox, aVal);
}
}
},
@ -718,14 +674,13 @@ let gHistorySwipeAnimation = {
let browser = gBrowser.getBrowserForTab(aEvent.target);
this._removeTrackedSnapshot(-1, browser);
break;
case "DOMModalDialogClosed":
this.stopAnimation();
break;
case "pageshow":
case "popstate":
if (aEvent.target != gBrowser.selectedBrowser.contentDocument)
break;
this.stopAnimation();
if (this.isAnimationRunning()) {
if (aEvent.target != gBrowser.selectedBrowser.contentDocument)
break;
this.stopAnimation();
}
this._historyIndex = gBrowser.webNavigation.sessionHistory.index;
break;
case "pagehide":
@ -793,7 +748,7 @@ let gHistorySwipeAnimation = {
* any. This will also result in the animation overlay to be torn down.
*/
swipeEndEventReceived: function HSA_swipeEndEventReceived() {
if (this._lastSwipeDir != "" && this._historyIndex != this._startingIndex)
if (this._lastSwipeDir != "")
this._navigateToHistoryIndex();
else
this.stopAnimation();
@ -821,10 +776,9 @@ let gHistorySwipeAnimation = {
* |this|.
*/
_navigateToHistoryIndex: function HSA__navigateToHistoryIndex() {
if (this._doesIndexExistInHistory(this._historyIndex))
if (this._doesIndexExistInHistory(this._historyIndex)) {
gBrowser.webNavigation.gotoIndex(this._historyIndex);
else
this.stopAnimation();
}
},
/**
@ -870,9 +824,7 @@ let gHistorySwipeAnimation = {
"box");
this._container.appendChild(this._nextBox);
// Cache width and height.
this._boxWidth = this._curBox.getBoundingClientRect().width;
this._boxHeight = this._curBox.getBoundingClientRect().height;
this._boxWidth = this._curBox.getBoundingClientRect().width; // cache width
},
/**
@ -886,7 +838,6 @@ let gHistorySwipeAnimation = {
this._container.parentNode.removeChild(this._container);
this._container = null;
this._boxWidth = -1;
this._boxHeight = -1;
},
/**
@ -914,14 +865,7 @@ let gHistorySwipeAnimation = {
* The position (in X coordinates) to move the box element to.
*/
_positionBox: function HSA__positionBox(aBox, aPosition) {
let transform = "";
if (this._isVerticalSwipe)
transform = "translateY(" + this._boxHeight * aPosition + "px)";
else
transform = "translateX(" + this._boxWidth * aPosition + "px)";
aBox.style.transform = transform;
aBox.style.transform = "translateX(" + this._boxWidth * aPosition + "px)";
},
/**
@ -1060,17 +1004,12 @@ let gHistorySwipeAnimation = {
return aBlob;
let img = new Image();
let url = "";
try {
url = URL.createObjectURL(aBlob);
img.onload = function() {
URL.revokeObjectURL(url);
};
}
finally {
img.src = url;
return img;
}
let url = URL.createObjectURL(aBlob);
img.onload = function() {
URL.revokeObjectURL(url);
};
img.src = url;
return img;
},
/**

View File

@ -36,6 +36,7 @@ SocialUI = {
Services.obs.addObserver(this, "social:frameworker-error", false);
Services.obs.addObserver(this, "social:provider-set", false);
Services.obs.addObserver(this, "social:providers-changed", false);
Services.obs.addObserver(this, "social:provider-reload", false);
Services.prefs.addObserver("social.sidebar.open", this, false);
Services.prefs.addObserver("social.toast-notifications.enabled", this, false);
@ -60,6 +61,7 @@ SocialUI = {
Services.obs.removeObserver(this, "social:frameworker-error");
Services.obs.removeObserver(this, "social:provider-set");
Services.obs.removeObserver(this, "social:providers-changed");
Services.obs.removeObserver(this, "social:provider-reload");
Services.prefs.removeObserver("social.sidebar.open", this);
Services.prefs.removeObserver("social.toast-notifications.enabled", this);
@ -74,6 +76,16 @@ SocialUI = {
// manually :(
try {
switch (topic) {
case "social:provider-reload":
// if the reloaded provider is our current provider, fall through
// to social:provider-set so the ui will be reset
if (!Social.provider || Social.provider.origin != data)
return;
// be sure to unload the sidebar as it will not reload if the origin
// has not changed, it will be loaded in provider-set below. Other
// panels will be unloaded or handle reload.
SocialSidebar.unloadSidebar();
// fall through to social:provider-set
case "social:provider-set":
// Social.provider has changed (possibly to null), update any state
// which depends on it.
@ -142,7 +154,7 @@ SocialUI = {
// Miscellaneous helpers
showProfile: function SocialUI_showProfile() {
if (Social.haveLoggedInUser())
if (Social.provider.haveLoggedInUser())
openUILinkIn(Social.provider.profile.profileURL, "tab");
else {
// XXX Bug 789585 will implement an API for provider-specified login pages.
@ -976,22 +988,20 @@ SocialToolbar = {
let toggleNotificationsCommand = document.getElementById("Social:ToggleNotifications");
toggleNotificationsCommand.setAttribute("hidden", !socialEnabled);
if (!Social.haveLoggedInUser() || !socialEnabled) {
let parent = document.getElementById("social-notification-panel");
while (parent.hasChildNodes()) {
let frame = parent.firstChild;
SharedFrame.forgetGroup(frame.id);
parent.removeChild(frame);
}
let parent = document.getElementById("social-notification-panel");
while (parent.hasChildNodes()) {
let frame = parent.firstChild;
SharedFrame.forgetGroup(frame.id);
parent.removeChild(frame);
}
let tbi = document.getElementById("social-toolbar-item");
if (tbi) {
// SocialMark is the last button allways
let next = SocialMark.button.previousSibling;
while (next != this.button) {
tbi.removeChild(next);
next = SocialMark.button.previousSibling;
}
let tbi = document.getElementById("social-toolbar-item");
if (tbi) {
// SocialMark is the last button allways
let next = SocialMark.button.previousSibling;
while (next != this.button) {
tbi.removeChild(next);
next = SocialMark.button.previousSibling;
}
}
},
@ -1035,7 +1045,7 @@ SocialToolbar = {
// provider.profile == undefined means no response yet from the provider
// to tell us whether the user is logged in or not.
if (!SocialUI.enabled ||
(!Social.haveLoggedInUser() && Social.provider.profile !== undefined)) {
(!Social.provider.haveLoggedInUser() && Social.provider.profile !== undefined)) {
// Either no enabled provider, or there is a provider and it has
// responded with a profile and the user isn't loggedin. The icons
// etc have already been removed by updateButtonHiddenState, so we want

View File

@ -905,10 +905,7 @@ nsContextMenu.prototype = {
reload: function(event) {
if (this.onSocial) {
// full reload of social provider
Social.enabled = false;
Services.tm.mainThread.dispatch(function() {
Social.enabled = true;
}, Components.interfaces.nsIThread.DISPATCH_NORMAL);
Social.provider.reload();
} else {
BrowserReloadOrDuplicate(event);
}

View File

@ -16,7 +16,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
"resource:///modules/DownloadsCommon.jsm");
function Sanitizer() {}
Sanitizer.prototype = {
// warning to the caller: this one may raise an exception (e.g. bug #265028)
@ -37,14 +37,14 @@ Sanitizer.prototype = {
aCallback(aItemName, canClear, aArg);
return canClear;
},
prefDomain: "",
getNameFromPreference: function (aPreferenceName)
{
return aPreferenceName.substr(this.prefDomain.length);
},
/**
* Deletes privacy sensitive data in a batch, according to user preferences.
* Returns a promise which is resolved if no errors occurred. If an error
@ -87,7 +87,8 @@ Sanitizer.prototype = {
item.clear();
} catch(er) {
seenError = true;
Cu.reportError("Error sanitizing " + itemName + ": " + er + "\n");
Components.utils.reportError("Error sanitizing " + itemName +
": " + er + "\n");
}
onItemComplete();
};
@ -99,7 +100,7 @@ Sanitizer.prototype = {
return deferred.promise;
},
// Time span only makes sense in certain cases. Consumers who want
// to only clear some private data can opt in by setting this to false,
// and can optionally specify a specific range. If timespan is not ignored,
@ -107,7 +108,7 @@ Sanitizer.prototype = {
// pref to determine a range
ignoreTimespan : true,
range : null,
items: {
cache: {
clear: function ()
@ -126,13 +127,13 @@ Sanitizer.prototype = {
imageCache.clearCache(false); // true=chrome, false=content
} catch(er) {}
},
get canClear()
{
return true;
}
},
cookies: {
clear: function ()
{
@ -143,7 +144,7 @@ Sanitizer.prototype = {
var cookiesEnum = cookieMgr.enumerator;
while (cookiesEnum.hasMoreElements()) {
var cookie = cookiesEnum.getNext().QueryInterface(Ci.nsICookie2);
if (cookie.creationTime > this.range[0])
// This cookie was created after our cutoff, clear it
cookieMgr.remove(cookie.host, cookie.name, cookie.path, false);
@ -211,14 +212,14 @@ Sanitizer.prototype = {
PlacesUtils.history.removeVisitsByTimeframe(this.range[0], this.range[1]);
else
PlacesUtils.history.removeAllPages();
try {
var os = Components.classes["@mozilla.org/observer-service;1"]
.getService(Components.interfaces.nsIObserverService);
os.notifyObservers(null, "browser:purge-session-history", "");
}
catch (e) { }
// Clear last URL of the Open Web Location dialog
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
.getService(Components.interfaces.nsIPrefBranch);
@ -227,7 +228,7 @@ Sanitizer.prototype = {
}
catch (e) { }
},
get canClear()
{
// bug 347231: Always allow clearing history due to dependencies on
@ -235,7 +236,7 @@ Sanitizer.prototype = {
return true;
}
},
formdata: {
clear: function ()
{
@ -305,15 +306,20 @@ Sanitizer.prototype = {
return false;
}
},
downloads: {
clear: function ()
{
if (DownloadsCommon.useJSTransfer) {
Task.spawn(function () {
let filterByTime = this.range ?
(download => download.startTime >= this.range[0] &&
download.startTime <= this.range[1]) : null;
let filterByTime = null;
if (this.range) {
// Convert microseconds back to milliseconds for date comparisons.
let rangeBeginMs = this.range[0] / 1000;
let rangeEndMs = this.range[1] / 1000;
filterByTime = download => download.startTime >= rangeBeginMs &&
download.startTime <= rangeEndMs;
}
// Clear all completed/cancelled downloads
let publicList = yield Downloads.getPublicDownloadList();
@ -321,7 +327,7 @@ Sanitizer.prototype = {
let privateList = yield Downloads.getPrivateDownloadList();
privateList.removeFinished(filterByTime);
}.bind(this)).then(null, Cu.reportError);
}.bind(this)).then(null, Components.utils.reportError);
}
else {
var dlMgr = Components.classes["@mozilla.org/download-manager;1"]
@ -352,7 +358,7 @@ Sanitizer.prototype = {
return false;
}
},
passwords: {
clear: function ()
{
@ -361,7 +367,7 @@ Sanitizer.prototype = {
// Passwords are timeless, and don't respect the timeSpan setting
pwmgr.removeAllLogins();
},
get canClear()
{
var pwmgr = Components.classes["@mozilla.org/login-manager;1"]
@ -370,7 +376,7 @@ Sanitizer.prototype = {
return (count > 0);
}
},
sessions: {
clear: function ()
{
@ -384,13 +390,13 @@ Sanitizer.prototype = {
.getService(Components.interfaces.nsIObserverService);
os.notifyObservers(null, "net:clear-active-logins", null);
},
get canClear()
{
return true;
}
},
siteSettings: {
clear: function ()
{
@ -398,12 +404,12 @@ Sanitizer.prototype = {
var pm = Components.classes["@mozilla.org/permissionmanager;1"]
.getService(Components.interfaces.nsIPermissionManager);
pm.removeAll();
// Clear site-specific settings like page-zoom level
var cps = Components.classes["@mozilla.org/content-pref/service;1"]
.getService(Components.interfaces.nsIContentPrefService2);
cps.removeAllDomains(null);
// Clear "Never remember passwords for this site", which is not handled by
// the permission manager
var pwmgr = Components.classes["@mozilla.org/login-manager;1"]
@ -413,7 +419,7 @@ Sanitizer.prototype = {
pwmgr.setLoginSavingEnabled(host, true);
}
},
get canClear()
{
return true;
@ -446,7 +452,7 @@ Sanitizer.getClearRange = function (ts) {
ts = Sanitizer.prefs.getIntPref("timeSpan");
if (ts === Sanitizer.TIMESPAN_EVERYTHING)
return null;
// PRTime is microseconds while JS time is milliseconds
var endDate = Date.now() * 1000;
switch (ts) {
@ -473,7 +479,7 @@ Sanitizer.getClearRange = function (ts) {
};
Sanitizer._prefs = null;
Sanitizer.__defineGetter__("prefs", function()
Sanitizer.__defineGetter__("prefs", function()
{
return Sanitizer._prefs ? Sanitizer._prefs
: Sanitizer._prefs = Components.classes["@mozilla.org/preferences-service;1"]
@ -482,7 +488,7 @@ Sanitizer.__defineGetter__("prefs", function()
});
// Shows sanitization UI
Sanitizer.showUI = function(aParentWindow)
Sanitizer.showUI = function(aParentWindow)
{
var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
.getService(Components.interfaces.nsIWindowWatcher);
@ -497,32 +503,32 @@ Sanitizer.showUI = function(aParentWindow)
null);
};
/**
* Deletes privacy sensitive data in a batch, optionally showing the
/**
* Deletes privacy sensitive data in a batch, optionally showing the
* sanitize UI, according to user preferences
*/
Sanitizer.sanitize = function(aParentWindow)
Sanitizer.sanitize = function(aParentWindow)
{
Sanitizer.showUI(aParentWindow);
};
Sanitizer.onStartup = function()
Sanitizer.onStartup = function()
{
// we check for unclean exit with pending sanitization
Sanitizer._checkAndSanitize();
};
Sanitizer.onShutdown = function()
Sanitizer.onShutdown = function()
{
// we check if sanitization is needed and perform it
Sanitizer._checkAndSanitize();
};
// this is called on startup and shutdown, to perform pending sanitizations
Sanitizer._checkAndSanitize = function()
Sanitizer._checkAndSanitize = function()
{
const prefs = Sanitizer.prefs;
if (prefs.getBoolPref(Sanitizer.prefShutdown) &&
if (prefs.getBoolPref(Sanitizer.prefShutdown) &&
!prefs.prefHasUserValue(Sanitizer.prefDidShutdown)) {
// this is a shutdown or a startup after an unclean exit
var s = new Sanitizer();

View File

@ -2,10 +2,10 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
// Bug 453440 - Test the timespan-based logic of the sanitizer code
var now_uSec = Date.now() * 1000;
const dm = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager);
let now_mSec = Date.now();
let now_uSec = now_mSec * 1000;
const kMsecPerMin = 60 * 1000;
const kUsecPerMin = 60 * 1000000;
let tempScope = {};
@ -14,6 +14,7 @@ Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
let Sanitizer = tempScope.Sanitizer;
let FormHistory = (Components.utils.import("resource://gre/modules/FormHistory.jsm", {})).FormHistory;
let Downloads = (Components.utils.import("resource://gre/modules/Downloads.jsm", {})).Downloads;
function promiseFormHistoryRemoved() {
let deferred = Promise.defer();
@ -24,15 +25,30 @@ function promiseFormHistoryRemoved() {
return deferred.promise;
}
function promiseDownloadRemoved(list) {
let deferred = Promise.defer();
let view = {
onDownloadRemoved: function(download) {
list.removeView(view);
deferred.resolve();
}
};
list.addView(view);
return deferred.promise;
}
function test() {
waitForExplicitFinish();
Task.spawn(function() {
setupDownloads();
yield setupDownloads();
yield setupFormHistory();
yield setupHistory();
yield onHistoryReady();
}).then(finish);
}).then(null, ex => ok(false, ex)).then(finish);
}
function countEntries(name, message, check) {
@ -80,12 +96,16 @@ function onHistoryReady() {
itemPrefs.setBoolPref("sessions", false);
itemPrefs.setBoolPref("siteSettings", false);
let publicList = yield Downloads.getPublicDownloadList();
let downloadPromise = promiseDownloadRemoved(publicList);
// Clear 10 minutes ago
s.range = [now_uSec - 10*60*1000000, now_uSec];
s.sanitize();
s.range = null;
yield promiseFormHistoryRemoved();
yield downloadPromise;
ok(!(yield promiseIsURIVisited(makeURI("http://10minutes.com"))),
"Pretend visit to 10minutes.com should now be deleted");
@ -122,23 +142,26 @@ function onHistoryReady() {
yield countEntries("today", "today form entry should still exist", checkOne);
yield countEntries("b4today", "b4today form entry should still exist", checkOne);
ok(!downloadExists(5555555), "10 minute download should now be deleted");
ok(downloadExists(5555551), "<1 hour download should still be present");
ok(downloadExists(5555556), "1 hour 10 minute download should still be present");
ok(downloadExists(5555550), "Year old download should still be present");
ok(downloadExists(5555552), "<2 hour old download should still be present");
ok(downloadExists(5555557), "2 hour 10 minute download should still be present");
ok(downloadExists(5555553), "<4 hour old download should still be present");
ok(downloadExists(5555558), "4 hour 10 minute download should still be present");
ok(!(yield downloadExists(publicList, "fakefile-10-minutes")), "10 minute download should now be deleted");
ok((yield downloadExists(publicList, "fakefile-1-hour")), "<1 hour download should still be present");
ok((yield downloadExists(publicList, "fakefile-1-hour-10-minutes")), "1 hour 10 minute download should still be present");
ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present");
ok((yield downloadExists(publicList, "fakefile-2-hour")), "<2 hour old download should still be present");
ok((yield downloadExists(publicList, "fakefile-2-hour-10-minutes")), "2 hour 10 minute download should still be present");
ok((yield downloadExists(publicList, "fakefile-4-hour")), "<4 hour old download should still be present");
ok((yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "4 hour 10 minute download should still be present");
if (minutesSinceMidnight > 10)
ok(downloadExists(5555554), "'Today' download should still be present");
ok((yield downloadExists(publicList, "fakefile-today")), "'Today' download should still be present");
downloadPromise = promiseDownloadRemoved(publicList);
// Clear 1 hour
Sanitizer.prefs.setIntPref("timeSpan", 1);
s.sanitize();
yield promiseFormHistoryRemoved();
yield downloadPromise;
ok(!(yield promiseIsURIVisited(makeURI("http://1hour.com"))),
"Pretend visit to 1hour.com should now be deleted");
@ -169,23 +192,26 @@ function onHistoryReady() {
yield countEntries("today", "today form entry should still exist", checkOne);
yield countEntries("b4today", "b4today form entry should still exist", checkOne);
ok(!downloadExists(5555551), "<1 hour download should now be deleted");
ok(downloadExists(5555556), "1 hour 10 minute download should still be present");
ok(downloadExists(5555550), "Year old download should still be present");
ok(downloadExists(5555552), "<2 hour old download should still be present");
ok(downloadExists(5555557), "2 hour 10 minute download should still be present");
ok(downloadExists(5555553), "<4 hour old download should still be present");
ok(downloadExists(5555558), "4 hour 10 minute download should still be present");
ok(!(yield downloadExists(publicList, "fakefile-1-hour")), "<1 hour download should now be deleted");
ok((yield downloadExists(publicList, "fakefile-1-hour-10-minutes")), "1 hour 10 minute download should still be present");
ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present");
ok((yield downloadExists(publicList, "fakefile-2-hour")), "<2 hour old download should still be present");
ok((yield downloadExists(publicList, "fakefile-2-hour-10-minutes")), "2 hour 10 minute download should still be present");
ok((yield downloadExists(publicList, "fakefile-4-hour")), "<4 hour old download should still be present");
ok((yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "4 hour 10 minute download should still be present");
if (hoursSinceMidnight > 1)
ok(downloadExists(5555554), "'Today' download should still be present");
ok((yield downloadExists(publicList, "fakefile-today")), "'Today' download should still be present");
downloadPromise = promiseDownloadRemoved(publicList);
// Clear 1 hour 10 minutes
s.range = [now_uSec - 70*60*1000000, now_uSec];
s.sanitize();
s.range = null;
yield promiseFormHistoryRemoved();
yield downloadPromise;
ok(!(yield promiseIsURIVisited(makeURI("http://1hour10minutes.com"))),
"Pretend visit to 1hour10minutes.com should now be deleted");
@ -213,20 +239,23 @@ function onHistoryReady() {
yield countEntries("today", "today form entry should still exist", checkOne);
yield countEntries("b4today", "b4today form entry should still exist", checkOne);
ok(!downloadExists(5555556), "1 hour 10 minute old download should now be deleted");
ok(downloadExists(5555550), "Year old download should still be present");
ok(downloadExists(5555552), "<2 hour old download should still be present");
ok(downloadExists(5555557), "2 hour 10 minute download should still be present");
ok(downloadExists(5555553), "<4 hour old download should still be present");
ok(downloadExists(5555558), "4 hour 10 minute download should still be present");
ok(!(yield downloadExists(publicList, "fakefile-1-hour-10-minutes")), "1 hour 10 minute old download should now be deleted");
ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present");
ok((yield downloadExists(publicList, "fakefile-2-hour")), "<2 hour old download should still be present");
ok((yield downloadExists(publicList, "fakefile-2-hour-10-minutes")), "2 hour 10 minute download should still be present");
ok((yield downloadExists(publicList, "fakefile-4-hour")), "<4 hour old download should still be present");
ok((yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "4 hour 10 minute download should still be present");
if (minutesSinceMidnight > 70)
ok(downloadExists(5555554), "'Today' download should still be present");
ok((yield downloadExists(publicList, "fakefile-today")), "'Today' download should still be present");
downloadPromise = promiseDownloadRemoved(publicList);
// Clear 2 hours
Sanitizer.prefs.setIntPref("timeSpan", 2);
s.sanitize();
yield promiseFormHistoryRemoved();
yield downloadPromise;
ok(!(yield promiseIsURIVisited(makeURI("http://2hour.com"))),
"Pretend visit to 2hour.com should now be deleted");
@ -251,20 +280,23 @@ function onHistoryReady() {
yield countEntries("today", "today form entry should still exist", checkOne);
yield countEntries("b4today", "b4today form entry should still exist", checkOne);
ok(!downloadExists(5555552), "<2 hour old download should now be deleted");
ok(downloadExists(5555550), "Year old download should still be present");
ok(downloadExists(5555557), "2 hour 10 minute download should still be present");
ok(downloadExists(5555553), "<4 hour old download should still be present");
ok(downloadExists(5555558), "4 hour 10 minute download should still be present");
ok(!(yield downloadExists(publicList, "fakefile-2-hour")), "<2 hour old download should now be deleted");
ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present");
ok((yield downloadExists(publicList, "fakefile-2-hour-10-minutes")), "2 hour 10 minute download should still be present");
ok((yield downloadExists(publicList, "fakefile-4-hour")), "<4 hour old download should still be present");
ok((yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "4 hour 10 minute download should still be present");
if (hoursSinceMidnight > 2)
ok(downloadExists(5555554), "'Today' download should still be present");
ok((yield downloadExists(publicList, "fakefile-today")), "'Today' download should still be present");
downloadPromise = promiseDownloadRemoved(publicList);
// Clear 2 hours 10 minutes
s.range = [now_uSec - 130*60*1000000, now_uSec];
s.sanitize();
s.range = null;
yield promiseFormHistoryRemoved();
yield downloadPromise;
ok(!(yield promiseIsURIVisited(makeURI("http://2hour10minutes.com"))),
"Pretend visit to 2hour10minutes.com should now be deleted");
@ -286,18 +318,21 @@ function onHistoryReady() {
yield countEntries("today", "today form entry should still exist", checkOne);
yield countEntries("b4today", "b4today form entry should still exist", checkOne);
ok(!downloadExists(5555557), "2 hour 10 minute old download should now be deleted");
ok(downloadExists(5555553), "<4 hour old download should still be present");
ok(downloadExists(5555558), "4 hour 10 minute download should still be present");
ok(downloadExists(5555550), "Year old download should still be present");
ok(!(yield downloadExists(publicList, "fakefile-2-hour-10-minutes")), "2 hour 10 minute old download should now be deleted");
ok((yield downloadExists(publicList, "fakefile-4-hour")), "<4 hour old download should still be present");
ok((yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "4 hour 10 minute download should still be present");
ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present");
if (minutesSinceMidnight > 130)
ok(downloadExists(5555554), "'Today' download should still be present");
ok((yield downloadExists(publicList, "fakefile-today")), "'Today' download should still be present");
downloadPromise = promiseDownloadRemoved(publicList);
// Clear 4 hours
Sanitizer.prefs.setIntPref("timeSpan", 3);
s.sanitize();
yield promiseFormHistoryRemoved();
yield downloadPromise;
ok(!(yield promiseIsURIVisited(makeURI("http://4hour.com"))),
"Pretend visit to 4hour.com should now be deleted");
@ -316,11 +351,13 @@ function onHistoryReady() {
yield countEntries("today", "today form entry should still exist", checkOne);
yield countEntries("b4today", "b4today form entry should still exist", checkOne);
ok(!downloadExists(5555553), "<4 hour old download should now be deleted");
ok(downloadExists(5555558), "4 hour 10 minute download should still be present");
ok(downloadExists(5555550), "Year old download should still be present");
ok(!(yield downloadExists(publicList, "fakefile-4-hour")), "<4 hour old download should now be deleted");
ok((yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "4 hour 10 minute download should still be present");
ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present");
if (hoursSinceMidnight > 4)
ok(downloadExists(5555554), "'Today' download should still be present");
ok((yield downloadExists(publicList, "fakefile-today")), "'Today' download should still be present");
downloadPromise = promiseDownloadRemoved(publicList);
// Clear 4 hours 10 minutes
s.range = [now_uSec - 250*60*1000000, now_uSec];
@ -328,6 +365,7 @@ function onHistoryReady() {
s.range = null;
yield promiseFormHistoryRemoved();
yield downloadPromise;
ok(!(yield promiseIsURIVisited(makeURI("http://4hour10minutes.com"))),
"Pretend visit to 4hour10minutes.com should now be deleted");
@ -343,48 +381,60 @@ function onHistoryReady() {
yield countEntries("today", "today form entry should still exist", checkOne);
yield countEntries("b4today", "b4today form entry should still exist", checkOne);
ok(!downloadExists(5555558), "4 hour 10 minute download should now be deleted");
ok(downloadExists(5555550), "Year old download should still be present");
ok(!(yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "4 hour 10 minute download should now be deleted");
ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present");
if (minutesSinceMidnight > 250)
ok(downloadExists(5555554), "'Today' download should still be present");
ok((yield downloadExists(publicList, "fakefile-today")), "'Today' download should still be present");
// The 'Today' download might have been already deleted, in which case we
// should not wait for a download removal notification.
if (minutesSinceMidnight > 250) {
downloadPromise = promiseDownloadRemoved(publicList);
} else {
downloadPromise = Promise.resolve();
}
// Clear Today
Sanitizer.prefs.setIntPref("timeSpan", 4);
s.sanitize();
yield promiseFormHistoryRemoved();
yield downloadPromise;
// Be careful. If we add our objectss just before midnight, and sanitize
// runs immediately after, they won't be expired. This is expected, but
// we should not test in that case. We cannot just test for opposite
// condition because we could cross midnight just one moment after we
// cache our time, then we would have an even worse random failure.
var today = isToday(new Date(now_uSec/1000));
var today = isToday(new Date(now_mSec));
if (today) {
ok(!(yield promiseIsURIVisited(makeURI("http://today.com"))),
"Pretend visit to today.com should now be deleted");
yield countEntries("today", "today form entry should be deleted", checkZero);
ok(!downloadExists(5555554), "'Today' download should now be deleted");
ok(!(yield downloadExists(publicList, "fakefile-today")), "'Today' download should now be deleted");
}
ok((yield promiseIsURIVisited(makeURI("http://before-today.com"))),
"Pretend visit to before-today.com should still exist");
yield countEntries("b4today", "b4today form entry should still exist", checkOne);
ok(downloadExists(5555550), "Year old download should still be present");
ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present");
downloadPromise = promiseDownloadRemoved(publicList);
// Choose everything
Sanitizer.prefs.setIntPref("timeSpan", 0);
s.sanitize();
yield promiseFormHistoryRemoved();
yield downloadPromise;
ok(!(yield promiseIsURIVisited(makeURI("http://before-today.com"))),
"Pretend visit to before-today.com should now be deleted");
yield countEntries("b4today", "b4today form entry should be deleted", checkZero);
ok(!downloadExists(5555550), "Year old download should now be deleted");
ok(!(yield downloadExists(publicList, "fakefile-old")), "Year old download should now be deleted");
}
function setupHistory() {
@ -562,227 +612,103 @@ function setupFormHistory() {
function setupDownloads() {
// Add 10-minutes download to DB
let data = {
id: "5555555",
name: "fakefile-10-minutes",
source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169",
target: "fakefile-10-minutes",
startTime: now_uSec - 10 * kUsecPerMin, // 10 minutes ago, in uSec
endTime: now_uSec - 11 * kUsecPerMin, // 1 minute later
state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
guid: "a1bcD23eF4g5"
};
let publicList = yield Downloads.getPublicDownloadList();
let db = dm.DBConnection;
let stmt = db.createStatement(
"INSERT INTO moz_downloads (id, name, source, target, startTime, endTime, " +
"state, currBytes, maxBytes, preferredAction, autoResume, guid) " +
"VALUES (:id, :name, :source, :target, :startTime, :endTime, :state, " +
":currBytes, :maxBytes, :preferredAction, :autoResume, :guid)");
try {
for (let prop in data)
stmt.params[prop] = data[prop];
stmt.execute();
}
finally {
stmt.reset();
}
// Add within-1-hour download to DB
data = {
id: "5555551",
name: "fakefile-1-hour",
let download = yield Downloads.createDownload({
source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169",
target: "fakefile-10-minutes"
});
download.startTime = new Date(now_mSec - 10 * kMsecPerMin), // 10 minutes ago
download.canceled = true;
publicList.add(download);
download = yield Downloads.createDownload({
source: "https://bugzilla.mozilla.org/show_bug.cgi?id=453440",
target: "fakefile-1-hour",
startTime: now_uSec - 45 * kUsecPerMin, // 45 minutes ago, in uSec
endTime: now_uSec - 44 * kUsecPerMin, // 1 minute later
state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
guid: "1bcD23eF4g5a"
};
target: "fakefile-1-hour"
});
download.startTime = new Date(now_mSec - 45 * kMsecPerMin), // 45 minutes ago
download.canceled = true;
publicList.add(download);
try {
for (let prop in data)
stmt.params[prop] = data[prop];
stmt.execute();
}
finally {
stmt.reset();
}
// Add 1-hour-10-minutes download to DB
data = {
id: "5555556",
name: "fakefile-1-hour-10-minutes",
download = yield Downloads.createDownload({
source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169",
target: "fakefile-1-hour-10-minutes",
startTime: now_uSec - 70 * kUsecPerMin, // 70 minutes ago, in uSec
endTime: now_uSec - 71 * kUsecPerMin, // 1 minute later
state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
guid: "a1cbD23e4Fg5"
};
target: "fakefile-1-hour-10-minutes"
});
download.startTime = new Date(now_mSec - 70 * kMsecPerMin), // 70 minutes ago
download.canceled = true;
publicList.add(download);
try {
for (let prop in data)
stmt.params[prop] = data[prop];
stmt.execute();
}
finally {
stmt.reset();
}
// Add within-2-hour download
data = {
id: "5555552",
name: "fakefile-2-hour",
download = yield Downloads.createDownload({
source: "https://bugzilla.mozilla.org/show_bug.cgi?id=453440",
target: "fakefile-2-hour",
startTime: now_uSec - 90 * kUsecPerMin, // 90 minutes ago, in uSec
endTime: now_uSec - 89 * kUsecPerMin, // 1 minute later
state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
guid: "b1aDc23eFg54"
};
target: "fakefile-2-hour"
});
download.startTime = new Date(now_mSec - 90 * kMsecPerMin), // 90 minutes ago
download.canceled = true;
publicList.add(download);
try {
for (let prop in data)
stmt.params[prop] = data[prop];
stmt.execute();
}
finally {
stmt.reset();
}
// Add 2-hour-10-minutes download
data = {
id: "5555557",
name: "fakefile-2-hour-10-minutes",
download = yield Downloads.createDownload({
source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169",
target: "fakefile-2-hour-10-minutes",
startTime: now_uSec - 130 * kUsecPerMin, // 130 minutes ago, in uSec
endTime: now_uSec - 131 * kUsecPerMin, // 1 minute later
state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
guid: "z1bcD23eF4g5"
};
target: "fakefile-2-hour-10-minutes"
});
download.startTime = new Date(now_mSec - 130 * kMsecPerMin), // 130 minutes ago
download.canceled = true;
publicList.add(download);
try {
for (let prop in data)
stmt.params[prop] = data[prop];
stmt.execute();
}
finally {
stmt.reset();
}
// Add within-4-hour download
data = {
id: "5555553",
name: "fakefile-4-hour",
download = yield Downloads.createDownload({
source: "https://bugzilla.mozilla.org/show_bug.cgi?id=453440",
target: "fakefile-4-hour",
startTime: now_uSec - 180 * kUsecPerMin, // 180 minutes ago, in uSec
endTime: now_uSec - 179 * kUsecPerMin, // 1 minute later
state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
guid: "zzzcD23eF4g5"
};
try {
for (let prop in data)
stmt.params[prop] = data[prop];
stmt.execute();
}
finally {
stmt.reset();
}
// Add 4-hour-10-minutes download
data = {
id: "5555558",
name: "fakefile-4-hour-10-minutes",
target: "fakefile-4-hour"
});
download.startTime = new Date(now_mSec - 180 * kMsecPerMin), // 180 minutes ago
download.canceled = true;
publicList.add(download);
download = yield Downloads.createDownload({
source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169",
target: "fakefile-4-hour-10-minutes",
startTime: now_uSec - 250 * kUsecPerMin, // 250 minutes ago, in uSec
endTime: now_uSec - 251 * kUsecPerMin, // 1 minute later
state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
guid: "z1bzz23eF4gz"
};
try {
for (let prop in data)
stmt.params[prop] = data[prop];
stmt.execute();
}
finally {
stmt.reset();
}
target: "fakefile-4-hour-10-minutes"
});
download.startTime = new Date(now_mSec - 250 * kMsecPerMin), // 250 minutes ago
download.canceled = true;
publicList.add(download);
// Add "today" download
let today = new Date();
today.setHours(0);
today.setMinutes(0);
today.setSeconds(1);
data = {
id: "5555554",
name: "fakefile-today",
download = yield Downloads.createDownload({
source: "https://bugzilla.mozilla.org/show_bug.cgi?id=453440",
target: "fakefile-today",
startTime: today.getTime() * 1000, // 12:00:30am this morning, in uSec
endTime: (today.getTime() + 1000) * 1000, // 1 second later
state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
guid: "ffffD23eF4g5"
};
try {
for (let prop in data)
stmt.params[prop] = data[prop];
stmt.execute();
}
finally {
stmt.reset();
}
target: "fakefile-today"
});
download.startTime = today, // 12:00:01 AM this morning
download.canceled = true;
publicList.add(download);
// Add "before today" download
let lastYear = new Date();
lastYear.setFullYear(lastYear.getFullYear() - 1);
data = {
id: "5555550",
name: "fakefile-old",
download = yield Downloads.createDownload({
source: "https://bugzilla.mozilla.org/show_bug.cgi?id=453440",
target: "fakefile-old",
startTime: lastYear.getTime() * 1000, // 1 year ago, in uSec
endTime: (lastYear.getTime() + 1000) * 1000, // 1 second later
state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
guid: "ggggg23eF4g5"
};
try {
for (let prop in data)
stmt.params[prop] = data[prop];
stmt.execute();
}
finally {
stmt.finalize();
}
target: "fakefile-old"
});
download.startTime = lastYear,
download.canceled = true;
publicList.add(download);
// Confirm everything worked
ok(downloadExists(5555550), "Pretend download for everything case should exist");
ok(downloadExists(5555555), "Pretend download for 10-minutes case should exist");
ok(downloadExists(5555551), "Pretend download for 1-hour case should exist");
ok(downloadExists(5555556), "Pretend download for 1-hour-10-minutes case should exist");
ok(downloadExists(5555552), "Pretend download for 2-hour case should exist");
ok(downloadExists(5555557), "Pretend download for 2-hour-10-minutes case should exist");
ok(downloadExists(5555553), "Pretend download for 4-hour case should exist");
ok(downloadExists(5555558), "Pretend download for 4-hour-10-minutes case should exist");
ok(downloadExists(5555554), "Pretend download for Today case should exist");
let downloads = yield publicList.getAll();
is(downloads.length, 9, "9 Pretend downloads added");
ok((yield downloadExists(publicList, "fakefile-old")), "Pretend download for everything case should exist");
ok((yield downloadExists(publicList, "fakefile-10-minutes")), "Pretend download for 10-minutes case should exist");
ok((yield downloadExists(publicList, "fakefile-1-hour")), "Pretend download for 1-hour case should exist");
ok((yield downloadExists(publicList, "fakefile-1-hour-10-minutes")), "Pretend download for 1-hour-10-minutes case should exist");
ok((yield downloadExists(publicList, "fakefile-2-hour")), "Pretend download for 2-hour case should exist");
ok((yield downloadExists(publicList, "fakefile-2-hour-10-minutes")), "Pretend download for 2-hour-10-minutes case should exist");
ok((yield downloadExists(publicList, "fakefile-4-hour")), "Pretend download for 4-hour case should exist");
ok((yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "Pretend download for 4-hour-10-minutes case should exist");
ok((yield downloadExists(publicList, "fakefile-today")), "Pretend download for Today case should exist");
}
/**
@ -791,18 +717,12 @@ function setupDownloads() {
* @param aID
* The ids of the downloads to check.
*/
function downloadExists(aID)
function downloadExists(list, path)
{
let db = dm.DBConnection;
let stmt = db.createStatement(
"SELECT * " +
"FROM moz_downloads " +
"WHERE id = :id"
);
stmt.params.id = aID;
var rows = stmt.executeStep();
stmt.finalize();
return rows;
return Task.spawn(function() {
let listArray = yield list.getAll();
throw new Task.Result(listArray.some(i => i.target.path == path));
});
}
function isToday(aDate) {

View File

@ -21,18 +21,18 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
"resource://gre/modules/FormHistory.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
"resource://gre/modules/Downloads.jsm");
let tempScope = {};
Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
.loadSubScript("chrome://browser/content/sanitize.js", tempScope);
let Sanitizer = tempScope.Sanitizer;
const dm = Cc["@mozilla.org/download-manager;1"].
getService(Ci.nsIDownloadManager);
const kMsecPerMin = 60 * 1000;
const kUsecPerMin = 60 * 1000000;
let formEntries;
let formEntries, downloadIDs, olderDownloadIDs;
// Add tests here. Each is a function that's called by doNextTest().
var gAllTests = [
@ -92,6 +92,23 @@ var gAllTests = [
});
},
function () {
// Add downloads (within the past hour).
Task.spawn(function () {
downloadIDs = [];
for (let i = 0; i < 5; i++) {
yield addDownloadWithMinutesAgo(downloadIDs, i);
}
// Add downloads (over an hour ago).
olderDownloadIDs = [];
for (let i = 0; i < 5; i++) {
yield addDownloadWithMinutesAgo(olderDownloadIDs, 61 + i);
}
doNextTest();
}).then(null, Components.utils.reportError);
},
/**
* Ensures that the combined history-downloads checkbox clears both history
* visits and downloads when checked; the dialog respects simple timespan.
@ -115,16 +132,6 @@ var gAllTests = [
}
addVisits(places, function() {
// Add downloads (within the past hour).
let downloadIDs = [];
for (let i = 0; i < 5; i++) {
downloadIDs.push(addDownloadWithMinutesAgo(i));
}
// Add downloads (over an hour ago).
let olderDownloadIDs = [];
for (let i = 0; i < 5; i++) {
olderDownloadIDs.push(addDownloadWithMinutesAgo(61 + i));
}
let totalHistoryVisits = uris.length + olderURIs.length;
let wh = new WindowHelper();
@ -146,16 +153,16 @@ var gAllTests = [
wh.onunload = function () {
// History visits and downloads within one hour should be cleared.
yield promiseHistoryClearedState(uris, true);
ensureDownloadsClearedState(downloadIDs, true);
yield ensureDownloadsClearedState(downloadIDs, true);
// Visits and downloads > 1 hour should still exist.
yield promiseHistoryClearedState(olderURIs, false);
ensureDownloadsClearedState(olderDownloadIDs, false);
yield ensureDownloadsClearedState(olderDownloadIDs, false);
// OK, done, cleanup after ourselves.
yield blankSlate();
yield promiseHistoryClearedState(olderURIs, true);
ensureDownloadsClearedState(olderDownloadIDs, true);
yield ensureDownloadsClearedState(olderDownloadIDs, true);
};
wh.open();
});
@ -178,6 +185,18 @@ var gAllTests = [
iter.next();
},
function () {
// Add downloads (within the past hour).
Task.spawn(function () {
downloadIDs = [];
for (let i = 0; i < 5; i++) {
yield addDownloadWithMinutesAgo(downloadIDs, i);
}
doNextTest();
}).then(null, Components.utils.reportError);
},
/**
* Ensures that the combined history-downloads checkbox removes neither
* history visits nor downloads when not checked.
@ -194,11 +213,6 @@ var gAllTests = [
}
addVisits(places, function() {
let downloadIDs = [];
for (let i = 0; i < 5; i++) {
downloadIDs.push(addDownloadWithMinutesAgo(i));
}
let wh = new WindowHelper();
wh.onload = function () {
is(this.isWarningPanelVisible(), false,
@ -224,7 +238,7 @@ var gAllTests = [
wh.onunload = function () {
// Of the three only form entries should be cleared.
yield promiseHistoryClearedState(uris, false);
ensureDownloadsClearedState(downloadIDs, false);
yield ensureDownloadsClearedState(downloadIDs, false);
formEntries.forEach(function (entry) {
let exists = yield formNameExists(entry);
@ -234,7 +248,7 @@ var gAllTests = [
// OK, done, cleanup after ourselves.
yield blankSlate();
yield promiseHistoryClearedState(uris, true);
ensureDownloadsClearedState(downloadIDs, true);
yield ensureDownloadsClearedState(downloadIDs, true);
};
wh.open();
});
@ -639,15 +653,12 @@ var gAllTests = [
}
];
// Used as the download database ID for a new download. Incremented for each
// new download. See addDownloadWithMinutesAgo().
var gDownloadId = 5555551;
// Index in gAllTests of the test currently being run. Incremented for each
// test run. See doNextTest().
var gCurrTest = 0;
var now_uSec = Date.now() * 1000;
let now_mSec = Date.now();
let now_uSec = now_mSec * 1000;
///////////////////////////////////////////////////////////////////////////////
@ -847,7 +858,7 @@ WindowHelper.prototype = {
if (wh.onunload) {
Task.spawn(wh.onunload).then(function() {
waitForAsyncUpdates(doNextTest);
});
}).then(null, Components.utils.reportError);
} else {
waitForAsyncUpdates(doNextTest);
}
@ -900,40 +911,23 @@ WindowHelper.prototype = {
* @param aMinutesAgo
* The download will be downloaded this many minutes ago
*/
function addDownloadWithMinutesAgo(aMinutesAgo) {
function addDownloadWithMinutesAgo(aExpectedPathList, aMinutesAgo) {
let publicList = yield Downloads.getPublicDownloadList();
let name = "fakefile-" + aMinutesAgo + "-minutes-ago";
let data = {
id: gDownloadId,
name: name,
source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169",
target: name,
startTime: now_uSec - (aMinutesAgo * kUsecPerMin),
endTime: now_uSec - ((aMinutesAgo + 1) * kUsecPerMin),
state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0
};
let download = yield Downloads.createDownload({
source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169",
target: name
});
download.startTime = new Date(now_mSec - (aMinutesAgo * kMsecPerMin));
download.canceled = true;
publicList.add(download);
let db = dm.DBConnection;
let stmt = db.createStatement(
"INSERT INTO moz_downloads (id, name, source, target, startTime, endTime, " +
"state, currBytes, maxBytes, preferredAction, autoResume) " +
"VALUES (:id, :name, :source, :target, :startTime, :endTime, :state, " +
":currBytes, :maxBytes, :preferredAction, :autoResume)");
try {
for (let prop in data) {
stmt.params[prop] = data[prop];
}
stmt.execute();
}
finally {
stmt.reset();
}
is(downloadExists(gDownloadId), true,
"Sanity check: download " + gDownloadId +
ok((yield downloadExists(name)),
"Sanity check: download " + name +
" should exist after creating it");
return gDownloadId++;
aExpectedPathList.push(name);
}
/**
@ -984,15 +978,37 @@ function formNameExists(name)
*/
function blankSlate() {
PlacesUtils.bhistory.removeAllPages();
dm.cleanUp();
// The promise is resolved only when removing both downloads and form history are done.
let deferred = Promise.defer();
let formHistoryDone = false, downloadsDone = false;
Task.spawn(function deleteAllDownloads() {
let publicList = yield Downloads.getPublicDownloadList();
let downloads = yield publicList.getAll();
for (let download of downloads) {
publicList.remove(download);
yield download.finalize(true);
}
downloadsDone = true;
if (formHistoryDone) {
deferred.resolve();
}
}).then(null, Components.utils.reportError);
FormHistory.update({ op: "remove" },
{ handleError: function (error) {
do_throw("Error occurred updating form history: " + error);
deferred.reject(error);
},
handleCompletion: function (reason) { if (!reason) deferred.resolve(); }
handleCompletion: function (reason) {
if (!reason) {
formHistoryDone = true;
if (downloadsDone) {
deferred.resolve();
}
}
}
});
return deferred.promise;
}
@ -1012,24 +1028,19 @@ function boolPrefIs(aPrefName, aExpectedVal, aMsg) {
}
/**
* Checks to see if the download with the specified ID exists.
* Checks to see if the download with the specified path exists.
*
* @param aID
* The ID of the download to check
* @param aPath
* The path of the download to check
* @return True if the download exists, false otherwise
*/
function downloadExists(aID)
function downloadExists(aPath)
{
let db = dm.DBConnection;
let stmt = db.createStatement(
"SELECT * " +
"FROM moz_downloads " +
"WHERE id = :id"
);
stmt.params.id = aID;
let rows = stmt.executeStep();
stmt.finalize();
return !!rows;
return Task.spawn(function() {
let publicList = yield Downloads.getPublicDownloadList();
let listArray = yield publicList.getAll();
throw new Task.Result(listArray.some(i => i.target.path == aPath));
});
}
/**
@ -1059,7 +1070,7 @@ function doNextTest() {
function ensureDownloadsClearedState(aDownloadIDs, aShouldBeCleared) {
let niceStr = aShouldBeCleared ? "no longer" : "still";
aDownloadIDs.forEach(function (id) {
is(downloadExists(id), !aShouldBeCleared,
is((yield downloadExists(id)), !aShouldBeCleared,
"download " + id + " should " + niceStr + " exist");
});
}

View File

@ -14,6 +14,7 @@ MOCHITEST_BROWSER_FILES = \
browser_newtab_block.js \
browser_newtab_disable.js \
browser_newtab_drag_drop.js \
browser_newtab_drag_drop_ext.js \
browser_newtab_drop_preview.js \
browser_newtab_focus.js \
browser_newtab_reset.js \

View File

@ -71,47 +71,4 @@ function runTests() {
yield simulateDrop(0, 4);
checkGrid("3,1p,2p,4,0p,5p,6,7,8");
// drag a new site onto the very first cell
yield setLinks("0,1,2,3,4,5,6,7,8");
setPinnedLinks(",,,,,,,7,8");
yield addNewTabPageTab();
checkGrid("0,1,2,3,4,5,6,7p,8p");
yield simulateExternalDrop(0);
checkGrid("99p,0,1,2,3,4,5,7p,8p");
// drag a new site onto the grid and make sure that pinned cells don't get
// pushed out
yield setLinks("0,1,2,3,4,5,6,7,8");
setPinnedLinks(",,,,,,,7,8");
yield addNewTabPageTab();
checkGrid("0,1,2,3,4,5,6,7p,8p");
yield simulateExternalDrop(7);
checkGrid("0,1,2,3,4,5,7p,99p,8p");
// drag a new site beneath a pinned cell and make sure the pinned cell is
// not moved
yield setLinks("0,1,2,3,4,5,6,7,8");
setPinnedLinks(",,,,,,,,8");
yield addNewTabPageTab();
checkGrid("0,1,2,3,4,5,6,7,8p");
yield simulateExternalDrop(7);
checkGrid("0,1,2,3,4,5,6,99p,8p");
// drag a new site onto a block of pinned sites and make sure they're shifted
// around accordingly
yield setLinks("0,1,2,3,4,5,6,7,8");
setPinnedLinks("0,1,2,,,,,,");
yield addNewTabPageTab();
checkGrid("0p,1p,2p");
yield simulateExternalDrop(1);
checkGrid("0p,99p,1p,2p,3,4,5,6,7");
}

View File

@ -0,0 +1,55 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/*
* These tests make sure that dragging and dropping sites works as expected.
* Sites contained in the grid need to shift around to indicate the result
* of the drag-and-drop operation. If the grid is full and we're dragging
* a new site into it another one gets pushed out.
* This is a continuation of browser_newtab_drag_drop.js
* to decrease test run time, focusing on external sites.
*/
function runTests() {
// drag a new site onto the very first cell
yield setLinks("0,1,2,3,4,5,6,7,8");
setPinnedLinks(",,,,,,,7,8");
yield addNewTabPageTab();
checkGrid("0,1,2,3,4,5,6,7p,8p");
yield simulateExternalDrop(0);
checkGrid("99p,0,1,2,3,4,5,7p,8p");
// drag a new site onto the grid and make sure that pinned cells don't get
// pushed out
yield setLinks("0,1,2,3,4,5,6,7,8");
setPinnedLinks(",,,,,,,7,8");
yield addNewTabPageTab();
checkGrid("0,1,2,3,4,5,6,7p,8p");
yield simulateExternalDrop(7);
checkGrid("0,1,2,3,4,5,7p,99p,8p");
// drag a new site beneath a pinned cell and make sure the pinned cell is
// not moved
yield setLinks("0,1,2,3,4,5,6,7,8");
setPinnedLinks(",,,,,,,,8");
yield addNewTabPageTab();
checkGrid("0,1,2,3,4,5,6,7,8p");
yield simulateExternalDrop(7);
checkGrid("0,1,2,3,4,5,6,99p,8p");
// drag a new site onto a block of pinned sites and make sure they're shifted
// around accordingly
yield setLinks("0,1,2,3,4,5,6,7,8");
setPinnedLinks("0,1,2,,,,,,");
yield addNewTabPageTab();
checkGrid("0p,1p,2p");
yield simulateExternalDrop(1);
checkGrid("0p,99p,1p,2p,3,4,5,6,7");
}

View File

@ -30,6 +30,7 @@ MOCHITEST_BROWSER_FILES = \
browser_social_chatwindow_resize.js \
browser_social_chatwindowfocus.js \
browser_social_multiprovider.js \
browser_social_multiworker.js \
browser_social_errorPage.js \
browser_social_window.js \
social_activate.html \

View File

@ -2,17 +2,71 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
let manifests = [
{
name: "provider@example.com",
origin: "https://example.com",
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html?example.com",
workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
iconURL: "chrome://branding/content/icon48.png"
},
{
name: "provider@test1",
origin: "https://test1.example.com",
sidebarURL: "https://test1.example.com/browser/browser/base/content/test/social/social_sidebar.html?test1",
workerURL: "https://test1.example.com/browser/browser/base/content/test/social/social_worker.js",
iconURL: "chrome://branding/content/icon48.png"
},
{
name: "provider@test2",
origin: "https://test2.example.com",
sidebarURL: "https://test2.example.com/browser/browser/base/content/test/social/social_sidebar.html?test2",
workerURL: "https://test2.example.com/browser/browser/base/content/test/social/social_worker.js",
iconURL: "chrome://branding/content/icon48.png"
}
];
let chatId = 0;
function openChat(provider, callback) {
let chatUrl = provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
let port = provider.getWorkerPort();
port.onmessage = function(e) {
if (e.data.topic == "got-chatbox-message") {
port.close();
callback();
}
}
let url = chatUrl + "?" + (chatId++);
port.postMessage({topic: "test-init"});
port.postMessage({topic: "test-worker-chat", data: url});
gURLsNotRemembered.push(url);
}
function waitPrefChange(cb) {
Services.prefs.addObserver("social.enabled", function prefObserver(subject, topic, data) {
Services.prefs.removeObserver("social.enabled", prefObserver);
executeSoon(cb);
}, false);
}
function setWorkerMode(multiple, cb) {
waitPrefChange(function() {
if (multiple)
Services.prefs.setBoolPref("social.allowMultipleWorkers", true);
else
Services.prefs.clearUserPref("social.allowMultipleWorkers");
waitPrefChange(cb);
Social.enabled = true;
});
Social.enabled = false;
}
function test() {
requestLongerTimeout(2); // only debug builds seem to need more time...
waitForExplicitFinish();
let manifest = { // normal provider
name: "provider 1",
origin: "https://example.com",
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
iconURL: "https://example.com/browser/browser/base/content/test/moz.png"
};
let oldwidth = window.outerWidth; // we futz with these, so we restore them
let oldleft = window.screenX;
window.moveTo(0, window.screenY)
@ -21,7 +75,7 @@ function test() {
ok(chats.children.length == 0, "no chatty children left behind");
cb();
};
runSocialTestWithProvider(manifest, function (finishcb) {
runSocialTestWithProvider(manifests, function (finishcb) {
runSocialTests(tests, undefined, postSubTest, function() {
window.moveTo(oldleft, window.screenY)
window.resizeTo(oldwidth, window.outerHeight);
@ -147,7 +201,7 @@ var tests = {
maybeOpenAnother();
},
testWorkerChatWindow: function(next) {
const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html";
const chatUrl = Social.provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
let chats = document.getElementById("pinnedchats");
let port = Social.provider.getWorkerPort();
ok(port, "provider has a port");
@ -384,7 +438,7 @@ var tests = {
testSecondTopLevelWindow: function(next) {
// Bug 817782 - check chats work in new top-level windows.
const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html";
const chatUrl = Social.provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
let port = Social.provider.getWorkerPort();
let secondWindow;
port.onmessage = function(e) {
@ -407,23 +461,9 @@ var tests = {
testChatWindowChooser: function(next) {
// Tests that when a worker creates a chat, it is opened in the correct
// window.
const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html";
let chatId = 1;
let port = Social.provider.getWorkerPort();
port.postMessage({topic: "test-init"});
function openChat(callback) {
port.onmessage = function(e) {
if (e.data.topic == "got-chatbox-message")
callback();
}
let url = chatUrl + "?" + (chatId++);
port.postMessage({topic: "test-worker-chat", data: url});
}
// open a chat (it will open in the main window)
ok(!window.SocialChatBar.hasChats, "first window should start with no chats");
openChat(function() {
openChat(Social.provider, function() {
ok(window.SocialChatBar.hasChats, "first window has the chat");
// create a second window - this will be the "most recent" and will
// therefore be the window that hosts the new chat (see bug 835111)
@ -431,27 +471,55 @@ var tests = {
secondWindow.addEventListener("load", function loadListener() {
secondWindow.removeEventListener("load", loadListener);
ok(!secondWindow.SocialChatBar.hasChats, "second window has no chats");
openChat(function() {
openChat(Social.provider, function() {
ok(secondWindow.SocialChatBar.hasChats, "second window now has chats");
is(window.SocialChatBar.chatbar.childElementCount, 1, "first window still has 1 chat");
window.SocialChatBar.chatbar.removeAll();
// now open another chat - it should still open in the second.
openChat(function() {
openChat(Social.provider, function() {
ok(!window.SocialChatBar.hasChats, "first window has no chats");
ok(secondWindow.SocialChatBar.hasChats, "second window has a chat");
secondWindow.close();
port.close();
next();
});
});
})
});
},
testMultipleProviderChat: function(next) {
// while pref'd off, we need to set the worker mode to multiple providers
setWorkerMode(true, function() {
// test incomming chats from all providers
openChat(Social.providers[0], function() {
openChat(Social.providers[1], function() {
openChat(Social.providers[2], function() {
let chats = document.getElementById("pinnedchats");
waitForCondition(function() chats.children.length == Social.providers.length,
function() {
ok(true, "one chat window per provider opened");
// test logout of a single provider
let provider = Social.providers[0];
let port = provider.getWorkerPort();
port.postMessage({topic: "test-logout"});
waitForCondition(function() chats.children.length == Social.providers.length - 1,
function() {
port.close();
chats.removeAll();
ok(!chats.selectedChat, "chats are all closed");
setWorkerMode(false, next);
},
"chat window didn't close");
}, "chat windows did not open");
});
});
});
});
},
// XXX - note this must be the last test until we restore the login state
// between tests...
testCloseOnLogout: function(next) {
const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html";
const chatUrl = Social.provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
let port = Social.provider.getWorkerPort();
ok(port, "provider has a port");
let opened = false;

View File

@ -0,0 +1,101 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
function test() {
waitForExplicitFinish();
Services.prefs.setBoolPref("social.allowMultipleWorkers", true);
runSocialTestWithProvider(gProviders, function (finishcb) {
runSocialTests(tests, undefined, undefined, function() {
Services.prefs.clearUserPref("social.allowMultipleWorkers");
finishcb();
});
});
}
let gProviders = [
{
name: "provider 1",
origin: "https://example.com",
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html?provider1",
workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
iconURL: "chrome://branding/content/icon48.png"
},
{
name: "provider 2",
origin: "https://test1.example.com",
sidebarURL: "https://test1.example.com/browser/browser/base/content/test/social/social_sidebar.html?provider2",
workerURL: "https://test1.example.com/browser/browser/base/content/test/social/social_worker.js",
iconURL: "chrome://branding/content/icon48.png"
}
];
var tests = {
testWorkersAlive: function(next) {
// verify we can get a message from all providers that are enabled
let messageReceived = 0;
function oneWorkerTest(provider) {
let port = provider.getWorkerPort();
port.onmessage = function (e) {
let topic = e.data.topic;
switch (topic) {
case "test-init-done":
ok(true, "got message from provider " + provider.name);
port.close();
messageReceived++;
break;
}
};
port.postMessage({topic: "test-init"});
}
for (let p of Social.providers) {
oneWorkerTest(p);
}
waitForCondition(function() messageReceived == Social.providers.length,
next, "received messages from all workers");
},
testWorkerDisabling: function(next) {
Social.enabled = false;
is(Social.providers.length, gProviders.length, "providers still available");
for (let p of Social.providers) {
ok(!p.enabled, "provider disabled");
ok(!p.getWorkerPort(), "worker disabled");
}
next();
},
testSingleWorkerEnabling: function(next) {
// test that only one worker is enabled when we limit workers
Services.prefs.setBoolPref("social.allowMultipleWorkers", false);
Social.enabled = true;
for (let p of Social.providers) {
if (p == Social.provider) {
ok(p.enabled, "primary provider enabled");
let port = p.getWorkerPort();
ok(port, "primary worker enabled");
port.close();
} else {
ok(!p.enabled, "secondary provider is not enabled");
ok(!p.getWorkerPort(), "secondary worker disabled");
}
}
next();
},
testMultipleWorkerEnabling: function(next) {
// test that all workers are enabled when we allow multiple workers
Social.enabled = false;
Services.prefs.setBoolPref("social.allowMultipleWorkers", true);
Social.enabled = true;
for (let p of Social.providers) {
ok(p.enabled, "provider enabled");
let port = p.getWorkerPort();
ok(port, "worker enabled");
port.close();
}
next();
}
}

View File

@ -417,8 +417,8 @@ function get3ChatsForCollapsing(mode, cb) {
function makeChat(mode, uniqueid, cb) {
info("making a chat window '" + uniqueid +"'");
const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html";
let provider = Social.provider;
const chatUrl = provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
let isOpened = window.SocialChatBar.openChat(provider, chatUrl + "?id=" + uniqueid, function(chat) {
info("chat window has opened");
// we can't callback immediately or we might close the chat during

View File

@ -61,20 +61,20 @@ NS_DEFINE_NAMED_CID(NS_SHELLSERVICE_CID);
#endif
static const mozilla::Module::CIDEntry kBrowserCIDs[] = {
{ &kNS_BROWSERDIRECTORYPROVIDER_CID, false, NULL, DirectoryProviderConstructor },
{ &kNS_BROWSERDIRECTORYPROVIDER_CID, false, nullptr, DirectoryProviderConstructor },
#if defined(XP_WIN)
{ &kNS_SHELLSERVICE_CID, false, NULL, nsWindowsShellServiceConstructor },
{ &kNS_SHELLSERVICE_CID, false, nullptr, nsWindowsShellServiceConstructor },
#elif defined(MOZ_WIDGET_GTK)
{ &kNS_SHELLSERVICE_CID, false, NULL, nsGNOMEShellServiceConstructor },
{ &kNS_SHELLSERVICE_CID, false, nullptr, nsGNOMEShellServiceConstructor },
#endif
{ &kNS_FEEDSNIFFER_CID, false, NULL, nsFeedSnifferConstructor },
{ &kNS_BROWSER_ABOUT_REDIRECTOR_CID, false, NULL, AboutRedirector::Create },
{ &kNS_FEEDSNIFFER_CID, false, nullptr, nsFeedSnifferConstructor },
{ &kNS_BROWSER_ABOUT_REDIRECTOR_CID, false, nullptr, AboutRedirector::Create },
#if defined(XP_WIN)
{ &kNS_WINIEHISTORYENUMERATOR_CID, false, NULL, nsIEHistoryEnumeratorConstructor },
{ &kNS_WINIEHISTORYENUMERATOR_CID, false, nullptr, nsIEHistoryEnumeratorConstructor },
#elif defined(XP_MACOSX)
{ &kNS_SHELLSERVICE_CID, false, NULL, nsMacShellServiceConstructor },
{ &kNS_SHELLSERVICE_CID, false, nullptr, nsMacShellServiceConstructor },
#endif
{ NULL }
{ nullptr }
};
static const mozilla::Module::ContractIDEntry kBrowserContracts[] = {
@ -113,13 +113,13 @@ static const mozilla::Module::ContractIDEntry kBrowserContracts[] = {
#elif defined(XP_MACOSX)
{ NS_SHELLSERVICE_CONTRACTID, &kNS_SHELLSERVICE_CID },
#endif
{ NULL }
{ nullptr }
};
static const mozilla::Module::CategoryEntry kBrowserCategories[] = {
{ XPCOM_DIRECTORY_PROVIDER_CATEGORY, "browser-directory-provider", NS_BROWSERDIRECTORYPROVIDER_CONTRACTID },
{ NS_CONTENT_SNIFFER_CATEGORY, "Feed Sniffer", NS_FEEDSNIFFER_CONTRACTID },
{ NULL }
{ nullptr }
};
static const mozilla::Module kBrowserModule = {

View File

@ -7,39 +7,33 @@
* Make sure the downloads panel can display items in the right order and
* contains the expected data.
*/
function gen_test()
function test_task()
{
// Display one of each download state.
const DownloadData = [
{ endTime: 1180493839859239, state: nsIDM.DOWNLOAD_NOTSTARTED },
{ endTime: 1180493839859238, state: nsIDM.DOWNLOAD_DOWNLOADING },
{ endTime: 1180493839859237, state: nsIDM.DOWNLOAD_PAUSED },
{ endTime: 1180493839859236, state: nsIDM.DOWNLOAD_SCANNING },
{ endTime: 1180493839859235, state: nsIDM.DOWNLOAD_QUEUED },
{ endTime: 1180493839859234, state: nsIDM.DOWNLOAD_FINISHED },
{ endTime: 1180493839859233, state: nsIDM.DOWNLOAD_FAILED },
{ endTime: 1180493839859232, state: nsIDM.DOWNLOAD_CANCELED },
{ endTime: 1180493839859231, state: nsIDM.DOWNLOAD_BLOCKED_PARENTAL },
{ endTime: 1180493839859230, state: nsIDM.DOWNLOAD_DIRTY },
{ endTime: 1180493839859229, state: nsIDM.DOWNLOAD_BLOCKED_POLICY },
{ state: nsIDM.DOWNLOAD_NOTSTARTED },
{ state: nsIDM.DOWNLOAD_PAUSED },
{ state: nsIDM.DOWNLOAD_FINISHED },
{ state: nsIDM.DOWNLOAD_FAILED },
{ state: nsIDM.DOWNLOAD_CANCELED },
];
// For testing purposes, show all the download items at once.
var originalCountLimit = DownloadsView.kItemCountLimit;
DownloadsView.kItemCountLimit = DownloadData.length;
registerCleanupFunction(function () {
DownloadsView.kItemCountLimit = originalCountLimit;
});
try {
// Ensure that state is reset in case previous tests didn't finish.
for (let yy in gen_resetState(DownloadsCommon.getData(window))) yield undefined;
yield task_resetState();
// For testing purposes, show all the download items at once.
var originalCountLimit = DownloadsView.kItemCountLimit;
DownloadsView.kItemCountLimit = DownloadData.length;
registerCleanupFunction(function () {
DownloadsView.kItemCountLimit = originalCountLimit;
});
// Populate the downloads database with the data required by this test.
for (let yy in gen_addDownloadRows(DownloadData)) yield undefined;
yield task_addDownloads(DownloadData);
// Open the user interface and wait for data to be fully loaded.
for (let yy in gen_openPanel(DownloadsCommon.getData(window))) yield undefined;
yield task_openPanel();
// Test item data and count. This also tests the ordering of the display.
let richlistbox = document.getElementById("downloadsListBox");
@ -47,16 +41,14 @@ function gen_test()
is(richlistbox.children.length, DownloadData.length,
"There is the correct number of richlistitems");
*/
for (let i = 0; i < richlistbox.children.length; i++) {
let element = richlistbox.children[i];
let itemCount = richlistbox.children.length;
for (let i = 0; i < itemCount; i++) {
let element = richlistbox.children[itemCount - i - 1];
let dataItem = new DownloadsViewItemController(element).dataItem;
is(dataItem.target, DownloadData[i].name, "Download names match up");
is(dataItem.state, DownloadData[i].state, "Download states match up");
is(dataItem.file, DownloadData[i].target, "Download targets match up");
is(dataItem.uri, DownloadData[i].source, "Download sources match up");
}
} finally {
// Clean up when the test finishes.
for (let yy in gen_resetState(DownloadsCommon.getData(window))) yield undefined;
yield task_resetState();
}
}

View File

@ -8,19 +8,19 @@
* download it notices. All subsequent downloads, even across sessions, should
* not open the panel automatically.
*/
function gen_test()
function test_task()
{
try {
// Ensure that state is reset in case previous tests didn't finish.
for (let yy in gen_resetState(DownloadsCommon.getData(window))) yield undefined;
yield task_resetState();
// With this set to false, we should automatically open the panel
// the first time a download is started.
// With this set to false, we should automatically open the panel the first
// time a download is started.
DownloadsCommon.getData(window).panelHasShownBefore = false;
prepareForPanelOpen();
let promise = promisePanelOpened();
DownloadsCommon.getData(window)._notifyDownloadEvent("start");
yield undefined;
yield promise;
// If we got here, that means the panel opened.
DownloadsPanel.hidePanel();
@ -28,29 +28,26 @@ function gen_test()
ok(DownloadsCommon.getData(window).panelHasShownBefore,
"Should have recorded that the panel was opened on a download.")
// Next, make sure that if we start another download, we don't open
// the panel automatically.
panelShouldNotOpen();
DownloadsCommon.getData(window)._notifyDownloadEvent("start");
yield waitFor(2);
} catch(e) {
ok(false, e);
// Next, make sure that if we start another download, we don't open the
// panel automatically.
let originalOnPopupShown = DownloadsPanel.onPopupShown;
DownloadsPanel.onPopupShown = function () {
originalOnPopupShown.apply(this, arguments);
ok(false, "Should not have opened the downloads panel.");
};
try {
DownloadsCommon.getData(window)._notifyDownloadEvent("start");
// Wait 2 seconds to ensure that the panel does not open.
let deferTimeout = Promise.defer();
setTimeout(deferTimeout.resolve, 2000);
yield deferTimeout.promise;
} finally {
DownloadsPanel.onPopupShown = originalOnPopupShown;
}
} finally {
// Clean up when the test finishes.
for (let yy in gen_resetState(DownloadsCommon.getData(window))) yield undefined;
yield task_resetState();
}
}
/**
* Call this to record a test failure for the next time the downloads panel
* opens.
*/
function panelShouldNotOpen()
{
// Hook to wait until the test data has been loaded.
let originalOnViewLoadCompleted = DownloadsPanel.onViewLoadCompleted;
DownloadsPanel.onViewLoadCompleted = function () {
DownloadsPanel.onViewLoadCompleted = originalOnViewLoadCompleted;
ok(false, "Should not have opened the downloads panel.");
};
}

View File

@ -10,10 +10,16 @@
////////////////////////////////////////////////////////////////////////////////
//// Globals
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
"resource://gre/modules/FileUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
"resource://gre/modules/Downloads.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
"resource:///modules/DownloadsCommon.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
"resource://gre/modules/FileUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/Promise.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
const nsIDM = Ci.nsIDownloadManager;
let gTestTargetFile = FileUtils.getFile("TmpD", ["dm-ui-test.file"]);
@ -22,253 +28,85 @@ registerCleanupFunction(function () {
gTestTargetFile.remove(false);
});
/**
* This objects contains a property for each column in the downloads table.
*/
let gDownloadRowTemplate = {
name: "test-download.txt",
source: "http://www.example.com/test-download.txt",
target: NetUtil.newURI(gTestTargetFile).spec,
startTime: 1180493839859230,
endTime: 1180493839859234,
state: nsIDM.DOWNLOAD_FINISHED,
currBytes: 0,
maxBytes: -1,
preferredAction: 0,
autoResume: 0
};
////////////////////////////////////////////////////////////////////////////////
//// Infrastructure
// All test are run through the test runner.
function test()
{
testRunner.runTest(this.gen_test);
waitForExplicitFinish();
Task.spawn(test_task).then(null, ex => ok(false, ex)).then(finish);
}
/**
* Runs a browser-chrome test defined through a generator function.
*
* This object is a singleton, initialized automatically when this script is
* included. Every browser-chrome test file includes a new copy of this object.
*/
var testRunner = {
_testIterator: null,
_lastEventResult: undefined,
_testRunning: false,
_eventRaised: false,
// --- Main test runner ---
/**
* Runs the test described by the provided generator function asynchronously.
*
* Calling yield in the generator will cause it to wait until continueTest is
* called. The parameter provided to continueTest will be the return value of
* the yield operator.
*
* @param aGenerator
* Test generator function. The function will be called with no
* arguments to retrieve its iterator.
*/
runTest: function TR_runTest(aGenerator) {
waitForExplicitFinish();
testRunner._testIterator = aGenerator();
testRunner.continueTest();
},
/**
* Continues the currently running test.
*
* @param aEventResult
* This will be the return value of the yield operator in the test.
*/
continueTest: function TR_continueTest(aEventResult) {
// Store the last event result, or set it to undefined.
testRunner._lastEventResult = aEventResult;
// Never reenter the main loop, but notify that the event has been raised.
if (testRunner._testRunning) {
testRunner._eventRaised = true;
return;
}
// Enter the main iteration loop.
testRunner._testRunning = true;
try {
do {
// Call the iterator, but don't leave the loop if the expected event is
// raised during the execution of the generator.
testRunner._eventRaised = false;
testRunner._testIterator.send(testRunner._lastEventResult);
} while (testRunner._eventRaised);
}
catch (e) {
// This block catches exceptions raised by the generator, including the
// normal StopIteration exception. Unexpected exceptions are reported as
// test failures.
if (!(e instanceof StopIteration))
ok(false, e);
// In any case, stop the tests in this file.
finish();
}
// Wait for the next event or finish.
testRunner._testRunning = false;
}
};
////////////////////////////////////////////////////////////////////////////////
//// Asynchronous generator-based support subroutines
//// Asynchronous support subroutines
//
// The following functions are all generators that can be used inside the main
// test generator to perform specific tasks asynchronously. To invoke these
// subroutines correctly, an iteration syntax should be used:
//
// for (let yy in gen_example("Parameter")) yield undefined;
//
function gen_resetState(aData)
function promiseFocus()
{
let statement = Services.downloads.DBConnection.createAsyncStatement(
"DELETE FROM moz_downloads");
try {
statement.executeAsync({
handleResult: function(aResultSet) { },
handleError: function(aError)
{
Cu.reportError(aError);
},
handleCompletion: function(aReason)
{
testRunner.continueTest();
}
});
yield undefined;
} finally {
statement.finalize();
let deferred = Promise.defer();
waitForFocus(deferred.resolve);
return deferred.promise;
}
function promisePanelOpened()
{
let deferred = Promise.defer();
// Hook to wait until the panel is shown.
let originalOnPopupShown = DownloadsPanel.onPopupShown;
DownloadsPanel.onPopupShown = function () {
DownloadsPanel.onPopupShown = originalOnPopupShown;
originalOnPopupShown.apply(this, arguments);
// Defer to the next tick of the event loop so that we don't continue
// processing during the DOM event handler itself.
setTimeout(deferred.resolve, 0);
};
return deferred.promise;
}
function task_resetState()
{
// Remove all downloads.
let publicList = yield Downloads.getPublicDownloadList();
let downloads = yield publicList.getAll();
for (let download of downloads) {
publicList.remove(download);
yield download.finalize(true);
}
// Reset any prefs that might have been changed.
Services.prefs.clearUserPref("browser.download.panel.shown");
// Ensure that the panel is closed and data is unloaded.
aData.clear();
aData._loadState = aData.kLoadNone;
DownloadsPanel.hidePanel();
// Wait for focus on the main window.
waitForFocus(testRunner.continueTest);
yield undefined;
yield promiseFocus();
}
function gen_addDownloadRows(aDataRows)
function task_addDownloads(aItems)
{
let columnNames = Object.keys(gDownloadRowTemplate).join(", ");
let parameterNames = Object.keys(gDownloadRowTemplate)
.map(function(n) ":" + n)
.join(", ");
let statement = Services.downloads.DBConnection.createAsyncStatement(
"INSERT INTO moz_downloads (" + columnNames +
", guid) VALUES(" + parameterNames + ", GENERATE_GUID())");
try {
// Execute the statement for each of the provided downloads in reverse.
for (let i = aDataRows.length - 1; i >= 0; i--) {
let dataRow = aDataRows[i];
let startTimeMs = Date.now();
// Populate insert parameters from the provided data.
for (let columnName in gDownloadRowTemplate) {
if (!(columnName in dataRow)) {
// Update the provided row object with data from the global template,
// for columns whose value is not provided explicitly.
dataRow[columnName] = gDownloadRowTemplate[columnName];
}
statement.params[columnName] = dataRow[columnName];
}
// Run the statement asynchronously and wait.
statement.executeAsync({
handleResult: function(aResultSet) { },
handleError: function(aError)
{
Cu.reportError(aError.message + " (Result = " + aError.result + ")");
},
handleCompletion: function(aReason)
{
testRunner.continueTest();
}
});
yield undefined;
// At each iteration, ensure that the start and end time in the global
// template is distinct, as these column are used to sort each download
// in its category.
gDownloadRowTemplate.startTime++;
gDownloadRowTemplate.endTime++;
}
} finally {
statement.finalize();
let publicList = yield Downloads.getPublicDownloadList();
for (let item of aItems) {
publicList.add(yield Downloads.createDownload({
source: "http://www.example.com/test-download.txt",
target: gTestTargetFile,
succeeded: item.state == nsIDM.DOWNLOAD_FINISHED,
canceled: item.state == nsIDM.DOWNLOAD_CANCELED ||
item.state == nsIDM.DOWNLOAD_PAUSED,
error: item.state == nsIDM.DOWNLOAD_FAILED ? new Error("Failed.") : null,
hasPartialData: item.state == nsIDM.DOWNLOAD_PAUSED,
startTime: new Date(startTimeMs++),
}));
}
}
function gen_openPanel(aData)
function task_openPanel()
{
// Hook to wait until the test data has been loaded.
let originalOnViewLoadCompleted = DownloadsPanel.onViewLoadCompleted;
DownloadsPanel.onViewLoadCompleted = function () {
DownloadsPanel.onViewLoadCompleted = originalOnViewLoadCompleted;
originalOnViewLoadCompleted.apply(this);
testRunner.continueTest();
};
yield promiseFocus();
// Start loading all the downloads from the database asynchronously.
aData.ensurePersistentDataLoaded(false);
// Wait for focus on the main window.
waitForFocus(testRunner.continueTest);
yield undefined;
// Open the downloads panel, waiting until loading is completed.
let promise = promisePanelOpened();
DownloadsPanel.showPanel();
yield undefined;
}
/**
* Spin the event loop for aSeconds seconds, and then signal the test to
* continue.
*
* @param aSeconds the number of seconds to wait.
* @note This helper should _only_ be used when there's no valid event to
* listen to and one can't be made.
*/
function waitFor(aSeconds)
{
setTimeout(function() {
testRunner.continueTest();
}, aSeconds * 1000);
}
/**
* Make it so that the next time the downloads panel opens, we signal to
* continue the test. This function needs to be called each time you want
* to wait for the panel to open.
*
* Example usage:
*
* prepareForPanelOpen();
* // Do something to open the panel
* yield undefined;
* // We can assume the panel is open now.
*/
function prepareForPanelOpen()
{
// Hook to wait until the test data has been loaded.
let originalOnPopupShown = DownloadsPanel.onPopupShown;
DownloadsPanel.onPopupShown = function (aEvent) {
DownloadsPanel.onPopupShown = originalOnPopupShown;
DownloadsPanel.onPopupShown.apply(this, [aEvent]);
testRunner.continueTest();
};
yield promise;
}

View File

@ -45,7 +45,7 @@ NS_IMPL_ISUPPORTS1(nsIEHistoryEnumerator, nsISimpleEnumerator)
nsIEHistoryEnumerator::nsIEHistoryEnumerator()
{
::CoInitialize(NULL);
::CoInitialize(nullptr);
}
nsIEHistoryEnumerator::~nsIEHistoryEnumerator()
@ -60,7 +60,7 @@ nsIEHistoryEnumerator::EnsureInitialized()
return;
HRESULT hr = ::CoCreateInstance(CLSID_CUrlHistory,
NULL,
nullptr,
CLSCTX_INPROC_SERVER,
IID_IUrlHistoryStg2,
getter_AddRefs(mIEHistory));

View File

@ -262,6 +262,12 @@ let SessionSaverInternal = {
return;
}
// We update the time stamp before writing so that we don't write again
// too soon, if saving is requested before the write completes. Without
// this update we may save repeatedly if actions cause a runDelayed
// before writing has completed. See Bug 902280
this.updateLastSaveTime();
// Write (atomically) to a session file, using a tmp file. Once the session
// file is successfully updated, save the time stamp of the last save and
// notify the observers.

View File

@ -75,7 +75,7 @@ this._SessionFile = {
* state. This must only be called once, it will throw an error otherwise.
*/
writeLoadStateOnceAfterStartup: function (aLoadState) {
return SessionFileInternal.writeLoadStateOnceAfterStartup(aLoadState);
SessionFileInternal.writeLoadStateOnceAfterStartup(aLoadState);
},
/**
* Create a backup copy, asynchronously.
@ -95,7 +95,7 @@ this._SessionFile = {
* Wipe the contents of the session file, asynchronously.
*/
wipe: function () {
return SessionFileInternal.wipe();
SessionFileInternal.wipe();
}
};
@ -231,7 +231,7 @@ let SessionFileInternal = {
},
writeLoadStateOnceAfterStartup: function (aLoadState) {
return SessionWorker.post("writeLoadStateOnceAfterStartup", [aLoadState]).then(msg => {
SessionWorker.post("writeLoadStateOnceAfterStartup", [aLoadState]).then(msg => {
this._recordTelemetry(msg.telemetry);
return msg;
});
@ -246,7 +246,7 @@ let SessionFileInternal = {
},
wipe: function () {
return SessionWorker.post("wipe");
SessionWorker.post("wipe");
},
_recordTelemetry: function(telemetry) {

View File

@ -152,7 +152,8 @@ nsGNOMEShellService::KeyMatchesAppName(const char *aKeyValue) const
gchar *commandPath;
if (mUseLocaleFilenames) {
gchar *nativePath = g_filename_from_utf8(aKeyValue, -1, NULL, NULL, NULL);
gchar *nativePath = g_filename_from_utf8(aKeyValue, -1,
nullptr, nullptr, nullptr);
if (!nativePath) {
NS_ERROR("Error converting path to filesystem encoding");
return false;
@ -182,7 +183,7 @@ nsGNOMEShellService::CheckHandlerMatchesAppName(const nsACString &handler) const
// The string will be something of the form: [/path/to/]browser "%s"
// We want to remove all of the parameters and get just the binary name.
if (g_shell_parse_argv(command.get(), &argc, &argv, NULL) && argc > 0) {
if (g_shell_parse_argv(command.get(), &argc, &argv, nullptr) && argc > 0) {
command.Assign(argv[0]);
g_strfreev(argv);
}
@ -380,7 +381,7 @@ WriteImage(const nsCString& aPath, imgIContainer* aImage)
if (!pixbuf)
return NS_ERROR_NOT_AVAILABLE;
gboolean res = gdk_pixbuf_save(pixbuf, aPath.get(), "png", NULL, NULL);
gboolean res = gdk_pixbuf_save(pixbuf, aPath.get(), "png", nullptr, nullptr);
g_object_unref(pixbuf);
return res ? NS_OK : NS_ERROR_FAILURE;
@ -454,7 +455,7 @@ nsGNOMEShellService::SetDesktopBackground(nsIDOMElement* aElement,
gsettings->GetCollectionForSchema(
NS_LITERAL_CSTRING(kDesktopBGSchema), getter_AddRefs(background_settings));
if (background_settings) {
gchar *file_uri = g_filename_to_uri(filePath.get(), NULL, NULL);
gchar *file_uri = g_filename_to_uri(filePath.get(), nullptr, nullptr);
if (!file_uri)
return NS_ERROR_FAILURE;
@ -615,7 +616,7 @@ nsGNOMEShellService::OpenApplication(int32_t aApplication)
// Perform shell argument expansion
int argc;
char **argv;
if (!g_shell_parse_argv(appCommand.get(), &argc, &argv, NULL))
if (!g_shell_parse_argv(appCommand.get(), &argc, &argv, nullptr))
return NS_ERROR_FAILURE;
char **newArgv = new char*[argc + 1];
@ -630,8 +631,8 @@ nsGNOMEShellService::OpenApplication(int32_t aApplication)
newArgv[newArgc] = nullptr;
gboolean err = g_spawn_async(NULL, newArgv, NULL, G_SPAWN_SEARCH_PATH,
NULL, NULL, NULL, NULL);
gboolean err = g_spawn_async(nullptr, newArgv, nullptr, G_SPAWN_SEARCH_PATH,
nullptr, nullptr, nullptr, nullptr);
g_strfreev(argv);
delete[] newArgv;

View File

@ -40,13 +40,14 @@ nsMacShellService::IsDefaultBrowser(bool aStartupCheck,
CFStringRef firefoxID = ::CFBundleGetIdentifier(::CFBundleGetMainBundle());
if (!firefoxID) {
// CFBundleGetIdentifier is expected to return NULL only if the specified
// CFBundleGetIdentifier is expected to return nullptr only if the specified
// bundle doesn't have a bundle identifier in its plist. In this case, that
// means a failure, since our bundle does have an identifier.
return NS_ERROR_FAILURE;
}
// Get the default http handler's bundle ID (or NULL if it has not been explicitly set)
// Get the default http handler's bundle ID (or nullptr if it has not been
// explicitly set)
CFStringRef defaultBrowserID = ::LSCopyDefaultHandlerForURLScheme(CFSTR("http"));
if (defaultBrowserID) {
*aIsDefaultBrowser = ::CFStringCompare(firefoxID, defaultBrowserID, 0) == kCFCompareEqualTo;
@ -259,7 +260,8 @@ nsMacShellService::OnStateChange(nsIWebProgress* aWebProgress,
OSStatus status;
// Convert the path into a FSRef
status = ::FSPathMakeRef((const UInt8*)nativePath.get(), &pictureRef, NULL);
status = ::FSPathMakeRef((const UInt8*)nativePath.get(), &pictureRef,
nullptr);
if (status == noErr) {
err = ::FSNewAlias(nil, &pictureRef, &aliasHandle);
if (err == noErr && aliasHandle == nil)
@ -312,22 +314,22 @@ nsMacShellService::OpenApplication(int32_t aApplication)
case nsIShellService::APPLICATION_MAIL:
{
CFURLRef tempURL = ::CFURLCreateWithString(kCFAllocatorDefault,
CFSTR("mailto:"), NULL);
err = ::LSGetApplicationForURL(tempURL, kLSRolesAll, NULL, &appURL);
CFSTR("mailto:"), nullptr);
err = ::LSGetApplicationForURL(tempURL, kLSRolesAll, nullptr, &appURL);
::CFRelease(tempURL);
}
break;
case nsIShellService::APPLICATION_NEWS:
{
CFURLRef tempURL = ::CFURLCreateWithString(kCFAllocatorDefault,
CFSTR("news:"), NULL);
err = ::LSGetApplicationForURL(tempURL, kLSRolesAll, NULL, &appURL);
CFSTR("news:"), nullptr);
err = ::LSGetApplicationForURL(tempURL, kLSRolesAll, nullptr, &appURL);
::CFRelease(tempURL);
}
break;
case nsIMacShellService::APPLICATION_KEYCHAIN_ACCESS:
err = ::LSGetApplicationForInfo('APPL', 'kcmr', NULL, kLSRolesAll, NULL,
&appURL);
err = ::LSGetApplicationForInfo('APPL', 'kcmr', nullptr, kLSRolesAll,
nullptr, &appURL);
break;
case nsIMacShellService::APPLICATION_NETWORK:
{
@ -356,7 +358,7 @@ nsMacShellService::OpenApplication(int32_t aApplication)
}
if (appURL && err == noErr) {
err = ::LSOpenCFURLRef(appURL, NULL);
err = ::LSOpenCFURLRef(appURL, nullptr);
rv = err != noErr ? NS_ERROR_FAILURE : NS_OK;
::CFRelease(appURL);
@ -394,12 +396,12 @@ nsMacShellService::OpenApplicationWithURI(nsIFile* aApplication, const nsACStrin
const nsCString spec(aURI);
const UInt8* uriString = (const UInt8*)spec.get();
CFURLRef uri = ::CFURLCreateWithBytes(NULL, uriString, aURI.Length(),
kCFStringEncodingUTF8, NULL);
CFURLRef uri = ::CFURLCreateWithBytes(nullptr, uriString, aURI.Length(),
kCFStringEncodingUTF8, nullptr);
if (!uri)
return NS_ERROR_OUT_OF_MEMORY;
CFArrayRef uris = ::CFArrayCreate(NULL, (const void**)&uri, 1, NULL);
CFArrayRef uris = ::CFArrayCreate(nullptr, (const void**)&uri, 1, nullptr);
if (!uris) {
::CFRelease(uri);
return NS_ERROR_OUT_OF_MEMORY;
@ -408,11 +410,11 @@ nsMacShellService::OpenApplicationWithURI(nsIFile* aApplication, const nsACStrin
LSLaunchURLSpec launchSpec;
launchSpec.appURL = appURL;
launchSpec.itemURLs = uris;
launchSpec.passThruParams = NULL;
launchSpec.passThruParams = nullptr;
launchSpec.launchFlags = kLSLaunchDefaults;
launchSpec.asyncRefCon = NULL;
launchSpec.asyncRefCon = nullptr;
OSErr err = ::LSOpenFromURLSpec(&launchSpec, NULL);
OSErr err = ::LSOpenFromURLSpec(&launchSpec, nullptr);
::CFRelease(uris);
::CFRelease(uri);
@ -433,11 +435,11 @@ nsMacShellService::GetDefaultFeedReader(nsIFile** _retval)
kCFStringEncodingASCII);
}
CFURLRef defaultHandlerURL = NULL;
CFURLRef defaultHandlerURL = nullptr;
OSStatus status = ::LSFindApplicationForInfo(kLSUnknownCreator,
defaultHandlerID,
NULL, // inName
NULL, // outAppRef
nullptr, // inName
nullptr, // outAppRef
&defaultHandlerURL);
if (status == noErr && defaultHandlerURL) {

View File

@ -223,8 +223,8 @@ LaunchHelper(nsAutoString& aPath)
STARTUPINFOW si = {sizeof(si), 0};
PROCESS_INFORMATION pi = {0};
if (!CreateProcessW(NULL, (LPWSTR)aPath.get(), NULL, NULL, FALSE, 0, NULL,
NULL, &si, &pi)) {
if (!CreateProcessW(nullptr, (LPWSTR)aPath.get(), nullptr, nullptr, FALSE,
0, nullptr, nullptr, &si, &pi)) {
return NS_ERROR_FAILURE;
}
@ -361,7 +361,7 @@ nsWindowsShellService::IsDefaultBrowserVista(bool aCheckAllTypes,
{
IApplicationAssociationRegistration* pAAR;
HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration,
NULL,
nullptr,
CLSCTX_INPROC,
IID_IApplicationAssociationRegistration,
(void**)&pAAR);
@ -447,7 +447,8 @@ nsWindowsShellService::IsDefaultBrowser(bool aStartupCheck,
::ZeroMemory(currValue, sizeof(currValue));
DWORD len = sizeof currValue;
res = ::RegQueryValueExW(theKey, L"", NULL, NULL, (LPBYTE)currValue, &len);
res = ::RegQueryValueExW(theKey, L"", nullptr, nullptr,
(LPBYTE)currValue, &len);
// Close the key that was opened.
::RegCloseKey(theKey);
if (REG_FAILED(res) ||
@ -516,8 +517,8 @@ nsWindowsShellService::IsDefaultBrowser(bool aStartupCheck,
::ZeroMemory(currValue, sizeof(currValue));
DWORD len = sizeof currValue;
res = ::RegQueryValueExW(theKey, L"", NULL, NULL, (LPBYTE)currValue,
&len);
res = ::RegQueryValueExW(theKey, L"", nullptr, nullptr,
(LPBYTE)currValue, &len);
// Close the key that was opened.
::RegCloseKey(theKey);
if (REG_FAILED(res) || PRUnichar('\0') != *currValue) {
@ -525,9 +526,9 @@ nsWindowsShellService::IsDefaultBrowser(bool aStartupCheck,
// Delete the key along with all of its childrean and then recreate it.
const nsString &flatName = PromiseFlatString(keyName);
::SHDeleteKeyW(HKEY_CURRENT_USER, flatName.get());
res = ::RegCreateKeyExW(HKEY_CURRENT_USER, flatName.get(), 0, NULL,
REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL,
&theKey, NULL);
res = ::RegCreateKeyExW(HKEY_CURRENT_USER, flatName.get(), 0, nullptr,
REG_OPTION_NON_VOLATILE, KEY_SET_VALUE,
nullptr, &theKey, nullptr);
if (REG_FAILED(res)) {
// If disabling DDE fails try to disable it using the helper
// application when setting Firefox as the default browser.
@ -564,7 +565,7 @@ nsWindowsShellService::IsDefaultBrowser(bool aStartupCheck,
::ZeroMemory(currValue, sizeof(currValue));
DWORD len = sizeof currValue;
res = ::RegQueryValueExW(theKey, L"", NULL, NULL, (LPBYTE)currValue,
res = ::RegQueryValueExW(theKey, L"", nullptr, nullptr, (LPBYTE)currValue,
&len);
// Don't update the FTP protocol handler's shell open command when the
@ -606,7 +607,7 @@ DynSHOpenWithDialog(HWND hwndParent, const OPENASINFO *poainfo)
{
typedef HRESULT (WINAPI * SHOpenWithDialogPtr)(HWND hwndParent,
const OPENASINFO *poainfo);
static SHOpenWithDialogPtr SHOpenWithDialogFn = NULL;
static SHOpenWithDialogPtr SHOpenWithDialogFn = nullptr;
if (!SHOpenWithDialogFn) {
// shell32.dll is in the knownDLLs list so will always be loaded from the
// system32 directory.
@ -650,8 +651,8 @@ nsWindowsShellService::LaunchControlPanelDefaultPrograms()
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOWDEFAULT;
PROCESS_INFORMATION pi = {0};
if (!CreateProcessW(controlEXEPath, params, NULL, NULL, FALSE, 0, NULL,
NULL, &si, &pi)) {
if (!CreateProcessW(controlEXEPath, params, nullptr, nullptr, FALSE,
0, nullptr, nullptr, &si, &pi)) {
return NS_ERROR_FAILURE;
}
CloseHandle(pi.hProcess);
@ -665,11 +666,11 @@ nsWindowsShellService::LaunchHTTPHandlerPane()
{
OPENASINFO info;
info.pcszFile = L"http";
info.pcszClass = NULL;
info.pcszClass = nullptr;
info.oaifInFlags = OAIF_FORCE_REGISTRATION |
OAIF_URL_PROTOCOL |
OAIF_REGISTER_EXT;
return DynSHOpenWithDialog(NULL, &info);
return DynSHOpenWithDialog(nullptr, &info);
}
NS_IMETHODIMP
@ -1028,8 +1029,8 @@ nsWindowsShellService::OpenApplication(int32_t aApplication)
::ZeroMemory(&si, sizeof(STARTUPINFOW));
::ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
BOOL success = ::CreateProcessW(NULL, (LPWSTR)path.get(), NULL,
NULL, FALSE, 0, NULL, NULL,
BOOL success = ::CreateProcessW(nullptr, (LPWSTR)path.get(), nullptr,
nullptr, FALSE, 0, nullptr, nullptr,
&si, &pi);
if (!success)
return NS_ERROR_FAILURE;

View File

@ -46,11 +46,13 @@ var APZCObserver = {
case 'TabOpen': {
let browser = aEvent.originalTarget.linkedBrowser;
browser.addEventListener("pageshow", this, true);
browser.messageManager.addMessageListener("scroll", this);
break;
}
case 'TabClose': {
let browser = aEvent.originalTarget.linkedBrowser;
browser.removeEventListener("pageshow", this);
browser.removeEventListener("pageshow", this, true);
browser.messageManager.removeMessageListener("scroll", this);
break;
}
}
@ -113,5 +115,16 @@ var APZCObserver = {
} else if (aTopic == "apzc-handle-pan-end") {
Util.dumpLn("APZC pan-end");
}
},
receiveMessage: function(aMessage) {
let json = aMessage.json;
switch (aMessage.name) {
case "scroll": {
let data = json.viewId + " " + json.presShellId + " (" + json.scrollOffset.x + ", " + json.scrollOffset.y + ")";
Services.obs.notifyObservers(null, "scroll-offset-changed", data);
break;
}
}
}
};

View File

@ -652,11 +652,7 @@ let ContentScroll = {
break;
case "scroll": {
let doc = aEvent.target;
if (doc != content.document)
break;
this.sendScroll();
this.sendScroll(aEvent.target);
break;
}
@ -683,13 +679,35 @@ let ContentScroll = {
}
},
sendScroll: function sendScroll() {
let scrollOffset = this.getScrollOffset(content);
if (this._scrollOffset.x == scrollOffset.x && this._scrollOffset.y == scrollOffset.y)
return;
sendScroll: function sendScroll(target) {
let isRoot = false;
if (target instanceof Ci.nsIDOMDocument) {
var window = target.defaultView;
var scrollOffset = this.getScrollOffset(window);
var element = target.documentElement;
this._scrollOffset = scrollOffset;
sendAsyncMessage("scroll", scrollOffset);
if (target == content.document) {
if (this._scrollOffset.x == scrollOffset.x && this._scrollOffset.y == scrollOffset.y) {
return;
}
this._scrollOffset = scrollOffset;
isRoot = true;
}
} else {
var window = target.currentDoc.defaultView;
var scrollOffset = this.getScrollOffsetForElement(target);
var element = target;
}
let utils = window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
let presShellId = {};
utils.getPresShellId(presShellId);
let viewId = utils.getViewId(element);
sendAsyncMessage("scroll", { presShellId: presShellId.value,
viewId: viewId,
scrollOffset: scrollOffset,
isRoot: isRoot });
}
};

View File

@ -859,9 +859,11 @@
switch (aMessage.name) {
case "scroll":
if (!json.isRoot)
return;
if (!self.scrollSync)
return;
this.doScroll(json.x, json.y, 0);
this.doScroll(json.scrollOffset.x, json.scrollOffset.y, 0);
break;
}
},

View File

@ -23,7 +23,7 @@ Log(const wchar_t *fmt, ...)
#if !defined(SHOW_CONSOLE)
return;
#endif
va_list a = NULL;
va_list a = nullptr;
wchar_t szDebugString[1024];
if(!lstrlenW(fmt))
return;
@ -34,8 +34,8 @@ Log(const wchar_t *fmt, ...)
return;
DWORD len;
WriteConsoleW(sCon, szDebugString, lstrlenW(szDebugString), &len, NULL);
WriteConsoleW(sCon, L"\n", 1, &len, NULL);
WriteConsoleW(sCon, szDebugString, lstrlenW(szDebugString), &len, nullptr);
WriteConsoleW(sCon, L"\n", 1, &len, nullptr);
if (IsDebuggerPresent()) {
OutputDebugStringW(szDebugString);
@ -53,7 +53,7 @@ SetupConsole()
int fd = _open_osfhandle(reinterpret_cast<intptr_t>(sCon), 0);
fp = _fdopen(fd, "w");
*stdout = *fp;
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stdout, nullptr, _IONBF, 0);
}
#endif
@ -104,19 +104,19 @@ IsDX10Available()
CComPtr<ID3D10Device1> device;
// Try for DX10.1
if (FAILED(createD3DDevice(adapter1, D3D10_DRIVER_TYPE_HARDWARE, NULL,
if (FAILED(createD3DDevice(adapter1, D3D10_DRIVER_TYPE_HARDWARE, nullptr,
D3D10_CREATE_DEVICE_BGRA_SUPPORT |
D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
D3D10_FEATURE_LEVEL_10_1,
D3D10_1_SDK_VERSION, &device))) {
// Try for DX10
if (FAILED(createD3DDevice(adapter1, D3D10_DRIVER_TYPE_HARDWARE, NULL,
if (FAILED(createD3DDevice(adapter1, D3D10_DRIVER_TYPE_HARDWARE, nullptr,
D3D10_CREATE_DEVICE_BGRA_SUPPORT |
D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
D3D10_FEATURE_LEVEL_10_0,
D3D10_1_SDK_VERSION, &device))) {
// Try for DX9.3 (we fall back to cairo and cairo has support for D3D 9.3)
if (FAILED(createD3DDevice(adapter1, D3D10_DRIVER_TYPE_HARDWARE, NULL,
if (FAILED(createD3DDevice(adapter1, D3D10_DRIVER_TYPE_HARDWARE, nullptr,
D3D10_CREATE_DEVICE_BGRA_SUPPORT |
D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
D3D10_FEATURE_LEVEL_9_3,

View File

@ -49,7 +49,7 @@ static bool GetModulePath(CStringW& aPathBuffer)
WCHAR buffer[MAX_PATH];
memset(buffer, 0, sizeof(buffer));
if (!GetModuleFileName(NULL, buffer, MAX_PATH)) {
if (!GetModuleFileName(nullptr, buffer, MAX_PATH)) {
Log(L"GetModuleFileName failed.");
return false;
}
@ -68,7 +68,7 @@ template <class T>void SafeRelease(T **ppT)
{
if (*ppT) {
(*ppT)->Release();
*ppT = NULL;
*ppT = nullptr;
}
}
@ -89,8 +89,8 @@ public:
CExecuteCommandVerb() :
mRef(1),
mShellItemArray(NULL),
mUnkSite(NULL),
mShellItemArray(nullptr),
mUnkSite(nullptr),
mTargetIsFileSystemLink(false),
mTargetIsDefaultBrowser(false),
mTargetIsBrowser(false),
@ -176,9 +176,9 @@ public:
#ifdef SHOW_CONSOLE
Log(L"SetSelection param count: %d", count);
for (DWORD idx = 0; idx < count; idx++) {
IShellItem* item = NULL;
IShellItem* item = nullptr;
if (SUCCEEDED(aArray->GetItemAt(idx, &item))) {
LPWSTR str = NULL;
LPWSTR str = nullptr;
if (FAILED(item->GetDisplayName(SIGDN_FILESYSPATH, &str))) {
if (FAILED(item->GetDisplayName(SIGDN_URL, &str))) {
Log(L"Failed to get a shell item array item.");
@ -193,7 +193,7 @@ public:
}
#endif
IShellItem* item = NULL;
IShellItem* item = nullptr;
if (FAILED(aArray->GetItemAt(0, &item))) {
return E_FAIL;
}
@ -211,7 +211,7 @@ public:
IFACEMETHODIMP GetSelection(REFIID aRefID, void **aInt)
{
*aInt = NULL;
*aInt = nullptr;
return mShellItemArray ? mShellItemArray->QueryInterface(aRefID, aInt) : E_FAIL;
}
@ -235,7 +235,7 @@ public:
IFACEMETHODIMP GetSite(REFIID aRefID, void **aInt)
{
*aInt = NULL;
*aInt = nullptr;
return mUnkSite ? mUnkSite->QueryInterface(aRefID, aInt) : E_FAIL;
}
@ -252,14 +252,14 @@ public:
}
HRESULT hr;
IServiceProvider* pSvcProvider = NULL;
IServiceProvider* pSvcProvider = nullptr;
hr = mUnkSite->QueryInterface(IID_IServiceProvider, (void**)&pSvcProvider);
if (!pSvcProvider) {
Log(L"Couldn't get IServiceProvider service from explorer. (%X)", hr);
return S_OK;
}
IExecuteCommandHost* pHost = NULL;
IExecuteCommandHost* pHost = nullptr;
// If we can't get this it's a conventional desktop launch
hr = pSvcProvider->QueryService(SID_ExecuteCommandHost,
IID_IExecuteCommandHost, (void**)&pHost);
@ -340,7 +340,7 @@ public:
{
IApplicationAssociationRegistration* pAAR;
HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration,
NULL,
nullptr,
CLSCTX_INPROC,
IID_IApplicationAssociationRegistration,
(void**)&pAAR);
@ -416,7 +416,7 @@ static bool GetDefaultBrowserPath(CStringW& aPathBuffer)
if (FAILED(AssocQueryStringW(ASSOCF_NOTRUNCATE | ASSOCF_INIT_IGNOREUNKNOWN,
ASSOCSTR_EXECUTABLE,
kDefaultMetroBrowserIDPathKey, NULL,
kDefaultMetroBrowserIDPathKey, nullptr,
buffer, &length))) {
Log(L"AssocQueryString failed.");
return false;
@ -451,7 +451,7 @@ static bool GetDefaultBrowserAppModelID(WCHAR* aIDBuffer,
}
DWORD len = aCharLength * sizeof(WCHAR);
memset(aIDBuffer, 0, len);
if (RegQueryValueExW(key, L"AppUserModelID", NULL, NULL,
if (RegQueryValueExW(key, L"AppUserModelID", nullptr, nullptr,
(LPBYTE)aIDBuffer, &len) != ERROR_SUCCESS || !len) {
RegCloseKey(key);
return false;
@ -513,7 +513,7 @@ bool CExecuteCommandVerb::SetTargetPath(IShellItem* aItem)
CComPtr<IDataObject> object;
// Check the underlying data object first to insure we get
// absolute uri. See chromium bug 157184.
if (SUCCEEDED(aItem->BindToHandler(NULL, BHID_DataObject,
if (SUCCEEDED(aItem->BindToHandler(nullptr, BHID_DataObject,
IID_IDataObject,
reinterpret_cast<void**>(&object))) &&
GetPlainText(object, cstrText)) {
@ -537,7 +537,7 @@ bool CExecuteCommandVerb::SetTargetPath(IShellItem* aItem)
Log(L"No data object or data object has no text.");
// Use the shell item display name
LPWSTR str = NULL;
LPWSTR str = nullptr;
mTargetIsFileSystemLink = true;
if (FAILED(aItem->GetDisplayName(SIGDN_FILESYSPATH, &str))) {
mTargetIsFileSystemLink = false;
@ -596,12 +596,12 @@ void CExecuteCommandVerb::LaunchDesktopBrowser()
SHELLEXECUTEINFOW seinfo;
memset(&seinfo, 0, sizeof(seinfo));
seinfo.cbSize = sizeof(SHELLEXECUTEINFOW);
seinfo.fMask = NULL;
seinfo.hwnd = NULL;
seinfo.lpVerb = NULL;
seinfo.fMask = 0;
seinfo.hwnd = nullptr;
seinfo.lpVerb = nullptr;
seinfo.lpFile = browserPath;
seinfo.lpParameters = params;
seinfo.lpDirectory = NULL;
seinfo.lpDirectory = nullptr;
seinfo.nShow = SW_SHOWNORMAL;
ShellExecuteExW(&seinfo);
@ -635,9 +635,9 @@ IFACEMETHODIMP CExecuteCommandVerb::Execute()
}
// Launch into Metro
IApplicationActivationManager* activateMgr = NULL;
IApplicationActivationManager* activateMgr = nullptr;
DWORD processID;
if (FAILED(CoCreateInstance(CLSID_ApplicationActivationManager, NULL,
if (FAILED(CoCreateInstance(CLSID_ApplicationActivationManager, nullptr,
CLSCTX_LOCAL_SERVER,
IID_IApplicationActivationManager,
(void**)&activateMgr))) {
@ -657,7 +657,7 @@ IFACEMETHODIMP CExecuteCommandVerb::Execute()
// Hand off focus rights to the out-of-process activation server. Without
// this the metro interface won't launch.
hr = CoAllowSetForegroundWindow(activateMgr, NULL);
hr = CoAllowSetForegroundWindow(activateMgr, nullptr);
if (FAILED(hr)) {
Log(L"CoAllowSetForegroundWindow result %X", hr);
activateMgr->Release();
@ -727,7 +727,7 @@ ClassFactory::Register(CLSCTX aClass, REGCLS aUse)
STDMETHODIMP
ClassFactory::QueryInterface(REFIID riid, void **ppv)
{
IUnknown *punk = NULL;
IUnknown *punk = nullptr;
if (riid == IID_IUnknown || riid == IID_IClassFactory) {
punk = static_cast<IClassFactory*>(this);
}
@ -743,7 +743,7 @@ ClassFactory::QueryInterface(REFIID riid, void **ppv)
STDMETHODIMP
ClassFactory::CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
{
*ppv = NULL;
*ppv = nullptr;
if (punkOuter)
return CLASS_E_NOAGGREGATION;
return mUnkObject->QueryInterface(riid, ppv);
@ -771,7 +771,7 @@ int APIENTRY wWinMain(HINSTANCE, HINSTANCE, PWSTR pszCmdLine, int)
if (!wcslen(pszCmdLine) || StrStrI(pszCmdLine, L"-Embedding"))
{
CoInitialize(NULL);
CoInitialize(nullptr);
CExecuteCommandVerb *pHandler = new CExecuteCommandVerb();
if (!pHandler)
@ -784,13 +784,13 @@ int APIENTRY wWinMain(HINSTANCE, HINSTANCE, PWSTR pszCmdLine, int)
ClassFactory classFactory(ppi);
ppi->Release();
ppi = NULL;
ppi = nullptr;
// REGCLS_SINGLEUSE insures we only get used once and then discarded.
if (FAILED(classFactory.Register(CLSCTX_LOCAL_SERVER, REGCLS_SINGLEUSE)))
return -1;
if (!SetTimer(NULL, 1, HEARTBEAT_MSEC, NULL)) {
if (!SetTimer(nullptr, 1, HEARTBEAT_MSEC, nullptr)) {
Log(L"Failed to set timer, can't process request.");
return -1;
}

View File

@ -37,10 +37,13 @@ HRESULT
SetShortcutProps(LPCWSTR aShortcutPath, LPCWSTR aAppModelID, bool aSetID, bool aSetMode)
{
HRESULT hres;
::CoInitialize(NULL);
::CoInitialize(nullptr);
IPropertyStore *m_pps = NULL;
if (FAILED(hres = SHGetPropertyStoreFromParsingName(aShortcutPath, NULL, GPS_READWRITE, IID_PPV_ARGS(&m_pps)))) {
IPropertyStore *m_pps = nullptr;
if (FAILED(hres = SHGetPropertyStoreFromParsingName(aShortcutPath,
nullptr,
GPS_READWRITE,
IID_PPV_ARGS(&m_pps)))) {
printf("SHGetPropertyStoreFromParsingName failed\n");
goto Exit;
}
@ -79,10 +82,13 @@ HRESULT
PrintShortcutProps(LPCWSTR aTargetPath)
{
HRESULT hres;
::CoInitialize(NULL);
::CoInitialize(nullptr);
IPropertyStore *m_pps = NULL;
if (FAILED(hres = SHGetPropertyStoreFromParsingName(aTargetPath, NULL, GPS_READWRITE, IID_PPV_ARGS(&m_pps)))) {
IPropertyStore *m_pps = nullptr;
if (FAILED(hres = SHGetPropertyStoreFromParsingName(aTargetPath,
nullptr,
GPS_READWRITE,
IID_PPV_ARGS(&m_pps)))) {
printf("SHGetPropertyStoreFromParsingName failed\n");
goto Exit;
}
@ -125,9 +131,9 @@ CreateLink(LPCWSTR aTargetPath, LPCWSTR aShortcutPath, LPCWSTR aDescription)
wprintf(L"creating shortcut: '%s'\n", aShortcutPath);
CoInitialize(NULL);
CoInitialize(nullptr);
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
IID_IShellLink, (LPVOID*)&psl);
if (FAILED(hres)) {
CoUninitialize();
@ -140,7 +146,7 @@ CreateLink(LPCWSTR aTargetPath, LPCWSTR aShortcutPath, LPCWSTR aDescription)
psl->SetDescription(L"");
}
IPersistFile* ppf = NULL;
IPersistFile* ppf = nullptr;
hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf);
if (SUCCEEDED(hres)) {
@ -261,7 +267,9 @@ int wmain(int argc, WCHAR* argv[])
}
if (createShortcutFound) {
if (FAILED(hres = CreateLink(targetPathStr, shortcutPathStr, (descriptionFound ? descriptionStr : NULL)))) {
if (FAILED(hres = CreateLink(targetPathStr,
shortcutPathStr,
(descriptionFound ? descriptionStr : nullptr)))) {
printf("failed creating shortcut HRESULT=%X\n", hres);
return -1;
}
@ -275,7 +283,9 @@ int wmain(int argc, WCHAR* argv[])
}
if (appModelIDFound || modeFound) {
if (FAILED(hres = SetShortcutProps(target, (appModelIDFound ? appModelIDStr : NULL), appModelIDFound, modeFound))) {
if (FAILED(hres = SetShortcutProps(target,
(appModelIDFound ? appModelIDStr : nullptr),
appModelIDFound, modeFound))) {
printf("failed adding property HRESULT=%X\n", hres);
return -1;
}

View File

@ -50,7 +50,7 @@ CString sFirefoxPath;
static void Log(const wchar_t *fmt, ...)
{
va_list a = NULL;
va_list a = nullptr;
wchar_t szDebugString[1024];
if(!lstrlenW(fmt))
return;
@ -66,7 +66,7 @@ static void Log(const wchar_t *fmt, ...)
static void Fail(bool aRequestRetry, const wchar_t *fmt, ...)
{
va_list a = NULL;
va_list a = nullptr;
wchar_t szDebugString[1024];
if(!lstrlenW(fmt))
return;
@ -93,7 +93,7 @@ static bool GetModulePath(CStringW& aPathBuffer)
WCHAR buffer[MAX_PATH];
memset(buffer, 0, sizeof(buffer));
if (!GetModuleFileName(NULL, buffer, MAX_PATH)) {
if (!GetModuleFileName(nullptr, buffer, MAX_PATH)) {
Fail(false, L"GetModuleFileName failed.");
return false;
}
@ -145,7 +145,7 @@ static bool GetDefaultBrowserAppModelID(WCHAR* aIDBuffer,
}
DWORD len = aCharLength * sizeof(WCHAR);
memset(aIDBuffer, 0, len);
if (RegQueryValueExW(key, L"AppUserModelID", NULL, NULL,
if (RegQueryValueExW(key, L"AppUserModelID", nullptr, nullptr,
(LPBYTE)aIDBuffer, &len) != ERROR_SUCCESS || !len) {
RegCloseKey(key);
return false;
@ -174,7 +174,7 @@ static bool SetupTestOutputPipe()
SECURITY_ATTRIBUTES saAttr;
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
saAttr.lpSecurityDescriptor = nullptr;
gTestOutputPipe =
CreateNamedPipeW(L"\\\\.\\pipe\\metrotestharness",
@ -182,7 +182,7 @@ static bool SetupTestOutputPipe()
PIPE_TYPE_BYTE|PIPE_WAIT,
1,
PIPE_BUFFER_SIZE,
PIPE_BUFFER_SIZE, 0, NULL);
PIPE_BUFFER_SIZE, 0, nullptr);
if (gTestOutputPipe == INVALID_HANDLE_VALUE) {
Log(L"Failed to create named logging pipe.");
@ -194,7 +194,8 @@ static bool SetupTestOutputPipe()
static void ReadPipe()
{
DWORD numBytesRead;
while (ReadFile(gTestOutputPipe, buffer, PIPE_BUFFER_SIZE, &numBytesRead, NULL) &&
while (ReadFile(gTestOutputPipe, buffer, PIPE_BUFFER_SIZE,
&numBytesRead, nullptr) &&
numBytesRead) {
buffer[numBytesRead] = '\0';
printf("%s", buffer);
@ -209,7 +210,7 @@ static int Launch()
// The interface that allows us to activate the browser
CComPtr<IApplicationActivationManager> activateMgr;
if (FAILED(CoCreateInstance(CLSID_ApplicationActivationManager, NULL,
if (FAILED(CoCreateInstance(CLSID_ApplicationActivationManager, nullptr,
CLSCTX_LOCAL_SERVER,
IID_IApplicationActivationManager,
(void**)&activateMgr))) {
@ -229,7 +230,7 @@ static int Launch()
// Hand off focus rights if the terminal has focus to the out-of-process
// activation server (explorer.exe). Without this the metro interface
// won't launch.
hr = CoAllowSetForegroundWindow(activateMgr, NULL);
hr = CoAllowSetForegroundWindow(activateMgr, nullptr);
if (FAILED(hr)) {
// Log but don't fail. This has happened on vms with certain terminals run by
// QA during mozmill testing.
@ -264,7 +265,7 @@ static int Launch()
} else {
// Use the module path
char path[MAX_PATH];
if (!GetModuleFileNameA(NULL, path, MAX_PATH)) {
if (!GetModuleFileNameA(nullptr, path, MAX_PATH)) {
Fail(false, L"GetModuleFileNameA errorno=%d", GetLastError());
return FAILURE;
}
@ -289,9 +290,9 @@ static int Launch()
Log(L"Writing out tests.ini to: '%s'", CStringW(testFilePath));
HANDLE hTestFile = CreateFileA(testFilePath, GENERIC_WRITE,
0, NULL, CREATE_ALWAYS,
0, nullptr, CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
nullptr);
if (hTestFile == INVALID_HANDLE_VALUE) {
Fail(false, L"CreateFileA errorno=%d", GetLastError());
return FAILURE;
@ -306,7 +307,8 @@ static int Launch()
asciiParams += sAppParams;
asciiParams.Trim();
Log(L"Browser command line args: '%s'", CString(asciiParams));
if (!WriteFile(hTestFile, asciiParams, asciiParams.GetLength(), NULL, 0)) {
if (!WriteFile(hTestFile, asciiParams, asciiParams.GetLength(),
nullptr, 0)) {
CloseHandle(hTestFile);
Fail(false, L"WriteFile errorno=%d", GetLastError());
return FAILURE;
@ -347,7 +349,7 @@ static int Launch()
} else if (waitResult == WAIT_OBJECT_0 + 1) {
ReadPipe();
} else if (waitResult == WAIT_OBJECT_0 + 2 &&
PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
@ -363,7 +365,7 @@ static int Launch()
int wmain(int argc, WCHAR* argv[])
{
CoInitialize(NULL);
CoInitialize(nullptr);
int idx;
bool firefoxParam = false;

View File

@ -91,6 +91,11 @@ this.Social = {
providers: [],
_disabledForSafeMode: false,
get allowMultipleWorkers() {
return Services.prefs.prefHasUserValue("social.allowMultipleWorkers") &&
Services.prefs.getBoolPref("social.allowMultipleWorkers");
},
get _currentProviderPref() {
try {
return Services.prefs.getComplexValue("social.provider.current",
@ -114,15 +119,14 @@ this.Social = {
this._setProvider(val);
},
// Sets the current provider and enables it. Also disables the
// previously set provider, and notifies observers of the change.
// Sets the current provider and notifies observers of the change.
_setProvider: function (provider) {
if (this._provider == provider)
return;
// Disable the previous provider, if any, since we want only one provider to
// be enabled at once.
if (this._provider)
// Disable the previous provider, if we are not allowing multiple workers,
// since we want only one provider to be enabled at once.
if (this._provider && !Social.allowMultipleWorkers)
this._provider.enabled = false;
this._provider = provider;
@ -134,7 +138,6 @@ this.Social = {
let enabled = !!provider;
if (enabled != SocialService.enabled) {
SocialService.enabled = enabled;
Services.prefs.setBoolPref("social.enabled", enabled);
}
let origin = this._provider && this._provider.origin;
@ -159,31 +162,40 @@ this.Social = {
if (SocialService.enabled) {
// Retrieve the current set of providers, and set the current provider.
SocialService.getOrderedProviderList(function (providers) {
this._updateProviderCache(providers);
}.bind(this));
Social._updateProviderCache(providers);
Social._updateWorkerState(true);
});
}
// Register an observer for changes to the provider list
SocialService.registerProviderListener(function providerListener(topic, data) {
// An engine change caused by adding/removing a provider should notify
// An engine change caused by adding/removing a provider should notify.
// any providers we receive are enabled in the AddonsManager
if (topic == "provider-added" || topic == "provider-removed") {
this._updateProviderCache(data);
Social._updateProviderCache(data);
Social._updateWorkerState(true);
Services.obs.notifyObservers(null, "social:providers-changed", null);
return;
}
if (topic == "provider-update") {
// a provider has self-updated its manifest, we need to update our
// cache and possibly reload if it was the current provider.
// a provider has self-updated its manifest, we need to update our cache
// and reload the provider.
let provider = data;
// if we need a reload, do it now
if (provider.enabled) {
Social.enabled = false;
Services.tm.mainThread.dispatch(function() {
Social.enabled = true;
}, Components.interfaces.nsIThread.DISPATCH_NORMAL);
}
SocialService.getOrderedProviderList(function(providers) {
Social._updateProviderCache(providers);
provider.reload();
Services.obs.notifyObservers(null, "social:providers-changed", null);
});
}
}.bind(this));
});
},
_updateWorkerState: function(enable) {
// ensure that our providers are all disabled, and enabled if we allow
// multiple workers
if (enable && !Social.allowMultipleWorkers)
return;
[p.enabled = enable for (p of Social.providers) if (p.enabled != enable)];
},
// Called to update our cache of providers and set the current provider
@ -203,6 +215,9 @@ this.Social = {
set enabled(val) {
// Setting .enabled is just a shortcut for setting the provider to either
// the default provider or null...
this._updateWorkerState(val);
if (val) {
if (!this.provider)
this.provider = this.defaultProvider;
@ -210,6 +225,7 @@ this.Social = {
this.provider = null;
}
},
get enabled() {
return this.provider != null;
},
@ -229,10 +245,6 @@ this.Social = {
Services.prefs.setBoolPref("social.toast-notifications.enabled", !prefValue);
},
haveLoggedInUser: function () {
return !!(this.provider && this.provider.profile && this.provider.profile.userName);
},
setProviderByOrigin: function (origin) {
this.provider = this._getProviderFromOrigin(origin);
},

View File

@ -2076,7 +2076,10 @@ ia64*-hpux*)
XPCOM_FROZEN_LDOPTS='$(LIBXUL_DIST)/lib/xul.lib $(LIBXUL_DIST)/lib/mozalloc.lib'
LIBXUL_LIBS='$(LIBXUL_DIST)/lib/xul.lib $(LIBXUL_DIST)/lib/mozalloc.lib'
MOZ_COMPONENT_NSPR_LIBS='$(NSPR_LIBS)'
LDFLAGS="$LDFLAGS -LARGEADDRESSAWARE -NXCOMPAT -RELEASE"
LDFLAGS="$LDFLAGS -LARGEADDRESSAWARE -NXCOMPAT"
if test -z "$DEVELOPER_OPTIONS"; then
LDFLAGS="$LDFLAGS -RELEASE"
fi
dnl For profile-guided optimization
PROFILE_GEN_CFLAGS="-GL"
PROFILE_GEN_LDFLAGS="-LTCG:PGINSTRUMENT"

View File

@ -1808,7 +1808,7 @@ nsINode::ReplaceOrInsertBefore(bool aReplace, nsINode* aNewChild,
}
// Verify that newContent has no parent.
if (newContent->GetParent()) {
if (newContent->GetParentNode()) {
aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
return nullptr;
}
@ -1885,7 +1885,7 @@ nsINode::ReplaceOrInsertBefore(bool aReplace, nsINode* aNewChild,
// Verify that all the things in fragChildren have no parent.
for (uint32_t i = 0; i < count; ++i) {
if (fragChildren.ref().ElementAt(i)->GetParent()) {
if (fragChildren.ref().ElementAt(i)->GetParentNode()) {
aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
return nullptr;
}

View File

@ -2643,12 +2643,6 @@ nsEventStateManager::ComputeScrollTarget(nsIFrame* aTargetFrame,
nsIScrollableFrame* frameToScroll =
lastScrollFrame->GetScrollTargetFrame();
if (frameToScroll) {
nsIFrame* activeRootFrame = nsLayoutUtils::GetActiveScrolledRootFor(
lastScrollFrame, nullptr);
if (!nsLayoutUtils::GetCrossDocParentFrame(activeRootFrame)) {
// Record the fact that the scroll occurred on the top-level page.
aEvent->viewPortIsScrollTargetParent = true;
}
return frameToScroll;
}
}
@ -2714,14 +2708,7 @@ nsEventStateManager::ComputeScrollTarget(nsIFrame* aTargetFrame,
aTargetFrame->PresContext()->FrameManager()->GetRootFrame());
aOptions =
static_cast<ComputeScrollTargetOptions>(aOptions & ~START_FROM_PARENT);
if (newFrame) {
return ComputeScrollTarget(newFrame, aEvent, aOptions);
}
// Record the fact that the scroll occurred past the bounds of the top-level
// page.
aEvent->viewPortIsScrollTargetParent = true;
return nullptr;
return newFrame ? ComputeScrollTarget(newFrame, aEvent, aOptions) : nullptr;
}
nsSize

View File

@ -13,6 +13,7 @@
#include "nsMappedAttributes.h"
#include "nsSize.h"
#include "nsIDocument.h"
#include "nsIDOMMutationEvent.h"
#include "nsIScriptContext.h"
#include "nsIURL.h"
#include "nsIIOService.h"
@ -251,6 +252,11 @@ HTMLImageElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
if (aAttribute == nsGkAtoms::usemap ||
aAttribute == nsGkAtoms::ismap) {
NS_UpdateHint(retval, NS_STYLE_HINT_FRAMECHANGE);
} else if (aAttribute == nsGkAtoms::alt) {
if (aModType == nsIDOMMutationEvent::ADDITION ||
aModType == nsIDOMMutationEvent::REMOVAL) {
NS_UpdateHint(retval, NS_STYLE_HINT_FRAMECHANGE);
}
}
return retval;
}

View File

@ -125,6 +125,7 @@ MOCHITEST_FILES = \
test_bug448534.html \
test_bug463162.xhtml \
test_decoder_disable.html \
test_mediarecorder_record_no_timeslice.html \
test_mediarecorder_reload_crash.html \
test_media_selection.html \
test_playback.html \

View File

@ -0,0 +1,117 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test MediaRecorder Record No Timeslice</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script type="text/javascript" src="manifest.js"></script>
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
var manager = new MediaTestManager;
/**
* Starts a test on every media recorder file included to check that a
* stream derived from the file can be recorded with no time slice provided.
*/
function startTest(test, token) {
var element = document.createElement('audio');
var expectedMimeType = test.type.substring(0, test.type.indexOf(';'));
element.token = token;
manager.started(token);
element.src = test.name;
element.test = test;
element.stream = element.mozCaptureStream();
var mediaRecorder = new MediaRecorder(element.stream);
var onStopFired = false;
var onDataAvailableFired = false;
mediaRecorder.onerror = function () {
ok(false, 'Unexpected onerror callback fired');
};
mediaRecorder.onwarning = function () {
ok(false, 'Unexpected onwarning callback fired');
};
// This handler verifies that only a single onstop event handler is fired.
mediaRecorder.onstop = function () {
if (onStopFired) {
ok(false, 'onstop unexpectedly fired more than once');
} else {
onStopFired = true;
// ondataavailable should always fire before onstop
if (onDataAvailableFired) {
manager.finished(token);
} else {
ok(false, 'onstop fired without an ondataavailable event first');
}
}
};
// This handler verifies that only a single ondataavailable event handler
// is fired with the blob generated having greater than zero size
// and a correct mime type.
mediaRecorder.ondataavailable = function (evt) {
if (onDataAvailableFired) {
ok(false, 'ondataavailable unexpectedly fired more than once');
} else {
onDataAvailableFired = true;
ok(evt instanceof BlobEvent,
'Events fired from ondataavailable should be BlobEvent');
is(evt.type, 'dataavailable',
'Event type should dataavailable');
ok(evt.data.size > 0,
'Blob data received should be greater than zero');
is(evt.data.type, expectedMimeType,
'Blob data received should have type = ' + expectedMimeType);
is(mediaRecorder.mimeType, expectedMimeType,
'Mime type in ondataavailable = ' + expectedMimeType);
// onstop should not have fired before ondataavailable
if (onStopFired) {
ok(false, 'ondataavailable unexpectedly fired later than onstop');
manager.finished(token);
}
}
};
element.oncanplaythrough = function () {
// If content has ended, skip the test
if (element.ended) {
ok(true, 'ended fired before canplaythrough, skipping test');
manager.finished(token);
} else {
// If content hasn't ended, start recording
mediaRecorder.start();
is(mediaRecorder.state, 'recording',
'Media recorder should be recording');
is(mediaRecorder.stream, element.stream,
'Media recorder stream = element stream at the start of recording');
// When ended fires, stop recording
element.onended = function () {
mediaRecorder.stop();
is(mediaRecorder.state, 'inactive',
'Media recorder is inactive afer being stopped');
is(mediaRecorder.stream, element.stream,
'Media recorder stream = element stream post recording');
}
}
}
element.play();
}
manager.runTests(gMediaRecorderTests, startTest);
</script>
</pre>
</body>
</html>

View File

@ -1694,6 +1694,16 @@ nsDOMWindowUtils::FindElementWithViewId(nsViewID aID,
return content ? CallQueryInterface(content, aResult) : NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::GetViewId(nsIDOMElement* aElement, nsViewID* aResult)
{
nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
if (content && nsLayoutUtils::FindIDFor(content, aResult)) {
return NS_OK;
}
return NS_ERROR_NOT_AVAILABLE;
}
NS_IMETHODIMP
nsDOMWindowUtils::GetScreenPixelsPerCSSPixel(float* aScreenPixels)
{

View File

@ -108,10 +108,6 @@ EXPORTS_GENERATED_DEST := $(DIST)/include/$(binding_include_path)
EXPORTS_GENERATED_TARGET := export
INSTALL_TARGETS += EXPORTS_GENERATED
ifdef GNU_CC
CXXFLAGS += -Wno-uninitialized
endif
# Install auto-generated GlobalGen files. The rules for the install must
# be in the same target/subtier as GlobalGen.py, otherwise the files will not
# get installed into the appropriate location as they are generated.
@ -128,6 +124,10 @@ INSTALL_TARGETS += globalgen_headers
include $(topsrcdir)/config/rules.mk
include $(topsrcdir)/ipc/chromium/chromium-config.mk
ifdef GNU_CC
CXXFLAGS += -Wno-uninitialized
endif
# If you change bindinggen_dependencies here, change it in
# dom/bindings/test/Makefile.in too.
bindinggen_dependencies := \

View File

@ -42,7 +42,7 @@ interface nsIURI;
interface nsIDOMEventTarget;
interface nsIRunnable;
[scriptable, uuid(ff1cec22-b183-40d3-8b42-b81a2f0ba4e6)]
[scriptable, uuid(d6e733ef-492b-4e67-b723-28571c2959f0)]
interface nsIDOMWindowUtils : nsISupports {
/**
@ -1177,6 +1177,12 @@ interface nsIDOMWindowUtils : nsISupports {
*/
nsIDOMElement findElementWithViewId(in nsViewID aId);
/**
* Find the view ID for a given element. This is the reverse of
* findElementWithViewId().
*/
nsViewID getViewId(in nsIDOMElement aElement);
/**
* Checks the layer tree for this window and returns true
* if all layers have transforms that are translations by integers,

View File

@ -129,6 +129,10 @@ expandAction = expanded
activateAction = activated
cycleAction = cycled
# Live regions
# 'hidden' will be spoken when something disappears in a live region.
hidden = hidden
# Tab states
tabLoading = loading
tabLoaded = loaded

View File

@ -1147,30 +1147,6 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri
bool isDefault = mFrameMetrics.IsDefault();
mFrameMetrics.mMayHaveTouchListeners = aLayerMetrics.mMayHaveTouchListeners;
// TODO: Once a mechanism for calling UpdateScrollOffset() when content does
// a scrollTo() is implemented for metro (bug 898580), this block can be removed.
#ifdef MOZ_METRO
if (!mPaintThrottler.IsOutstanding()) {
// No paint was requested, but we got one anyways. One possible cause of this
// is that content could have fired a scrollTo(). In this case, we should take
// the new scroll offset. Document/viewport changes are handled elsewhere.
// Also note that, since NotifyLayersUpdated() is called whenever there's a
// layers update, we didn't necessarily get a new scroll offset, but we're
// updating our local copy of it anyways just in case.
switch (mState) {
case NOTHING:
case FLING:
case TOUCHING:
case WAITING_LISTENERS:
mFrameMetrics.mScrollOffset = aLayerMetrics.mScrollOffset;
break;
// Don't clobber if we're in other states.
default:
break;
}
}
#endif
mPaintThrottler.TaskComplete(GetFrameTime());
bool needContentRepaint = false;
if (aLayerMetrics.mCompositionBounds.width == mFrameMetrics.mCompositionBounds.width &&

View File

@ -78,7 +78,7 @@ nsUTF16ToUnicodeBase::UTF16ConvertToUnicode(const char * aSrc,
// previous run while the 2nd byte has to come from |*src|.
mState = STATE_NORMAL;
#ifdef IS_BIG_ENDIAN
u = (mOddByte << 8) | *src++; // safe, we know we have at least one byte.
u = (mOddByte << 8) | uint8_t(*src++); // safe, we know we have at least one byte.
#else
u = (*src++ << 8) | mOddByte; // safe, we know we have at least one byte.
#endif

View File

@ -1629,7 +1629,10 @@ ia64*-hpux*)
XPCOM_FROZEN_LDOPTS='$(LIBXUL_DIST)/lib/xpcom.lib $(LIBXUL_DIST)/lib/mozalloc.lib'
LIBXUL_LIBS='$(LIBXUL_DIST)/lib/xpcom.lib $(LIBXUL_DIST)/lib/xul.lib $(LIBXUL_DIST)/lib/mozalloc.lib'
MOZ_COMPONENT_NSPR_LIBS='$(NSPR_LIBS)'
LDFLAGS="$LDFLAGS -LARGEADDRESSAWARE -NXCOMPAT -RELEASE"
LDFLAGS="$LDFLAGS -LARGEADDRESSAWARE -NXCOMPAT"
if test -z "$DEVELOPER_OPTIONS"; then
LDFLAGS="$LDFLAGS -RELEASE"
fi
dnl For profile-guided optimization
PROFILE_GEN_CFLAGS="-GL"
PROFILE_GEN_LDFLAGS="-LTCG:PGINSTRUMENT"

View File

@ -69,7 +69,7 @@ CheckArgumentsWithinEval(JSContext *cx, Parser<FullParseHandler> &parser, Handle
}
// It's an error to use |arguments| in a legacy generator expression.
if (script->isGeneratorExp && script->isLegacyGenerator) {
if (script->isGeneratorExp && script->isLegacyGenerator()) {
parser.report(ParseError, false, NULL, JSMSG_BAD_GENEXP_BODY, js_arguments_str);
return false;
}
@ -294,7 +294,8 @@ frontend::CompileScript(ExclusiveContext *cx, LifoAlloc *alloc, HandleObject sco
*/
JSFunction *fun = evalCaller->functionOrCallerFunction();
Directives directives(/* strict = */ fun->strict());
ObjectBox *funbox = parser.newFunctionBox(/* fn = */ NULL, fun, pc.addr(), directives);
ObjectBox *funbox = parser.newFunctionBox(/* fn = */ NULL, fun, pc.addr(),
directives, fun->generatorKind());
if (!funbox)
return NULL;
bce.objectList.add(funbox);
@ -414,7 +415,9 @@ frontend::CompileLazyFunction(JSContext *cx, LazyScript *lazy, const jschar *cha
uint32_t staticLevel = lazy->staticLevel(cx);
Rooted<JSFunction*> fun(cx, lazy->function());
ParseNode *pn = parser.standaloneLazyFunction(fun, staticLevel, lazy->strict());
JS_ASSERT(!lazy->isLegacyGenerator());
ParseNode *pn = parser.standaloneLazyFunction(fun, staticLevel, lazy->strict(),
lazy->generatorKind());
if (!pn)
return false;
@ -518,7 +521,7 @@ frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileO
ParseNode *fn;
while (true) {
Directives newDirectives = directives;
fn = parser.standaloneFunctionBody(fun, formals, directives, &newDirectives);
fn = parser.standaloneFunctionBody(fun, formals, NotGenerator, directives, &newDirectives);
if (fn)
break;

View File

@ -384,9 +384,9 @@ Parser<FullParseHandler>::cloneParseTree(ParseNode *opn)
if (pn->getKind() == PNK_MODULE) {
MOZ_ASSUME_UNREACHABLE("module nodes cannot be cloned");
}
NULLCHECK(pn->pn_funbox =
newFunctionBox(pn, opn->pn_funbox->function(), pc,
Directives(/* strict = */ opn->pn_funbox->strict)));
NULLCHECK(pn->pn_funbox = newFunctionBox(pn, opn->pn_funbox->function(), pc,
Directives(/* strict = */ opn->pn_funbox->strict),
opn->pn_funbox->generatorKind()));
NULLCHECK(pn->pn_body = cloneParseTree(opn->pn_body));
pn->pn_cookie = opn->pn_cookie;
pn->pn_dflags = opn->pn_dflags;

View File

@ -56,7 +56,6 @@ namespace frontend {
typedef Rooted<StaticBlockObject*> RootedStaticBlockObject;
typedef Handle<StaticBlockObject*> HandleStaticBlockObject;
typedef MutableHandle<PropertyName*> MutableHandlePropertyName;
/*
* Insist that the next token be of type tt, or report errno and return null.
@ -466,13 +465,14 @@ Parser<ParseHandler>::newObjectBox(JSObject *obj)
template <typename ParseHandler>
FunctionBox::FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunction *fun,
ParseContext<ParseHandler> *outerpc, Directives directives,
bool extraWarnings)
bool extraWarnings, GeneratorKind generatorKind)
: ObjectBox(fun, traceListHead),
SharedContext(cx, directives, extraWarnings),
bindings(),
bufStart(0),
bufEnd(0),
ndefaults(0),
generatorKindBits_(GeneratorKindAsBits(generatorKind)),
inWith(false), // initialized below
inGenexpLambda(false),
hasDestructuringArgs(false),
@ -533,7 +533,7 @@ FunctionBox::FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunct
template <typename ParseHandler>
FunctionBox *
Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction *fun, ParseContext<ParseHandler> *outerpc,
Directives inheritedDirectives)
Directives inheritedDirectives, GeneratorKind generatorKind)
{
JS_ASSERT(fun && !IsPoisonedPtr(fun));
@ -546,7 +546,8 @@ Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction *fun, ParseContext<Pars
*/
FunctionBox *funbox =
alloc.new_<FunctionBox>(context, traceListHead, fun, outerpc,
inheritedDirectives, options().extraWarningsOption);
inheritedDirectives, options().extraWarningsOption,
generatorKind);
if (!funbox) {
js_ReportOutOfMemory(context);
return NULL;
@ -863,6 +864,7 @@ Parser<ParseHandler>::checkStrictBinding(PropertyName *name, Node pn)
template <>
ParseNode *
Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun, const AutoNameVector &formals,
GeneratorKind generatorKind,
Directives inheritedDirectives,
Directives *newDirectives)
{
@ -877,7 +879,8 @@ Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun, const AutoN
argsbody->makeEmpty();
fn->pn_body = argsbody;
FunctionBox *funbox = newFunctionBox(fn, fun, /* outerpc = */ NULL, inheritedDirectives);
FunctionBox *funbox = newFunctionBox(fn, fun, /* outerpc = */ NULL, inheritedDirectives,
generatorKind);
if (!funbox)
return null();
handler.setFunctionBox(fn, funbox);
@ -1062,7 +1065,9 @@ Parser<ParseHandler>::functionBody(FunctionSyntaxKind kind, FunctionBodyType typ
JS_ASSERT(pc->sc->isFunctionBox());
JS_ASSERT(!pc->funHasReturnExpr && !pc->funHasReturnVoid);
#ifdef DEBUG
uint32_t startYieldOffset = pc->lastYieldOffset;
#endif
Node pn;
if (type == StatementListBody) {
@ -1082,8 +1087,14 @@ Parser<ParseHandler>::functionBody(FunctionSyntaxKind kind, FunctionBodyType typ
return null();
}
if (pc->lastYieldOffset != startYieldOffset) {
JS_ASSERT(pc->isLegacyGenerator());
switch (pc->generatorKind()) {
case NotGenerator:
JS_ASSERT(pc->lastYieldOffset == startYieldOffset);
break;
case LegacyGenerator:
// FIXME: Catch these errors eagerly, in yieldExpression().
JS_ASSERT(pc->lastYieldOffset != startYieldOffset);
if (kind == Arrow) {
reportWithOffset(ParseError, false, pc->lastYieldOffset,
JSMSG_YIELD_IN_ARROW, js_yield_str);
@ -1095,9 +1106,12 @@ Parser<ParseHandler>::functionBody(FunctionSyntaxKind kind, FunctionBodyType typ
JSMSG_BAD_ANON_GENERATOR_RETURN);
return null();
}
pc->sc->asFunctionBox()->setIsLegacyGenerator();
} else {
JS_ASSERT(!pc->isLegacyGenerator());
break;
case StarGenerator:
JS_ASSERT(kind != Arrow);
JS_ASSERT(type == StatementListBody);
break;
}
/* Check for falling off the end of a function that returns a value. */
@ -1627,6 +1641,11 @@ Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node *listp, No
}
#endif /* JS_HAS_DESTRUCTURING */
case TOK_YIELD:
if (!checkYieldNameValidity(JSMSG_MISSING_FORMAL))
return false;
goto TOK_NAME;
case TOK_TRIPLEDOT:
{
hasRest = true;
@ -1636,15 +1655,16 @@ Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node *listp, No
report(ParseError, false, null(), JSMSG_NO_REST_NAME);
return false;
}
/* Fall through */
goto TOK_NAME;
}
TOK_NAME:
case TOK_NAME:
{
if (parenFreeArrow)
funbox->setStart(tokenStream);
RootedPropertyName name(context, tokenStream.currentToken().name());
RootedPropertyName name(context, tokenStream.currentName());
bool disallowDuplicateArgs = funbox->hasDestructuringArgs || hasDefaults;
if (!defineArg(funcpn, name, disallowDuplicateArgs, &duplicatedArg))
return false;
@ -1695,6 +1715,20 @@ Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node *listp, No
return true;
}
template <typename ParseHandler>
bool
Parser<ParseHandler>::checkFunctionName(HandlePropertyName funName)
{
if (pc->isStarGenerator() && funName == context->names().yield) {
// The name of a named function expression is specified to be bound in
// the outer context as if via "let". In an ES6 generator, "yield" is
// not a valid name for a let-bound variable.
report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
return false;
}
return true;
}
template <>
bool
Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
@ -1707,6 +1741,9 @@ Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
/* Function statements add a binding to the enclosing scope. */
bool bodyLevel = pc->atBodyLevel();
if (!checkFunctionName(funName))
return false;
if (kind == Statement) {
/*
* Handle redeclaration and optimize cases where we can statically bind the
@ -1820,7 +1857,9 @@ Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
// so we can skip over them after accounting for their free variables.
if (LazyScript *lazyOuter = handler.lazyOuterFunction()) {
JSFunction *fun = handler.nextLazyInnerFunction();
FunctionBox *funbox = newFunctionBox(pn, fun, pc, Directives(/* strict = */ false));
JS_ASSERT(!fun->isLegacyGenerator());
FunctionBox *funbox = newFunctionBox(pn, fun, pc, Directives(/* strict = */ false),
fun->generatorKind());
if (!funbox)
return false;
@ -1896,6 +1935,9 @@ Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
/* Function statements add a binding to the enclosing scope. */
bool bodyLevel = pc->atBodyLevel();
if (!checkFunctionName(funName))
return false;
if (kind == Statement) {
/*
* Handle redeclaration and optimize cases where we can statically bind the
@ -1934,7 +1976,8 @@ Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream::Position &start,
FunctionType type, FunctionSyntaxKind kind)
FunctionType type, FunctionSyntaxKind kind,
GeneratorKind generatorKind)
{
JS_ASSERT_IF(kind == Statement, funName);
@ -1962,7 +2005,7 @@ Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream:
Directives newDirectives = directives;
while (true) {
if (functionArgsAndBody(pn, fun, type, kind, directives, &newDirectives))
if (functionArgsAndBody(pn, fun, type, kind, generatorKind, directives, &newDirectives))
break;
if (tokenStream.hadError() || directives == newDirectives)
return null();
@ -2066,6 +2109,7 @@ Parser<SyntaxParseHandler>::finishFunctionDefinition(Node pn, FunctionBox *funbo
if (pc->sc->strict)
lazy->setStrict();
lazy->setGeneratorKind(funbox->generatorKind());
if (funbox->usesArguments && funbox->usesApply)
lazy->setUsesArgumentsAndApply();
PropagateTransitiveParseFlags(funbox, lazy);
@ -2078,13 +2122,14 @@ template <>
bool
Parser<FullParseHandler>::functionArgsAndBody(ParseNode *pn, HandleFunction fun,
FunctionType type, FunctionSyntaxKind kind,
GeneratorKind generatorKind,
Directives inheritedDirectives,
Directives *newDirectives)
{
ParseContext<FullParseHandler> *outerpc = pc;
// Create box for fun->object early to protect against last-ditch GC.
FunctionBox *funbox = newFunctionBox(pn, fun, pc, inheritedDirectives);
FunctionBox *funbox = newFunctionBox(pn, fun, pc, inheritedDirectives, generatorKind);
if (!funbox)
return false;
@ -2160,13 +2205,14 @@ template <>
bool
Parser<SyntaxParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun,
FunctionType type, FunctionSyntaxKind kind,
GeneratorKind generatorKind,
Directives inheritedDirectives,
Directives *newDirectives)
{
ParseContext<SyntaxParseHandler> *outerpc = pc;
// Create box for fun->object early to protect against last-ditch GC.
FunctionBox *funbox = newFunctionBox(pn, fun, pc, inheritedDirectives);
FunctionBox *funbox = newFunctionBox(pn, fun, pc, inheritedDirectives, generatorKind);
if (!funbox)
return false;
@ -2192,14 +2238,15 @@ Parser<SyntaxParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun,
template <>
ParseNode *
Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, unsigned staticLevel,
bool strict)
bool strict, GeneratorKind generatorKind)
{
Node pn = handler.newFunctionDefinition();
if (!pn)
return null();
Directives directives(/* strict = */ strict);
FunctionBox *funbox = newFunctionBox(pn, fun, /* outerpc = */ NULL, directives);
FunctionBox *funbox = newFunctionBox(pn, fun, /* outerpc = */ NULL, directives,
generatorKind);
if (!funbox)
return null();
@ -2273,6 +2320,10 @@ Parser<ParseHandler>::functionArgsAndBodyGeneric(Node pn, HandleFunction fun, Fu
// Parse the function body.
FunctionBodyType bodyType = StatementListBody;
if (tokenStream.getToken(TokenStream::Operand) != TOK_LC) {
if (funbox->isStarGenerator()) {
report(ParseError, false, null(), JSMSG_CURLY_BEFORE_BODY);
return false;
}
tokenStream.ungetToken();
bodyType = ExpressionBody;
fun->setIsExprClosure();
@ -2310,7 +2361,7 @@ template <>
ParseNode *
Parser<FullParseHandler>::moduleDecl()
{
JS_ASSERT(tokenStream.currentToken().name() == context->names().module);
JS_ASSERT(tokenStream.currentName() == context->names().module);
if (!((pc->sc->isGlobalSharedContext() || pc->sc->isModuleBox()) && pc->atBodyLevel()))
{
report(ParseError, false, NULL, JSMSG_MODULE_STATEMENT);
@ -2352,18 +2403,44 @@ Parser<SyntaxParseHandler>::moduleDecl()
return SyntaxParseHandler::NodeFailure;
}
template <typename ParseHandler>
bool
Parser<ParseHandler>::checkYieldNameValidity(unsigned errorNumber)
{
// In star generators and in JS >= 1.7, yield is a keyword.
if (pc->isStarGenerator() || versionNumber() >= JSVERSION_1_7) {
report(ParseError, false, null(), errorNumber);
return false;
}
// Otherwise in strict mode, yield is a future reserved word.
if (pc->sc->strict) {
report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
return false;
}
return true;
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::functionStmt()
{
JS_ASSERT(tokenStream.currentToken().type == TOK_FUNCTION);
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
TokenStream::Position start(keepAtoms);
tokenStream.tell(&start);
RootedPropertyName name(context);
if (tokenStream.getToken(TokenStream::KeywordIsName) == TOK_NAME) {
name = tokenStream.currentToken().name();
GeneratorKind generatorKind = NotGenerator;
TokenKind tt = tokenStream.getToken(TokenStream::KeywordIsName);
if (tt == TOK_MUL) {
tokenStream.tell(&start);
tt = tokenStream.getToken(TokenStream::KeywordIsName);
generatorKind = StarGenerator;
}
if (tt == TOK_NAME) {
name = tokenStream.currentName();
} else {
/* Unnamed function expressions are forbidden in statement context. */
report(ParseError, false, null(), JSMSG_UNNAMED_FUNCTION_STMT);
@ -2375,22 +2452,34 @@ Parser<ParseHandler>::functionStmt()
!report(ParseStrictError, pc->sc->strict, null(), JSMSG_STRICT_FUNCTION_STATEMENT))
return null();
return functionDef(name, start, Normal, Statement);
return functionDef(name, start, Normal, Statement, generatorKind);
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::functionExpr()
{
RootedPropertyName name(context);
JS_ASSERT(tokenStream.currentToken().type == TOK_FUNCTION);
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
TokenStream::Position start(keepAtoms);
tokenStream.tell(&start);
if (tokenStream.getToken(TokenStream::KeywordIsName) == TOK_NAME)
name = tokenStream.currentToken().name();
RootedPropertyName name(context);
GeneratorKind generatorKind = NotGenerator;
TokenKind tt = tokenStream.getToken(TokenStream::KeywordIsName);
if (tt == TOK_MUL) {
tokenStream.tell(&start);
tt = tokenStream.getToken(TokenStream::KeywordIsName);
generatorKind = StarGenerator;
}
if (tt == TOK_NAME)
name = tokenStream.currentName();
else
tokenStream.ungetToken();
return functionDef(name, start, Normal, Expression);
return functionDef(name, start, Normal, Expression, generatorKind);
}
/*
@ -2524,7 +2613,7 @@ Parser<ParseHandler>::maybeParseDirective(Node list, Node pn, bool *cont)
}
}
} else if (directive == context->names().useAsm) {
if (pc->sc->isFunctionBox())
if (pc->sc->isFunctionBox() && !pc->isGenerator())
return asmJS(list);
return report(ParseWarning, false, pn, JSMSG_USE_ASM_DIRECTIVE_FAIL);
}
@ -2606,15 +2695,21 @@ Parser<ParseHandler>::condition()
return pn;
}
static bool
MatchLabel(TokenStream &ts, MutableHandlePropertyName label)
template <typename ParseHandler>
bool
Parser<ParseHandler>::matchLabel(MutableHandle<PropertyName*> label)
{
TokenKind tt = ts.peekTokenSameLine(TokenStream::Operand);
TokenKind tt = tokenStream.peekTokenSameLine(TokenStream::Operand);
if (tt == TOK_ERROR)
return false;
if (tt == TOK_NAME) {
(void) ts.getToken();
label.set(ts.currentToken().name());
tokenStream.consumeKnownToken(TOK_NAME);
label.set(tokenStream.currentName());
} else if (tt == TOK_YIELD) {
tokenStream.consumeKnownToken(TOK_YIELD);
if (!checkYieldNameValidity())
return false;
label.set(tokenStream.currentName());
} else {
label.set(NULL);
}
@ -3240,7 +3335,7 @@ template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::letBlock(LetContext letContext)
{
JS_ASSERT(tokenStream.currentToken().type == TOK_LET);
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LET));
RootedStaticBlockObject blockObj(context, StaticBlockObject::create(context));
if (!blockObj)
@ -3330,7 +3425,7 @@ template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::blockStatement()
{
JS_ASSERT(tokenStream.currentToken().type == TOK_LC);
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
StmtInfoPC stmtInfo(context);
if (!PushBlocklikeStatement(&stmtInfo, STMT_BLOCK, pc))
@ -3372,7 +3467,6 @@ Parser<ParseHandler>::newBindingNode(PropertyName *name, bool functionScope, Var
}
/* Make a new node for this declarator name (or destructuring pattern). */
JS_ASSERT(tokenStream.currentToken().type == TOK_NAME);
return newName(name);
}
@ -3461,12 +3555,17 @@ Parser<ParseHandler>::variables(ParseNodeKind kind, bool *psimple,
#endif /* JS_HAS_DESTRUCTURING */
if (tt != TOK_NAME) {
if (tt != TOK_ERROR)
report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME);
return null();
if (tt == TOK_YIELD) {
if (!checkYieldNameValidity(JSMSG_NO_VARIABLE_NAME))
return null();
} else {
if (tt != TOK_ERROR)
report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME);
return null();
}
}
RootedPropertyName name(context, tokenStream.currentToken().name());
RootedPropertyName name(context, tokenStream.currentName());
pn2 = newBindingNode(name, kind == PNK_VAR || kind == PNK_CONST, varContext);
if (!pn2)
return null();
@ -4123,9 +4222,14 @@ Parser<SyntaxParseHandler>::forStatement()
PushStatementPC(pc, &forStmt, STMT_FOR_LOOP);
/* Don't parse 'for each' loops. */
if (allowsForEachIn() && tokenStream.peekToken() == TOK_NAME) {
JS_ALWAYS_FALSE(abortIfSyntaxParser());
return null();
if (allowsForEachIn()) {
TokenKind tt = tokenStream.peekToken();
// Not all "yield" tokens are names, but the ones that aren't names are
// invalid in this context anyway.
if (tt == TOK_NAME || tt == TOK_YIELD) {
JS_ALWAYS_FALSE(abortIfSyntaxParser());
return null();
}
}
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
@ -4327,7 +4431,7 @@ Parser<ParseHandler>::continueStatement()
uint32_t begin = pos().begin;
RootedPropertyName label(context);
if (!MatchLabel(tokenStream, &label))
if (!matchLabel(&label))
return null();
StmtInfoPC *stmt = pc->topStmt;
@ -4374,7 +4478,7 @@ Parser<ParseHandler>::breakStatement()
uint32_t begin = pos().begin;
RootedPropertyName label(context);
if (!MatchLabel(tokenStream, &label))
if (!matchLabel(&label))
return null();
StmtInfoPC *stmt = pc->topStmt;
if (label) {
@ -4405,98 +4509,45 @@ Parser<ParseHandler>::breakStatement()
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::returnStatementOrYieldExpression()
Parser<ParseHandler>::returnStatement()
{
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_RETURN) ||
tokenStream.isCurrentTokenType(TOK_YIELD));
bool isYield = tokenStream.isCurrentTokenType(TOK_YIELD);
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_RETURN));
uint32_t begin = pos().begin;
if (!pc->sc->isFunctionBox()) {
report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD,
isYield ? js_yield_str : js_return_str);
report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
return null();
}
// Legacy generators are identified by the presence of "yield" in their
// bodies. We only see "yield" as TOK_YIELD in JS 1.7+.
if (isYield) {
JS_ASSERT(tokenStream.versionNumber() >= JSVERSION_1_7);
if (!abortIfSyntaxParser())
return null();
if (pc->isLegacyGenerator()) {
// We are in a legacy generator: a function that has already seen a
// yield.
JS_ASSERT(pc->sc->isFunctionBox());
JS_ASSERT(pc->lastYieldOffset != ParseContext<ParseHandler>::NoYieldOffset);
} else {
// We are in a code that has not seen a yield, and in JS 1.8 so
// "yield" parsed as TOK_YIELD. Try to transition to being a legacy
// generator.
JS_ASSERT(tokenStream.currentToken().type == TOK_YIELD);
JS_ASSERT(pc->lastYieldOffset == ParseContext<ParseHandler>::NoYieldOffset);
if (!pc->sc->isFunctionBox()) {
report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_yield_str);
return null();
}
pc->generatorParseMode = ParseContext<ParseHandler>::LegacyGenerator;
}
pc->lastYieldOffset = begin;
}
// Parse an optional operand.
//
// Checking whether yield has an operand is especially wonky since
// there is not a mandatory semicolon.
//
// ES6 does not permit yield without an operand. We will have to sunset
// this extension in order to conform to the ES6 syntax, which treats
// "yield \n expr;" as a single ExpressionStatement.
// This is ugly, but we don't want to require a semicolon.
Node exprNode;
TokenKind next = tokenStream.peekTokenSameLine(TokenStream::Operand);
if (next == TOK_ERROR)
switch (tokenStream.peekTokenSameLine(TokenStream::Operand)) {
case TOK_ERROR:
return null();
if (next == TOK_EOF || next == TOK_EOL || next == TOK_SEMI || next == TOK_RC ||
(isYield && (next == TOK_RB || next == TOK_RP || next == TOK_COLON || next == TOK_COMMA)))
{
if (isYield) {
if (!reportWithOffset(ParseWarning, false, pos().begin, JSMSG_YIELD_WITHOUT_OPERAND))
return null();
}
case TOK_EOF:
case TOK_EOL:
case TOK_SEMI:
case TOK_RC:
exprNode = null();
if (!isYield)
pc->funHasReturnVoid = true;
} else {
exprNode = isYield ? assignExpr() : expr();
pc->funHasReturnVoid = true;
break;
default: {
exprNode = expr();
if (!exprNode)
return null();
if (!isYield)
pc->funHasReturnExpr = true;
pc->funHasReturnExpr = true;
}
}
if (!isYield) {
if (!MatchOrInsertSemicolon(tokenStream))
return null();
}
if (!MatchOrInsertSemicolon(tokenStream))
return null();
Node pn = isYield
? handler.newUnary(PNK_YIELD, JSOP_YIELD, begin, exprNode)
: handler.newReturnStatement(exprNode, TokenPos(begin, pos().end));
Node pn = handler.newReturnStatement(exprNode, TokenPos(begin, pos().end));
if (!pn)
return null();
if (pc->funHasReturnExpr && pc->isLegacyGenerator()) {
/* As in Python (see PEP-255), disallow return v; in generators. */
reportBadReturn(pn, ParseError, JSMSG_BAD_GENERATOR_RETURN,
JSMSG_BAD_ANON_GENERATOR_RETURN);
return null();
}
if (options().extraWarningsOption && pc->funHasReturnExpr && pc->funHasReturnVoid &&
!reportBadReturn(pn, ParseExtraWarning,
JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE))
@ -4504,9 +4555,108 @@ Parser<ParseHandler>::returnStatementOrYieldExpression()
return null();
}
if (pc->isLegacyGenerator() && exprNode) {
/* Disallow "return v;" in legacy generators. */
reportBadReturn(pn, ParseError, JSMSG_BAD_GENERATOR_RETURN,
JSMSG_BAD_ANON_GENERATOR_RETURN);
return null();
}
return pn;
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::yieldExpression()
{
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_YIELD));
uint32_t begin = pos().begin;
switch (pc->generatorKind()) {
case StarGenerator:
{
JS_ASSERT(pc->sc->isFunctionBox());
pc->lastYieldOffset = begin;
bool isDelegatingYield = tokenStream.matchToken(TOK_MUL);
// ES6 generators require a value.
Node exprNode = assignExpr();
if (!exprNode)
return null();
// FIXME: Plumb isDelegatingYield appropriately.
(void) isDelegatingYield;
return handler.newUnary(PNK_YIELD, JSOP_YIELD, begin, exprNode);
}
case NotGenerator:
// We are in code that has not seen a yield, but we are in JS 1.7 or
// later. Try to transition to being a legacy generator.
JS_ASSERT(tokenStream.versionNumber() >= JSVERSION_1_7);
JS_ASSERT(pc->lastYieldOffset == ParseContext<ParseHandler>::NoYieldOffset);
if (!abortIfSyntaxParser())
return null();
if (!pc->sc->isFunctionBox()) {
report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_yield_str);
return null();
}
pc->sc->asFunctionBox()->setGeneratorKind(LegacyGenerator);
if (pc->funHasReturnExpr) {
/* As in Python (see PEP-255), disallow return v; in generators. */
reportBadReturn(null(), ParseError, JSMSG_BAD_GENERATOR_RETURN,
JSMSG_BAD_ANON_GENERATOR_RETURN);
return null();
}
// Fall through.
case LegacyGenerator:
{
// We are in a legacy generator: a function that has already seen a
// yield, or in a legacy generator comprehension.
JS_ASSERT(pc->sc->isFunctionBox());
pc->lastYieldOffset = begin;
// Legacy generators do not require a value.
Node exprNode;
switch (tokenStream.peekTokenSameLine(TokenStream::Operand)) {
case TOK_ERROR:
return null();
case TOK_EOF:
case TOK_EOL:
case TOK_SEMI:
case TOK_RC:
case TOK_RB:
case TOK_RP:
case TOK_COLON:
case TOK_COMMA:
// No value.
exprNode = null();
// ES6 does not permit yield without an operand. We should
// encourage users of yield expressions of this kind to pass an
// operand, to bring users closer to standard syntax.
if (!reportWithOffset(ParseWarning, false, pos().begin, JSMSG_YIELD_WITHOUT_OPERAND))
return null();
break;
default:
exprNode = assignExpr();
if (!exprNode)
return null();
}
return handler.newUnary(PNK_YIELD, JSOP_YIELD, begin, exprNode);
}
}
MOZ_ASSUME_UNREACHABLE("yieldExpr");
}
template <>
ParseNode *
Parser<FullParseHandler>::withStatement()
@ -4576,7 +4726,7 @@ typename ParseHandler::Node
Parser<ParseHandler>::labeledStatement()
{
uint32_t begin = pos().begin;
RootedPropertyName label(context, tokenStream.currentToken().name());
RootedPropertyName label(context, tokenStream.currentName());
for (StmtInfoPC *stmt = pc->topStmt; stmt; stmt = stmt->down) {
if (stmt->type == STMT_LABEL && stmt->label == label) {
report(ParseError, false, null(), JSMSG_DUPLICATE_LABEL);
@ -4717,9 +4867,13 @@ Parser<ParseHandler>::tryStatement()
break;
#endif
case TOK_YIELD:
if (!checkYieldNameValidity(JSMSG_CATCH_IDENTIFIER))
return null();
// Fall through.
case TOK_NAME:
{
RootedPropertyName label(context, tokenStream.currentToken().name());
RootedPropertyName label(context, tokenStream.currentName());
catchName = newBindingNode(label, false);
if (!catchName)
return null();
@ -4855,7 +5009,7 @@ Parser<ParseHandler>::statement(bool canHaveDirectives)
case TOK_BREAK:
return breakStatement();
case TOK_RETURN:
return returnStatementOrYieldExpression();
return returnStatement();
case TOK_WITH:
return withStatement();
case TOK_THROW:
@ -4886,10 +5040,18 @@ Parser<ParseHandler>::statement(bool canHaveDirectives)
}
return expressionStatement();
case TOK_YIELD:
if (tokenStream.peekToken() == TOK_COLON) {
if (!checkYieldNameValidity())
return null();
return labeledStatement();
}
return expressionStatement();
case TOK_NAME:
if (tokenStream.peekToken() == TOK_COLON)
return labeledStatement();
if (tokenStream.currentToken().name() == context->names().module
if (tokenStream.currentName() == context->names().module
&& tokenStream.peekTokenSameLine() == TOK_STRING)
{
return moduleDecl();
@ -5202,8 +5364,8 @@ Parser<ParseHandler>::assignExpr()
if (tt == TOK_STRING && tokenStream.nextTokenEndsExpr())
return stringLiteral();
if (tt == TOK_YIELD)
return returnStatementOrYieldExpression();
if (tt == TOK_YIELD && (versionNumber() >= JSVERSION_1_7 || pc->isGenerator()))
return yieldExpression();
tokenStream.ungetToken();
@ -5241,7 +5403,7 @@ Parser<ParseHandler>::assignExpr()
return null();
tokenStream.ungetToken();
return functionDef(NullPtr(), start, Normal, Arrow);
return functionDef(NullPtr(), start, Normal, Arrow, NotGenerator);
}
default:
@ -5662,7 +5824,7 @@ Parser<FullParseHandler>::comprehensionTail(ParseNode *kid, unsigned blockid, bo
BindData<FullParseHandler> data(context);
TokenKind tt;
JS_ASSERT(tokenStream.currentToken().type == TOK_FOR);
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
if (kind == PNK_SEMI) {
/*
@ -5747,7 +5909,7 @@ Parser<FullParseHandler>::comprehensionTail(ParseNode *kid, unsigned blockid, bo
#endif
case TOK_NAME:
name = tokenStream.currentToken().name();
name = tokenStream.currentName();
/*
* Create a name node with pn_op JSOP_NAME. We can't set pn_op to
@ -5955,7 +6117,8 @@ Parser<FullParseHandler>::generatorExpr(ParseNode *kid)
/* Create box for fun->object early to protect against last-ditch GC. */
Directives directives(/* strict = */ outerpc->sc->strict);
FunctionBox *genFunbox = newFunctionBox(genfn, fun, outerpc, directives);
FunctionBox *genFunbox = newFunctionBox(genfn, fun, outerpc, directives,
LegacyGenerator);
if (!genFunbox)
return null();
@ -5975,7 +6138,7 @@ Parser<FullParseHandler>::generatorExpr(ParseNode *kid)
if (outerpc->sc->isFunctionBox())
genFunbox->funCxFlags = outerpc->sc->asFunctionBox()->funCxFlags;
genFunbox->setIsLegacyGenerator();
JS_ASSERT(genFunbox->isLegacyGenerator());
genFunbox->inGenexpLambda = true;
genfn->pn_blockid = genpc.bodyid;
@ -6116,7 +6279,7 @@ Parser<ParseHandler>::memberExpr(TokenKind tt, bool allowCallSyntax)
if (tt == TOK_ERROR)
return null();
if (tt == TOK_NAME) {
PropertyName *field = tokenStream.currentToken().name();
PropertyName *field = tokenStream.currentName();
nextMember = handler.newPropertyAccess(lhs, field, pos().end);
if (!nextMember)
return null();
@ -6191,9 +6354,7 @@ template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::identifierName()
{
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_NAME));
RootedPropertyName name(context, tokenStream.currentToken().name());
RootedPropertyName name(context, tokenStream.currentName());
Node pn = newName(name);
if (!pn)
return null();
@ -6395,7 +6556,7 @@ Parser<ParseHandler>::objectLiteral()
break;
case TOK_NAME: {
atom = tokenStream.currentToken().name();
atom = tokenStream.currentName();
if (atom == context->names().get) {
op = JSOP_INITPROP_GETTER;
} else if (atom == context->names().set) {
@ -6411,7 +6572,7 @@ Parser<ParseHandler>::objectLiteral()
// name next.
TokenKind tt = tokenStream.getToken(TokenStream::KeywordIsName);
if (tt == TOK_NAME) {
atom = tokenStream.currentToken().name();
atom = tokenStream.currentName();
propname = newName(atom->asPropertyName());
if (!propname)
return null();
@ -6527,7 +6688,7 @@ Parser<ParseHandler>::objectLiteral()
TokenStream::Position start(keepAtoms);
tokenStream.tell(&start);
Node accessor = functionDef(funName, start, op == JSOP_INITPROP_GETTER ? Getter : Setter,
Expression);
Expression, NotGenerator);
if (!accessor)
return null();
if (!handler.addAccessorPropertyDefinition(literal, propname, accessor, op))
@ -6628,6 +6789,10 @@ Parser<ParseHandler>::primaryExpr(TokenKind tt)
case TOK_STRING:
return stringLiteral();
case TOK_YIELD:
if (!checkYieldNameValidity())
return null();
// Fall through.
case TOK_NAME:
return identifierName();
@ -6689,7 +6854,7 @@ template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::parenExpr(bool *genexp)
{
JS_ASSERT(tokenStream.currentToken().type == TOK_LP);
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
uint32_t begin = pos().begin;
if (genexp)

View File

@ -104,18 +104,20 @@ struct ParseContext : public GenericParseContext
const unsigned staticLevel; /* static compilation unit nesting level */
// Functions start off being parsed as NotGenerator.
// NotGenerator transitions to LegacyGenerator on parsing "yield" in JS 1.7.
enum GeneratorParseMode { NotGenerator, LegacyGenerator };
GeneratorParseMode generatorParseMode;
// lastYieldOffset stores the offset of the last yield that was parsed.
// NoYieldOffset is its initial value.
static const uint32_t NoYieldOffset = UINT32_MAX;
uint32_t lastYieldOffset;
bool isGenerator() const { return generatorParseMode != NotGenerator; }
bool isLegacyGenerator() const { return generatorParseMode == LegacyGenerator; }
// Most functions start off being parsed as non-generators.
// Non-generators transition to LegacyGenerator on parsing "yield" in JS 1.7.
// An ES6 generator is marked as a "star generator" before its body is parsed.
GeneratorKind generatorKind() const {
return sc->isFunctionBox() ? sc->asFunctionBox()->generatorKind() : NotGenerator;
}
bool isGenerator() const { return generatorKind() != NotGenerator; }
bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; }
bool isStarGenerator() const { return generatorKind() == StarGenerator; }
Node blockNode; /* parse node for a block with let declarations
(block with its own lexical scope) */
@ -249,7 +251,6 @@ struct ParseContext : public GenericParseContext
blockChain(prs->context),
maybeFunction(maybeFunction),
staticLevel(staticLevel),
generatorParseMode(NotGenerator),
lastYieldOffset(NoYieldOffset),
blockNode(ParseHandler::null()),
decls_(prs->context, prs->alloc),
@ -400,7 +401,7 @@ class Parser : private AutoGCRooter, public StrictModeGetter
ObjectBox *newObjectBox(JSObject *obj);
ModuleBox *newModuleBox(Module *module, ParseContext<ParseHandler> *pc);
FunctionBox *newFunctionBox(Node fn, JSFunction *fun, ParseContext<ParseHandler> *pc,
Directives directives);
Directives directives, GeneratorKind generatorKind);
/*
* Create a new function object given parse context (pc) and a name (which
@ -433,13 +434,16 @@ class Parser : private AutoGCRooter, public StrictModeGetter
Node statement(bool canHaveDirectives = false);
bool maybeParseDirective(Node list, Node pn, bool *cont);
// Parse a function, given only its body. Used for the Function constructor.
// Parse a function, given only its body. Used for the Function and
// Generator constructors.
Node standaloneFunctionBody(HandleFunction fun, const AutoNameVector &formals,
GeneratorKind generatorKind,
Directives inheritedDirectives, Directives *newDirectives);
// Parse a function, given only its arguments and body. Used for lazily
// parsed functions.
Node standaloneLazyFunction(HandleFunction fun, unsigned staticLevel, bool strict);
Node standaloneLazyFunction(HandleFunction fun, unsigned staticLevel, bool strict,
GeneratorKind generatorKind);
/*
* Parse a function body. Pass StatementListBody if the body is a list of
@ -487,7 +491,7 @@ class Parser : private AutoGCRooter, public StrictModeGetter
Node switchStatement();
Node continueStatement();
Node breakStatement();
Node returnStatementOrYieldExpression();
Node returnStatement();
Node withStatement();
Node labeledStatement();
Node throwStatement();
@ -504,6 +508,7 @@ class Parser : private AutoGCRooter, public StrictModeGetter
Node expr();
Node assignExpr();
Node assignExprWithoutYield(unsigned err);
Node yieldExpression();
Node condExpr1();
Node orExpr1();
Node unaryExpr();
@ -517,9 +522,10 @@ class Parser : private AutoGCRooter, public StrictModeGetter
bool functionArguments(FunctionSyntaxKind kind, Node *list, Node funcpn, bool &hasRest);
Node functionDef(HandlePropertyName name, const TokenStream::Position &start,
FunctionType type, FunctionSyntaxKind kind);
FunctionType type, FunctionSyntaxKind kind, GeneratorKind generatorKind);
bool functionArgsAndBody(Node pn, HandleFunction fun,
FunctionType type, FunctionSyntaxKind kind,
GeneratorKind generatorKind,
Directives inheritedDirectives, Directives *newDirectives);
Node unaryOpExpr(ParseNodeKind kind, JSOp op, uint32_t begin);
@ -536,6 +542,9 @@ class Parser : private AutoGCRooter, public StrictModeGetter
Node identifierName();
bool matchLabel(MutableHandle<PropertyName*> label);
bool checkYieldNameValidity(unsigned errorNumber = JSMSG_SYNTAX_ERROR);
bool allowsForEachIn() {
#if !JS_HAS_FOR_EACH_IN
return false;
@ -556,6 +565,7 @@ class Parser : private AutoGCRooter, public StrictModeGetter
bool checkFunctionArguments();
bool makeDefIntoUse(Definition *dn, Node pn, JSAtom *atom);
bool checkFunctionName(HandlePropertyName funName);
bool checkFunctionDefinition(HandlePropertyName funName, Node *pn, FunctionSyntaxKind kind,
bool *pbodyProcessed);
bool finishFunctionDefinition(Node pn, FunctionBox *funbox, Node prelude, Node body);

View File

@ -72,10 +72,6 @@ class FunctionContextFlags
// This class's data is all private and so only visible to these friends.
friend class FunctionBox;
// We parsed a yield statement in the function, which can happen in JS1.7+
// mode.
bool isLegacyGenerator:1;
// The function or a function that encloses it may define new local names
// at runtime through means other than calling eval.
bool mightAliasLocals:1;
@ -129,8 +125,7 @@ class FunctionContextFlags
public:
FunctionContextFlags()
: isLegacyGenerator(false),
mightAliasLocals(false),
: mightAliasLocals(false),
hasExtensibleScope(false),
needsDeclEnvObject(false),
argumentsHasLocalBinding(false),
@ -264,6 +259,8 @@ class FunctionBox : public ObjectBox, public SharedContext
uint32_t startLine;
uint32_t startColumn;
uint16_t ndefaults;
uint8_t generatorKindBits_; /* The GeneratorKind of this function. */
bool inWith:1; /* some enclosing scope is a with-statement */
bool inGenexpLambda:1; /* lambda from generator expression */
bool hasDestructuringArgs:1; /* arguments list contains destructuring expression */
@ -279,21 +276,30 @@ class FunctionBox : public ObjectBox, public SharedContext
template <typename ParseHandler>
FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunction *fun,
ParseContext<ParseHandler> *pc, Directives directives,
bool extraWarnings);
bool extraWarnings, GeneratorKind generatorKind);
ObjectBox *toObjectBox() { return this; }
JSFunction *function() const { return &object->as<JSFunction>(); }
// In the future, isGenerator will also return true for ES6 generators.
bool isGenerator() const { return isLegacyGenerator(); }
bool isLegacyGenerator() const { return funCxFlags.isLegacyGenerator; }
GeneratorKind generatorKind() const { return GeneratorKindFromBits(generatorKindBits_); }
bool isGenerator() const { return generatorKind() != NotGenerator; }
bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; }
bool isStarGenerator() const { return generatorKind() == StarGenerator; }
void setGeneratorKind(GeneratorKind kind) {
// A generator kind can be set at initialization, or when "yield" is
// first seen. In both cases the transition can only happen from
// NotGenerator.
JS_ASSERT(!isGenerator());
generatorKindBits_ = GeneratorKindAsBits(kind);
}
bool mightAliasLocals() const { return funCxFlags.mightAliasLocals; }
bool hasExtensibleScope() const { return funCxFlags.hasExtensibleScope; }
bool needsDeclEnvObject() const { return funCxFlags.needsDeclEnvObject; }
bool argumentsHasLocalBinding() const { return funCxFlags.argumentsHasLocalBinding; }
bool definitelyNeedsArgsObj() const { return funCxFlags.definitelyNeedsArgsObj; }
void setIsLegacyGenerator() { funCxFlags.isLegacyGenerator = true; }
void setMightAliasLocals() { funCxFlags.mightAliasLocals = true; }
void setHasExtensibleScope() { funCxFlags.hasExtensibleScope = true; }
void setNeedsDeclEnvObject() { funCxFlags.needsDeclEnvObject = true; }

View File

@ -937,10 +937,10 @@ TokenStream::checkForKeyword(const jschar *s, size_t length, TokenKind *ttp)
return reportError(JSMSG_RESERVED_ID, kw->chars);
}
// The keyword is not in this version. Treat it as an identifier,
// unless it is let or yield which we treat as TOK_STRICT_RESERVED by
// falling through to the code below (ES5 forbids them in strict mode).
if (kw->tokentype != TOK_LET && kw->tokentype != TOK_YIELD)
// The keyword is not in this version. Treat it as an identifier, unless
// it is let which we treat as TOK_STRICT_RESERVED by falling through to
// the code below (ES5 forbids it in strict mode).
if (kw->tokentype != TOK_LET)
return true;
}

View File

@ -407,6 +407,13 @@ class MOZ_STACK_CLASS TokenStream
JSVersion versionNumber() const { return VersionNumber(options().version); }
JSVersion versionWithFlags() const { return options().version; }
PropertyName *currentName() const {
if (isCurrentTokenType(TOK_YIELD))
return cx->names().yield;
JS_ASSERT(isCurrentTokenType(TOK_NAME));
return currentToken().name();
}
bool isCurrentTokenAssignment() const {
return TokenKindIsAssignment(currentToken().type);
}

View File

@ -3,7 +3,7 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import os, shlex, subprocess, sys, traceback
import os, posixpath, shlex, subprocess, sys, traceback
def add_libdir_to_path():
from os.path import dirname, exists, join, realpath
@ -74,6 +74,23 @@ def main(argv):
help='Run tests with all IonMonkey option combinations (ignores --jitflags)')
op.add_option('-j', '--worker-count', dest='max_jobs', type=int, default=max_jobs_default,
help='Number of tests to run in parallel (default %default)')
op.add_option('--remote', action='store_true',
help='Run tests on a remote device')
op.add_option('--deviceIP', action='store',
type='string', dest='device_ip',
help='IP address of remote device to test')
op.add_option('--devicePort', action='store',
type=int, dest='device_port', default=20701,
help='port of remote device to test')
op.add_option('--deviceTransport', action='store',
type='string', dest='device_transport', default='sut',
help='The transport to use to communicate with device: [adb|sut]; default=sut')
op.add_option('--remoteTestRoot', dest='remote_test_root', action='store',
type='string', default='/data/local/tests',
help='The remote directory to use as test root (eg. /data/local/tests)')
op.add_option('--localLib', dest='local_lib', action='store',
type='string',
help='The location of libraries to push -- preferably stripped')
options, args = op.parse_args(argv)
if len(args) < 1:
@ -171,7 +188,11 @@ def main(argv):
job_list.append(new_test)
prefix = [os.path.abspath(args[0])] + shlex.split(options.shell_args)
prefix += ['-f', os.path.join(jittests.LIB_DIR, 'prolog.js')]
prolog = os.path.join(jittests.LIB_DIR, 'prolog.js')
if options.remote:
prolog = posixpath.join(options.remote_test_root, 'jit-tests/lib/prolog.js')
prefix += ['-f', prolog]
if options.debug:
if len(job_list) > 1:
print 'Multiple tests match command line arguments, debugger can only run one'
@ -186,7 +207,9 @@ def main(argv):
try:
ok = None
if options.max_jobs > 1 and jittests.HAVE_MULTIPROCESSING:
if options.remote:
ok = jittests.run_tests_remote(job_list, prefix, options)
elif options.max_jobs > 1 and jittests.HAVE_MULTIPROCESSING:
ok = jittests.run_tests_parallel(job_list, prefix, options)
else:
ok = jittests.run_tests(job_list, prefix, options)

View File

@ -0,0 +1,38 @@
// Test interactions between ES6 generators and not-yet-standard
// features.
function assertSyntaxError(str) {
var msg;
var evil = eval;
try {
// Non-direct eval.
evil(str);
} catch (exc) {
if (exc instanceof SyntaxError)
return;
msg = "Assertion failed: expected SyntaxError, got " + exc;
}
if (msg === undefined)
msg = "Assertion failed: expected SyntaxError, but no exception thrown";
throw new Error(msg + " - " + str);
}
// Destructuring binding.
assertSyntaxError("function* f(x = yield) {}");
assertSyntaxError("function* f(x = yield 17) {}");
assertSyntaxError("function* f([yield]) {}");
assertSyntaxError("function* f({ yield }) {}");
assertSyntaxError("function* f(...yield) {}");
// For each.
assertSyntaxError("for yield");
assertSyntaxError("for yield (;;) {}");
assertSyntaxError("for yield (x of y) {}");
assertSyntaxError("for yield (var i in o) {}");
// Expression bodies.
assertSyntaxError("function* f() yield 7");
// Asm.js.
load(libdir + "asm.js");
assertAsmDirectiveFail("function* f() { 'use asm'; function g() { return 0; } return g; })()")

View File

@ -4607,7 +4607,7 @@ ParseFunction(ModuleCompiler &m, ParseNode **fnOut)
AsmJSParseContext *outerpc = m.parser().pc;
Directives directives(outerpc);
FunctionBox *funbox = m.parser().newFunctionBox(fn, fun, outerpc, directives);
FunctionBox *funbox = m.parser().newFunctionBox(fn, fun, outerpc, directives, NotGenerator);
if (!funbox)
return false;

View File

@ -409,3 +409,4 @@ MSG_DEF(JSMSG_BINARYDATA_NOT_BINARYSTRUCT, 355, 1, JSEXN_TYPEERR, "{0} is not
MSG_DEF(JSMSG_BINARYDATA_SUBARRAY_INTEGER_ARG, 356, 1, JSEXN_ERR, "argument {0} must be an integer")
MSG_DEF(JSMSG_BINARYDATA_STRUCTTYPE_EMPTY_DESCRIPTOR, 357, 0, JSEXN_ERR, "field descriptor cannot be empty")
MSG_DEF(JSMSG_BINARYDATA_STRUCTTYPE_BAD_FIELD, 358, 1, JSEXN_ERR, "field {0} is not a valid BinaryData Type descriptor")
MSG_DEF(JSMSG_ES6_UNIMPLEMENTED, 359, 0, JSEXN_ERR, "ES6 functionality not yet implemented")

View File

@ -1749,7 +1749,7 @@ ScriptAnalysis::needsArgsObj(JSContext *cx)
*
* FIXME: Don't build arguments for ES6 generator expressions.
*/
if (cx->compartment()->debugMode() || script_->isGenerator)
if (cx->compartment()->debugMode() || script_->isGenerator())
return true;
/*

View File

@ -104,7 +104,6 @@ const char js_typeof_str[] = "typeof";
const char js_void_str[] = "void";
const char js_while_str[] = "while";
const char js_with_str[] = "with";
const char js_yield_str[] = "yield";
/*
* For a browser build from 2007-08-09 after the browser starts up there are

View File

@ -153,7 +153,6 @@ extern const char js_typeof_str[];
extern const char js_void_str[];
extern const char js_while_str[];
extern const char js_with_str[];
extern const char js_yield_str[];
namespace js {

View File

@ -526,6 +526,7 @@ FindBody(JSContext *cx, HandleFunction fun, StableCharPtr chars, size_t length,
do {
switch (ts.getToken()) {
case TOK_NAME:
case TOK_YIELD:
if (nest == 0)
onward = false;
break;
@ -1228,14 +1229,7 @@ fun_isGenerator(JSContext *cx, unsigned argc, Value *vp)
return true;
}
bool result = false;
if (fun->hasScript()) {
JSScript *script = fun->nonLazyScript();
JS_ASSERT(script->length != 0);
result = script->isGenerator;
}
JS_SET_RVAL(cx, vp, BooleanValue(result));
JS_SET_RVAL(cx, vp, BooleanValue(fun->isGenerator()));
return true;
}
@ -1456,10 +1450,15 @@ js::Function(JSContext *cx, unsigned argc, Value *vp)
return false;
}
if (tt == TOK_YIELD && ts.versionNumber() < JSVERSION_1_7)
tt = TOK_NAME;
if (tt != TOK_NAME) {
if (tt == TOK_TRIPLEDOT) {
hasRest = true;
tt = ts.getToken();
if (tt == TOK_YIELD && ts.versionNumber() < JSVERSION_1_7)
tt = TOK_NAME;
if (tt != TOK_NAME) {
if (tt != TOK_ERROR)
ts.reportError(JSMSG_NO_REST_NAME);
@ -1470,7 +1469,7 @@ js::Function(JSContext *cx, unsigned argc, Value *vp)
}
}
if (!formals.append(ts.currentToken().name()))
if (!formals.append(ts.currentName()))
return false;
/*

View File

@ -284,6 +284,18 @@ class JSFunction : public JSObject
return u.i.s.lazy_;
}
js::GeneratorKind generatorKind() const {
if (!isInterpreted())
return js::NotGenerator;
return hasScript() ? nonLazyScript()->generatorKind() : lazyScript()->generatorKind();
}
bool isGenerator() const { return generatorKind() != js::NotGenerator; }
bool isLegacyGenerator() const { return generatorKind() == js::LegacyGenerator; }
bool isStarGenerator() const { return generatorKind() == js::StarGenerator; }
inline void setScript(JSScript *script_);
inline void initScript(JSScript *script_);
void initLazyScript(js::LazyScript *lazy) {

View File

@ -1475,6 +1475,13 @@ js_NewGenerator(JSContext *cx, const FrameRegs &stackRegs)
JS_ASSERT(stackRegs.stackDepth() == 0);
StackFrame *stackfp = stackRegs.fp();
JS_ASSERT(stackfp->script()->isGenerator());
if (stackfp->script()->isStarGenerator()) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_ES6_UNIMPLEMENTED);
return NULL;
}
Rooted<GlobalObject*> global(cx, &stackfp->global());
RootedObject obj(cx);
{

View File

@ -2787,6 +2787,7 @@ ASTSerializer::function(ParseNode *pn, ASTType type, MutableHandleValue dst)
{
RootedFunction func(cx, pn->pn_funbox->function());
// FIXME: Provide more information (legacy generator vs star generator).
bool isGenerator = pn->pn_funbox->isGenerator();
bool isExpression =

View File

@ -408,9 +408,9 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
FunHasAnyAliasedFormal,
ArgumentsHasVarBinding,
NeedsArgsObj,
IsGenerator,
IsGeneratorExp,
IsLegacyGenerator,
IsStarGenerator,
OwnSource,
ExplicitUseStrict,
SelfHosted
@ -497,12 +497,12 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
scriptBits |= (1 << NeedsArgsObj);
if (!enclosingScript || enclosingScript->scriptSource() != script->scriptSource())
scriptBits |= (1 << OwnSource);
if (script->isGenerator)
scriptBits |= (1 << IsGenerator);
if (script->isGeneratorExp)
scriptBits |= (1 << IsGeneratorExp);
if (script->isLegacyGenerator)
if (script->isLegacyGenerator())
scriptBits |= (1 << IsLegacyGenerator);
if (script->isStarGenerator())
scriptBits |= (1 << IsStarGenerator);
JS_ASSERT(!script->compileAndGo);
JS_ASSERT(!script->hasSingletons);
@ -597,12 +597,14 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
script->setArgumentsHasVarBinding();
if (scriptBits & (1 << NeedsArgsObj))
script->setNeedsArgsObj(true);
if (scriptBits & (1 << IsGenerator))
script->isGenerator = true;
if (scriptBits & (1 << IsGeneratorExp))
script->isGeneratorExp = true;
if (scriptBits & (1 << IsLegacyGenerator))
script->isLegacyGenerator = true;
if (scriptBits & (1 << IsLegacyGenerator)) {
JS_ASSERT(!(scriptBits & (1 << IsStarGenerator)));
script->setGeneratorKind(LegacyGenerator);
} else if (scriptBits & (1 << IsStarGenerator))
script->setGeneratorKind(StarGenerator);
}
JS_STATIC_ASSERT(sizeof(jsbytecode) == 1);
@ -1974,9 +1976,8 @@ JSScript::fullyInitFromEmitter(ExclusiveContext *cx, HandleScript script, Byteco
RootedFunction fun(cx, NULL);
if (funbox) {
JS_ASSERT(!bce->script->noScriptRval);
script->isGenerator = funbox->isGenerator();
script->isGeneratorExp = funbox->inGenexpLambda;
script->isLegacyGenerator = funbox->isLegacyGenerator();
script->setGeneratorKind(funbox->generatorKind());
script->setFunction(funbox->function());
}
@ -2477,8 +2478,8 @@ js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun,
dst->funHasAnyAliasedFormal = src->funHasAnyAliasedFormal;
dst->hasSingletons = src->hasSingletons;
dst->treatAsRunOnce = src->treatAsRunOnce;
dst->isGenerator = src->isGenerator;
dst->isGeneratorExp = src->isGeneratorExp;
dst->setGeneratorKind(src->generatorKind());
/* Copy over hints. */
dst->shouldInline = src->shouldInline;
@ -2919,7 +2920,7 @@ JSScript::argumentsOptimizationFailed(JSContext *cx, HandleScript script)
if (script->needsArgsObj())
return true;
JS_ASSERT(!script->isGenerator);
JS_ASSERT(!script->isGenerator());
script->needsArgsObj_ = true;
@ -3011,6 +3012,7 @@ LazyScript::LazyScript(JSFunction *fun, void *table, uint32_t numFreeVariables,
version_(version),
numFreeVariables_(numFreeVariables),
numInnerFunctions_(numInnerFunctions),
generatorKindBits_(GeneratorKindAsBits(NotGenerator)),
strict_(false),
bindingsAccessedDynamically_(false),
hasDebuggerStatement_(false),

View File

@ -414,6 +414,19 @@ class ScriptSourceObject : public JSObject
static const uint32_t SOURCE_SLOT = 0;
};
enum GeneratorKind { NotGenerator, LegacyGenerator, StarGenerator };
static inline unsigned
GeneratorKindAsBits(GeneratorKind generatorKind) {
return static_cast<unsigned>(generatorKind);
}
static inline GeneratorKind
GeneratorKindFromBits(unsigned val) {
JS_ASSERT(val <= StarGenerator);
return static_cast<GeneratorKind>(val);
}
} /* namespace js */
class JSScript : public js::gc::Cell
@ -519,7 +532,7 @@ class JSScript : public js::gc::Cell
uint16_t nslots; /* vars plus maximum stack depth */
uint16_t staticLevel;/* static level for display maintenance */
// 8-bit fields.
// 4-bit fields.
public:
// The kinds of the optional arrays.
@ -528,15 +541,16 @@ class JSScript : public js::gc::Cell
OBJECTS,
REGEXPS,
TRYNOTES,
LIMIT
ARRAY_KIND_BITS
};
typedef uint8_t ArrayBitsT;
private:
// The bits in this field indicate the presence/non-presence of several
// optional arrays in |data|. See the comments above Create() for details.
ArrayBitsT hasArrayBits;
uint8_t hasArrayBits:4;
// The GeneratorKind of the script.
uint8_t generatorKindBits_:4;
// 1-bit fields.
@ -589,14 +603,9 @@ class JSScript : public js::gc::Cell
#endif
bool invalidatedIdempotentCache:1; /* idempotent cache has triggered invalidation */
// All generators have isGenerator set to true.
bool isGenerator:1;
// If the generator was created implicitly via a generator expression,
// isGeneratorExp will be true.
bool isGeneratorExp:1;
// Generators are either legacy-style (JS 1.7+ starless generators with
// StopIteration), or ES6-style (function* with boxed return values).
bool isLegacyGenerator:1;
bool hasScriptCounts:1;/* script has an entry in
JSCompartment::scriptCountsMap */
@ -647,6 +656,19 @@ class JSScript : public js::gc::Cell
jsbytecode *argumentsBytecode() const { JS_ASSERT(code[0] == JSOP_ARGUMENTS); return code; }
void setArgumentsHasVarBinding();
js::GeneratorKind generatorKind() const {
return js::GeneratorKindFromBits(generatorKindBits_);
}
bool isGenerator() const { return generatorKind() != js::NotGenerator; }
bool isLegacyGenerator() const { return generatorKind() == js::LegacyGenerator; }
bool isStarGenerator() const { return generatorKind() == js::StarGenerator; }
void setGeneratorKind(js::GeneratorKind kind) {
// A script only gets its generator kind set as part of initialization,
// so it can only transition from not being a generator.
JS_ASSERT(!isGenerator());
generatorKindBits_ = GeneratorKindAsBits(kind);
}
/*
* As an optimization, even when argsHasLocalBinding, the function prologue
* may not need to create an arguments object. This is determined by
@ -1038,7 +1060,8 @@ class JSScript : public js::gc::Cell
void markChildren(JSTracer *trc);
};
JS_STATIC_ASSERT(sizeof(JSScript::ArrayBitsT) * 8 >= JSScript::LIMIT);
/* The array kind flags are stored in a 4-bit field; make sure they fit. */
JS_STATIC_ASSERT(JSScript::ARRAY_KIND_BITS <= 4);
/* If this fails, add/remove padding within JSScript. */
JS_STATIC_ASSERT(sizeof(JSScript) % js::gc::CellSize == 0);
@ -1145,7 +1168,9 @@ class LazyScript : public js::gc::Cell
uint32_t version_ : 8;
uint32_t numFreeVariables_ : 24;
uint32_t numInnerFunctions_ : 26;
uint32_t numInnerFunctions_ : 24;
uint32_t generatorKindBits_:2;
// N.B. These are booleans but need to be uint32_t to pack correctly on MSVC.
uint32_t strict_ : 1;
@ -1211,6 +1236,23 @@ class LazyScript : public js::gc::Cell
return (HeapPtrFunction *)&freeVariables()[numFreeVariables()];
}
GeneratorKind generatorKind() const { return GeneratorKindFromBits(generatorKindBits_); }
bool isGenerator() const { return generatorKind() != NotGenerator; }
bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; }
bool isStarGenerator() const { return generatorKind() == StarGenerator; }
void setGeneratorKind(GeneratorKind kind) {
// A script only gets its generator kind set as part of initialization,
// so it can only transition from NotGenerator.
JS_ASSERT(!isGenerator());
// Legacy generators cannot currently be lazy.
JS_ASSERT(kind != LegacyGenerator);
generatorKindBits_ = GeneratorKindAsBits(kind);
}
bool strict() const {
return strict_;
}

View File

View File

@ -0,0 +1,97 @@
// This file was written by Andy Wingo <wingo@igalia.com> and originally
// contributed to V8 as generators-parsing.js, available here:
//
// http://code.google.com/p/v8/source/browse/branches/bleeding_edge/test/mjsunit/harmony/generators-parsing.js
function assertSyntaxError(str) {
var msg;
var evil = eval;
try {
// Non-direct eval.
evil(str);
} catch (exc) {
if (exc instanceof SyntaxError)
return;
msg = "Assertion failed: expected SyntaxError, got " + exc;
}
if (msg === undefined)
msg = "Assertion failed: expected SyntaxError, but no exception thrown";
throw new Error(msg + " - " + str);
}
// Yield statements.
function* g() { yield 3; yield 4; }
// Yield expressions.
function* g() { (yield 3) + (yield 4); }
// You can have a generator in strict mode.
function* g() { "use strict"; yield 3; yield 4; }
// Generators can have return statements also, which internally parse to a kind
// of yield expression.
function* g() { yield 1; return; }
function* g() { yield 1; return 2; }
function* g() { yield 1; return 2; yield "dead"; }
// Generator expression.
(function* () { yield 3; });
// Named generator expression.
(function* g() { yield 3; });
// Generators do not have to contain yield expressions.
function* g() { }
// YieldExpressions can occur in the RHS of a YieldExpression.
function* g() { yield yield 1; }
function* g() { yield 3 + (yield 4); }
// Generator definitions with a name of "yield" are not specifically ruled out
// by the spec, as the `yield' name is outside the generator itself. However,
// in strict-mode, "yield" is an invalid identifier.
function* yield() { (yield 3) + (yield 4); }
assertSyntaxError("function* yield() { 'use strict'; (yield 3) + (yield 4); }");
// In classic mode, yield is a normal identifier, outside of generators.
function yield(yield) { yield: yield (yield + yield (0)); }
// Yield is always valid as a key in an object literal.
({ yield: 1 });
function* g() { yield ({ yield: 1 }) }
function* g() { yield ({ get yield() { return 1; }}) }
// Yield is a valid property name.
function* g(obj) { yield obj.yield; }
// Checks that yield is a valid label in classic mode, but not valid in a strict
// mode or in generators.
function f() { yield: 1 }
assertSyntaxError("function f() { 'use strict'; yield: 1 }")
assertSyntaxError("function* g() { yield: 1 }")
// Yield is only a keyword in the body of the generator, not in nested
// functions.
function* g() { function f(yield) { yield (yield + yield (0)); } }
// Yield needs a RHS.
assertSyntaxError("function* g() { yield; }");
// Yield in a generator is not an identifier.
assertSyntaxError("function* g() { yield = 10; }");
// Yield binds very loosely, so this parses as "yield (3 + yield 4)", which is
// invalid.
assertSyntaxError("function* g() { yield 3 + yield 4; }");
// Yield is still a future-reserved-word in strict mode
assertSyntaxError("function f() { 'use strict'; var yield = 13; }");
// The name of the NFE is let-bound in G, so is invalid.
assertSyntaxError("function* g() { yield (function yield() {}); }");
// In generators, yield is invalid as a formal argument name.
assertSyntaxError("function* g(yield) { yield (10); }");
if (typeof reportCompare == "function")
reportCompare(true, true);

View File

@ -7,11 +7,12 @@
# jit_test.py -- Python harness for JavaScript trace tests.
from __future__ import print_function
import os, sys, tempfile, traceback, time
import os, posixpath, sys, tempfile, traceback, time
import subprocess
from subprocess import Popen, PIPE
from threading import Thread
import signal
import StringIO
try:
from multiprocessing import Process, Manager, cpu_count
@ -152,15 +153,29 @@ class Test:
return test
def command(self, prefix):
scriptdir_var = os.path.dirname(self.path);
def command(self, prefix, libdir, remote_prefix=None):
path = self.path
if remote_prefix:
path = self.path.replace(TEST_DIR, remote_prefix)
scriptdir_var = os.path.dirname(path);
if not scriptdir_var.endswith('/'):
scriptdir_var += '/'
expr = ("const platform=%r; const libdir=%r; const scriptdir=%r"
% (sys.platform, LIB_DIR, scriptdir_var))
# Platforms where subprocess immediately invokes exec do not care
# whether we use double or single quotes. On windows and when using
# a remote device, however, we have to be careful to use the quote
# style that is the opposite of what the exec wrapper uses.
# This uses %r to get single quotes on windows and special cases
# the remote device.
fmt = 'const platform=%r; const libdir=%r; const scriptdir=%r'
if remote_prefix:
fmt = 'const platform="%s"; const libdir="%s"; const scriptdir="%s"'
expr = fmt % (sys.platform, libdir, scriptdir_var)
# We may have specified '-a' or '-d' twice: once via --jitflags, once
# via the "|jit-test|" line. Remove dups because they are toggles.
cmd = prefix + list(set(self.jitflags)) + ['-e', expr, '-f', self.path]
cmd = prefix + list(set(self.jitflags)) + ['-e', expr, '-f', path]
if self.valgrind:
cmd = self.VALGRIND_CMD + cmd
return cmd
@ -268,7 +283,7 @@ def run_cmd_avoid_stdio(cmdline, env, timeout):
return read_and_unlink(stdoutPath), read_and_unlink(stderrPath), code
def run_test(test, prefix, options):
cmd = test.command(prefix)
cmd = test.command(prefix, LIB_DIR)
if options.show_cmd:
print(subprocess.list2cmdline(cmd))
@ -284,6 +299,26 @@ def run_test(test, prefix, options):
out, err, code, timed_out = run(cmd, env, options.timeout)
return TestOutput(test, cmd, out, err, code, None, timed_out)
def run_test_remote(test, device, prefix, options):
cmd = test.command(prefix, posixpath.join(options.remote_test_root, 'lib/'), posixpath.join(options.remote_test_root, 'tests'))
if options.show_cmd:
print(subprocess.list2cmdline(cmd))
env = {}
if test.tz_pacific:
env['TZ'] = 'PST8PDT'
env['LD_LIBRARY_PATH'] = options.remote_test_root
buf = StringIO.StringIO()
returncode = device.shell(cmd, buf, env=env, cwd=options.remote_test_root,
timeout=int(options.timeout))
out = buf.getvalue()
# We can't distinguish between stdout and stderr so we pass
# the same buffer to both.
return TestOutput(test, cmd, out, out, returncode, None, False)
def check_output(out, err, rc, test):
if test.expect_error:
# The shell exits with code 3 on uncaught exceptions.
@ -541,6 +576,58 @@ def run_tests(tests, prefix, options):
ok = process_test_results(gen, len(tests), options)
return ok
def get_remote_results(tests, device, prefix, options):
for test in tests:
yield run_test_remote(test, device, prefix, options)
def push_libs(options, device):
# This saves considerable time in pushing unnecessary libraries
# to the device but needs to be updated if the dependencies change.
required_libs = ['libnss3.so', 'libmozglue.so']
for file in os.listdir(options.local_lib):
if file in required_libs:
remote_file = posixpath.join(options.remote_test_root, file)
device.pushFile(os.path.join(options.local_lib, file), remote_file)
def push_progs(options, device, progs):
for local_file in progs:
remote_file = posixpath.join(options.remote_test_root, os.path.basename(local_file))
device.pushFile(local_file, remote_file)
def run_tests_remote(tests, prefix, options):
# Setup device with everything needed to run our tests.
from mozdevice import devicemanager, devicemanagerADB, devicemanagerSUT
if options.device_transport == 'adb':
if options.device_ip:
dm = devicemanagerADB.DeviceManagerADB(options.device_ip, options.devicePort, packageName=None, deviceRoot=options.remoteTestRoot)
else:
dm = devicemanagerADB.DeviceManagerADB(packageName=None, deviceRoot=options.remote_test_root)
else:
dm = devicemanagerSUT.DeviceManagerSUT(options.device_ip, options.device_port, deviceRoot=options.remote_test_root)
if options.device_ip == None:
print('Error: you must provide a device IP to connect to via the --device option')
sys.exit(1)
# Update the test root to point to our test directory.
options.remote_test_root = posixpath.join(options.remote_test_root, 'jit-tests')
# Push js shell and libraries.
if dm.dirExists(options.remote_test_root):
dm.removeDir(options.remote_test_root)
dm.mkDir(options.remote_test_root)
push_libs(options, dm)
push_progs(options, dm, [prefix[0]])
dm.chmodDir(options.remote_test_root)
dm.pushDir(os.path.dirname(TEST_DIR), options.remote_test_root)
prefix[0] = os.path.join(options.remote_test_root, 'js')
# Run all tests.
gen = get_remote_results(tests, dm, prefix, options)
ok = process_test_results(gen, len(tests), options)
return ok
def parse_jitflags(options):
jitflags = [ [ '-' + flag for flag in flags ]
for flags in options.jitflags.split(',') ]

View File

@ -152,6 +152,7 @@
macro(void0, void0, "(void 0)") \
macro(watch, watch, "watch") \
macro(writable, writable, "writable") \
macro(yield, yield, "yield") \
/* Type names must be contiguous and ordered; see js::TypeName. */ \
macro(undefined, undefined, "undefined") \
macro(object, object, "object") \

View File

@ -72,8 +72,12 @@
macro(protected, protected_, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \
macro(public, public_, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \
macro(static, static_, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \
/* ES5 future reserved keyword in strict mode, keyword in JS1.7 even when not strict. */ \
macro(yield, yield, TOK_YIELD, JSVERSION_1_7) \
/* \
* ES5 future reserved keyword in strict mode, keyword in JS1.7 even when \
* not strict, keyword inside function* in all versions. Punt logic to \
* parser. \
*/ \
macro(yield, yield, TOK_YIELD, JSVERSION_DEFAULT) \
/* Various conditional keywords. */ \
FOR_CONST_KEYWORD(macro) \
FOR_LET_KEYWORD(macro)

View File

@ -118,7 +118,6 @@ if (!isWindows) {
tests.push([ 'bug240933-2.html' , 'bug240933-1-ref.html' ]); // bug 681162
tests.push([ 'bug389321-1.html' , 'bug389321-1-ref.html' ]); // bug 683163
tests.push([ 'bug482484.html' , 'bug482484-ref.html' ]); // bug 688575
tests.push([ 'bug512295-1.html' , 'bug512295-1-ref.html' ]); // bug 681152
tests.push([ 'bug512295-2.html' , 'bug512295-2-ref.html' ]); // bug 681331
tests.push([ 'bug597519-1.html' , 'bug597519-1-ref.html' ]); // bug 680579
tests.push([ 'bug602141-1.html' , 'bug602141-1-ref.html' ]); // bug 681334
@ -142,6 +141,7 @@ if (!isWindows) {
if (!isWindows && !isOSXMtnLion) {
tests.push([ 'bug106855-1.html' , 'bug106855-1-ref.html' ]); // bug 682837
tests.push([ 'bug106855-2.html' , 'bug106855-1-ref.html?' ]); // bug 681138
tests.push([ 'bug512295-1.html' , 'bug512295-1-ref.html' ]); // bug 681152
}
*/

View File

@ -129,8 +129,6 @@ struct CachedOffsetForFrame {
bool mCanCacheFrameOffset; // cached frame offset is valid?
};
static RangeData sEmptyData(nullptr);
// Stack-class to turn on/off selection batching for table selection
class MOZ_STACK_CLASS nsSelectionBatcher MOZ_FINAL
{
@ -4525,7 +4523,8 @@ Selection::GetRangeCount(int32_t* aRangeCount)
NS_IMETHODIMP
Selection::GetRangeAt(int32_t aIndex, nsIDOMRange** aReturn)
{
*aReturn = mRanges.SafeElementAt(aIndex, sEmptyData).mRange;
RangeData empty(nullptr);
*aReturn = mRanges.SafeElementAt(aIndex, empty).mRange;
if (!*aReturn) {
return NS_ERROR_DOM_INDEX_SIZE_ERR;
}
@ -4538,7 +4537,8 @@ Selection::GetRangeAt(int32_t aIndex, nsIDOMRange** aReturn)
nsRange*
Selection::GetRangeAt(int32_t aIndex)
{
return mRanges.SafeElementAt(aIndex, sEmptyData).mRange;
RangeData empty(nullptr);
return mRanges.SafeElementAt(aIndex, empty).mRange;
}
/*

View File

@ -0,0 +1,7 @@
<!DOCTYPE HTML>
<html>
<body>
<img id="img1" src="about:blank" alt="alt1">
<img id="img2" src="about:blank">
</body>
</html>

View File

@ -0,0 +1,15 @@
<!DOCTYPE HTML>
<html class="reftest-wait">
<body>
<img id="img1" src="about:blank">
<img id="img2" src="about:blank" alt="alt2">
<script>
function doTest() {
document.getElementById("img1").setAttribute("alt", "alt1");
document.getElementById("img2").removeAttribute("alt");
document.documentElement.removeAttribute("class");
}
window.addEventListener("load", doTest, false);
</script>
</body>
</html>

View File

@ -0,0 +1,7 @@
<!--quirks mode test-->
<html>
<body>
<img id="img1" src="about:blank" alt="alt1">
<img id="img2" src="about:blank">
</body>
</html>

View File

@ -0,0 +1,15 @@
<!--quirks mode test-->
<html class="reftest-wait">
<body>
<img id="img1" src="about:blank">
<img id="img2" src="about:blank" alt="alt2">
<script>
function doTest() {
document.getElementById("img1").setAttribute("alt", "alt1");
document.getElementById("img2").removeAttribute("alt");
document.documentElement.removeAttribute("class");
}
window.addEventListener("load", doTest, false);
</script>
</body>
</html>

View File

@ -1772,3 +1772,5 @@ test-pref(layout.css.flexbox.enabled,true) == 849407-1.html 849407-1-ref.html
== 883987-1f.html 883987-1-ref.html
== 890495-1.html 890495-1-ref.html
== 894931-1.html 894931-1-ref.html
== 897491-1.html 897491-1-ref.html
== 897491-2.html 897491-2-ref.html

View File

@ -25,8 +25,8 @@ skip-if(B2G) asserts-if(cocoaWidget,0-2) == size-change-1.html size-change-1-ref
== text-ltr-alignment-test.html text-ltr-alignment-ref.html
== text-rtl-alignment-test.html text-rtl-alignment-ref.html
== text-horzline-with-bottom.html text-horzline.html
== text-horzline-with-top.html text-horzline.html
fuzzy-if(B2G&&azureSkiaGL,1,256) == text-horzline-with-bottom.html text-horzline.html
fuzzy-if(B2G&&azureSkiaGL,1,256) == text-horzline-with-top.html text-horzline.html
!= text-big-stroke.html text-blank.html
!= text-big-stroke.html text-big-fill.html

View File

@ -200,7 +200,7 @@ pref(layout.css.masking.enabled,true) fuzzy-if(d2d,1,6400) == mask-type-04.svg m
== nested-viewBox-01.svg pass.svg
== nesting-invalid-01.svg nesting-invalid-01-ref.svg
== non-scaling-stroke-01.svg non-scaling-stroke-01-ref.svg
fuzzy-if(Android,1,99) fuzzy-if(!contentSameGfxBackendAsCanvas,9,99) == non-scaling-stroke-02.svg non-scaling-stroke-02-ref.svg
fuzzy-if(Android||B2G,1,99) fuzzy-if(!contentSameGfxBackendAsCanvas,9,99) == non-scaling-stroke-02.svg non-scaling-stroke-02-ref.svg
== non-scaling-stroke-03.svg non-scaling-stroke-03-ref.svg
== objectBoundingBox-and-clipPath.svg pass.svg
# Bug 588684

View File

@ -538,14 +538,6 @@ function BuildConditionSandbox(aURL) {
sandbox.smallScreen = true;
}
#if REFTEST_B2G
// XXX nsIGfxInfo isn't available in B2G
sandbox.d2d = false;
sandbox.azureQuartz = false;
sandbox.azureSkia = false;
sandbox.azureSkiaGL = false;
sandbox.contentSameGfxBackendAsCanvas = false;
#else
var gfxInfo = (NS_GFXINFO_CONTRACTID in CC) && CC[NS_GFXINFO_CONTRACTID].getService(CI.nsIGfxInfo);
try {
sandbox.d2d = gfxInfo.D2DEnabled;
@ -559,7 +551,6 @@ function BuildConditionSandbox(aURL) {
// true if we are using the same Azure backend for rendering canvas and content
sandbox.contentSameGfxBackendAsCanvas = info.AzureContentBackend == info.AzureCanvasBackend
|| (info.AzureContentBackend == "none" && info.AzureCanvasBackend == "cairo");
#endif
sandbox.layersGPUAccelerated =
gWindowUtils.layerManagerType != "Basic";

View File

@ -38,9 +38,9 @@ public class BrowserDB {
public Cursor filter(ContentResolver cr, CharSequence constraint, int limit);
// This should onlyl return frecent sites, BrowserDB.getTopSites will do the
// This should only return frecent bookmarks, BrowserDB.getTopBookmarks will do the
// work to combine that list with the pinned sites list
public Cursor getTopSites(ContentResolver cr, int limit);
public Cursor getTopBookmarks(ContentResolver cr, int limit);
public void updateVisitedHistory(ContentResolver cr, String uri);
@ -137,12 +137,12 @@ public class BrowserDB {
return sDb.filter(cr, constraint, limit);
}
public static Cursor getTopSites(ContentResolver cr, int limit) {
// Note this is not a single query anymore, but actually returns a mixture of two queries, one for topSites
// and one for pinned sites
Cursor topSites = sDb.getTopSites(cr, limit);
public static Cursor getTopBookmarks(ContentResolver cr, int limit) {
// Note this is not a single query anymore, but actually returns a mixture of two queries,
// one for top bookmarks, and one for pinned sites (which are actually bookmarks as well).
Cursor topBookmarks = sDb.getTopBookmarks(cr, limit);
Cursor pinnedSites = sDb.getPinnedSites(cr, limit);
return new TopSitesCursorWrapper(pinnedSites, topSites, limit);
return new TopSitesCursorWrapper(pinnedSites, topBookmarks, limit);
}
public static void updateVisitedHistory(ContentResolver cr, String uri) {

View File

@ -231,9 +231,13 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
}
@Override
public Cursor getTopSites(ContentResolver cr, int limit) {
// Filter out sites that are pinned
String selection = DBUtils.concatenateWhere("", Combined.URL + " NOT IN (SELECT " +
public Cursor getTopBookmarks(ContentResolver cr, int limit) {
// Only select bookmarks. Unfortunately, we need to query the combined view,
// instead of just the bookmarks table, in order to do the frecency calculation.
String selection = Combined.BOOKMARK_ID + " IS NOT NULL";
// Filter out sites that are pinned.
selection = DBUtils.concatenateWhere(selection, Combined.URL + " NOT IN (SELECT " +
Bookmarks.URL + " FROM bookmarks WHERE " +
DBUtils.qualifyColumn("bookmarks", Bookmarks.PARENT) + " == ? AND " +
DBUtils.qualifyColumn("bookmarks", Bookmarks.IS_DELETED) + " == 0)");

View File

@ -60,13 +60,18 @@ class BookmarksListAdapter extends MultiTypeCursorAdapter {
/**
* Moves to parent folder, if one exists.
*
* @return Whether the adapter successfully moved to a parent folder.
*/
public void moveToParentFolder() {
public boolean moveToParentFolder() {
// If we're already at the root, we can't move to a parent folder
if (mParentStack.size() != 1) {
mParentStack.removeFirst();
refreshCurrentFolder();
if (mParentStack.size() == 1) {
return false;
}
mParentStack.removeFirst();
refreshCurrentFolder();
return true;
}
/**

View File

@ -13,6 +13,7 @@ import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
import android.content.Context;
import android.database.Cursor;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
@ -56,6 +57,17 @@ public class BookmarksListView extends HomeListView
super.onAttachedToWindow();
setOnItemClickListener(this);
setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
// If the user hit the BACK key, try to move to the parent folder.
if (keyCode == KeyEvent.KEYCODE_BACK) {
return getBookmarksListAdapter().moveToParentFolder();
}
return false;
}
});
}
@Override
@ -101,14 +113,7 @@ public class BookmarksListView extends HomeListView
// Absolute position for the adapter.
position -= headerCount;
BookmarksListAdapter adapter;
ListAdapter listAdapter = getAdapter();
if (listAdapter instanceof HeaderViewListAdapter) {
adapter = (BookmarksListAdapter) ((HeaderViewListAdapter) listAdapter).getWrappedAdapter();
} else {
adapter = (BookmarksListAdapter) listAdapter;
}
final BookmarksListAdapter adapter = getBookmarksListAdapter();
if (adapter.isShowingChildFolder()) {
if (position == 0) {
// If we tap on an opened folder, move back to parent folder.
@ -141,4 +146,15 @@ public class BookmarksListView extends HomeListView
getOnUrlOpenListener().onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
}
}
private BookmarksListAdapter getBookmarksListAdapter() {
BookmarksListAdapter adapter;
ListAdapter listAdapter = getAdapter();
if (listAdapter instanceof HeaderViewListAdapter) {
adapter = (BookmarksListAdapter) ((HeaderViewListAdapter) listAdapter).getWrappedAdapter();
} else {
adapter = (BookmarksListAdapter) listAdapter;
}
return adapter;
}
}

View File

@ -149,6 +149,10 @@ public class BookmarksPage extends HomeFragment {
});
mList.setAdapter(mListAdapter);
// Invalidate the cached value that keeps track of whether or
// not desktop bookmarks (or reading list items) exist.
BrowserDB.invalidateCachedState();
// Create callbacks before the initial loader is started.
mLoaderCallbacks = new CursorLoaderCallbacks(activity, getLoaderManager());
mThumbnailsLoaderCallbacks = new ThumbnailsLoaderCallbacks();
@ -362,7 +366,7 @@ public class BookmarksPage extends HomeFragment {
@Override
public Cursor loadCursor() {
final int max = getContext().getResources().getInteger(R.integer.number_of_top_sites);
return BrowserDB.getTopSites(getContext().getContentResolver(), max);
return BrowserDB.getTopBookmarks(getContext().getContentResolver(), max);
}
}

View File

@ -6,6 +6,7 @@
package org.mozilla.gecko.home;
import org.mozilla.gecko.EditBookmarkDialog;
import org.mozilla.gecko.Favicons;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoEvent;
import org.mozilla.gecko.R;
@ -132,14 +133,8 @@ abstract class HomeFragment extends Fragment {
return false;
}
// FIXME: bug 897772
Bitmap bitmap = null;
if (info.favicon != null) {
bitmap = BitmapUtils.decodeByteArray(info.favicon);
}
String shortcutTitle = TextUtils.isEmpty(info.title) ? info.url.replaceAll(REGEX_URL_TO_TITLE, "") : info.title;
GeckoAppShell.createShortcut(shortcutTitle, info.url, bitmap, "");
new AddToLauncherTask(info.url, shortcutTitle).execute();
return true;
}
@ -225,6 +220,35 @@ abstract class HomeFragment extends Fragment {
}
}
private static class AddToLauncherTask extends UiAsyncTask<Void, Void, String> {
private final String mUrl;
private final String mTitle;
public AddToLauncherTask(String url, String title) {
super(ThreadUtils.getBackgroundHandler());
mUrl = url;
mTitle = title;
}
@Override
public String doInBackground(Void... params) {
return Favicons.getInstance().getFaviconUrlForPageUrl(mUrl);
}
@Override
public void onPostExecute(String faviconUrl) {
Favicons.OnFaviconLoadedListener listener = new Favicons.OnFaviconLoadedListener() {
@Override
public void onFaviconLoaded(String url, Bitmap favicon) {
GeckoAppShell.createShortcut(mTitle, mUrl, favicon, "");
}
};
Favicons.getInstance().loadFavicon(mUrl, faviconUrl, 0, listener);
}
}
private static class RemoveBookmarkTask extends UiAsyncTask<Void, Void, Void> {
private final Context mContext;
private final int mId;

View File

@ -113,7 +113,6 @@ public class HomeListView extends ListView
public int bookmarkId;
public int historyId;
public String url;
public byte[] favicon;
public String title;
public int display;
public boolean isFolder;
@ -165,13 +164,6 @@ public class HomeListView extends ListView
historyId = -1;
}
final int faviconCol = cursor.getColumnIndex(Combined.FAVICON);
if (faviconCol != -1) {
favicon = cursor.getBlob(faviconCol);
} else {
favicon = null;
}
// We only have the parent column in cursors from getBookmarksInFolder.
final int parentCol = cursor.getColumnIndex(Bookmarks.PARENT);
if (parentCol != -1) {

View File

@ -587,11 +587,11 @@ AboutReader.prototype = {
let start = 0;
if (host.startsWith("www"))
if (host.startsWith("www."))
start = 4;
else if (host.startsWith("m"))
else if (host.startsWith("m."))
start = 2;
else if (host.startsWith("mobile"))
else if (host.startsWith("mobile."))
start = 7;
return host.substring(start);

View File

@ -38,6 +38,7 @@
#include "nsThreadUtils.h"
#include "nsAutoPtr.h"
#include "nsNetUtil.h"
#include "mozilla/StaticPtr.h"
#ifdef MOZ_PEERCONNECTION
#include "mtransport/runnable_utils.h"
#endif
@ -78,7 +79,7 @@ static bool sctp_initialized;
namespace mozilla {
class DataChannelShutdown;
nsRefPtr<DataChannelShutdown> gDataChannelShutdown;
StaticRefPtr<DataChannelShutdown> gDataChannelShutdown;
class DataChannelShutdown : public nsIObserver
{

Some files were not shown because too many files have changed in this diff Show More