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:LongPress');
Services.obs.removeObserver(this, 'Accessibility:MoveByGranularity'); Services.obs.removeObserver(this, 'Accessibility:MoveByGranularity');
delete this._quicknavModesPref;
delete this._notifyOutputPref;
if (this.doneCallback) { if (this.doneCallback) {
this.doneCallback(); this.doneCallback();
delete this.doneCallback; delete this.doneCallback;
@ -171,6 +174,9 @@ this.AccessFu = {
_enableOrDisable: function _enableOrDisable() { _enableOrDisable: function _enableOrDisable() {
try { try {
if (!this._activatePref) {
return;
}
let activatePref = this._activatePref.value; let activatePref = this._activatePref.value;
if (activatePref == ACCESSFU_ENABLE || if (activatePref == ACCESSFU_ENABLE ||
this._systemPref && activatePref == ACCESSFU_AUTO) 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_INSERTED = Ci.nsIAccessibleEvent.EVENT_TEXT_INSERTED;
const EVENT_TEXT_REMOVED = Ci.nsIAccessibleEvent.EVENT_TEXT_REMOVED; const EVENT_TEXT_REMOVED = Ci.nsIAccessibleEvent.EVENT_TEXT_REMOVED;
const EVENT_FOCUS = Ci.nsIAccessibleEvent.EVENT_FOCUS; 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_INTERNAL_FRAME = Ci.nsIAccessibleRole.ROLE_INTERNAL_FRAME;
const ROLE_DOCUMENT = Ci.nsIAccessibleRole.ROLE_DOCUMENT; const ROLE_DOCUMENT = Ci.nsIAccessibleRole.ROLE_DOCUMENT;
const ROLE_CHROME_WINDOW = Ci.nsIAccessibleRole.ROLE_CHROME_WINDOW; 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'); Cu.import('resource://gre/modules/XPCOMUtils.jsm');
XPCOMUtils.defineLazyModuleGetter(this, 'Services', XPCOMUtils.defineLazyModuleGetter(this, 'Services',
@ -140,7 +145,11 @@ this.EventManager.prototype = {
// Don't bother with non-content events in firefox. // Don't bother with non-content events in firefox.
if (Utils.MozBuildApp == 'browser' && if (Utils.MozBuildApp == 'browser' &&
aEvent.eventType != EVENT_VIRTUALCURSOR_CHANGED && 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; return;
} }
@ -219,28 +228,47 @@ this.EventManager.prototype = {
this.editState = editState; this.editState = editState;
break; 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_INSERTED:
case EVENT_TEXT_REMOVED: case EVENT_TEXT_REMOVED:
{ {
if (aEvent.isFromUserInput) { let {liveRegion, isPolite} = this._handleLiveRegion(aEvent,
// XXX support live regions as well. ['text', 'all']);
let event = aEvent.QueryInterface(Ci.nsIAccessibleTextChangeEvent); if (aEvent.isFromUserInput || liveRegion) {
let isInserted = event.isInserted; // Handle all text mutations coming from the user or if they happen
let txtIface = aEvent.accessible.QueryInterface(Ci.nsIAccessibleText); // on a live region.
this._handleText(aEvent, liveRegion, isPolite);
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));
} }
break; 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) { present: function present(aPresentationData) {
this.sendMsgFunc("AccessFu:Present", aPresentationData); this.sendMsgFunc("AccessFu:Present", aPresentationData);
}, },

View File

@ -116,7 +116,6 @@ this.OutputGenerator = {
let extState = {}; let extState = {};
aAccessible.getState(state, extState); aAccessible.getState(state, extState);
let states = {base: state.value, ext: extState.value}; let states = {base: state.value, ext: extState.value};
return func.apply(this, [aAccessible, roleString, states, flags, aContext]); return func.apply(this, [aAccessible, roleString, states, flags, aContext]);
}, },
@ -418,6 +417,15 @@ this.UtteranceGenerator = {
return [gStringBundle.GetStringFromName(this.gActionMap[aActionName])]; 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) { genForAnnouncement: function genForAnnouncement(aAnnouncement) {
try { try {
return [gStringBundle.GetStringFromName(aAnnouncement)]; return [gStringBundle.GetStringFromName(aAnnouncement)];

View File

@ -102,7 +102,19 @@ Presenter.prototype = {
/** /**
* Announce something. Typically an app state change. * 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 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. // but there really isn't a point here.
return [p.announce(UtteranceGenerator.genForAnnouncement(aAnnouncement)[0]) return [p.announce(UtteranceGenerator.genForAnnouncement(aAnnouncement)[0])
for each (p in this.presenters)]; 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; 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) { getLandmarkName: function getLandmarkName(aAccessible) {
const landmarks = [ const landmarks = [
'banner', 'banner',
@ -281,11 +290,7 @@ this.Utils = {
} }
// Looking up a role that would match a landmark. // Looking up a role that would match a landmark.
for (let landmark of landmarks) { return this.matchAttributeValue(roles, landmarks);
if (roles.indexOf(landmark) > -1) {
return landmark;
}
}
} }
}; };
@ -422,12 +427,15 @@ this.Logger = {
* for a given accessible and its relationship with another accessible. * for a given accessible and its relationship with another accessible.
*/ */
this.PivotContext = function PivotContext(aAccessible, aOldAccessible, this.PivotContext = function PivotContext(aAccessible, aOldAccessible,
aStartOffset, aEndOffset) { aStartOffset, aEndOffset, aIgnoreAncestry = false,
aIncludeInvisible = false) {
this._accessible = aAccessible; this._accessible = aAccessible;
this._oldAccessible = this._oldAccessible =
this._isDefunct(aOldAccessible) ? null : aOldAccessible; this._isDefunct(aOldAccessible) ? null : aOldAccessible;
this.startOffset = aStartOffset; this.startOffset = aStartOffset;
this.endOffset = aEndOffset; this.endOffset = aEndOffset;
this._ignoreAncestry = aIgnoreAncestry;
this._includeInvisible = aIncludeInvisible;
} }
PivotContext.prototype = { PivotContext.prototype = {
@ -497,7 +505,7 @@ PivotContext.prototype = {
*/ */
get oldAncestry() { get oldAncestry() {
if (!this._oldAncestry) { if (!this._oldAncestry) {
if (!this._oldAccessible) { if (!this._oldAccessible || this._ignoreAncestry) {
this._oldAncestry = []; this._oldAncestry = [];
} else { } else {
this._oldAncestry = this._getAncestry(this._oldAccessible); this._oldAncestry = this._getAncestry(this._oldAccessible);
@ -512,7 +520,8 @@ PivotContext.prototype = {
*/ */
get currentAncestry() { get currentAncestry() {
if (!this._currentAncestry) { if (!this._currentAncestry) {
this._currentAncestry = this._getAncestry(this._accessible); this._currentAncestry = this._ignoreAncestry ? [] :
this._getAncestry(this._accessible);
} }
return this._currentAncestry; return this._currentAncestry;
}, },
@ -524,7 +533,7 @@ PivotContext.prototype = {
*/ */
get newAncestry() { get newAncestry() {
if (!this._newAncestry) { if (!this._newAncestry) {
this._newAncestry = [currentAncestor for ( this._newAncestry = this._ignoreAncestry ? [] : [currentAncestor for (
[index, currentAncestor] of Iterator(this.currentAncestry)) if ( [index, currentAncestor] of Iterator(this.currentAncestry)) if (
currentAncestor !== this.oldAncestry[index])]; currentAncestor !== this.oldAncestry[index])];
} }
@ -543,9 +552,14 @@ PivotContext.prototype = {
} }
let child = aAccessible.firstChild; let child = aAccessible.firstChild;
while (child) { while (child) {
let state = {}; let include;
child.getState(state, {}); if (this._includeInvisible) {
if (!(state.value & Ci.nsIAccessibleStates.STATE_INVISIBLE)) { include = true;
} else {
let [state,] = Utils.getStates(child);
include = !(state.value & Ci.nsIAccessibleStates.STATE_INVISIBLE);
}
if (include) {
if (aPreorder) { if (aPreorder) {
yield child; yield child;
[yield node for (node of this._traverse(child, aPreorder, aStop))]; [yield node for (node of this._traverse(child, aPreorder, aStop))];
@ -703,7 +717,6 @@ PrefCache.prototype = {
if (!this.type) { if (!this.type) {
this.type = aBranch.getPrefType(this.name); this.type = aBranch.getPrefType(this.name);
} }
switch (this.type) { switch (this.type) {
case Ci.nsIPrefBranch.PREF_STRING: case Ci.nsIPrefBranch.PREF_STRING:
return aBranch.getCharPref(this.name); return aBranch.getCharPref(this.name);

View File

@ -18,6 +18,7 @@ test_alive.html \
test_braille.html \ test_braille.html \
test_explicit_names.html \ test_explicit_names.html \
test_landmarks.html \ test_landmarks.html \
test_live_regions.html \
test_tables.html \ test_tables.html \
test_utterance_order.html \ test_utterance_order.html \
$(NULL) $(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: false,
waitForExplicitFinish: function AccessFuTest_waitForExplicitFinish() { waitForExplicitFinish: function AccessFuTest_waitForExplicitFinish() {
@ -38,6 +100,7 @@ var AccessFuTest = {
SimpleTest.finish(); SimpleTest.finish();
}; };
// Tear down accessibility and make AccessFu stop. // Tear down accessibility and make AccessFu stop.
SpecialPowers.setIntPref("accessibility.accessfu.notify_output", 0);
SpecialPowers.setIntPref("accessibility.accessfu.activate", 0); SpecialPowers.setIntPref("accessibility.accessfu.activate", 0);
}, },
@ -87,5 +150,6 @@ var AccessFuTest = {
// Invoke the whole thing. // Invoke the whole thing.
SpecialPowers.setIntPref("accessibility.accessfu.activate", 1); SpecialPowers.setIntPref("accessibility.accessfu.activate", 1);
SpecialPowers.setIntPref("accessibility.accessfu.notify_output", 1);
} }
}; };

View File

@ -18,52 +18,38 @@
AccessFuTest.nextTest(); AccessFuTest.nextTest();
} }
function makeEventManagerListener(waitForMessage, callback) { // Listen for 'EventManager.stop' and enable AccessFu again.
return { function onStop() {
observe: function observe(aMessage) { ok(true, "EventManager was stopped.");
// Ignore unexpected messages. isnot(AccessFu._enabled, true, "AccessFu was disabled.");
if (!(aMessage instanceof Components.interfaces.nsIConsoleMessage)) { AccessFuTest.once_log("EventManager.start", AccessFuTest.nextTest);
return; AccessFu._enable();
}
if (aMessage.message.indexOf(waitForMessage) < 0) {
return;
}
Services.console.unregisterListener(this);
callback();
}
};
} }
function testEventManagerStartStop() { // Make sure EventManager is started again.
// Firs listen for initial 'EventManager.start' and disable AccessFu. function onFinalStart() {
var initialStartListener = makeEventManagerListener("EventManager.start", ok(true, "EventManager was started again.");
function () { ok(AccessFu._enabled, "AccessFu was enabled again.");
ok(true, "EventManager was started."); AccessFuTest.finish();
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();
});
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() { function doTest() {
AccessFuTest.addFunc(confirmAccessFuStart); AccessFuTest.addFunc(confirmAccessFuStart);
AccessFuTest.addFunc(testEventManagerStartStop); AccessFuTest.addFunc(init);
AccessFuTest.addFunc(onInitialStart);
AccessFuTest.addFunc(onStop);
AccessFuTest.addFunc(onFinalStart);
AccessFuTest.waitForExplicitFinish(); AccessFuTest.waitForExplicitFinish();
AccessFuTest.runTests(); // Will call SimpleTest.finish(); 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 #if MOZ_WINCONSOLE
fwprintf_s(stderr, wide_msg); fwprintf_s(stderr, wide_msg);
#else #else
MessageBoxW(NULL, wide_msg, L"Firefox", MB_OK MessageBoxW(nullptr, wide_msg, L"Firefox", MB_OK
| MB_ICONERROR | MB_ICONERROR
| MB_SETFOREGROUND); | MB_SETFOREGROUND);
#endif #endif
#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 // Check for a metro test harness command line args file
HANDLE hTestFile = CreateFileA(path.get(), HANDLE hTestFile = CreateFileA(path.get(),
GENERIC_READ, GENERIC_READ,
0, NULL, OPEN_EXISTING, 0, nullptr, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, FILE_ATTRIBUTE_NORMAL,
NULL); nullptr);
if (hTestFile != INVALID_HANDLE_VALUE) { if (hTestFile != INVALID_HANDLE_VALUE) {
// Typical test harness command line args string is around 100 bytes. // Typical test harness command line args string is around 100 bytes.
char buffer[1024]; char buffer[1024];
memset(buffer, 0, sizeof(buffer)); memset(buffer, 0, sizeof(buffer));
DWORD bytesRead = 0; DWORD bytesRead = 0;
if (!ReadFile(hTestFile, (VOID*)buffer, sizeof(buffer)-1, if (!ReadFile(hTestFile, (VOID*)buffer, sizeof(buffer)-1,
&bytesRead, NULL) || !bytesRead) { &bytesRead, nullptr) || !bytesRead) {
CloseHandle(hTestFile); CloseHandle(hTestFile);
printf("failed to read test file '%s'", testFile); printf("failed to read test file '%s'", testFile);
return -1; return -1;
@ -351,7 +351,7 @@ static int do_main(int argc, char* argv[], nsIFile *xreDirectory)
char* ptr = buffer; char* ptr = buffer;
newArgv[0] = ptr; newArgv[0] = ptr;
while (*ptr != NULL && while (*ptr != '\0' &&
(ptr - buffer) < sizeof(buffer) && (ptr - buffer) < sizeof(buffer) &&
newArgc < ARRAYSIZE(newArgv)) { newArgc < ARRAYSIZE(newArgv)) {
if (isspace(*ptr)) { if (isspace(*ptr)) {
@ -513,13 +513,13 @@ InitXPCOMGlue(const char *argv0, nsIFile **xreDirectory)
} }
if (absfwurl) { if (absfwurl) {
CFURLRef xulurl = CFURLRef xulurl =
CFURLCreateCopyAppendingPathComponent(NULL, absfwurl, CFURLCreateCopyAppendingPathComponent(nullptr, absfwurl,
CFSTR("XUL.framework"), CFSTR("XUL.framework"),
true); true);
if (xulurl) { if (xulurl) {
CFURLRef xpcomurl = CFURLRef xpcomurl =
CFURLCreateCopyAppendingPathComponent(NULL, xulurl, CFURLCreateCopyAppendingPathComponent(nullptr, xulurl,
CFSTR("libxpcom.dylib"), CFSTR("libxpcom.dylib"),
false); false);

View File

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

View File

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

View File

@ -195,21 +195,7 @@ let gGestureSupport = {
aEvent.allowedDirections |= isLTR ? aEvent.DIRECTION_RIGHT : aEvent.allowedDirections |= isLTR ? aEvent.DIRECTION_RIGHT :
aEvent.DIRECTION_LEFT; aEvent.DIRECTION_LEFT;
let isVerticalSwipe = false; gHistorySwipeAnimation.startAnimation();
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);
this._doUpdate = function GS__doUpdate(aEvent) { this._doUpdate = function GS__doUpdate(aEvent) {
gHistorySwipeAnimation.updateAnimation(aEvent.delta); gHistorySwipeAnimation.updateAnimation(aEvent.delta);
@ -563,13 +549,10 @@ let gHistorySwipeAnimation = {
this.isLTR = document.documentElement.mozMatchesSelector( this.isLTR = document.documentElement.mozMatchesSelector(
":-moz-locale-dir(ltr)"); ":-moz-locale-dir(ltr)");
this._trackedSnapshots = []; this._trackedSnapshots = [];
this._startingIndex = -1;
this._historyIndex = -1; this._historyIndex = -1;
this._boxWidth = -1; this._boxWidth = -1;
this._boxHeight = -1;
this._maxSnapshots = this._getMaxSnapshots(); this._maxSnapshots = this._getMaxSnapshots();
this._lastSwipeDir = ""; this._lastSwipeDir = "";
this._isVerticalSwipe = false;
// We only want to activate history swipe animations if we store snapshots. // We only want to activate history swipe animations if we store snapshots.
// If we don't store any, we handle horizontal swipes without animations. // 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("pagehide", this, false);
gBrowser.addEventListener("pageshow", this, false); gBrowser.addEventListener("pageshow", this, false);
gBrowser.addEventListener("popstate", this, false); gBrowser.addEventListener("popstate", this, false);
gBrowser.addEventListener("DOMModalDialogClosed", this, false);
gBrowser.tabContainer.addEventListener("TabClose", this, false); gBrowser.tabContainer.addEventListener("TabClose", this, false);
} }
}, },
@ -590,7 +572,6 @@ let gHistorySwipeAnimation = {
gBrowser.removeEventListener("pagehide", this, false); gBrowser.removeEventListener("pagehide", this, false);
gBrowser.removeEventListener("pageshow", this, false); gBrowser.removeEventListener("pageshow", this, false);
gBrowser.removeEventListener("popstate", this, false); gBrowser.removeEventListener("popstate", this, false);
gBrowser.removeEventListener("DOMModalDialogClosed", this, false);
gBrowser.tabContainer.removeEventListener("TabClose", this, false); gBrowser.tabContainer.removeEventListener("TabClose", this, false);
this.active = false; this.active = false;
@ -600,32 +581,17 @@ let gHistorySwipeAnimation = {
/** /**
* Starts the swipe animation and handles fast swiping (i.e. a swipe animation * Starts the swipe animation and handles fast swiping (i.e. a swipe animation
* is already in progress when a new one is initiated). * 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) { startAnimation: function HSA_startAnimation() {
this._isVerticalSwipe = aIsVerticalSwipe;
if (this.isAnimationRunning()) { if (this.isAnimationRunning()) {
// If this is a horizontal scroll, or if this is a vertical scroll that gBrowser.stop();
// was started while a horizontal scroll was still running, handle it as this._lastSwipeDir = "RELOAD"; // just ensure that != ""
// as a fast swipe. In the case of the latter scenario, this allows us to this._canGoBack = this.canGoBack();
// start the vertical animation without first loading the final page, or this._canGoForward = this.canGoForward();
// taking another snapshot. If vertical scrolls are initiated repeatedly this._handleFastSwiping();
// 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();
}
} }
else { else {
this._startingIndex = gBrowser.webNavigation.sessionHistory.index; this._historyIndex = gBrowser.webNavigation.sessionHistory.index;
this._historyIndex = this._startingIndex;
this._canGoBack = this.canGoBack(); this._canGoBack = this.canGoBack();
this._canGoForward = this.canGoForward(); this._canGoForward = this.canGoForward();
if (this.active) { if (this.active) {
@ -656,29 +622,20 @@ let gHistorySwipeAnimation = {
if (!this.isAnimationRunning()) if (!this.isAnimationRunning())
return; return;
// We use the following value to decrease the bounce effect when scrolling if ((aVal >= 0 && this.isLTR) ||
// to the top or bottom of the page, or when swiping back/forward past the (aVal <= 0 && !this.isLTR)) {
// browsing history. This value was determined experimentally. if (aVal > 1)
let dampValue = 4; aVal = 1; // Cap value to avoid sliding the page further than allowed.
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 (this._canGoBack) if (this._canGoBack)
this._prevBox.collapsed = false; this._prevBox.collapsed = false;
else { else
tempDampValue = dampValue;
this._prevBox.collapsed = true; this._prevBox.collapsed = true;
}
// The current page is pushed to the right (LTR) or left (RTL), // The current page is pushed to the right (LTR) or left (RTL),
// the intention is to go back. // the intention is to go back.
// If there is a page to go back to, it should show in the background. // 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. // The forward page should be pushed offscreen all the way to the right.
this._positionBox(this._nextBox, 1); this._positionBox(this._nextBox, 1);
@ -694,14 +651,13 @@ let gHistorySwipeAnimation = {
// For the backdrop to be visible in that case, the previous page needs // For the backdrop to be visible in that case, the previous page needs
// to be hidden (if it exists). // to be hidden (if it exists).
if (this._canGoForward) { if (this._canGoForward) {
this._nextBox.collapsed = false;
let offset = this.isLTR ? 1 : -1; let offset = this.isLTR ? 1 : -1;
this._positionBox(this._curBox, 0); this._positionBox(this._curBox, 0);
this._positionBox(this._nextBox, offset + aVal); this._positionBox(this._nextBox, offset + aVal); // aVal is negative
} }
else { else {
this._prevBox.collapsed = true; 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); let browser = gBrowser.getBrowserForTab(aEvent.target);
this._removeTrackedSnapshot(-1, browser); this._removeTrackedSnapshot(-1, browser);
break; break;
case "DOMModalDialogClosed":
this.stopAnimation();
break;
case "pageshow": case "pageshow":
case "popstate": case "popstate":
if (aEvent.target != gBrowser.selectedBrowser.contentDocument) if (this.isAnimationRunning()) {
break; if (aEvent.target != gBrowser.selectedBrowser.contentDocument)
this.stopAnimation(); break;
this.stopAnimation();
}
this._historyIndex = gBrowser.webNavigation.sessionHistory.index; this._historyIndex = gBrowser.webNavigation.sessionHistory.index;
break; break;
case "pagehide": case "pagehide":
@ -793,7 +748,7 @@ let gHistorySwipeAnimation = {
* any. This will also result in the animation overlay to be torn down. * any. This will also result in the animation overlay to be torn down.
*/ */
swipeEndEventReceived: function HSA_swipeEndEventReceived() { swipeEndEventReceived: function HSA_swipeEndEventReceived() {
if (this._lastSwipeDir != "" && this._historyIndex != this._startingIndex) if (this._lastSwipeDir != "")
this._navigateToHistoryIndex(); this._navigateToHistoryIndex();
else else
this.stopAnimation(); this.stopAnimation();
@ -821,10 +776,9 @@ let gHistorySwipeAnimation = {
* |this|. * |this|.
*/ */
_navigateToHistoryIndex: function HSA__navigateToHistoryIndex() { _navigateToHistoryIndex: function HSA__navigateToHistoryIndex() {
if (this._doesIndexExistInHistory(this._historyIndex)) if (this._doesIndexExistInHistory(this._historyIndex)) {
gBrowser.webNavigation.gotoIndex(this._historyIndex); gBrowser.webNavigation.gotoIndex(this._historyIndex);
else }
this.stopAnimation();
}, },
/** /**
@ -870,9 +824,7 @@ let gHistorySwipeAnimation = {
"box"); "box");
this._container.appendChild(this._nextBox); this._container.appendChild(this._nextBox);
// Cache width and height. this._boxWidth = this._curBox.getBoundingClientRect().width; // cache width
this._boxWidth = this._curBox.getBoundingClientRect().width;
this._boxHeight = this._curBox.getBoundingClientRect().height;
}, },
/** /**
@ -886,7 +838,6 @@ let gHistorySwipeAnimation = {
this._container.parentNode.removeChild(this._container); this._container.parentNode.removeChild(this._container);
this._container = null; this._container = null;
this._boxWidth = -1; this._boxWidth = -1;
this._boxHeight = -1;
}, },
/** /**
@ -914,14 +865,7 @@ let gHistorySwipeAnimation = {
* The position (in X coordinates) to move the box element to. * The position (in X coordinates) to move the box element to.
*/ */
_positionBox: function HSA__positionBox(aBox, aPosition) { _positionBox: function HSA__positionBox(aBox, aPosition) {
let transform = ""; aBox.style.transform = "translateX(" + this._boxWidth * aPosition + "px)";
if (this._isVerticalSwipe)
transform = "translateY(" + this._boxHeight * aPosition + "px)";
else
transform = "translateX(" + this._boxWidth * aPosition + "px)";
aBox.style.transform = transform;
}, },
/** /**
@ -1060,17 +1004,12 @@ let gHistorySwipeAnimation = {
return aBlob; return aBlob;
let img = new Image(); let img = new Image();
let url = ""; let url = URL.createObjectURL(aBlob);
try { img.onload = function() {
url = URL.createObjectURL(aBlob); URL.revokeObjectURL(url);
img.onload = function() { };
URL.revokeObjectURL(url); img.src = url;
}; return img;
}
finally {
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:frameworker-error", false);
Services.obs.addObserver(this, "social:provider-set", false); Services.obs.addObserver(this, "social:provider-set", false);
Services.obs.addObserver(this, "social:providers-changed", 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.sidebar.open", this, false);
Services.prefs.addObserver("social.toast-notifications.enabled", 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:frameworker-error");
Services.obs.removeObserver(this, "social:provider-set"); Services.obs.removeObserver(this, "social:provider-set");
Services.obs.removeObserver(this, "social:providers-changed"); 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.sidebar.open", this);
Services.prefs.removeObserver("social.toast-notifications.enabled", this); Services.prefs.removeObserver("social.toast-notifications.enabled", this);
@ -74,6 +76,16 @@ SocialUI = {
// manually :( // manually :(
try { try {
switch (topic) { 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": case "social:provider-set":
// Social.provider has changed (possibly to null), update any state // Social.provider has changed (possibly to null), update any state
// which depends on it. // which depends on it.
@ -142,7 +154,7 @@ SocialUI = {
// Miscellaneous helpers // Miscellaneous helpers
showProfile: function SocialUI_showProfile() { showProfile: function SocialUI_showProfile() {
if (Social.haveLoggedInUser()) if (Social.provider.haveLoggedInUser())
openUILinkIn(Social.provider.profile.profileURL, "tab"); openUILinkIn(Social.provider.profile.profileURL, "tab");
else { else {
// XXX Bug 789585 will implement an API for provider-specified login pages. // XXX Bug 789585 will implement an API for provider-specified login pages.
@ -976,22 +988,20 @@ SocialToolbar = {
let toggleNotificationsCommand = document.getElementById("Social:ToggleNotifications"); let toggleNotificationsCommand = document.getElementById("Social:ToggleNotifications");
toggleNotificationsCommand.setAttribute("hidden", !socialEnabled); toggleNotificationsCommand.setAttribute("hidden", !socialEnabled);
if (!Social.haveLoggedInUser() || !socialEnabled) { let parent = document.getElementById("social-notification-panel");
let parent = document.getElementById("social-notification-panel"); while (parent.hasChildNodes()) {
while (parent.hasChildNodes()) { let frame = parent.firstChild;
let frame = parent.firstChild; SharedFrame.forgetGroup(frame.id);
SharedFrame.forgetGroup(frame.id); parent.removeChild(frame);
parent.removeChild(frame); }
}
let tbi = document.getElementById("social-toolbar-item"); let tbi = document.getElementById("social-toolbar-item");
if (tbi) { if (tbi) {
// SocialMark is the last button allways // SocialMark is the last button allways
let next = SocialMark.button.previousSibling; let next = SocialMark.button.previousSibling;
while (next != this.button) { while (next != this.button) {
tbi.removeChild(next); tbi.removeChild(next);
next = SocialMark.button.previousSibling; next = SocialMark.button.previousSibling;
}
} }
} }
}, },
@ -1035,7 +1045,7 @@ SocialToolbar = {
// provider.profile == undefined means no response yet from the provider // provider.profile == undefined means no response yet from the provider
// to tell us whether the user is logged in or not. // to tell us whether the user is logged in or not.
if (!SocialUI.enabled || 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 // Either no enabled provider, or there is a provider and it has
// responded with a profile and the user isn't loggedin. The icons // responded with a profile and the user isn't loggedin. The icons
// etc have already been removed by updateButtonHiddenState, so we want // etc have already been removed by updateButtonHiddenState, so we want

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -71,47 +71,4 @@ function runTests() {
yield simulateDrop(0, 4); yield simulateDrop(0, 4);
checkGrid("3,1p,2p,4,0p,5p,6,7,8"); 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_chatwindow_resize.js \
browser_social_chatwindowfocus.js \ browser_social_chatwindowfocus.js \
browser_social_multiprovider.js \ browser_social_multiprovider.js \
browser_social_multiworker.js \
browser_social_errorPage.js \ browser_social_errorPage.js \
browser_social_window.js \ browser_social_window.js \
social_activate.html \ social_activate.html \

View File

@ -2,17 +2,71 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * 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() { function test() {
requestLongerTimeout(2); // only debug builds seem to need more time... requestLongerTimeout(2); // only debug builds seem to need more time...
waitForExplicitFinish(); 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 oldwidth = window.outerWidth; // we futz with these, so we restore them
let oldleft = window.screenX; let oldleft = window.screenX;
window.moveTo(0, window.screenY) window.moveTo(0, window.screenY)
@ -21,7 +75,7 @@ function test() {
ok(chats.children.length == 0, "no chatty children left behind"); ok(chats.children.length == 0, "no chatty children left behind");
cb(); cb();
}; };
runSocialTestWithProvider(manifest, function (finishcb) { runSocialTestWithProvider(manifests, function (finishcb) {
runSocialTests(tests, undefined, postSubTest, function() { runSocialTests(tests, undefined, postSubTest, function() {
window.moveTo(oldleft, window.screenY) window.moveTo(oldleft, window.screenY)
window.resizeTo(oldwidth, window.outerHeight); window.resizeTo(oldwidth, window.outerHeight);
@ -147,7 +201,7 @@ var tests = {
maybeOpenAnother(); maybeOpenAnother();
}, },
testWorkerChatWindow: function(next) { 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 chats = document.getElementById("pinnedchats");
let port = Social.provider.getWorkerPort(); let port = Social.provider.getWorkerPort();
ok(port, "provider has a port"); ok(port, "provider has a port");
@ -384,7 +438,7 @@ var tests = {
testSecondTopLevelWindow: function(next) { testSecondTopLevelWindow: function(next) {
// Bug 817782 - check chats work in new top-level windows. // 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 port = Social.provider.getWorkerPort();
let secondWindow; let secondWindow;
port.onmessage = function(e) { port.onmessage = function(e) {
@ -407,23 +461,9 @@ var tests = {
testChatWindowChooser: function(next) { testChatWindowChooser: function(next) {
// Tests that when a worker creates a chat, it is opened in the correct // Tests that when a worker creates a chat, it is opened in the correct
// window. // 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) // open a chat (it will open in the main window)
ok(!window.SocialChatBar.hasChats, "first window should start with no chats"); 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"); ok(window.SocialChatBar.hasChats, "first window has the chat");
// create a second window - this will be the "most recent" and will // create a second window - this will be the "most recent" and will
// therefore be the window that hosts the new chat (see bug 835111) // therefore be the window that hosts the new chat (see bug 835111)
@ -431,27 +471,55 @@ var tests = {
secondWindow.addEventListener("load", function loadListener() { secondWindow.addEventListener("load", function loadListener() {
secondWindow.removeEventListener("load", loadListener); secondWindow.removeEventListener("load", loadListener);
ok(!secondWindow.SocialChatBar.hasChats, "second window has no chats"); ok(!secondWindow.SocialChatBar.hasChats, "second window has no chats");
openChat(function() { openChat(Social.provider, function() {
ok(secondWindow.SocialChatBar.hasChats, "second window now has chats"); ok(secondWindow.SocialChatBar.hasChats, "second window now has chats");
is(window.SocialChatBar.chatbar.childElementCount, 1, "first window still has 1 chat"); is(window.SocialChatBar.chatbar.childElementCount, 1, "first window still has 1 chat");
window.SocialChatBar.chatbar.removeAll(); window.SocialChatBar.chatbar.removeAll();
// now open another chat - it should still open in the second. // 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(!window.SocialChatBar.hasChats, "first window has no chats");
ok(secondWindow.SocialChatBar.hasChats, "second window has a chat"); ok(secondWindow.SocialChatBar.hasChats, "second window has a chat");
secondWindow.close(); secondWindow.close();
port.close();
next(); 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 // XXX - note this must be the last test until we restore the login state
// between tests... // between tests...
testCloseOnLogout: function(next) { 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(); let port = Social.provider.getWorkerPort();
ok(port, "provider has a port"); ok(port, "provider has a port");
let opened = false; 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) { function makeChat(mode, uniqueid, cb) {
info("making a chat window '" + uniqueid +"'"); info("making a chat window '" + uniqueid +"'");
const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html";
let provider = Social.provider; 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) { let isOpened = window.SocialChatBar.openChat(provider, chatUrl + "?id=" + uniqueid, function(chat) {
info("chat window has opened"); info("chat window has opened");
// we can't callback immediately or we might close the chat during // 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 #endif
static const mozilla::Module::CIDEntry kBrowserCIDs[] = { static const mozilla::Module::CIDEntry kBrowserCIDs[] = {
{ &kNS_BROWSERDIRECTORYPROVIDER_CID, false, NULL, DirectoryProviderConstructor }, { &kNS_BROWSERDIRECTORYPROVIDER_CID, false, nullptr, DirectoryProviderConstructor },
#if defined(XP_WIN) #if defined(XP_WIN)
{ &kNS_SHELLSERVICE_CID, false, NULL, nsWindowsShellServiceConstructor }, { &kNS_SHELLSERVICE_CID, false, nullptr, nsWindowsShellServiceConstructor },
#elif defined(MOZ_WIDGET_GTK) #elif defined(MOZ_WIDGET_GTK)
{ &kNS_SHELLSERVICE_CID, false, NULL, nsGNOMEShellServiceConstructor }, { &kNS_SHELLSERVICE_CID, false, nullptr, nsGNOMEShellServiceConstructor },
#endif #endif
{ &kNS_FEEDSNIFFER_CID, false, NULL, nsFeedSnifferConstructor }, { &kNS_FEEDSNIFFER_CID, false, nullptr, nsFeedSnifferConstructor },
{ &kNS_BROWSER_ABOUT_REDIRECTOR_CID, false, NULL, AboutRedirector::Create }, { &kNS_BROWSER_ABOUT_REDIRECTOR_CID, false, nullptr, AboutRedirector::Create },
#if defined(XP_WIN) #if defined(XP_WIN)
{ &kNS_WINIEHISTORYENUMERATOR_CID, false, NULL, nsIEHistoryEnumeratorConstructor }, { &kNS_WINIEHISTORYENUMERATOR_CID, false, nullptr, nsIEHistoryEnumeratorConstructor },
#elif defined(XP_MACOSX) #elif defined(XP_MACOSX)
{ &kNS_SHELLSERVICE_CID, false, NULL, nsMacShellServiceConstructor }, { &kNS_SHELLSERVICE_CID, false, nullptr, nsMacShellServiceConstructor },
#endif #endif
{ NULL } { nullptr }
}; };
static const mozilla::Module::ContractIDEntry kBrowserContracts[] = { static const mozilla::Module::ContractIDEntry kBrowserContracts[] = {
@ -113,13 +113,13 @@ static const mozilla::Module::ContractIDEntry kBrowserContracts[] = {
#elif defined(XP_MACOSX) #elif defined(XP_MACOSX)
{ NS_SHELLSERVICE_CONTRACTID, &kNS_SHELLSERVICE_CID }, { NS_SHELLSERVICE_CONTRACTID, &kNS_SHELLSERVICE_CID },
#endif #endif
{ NULL } { nullptr }
}; };
static const mozilla::Module::CategoryEntry kBrowserCategories[] = { static const mozilla::Module::CategoryEntry kBrowserCategories[] = {
{ XPCOM_DIRECTORY_PROVIDER_CATEGORY, "browser-directory-provider", NS_BROWSERDIRECTORYPROVIDER_CONTRACTID }, { XPCOM_DIRECTORY_PROVIDER_CATEGORY, "browser-directory-provider", NS_BROWSERDIRECTORYPROVIDER_CONTRACTID },
{ NS_CONTENT_SNIFFER_CATEGORY, "Feed Sniffer", NS_FEEDSNIFFER_CONTRACTID }, { NS_CONTENT_SNIFFER_CATEGORY, "Feed Sniffer", NS_FEEDSNIFFER_CONTRACTID },
{ NULL } { nullptr }
}; };
static const mozilla::Module kBrowserModule = { static const mozilla::Module kBrowserModule = {

View File

@ -7,39 +7,33 @@
* Make sure the downloads panel can display items in the right order and * Make sure the downloads panel can display items in the right order and
* contains the expected data. * contains the expected data.
*/ */
function gen_test() function test_task()
{ {
// Display one of each download state. // Display one of each download state.
const DownloadData = [ const DownloadData = [
{ endTime: 1180493839859239, state: nsIDM.DOWNLOAD_NOTSTARTED }, { state: nsIDM.DOWNLOAD_NOTSTARTED },
{ endTime: 1180493839859238, state: nsIDM.DOWNLOAD_DOWNLOADING }, { state: nsIDM.DOWNLOAD_PAUSED },
{ endTime: 1180493839859237, state: nsIDM.DOWNLOAD_PAUSED }, { state: nsIDM.DOWNLOAD_FINISHED },
{ endTime: 1180493839859236, state: nsIDM.DOWNLOAD_SCANNING }, { state: nsIDM.DOWNLOAD_FAILED },
{ endTime: 1180493839859235, state: nsIDM.DOWNLOAD_QUEUED }, { state: nsIDM.DOWNLOAD_CANCELED },
{ 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 },
]; ];
// For testing purposes, show all the download items at once.
var originalCountLimit = DownloadsView.kItemCountLimit;
DownloadsView.kItemCountLimit = DownloadData.length;
registerCleanupFunction(function () {
DownloadsView.kItemCountLimit = originalCountLimit;
});
try { try {
// Ensure that state is reset in case previous tests didn't finish. // 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. // 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. // 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. // Test item data and count. This also tests the ordering of the display.
let richlistbox = document.getElementById("downloadsListBox"); let richlistbox = document.getElementById("downloadsListBox");
@ -47,16 +41,14 @@ function gen_test()
is(richlistbox.children.length, DownloadData.length, is(richlistbox.children.length, DownloadData.length,
"There is the correct number of richlistitems"); "There is the correct number of richlistitems");
*/ */
for (let i = 0; i < richlistbox.children.length; i++) { let itemCount = richlistbox.children.length;
let element = richlistbox.children[i]; for (let i = 0; i < itemCount; i++) {
let element = richlistbox.children[itemCount - i - 1];
let dataItem = new DownloadsViewItemController(element).dataItem; 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.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 { } finally {
// Clean up when the test finishes. // 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 * download it notices. All subsequent downloads, even across sessions, should
* not open the panel automatically. * not open the panel automatically.
*/ */
function gen_test() function test_task()
{ {
try { try {
// Ensure that state is reset in case previous tests didn't finish. // 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 // With this set to false, we should automatically open the panel the first
// the first time a download is started. // time a download is started.
DownloadsCommon.getData(window).panelHasShownBefore = false; DownloadsCommon.getData(window).panelHasShownBefore = false;
prepareForPanelOpen(); let promise = promisePanelOpened();
DownloadsCommon.getData(window)._notifyDownloadEvent("start"); DownloadsCommon.getData(window)._notifyDownloadEvent("start");
yield undefined; yield promise;
// If we got here, that means the panel opened. // If we got here, that means the panel opened.
DownloadsPanel.hidePanel(); DownloadsPanel.hidePanel();
@ -28,29 +28,26 @@ function gen_test()
ok(DownloadsCommon.getData(window).panelHasShownBefore, ok(DownloadsCommon.getData(window).panelHasShownBefore,
"Should have recorded that the panel was opened on a download.") "Should have recorded that the panel was opened on a download.")
// Next, make sure that if we start another download, we don't open // Next, make sure that if we start another download, we don't open the
// the panel automatically. // panel automatically.
panelShouldNotOpen(); let originalOnPopupShown = DownloadsPanel.onPopupShown;
DownloadsCommon.getData(window)._notifyDownloadEvent("start"); DownloadsPanel.onPopupShown = function () {
yield waitFor(2); originalOnPopupShown.apply(this, arguments);
} catch(e) { ok(false, "Should not have opened the downloads panel.");
ok(false, e); };
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 { } finally {
// Clean up when the test finishes. // 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 //// Globals
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
"resource://gre/modules/FileUtils.jsm"); "resource://gre/modules/Downloads.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon", XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
"resource:///modules/DownloadsCommon.jsm"); "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; const nsIDM = Ci.nsIDownloadManager;
let gTestTargetFile = FileUtils.getFile("TmpD", ["dm-ui-test.file"]); let gTestTargetFile = FileUtils.getFile("TmpD", ["dm-ui-test.file"]);
@ -22,253 +28,85 @@ registerCleanupFunction(function () {
gTestTargetFile.remove(false); 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 //// Infrastructure
// All test are run through the test runner.
function test() 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
// function promiseFocus()
// 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)
{ {
let statement = Services.downloads.DBConnection.createAsyncStatement( let deferred = Promise.defer();
"DELETE FROM moz_downloads"); waitForFocus(deferred.resolve);
try { return deferred.promise;
statement.executeAsync({ }
handleResult: function(aResultSet) { },
handleError: function(aError) function promisePanelOpened()
{ {
Cu.reportError(aError); let deferred = Promise.defer();
},
handleCompletion: function(aReason) // Hook to wait until the panel is shown.
{ let originalOnPopupShown = DownloadsPanel.onPopupShown;
testRunner.continueTest(); DownloadsPanel.onPopupShown = function () {
} DownloadsPanel.onPopupShown = originalOnPopupShown;
}); originalOnPopupShown.apply(this, arguments);
yield undefined;
} finally { // Defer to the next tick of the event loop so that we don't continue
statement.finalize(); // 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. // Reset any prefs that might have been changed.
Services.prefs.clearUserPref("browser.download.panel.shown"); 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(); DownloadsPanel.hidePanel();
// Wait for focus on the main window. yield promiseFocus();
waitForFocus(testRunner.continueTest);
yield undefined;
} }
function gen_addDownloadRows(aDataRows) function task_addDownloads(aItems)
{ {
let columnNames = Object.keys(gDownloadRowTemplate).join(", "); let startTimeMs = Date.now();
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];
// Populate insert parameters from the provided data. let publicList = yield Downloads.getPublicDownloadList();
for (let columnName in gDownloadRowTemplate) { for (let item of aItems) {
if (!(columnName in dataRow)) { publicList.add(yield Downloads.createDownload({
// Update the provided row object with data from the global template, source: "http://www.example.com/test-download.txt",
// for columns whose value is not provided explicitly. target: gTestTargetFile,
dataRow[columnName] = gDownloadRowTemplate[columnName]; succeeded: item.state == nsIDM.DOWNLOAD_FINISHED,
} canceled: item.state == nsIDM.DOWNLOAD_CANCELED ||
statement.params[columnName] = dataRow[columnName]; item.state == nsIDM.DOWNLOAD_PAUSED,
} error: item.state == nsIDM.DOWNLOAD_FAILED ? new Error("Failed.") : null,
hasPartialData: item.state == nsIDM.DOWNLOAD_PAUSED,
// Run the statement asynchronously and wait. startTime: new Date(startTimeMs++),
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();
} }
} }
function gen_openPanel(aData) function task_openPanel()
{ {
// Hook to wait until the test data has been loaded. yield promiseFocus();
let originalOnViewLoadCompleted = DownloadsPanel.onViewLoadCompleted;
DownloadsPanel.onViewLoadCompleted = function () {
DownloadsPanel.onViewLoadCompleted = originalOnViewLoadCompleted;
originalOnViewLoadCompleted.apply(this);
testRunner.continueTest();
};
// Start loading all the downloads from the database asynchronously. let promise = promisePanelOpened();
aData.ensurePersistentDataLoaded(false);
// Wait for focus on the main window.
waitForFocus(testRunner.continueTest);
yield undefined;
// Open the downloads panel, waiting until loading is completed.
DownloadsPanel.showPanel(); DownloadsPanel.showPanel();
yield undefined; yield promise;
}
/**
* 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();
};
} }

View File

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

View File

@ -262,6 +262,12 @@ let SessionSaverInternal = {
return; 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 // 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 // file is successfully updated, save the time stamp of the last save and
// notify the observers. // notify the observers.

View File

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

View File

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

View File

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

View File

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

View File

@ -46,11 +46,13 @@ var APZCObserver = {
case 'TabOpen': { case 'TabOpen': {
let browser = aEvent.originalTarget.linkedBrowser; let browser = aEvent.originalTarget.linkedBrowser;
browser.addEventListener("pageshow", this, true); browser.addEventListener("pageshow", this, true);
browser.messageManager.addMessageListener("scroll", this);
break; break;
} }
case 'TabClose': { case 'TabClose': {
let browser = aEvent.originalTarget.linkedBrowser; let browser = aEvent.originalTarget.linkedBrowser;
browser.removeEventListener("pageshow", this); browser.removeEventListener("pageshow", this, true);
browser.messageManager.removeMessageListener("scroll", this);
break; break;
} }
} }
@ -113,5 +115,16 @@ var APZCObserver = {
} else if (aTopic == "apzc-handle-pan-end") { } else if (aTopic == "apzc-handle-pan-end") {
Util.dumpLn("APZC 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; break;
case "scroll": { case "scroll": {
let doc = aEvent.target; this.sendScroll(aEvent.target);
if (doc != content.document)
break;
this.sendScroll();
break; break;
} }
@ -683,13 +679,35 @@ let ContentScroll = {
} }
}, },
sendScroll: function sendScroll() { sendScroll: function sendScroll(target) {
let scrollOffset = this.getScrollOffset(content); let isRoot = false;
if (this._scrollOffset.x == scrollOffset.x && this._scrollOffset.y == scrollOffset.y) if (target instanceof Ci.nsIDOMDocument) {
return; var window = target.defaultView;
var scrollOffset = this.getScrollOffset(window);
var element = target.documentElement;
this._scrollOffset = scrollOffset; if (target == content.document) {
sendAsyncMessage("scroll", scrollOffset); 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) { switch (aMessage.name) {
case "scroll": case "scroll":
if (!json.isRoot)
return;
if (!self.scrollSync) if (!self.scrollSync)
return; return;
this.doScroll(json.x, json.y, 0); this.doScroll(json.scrollOffset.x, json.scrollOffset.y, 0);
break; break;
} }
}, },

View File

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

View File

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

View File

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

View File

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

View File

@ -91,6 +91,11 @@ this.Social = {
providers: [], providers: [],
_disabledForSafeMode: false, _disabledForSafeMode: false,
get allowMultipleWorkers() {
return Services.prefs.prefHasUserValue("social.allowMultipleWorkers") &&
Services.prefs.getBoolPref("social.allowMultipleWorkers");
},
get _currentProviderPref() { get _currentProviderPref() {
try { try {
return Services.prefs.getComplexValue("social.provider.current", return Services.prefs.getComplexValue("social.provider.current",
@ -114,15 +119,14 @@ this.Social = {
this._setProvider(val); this._setProvider(val);
}, },
// Sets the current provider and enables it. Also disables the // Sets the current provider and notifies observers of the change.
// previously set provider, and notifies observers of the change.
_setProvider: function (provider) { _setProvider: function (provider) {
if (this._provider == provider) if (this._provider == provider)
return; return;
// Disable the previous provider, if any, since we want only one provider to // Disable the previous provider, if we are not allowing multiple workers,
// be enabled at once. // since we want only one provider to be enabled at once.
if (this._provider) if (this._provider && !Social.allowMultipleWorkers)
this._provider.enabled = false; this._provider.enabled = false;
this._provider = provider; this._provider = provider;
@ -134,7 +138,6 @@ this.Social = {
let enabled = !!provider; let enabled = !!provider;
if (enabled != SocialService.enabled) { if (enabled != SocialService.enabled) {
SocialService.enabled = enabled; SocialService.enabled = enabled;
Services.prefs.setBoolPref("social.enabled", enabled);
} }
let origin = this._provider && this._provider.origin; let origin = this._provider && this._provider.origin;
@ -159,31 +162,40 @@ this.Social = {
if (SocialService.enabled) { if (SocialService.enabled) {
// Retrieve the current set of providers, and set the current provider. // Retrieve the current set of providers, and set the current provider.
SocialService.getOrderedProviderList(function (providers) { SocialService.getOrderedProviderList(function (providers) {
this._updateProviderCache(providers); Social._updateProviderCache(providers);
}.bind(this)); Social._updateWorkerState(true);
});
} }
// Register an observer for changes to the provider list // Register an observer for changes to the provider list
SocialService.registerProviderListener(function providerListener(topic, data) { 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") { if (topic == "provider-added" || topic == "provider-removed") {
this._updateProviderCache(data); Social._updateProviderCache(data);
Social._updateWorkerState(true);
Services.obs.notifyObservers(null, "social:providers-changed", null); Services.obs.notifyObservers(null, "social:providers-changed", null);
return; return;
} }
if (topic == "provider-update") { if (topic == "provider-update") {
// a provider has self-updated its manifest, we need to update our // a provider has self-updated its manifest, we need to update our cache
// cache and possibly reload if it was the current provider. // and reload the provider.
let provider = data; let provider = data;
// if we need a reload, do it now SocialService.getOrderedProviderList(function(providers) {
if (provider.enabled) { Social._updateProviderCache(providers);
Social.enabled = false; provider.reload();
Services.tm.mainThread.dispatch(function() { Services.obs.notifyObservers(null, "social:providers-changed", null);
Social.enabled = true; });
}, Components.interfaces.nsIThread.DISPATCH_NORMAL);
}
} }
}.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 // Called to update our cache of providers and set the current provider
@ -203,6 +215,9 @@ this.Social = {
set enabled(val) { set enabled(val) {
// Setting .enabled is just a shortcut for setting the provider to either // Setting .enabled is just a shortcut for setting the provider to either
// the default provider or null... // the default provider or null...
this._updateWorkerState(val);
if (val) { if (val) {
if (!this.provider) if (!this.provider)
this.provider = this.defaultProvider; this.provider = this.defaultProvider;
@ -210,6 +225,7 @@ this.Social = {
this.provider = null; this.provider = null;
} }
}, },
get enabled() { get enabled() {
return this.provider != null; return this.provider != null;
}, },
@ -229,10 +245,6 @@ this.Social = {
Services.prefs.setBoolPref("social.toast-notifications.enabled", !prefValue); Services.prefs.setBoolPref("social.toast-notifications.enabled", !prefValue);
}, },
haveLoggedInUser: function () {
return !!(this.provider && this.provider.profile && this.provider.profile.userName);
},
setProviderByOrigin: function (origin) { setProviderByOrigin: function (origin) {
this.provider = this._getProviderFromOrigin(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' 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' LIBXUL_LIBS='$(LIBXUL_DIST)/lib/xul.lib $(LIBXUL_DIST)/lib/mozalloc.lib'
MOZ_COMPONENT_NSPR_LIBS='$(NSPR_LIBS)' 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 dnl For profile-guided optimization
PROFILE_GEN_CFLAGS="-GL" PROFILE_GEN_CFLAGS="-GL"
PROFILE_GEN_LDFLAGS="-LTCG:PGINSTRUMENT" PROFILE_GEN_LDFLAGS="-LTCG:PGINSTRUMENT"

View File

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

View File

@ -2643,12 +2643,6 @@ nsEventStateManager::ComputeScrollTarget(nsIFrame* aTargetFrame,
nsIScrollableFrame* frameToScroll = nsIScrollableFrame* frameToScroll =
lastScrollFrame->GetScrollTargetFrame(); lastScrollFrame->GetScrollTargetFrame();
if (frameToScroll) { 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; return frameToScroll;
} }
} }
@ -2714,14 +2708,7 @@ nsEventStateManager::ComputeScrollTarget(nsIFrame* aTargetFrame,
aTargetFrame->PresContext()->FrameManager()->GetRootFrame()); aTargetFrame->PresContext()->FrameManager()->GetRootFrame());
aOptions = aOptions =
static_cast<ComputeScrollTargetOptions>(aOptions & ~START_FROM_PARENT); static_cast<ComputeScrollTargetOptions>(aOptions & ~START_FROM_PARENT);
if (newFrame) { return newFrame ? ComputeScrollTarget(newFrame, aEvent, aOptions) : nullptr;
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;
} }
nsSize nsSize

View File

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

View File

@ -125,6 +125,7 @@ MOCHITEST_FILES = \
test_bug448534.html \ test_bug448534.html \
test_bug463162.xhtml \ test_bug463162.xhtml \
test_decoder_disable.html \ test_decoder_disable.html \
test_mediarecorder_record_no_timeslice.html \
test_mediarecorder_reload_crash.html \ test_mediarecorder_reload_crash.html \
test_media_selection.html \ test_media_selection.html \
test_playback.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; 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 NS_IMETHODIMP
nsDOMWindowUtils::GetScreenPixelsPerCSSPixel(float* aScreenPixels) nsDOMWindowUtils::GetScreenPixelsPerCSSPixel(float* aScreenPixels)
{ {

View File

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

View File

@ -42,7 +42,7 @@ interface nsIURI;
interface nsIDOMEventTarget; interface nsIDOMEventTarget;
interface nsIRunnable; interface nsIRunnable;
[scriptable, uuid(ff1cec22-b183-40d3-8b42-b81a2f0ba4e6)] [scriptable, uuid(d6e733ef-492b-4e67-b723-28571c2959f0)]
interface nsIDOMWindowUtils : nsISupports { interface nsIDOMWindowUtils : nsISupports {
/** /**
@ -1177,6 +1177,12 @@ interface nsIDOMWindowUtils : nsISupports {
*/ */
nsIDOMElement findElementWithViewId(in nsViewID aId); 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 * Checks the layer tree for this window and returns true
* if all layers have transforms that are translations by integers, * if all layers have transforms that are translations by integers,

View File

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

View File

@ -1147,30 +1147,6 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri
bool isDefault = mFrameMetrics.IsDefault(); bool isDefault = mFrameMetrics.IsDefault();
mFrameMetrics.mMayHaveTouchListeners = aLayerMetrics.mMayHaveTouchListeners; 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()); mPaintThrottler.TaskComplete(GetFrameTime());
bool needContentRepaint = false; bool needContentRepaint = false;
if (aLayerMetrics.mCompositionBounds.width == mFrameMetrics.mCompositionBounds.width && 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|. // previous run while the 2nd byte has to come from |*src|.
mState = STATE_NORMAL; mState = STATE_NORMAL;
#ifdef IS_BIG_ENDIAN #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 #else
u = (*src++ << 8) | mOddByte; // safe, we know we have at least one byte. u = (*src++ << 8) | mOddByte; // safe, we know we have at least one byte.
#endif #endif

View File

@ -1629,7 +1629,10 @@ ia64*-hpux*)
XPCOM_FROZEN_LDOPTS='$(LIBXUL_DIST)/lib/xpcom.lib $(LIBXUL_DIST)/lib/mozalloc.lib' 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' LIBXUL_LIBS='$(LIBXUL_DIST)/lib/xpcom.lib $(LIBXUL_DIST)/lib/xul.lib $(LIBXUL_DIST)/lib/mozalloc.lib'
MOZ_COMPONENT_NSPR_LIBS='$(NSPR_LIBS)' 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 dnl For profile-guided optimization
PROFILE_GEN_CFLAGS="-GL" PROFILE_GEN_CFLAGS="-GL"
PROFILE_GEN_LDFLAGS="-LTCG:PGINSTRUMENT" 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. // 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); parser.report(ParseError, false, NULL, JSMSG_BAD_GENEXP_BODY, js_arguments_str);
return false; return false;
} }
@ -294,7 +294,8 @@ frontend::CompileScript(ExclusiveContext *cx, LifoAlloc *alloc, HandleObject sco
*/ */
JSFunction *fun = evalCaller->functionOrCallerFunction(); JSFunction *fun = evalCaller->functionOrCallerFunction();
Directives directives(/* strict = */ fun->strict()); 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) if (!funbox)
return NULL; return NULL;
bce.objectList.add(funbox); bce.objectList.add(funbox);
@ -414,7 +415,9 @@ frontend::CompileLazyFunction(JSContext *cx, LazyScript *lazy, const jschar *cha
uint32_t staticLevel = lazy->staticLevel(cx); uint32_t staticLevel = lazy->staticLevel(cx);
Rooted<JSFunction*> fun(cx, lazy->function()); 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) if (!pn)
return false; return false;
@ -518,7 +521,7 @@ frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileO
ParseNode *fn; ParseNode *fn;
while (true) { while (true) {
Directives newDirectives = directives; Directives newDirectives = directives;
fn = parser.standaloneFunctionBody(fun, formals, directives, &newDirectives); fn = parser.standaloneFunctionBody(fun, formals, NotGenerator, directives, &newDirectives);
if (fn) if (fn)
break; break;

View File

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

View File

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

View File

@ -104,18 +104,20 @@ struct ParseContext : public GenericParseContext
const unsigned staticLevel; /* static compilation unit nesting level */ 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. // lastYieldOffset stores the offset of the last yield that was parsed.
// NoYieldOffset is its initial value. // NoYieldOffset is its initial value.
static const uint32_t NoYieldOffset = UINT32_MAX; static const uint32_t NoYieldOffset = UINT32_MAX;
uint32_t lastYieldOffset; uint32_t lastYieldOffset;
bool isGenerator() const { return generatorParseMode != NotGenerator; } // Most functions start off being parsed as non-generators.
bool isLegacyGenerator() const { return generatorParseMode == LegacyGenerator; } // 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 Node blockNode; /* parse node for a block with let declarations
(block with its own lexical scope) */ (block with its own lexical scope) */
@ -249,7 +251,6 @@ struct ParseContext : public GenericParseContext
blockChain(prs->context), blockChain(prs->context),
maybeFunction(maybeFunction), maybeFunction(maybeFunction),
staticLevel(staticLevel), staticLevel(staticLevel),
generatorParseMode(NotGenerator),
lastYieldOffset(NoYieldOffset), lastYieldOffset(NoYieldOffset),
blockNode(ParseHandler::null()), blockNode(ParseHandler::null()),
decls_(prs->context, prs->alloc), decls_(prs->context, prs->alloc),
@ -400,7 +401,7 @@ class Parser : private AutoGCRooter, public StrictModeGetter
ObjectBox *newObjectBox(JSObject *obj); ObjectBox *newObjectBox(JSObject *obj);
ModuleBox *newModuleBox(Module *module, ParseContext<ParseHandler> *pc); ModuleBox *newModuleBox(Module *module, ParseContext<ParseHandler> *pc);
FunctionBox *newFunctionBox(Node fn, JSFunction *fun, 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 * 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); Node statement(bool canHaveDirectives = false);
bool maybeParseDirective(Node list, Node pn, bool *cont); 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, Node standaloneFunctionBody(HandleFunction fun, const AutoNameVector &formals,
GeneratorKind generatorKind,
Directives inheritedDirectives, Directives *newDirectives); Directives inheritedDirectives, Directives *newDirectives);
// Parse a function, given only its arguments and body. Used for lazily // Parse a function, given only its arguments and body. Used for lazily
// parsed functions. // 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 * 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 switchStatement();
Node continueStatement(); Node continueStatement();
Node breakStatement(); Node breakStatement();
Node returnStatementOrYieldExpression(); Node returnStatement();
Node withStatement(); Node withStatement();
Node labeledStatement(); Node labeledStatement();
Node throwStatement(); Node throwStatement();
@ -504,6 +508,7 @@ class Parser : private AutoGCRooter, public StrictModeGetter
Node expr(); Node expr();
Node assignExpr(); Node assignExpr();
Node assignExprWithoutYield(unsigned err); Node assignExprWithoutYield(unsigned err);
Node yieldExpression();
Node condExpr1(); Node condExpr1();
Node orExpr1(); Node orExpr1();
Node unaryExpr(); Node unaryExpr();
@ -517,9 +522,10 @@ class Parser : private AutoGCRooter, public StrictModeGetter
bool functionArguments(FunctionSyntaxKind kind, Node *list, Node funcpn, bool &hasRest); bool functionArguments(FunctionSyntaxKind kind, Node *list, Node funcpn, bool &hasRest);
Node functionDef(HandlePropertyName name, const TokenStream::Position &start, Node functionDef(HandlePropertyName name, const TokenStream::Position &start,
FunctionType type, FunctionSyntaxKind kind); FunctionType type, FunctionSyntaxKind kind, GeneratorKind generatorKind);
bool functionArgsAndBody(Node pn, HandleFunction fun, bool functionArgsAndBody(Node pn, HandleFunction fun,
FunctionType type, FunctionSyntaxKind kind, FunctionType type, FunctionSyntaxKind kind,
GeneratorKind generatorKind,
Directives inheritedDirectives, Directives *newDirectives); Directives inheritedDirectives, Directives *newDirectives);
Node unaryOpExpr(ParseNodeKind kind, JSOp op, uint32_t begin); Node unaryOpExpr(ParseNodeKind kind, JSOp op, uint32_t begin);
@ -536,6 +542,9 @@ class Parser : private AutoGCRooter, public StrictModeGetter
Node identifierName(); Node identifierName();
bool matchLabel(MutableHandle<PropertyName*> label);
bool checkYieldNameValidity(unsigned errorNumber = JSMSG_SYNTAX_ERROR);
bool allowsForEachIn() { bool allowsForEachIn() {
#if !JS_HAS_FOR_EACH_IN #if !JS_HAS_FOR_EACH_IN
return false; return false;
@ -556,6 +565,7 @@ class Parser : private AutoGCRooter, public StrictModeGetter
bool checkFunctionArguments(); bool checkFunctionArguments();
bool makeDefIntoUse(Definition *dn, Node pn, JSAtom *atom); bool makeDefIntoUse(Definition *dn, Node pn, JSAtom *atom);
bool checkFunctionName(HandlePropertyName funName);
bool checkFunctionDefinition(HandlePropertyName funName, Node *pn, FunctionSyntaxKind kind, bool checkFunctionDefinition(HandlePropertyName funName, Node *pn, FunctionSyntaxKind kind,
bool *pbodyProcessed); bool *pbodyProcessed);
bool finishFunctionDefinition(Node pn, FunctionBox *funbox, Node prelude, Node body); 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. // This class's data is all private and so only visible to these friends.
friend class FunctionBox; 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 // The function or a function that encloses it may define new local names
// at runtime through means other than calling eval. // at runtime through means other than calling eval.
bool mightAliasLocals:1; bool mightAliasLocals:1;
@ -129,8 +125,7 @@ class FunctionContextFlags
public: public:
FunctionContextFlags() FunctionContextFlags()
: isLegacyGenerator(false), : mightAliasLocals(false),
mightAliasLocals(false),
hasExtensibleScope(false), hasExtensibleScope(false),
needsDeclEnvObject(false), needsDeclEnvObject(false),
argumentsHasLocalBinding(false), argumentsHasLocalBinding(false),
@ -264,6 +259,8 @@ class FunctionBox : public ObjectBox, public SharedContext
uint32_t startLine; uint32_t startLine;
uint32_t startColumn; uint32_t startColumn;
uint16_t ndefaults; uint16_t ndefaults;
uint8_t generatorKindBits_; /* The GeneratorKind of this function. */
bool inWith:1; /* some enclosing scope is a with-statement */ bool inWith:1; /* some enclosing scope is a with-statement */
bool inGenexpLambda:1; /* lambda from generator expression */ bool inGenexpLambda:1; /* lambda from generator expression */
bool hasDestructuringArgs:1; /* arguments list contains destructuring expression */ bool hasDestructuringArgs:1; /* arguments list contains destructuring expression */
@ -279,21 +276,30 @@ class FunctionBox : public ObjectBox, public SharedContext
template <typename ParseHandler> template <typename ParseHandler>
FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunction *fun, FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunction *fun,
ParseContext<ParseHandler> *pc, Directives directives, ParseContext<ParseHandler> *pc, Directives directives,
bool extraWarnings); bool extraWarnings, GeneratorKind generatorKind);
ObjectBox *toObjectBox() { return this; } ObjectBox *toObjectBox() { return this; }
JSFunction *function() const { return &object->as<JSFunction>(); } JSFunction *function() const { return &object->as<JSFunction>(); }
// In the future, isGenerator will also return true for ES6 generators. GeneratorKind generatorKind() const { return GeneratorKindFromBits(generatorKindBits_); }
bool isGenerator() const { return isLegacyGenerator(); } bool isGenerator() const { return generatorKind() != NotGenerator; }
bool isLegacyGenerator() const { return funCxFlags.isLegacyGenerator; } 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 mightAliasLocals() const { return funCxFlags.mightAliasLocals; }
bool hasExtensibleScope() const { return funCxFlags.hasExtensibleScope; } bool hasExtensibleScope() const { return funCxFlags.hasExtensibleScope; }
bool needsDeclEnvObject() const { return funCxFlags.needsDeclEnvObject; } bool needsDeclEnvObject() const { return funCxFlags.needsDeclEnvObject; }
bool argumentsHasLocalBinding() const { return funCxFlags.argumentsHasLocalBinding; } bool argumentsHasLocalBinding() const { return funCxFlags.argumentsHasLocalBinding; }
bool definitelyNeedsArgsObj() const { return funCxFlags.definitelyNeedsArgsObj; } bool definitelyNeedsArgsObj() const { return funCxFlags.definitelyNeedsArgsObj; }
void setIsLegacyGenerator() { funCxFlags.isLegacyGenerator = true; }
void setMightAliasLocals() { funCxFlags.mightAliasLocals = true; } void setMightAliasLocals() { funCxFlags.mightAliasLocals = true; }
void setHasExtensibleScope() { funCxFlags.hasExtensibleScope = true; } void setHasExtensibleScope() { funCxFlags.hasExtensibleScope = true; }
void setNeedsDeclEnvObject() { funCxFlags.needsDeclEnvObject = 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); return reportError(JSMSG_RESERVED_ID, kw->chars);
} }
// The keyword is not in this version. Treat it as an identifier, // The keyword is not in this version. Treat it as an identifier, unless
// unless it is let or yield which we treat as TOK_STRICT_RESERVED by // it is let which we treat as TOK_STRICT_RESERVED by falling through to
// falling through to the code below (ES5 forbids them in strict mode). // the code below (ES5 forbids it in strict mode).
if (kw->tokentype != TOK_LET && kw->tokentype != TOK_YIELD) if (kw->tokentype != TOK_LET)
return true; return true;
} }

View File

@ -407,6 +407,13 @@ class MOZ_STACK_CLASS TokenStream
JSVersion versionNumber() const { return VersionNumber(options().version); } JSVersion versionNumber() const { return VersionNumber(options().version); }
JSVersion versionWithFlags() const { return 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 { bool isCurrentTokenAssignment() const {
return TokenKindIsAssignment(currentToken().type); 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 # 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/. # 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(): def add_libdir_to_path():
from os.path import dirname, exists, join, realpath 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)') 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, 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)') 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) options, args = op.parse_args(argv)
if len(args) < 1: if len(args) < 1:
@ -171,7 +188,11 @@ def main(argv):
job_list.append(new_test) job_list.append(new_test)
prefix = [os.path.abspath(args[0])] + shlex.split(options.shell_args) 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 options.debug:
if len(job_list) > 1: if len(job_list) > 1:
print 'Multiple tests match command line arguments, debugger can only run one' print 'Multiple tests match command line arguments, debugger can only run one'
@ -186,7 +207,9 @@ def main(argv):
try: try:
ok = None 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) ok = jittests.run_tests_parallel(job_list, prefix, options)
else: else:
ok = jittests.run_tests(job_list, prefix, options) 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; AsmJSParseContext *outerpc = m.parser().pc;
Directives directives(outerpc); Directives directives(outerpc);
FunctionBox *funbox = m.parser().newFunctionBox(fn, fun, outerpc, directives); FunctionBox *funbox = m.parser().newFunctionBox(fn, fun, outerpc, directives, NotGenerator);
if (!funbox) if (!funbox)
return false; 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_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_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_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. * FIXME: Don't build arguments for ES6 generator expressions.
*/ */
if (cx->compartment()->debugMode() || script_->isGenerator) if (cx->compartment()->debugMode() || script_->isGenerator())
return true; return true;
/* /*

View File

@ -104,7 +104,6 @@ const char js_typeof_str[] = "typeof";
const char js_void_str[] = "void"; const char js_void_str[] = "void";
const char js_while_str[] = "while"; const char js_while_str[] = "while";
const char js_with_str[] = "with"; 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 * 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_void_str[];
extern const char js_while_str[]; extern const char js_while_str[];
extern const char js_with_str[]; extern const char js_with_str[];
extern const char js_yield_str[];
namespace js { namespace js {

View File

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

View File

@ -284,6 +284,18 @@ class JSFunction : public JSObject
return u.i.s.lazy_; 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 setScript(JSScript *script_);
inline void initScript(JSScript *script_); inline void initScript(JSScript *script_);
void initLazyScript(js::LazyScript *lazy) { void initLazyScript(js::LazyScript *lazy) {

View File

@ -1475,6 +1475,13 @@ js_NewGenerator(JSContext *cx, const FrameRegs &stackRegs)
JS_ASSERT(stackRegs.stackDepth() == 0); JS_ASSERT(stackRegs.stackDepth() == 0);
StackFrame *stackfp = stackRegs.fp(); 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()); Rooted<GlobalObject*> global(cx, &stackfp->global());
RootedObject obj(cx); RootedObject obj(cx);
{ {

View File

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

View File

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

View File

@ -414,6 +414,19 @@ class ScriptSourceObject : public JSObject
static const uint32_t SOURCE_SLOT = 0; 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 */ } /* namespace js */
class JSScript : public js::gc::Cell 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 nslots; /* vars plus maximum stack depth */
uint16_t staticLevel;/* static level for display maintenance */ uint16_t staticLevel;/* static level for display maintenance */
// 8-bit fields. // 4-bit fields.
public: public:
// The kinds of the optional arrays. // The kinds of the optional arrays.
@ -528,15 +541,16 @@ class JSScript : public js::gc::Cell
OBJECTS, OBJECTS,
REGEXPS, REGEXPS,
TRYNOTES, TRYNOTES,
LIMIT ARRAY_KIND_BITS
}; };
typedef uint8_t ArrayBitsT;
private: private:
// The bits in this field indicate the presence/non-presence of several // The bits in this field indicate the presence/non-presence of several
// optional arrays in |data|. See the comments above Create() for details. // 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. // 1-bit fields.
@ -589,14 +603,9 @@ class JSScript : public js::gc::Cell
#endif #endif
bool invalidatedIdempotentCache:1; /* idempotent cache has triggered invalidation */ 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, // If the generator was created implicitly via a generator expression,
// isGeneratorExp will be true. // isGeneratorExp will be true.
bool isGeneratorExp:1; 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 bool hasScriptCounts:1;/* script has an entry in
JSCompartment::scriptCountsMap */ JSCompartment::scriptCountsMap */
@ -647,6 +656,19 @@ class JSScript : public js::gc::Cell
jsbytecode *argumentsBytecode() const { JS_ASSERT(code[0] == JSOP_ARGUMENTS); return code; } jsbytecode *argumentsBytecode() const { JS_ASSERT(code[0] == JSOP_ARGUMENTS); return code; }
void setArgumentsHasVarBinding(); 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 * As an optimization, even when argsHasLocalBinding, the function prologue
* may not need to create an arguments object. This is determined by * 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); 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. */ /* If this fails, add/remove padding within JSScript. */
JS_STATIC_ASSERT(sizeof(JSScript) % js::gc::CellSize == 0); 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 version_ : 8;
uint32_t numFreeVariables_ : 24; 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. // N.B. These are booleans but need to be uint32_t to pack correctly on MSVC.
uint32_t strict_ : 1; uint32_t strict_ : 1;
@ -1211,6 +1236,23 @@ class LazyScript : public js::gc::Cell
return (HeapPtrFunction *)&freeVariables()[numFreeVariables()]; 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 { bool strict() const {
return strict_; 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. # jit_test.py -- Python harness for JavaScript trace tests.
from __future__ import print_function from __future__ import print_function
import os, sys, tempfile, traceback, time import os, posixpath, sys, tempfile, traceback, time
import subprocess import subprocess
from subprocess import Popen, PIPE from subprocess import Popen, PIPE
from threading import Thread from threading import Thread
import signal import signal
import StringIO
try: try:
from multiprocessing import Process, Manager, cpu_count from multiprocessing import Process, Manager, cpu_count
@ -152,15 +153,29 @@ class Test:
return test return test
def command(self, prefix): def command(self, prefix, libdir, remote_prefix=None):
scriptdir_var = os.path.dirname(self.path); 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('/'): if not scriptdir_var.endswith('/'):
scriptdir_var += '/' 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 # We may have specified '-a' or '-d' twice: once via --jitflags, once
# via the "|jit-test|" line. Remove dups because they are toggles. # 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: if self.valgrind:
cmd = self.VALGRIND_CMD + cmd cmd = self.VALGRIND_CMD + cmd
return 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 return read_and_unlink(stdoutPath), read_and_unlink(stderrPath), code
def run_test(test, prefix, options): def run_test(test, prefix, options):
cmd = test.command(prefix) cmd = test.command(prefix, LIB_DIR)
if options.show_cmd: if options.show_cmd:
print(subprocess.list2cmdline(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) out, err, code, timed_out = run(cmd, env, options.timeout)
return TestOutput(test, cmd, out, err, code, None, timed_out) 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): def check_output(out, err, rc, test):
if test.expect_error: if test.expect_error:
# The shell exits with code 3 on uncaught exceptions. # 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) ok = process_test_results(gen, len(tests), options)
return ok 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): def parse_jitflags(options):
jitflags = [ [ '-' + flag for flag in flags ] jitflags = [ [ '-' + flag for flag in flags ]
for flags in options.jitflags.split(',') ] for flags in options.jitflags.split(',') ]

View File

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

View File

@ -72,8 +72,12 @@
macro(protected, protected_, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \ macro(protected, protected_, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \
macro(public, public_, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \ macro(public, public_, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \
macro(static, static_, 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. */ \ /* Various conditional keywords. */ \
FOR_CONST_KEYWORD(macro) \ FOR_CONST_KEYWORD(macro) \
FOR_LET_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([ 'bug240933-2.html' , 'bug240933-1-ref.html' ]); // bug 681162
tests.push([ 'bug389321-1.html' , 'bug389321-1-ref.html' ]); // bug 683163 tests.push([ 'bug389321-1.html' , 'bug389321-1-ref.html' ]); // bug 683163
tests.push([ 'bug482484.html' , 'bug482484-ref.html' ]); // bug 688575 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([ 'bug512295-2.html' , 'bug512295-2-ref.html' ]); // bug 681331
tests.push([ 'bug597519-1.html' , 'bug597519-1-ref.html' ]); // bug 680579 tests.push([ 'bug597519-1.html' , 'bug597519-1-ref.html' ]); // bug 680579
tests.push([ 'bug602141-1.html' , 'bug602141-1-ref.html' ]); // bug 681334 tests.push([ 'bug602141-1.html' , 'bug602141-1-ref.html' ]); // bug 681334
@ -142,6 +141,7 @@ if (!isWindows) {
if (!isWindows && !isOSXMtnLion) { if (!isWindows && !isOSXMtnLion) {
tests.push([ 'bug106855-1.html' , 'bug106855-1-ref.html' ]); // bug 682837 tests.push([ 'bug106855-1.html' , 'bug106855-1-ref.html' ]); // bug 682837
tests.push([ 'bug106855-2.html' , 'bug106855-1-ref.html?' ]); // bug 681138 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? bool mCanCacheFrameOffset; // cached frame offset is valid?
}; };
static RangeData sEmptyData(nullptr);
// Stack-class to turn on/off selection batching for table selection // Stack-class to turn on/off selection batching for table selection
class MOZ_STACK_CLASS nsSelectionBatcher MOZ_FINAL class MOZ_STACK_CLASS nsSelectionBatcher MOZ_FINAL
{ {
@ -4525,7 +4523,8 @@ Selection::GetRangeCount(int32_t* aRangeCount)
NS_IMETHODIMP NS_IMETHODIMP
Selection::GetRangeAt(int32_t aIndex, nsIDOMRange** aReturn) Selection::GetRangeAt(int32_t aIndex, nsIDOMRange** aReturn)
{ {
*aReturn = mRanges.SafeElementAt(aIndex, sEmptyData).mRange; RangeData empty(nullptr);
*aReturn = mRanges.SafeElementAt(aIndex, empty).mRange;
if (!*aReturn) { if (!*aReturn) {
return NS_ERROR_DOM_INDEX_SIZE_ERR; return NS_ERROR_DOM_INDEX_SIZE_ERR;
} }
@ -4538,7 +4537,8 @@ Selection::GetRangeAt(int32_t aIndex, nsIDOMRange** aReturn)
nsRange* nsRange*
Selection::GetRangeAt(int32_t aIndex) 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 == 883987-1f.html 883987-1-ref.html
== 890495-1.html 890495-1-ref.html == 890495-1.html 890495-1-ref.html
== 894931-1.html 894931-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-ltr-alignment-test.html text-ltr-alignment-ref.html
== text-rtl-alignment-test.html text-rtl-alignment-ref.html == text-rtl-alignment-test.html text-rtl-alignment-ref.html
== text-horzline-with-bottom.html text-horzline.html fuzzy-if(B2G&&azureSkiaGL,1,256) == 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-top.html text-horzline.html
!= text-big-stroke.html text-blank.html != text-big-stroke.html text-blank.html
!= text-big-stroke.html text-big-fill.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 == nested-viewBox-01.svg pass.svg
== nesting-invalid-01.svg nesting-invalid-01-ref.svg == nesting-invalid-01.svg nesting-invalid-01-ref.svg
== non-scaling-stroke-01.svg non-scaling-stroke-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 == non-scaling-stroke-03.svg non-scaling-stroke-03-ref.svg
== objectBoundingBox-and-clipPath.svg pass.svg == objectBoundingBox-and-clipPath.svg pass.svg
# Bug 588684 # Bug 588684

View File

@ -538,14 +538,6 @@ function BuildConditionSandbox(aURL) {
sandbox.smallScreen = true; 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); var gfxInfo = (NS_GFXINFO_CONTRACTID in CC) && CC[NS_GFXINFO_CONTRACTID].getService(CI.nsIGfxInfo);
try { try {
sandbox.d2d = gfxInfo.D2DEnabled; 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 // true if we are using the same Azure backend for rendering canvas and content
sandbox.contentSameGfxBackendAsCanvas = info.AzureContentBackend == info.AzureCanvasBackend sandbox.contentSameGfxBackendAsCanvas = info.AzureContentBackend == info.AzureCanvasBackend
|| (info.AzureContentBackend == "none" && info.AzureCanvasBackend == "cairo"); || (info.AzureContentBackend == "none" && info.AzureCanvasBackend == "cairo");
#endif
sandbox.layersGPUAccelerated = sandbox.layersGPUAccelerated =
gWindowUtils.layerManagerType != "Basic"; gWindowUtils.layerManagerType != "Basic";

View File

@ -38,9 +38,9 @@ public class BrowserDB {
public Cursor filter(ContentResolver cr, CharSequence constraint, int limit); 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 // 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); public void updateVisitedHistory(ContentResolver cr, String uri);
@ -137,12 +137,12 @@ public class BrowserDB {
return sDb.filter(cr, constraint, limit); return sDb.filter(cr, constraint, limit);
} }
public static Cursor getTopSites(ContentResolver cr, int 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 topSites // Note this is not a single query anymore, but actually returns a mixture of two queries,
// and one for pinned sites // one for top bookmarks, and one for pinned sites (which are actually bookmarks as well).
Cursor topSites = sDb.getTopSites(cr, limit); Cursor topBookmarks = sDb.getTopBookmarks(cr, limit);
Cursor pinnedSites = sDb.getPinnedSites(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) { public static void updateVisitedHistory(ContentResolver cr, String uri) {

View File

@ -231,9 +231,13 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
} }
@Override @Override
public Cursor getTopSites(ContentResolver cr, int limit) { public Cursor getTopBookmarks(ContentResolver cr, int limit) {
// Filter out sites that are pinned // Only select bookmarks. Unfortunately, we need to query the combined view,
String selection = DBUtils.concatenateWhere("", Combined.URL + " NOT IN (SELECT " + // 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 " + Bookmarks.URL + " FROM bookmarks WHERE " +
DBUtils.qualifyColumn("bookmarks", Bookmarks.PARENT) + " == ? AND " + DBUtils.qualifyColumn("bookmarks", Bookmarks.PARENT) + " == ? AND " +
DBUtils.qualifyColumn("bookmarks", Bookmarks.IS_DELETED) + " == 0)"); DBUtils.qualifyColumn("bookmarks", Bookmarks.IS_DELETED) + " == 0)");

View File

@ -60,13 +60,18 @@ class BookmarksListAdapter extends MultiTypeCursorAdapter {
/** /**
* Moves to parent folder, if one exists. * 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 we're already at the root, we can't move to a parent folder
if (mParentStack.size() != 1) { if (mParentStack.size() == 1) {
mParentStack.removeFirst(); return false;
refreshCurrentFolder();
} }
mParentStack.removeFirst();
refreshCurrentFolder();
return true;
} }
/** /**

View File

@ -13,6 +13,7 @@ import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
import android.content.Context; import android.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.ViewConfiguration; import android.view.ViewConfiguration;
@ -56,6 +57,17 @@ public class BookmarksListView extends HomeListView
super.onAttachedToWindow(); super.onAttachedToWindow();
setOnItemClickListener(this); 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 @Override
@ -101,14 +113,7 @@ public class BookmarksListView extends HomeListView
// Absolute position for the adapter. // Absolute position for the adapter.
position -= headerCount; position -= headerCount;
BookmarksListAdapter adapter; final BookmarksListAdapter adapter = getBookmarksListAdapter();
ListAdapter listAdapter = getAdapter();
if (listAdapter instanceof HeaderViewListAdapter) {
adapter = (BookmarksListAdapter) ((HeaderViewListAdapter) listAdapter).getWrappedAdapter();
} else {
adapter = (BookmarksListAdapter) listAdapter;
}
if (adapter.isShowingChildFolder()) { if (adapter.isShowingChildFolder()) {
if (position == 0) { if (position == 0) {
// If we tap on an opened folder, move back to parent folder. // 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)); 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); 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. // Create callbacks before the initial loader is started.
mLoaderCallbacks = new CursorLoaderCallbacks(activity, getLoaderManager()); mLoaderCallbacks = new CursorLoaderCallbacks(activity, getLoaderManager());
mThumbnailsLoaderCallbacks = new ThumbnailsLoaderCallbacks(); mThumbnailsLoaderCallbacks = new ThumbnailsLoaderCallbacks();
@ -362,7 +366,7 @@ public class BookmarksPage extends HomeFragment {
@Override @Override
public Cursor loadCursor() { public Cursor loadCursor() {
final int max = getContext().getResources().getInteger(R.integer.number_of_top_sites); 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; package org.mozilla.gecko.home;
import org.mozilla.gecko.EditBookmarkDialog; import org.mozilla.gecko.EditBookmarkDialog;
import org.mozilla.gecko.Favicons;
import org.mozilla.gecko.GeckoAppShell; import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoEvent; import org.mozilla.gecko.GeckoEvent;
import org.mozilla.gecko.R; import org.mozilla.gecko.R;
@ -132,14 +133,8 @@ abstract class HomeFragment extends Fragment {
return false; 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; 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; 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 static class RemoveBookmarkTask extends UiAsyncTask<Void, Void, Void> {
private final Context mContext; private final Context mContext;
private final int mId; private final int mId;

View File

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

View File

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

View File

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

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