mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to b2g-inbound.
This commit is contained in:
commit
6bfa72099d
@ -163,6 +163,9 @@ this.AccessFu = {
|
||||
Services.obs.removeObserver(this, 'Accessibility:LongPress');
|
||||
Services.obs.removeObserver(this, 'Accessibility:MoveByGranularity');
|
||||
|
||||
delete this._quicknavModesPref;
|
||||
delete this._notifyOutputPref;
|
||||
|
||||
if (this.doneCallback) {
|
||||
this.doneCallback();
|
||||
delete this.doneCallback;
|
||||
@ -171,6 +174,9 @@ this.AccessFu = {
|
||||
|
||||
_enableOrDisable: function _enableOrDisable() {
|
||||
try {
|
||||
if (!this._activatePref) {
|
||||
return;
|
||||
}
|
||||
let activatePref = this._activatePref.value;
|
||||
if (activatePref == ACCESSFU_ENABLE ||
|
||||
this._systemPref && activatePref == ACCESSFU_AUTO)
|
||||
|
@ -14,10 +14,15 @@ const EVENT_TEXT_CARET_MOVED = Ci.nsIAccessibleEvent.EVENT_TEXT_CARET_MOVED;
|
||||
const EVENT_TEXT_INSERTED = Ci.nsIAccessibleEvent.EVENT_TEXT_INSERTED;
|
||||
const EVENT_TEXT_REMOVED = Ci.nsIAccessibleEvent.EVENT_TEXT_REMOVED;
|
||||
const EVENT_FOCUS = Ci.nsIAccessibleEvent.EVENT_FOCUS;
|
||||
const EVENT_SHOW = Ci.nsIAccessibleEvent.EVENT_SHOW;
|
||||
const EVENT_HIDE = Ci.nsIAccessibleEvent.EVENT_HIDE;
|
||||
|
||||
const ROLE_INTERNAL_FRAME = Ci.nsIAccessibleRole.ROLE_INTERNAL_FRAME;
|
||||
const ROLE_DOCUMENT = Ci.nsIAccessibleRole.ROLE_DOCUMENT;
|
||||
const ROLE_CHROME_WINDOW = Ci.nsIAccessibleRole.ROLE_CHROME_WINDOW;
|
||||
const ROLE_TEXT_LEAF = Ci.nsIAccessibleRole.ROLE_TEXT_LEAF;
|
||||
|
||||
const TEXT_NODE = 3;
|
||||
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
XPCOMUtils.defineLazyModuleGetter(this, 'Services',
|
||||
@ -140,7 +145,11 @@ this.EventManager.prototype = {
|
||||
// Don't bother with non-content events in firefox.
|
||||
if (Utils.MozBuildApp == 'browser' &&
|
||||
aEvent.eventType != EVENT_VIRTUALCURSOR_CHANGED &&
|
||||
aEvent.accessibleDocument.docType == 'window') {
|
||||
// XXX Bug 442005 results in DocAccessible::getDocType returning
|
||||
// NS_ERROR_FAILURE. Checking for aEvent.accessibleDocument.docType ==
|
||||
// 'window' does not currently work.
|
||||
(aEvent.accessibleDocument.DOMDocument.doctype &&
|
||||
aEvent.accessibleDocument.DOMDocument.doctype.name === 'window')) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -219,28 +228,47 @@ this.EventManager.prototype = {
|
||||
this.editState = editState;
|
||||
break;
|
||||
}
|
||||
case EVENT_SHOW:
|
||||
{
|
||||
let {liveRegion, isPolite} = this._handleLiveRegion(aEvent,
|
||||
['additions', 'all']);
|
||||
// Only handle show if it is a relevant live region.
|
||||
if (!liveRegion) {
|
||||
break;
|
||||
}
|
||||
// Show for text is handled by the EVENT_TEXT_INSERTED handler.
|
||||
if (aEvent.accessible.role === ROLE_TEXT_LEAF) {
|
||||
break;
|
||||
}
|
||||
this._dequeueLiveEvent(EVENT_HIDE, liveRegion);
|
||||
this.present(Presentation.liveRegion(liveRegion, isPolite, false));
|
||||
break;
|
||||
}
|
||||
case EVENT_HIDE:
|
||||
{
|
||||
let {liveRegion, isPolite} = this._handleLiveRegion(
|
||||
aEvent.QueryInterface(Ci.nsIAccessibleHideEvent),
|
||||
['removals', 'all']);
|
||||
// Only handle hide if it is a relevant live region.
|
||||
if (!liveRegion) {
|
||||
break;
|
||||
}
|
||||
// Hide for text is handled by the EVENT_TEXT_REMOVED handler.
|
||||
if (aEvent.accessible.role === ROLE_TEXT_LEAF) {
|
||||
break;
|
||||
}
|
||||
this._queueLiveEvent(EVENT_HIDE, liveRegion, isPolite);
|
||||
break;
|
||||
}
|
||||
case EVENT_TEXT_INSERTED:
|
||||
case EVENT_TEXT_REMOVED:
|
||||
{
|
||||
if (aEvent.isFromUserInput) {
|
||||
// XXX support live regions as well.
|
||||
let event = aEvent.QueryInterface(Ci.nsIAccessibleTextChangeEvent);
|
||||
let isInserted = event.isInserted;
|
||||
let txtIface = aEvent.accessible.QueryInterface(Ci.nsIAccessibleText);
|
||||
|
||||
let text = '';
|
||||
try {
|
||||
text = txtIface.
|
||||
getText(0, Ci.nsIAccessibleText.TEXT_OFFSET_END_OF_TEXT);
|
||||
} catch (x) {
|
||||
// XXX we might have gotten an exception with of a
|
||||
// zero-length text. If we did, ignore it (bug #749810).
|
||||
if (txtIface.characterCount)
|
||||
throw x;
|
||||
}
|
||||
this.present(Presentation.textChanged(
|
||||
isInserted, event.start, event.length,
|
||||
text, event.modifiedText));
|
||||
let {liveRegion, isPolite} = this._handleLiveRegion(aEvent,
|
||||
['text', 'all']);
|
||||
if (aEvent.isFromUserInput || liveRegion) {
|
||||
// Handle all text mutations coming from the user or if they happen
|
||||
// on a live region.
|
||||
this._handleText(aEvent, liveRegion, isPolite);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -258,6 +286,130 @@ this.EventManager.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
_handleText: function _handleText(aEvent, aLiveRegion, aIsPolite) {
|
||||
let event = aEvent.QueryInterface(Ci.nsIAccessibleTextChangeEvent);
|
||||
let isInserted = event.isInserted;
|
||||
let txtIface = aEvent.accessible.QueryInterface(Ci.nsIAccessibleText);
|
||||
|
||||
let text = '';
|
||||
try {
|
||||
text = txtIface.getText(0, Ci.nsIAccessibleText.TEXT_OFFSET_END_OF_TEXT);
|
||||
} catch (x) {
|
||||
// XXX we might have gotten an exception with of a
|
||||
// zero-length text. If we did, ignore it (bug #749810).
|
||||
if (txtIface.characterCount) {
|
||||
throw x;
|
||||
}
|
||||
}
|
||||
// If there are embedded objects in the text, ignore them.
|
||||
// Assuming changes to the descendants would already be handled by the
|
||||
// show/hide event.
|
||||
let modifiedText = event.modifiedText.replace(/\uFFFC/g, '').trim();
|
||||
if (!modifiedText) {
|
||||
return;
|
||||
}
|
||||
if (aLiveRegion) {
|
||||
if (aEvent.eventType === EVENT_TEXT_REMOVED) {
|
||||
this._queueLiveEvent(EVENT_TEXT_REMOVED, aLiveRegion, aIsPolite,
|
||||
modifiedText);
|
||||
} else {
|
||||
this._dequeueLiveEvent(EVENT_TEXT_REMOVED, aLiveRegion);
|
||||
this.present(Presentation.liveRegion(aLiveRegion, aIsPolite, false,
|
||||
modifiedText));
|
||||
}
|
||||
} else {
|
||||
this.present(Presentation.textChanged(isInserted, event.start,
|
||||
event.length, text, modifiedText));
|
||||
}
|
||||
},
|
||||
|
||||
_handleLiveRegion: function _handleLiveRegion(aEvent, aRelevant) {
|
||||
if (aEvent.isFromUserInput) {
|
||||
return {};
|
||||
}
|
||||
let parseLiveAttrs = function parseLiveAttrs(aAccessible) {
|
||||
let attrs = Utils.getAttributes(aAccessible);
|
||||
if (attrs['container-live']) {
|
||||
return {
|
||||
live: attrs['container-live'],
|
||||
relevant: attrs['container-relevant'] || 'additions text',
|
||||
busy: attrs['container-busy'],
|
||||
atomic: attrs['container-atomic'],
|
||||
memberOf: attrs['member-of']
|
||||
};
|
||||
}
|
||||
return null;
|
||||
};
|
||||
// XXX live attributes are not set for hidden accessibles yet. Need to
|
||||
// climb up the tree to check for them.
|
||||
let getLiveAttributes = function getLiveAttributes(aEvent) {
|
||||
let liveAttrs = parseLiveAttrs(aEvent.accessible);
|
||||
if (liveAttrs) {
|
||||
return liveAttrs;
|
||||
}
|
||||
let parent = aEvent.targetParent;
|
||||
while (parent) {
|
||||
liveAttrs = parseLiveAttrs(parent);
|
||||
if (liveAttrs) {
|
||||
return liveAttrs;
|
||||
}
|
||||
parent = parent.parent
|
||||
}
|
||||
return {};
|
||||
};
|
||||
let {live, relevant, busy, atomic, memberOf} = getLiveAttributes(aEvent);
|
||||
// If container-live is not present or is set to |off| ignore the event.
|
||||
if (!live || live === 'off') {
|
||||
return {};
|
||||
}
|
||||
// XXX: support busy and atomic.
|
||||
|
||||
// Determine if the type of the mutation is relevant. Default is additions
|
||||
// and text.
|
||||
let isRelevant = Utils.matchAttributeValue(relevant, aRelevant);
|
||||
if (!isRelevant) {
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
liveRegion: aEvent.accessible,
|
||||
isPolite: live === 'polite'
|
||||
};
|
||||
},
|
||||
|
||||
_dequeueLiveEvent: function _dequeueLiveEvent(aEventType, aLiveRegion) {
|
||||
let domNode = aLiveRegion.DOMNode;
|
||||
if (this._liveEventQueue && this._liveEventQueue.has(domNode)) {
|
||||
let queue = this._liveEventQueue.get(domNode);
|
||||
let nextEvent = queue[0];
|
||||
if (nextEvent.eventType === aEventType) {
|
||||
Utils.win.clearTimeout(nextEvent.timeoutID);
|
||||
queue.shift();
|
||||
if (queue.length === 0) {
|
||||
this._liveEventQueue.delete(domNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_queueLiveEvent: function _queueLiveEvent(aEventType, aLiveRegion, aIsPolite, aModifiedText) {
|
||||
if (!this._liveEventQueue) {
|
||||
this._liveEventQueue = new WeakMap();
|
||||
}
|
||||
let eventHandler = {
|
||||
eventType: aEventType,
|
||||
timeoutID: Utils.win.setTimeout(this.present.bind(this),
|
||||
20, // Wait for a possible EVENT_SHOW or EVENT_TEXT_INSERTED event.
|
||||
Presentation.liveRegion(aLiveRegion, aIsPolite, true, aModifiedText))
|
||||
};
|
||||
|
||||
let domNode = aLiveRegion.DOMNode;
|
||||
if (this._liveEventQueue.has(domNode)) {
|
||||
this._liveEventQueue.get(domNode).push(eventHandler);
|
||||
} else {
|
||||
this._liveEventQueue.set(domNode, [eventHandler]);
|
||||
}
|
||||
},
|
||||
|
||||
present: function present(aPresentationData) {
|
||||
this.sendMsgFunc("AccessFu:Present", aPresentationData);
|
||||
},
|
||||
|
@ -116,7 +116,6 @@ this.OutputGenerator = {
|
||||
let extState = {};
|
||||
aAccessible.getState(state, extState);
|
||||
let states = {base: state.value, ext: extState.value};
|
||||
|
||||
return func.apply(this, [aAccessible, roleString, states, flags, aContext]);
|
||||
},
|
||||
|
||||
@ -418,6 +417,15 @@ this.UtteranceGenerator = {
|
||||
return [gStringBundle.GetStringFromName(this.gActionMap[aActionName])];
|
||||
},
|
||||
|
||||
genForLiveRegion: function genForLiveRegion(aContext, aIsHide, aModifiedText) {
|
||||
let utterance = [];
|
||||
if (aIsHide) {
|
||||
utterance.push(gStringBundle.GetStringFromName('hidden'));
|
||||
}
|
||||
return utterance.concat(
|
||||
aModifiedText || this.genForContext(aContext).output);
|
||||
},
|
||||
|
||||
genForAnnouncement: function genForAnnouncement(aAnnouncement) {
|
||||
try {
|
||||
return [gStringBundle.GetStringFromName(aAnnouncement)];
|
||||
|
@ -102,7 +102,19 @@ Presenter.prototype = {
|
||||
/**
|
||||
* Announce something. Typically an app state change.
|
||||
*/
|
||||
announce: function announce(aAnnouncement) {}
|
||||
announce: function announce(aAnnouncement) {},
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Announce a live region.
|
||||
* @param {PivotContext} aContext context object for an accessible.
|
||||
* @param {boolean} aIsPolite A politeness level for a live region.
|
||||
* @param {boolean} aIsHide An indicator of hide/remove event.
|
||||
* @param {string} aModifiedText Optional modified text.
|
||||
*/
|
||||
liveRegion: function liveRegionShown(aContext, aIsPolite, aIsHide,
|
||||
aModifiedText) {}
|
||||
};
|
||||
|
||||
/**
|
||||
@ -409,6 +421,13 @@ AndroidPresenter.prototype = {
|
||||
fromIndex: 0
|
||||
}]
|
||||
};
|
||||
},
|
||||
|
||||
liveRegion: function AndroidPresenter_liveRegion(aContext, aIsPolite,
|
||||
aIsHide, aModifiedText) {
|
||||
return this.announce(
|
||||
UtteranceGenerator.genForLiveRegion(aContext, aIsHide,
|
||||
aModifiedText).join(' '));
|
||||
}
|
||||
};
|
||||
|
||||
@ -451,6 +470,21 @@ SpeechPresenter.prototype = {
|
||||
]
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
liveRegion: function SpeechPresenter_liveRegion(aContext, aIsPolite, aIsHide,
|
||||
aModifiedText) {
|
||||
return {
|
||||
type: this.type,
|
||||
details: {
|
||||
actions: [{
|
||||
method: 'speak',
|
||||
data: UtteranceGenerator.genForLiveRegion(aContext, aIsHide,
|
||||
aModifiedText).join(' '),
|
||||
options: {enqueue: aIsPolite}
|
||||
}]
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@ -570,5 +604,16 @@ this.Presentation = {
|
||||
// but there really isn't a point here.
|
||||
return [p.announce(UtteranceGenerator.genForAnnouncement(aAnnouncement)[0])
|
||||
for each (p in this.presenters)];
|
||||
},
|
||||
|
||||
liveRegion: function Presentation_liveRegion(aAccessible, aIsPolite, aIsHide,
|
||||
aModifiedText) {
|
||||
let context;
|
||||
if (!aModifiedText) {
|
||||
context = new PivotContext(aAccessible, null, -1, -1, true,
|
||||
aIsHide ? true : false);
|
||||
}
|
||||
return [p.liveRegion(context, aIsPolite, aIsHide, aModifiedText) for (
|
||||
p of this.presenters)];
|
||||
}
|
||||
};
|
||||
|
@ -266,6 +266,15 @@ this.Utils = {
|
||||
return true;
|
||||
},
|
||||
|
||||
matchAttributeValue: function matchAttributeValue(aAttributeValue, values) {
|
||||
let attrSet = new Set(aAttributeValue.split(' '));
|
||||
for (let value of values) {
|
||||
if (attrSet.has(value)) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getLandmarkName: function getLandmarkName(aAccessible) {
|
||||
const landmarks = [
|
||||
'banner',
|
||||
@ -281,11 +290,7 @@ this.Utils = {
|
||||
}
|
||||
|
||||
// Looking up a role that would match a landmark.
|
||||
for (let landmark of landmarks) {
|
||||
if (roles.indexOf(landmark) > -1) {
|
||||
return landmark;
|
||||
}
|
||||
}
|
||||
return this.matchAttributeValue(roles, landmarks);
|
||||
}
|
||||
};
|
||||
|
||||
@ -422,12 +427,15 @@ this.Logger = {
|
||||
* for a given accessible and its relationship with another accessible.
|
||||
*/
|
||||
this.PivotContext = function PivotContext(aAccessible, aOldAccessible,
|
||||
aStartOffset, aEndOffset) {
|
||||
aStartOffset, aEndOffset, aIgnoreAncestry = false,
|
||||
aIncludeInvisible = false) {
|
||||
this._accessible = aAccessible;
|
||||
this._oldAccessible =
|
||||
this._isDefunct(aOldAccessible) ? null : aOldAccessible;
|
||||
this.startOffset = aStartOffset;
|
||||
this.endOffset = aEndOffset;
|
||||
this._ignoreAncestry = aIgnoreAncestry;
|
||||
this._includeInvisible = aIncludeInvisible;
|
||||
}
|
||||
|
||||
PivotContext.prototype = {
|
||||
@ -497,7 +505,7 @@ PivotContext.prototype = {
|
||||
*/
|
||||
get oldAncestry() {
|
||||
if (!this._oldAncestry) {
|
||||
if (!this._oldAccessible) {
|
||||
if (!this._oldAccessible || this._ignoreAncestry) {
|
||||
this._oldAncestry = [];
|
||||
} else {
|
||||
this._oldAncestry = this._getAncestry(this._oldAccessible);
|
||||
@ -512,7 +520,8 @@ PivotContext.prototype = {
|
||||
*/
|
||||
get currentAncestry() {
|
||||
if (!this._currentAncestry) {
|
||||
this._currentAncestry = this._getAncestry(this._accessible);
|
||||
this._currentAncestry = this._ignoreAncestry ? [] :
|
||||
this._getAncestry(this._accessible);
|
||||
}
|
||||
return this._currentAncestry;
|
||||
},
|
||||
@ -524,7 +533,7 @@ PivotContext.prototype = {
|
||||
*/
|
||||
get newAncestry() {
|
||||
if (!this._newAncestry) {
|
||||
this._newAncestry = [currentAncestor for (
|
||||
this._newAncestry = this._ignoreAncestry ? [] : [currentAncestor for (
|
||||
[index, currentAncestor] of Iterator(this.currentAncestry)) if (
|
||||
currentAncestor !== this.oldAncestry[index])];
|
||||
}
|
||||
@ -543,9 +552,14 @@ PivotContext.prototype = {
|
||||
}
|
||||
let child = aAccessible.firstChild;
|
||||
while (child) {
|
||||
let state = {};
|
||||
child.getState(state, {});
|
||||
if (!(state.value & Ci.nsIAccessibleStates.STATE_INVISIBLE)) {
|
||||
let include;
|
||||
if (this._includeInvisible) {
|
||||
include = true;
|
||||
} else {
|
||||
let [state,] = Utils.getStates(child);
|
||||
include = !(state.value & Ci.nsIAccessibleStates.STATE_INVISIBLE);
|
||||
}
|
||||
if (include) {
|
||||
if (aPreorder) {
|
||||
yield child;
|
||||
[yield node for (node of this._traverse(child, aPreorder, aStop))];
|
||||
@ -703,7 +717,6 @@ PrefCache.prototype = {
|
||||
if (!this.type) {
|
||||
this.type = aBranch.getPrefType(this.name);
|
||||
}
|
||||
|
||||
switch (this.type) {
|
||||
case Ci.nsIPrefBranch.PREF_STRING:
|
||||
return aBranch.getCharPref(this.name);
|
||||
|
@ -18,6 +18,7 @@ test_alive.html \
|
||||
test_braille.html \
|
||||
test_explicit_names.html \
|
||||
test_landmarks.html \
|
||||
test_live_regions.html \
|
||||
test_tables.html \
|
||||
test_utterance_order.html \
|
||||
$(NULL)
|
||||
|
@ -21,6 +21,68 @@ var AccessFuTest = {
|
||||
}
|
||||
},
|
||||
|
||||
_registerListener: function AccessFuTest__registerListener(aWaitForMessage, aListenerFunc) {
|
||||
var listener = {
|
||||
observe: function observe(aMessage) {
|
||||
// Ignore unexpected messages.
|
||||
if (!(aMessage instanceof Components.interfaces.nsIConsoleMessage)) {
|
||||
return;
|
||||
}
|
||||
if (aMessage.message.indexOf(aWaitForMessage) < 0) {
|
||||
return;
|
||||
}
|
||||
aListenerFunc.apply(listener);
|
||||
}
|
||||
};
|
||||
Services.console.registerListener(listener);
|
||||
return listener;
|
||||
},
|
||||
|
||||
on_log: function AccessFuTest_on_log(aWaitForMessage, aListenerFunc) {
|
||||
return this._registerListener(aWaitForMessage, aListenerFunc);
|
||||
},
|
||||
|
||||
off_log: function AccessFuTest_off_log(aListener) {
|
||||
Services.console.unregisterListener(aListener);
|
||||
},
|
||||
|
||||
once_log: function AccessFuTest_once_log(aWaitForMessage, aListenerFunc) {
|
||||
return this._registerListener(aWaitForMessage,
|
||||
function listenAndUnregister() {
|
||||
Services.console.unregisterListener(this);
|
||||
aListenerFunc();
|
||||
});
|
||||
},
|
||||
|
||||
_addObserver: function AccessFuTest__addObserver(aWaitForData, aListener) {
|
||||
var listener = function listener(aSubject, aTopic, aData) {
|
||||
var data = JSON.parse(aData)[1];
|
||||
// Ignore non-relevant outputs.
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
isDeeply(data.details.actions, aWaitForData, "Data is correct");
|
||||
aListener.apply(listener);
|
||||
};
|
||||
Services.obs.addObserver(listener, 'accessfu-output', false);
|
||||
return listener;
|
||||
},
|
||||
|
||||
on: function AccessFuTest_on(aWaitForData, aListener) {
|
||||
return this._addObserver(aWaitForData, aListener);
|
||||
},
|
||||
|
||||
off: function AccessFuTest_off(aListener) {
|
||||
Services.obs.removeObserver(aListener, 'accessfu-output');
|
||||
},
|
||||
|
||||
once: function AccessFuTest_once(aWaitForData, aListener) {
|
||||
return this._addObserver(aWaitForData, function observerAndRemove() {
|
||||
Services.obs.removeObserver(this, 'accessfu-output');
|
||||
aListener();
|
||||
});
|
||||
},
|
||||
|
||||
_waitForExplicitFinish: false,
|
||||
|
||||
waitForExplicitFinish: function AccessFuTest_waitForExplicitFinish() {
|
||||
@ -38,6 +100,7 @@ var AccessFuTest = {
|
||||
SimpleTest.finish();
|
||||
};
|
||||
// Tear down accessibility and make AccessFu stop.
|
||||
SpecialPowers.setIntPref("accessibility.accessfu.notify_output", 0);
|
||||
SpecialPowers.setIntPref("accessibility.accessfu.activate", 0);
|
||||
},
|
||||
|
||||
@ -87,5 +150,6 @@ var AccessFuTest = {
|
||||
|
||||
// Invoke the whole thing.
|
||||
SpecialPowers.setIntPref("accessibility.accessfu.activate", 1);
|
||||
SpecialPowers.setIntPref("accessibility.accessfu.notify_output", 1);
|
||||
}
|
||||
};
|
||||
|
@ -18,52 +18,38 @@
|
||||
AccessFuTest.nextTest();
|
||||
}
|
||||
|
||||
function makeEventManagerListener(waitForMessage, callback) {
|
||||
return {
|
||||
observe: function observe(aMessage) {
|
||||
// Ignore unexpected messages.
|
||||
if (!(aMessage instanceof Components.interfaces.nsIConsoleMessage)) {
|
||||
return;
|
||||
}
|
||||
if (aMessage.message.indexOf(waitForMessage) < 0) {
|
||||
return;
|
||||
}
|
||||
Services.console.unregisterListener(this);
|
||||
callback();
|
||||
}
|
||||
};
|
||||
// Listen for 'EventManager.stop' and enable AccessFu again.
|
||||
function onStop() {
|
||||
ok(true, "EventManager was stopped.");
|
||||
isnot(AccessFu._enabled, true, "AccessFu was disabled.");
|
||||
AccessFuTest.once_log("EventManager.start", AccessFuTest.nextTest);
|
||||
AccessFu._enable();
|
||||
}
|
||||
|
||||
function testEventManagerStartStop() {
|
||||
// Firs listen for initial 'EventManager.start' and disable AccessFu.
|
||||
var initialStartListener = makeEventManagerListener("EventManager.start",
|
||||
function () {
|
||||
ok(true, "EventManager was started.");
|
||||
Services.console.registerListener(stopListener);
|
||||
AccessFu._disable();
|
||||
});
|
||||
// Listen for 'EventManager.stop' and enable AccessFu again.
|
||||
var stopListener = makeEventManagerListener("EventManager.stop",
|
||||
function () {
|
||||
ok(true, "EventManager was stopped.");
|
||||
isnot(AccessFu._enabled, true, "AccessFu was disabled.");
|
||||
Services.console.registerListener(finalStartListener);
|
||||
AccessFu._enable();
|
||||
});
|
||||
// Make sure EventManager is started again.
|
||||
var finalStartListener = makeEventManagerListener("EventManager.start",
|
||||
function () {
|
||||
ok(true, "EventManager was started again.");
|
||||
ok(AccessFu._enabled, "AccessFu was enabled again.");
|
||||
AccessFuTest.finish();
|
||||
});
|
||||
// Make sure EventManager is started again.
|
||||
function onFinalStart() {
|
||||
ok(true, "EventManager was started again.");
|
||||
ok(AccessFu._enabled, "AccessFu was enabled again.");
|
||||
AccessFuTest.finish();
|
||||
}
|
||||
|
||||
Services.console.registerListener(initialStartListener);
|
||||
// Listen for initial 'EventManager.start' and disable AccessFu.
|
||||
function onInitialStart() {
|
||||
ok(true, "EventManager was started.");
|
||||
AccessFuTest.once_log("EventManager.stop", AccessFuTest.nextTest);
|
||||
AccessFu._disable();
|
||||
}
|
||||
|
||||
function init() {
|
||||
AccessFuTest.once_log("EventManager.start", AccessFuTest.nextTest);
|
||||
}
|
||||
|
||||
function doTest() {
|
||||
AccessFuTest.addFunc(confirmAccessFuStart);
|
||||
AccessFuTest.addFunc(testEventManagerStartStop);
|
||||
AccessFuTest.addFunc(init);
|
||||
AccessFuTest.addFunc(onInitialStart);
|
||||
AccessFuTest.addFunc(onStop);
|
||||
AccessFuTest.addFunc(onFinalStart);
|
||||
AccessFuTest.waitForExplicitFinish();
|
||||
AccessFuTest.runTests(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
342
accessible/tests/mochitest/jsat/test_live_regions.html
Normal file
342
accessible/tests/mochitest/jsat/test_live_regions.html
Normal 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>
|
@ -78,9 +78,9 @@ static void Output(const char *fmt, ... )
|
||||
#if MOZ_WINCONSOLE
|
||||
fwprintf_s(stderr, wide_msg);
|
||||
#else
|
||||
MessageBoxW(NULL, wide_msg, L"Firefox", MB_OK
|
||||
| MB_ICONERROR
|
||||
| MB_SETFOREGROUND);
|
||||
MessageBoxW(nullptr, wide_msg, L"Firefox", MB_OK
|
||||
| MB_ICONERROR
|
||||
| MB_SETFOREGROUND);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@ -327,16 +327,16 @@ static int do_main(int argc, char* argv[], nsIFile *xreDirectory)
|
||||
// Check for a metro test harness command line args file
|
||||
HANDLE hTestFile = CreateFileA(path.get(),
|
||||
GENERIC_READ,
|
||||
0, NULL, OPEN_EXISTING,
|
||||
0, nullptr, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
nullptr);
|
||||
if (hTestFile != INVALID_HANDLE_VALUE) {
|
||||
// Typical test harness command line args string is around 100 bytes.
|
||||
char buffer[1024];
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
DWORD bytesRead = 0;
|
||||
if (!ReadFile(hTestFile, (VOID*)buffer, sizeof(buffer)-1,
|
||||
&bytesRead, NULL) || !bytesRead) {
|
||||
&bytesRead, nullptr) || !bytesRead) {
|
||||
CloseHandle(hTestFile);
|
||||
printf("failed to read test file '%s'", testFile);
|
||||
return -1;
|
||||
@ -351,7 +351,7 @@ static int do_main(int argc, char* argv[], nsIFile *xreDirectory)
|
||||
|
||||
char* ptr = buffer;
|
||||
newArgv[0] = ptr;
|
||||
while (*ptr != NULL &&
|
||||
while (*ptr != '\0' &&
|
||||
(ptr - buffer) < sizeof(buffer) &&
|
||||
newArgc < ARRAYSIZE(newArgv)) {
|
||||
if (isspace(*ptr)) {
|
||||
@ -513,13 +513,13 @@ InitXPCOMGlue(const char *argv0, nsIFile **xreDirectory)
|
||||
}
|
||||
if (absfwurl) {
|
||||
CFURLRef xulurl =
|
||||
CFURLCreateCopyAppendingPathComponent(NULL, absfwurl,
|
||||
CFURLCreateCopyAppendingPathComponent(nullptr, absfwurl,
|
||||
CFSTR("XUL.framework"),
|
||||
true);
|
||||
|
||||
if (xulurl) {
|
||||
CFURLRef xpcomurl =
|
||||
CFURLCreateCopyAppendingPathComponent(NULL, xulurl,
|
||||
CFURLCreateCopyAppendingPathComponent(nullptr, xulurl,
|
||||
CFSTR("libxpcom.dylib"),
|
||||
false);
|
||||
|
||||
|
@ -336,7 +336,7 @@ pref("browser.download.manager.scanWhenDone", true);
|
||||
pref("browser.download.manager.resumeOnWakeDelay", 10000);
|
||||
|
||||
// Enables the asynchronous Downloads API in the Downloads Panel.
|
||||
pref("browser.download.useJSTransfer", false);
|
||||
pref("browser.download.useJSTransfer", true);
|
||||
|
||||
// This allows disabling the Downloads Panel in favor of the old interface.
|
||||
pref("browser.download.useToolkitUI", false);
|
||||
|
@ -112,10 +112,7 @@
|
||||
}
|
||||
|
||||
function reloadProvider() {
|
||||
Social.enabled = false;
|
||||
Services.tm.mainThread.dispatch(function() {
|
||||
Social.enabled = true;
|
||||
}, Components.interfaces.nsIThread.DISPATCH_NORMAL);
|
||||
Social.provider.reload();
|
||||
}
|
||||
|
||||
parseQueryString();
|
||||
|
@ -195,21 +195,7 @@ let gGestureSupport = {
|
||||
aEvent.allowedDirections |= isLTR ? aEvent.DIRECTION_RIGHT :
|
||||
aEvent.DIRECTION_LEFT;
|
||||
|
||||
let isVerticalSwipe = false;
|
||||
if (gHistorySwipeAnimation.active) {
|
||||
if (aEvent.direction == aEvent.DIRECTION_UP) {
|
||||
isVerticalSwipe = true;
|
||||
// Force a synchronous scroll to the top of the page.
|
||||
content.scrollTo(content.scrollX, 0);
|
||||
}
|
||||
else if (aEvent.direction == aEvent.DIRECTION_DOWN) {
|
||||
isVerticalSwipe = true;
|
||||
// Force a synchronous scroll to the bottom of the page.
|
||||
content.scrollTo(content.scrollX, content.scrollMaxY);
|
||||
}
|
||||
}
|
||||
|
||||
gHistorySwipeAnimation.startAnimation(isVerticalSwipe);
|
||||
gHistorySwipeAnimation.startAnimation();
|
||||
|
||||
this._doUpdate = function GS__doUpdate(aEvent) {
|
||||
gHistorySwipeAnimation.updateAnimation(aEvent.delta);
|
||||
@ -563,13 +549,10 @@ let gHistorySwipeAnimation = {
|
||||
this.isLTR = document.documentElement.mozMatchesSelector(
|
||||
":-moz-locale-dir(ltr)");
|
||||
this._trackedSnapshots = [];
|
||||
this._startingIndex = -1;
|
||||
this._historyIndex = -1;
|
||||
this._boxWidth = -1;
|
||||
this._boxHeight = -1;
|
||||
this._maxSnapshots = this._getMaxSnapshots();
|
||||
this._lastSwipeDir = "";
|
||||
this._isVerticalSwipe = false;
|
||||
|
||||
// We only want to activate history swipe animations if we store snapshots.
|
||||
// If we don't store any, we handle horizontal swipes without animations.
|
||||
@ -578,7 +561,6 @@ let gHistorySwipeAnimation = {
|
||||
gBrowser.addEventListener("pagehide", this, false);
|
||||
gBrowser.addEventListener("pageshow", this, false);
|
||||
gBrowser.addEventListener("popstate", this, false);
|
||||
gBrowser.addEventListener("DOMModalDialogClosed", this, false);
|
||||
gBrowser.tabContainer.addEventListener("TabClose", this, false);
|
||||
}
|
||||
},
|
||||
@ -590,7 +572,6 @@ let gHistorySwipeAnimation = {
|
||||
gBrowser.removeEventListener("pagehide", this, false);
|
||||
gBrowser.removeEventListener("pageshow", this, false);
|
||||
gBrowser.removeEventListener("popstate", this, false);
|
||||
gBrowser.removeEventListener("DOMModalDialogClosed", this, false);
|
||||
gBrowser.tabContainer.removeEventListener("TabClose", this, false);
|
||||
|
||||
this.active = false;
|
||||
@ -600,32 +581,17 @@ let gHistorySwipeAnimation = {
|
||||
/**
|
||||
* Starts the swipe animation and handles fast swiping (i.e. a swipe animation
|
||||
* is already in progress when a new one is initiated).
|
||||
*
|
||||
* @param aIsVerticalSwipe
|
||||
* Whether we're dealing with a vertical swipe or not.
|
||||
*/
|
||||
startAnimation: function HSA_startAnimation(aIsVerticalSwipe) {
|
||||
this._isVerticalSwipe = aIsVerticalSwipe;
|
||||
|
||||
startAnimation: function HSA_startAnimation() {
|
||||
if (this.isAnimationRunning()) {
|
||||
// If this is a horizontal scroll, or if this is a vertical scroll that
|
||||
// was started while a horizontal scroll was still running, handle it as
|
||||
// as a fast swipe. In the case of the latter scenario, this allows us to
|
||||
// start the vertical animation without first loading the final page, or
|
||||
// taking another snapshot. If vertical scrolls are initiated repeatedly
|
||||
// without prior horizontal scroll we skip this and restart the animation
|
||||
// from 0.
|
||||
if (!this._isVerticalSwipe || this._lastSwipeDir != "") {
|
||||
gBrowser.stop();
|
||||
this._lastSwipeDir = "RELOAD"; // just ensure that != ""
|
||||
this._canGoBack = this.canGoBack();
|
||||
this._canGoForward = this.canGoForward();
|
||||
this._handleFastSwiping();
|
||||
}
|
||||
gBrowser.stop();
|
||||
this._lastSwipeDir = "RELOAD"; // just ensure that != ""
|
||||
this._canGoBack = this.canGoBack();
|
||||
this._canGoForward = this.canGoForward();
|
||||
this._handleFastSwiping();
|
||||
}
|
||||
else {
|
||||
this._startingIndex = gBrowser.webNavigation.sessionHistory.index;
|
||||
this._historyIndex = this._startingIndex;
|
||||
this._historyIndex = gBrowser.webNavigation.sessionHistory.index;
|
||||
this._canGoBack = this.canGoBack();
|
||||
this._canGoForward = this.canGoForward();
|
||||
if (this.active) {
|
||||
@ -656,29 +622,20 @@ let gHistorySwipeAnimation = {
|
||||
if (!this.isAnimationRunning())
|
||||
return;
|
||||
|
||||
// We use the following value to decrease the bounce effect when scrolling
|
||||
// to the top or bottom of the page, or when swiping back/forward past the
|
||||
// browsing history. This value was determined experimentally.
|
||||
let dampValue = 4;
|
||||
if (this._isVerticalSwipe) {
|
||||
this._prevBox.collapsed = true;
|
||||
this._nextBox.collapsed = true;
|
||||
this._positionBox(this._curBox, -1 * aVal / dampValue);
|
||||
}
|
||||
else if ((aVal >= 0 && this.isLTR) ||
|
||||
(aVal <= 0 && !this.isLTR)) {
|
||||
let tempDampValue = 1;
|
||||
if ((aVal >= 0 && this.isLTR) ||
|
||||
(aVal <= 0 && !this.isLTR)) {
|
||||
if (aVal > 1)
|
||||
aVal = 1; // Cap value to avoid sliding the page further than allowed.
|
||||
|
||||
if (this._canGoBack)
|
||||
this._prevBox.collapsed = false;
|
||||
else {
|
||||
tempDampValue = dampValue;
|
||||
else
|
||||
this._prevBox.collapsed = true;
|
||||
}
|
||||
|
||||
// The current page is pushed to the right (LTR) or left (RTL),
|
||||
// the intention is to go back.
|
||||
// If there is a page to go back to, it should show in the background.
|
||||
this._positionBox(this._curBox, aVal / tempDampValue);
|
||||
this._positionBox(this._curBox, aVal);
|
||||
|
||||
// The forward page should be pushed offscreen all the way to the right.
|
||||
this._positionBox(this._nextBox, 1);
|
||||
@ -694,14 +651,13 @@ let gHistorySwipeAnimation = {
|
||||
// For the backdrop to be visible in that case, the previous page needs
|
||||
// to be hidden (if it exists).
|
||||
if (this._canGoForward) {
|
||||
this._nextBox.collapsed = false;
|
||||
let offset = this.isLTR ? 1 : -1;
|
||||
this._positionBox(this._curBox, 0);
|
||||
this._positionBox(this._nextBox, offset + aVal);
|
||||
this._positionBox(this._nextBox, offset + aVal); // aVal is negative
|
||||
}
|
||||
else {
|
||||
this._prevBox.collapsed = true;
|
||||
this._positionBox(this._curBox, aVal / dampValue);
|
||||
this._positionBox(this._curBox, aVal);
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -718,14 +674,13 @@ let gHistorySwipeAnimation = {
|
||||
let browser = gBrowser.getBrowserForTab(aEvent.target);
|
||||
this._removeTrackedSnapshot(-1, browser);
|
||||
break;
|
||||
case "DOMModalDialogClosed":
|
||||
this.stopAnimation();
|
||||
break;
|
||||
case "pageshow":
|
||||
case "popstate":
|
||||
if (aEvent.target != gBrowser.selectedBrowser.contentDocument)
|
||||
break;
|
||||
this.stopAnimation();
|
||||
if (this.isAnimationRunning()) {
|
||||
if (aEvent.target != gBrowser.selectedBrowser.contentDocument)
|
||||
break;
|
||||
this.stopAnimation();
|
||||
}
|
||||
this._historyIndex = gBrowser.webNavigation.sessionHistory.index;
|
||||
break;
|
||||
case "pagehide":
|
||||
@ -793,7 +748,7 @@ let gHistorySwipeAnimation = {
|
||||
* any. This will also result in the animation overlay to be torn down.
|
||||
*/
|
||||
swipeEndEventReceived: function HSA_swipeEndEventReceived() {
|
||||
if (this._lastSwipeDir != "" && this._historyIndex != this._startingIndex)
|
||||
if (this._lastSwipeDir != "")
|
||||
this._navigateToHistoryIndex();
|
||||
else
|
||||
this.stopAnimation();
|
||||
@ -821,10 +776,9 @@ let gHistorySwipeAnimation = {
|
||||
* |this|.
|
||||
*/
|
||||
_navigateToHistoryIndex: function HSA__navigateToHistoryIndex() {
|
||||
if (this._doesIndexExistInHistory(this._historyIndex))
|
||||
if (this._doesIndexExistInHistory(this._historyIndex)) {
|
||||
gBrowser.webNavigation.gotoIndex(this._historyIndex);
|
||||
else
|
||||
this.stopAnimation();
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
@ -870,9 +824,7 @@ let gHistorySwipeAnimation = {
|
||||
"box");
|
||||
this._container.appendChild(this._nextBox);
|
||||
|
||||
// Cache width and height.
|
||||
this._boxWidth = this._curBox.getBoundingClientRect().width;
|
||||
this._boxHeight = this._curBox.getBoundingClientRect().height;
|
||||
this._boxWidth = this._curBox.getBoundingClientRect().width; // cache width
|
||||
},
|
||||
|
||||
/**
|
||||
@ -886,7 +838,6 @@ let gHistorySwipeAnimation = {
|
||||
this._container.parentNode.removeChild(this._container);
|
||||
this._container = null;
|
||||
this._boxWidth = -1;
|
||||
this._boxHeight = -1;
|
||||
},
|
||||
|
||||
/**
|
||||
@ -914,14 +865,7 @@ let gHistorySwipeAnimation = {
|
||||
* The position (in X coordinates) to move the box element to.
|
||||
*/
|
||||
_positionBox: function HSA__positionBox(aBox, aPosition) {
|
||||
let transform = "";
|
||||
|
||||
if (this._isVerticalSwipe)
|
||||
transform = "translateY(" + this._boxHeight * aPosition + "px)";
|
||||
else
|
||||
transform = "translateX(" + this._boxWidth * aPosition + "px)";
|
||||
|
||||
aBox.style.transform = transform;
|
||||
aBox.style.transform = "translateX(" + this._boxWidth * aPosition + "px)";
|
||||
},
|
||||
|
||||
/**
|
||||
@ -1060,17 +1004,12 @@ let gHistorySwipeAnimation = {
|
||||
return aBlob;
|
||||
|
||||
let img = new Image();
|
||||
let url = "";
|
||||
try {
|
||||
url = URL.createObjectURL(aBlob);
|
||||
img.onload = function() {
|
||||
URL.revokeObjectURL(url);
|
||||
};
|
||||
}
|
||||
finally {
|
||||
img.src = url;
|
||||
return img;
|
||||
}
|
||||
let url = URL.createObjectURL(aBlob);
|
||||
img.onload = function() {
|
||||
URL.revokeObjectURL(url);
|
||||
};
|
||||
img.src = url;
|
||||
return img;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -36,6 +36,7 @@ SocialUI = {
|
||||
Services.obs.addObserver(this, "social:frameworker-error", false);
|
||||
Services.obs.addObserver(this, "social:provider-set", false);
|
||||
Services.obs.addObserver(this, "social:providers-changed", false);
|
||||
Services.obs.addObserver(this, "social:provider-reload", false);
|
||||
|
||||
Services.prefs.addObserver("social.sidebar.open", this, false);
|
||||
Services.prefs.addObserver("social.toast-notifications.enabled", this, false);
|
||||
@ -60,6 +61,7 @@ SocialUI = {
|
||||
Services.obs.removeObserver(this, "social:frameworker-error");
|
||||
Services.obs.removeObserver(this, "social:provider-set");
|
||||
Services.obs.removeObserver(this, "social:providers-changed");
|
||||
Services.obs.removeObserver(this, "social:provider-reload");
|
||||
|
||||
Services.prefs.removeObserver("social.sidebar.open", this);
|
||||
Services.prefs.removeObserver("social.toast-notifications.enabled", this);
|
||||
@ -74,6 +76,16 @@ SocialUI = {
|
||||
// manually :(
|
||||
try {
|
||||
switch (topic) {
|
||||
case "social:provider-reload":
|
||||
// if the reloaded provider is our current provider, fall through
|
||||
// to social:provider-set so the ui will be reset
|
||||
if (!Social.provider || Social.provider.origin != data)
|
||||
return;
|
||||
// be sure to unload the sidebar as it will not reload if the origin
|
||||
// has not changed, it will be loaded in provider-set below. Other
|
||||
// panels will be unloaded or handle reload.
|
||||
SocialSidebar.unloadSidebar();
|
||||
// fall through to social:provider-set
|
||||
case "social:provider-set":
|
||||
// Social.provider has changed (possibly to null), update any state
|
||||
// which depends on it.
|
||||
@ -142,7 +154,7 @@ SocialUI = {
|
||||
|
||||
// Miscellaneous helpers
|
||||
showProfile: function SocialUI_showProfile() {
|
||||
if (Social.haveLoggedInUser())
|
||||
if (Social.provider.haveLoggedInUser())
|
||||
openUILinkIn(Social.provider.profile.profileURL, "tab");
|
||||
else {
|
||||
// XXX Bug 789585 will implement an API for provider-specified login pages.
|
||||
@ -976,22 +988,20 @@ SocialToolbar = {
|
||||
let toggleNotificationsCommand = document.getElementById("Social:ToggleNotifications");
|
||||
toggleNotificationsCommand.setAttribute("hidden", !socialEnabled);
|
||||
|
||||
if (!Social.haveLoggedInUser() || !socialEnabled) {
|
||||
let parent = document.getElementById("social-notification-panel");
|
||||
while (parent.hasChildNodes()) {
|
||||
let frame = parent.firstChild;
|
||||
SharedFrame.forgetGroup(frame.id);
|
||||
parent.removeChild(frame);
|
||||
}
|
||||
let parent = document.getElementById("social-notification-panel");
|
||||
while (parent.hasChildNodes()) {
|
||||
let frame = parent.firstChild;
|
||||
SharedFrame.forgetGroup(frame.id);
|
||||
parent.removeChild(frame);
|
||||
}
|
||||
|
||||
let tbi = document.getElementById("social-toolbar-item");
|
||||
if (tbi) {
|
||||
// SocialMark is the last button allways
|
||||
let next = SocialMark.button.previousSibling;
|
||||
while (next != this.button) {
|
||||
tbi.removeChild(next);
|
||||
next = SocialMark.button.previousSibling;
|
||||
}
|
||||
let tbi = document.getElementById("social-toolbar-item");
|
||||
if (tbi) {
|
||||
// SocialMark is the last button allways
|
||||
let next = SocialMark.button.previousSibling;
|
||||
while (next != this.button) {
|
||||
tbi.removeChild(next);
|
||||
next = SocialMark.button.previousSibling;
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -1035,7 +1045,7 @@ SocialToolbar = {
|
||||
// provider.profile == undefined means no response yet from the provider
|
||||
// to tell us whether the user is logged in or not.
|
||||
if (!SocialUI.enabled ||
|
||||
(!Social.haveLoggedInUser() && Social.provider.profile !== undefined)) {
|
||||
(!Social.provider.haveLoggedInUser() && Social.provider.profile !== undefined)) {
|
||||
// Either no enabled provider, or there is a provider and it has
|
||||
// responded with a profile and the user isn't loggedin. The icons
|
||||
// etc have already been removed by updateButtonHiddenState, so we want
|
||||
|
@ -905,10 +905,7 @@ nsContextMenu.prototype = {
|
||||
reload: function(event) {
|
||||
if (this.onSocial) {
|
||||
// full reload of social provider
|
||||
Social.enabled = false;
|
||||
Services.tm.mainThread.dispatch(function() {
|
||||
Social.enabled = true;
|
||||
}, Components.interfaces.nsIThread.DISPATCH_NORMAL);
|
||||
Social.provider.reload();
|
||||
} else {
|
||||
BrowserReloadOrDuplicate(event);
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||
"resource://gre/modules/Task.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
|
||||
"resource:///modules/DownloadsCommon.jsm");
|
||||
|
||||
|
||||
function Sanitizer() {}
|
||||
Sanitizer.prototype = {
|
||||
// warning to the caller: this one may raise an exception (e.g. bug #265028)
|
||||
@ -37,14 +37,14 @@ Sanitizer.prototype = {
|
||||
aCallback(aItemName, canClear, aArg);
|
||||
return canClear;
|
||||
},
|
||||
|
||||
|
||||
prefDomain: "",
|
||||
|
||||
|
||||
getNameFromPreference: function (aPreferenceName)
|
||||
{
|
||||
return aPreferenceName.substr(this.prefDomain.length);
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Deletes privacy sensitive data in a batch, according to user preferences.
|
||||
* Returns a promise which is resolved if no errors occurred. If an error
|
||||
@ -87,7 +87,8 @@ Sanitizer.prototype = {
|
||||
item.clear();
|
||||
} catch(er) {
|
||||
seenError = true;
|
||||
Cu.reportError("Error sanitizing " + itemName + ": " + er + "\n");
|
||||
Components.utils.reportError("Error sanitizing " + itemName +
|
||||
": " + er + "\n");
|
||||
}
|
||||
onItemComplete();
|
||||
};
|
||||
@ -99,7 +100,7 @@ Sanitizer.prototype = {
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
|
||||
// Time span only makes sense in certain cases. Consumers who want
|
||||
// to only clear some private data can opt in by setting this to false,
|
||||
// and can optionally specify a specific range. If timespan is not ignored,
|
||||
@ -107,7 +108,7 @@ Sanitizer.prototype = {
|
||||
// pref to determine a range
|
||||
ignoreTimespan : true,
|
||||
range : null,
|
||||
|
||||
|
||||
items: {
|
||||
cache: {
|
||||
clear: function ()
|
||||
@ -126,13 +127,13 @@ Sanitizer.prototype = {
|
||||
imageCache.clearCache(false); // true=chrome, false=content
|
||||
} catch(er) {}
|
||||
},
|
||||
|
||||
|
||||
get canClear()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
cookies: {
|
||||
clear: function ()
|
||||
{
|
||||
@ -143,7 +144,7 @@ Sanitizer.prototype = {
|
||||
var cookiesEnum = cookieMgr.enumerator;
|
||||
while (cookiesEnum.hasMoreElements()) {
|
||||
var cookie = cookiesEnum.getNext().QueryInterface(Ci.nsICookie2);
|
||||
|
||||
|
||||
if (cookie.creationTime > this.range[0])
|
||||
// This cookie was created after our cutoff, clear it
|
||||
cookieMgr.remove(cookie.host, cookie.name, cookie.path, false);
|
||||
@ -211,14 +212,14 @@ Sanitizer.prototype = {
|
||||
PlacesUtils.history.removeVisitsByTimeframe(this.range[0], this.range[1]);
|
||||
else
|
||||
PlacesUtils.history.removeAllPages();
|
||||
|
||||
|
||||
try {
|
||||
var os = Components.classes["@mozilla.org/observer-service;1"]
|
||||
.getService(Components.interfaces.nsIObserverService);
|
||||
os.notifyObservers(null, "browser:purge-session-history", "");
|
||||
}
|
||||
catch (e) { }
|
||||
|
||||
|
||||
// Clear last URL of the Open Web Location dialog
|
||||
var prefs = Components.classes["@mozilla.org/preferences-service;1"]
|
||||
.getService(Components.interfaces.nsIPrefBranch);
|
||||
@ -227,7 +228,7 @@ Sanitizer.prototype = {
|
||||
}
|
||||
catch (e) { }
|
||||
},
|
||||
|
||||
|
||||
get canClear()
|
||||
{
|
||||
// bug 347231: Always allow clearing history due to dependencies on
|
||||
@ -235,7 +236,7 @@ Sanitizer.prototype = {
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
formdata: {
|
||||
clear: function ()
|
||||
{
|
||||
@ -305,15 +306,20 @@ Sanitizer.prototype = {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
downloads: {
|
||||
clear: function ()
|
||||
{
|
||||
if (DownloadsCommon.useJSTransfer) {
|
||||
Task.spawn(function () {
|
||||
let filterByTime = this.range ?
|
||||
(download => download.startTime >= this.range[0] &&
|
||||
download.startTime <= this.range[1]) : null;
|
||||
let filterByTime = null;
|
||||
if (this.range) {
|
||||
// Convert microseconds back to milliseconds for date comparisons.
|
||||
let rangeBeginMs = this.range[0] / 1000;
|
||||
let rangeEndMs = this.range[1] / 1000;
|
||||
filterByTime = download => download.startTime >= rangeBeginMs &&
|
||||
download.startTime <= rangeEndMs;
|
||||
}
|
||||
|
||||
// Clear all completed/cancelled downloads
|
||||
let publicList = yield Downloads.getPublicDownloadList();
|
||||
@ -321,7 +327,7 @@ Sanitizer.prototype = {
|
||||
|
||||
let privateList = yield Downloads.getPrivateDownloadList();
|
||||
privateList.removeFinished(filterByTime);
|
||||
}.bind(this)).then(null, Cu.reportError);
|
||||
}.bind(this)).then(null, Components.utils.reportError);
|
||||
}
|
||||
else {
|
||||
var dlMgr = Components.classes["@mozilla.org/download-manager;1"]
|
||||
@ -352,7 +358,7 @@ Sanitizer.prototype = {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
passwords: {
|
||||
clear: function ()
|
||||
{
|
||||
@ -361,7 +367,7 @@ Sanitizer.prototype = {
|
||||
// Passwords are timeless, and don't respect the timeSpan setting
|
||||
pwmgr.removeAllLogins();
|
||||
},
|
||||
|
||||
|
||||
get canClear()
|
||||
{
|
||||
var pwmgr = Components.classes["@mozilla.org/login-manager;1"]
|
||||
@ -370,7 +376,7 @@ Sanitizer.prototype = {
|
||||
return (count > 0);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
sessions: {
|
||||
clear: function ()
|
||||
{
|
||||
@ -384,13 +390,13 @@ Sanitizer.prototype = {
|
||||
.getService(Components.interfaces.nsIObserverService);
|
||||
os.notifyObservers(null, "net:clear-active-logins", null);
|
||||
},
|
||||
|
||||
|
||||
get canClear()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
siteSettings: {
|
||||
clear: function ()
|
||||
{
|
||||
@ -398,12 +404,12 @@ Sanitizer.prototype = {
|
||||
var pm = Components.classes["@mozilla.org/permissionmanager;1"]
|
||||
.getService(Components.interfaces.nsIPermissionManager);
|
||||
pm.removeAll();
|
||||
|
||||
|
||||
// Clear site-specific settings like page-zoom level
|
||||
var cps = Components.classes["@mozilla.org/content-pref/service;1"]
|
||||
.getService(Components.interfaces.nsIContentPrefService2);
|
||||
cps.removeAllDomains(null);
|
||||
|
||||
|
||||
// Clear "Never remember passwords for this site", which is not handled by
|
||||
// the permission manager
|
||||
var pwmgr = Components.classes["@mozilla.org/login-manager;1"]
|
||||
@ -413,7 +419,7 @@ Sanitizer.prototype = {
|
||||
pwmgr.setLoginSavingEnabled(host, true);
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
get canClear()
|
||||
{
|
||||
return true;
|
||||
@ -446,7 +452,7 @@ Sanitizer.getClearRange = function (ts) {
|
||||
ts = Sanitizer.prefs.getIntPref("timeSpan");
|
||||
if (ts === Sanitizer.TIMESPAN_EVERYTHING)
|
||||
return null;
|
||||
|
||||
|
||||
// PRTime is microseconds while JS time is milliseconds
|
||||
var endDate = Date.now() * 1000;
|
||||
switch (ts) {
|
||||
@ -473,7 +479,7 @@ Sanitizer.getClearRange = function (ts) {
|
||||
};
|
||||
|
||||
Sanitizer._prefs = null;
|
||||
Sanitizer.__defineGetter__("prefs", function()
|
||||
Sanitizer.__defineGetter__("prefs", function()
|
||||
{
|
||||
return Sanitizer._prefs ? Sanitizer._prefs
|
||||
: Sanitizer._prefs = Components.classes["@mozilla.org/preferences-service;1"]
|
||||
@ -482,7 +488,7 @@ Sanitizer.__defineGetter__("prefs", function()
|
||||
});
|
||||
|
||||
// Shows sanitization UI
|
||||
Sanitizer.showUI = function(aParentWindow)
|
||||
Sanitizer.showUI = function(aParentWindow)
|
||||
{
|
||||
var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
|
||||
.getService(Components.interfaces.nsIWindowWatcher);
|
||||
@ -497,32 +503,32 @@ Sanitizer.showUI = function(aParentWindow)
|
||||
null);
|
||||
};
|
||||
|
||||
/**
|
||||
* Deletes privacy sensitive data in a batch, optionally showing the
|
||||
/**
|
||||
* Deletes privacy sensitive data in a batch, optionally showing the
|
||||
* sanitize UI, according to user preferences
|
||||
*/
|
||||
Sanitizer.sanitize = function(aParentWindow)
|
||||
Sanitizer.sanitize = function(aParentWindow)
|
||||
{
|
||||
Sanitizer.showUI(aParentWindow);
|
||||
};
|
||||
|
||||
Sanitizer.onStartup = function()
|
||||
Sanitizer.onStartup = function()
|
||||
{
|
||||
// we check for unclean exit with pending sanitization
|
||||
Sanitizer._checkAndSanitize();
|
||||
};
|
||||
|
||||
Sanitizer.onShutdown = function()
|
||||
Sanitizer.onShutdown = function()
|
||||
{
|
||||
// we check if sanitization is needed and perform it
|
||||
Sanitizer._checkAndSanitize();
|
||||
};
|
||||
|
||||
// this is called on startup and shutdown, to perform pending sanitizations
|
||||
Sanitizer._checkAndSanitize = function()
|
||||
Sanitizer._checkAndSanitize = function()
|
||||
{
|
||||
const prefs = Sanitizer.prefs;
|
||||
if (prefs.getBoolPref(Sanitizer.prefShutdown) &&
|
||||
if (prefs.getBoolPref(Sanitizer.prefShutdown) &&
|
||||
!prefs.prefHasUserValue(Sanitizer.prefDidShutdown)) {
|
||||
// this is a shutdown or a startup after an unclean exit
|
||||
var s = new Sanitizer();
|
||||
|
@ -2,10 +2,10 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// Bug 453440 - Test the timespan-based logic of the sanitizer code
|
||||
var now_uSec = Date.now() * 1000;
|
||||
|
||||
const dm = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager);
|
||||
let now_mSec = Date.now();
|
||||
let now_uSec = now_mSec * 1000;
|
||||
|
||||
const kMsecPerMin = 60 * 1000;
|
||||
const kUsecPerMin = 60 * 1000000;
|
||||
|
||||
let tempScope = {};
|
||||
@ -14,6 +14,7 @@ Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
|
||||
let Sanitizer = tempScope.Sanitizer;
|
||||
|
||||
let FormHistory = (Components.utils.import("resource://gre/modules/FormHistory.jsm", {})).FormHistory;
|
||||
let Downloads = (Components.utils.import("resource://gre/modules/Downloads.jsm", {})).Downloads;
|
||||
|
||||
function promiseFormHistoryRemoved() {
|
||||
let deferred = Promise.defer();
|
||||
@ -24,15 +25,30 @@ function promiseFormHistoryRemoved() {
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function promiseDownloadRemoved(list) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
let view = {
|
||||
onDownloadRemoved: function(download) {
|
||||
list.removeView(view);
|
||||
deferred.resolve();
|
||||
}
|
||||
};
|
||||
|
||||
list.addView(view);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
Task.spawn(function() {
|
||||
setupDownloads();
|
||||
yield setupDownloads();
|
||||
yield setupFormHistory();
|
||||
yield setupHistory();
|
||||
yield onHistoryReady();
|
||||
}).then(finish);
|
||||
}).then(null, ex => ok(false, ex)).then(finish);
|
||||
}
|
||||
|
||||
function countEntries(name, message, check) {
|
||||
@ -80,12 +96,16 @@ function onHistoryReady() {
|
||||
itemPrefs.setBoolPref("sessions", false);
|
||||
itemPrefs.setBoolPref("siteSettings", false);
|
||||
|
||||
let publicList = yield Downloads.getPublicDownloadList();
|
||||
let downloadPromise = promiseDownloadRemoved(publicList);
|
||||
|
||||
// Clear 10 minutes ago
|
||||
s.range = [now_uSec - 10*60*1000000, now_uSec];
|
||||
s.sanitize();
|
||||
s.range = null;
|
||||
|
||||
yield promiseFormHistoryRemoved();
|
||||
yield downloadPromise;
|
||||
|
||||
ok(!(yield promiseIsURIVisited(makeURI("http://10minutes.com"))),
|
||||
"Pretend visit to 10minutes.com should now be deleted");
|
||||
@ -122,23 +142,26 @@ function onHistoryReady() {
|
||||
yield countEntries("today", "today form entry should still exist", checkOne);
|
||||
yield countEntries("b4today", "b4today form entry should still exist", checkOne);
|
||||
|
||||
ok(!downloadExists(5555555), "10 minute download should now be deleted");
|
||||
ok(downloadExists(5555551), "<1 hour download should still be present");
|
||||
ok(downloadExists(5555556), "1 hour 10 minute download should still be present");
|
||||
ok(downloadExists(5555550), "Year old download should still be present");
|
||||
ok(downloadExists(5555552), "<2 hour old download should still be present");
|
||||
ok(downloadExists(5555557), "2 hour 10 minute download should still be present");
|
||||
ok(downloadExists(5555553), "<4 hour old download should still be present");
|
||||
ok(downloadExists(5555558), "4 hour 10 minute download should still be present");
|
||||
ok(!(yield downloadExists(publicList, "fakefile-10-minutes")), "10 minute download should now be deleted");
|
||||
ok((yield downloadExists(publicList, "fakefile-1-hour")), "<1 hour download should still be present");
|
||||
ok((yield downloadExists(publicList, "fakefile-1-hour-10-minutes")), "1 hour 10 minute download should still be present");
|
||||
ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present");
|
||||
ok((yield downloadExists(publicList, "fakefile-2-hour")), "<2 hour old download should still be present");
|
||||
ok((yield downloadExists(publicList, "fakefile-2-hour-10-minutes")), "2 hour 10 minute download should still be present");
|
||||
ok((yield downloadExists(publicList, "fakefile-4-hour")), "<4 hour old download should still be present");
|
||||
ok((yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "4 hour 10 minute download should still be present");
|
||||
|
||||
if (minutesSinceMidnight > 10)
|
||||
ok(downloadExists(5555554), "'Today' download should still be present");
|
||||
ok((yield downloadExists(publicList, "fakefile-today")), "'Today' download should still be present");
|
||||
|
||||
downloadPromise = promiseDownloadRemoved(publicList);
|
||||
|
||||
// Clear 1 hour
|
||||
Sanitizer.prefs.setIntPref("timeSpan", 1);
|
||||
s.sanitize();
|
||||
|
||||
yield promiseFormHistoryRemoved();
|
||||
yield downloadPromise;
|
||||
|
||||
ok(!(yield promiseIsURIVisited(makeURI("http://1hour.com"))),
|
||||
"Pretend visit to 1hour.com should now be deleted");
|
||||
@ -169,23 +192,26 @@ function onHistoryReady() {
|
||||
yield countEntries("today", "today form entry should still exist", checkOne);
|
||||
yield countEntries("b4today", "b4today form entry should still exist", checkOne);
|
||||
|
||||
ok(!downloadExists(5555551), "<1 hour download should now be deleted");
|
||||
ok(downloadExists(5555556), "1 hour 10 minute download should still be present");
|
||||
ok(downloadExists(5555550), "Year old download should still be present");
|
||||
ok(downloadExists(5555552), "<2 hour old download should still be present");
|
||||
ok(downloadExists(5555557), "2 hour 10 minute download should still be present");
|
||||
ok(downloadExists(5555553), "<4 hour old download should still be present");
|
||||
ok(downloadExists(5555558), "4 hour 10 minute download should still be present");
|
||||
ok(!(yield downloadExists(publicList, "fakefile-1-hour")), "<1 hour download should now be deleted");
|
||||
ok((yield downloadExists(publicList, "fakefile-1-hour-10-minutes")), "1 hour 10 minute download should still be present");
|
||||
ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present");
|
||||
ok((yield downloadExists(publicList, "fakefile-2-hour")), "<2 hour old download should still be present");
|
||||
ok((yield downloadExists(publicList, "fakefile-2-hour-10-minutes")), "2 hour 10 minute download should still be present");
|
||||
ok((yield downloadExists(publicList, "fakefile-4-hour")), "<4 hour old download should still be present");
|
||||
ok((yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "4 hour 10 minute download should still be present");
|
||||
|
||||
if (hoursSinceMidnight > 1)
|
||||
ok(downloadExists(5555554), "'Today' download should still be present");
|
||||
ok((yield downloadExists(publicList, "fakefile-today")), "'Today' download should still be present");
|
||||
|
||||
downloadPromise = promiseDownloadRemoved(publicList);
|
||||
|
||||
// Clear 1 hour 10 minutes
|
||||
s.range = [now_uSec - 70*60*1000000, now_uSec];
|
||||
s.sanitize();
|
||||
s.range = null;
|
||||
|
||||
yield promiseFormHistoryRemoved();
|
||||
yield downloadPromise;
|
||||
|
||||
ok(!(yield promiseIsURIVisited(makeURI("http://1hour10minutes.com"))),
|
||||
"Pretend visit to 1hour10minutes.com should now be deleted");
|
||||
@ -213,20 +239,23 @@ function onHistoryReady() {
|
||||
yield countEntries("today", "today form entry should still exist", checkOne);
|
||||
yield countEntries("b4today", "b4today form entry should still exist", checkOne);
|
||||
|
||||
ok(!downloadExists(5555556), "1 hour 10 minute old download should now be deleted");
|
||||
ok(downloadExists(5555550), "Year old download should still be present");
|
||||
ok(downloadExists(5555552), "<2 hour old download should still be present");
|
||||
ok(downloadExists(5555557), "2 hour 10 minute download should still be present");
|
||||
ok(downloadExists(5555553), "<4 hour old download should still be present");
|
||||
ok(downloadExists(5555558), "4 hour 10 minute download should still be present");
|
||||
ok(!(yield downloadExists(publicList, "fakefile-1-hour-10-minutes")), "1 hour 10 minute old download should now be deleted");
|
||||
ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present");
|
||||
ok((yield downloadExists(publicList, "fakefile-2-hour")), "<2 hour old download should still be present");
|
||||
ok((yield downloadExists(publicList, "fakefile-2-hour-10-minutes")), "2 hour 10 minute download should still be present");
|
||||
ok((yield downloadExists(publicList, "fakefile-4-hour")), "<4 hour old download should still be present");
|
||||
ok((yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "4 hour 10 minute download should still be present");
|
||||
if (minutesSinceMidnight > 70)
|
||||
ok(downloadExists(5555554), "'Today' download should still be present");
|
||||
ok((yield downloadExists(publicList, "fakefile-today")), "'Today' download should still be present");
|
||||
|
||||
downloadPromise = promiseDownloadRemoved(publicList);
|
||||
|
||||
// Clear 2 hours
|
||||
Sanitizer.prefs.setIntPref("timeSpan", 2);
|
||||
s.sanitize();
|
||||
|
||||
yield promiseFormHistoryRemoved();
|
||||
yield downloadPromise;
|
||||
|
||||
ok(!(yield promiseIsURIVisited(makeURI("http://2hour.com"))),
|
||||
"Pretend visit to 2hour.com should now be deleted");
|
||||
@ -251,20 +280,23 @@ function onHistoryReady() {
|
||||
yield countEntries("today", "today form entry should still exist", checkOne);
|
||||
yield countEntries("b4today", "b4today form entry should still exist", checkOne);
|
||||
|
||||
ok(!downloadExists(5555552), "<2 hour old download should now be deleted");
|
||||
ok(downloadExists(5555550), "Year old download should still be present");
|
||||
ok(downloadExists(5555557), "2 hour 10 minute download should still be present");
|
||||
ok(downloadExists(5555553), "<4 hour old download should still be present");
|
||||
ok(downloadExists(5555558), "4 hour 10 minute download should still be present");
|
||||
ok(!(yield downloadExists(publicList, "fakefile-2-hour")), "<2 hour old download should now be deleted");
|
||||
ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present");
|
||||
ok((yield downloadExists(publicList, "fakefile-2-hour-10-minutes")), "2 hour 10 minute download should still be present");
|
||||
ok((yield downloadExists(publicList, "fakefile-4-hour")), "<4 hour old download should still be present");
|
||||
ok((yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "4 hour 10 minute download should still be present");
|
||||
if (hoursSinceMidnight > 2)
|
||||
ok(downloadExists(5555554), "'Today' download should still be present");
|
||||
ok((yield downloadExists(publicList, "fakefile-today")), "'Today' download should still be present");
|
||||
|
||||
downloadPromise = promiseDownloadRemoved(publicList);
|
||||
|
||||
// Clear 2 hours 10 minutes
|
||||
s.range = [now_uSec - 130*60*1000000, now_uSec];
|
||||
s.sanitize();
|
||||
s.range = null;
|
||||
|
||||
yield promiseFormHistoryRemoved();
|
||||
yield downloadPromise;
|
||||
|
||||
ok(!(yield promiseIsURIVisited(makeURI("http://2hour10minutes.com"))),
|
||||
"Pretend visit to 2hour10minutes.com should now be deleted");
|
||||
@ -286,18 +318,21 @@ function onHistoryReady() {
|
||||
yield countEntries("today", "today form entry should still exist", checkOne);
|
||||
yield countEntries("b4today", "b4today form entry should still exist", checkOne);
|
||||
|
||||
ok(!downloadExists(5555557), "2 hour 10 minute old download should now be deleted");
|
||||
ok(downloadExists(5555553), "<4 hour old download should still be present");
|
||||
ok(downloadExists(5555558), "4 hour 10 minute download should still be present");
|
||||
ok(downloadExists(5555550), "Year old download should still be present");
|
||||
ok(!(yield downloadExists(publicList, "fakefile-2-hour-10-minutes")), "2 hour 10 minute old download should now be deleted");
|
||||
ok((yield downloadExists(publicList, "fakefile-4-hour")), "<4 hour old download should still be present");
|
||||
ok((yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "4 hour 10 minute download should still be present");
|
||||
ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present");
|
||||
if (minutesSinceMidnight > 130)
|
||||
ok(downloadExists(5555554), "'Today' download should still be present");
|
||||
ok((yield downloadExists(publicList, "fakefile-today")), "'Today' download should still be present");
|
||||
|
||||
downloadPromise = promiseDownloadRemoved(publicList);
|
||||
|
||||
// Clear 4 hours
|
||||
Sanitizer.prefs.setIntPref("timeSpan", 3);
|
||||
s.sanitize();
|
||||
|
||||
yield promiseFormHistoryRemoved();
|
||||
yield downloadPromise;
|
||||
|
||||
ok(!(yield promiseIsURIVisited(makeURI("http://4hour.com"))),
|
||||
"Pretend visit to 4hour.com should now be deleted");
|
||||
@ -316,11 +351,13 @@ function onHistoryReady() {
|
||||
yield countEntries("today", "today form entry should still exist", checkOne);
|
||||
yield countEntries("b4today", "b4today form entry should still exist", checkOne);
|
||||
|
||||
ok(!downloadExists(5555553), "<4 hour old download should now be deleted");
|
||||
ok(downloadExists(5555558), "4 hour 10 minute download should still be present");
|
||||
ok(downloadExists(5555550), "Year old download should still be present");
|
||||
ok(!(yield downloadExists(publicList, "fakefile-4-hour")), "<4 hour old download should now be deleted");
|
||||
ok((yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "4 hour 10 minute download should still be present");
|
||||
ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present");
|
||||
if (hoursSinceMidnight > 4)
|
||||
ok(downloadExists(5555554), "'Today' download should still be present");
|
||||
ok((yield downloadExists(publicList, "fakefile-today")), "'Today' download should still be present");
|
||||
|
||||
downloadPromise = promiseDownloadRemoved(publicList);
|
||||
|
||||
// Clear 4 hours 10 minutes
|
||||
s.range = [now_uSec - 250*60*1000000, now_uSec];
|
||||
@ -328,6 +365,7 @@ function onHistoryReady() {
|
||||
s.range = null;
|
||||
|
||||
yield promiseFormHistoryRemoved();
|
||||
yield downloadPromise;
|
||||
|
||||
ok(!(yield promiseIsURIVisited(makeURI("http://4hour10minutes.com"))),
|
||||
"Pretend visit to 4hour10minutes.com should now be deleted");
|
||||
@ -343,48 +381,60 @@ function onHistoryReady() {
|
||||
yield countEntries("today", "today form entry should still exist", checkOne);
|
||||
yield countEntries("b4today", "b4today form entry should still exist", checkOne);
|
||||
|
||||
ok(!downloadExists(5555558), "4 hour 10 minute download should now be deleted");
|
||||
ok(downloadExists(5555550), "Year old download should still be present");
|
||||
ok(!(yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "4 hour 10 minute download should now be deleted");
|
||||
ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present");
|
||||
if (minutesSinceMidnight > 250)
|
||||
ok(downloadExists(5555554), "'Today' download should still be present");
|
||||
ok((yield downloadExists(publicList, "fakefile-today")), "'Today' download should still be present");
|
||||
|
||||
// The 'Today' download might have been already deleted, in which case we
|
||||
// should not wait for a download removal notification.
|
||||
if (minutesSinceMidnight > 250) {
|
||||
downloadPromise = promiseDownloadRemoved(publicList);
|
||||
} else {
|
||||
downloadPromise = Promise.resolve();
|
||||
}
|
||||
|
||||
// Clear Today
|
||||
Sanitizer.prefs.setIntPref("timeSpan", 4);
|
||||
s.sanitize();
|
||||
|
||||
yield promiseFormHistoryRemoved();
|
||||
yield downloadPromise;
|
||||
|
||||
// Be careful. If we add our objectss just before midnight, and sanitize
|
||||
// runs immediately after, they won't be expired. This is expected, but
|
||||
// we should not test in that case. We cannot just test for opposite
|
||||
// condition because we could cross midnight just one moment after we
|
||||
// cache our time, then we would have an even worse random failure.
|
||||
var today = isToday(new Date(now_uSec/1000));
|
||||
var today = isToday(new Date(now_mSec));
|
||||
if (today) {
|
||||
ok(!(yield promiseIsURIVisited(makeURI("http://today.com"))),
|
||||
"Pretend visit to today.com should now be deleted");
|
||||
|
||||
yield countEntries("today", "today form entry should be deleted", checkZero);
|
||||
ok(!downloadExists(5555554), "'Today' download should now be deleted");
|
||||
ok(!(yield downloadExists(publicList, "fakefile-today")), "'Today' download should now be deleted");
|
||||
}
|
||||
|
||||
ok((yield promiseIsURIVisited(makeURI("http://before-today.com"))),
|
||||
"Pretend visit to before-today.com should still exist");
|
||||
yield countEntries("b4today", "b4today form entry should still exist", checkOne);
|
||||
ok(downloadExists(5555550), "Year old download should still be present");
|
||||
ok((yield downloadExists(publicList, "fakefile-old")), "Year old download should still be present");
|
||||
|
||||
downloadPromise = promiseDownloadRemoved(publicList);
|
||||
|
||||
// Choose everything
|
||||
Sanitizer.prefs.setIntPref("timeSpan", 0);
|
||||
s.sanitize();
|
||||
|
||||
yield promiseFormHistoryRemoved();
|
||||
yield downloadPromise;
|
||||
|
||||
ok(!(yield promiseIsURIVisited(makeURI("http://before-today.com"))),
|
||||
"Pretend visit to before-today.com should now be deleted");
|
||||
|
||||
yield countEntries("b4today", "b4today form entry should be deleted", checkZero);
|
||||
|
||||
ok(!downloadExists(5555550), "Year old download should now be deleted");
|
||||
ok(!(yield downloadExists(publicList, "fakefile-old")), "Year old download should now be deleted");
|
||||
}
|
||||
|
||||
function setupHistory() {
|
||||
@ -562,227 +612,103 @@ function setupFormHistory() {
|
||||
|
||||
function setupDownloads() {
|
||||
|
||||
// Add 10-minutes download to DB
|
||||
let data = {
|
||||
id: "5555555",
|
||||
name: "fakefile-10-minutes",
|
||||
source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169",
|
||||
target: "fakefile-10-minutes",
|
||||
startTime: now_uSec - 10 * kUsecPerMin, // 10 minutes ago, in uSec
|
||||
endTime: now_uSec - 11 * kUsecPerMin, // 1 minute later
|
||||
state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
|
||||
currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
|
||||
guid: "a1bcD23eF4g5"
|
||||
};
|
||||
let publicList = yield Downloads.getPublicDownloadList();
|
||||
|
||||
let db = dm.DBConnection;
|
||||
let stmt = db.createStatement(
|
||||
"INSERT INTO moz_downloads (id, name, source, target, startTime, endTime, " +
|
||||
"state, currBytes, maxBytes, preferredAction, autoResume, guid) " +
|
||||
"VALUES (:id, :name, :source, :target, :startTime, :endTime, :state, " +
|
||||
":currBytes, :maxBytes, :preferredAction, :autoResume, :guid)");
|
||||
try {
|
||||
for (let prop in data)
|
||||
stmt.params[prop] = data[prop];
|
||||
stmt.execute();
|
||||
}
|
||||
finally {
|
||||
stmt.reset();
|
||||
}
|
||||
|
||||
// Add within-1-hour download to DB
|
||||
data = {
|
||||
id: "5555551",
|
||||
name: "fakefile-1-hour",
|
||||
let download = yield Downloads.createDownload({
|
||||
source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169",
|
||||
target: "fakefile-10-minutes"
|
||||
});
|
||||
download.startTime = new Date(now_mSec - 10 * kMsecPerMin), // 10 minutes ago
|
||||
download.canceled = true;
|
||||
publicList.add(download);
|
||||
|
||||
download = yield Downloads.createDownload({
|
||||
source: "https://bugzilla.mozilla.org/show_bug.cgi?id=453440",
|
||||
target: "fakefile-1-hour",
|
||||
startTime: now_uSec - 45 * kUsecPerMin, // 45 minutes ago, in uSec
|
||||
endTime: now_uSec - 44 * kUsecPerMin, // 1 minute later
|
||||
state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
|
||||
currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
|
||||
guid: "1bcD23eF4g5a"
|
||||
};
|
||||
target: "fakefile-1-hour"
|
||||
});
|
||||
download.startTime = new Date(now_mSec - 45 * kMsecPerMin), // 45 minutes ago
|
||||
download.canceled = true;
|
||||
publicList.add(download);
|
||||
|
||||
try {
|
||||
for (let prop in data)
|
||||
stmt.params[prop] = data[prop];
|
||||
stmt.execute();
|
||||
}
|
||||
finally {
|
||||
stmt.reset();
|
||||
}
|
||||
|
||||
// Add 1-hour-10-minutes download to DB
|
||||
data = {
|
||||
id: "5555556",
|
||||
name: "fakefile-1-hour-10-minutes",
|
||||
download = yield Downloads.createDownload({
|
||||
source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169",
|
||||
target: "fakefile-1-hour-10-minutes",
|
||||
startTime: now_uSec - 70 * kUsecPerMin, // 70 minutes ago, in uSec
|
||||
endTime: now_uSec - 71 * kUsecPerMin, // 1 minute later
|
||||
state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
|
||||
currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
|
||||
guid: "a1cbD23e4Fg5"
|
||||
};
|
||||
target: "fakefile-1-hour-10-minutes"
|
||||
});
|
||||
download.startTime = new Date(now_mSec - 70 * kMsecPerMin), // 70 minutes ago
|
||||
download.canceled = true;
|
||||
publicList.add(download);
|
||||
|
||||
try {
|
||||
for (let prop in data)
|
||||
stmt.params[prop] = data[prop];
|
||||
stmt.execute();
|
||||
}
|
||||
finally {
|
||||
stmt.reset();
|
||||
}
|
||||
|
||||
// Add within-2-hour download
|
||||
data = {
|
||||
id: "5555552",
|
||||
name: "fakefile-2-hour",
|
||||
download = yield Downloads.createDownload({
|
||||
source: "https://bugzilla.mozilla.org/show_bug.cgi?id=453440",
|
||||
target: "fakefile-2-hour",
|
||||
startTime: now_uSec - 90 * kUsecPerMin, // 90 minutes ago, in uSec
|
||||
endTime: now_uSec - 89 * kUsecPerMin, // 1 minute later
|
||||
state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
|
||||
currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
|
||||
guid: "b1aDc23eFg54"
|
||||
};
|
||||
target: "fakefile-2-hour"
|
||||
});
|
||||
download.startTime = new Date(now_mSec - 90 * kMsecPerMin), // 90 minutes ago
|
||||
download.canceled = true;
|
||||
publicList.add(download);
|
||||
|
||||
try {
|
||||
for (let prop in data)
|
||||
stmt.params[prop] = data[prop];
|
||||
stmt.execute();
|
||||
}
|
||||
finally {
|
||||
stmt.reset();
|
||||
}
|
||||
|
||||
// Add 2-hour-10-minutes download
|
||||
data = {
|
||||
id: "5555557",
|
||||
name: "fakefile-2-hour-10-minutes",
|
||||
download = yield Downloads.createDownload({
|
||||
source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169",
|
||||
target: "fakefile-2-hour-10-minutes",
|
||||
startTime: now_uSec - 130 * kUsecPerMin, // 130 minutes ago, in uSec
|
||||
endTime: now_uSec - 131 * kUsecPerMin, // 1 minute later
|
||||
state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
|
||||
currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
|
||||
guid: "z1bcD23eF4g5"
|
||||
};
|
||||
target: "fakefile-2-hour-10-minutes"
|
||||
});
|
||||
download.startTime = new Date(now_mSec - 130 * kMsecPerMin), // 130 minutes ago
|
||||
download.canceled = true;
|
||||
publicList.add(download);
|
||||
|
||||
try {
|
||||
for (let prop in data)
|
||||
stmt.params[prop] = data[prop];
|
||||
stmt.execute();
|
||||
}
|
||||
finally {
|
||||
stmt.reset();
|
||||
}
|
||||
|
||||
// Add within-4-hour download
|
||||
data = {
|
||||
id: "5555553",
|
||||
name: "fakefile-4-hour",
|
||||
download = yield Downloads.createDownload({
|
||||
source: "https://bugzilla.mozilla.org/show_bug.cgi?id=453440",
|
||||
target: "fakefile-4-hour",
|
||||
startTime: now_uSec - 180 * kUsecPerMin, // 180 minutes ago, in uSec
|
||||
endTime: now_uSec - 179 * kUsecPerMin, // 1 minute later
|
||||
state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
|
||||
currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
|
||||
guid: "zzzcD23eF4g5"
|
||||
};
|
||||
|
||||
try {
|
||||
for (let prop in data)
|
||||
stmt.params[prop] = data[prop];
|
||||
stmt.execute();
|
||||
}
|
||||
finally {
|
||||
stmt.reset();
|
||||
}
|
||||
|
||||
// Add 4-hour-10-minutes download
|
||||
data = {
|
||||
id: "5555558",
|
||||
name: "fakefile-4-hour-10-minutes",
|
||||
target: "fakefile-4-hour"
|
||||
});
|
||||
download.startTime = new Date(now_mSec - 180 * kMsecPerMin), // 180 minutes ago
|
||||
download.canceled = true;
|
||||
publicList.add(download);
|
||||
|
||||
download = yield Downloads.createDownload({
|
||||
source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169",
|
||||
target: "fakefile-4-hour-10-minutes",
|
||||
startTime: now_uSec - 250 * kUsecPerMin, // 250 minutes ago, in uSec
|
||||
endTime: now_uSec - 251 * kUsecPerMin, // 1 minute later
|
||||
state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
|
||||
currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
|
||||
guid: "z1bzz23eF4gz"
|
||||
};
|
||||
|
||||
try {
|
||||
for (let prop in data)
|
||||
stmt.params[prop] = data[prop];
|
||||
stmt.execute();
|
||||
}
|
||||
finally {
|
||||
stmt.reset();
|
||||
}
|
||||
target: "fakefile-4-hour-10-minutes"
|
||||
});
|
||||
download.startTime = new Date(now_mSec - 250 * kMsecPerMin), // 250 minutes ago
|
||||
download.canceled = true;
|
||||
publicList.add(download);
|
||||
|
||||
// Add "today" download
|
||||
let today = new Date();
|
||||
today.setHours(0);
|
||||
today.setMinutes(0);
|
||||
today.setSeconds(1);
|
||||
|
||||
data = {
|
||||
id: "5555554",
|
||||
name: "fakefile-today",
|
||||
|
||||
download = yield Downloads.createDownload({
|
||||
source: "https://bugzilla.mozilla.org/show_bug.cgi?id=453440",
|
||||
target: "fakefile-today",
|
||||
startTime: today.getTime() * 1000, // 12:00:30am this morning, in uSec
|
||||
endTime: (today.getTime() + 1000) * 1000, // 1 second later
|
||||
state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
|
||||
currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
|
||||
guid: "ffffD23eF4g5"
|
||||
};
|
||||
|
||||
try {
|
||||
for (let prop in data)
|
||||
stmt.params[prop] = data[prop];
|
||||
stmt.execute();
|
||||
}
|
||||
finally {
|
||||
stmt.reset();
|
||||
}
|
||||
target: "fakefile-today"
|
||||
});
|
||||
download.startTime = today, // 12:00:01 AM this morning
|
||||
download.canceled = true;
|
||||
publicList.add(download);
|
||||
|
||||
// Add "before today" download
|
||||
let lastYear = new Date();
|
||||
lastYear.setFullYear(lastYear.getFullYear() - 1);
|
||||
data = {
|
||||
id: "5555550",
|
||||
name: "fakefile-old",
|
||||
|
||||
download = yield Downloads.createDownload({
|
||||
source: "https://bugzilla.mozilla.org/show_bug.cgi?id=453440",
|
||||
target: "fakefile-old",
|
||||
startTime: lastYear.getTime() * 1000, // 1 year ago, in uSec
|
||||
endTime: (lastYear.getTime() + 1000) * 1000, // 1 second later
|
||||
state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
|
||||
currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0,
|
||||
guid: "ggggg23eF4g5"
|
||||
};
|
||||
|
||||
try {
|
||||
for (let prop in data)
|
||||
stmt.params[prop] = data[prop];
|
||||
stmt.execute();
|
||||
}
|
||||
finally {
|
||||
stmt.finalize();
|
||||
}
|
||||
target: "fakefile-old"
|
||||
});
|
||||
download.startTime = lastYear,
|
||||
download.canceled = true;
|
||||
publicList.add(download);
|
||||
|
||||
// Confirm everything worked
|
||||
ok(downloadExists(5555550), "Pretend download for everything case should exist");
|
||||
ok(downloadExists(5555555), "Pretend download for 10-minutes case should exist");
|
||||
ok(downloadExists(5555551), "Pretend download for 1-hour case should exist");
|
||||
ok(downloadExists(5555556), "Pretend download for 1-hour-10-minutes case should exist");
|
||||
ok(downloadExists(5555552), "Pretend download for 2-hour case should exist");
|
||||
ok(downloadExists(5555557), "Pretend download for 2-hour-10-minutes case should exist");
|
||||
ok(downloadExists(5555553), "Pretend download for 4-hour case should exist");
|
||||
ok(downloadExists(5555558), "Pretend download for 4-hour-10-minutes case should exist");
|
||||
ok(downloadExists(5555554), "Pretend download for Today case should exist");
|
||||
let downloads = yield publicList.getAll();
|
||||
is(downloads.length, 9, "9 Pretend downloads added");
|
||||
|
||||
ok((yield downloadExists(publicList, "fakefile-old")), "Pretend download for everything case should exist");
|
||||
ok((yield downloadExists(publicList, "fakefile-10-minutes")), "Pretend download for 10-minutes case should exist");
|
||||
ok((yield downloadExists(publicList, "fakefile-1-hour")), "Pretend download for 1-hour case should exist");
|
||||
ok((yield downloadExists(publicList, "fakefile-1-hour-10-minutes")), "Pretend download for 1-hour-10-minutes case should exist");
|
||||
ok((yield downloadExists(publicList, "fakefile-2-hour")), "Pretend download for 2-hour case should exist");
|
||||
ok((yield downloadExists(publicList, "fakefile-2-hour-10-minutes")), "Pretend download for 2-hour-10-minutes case should exist");
|
||||
ok((yield downloadExists(publicList, "fakefile-4-hour")), "Pretend download for 4-hour case should exist");
|
||||
ok((yield downloadExists(publicList, "fakefile-4-hour-10-minutes")), "Pretend download for 4-hour-10-minutes case should exist");
|
||||
ok((yield downloadExists(publicList, "fakefile-today")), "Pretend download for Today case should exist");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -791,18 +717,12 @@ function setupDownloads() {
|
||||
* @param aID
|
||||
* The ids of the downloads to check.
|
||||
*/
|
||||
function downloadExists(aID)
|
||||
function downloadExists(list, path)
|
||||
{
|
||||
let db = dm.DBConnection;
|
||||
let stmt = db.createStatement(
|
||||
"SELECT * " +
|
||||
"FROM moz_downloads " +
|
||||
"WHERE id = :id"
|
||||
);
|
||||
stmt.params.id = aID;
|
||||
var rows = stmt.executeStep();
|
||||
stmt.finalize();
|
||||
return rows;
|
||||
return Task.spawn(function() {
|
||||
let listArray = yield list.getAll();
|
||||
throw new Task.Result(listArray.some(i => i.target.path == path));
|
||||
});
|
||||
}
|
||||
|
||||
function isToday(aDate) {
|
||||
|
@ -21,18 +21,18 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
|
||||
"resource://gre/modules/FormHistory.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
|
||||
"resource://gre/modules/Downloads.jsm");
|
||||
|
||||
let tempScope = {};
|
||||
Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
|
||||
.loadSubScript("chrome://browser/content/sanitize.js", tempScope);
|
||||
let Sanitizer = tempScope.Sanitizer;
|
||||
|
||||
const dm = Cc["@mozilla.org/download-manager;1"].
|
||||
getService(Ci.nsIDownloadManager);
|
||||
|
||||
const kMsecPerMin = 60 * 1000;
|
||||
const kUsecPerMin = 60 * 1000000;
|
||||
|
||||
let formEntries;
|
||||
let formEntries, downloadIDs, olderDownloadIDs;
|
||||
|
||||
// Add tests here. Each is a function that's called by doNextTest().
|
||||
var gAllTests = [
|
||||
@ -92,6 +92,23 @@ var gAllTests = [
|
||||
});
|
||||
},
|
||||
|
||||
function () {
|
||||
// Add downloads (within the past hour).
|
||||
Task.spawn(function () {
|
||||
downloadIDs = [];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
yield addDownloadWithMinutesAgo(downloadIDs, i);
|
||||
}
|
||||
// Add downloads (over an hour ago).
|
||||
olderDownloadIDs = [];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
yield addDownloadWithMinutesAgo(olderDownloadIDs, 61 + i);
|
||||
}
|
||||
|
||||
doNextTest();
|
||||
}).then(null, Components.utils.reportError);
|
||||
},
|
||||
|
||||
/**
|
||||
* Ensures that the combined history-downloads checkbox clears both history
|
||||
* visits and downloads when checked; the dialog respects simple timespan.
|
||||
@ -115,16 +132,6 @@ var gAllTests = [
|
||||
}
|
||||
|
||||
addVisits(places, function() {
|
||||
// Add downloads (within the past hour).
|
||||
let downloadIDs = [];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
downloadIDs.push(addDownloadWithMinutesAgo(i));
|
||||
}
|
||||
// Add downloads (over an hour ago).
|
||||
let olderDownloadIDs = [];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
olderDownloadIDs.push(addDownloadWithMinutesAgo(61 + i));
|
||||
}
|
||||
let totalHistoryVisits = uris.length + olderURIs.length;
|
||||
|
||||
let wh = new WindowHelper();
|
||||
@ -146,16 +153,16 @@ var gAllTests = [
|
||||
wh.onunload = function () {
|
||||
// History visits and downloads within one hour should be cleared.
|
||||
yield promiseHistoryClearedState(uris, true);
|
||||
ensureDownloadsClearedState(downloadIDs, true);
|
||||
yield ensureDownloadsClearedState(downloadIDs, true);
|
||||
|
||||
// Visits and downloads > 1 hour should still exist.
|
||||
yield promiseHistoryClearedState(olderURIs, false);
|
||||
ensureDownloadsClearedState(olderDownloadIDs, false);
|
||||
yield ensureDownloadsClearedState(olderDownloadIDs, false);
|
||||
|
||||
// OK, done, cleanup after ourselves.
|
||||
yield blankSlate();
|
||||
yield promiseHistoryClearedState(olderURIs, true);
|
||||
ensureDownloadsClearedState(olderDownloadIDs, true);
|
||||
yield ensureDownloadsClearedState(olderDownloadIDs, true);
|
||||
};
|
||||
wh.open();
|
||||
});
|
||||
@ -178,6 +185,18 @@ var gAllTests = [
|
||||
iter.next();
|
||||
},
|
||||
|
||||
function () {
|
||||
// Add downloads (within the past hour).
|
||||
Task.spawn(function () {
|
||||
downloadIDs = [];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
yield addDownloadWithMinutesAgo(downloadIDs, i);
|
||||
}
|
||||
|
||||
doNextTest();
|
||||
}).then(null, Components.utils.reportError);
|
||||
},
|
||||
|
||||
/**
|
||||
* Ensures that the combined history-downloads checkbox removes neither
|
||||
* history visits nor downloads when not checked.
|
||||
@ -194,11 +213,6 @@ var gAllTests = [
|
||||
}
|
||||
|
||||
addVisits(places, function() {
|
||||
let downloadIDs = [];
|
||||
for (let i = 0; i < 5; i++) {
|
||||
downloadIDs.push(addDownloadWithMinutesAgo(i));
|
||||
}
|
||||
|
||||
let wh = new WindowHelper();
|
||||
wh.onload = function () {
|
||||
is(this.isWarningPanelVisible(), false,
|
||||
@ -224,7 +238,7 @@ var gAllTests = [
|
||||
wh.onunload = function () {
|
||||
// Of the three only form entries should be cleared.
|
||||
yield promiseHistoryClearedState(uris, false);
|
||||
ensureDownloadsClearedState(downloadIDs, false);
|
||||
yield ensureDownloadsClearedState(downloadIDs, false);
|
||||
|
||||
formEntries.forEach(function (entry) {
|
||||
let exists = yield formNameExists(entry);
|
||||
@ -234,7 +248,7 @@ var gAllTests = [
|
||||
// OK, done, cleanup after ourselves.
|
||||
yield blankSlate();
|
||||
yield promiseHistoryClearedState(uris, true);
|
||||
ensureDownloadsClearedState(downloadIDs, true);
|
||||
yield ensureDownloadsClearedState(downloadIDs, true);
|
||||
};
|
||||
wh.open();
|
||||
});
|
||||
@ -639,15 +653,12 @@ var gAllTests = [
|
||||
}
|
||||
];
|
||||
|
||||
// Used as the download database ID for a new download. Incremented for each
|
||||
// new download. See addDownloadWithMinutesAgo().
|
||||
var gDownloadId = 5555551;
|
||||
|
||||
// Index in gAllTests of the test currently being run. Incremented for each
|
||||
// test run. See doNextTest().
|
||||
var gCurrTest = 0;
|
||||
|
||||
var now_uSec = Date.now() * 1000;
|
||||
let now_mSec = Date.now();
|
||||
let now_uSec = now_mSec * 1000;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@ -847,7 +858,7 @@ WindowHelper.prototype = {
|
||||
if (wh.onunload) {
|
||||
Task.spawn(wh.onunload).then(function() {
|
||||
waitForAsyncUpdates(doNextTest);
|
||||
});
|
||||
}).then(null, Components.utils.reportError);
|
||||
} else {
|
||||
waitForAsyncUpdates(doNextTest);
|
||||
}
|
||||
@ -900,40 +911,23 @@ WindowHelper.prototype = {
|
||||
* @param aMinutesAgo
|
||||
* The download will be downloaded this many minutes ago
|
||||
*/
|
||||
function addDownloadWithMinutesAgo(aMinutesAgo) {
|
||||
function addDownloadWithMinutesAgo(aExpectedPathList, aMinutesAgo) {
|
||||
let publicList = yield Downloads.getPublicDownloadList();
|
||||
|
||||
let name = "fakefile-" + aMinutesAgo + "-minutes-ago";
|
||||
let data = {
|
||||
id: gDownloadId,
|
||||
name: name,
|
||||
source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169",
|
||||
target: name,
|
||||
startTime: now_uSec - (aMinutesAgo * kUsecPerMin),
|
||||
endTime: now_uSec - ((aMinutesAgo + 1) * kUsecPerMin),
|
||||
state: Ci.nsIDownloadManager.DOWNLOAD_FINISHED,
|
||||
currBytes: 0, maxBytes: -1, preferredAction: 0, autoResume: 0
|
||||
};
|
||||
let download = yield Downloads.createDownload({
|
||||
source: "https://bugzilla.mozilla.org/show_bug.cgi?id=480169",
|
||||
target: name
|
||||
});
|
||||
download.startTime = new Date(now_mSec - (aMinutesAgo * kMsecPerMin));
|
||||
download.canceled = true;
|
||||
publicList.add(download);
|
||||
|
||||
let db = dm.DBConnection;
|
||||
let stmt = db.createStatement(
|
||||
"INSERT INTO moz_downloads (id, name, source, target, startTime, endTime, " +
|
||||
"state, currBytes, maxBytes, preferredAction, autoResume) " +
|
||||
"VALUES (:id, :name, :source, :target, :startTime, :endTime, :state, " +
|
||||
":currBytes, :maxBytes, :preferredAction, :autoResume)");
|
||||
try {
|
||||
for (let prop in data) {
|
||||
stmt.params[prop] = data[prop];
|
||||
}
|
||||
stmt.execute();
|
||||
}
|
||||
finally {
|
||||
stmt.reset();
|
||||
}
|
||||
|
||||
is(downloadExists(gDownloadId), true,
|
||||
"Sanity check: download " + gDownloadId +
|
||||
ok((yield downloadExists(name)),
|
||||
"Sanity check: download " + name +
|
||||
" should exist after creating it");
|
||||
|
||||
return gDownloadId++;
|
||||
aExpectedPathList.push(name);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -984,15 +978,37 @@ function formNameExists(name)
|
||||
*/
|
||||
function blankSlate() {
|
||||
PlacesUtils.bhistory.removeAllPages();
|
||||
dm.cleanUp();
|
||||
|
||||
// The promise is resolved only when removing both downloads and form history are done.
|
||||
let deferred = Promise.defer();
|
||||
let formHistoryDone = false, downloadsDone = false;
|
||||
|
||||
Task.spawn(function deleteAllDownloads() {
|
||||
let publicList = yield Downloads.getPublicDownloadList();
|
||||
let downloads = yield publicList.getAll();
|
||||
for (let download of downloads) {
|
||||
publicList.remove(download);
|
||||
yield download.finalize(true);
|
||||
}
|
||||
downloadsDone = true;
|
||||
if (formHistoryDone) {
|
||||
deferred.resolve();
|
||||
}
|
||||
}).then(null, Components.utils.reportError);
|
||||
|
||||
FormHistory.update({ op: "remove" },
|
||||
{ handleError: function (error) {
|
||||
do_throw("Error occurred updating form history: " + error);
|
||||
deferred.reject(error);
|
||||
},
|
||||
handleCompletion: function (reason) { if (!reason) deferred.resolve(); }
|
||||
handleCompletion: function (reason) {
|
||||
if (!reason) {
|
||||
formHistoryDone = true;
|
||||
if (downloadsDone) {
|
||||
deferred.resolve();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
@ -1012,24 +1028,19 @@ function boolPrefIs(aPrefName, aExpectedVal, aMsg) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the download with the specified ID exists.
|
||||
* Checks to see if the download with the specified path exists.
|
||||
*
|
||||
* @param aID
|
||||
* The ID of the download to check
|
||||
* @param aPath
|
||||
* The path of the download to check
|
||||
* @return True if the download exists, false otherwise
|
||||
*/
|
||||
function downloadExists(aID)
|
||||
function downloadExists(aPath)
|
||||
{
|
||||
let db = dm.DBConnection;
|
||||
let stmt = db.createStatement(
|
||||
"SELECT * " +
|
||||
"FROM moz_downloads " +
|
||||
"WHERE id = :id"
|
||||
);
|
||||
stmt.params.id = aID;
|
||||
let rows = stmt.executeStep();
|
||||
stmt.finalize();
|
||||
return !!rows;
|
||||
return Task.spawn(function() {
|
||||
let publicList = yield Downloads.getPublicDownloadList();
|
||||
let listArray = yield publicList.getAll();
|
||||
throw new Task.Result(listArray.some(i => i.target.path == aPath));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1059,7 +1070,7 @@ function doNextTest() {
|
||||
function ensureDownloadsClearedState(aDownloadIDs, aShouldBeCleared) {
|
||||
let niceStr = aShouldBeCleared ? "no longer" : "still";
|
||||
aDownloadIDs.forEach(function (id) {
|
||||
is(downloadExists(id), !aShouldBeCleared,
|
||||
is((yield downloadExists(id)), !aShouldBeCleared,
|
||||
"download " + id + " should " + niceStr + " exist");
|
||||
});
|
||||
}
|
||||
|
@ -14,6 +14,7 @@ MOCHITEST_BROWSER_FILES = \
|
||||
browser_newtab_block.js \
|
||||
browser_newtab_disable.js \
|
||||
browser_newtab_drag_drop.js \
|
||||
browser_newtab_drag_drop_ext.js \
|
||||
browser_newtab_drop_preview.js \
|
||||
browser_newtab_focus.js \
|
||||
browser_newtab_reset.js \
|
||||
|
@ -71,47 +71,4 @@ function runTests() {
|
||||
|
||||
yield simulateDrop(0, 4);
|
||||
checkGrid("3,1p,2p,4,0p,5p,6,7,8");
|
||||
|
||||
// drag a new site onto the very first cell
|
||||
yield setLinks("0,1,2,3,4,5,6,7,8");
|
||||
setPinnedLinks(",,,,,,,7,8");
|
||||
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("0,1,2,3,4,5,6,7p,8p");
|
||||
|
||||
yield simulateExternalDrop(0);
|
||||
checkGrid("99p,0,1,2,3,4,5,7p,8p");
|
||||
|
||||
// drag a new site onto the grid and make sure that pinned cells don't get
|
||||
// pushed out
|
||||
yield setLinks("0,1,2,3,4,5,6,7,8");
|
||||
setPinnedLinks(",,,,,,,7,8");
|
||||
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("0,1,2,3,4,5,6,7p,8p");
|
||||
|
||||
yield simulateExternalDrop(7);
|
||||
checkGrid("0,1,2,3,4,5,7p,99p,8p");
|
||||
|
||||
// drag a new site beneath a pinned cell and make sure the pinned cell is
|
||||
// not moved
|
||||
yield setLinks("0,1,2,3,4,5,6,7,8");
|
||||
setPinnedLinks(",,,,,,,,8");
|
||||
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("0,1,2,3,4,5,6,7,8p");
|
||||
|
||||
yield simulateExternalDrop(7);
|
||||
checkGrid("0,1,2,3,4,5,6,99p,8p");
|
||||
|
||||
// drag a new site onto a block of pinned sites and make sure they're shifted
|
||||
// around accordingly
|
||||
yield setLinks("0,1,2,3,4,5,6,7,8");
|
||||
setPinnedLinks("0,1,2,,,,,,");
|
||||
|
||||
yield addNewTabPageTab();
|
||||
checkGrid("0p,1p,2p");
|
||||
|
||||
yield simulateExternalDrop(1);
|
||||
checkGrid("0p,99p,1p,2p,3,4,5,6,7");
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
@ -30,6 +30,7 @@ MOCHITEST_BROWSER_FILES = \
|
||||
browser_social_chatwindow_resize.js \
|
||||
browser_social_chatwindowfocus.js \
|
||||
browser_social_multiprovider.js \
|
||||
browser_social_multiworker.js \
|
||||
browser_social_errorPage.js \
|
||||
browser_social_window.js \
|
||||
social_activate.html \
|
||||
|
@ -2,17 +2,71 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
|
||||
|
||||
let manifests = [
|
||||
{
|
||||
name: "provider@example.com",
|
||||
origin: "https://example.com",
|
||||
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html?example.com",
|
||||
workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
|
||||
iconURL: "chrome://branding/content/icon48.png"
|
||||
},
|
||||
{
|
||||
name: "provider@test1",
|
||||
origin: "https://test1.example.com",
|
||||
sidebarURL: "https://test1.example.com/browser/browser/base/content/test/social/social_sidebar.html?test1",
|
||||
workerURL: "https://test1.example.com/browser/browser/base/content/test/social/social_worker.js",
|
||||
iconURL: "chrome://branding/content/icon48.png"
|
||||
},
|
||||
{
|
||||
name: "provider@test2",
|
||||
origin: "https://test2.example.com",
|
||||
sidebarURL: "https://test2.example.com/browser/browser/base/content/test/social/social_sidebar.html?test2",
|
||||
workerURL: "https://test2.example.com/browser/browser/base/content/test/social/social_worker.js",
|
||||
iconURL: "chrome://branding/content/icon48.png"
|
||||
}
|
||||
];
|
||||
|
||||
let chatId = 0;
|
||||
function openChat(provider, callback) {
|
||||
let chatUrl = provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
|
||||
let port = provider.getWorkerPort();
|
||||
port.onmessage = function(e) {
|
||||
if (e.data.topic == "got-chatbox-message") {
|
||||
port.close();
|
||||
callback();
|
||||
}
|
||||
}
|
||||
let url = chatUrl + "?" + (chatId++);
|
||||
port.postMessage({topic: "test-init"});
|
||||
port.postMessage({topic: "test-worker-chat", data: url});
|
||||
gURLsNotRemembered.push(url);
|
||||
}
|
||||
|
||||
function waitPrefChange(cb) {
|
||||
Services.prefs.addObserver("social.enabled", function prefObserver(subject, topic, data) {
|
||||
Services.prefs.removeObserver("social.enabled", prefObserver);
|
||||
executeSoon(cb);
|
||||
}, false);
|
||||
}
|
||||
|
||||
function setWorkerMode(multiple, cb) {
|
||||
waitPrefChange(function() {
|
||||
if (multiple)
|
||||
Services.prefs.setBoolPref("social.allowMultipleWorkers", true);
|
||||
else
|
||||
Services.prefs.clearUserPref("social.allowMultipleWorkers");
|
||||
waitPrefChange(cb);
|
||||
Social.enabled = true;
|
||||
});
|
||||
Social.enabled = false;
|
||||
}
|
||||
|
||||
function test() {
|
||||
requestLongerTimeout(2); // only debug builds seem to need more time...
|
||||
waitForExplicitFinish();
|
||||
|
||||
let manifest = { // normal provider
|
||||
name: "provider 1",
|
||||
origin: "https://example.com",
|
||||
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
|
||||
workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
|
||||
iconURL: "https://example.com/browser/browser/base/content/test/moz.png"
|
||||
};
|
||||
let oldwidth = window.outerWidth; // we futz with these, so we restore them
|
||||
let oldleft = window.screenX;
|
||||
window.moveTo(0, window.screenY)
|
||||
@ -21,7 +75,7 @@ function test() {
|
||||
ok(chats.children.length == 0, "no chatty children left behind");
|
||||
cb();
|
||||
};
|
||||
runSocialTestWithProvider(manifest, function (finishcb) {
|
||||
runSocialTestWithProvider(manifests, function (finishcb) {
|
||||
runSocialTests(tests, undefined, postSubTest, function() {
|
||||
window.moveTo(oldleft, window.screenY)
|
||||
window.resizeTo(oldwidth, window.outerHeight);
|
||||
@ -147,7 +201,7 @@ var tests = {
|
||||
maybeOpenAnother();
|
||||
},
|
||||
testWorkerChatWindow: function(next) {
|
||||
const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html";
|
||||
const chatUrl = Social.provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
|
||||
let chats = document.getElementById("pinnedchats");
|
||||
let port = Social.provider.getWorkerPort();
|
||||
ok(port, "provider has a port");
|
||||
@ -384,7 +438,7 @@ var tests = {
|
||||
|
||||
testSecondTopLevelWindow: function(next) {
|
||||
// Bug 817782 - check chats work in new top-level windows.
|
||||
const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html";
|
||||
const chatUrl = Social.provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
|
||||
let port = Social.provider.getWorkerPort();
|
||||
let secondWindow;
|
||||
port.onmessage = function(e) {
|
||||
@ -407,23 +461,9 @@ var tests = {
|
||||
testChatWindowChooser: function(next) {
|
||||
// Tests that when a worker creates a chat, it is opened in the correct
|
||||
// window.
|
||||
const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html";
|
||||
let chatId = 1;
|
||||
let port = Social.provider.getWorkerPort();
|
||||
port.postMessage({topic: "test-init"});
|
||||
|
||||
function openChat(callback) {
|
||||
port.onmessage = function(e) {
|
||||
if (e.data.topic == "got-chatbox-message")
|
||||
callback();
|
||||
}
|
||||
let url = chatUrl + "?" + (chatId++);
|
||||
port.postMessage({topic: "test-worker-chat", data: url});
|
||||
}
|
||||
|
||||
// open a chat (it will open in the main window)
|
||||
ok(!window.SocialChatBar.hasChats, "first window should start with no chats");
|
||||
openChat(function() {
|
||||
openChat(Social.provider, function() {
|
||||
ok(window.SocialChatBar.hasChats, "first window has the chat");
|
||||
// create a second window - this will be the "most recent" and will
|
||||
// therefore be the window that hosts the new chat (see bug 835111)
|
||||
@ -431,27 +471,55 @@ var tests = {
|
||||
secondWindow.addEventListener("load", function loadListener() {
|
||||
secondWindow.removeEventListener("load", loadListener);
|
||||
ok(!secondWindow.SocialChatBar.hasChats, "second window has no chats");
|
||||
openChat(function() {
|
||||
openChat(Social.provider, function() {
|
||||
ok(secondWindow.SocialChatBar.hasChats, "second window now has chats");
|
||||
is(window.SocialChatBar.chatbar.childElementCount, 1, "first window still has 1 chat");
|
||||
window.SocialChatBar.chatbar.removeAll();
|
||||
// now open another chat - it should still open in the second.
|
||||
openChat(function() {
|
||||
openChat(Social.provider, function() {
|
||||
ok(!window.SocialChatBar.hasChats, "first window has no chats");
|
||||
ok(secondWindow.SocialChatBar.hasChats, "second window has a chat");
|
||||
secondWindow.close();
|
||||
port.close();
|
||||
next();
|
||||
});
|
||||
});
|
||||
})
|
||||
});
|
||||
},
|
||||
testMultipleProviderChat: function(next) {
|
||||
// while pref'd off, we need to set the worker mode to multiple providers
|
||||
setWorkerMode(true, function() {
|
||||
// test incomming chats from all providers
|
||||
openChat(Social.providers[0], function() {
|
||||
openChat(Social.providers[1], function() {
|
||||
openChat(Social.providers[2], function() {
|
||||
let chats = document.getElementById("pinnedchats");
|
||||
waitForCondition(function() chats.children.length == Social.providers.length,
|
||||
function() {
|
||||
ok(true, "one chat window per provider opened");
|
||||
// test logout of a single provider
|
||||
let provider = Social.providers[0];
|
||||
let port = provider.getWorkerPort();
|
||||
port.postMessage({topic: "test-logout"});
|
||||
waitForCondition(function() chats.children.length == Social.providers.length - 1,
|
||||
function() {
|
||||
port.close();
|
||||
chats.removeAll();
|
||||
ok(!chats.selectedChat, "chats are all closed");
|
||||
setWorkerMode(false, next);
|
||||
},
|
||||
"chat window didn't close");
|
||||
}, "chat windows did not open");
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
// XXX - note this must be the last test until we restore the login state
|
||||
// between tests...
|
||||
testCloseOnLogout: function(next) {
|
||||
const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html";
|
||||
const chatUrl = Social.provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
|
||||
let port = Social.provider.getWorkerPort();
|
||||
ok(port, "provider has a port");
|
||||
let opened = false;
|
||||
|
101
browser/base/content/test/social/browser_social_multiworker.js
Normal file
101
browser/base/content/test/social/browser_social_multiworker.js
Normal 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();
|
||||
}
|
||||
}
|
@ -417,8 +417,8 @@ function get3ChatsForCollapsing(mode, cb) {
|
||||
|
||||
function makeChat(mode, uniqueid, cb) {
|
||||
info("making a chat window '" + uniqueid +"'");
|
||||
const chatUrl = "https://example.com/browser/browser/base/content/test/social/social_chat.html";
|
||||
let provider = Social.provider;
|
||||
const chatUrl = provider.origin + "/browser/browser/base/content/test/social/social_chat.html";
|
||||
let isOpened = window.SocialChatBar.openChat(provider, chatUrl + "?id=" + uniqueid, function(chat) {
|
||||
info("chat window has opened");
|
||||
// we can't callback immediately or we might close the chat during
|
||||
|
@ -61,20 +61,20 @@ NS_DEFINE_NAMED_CID(NS_SHELLSERVICE_CID);
|
||||
#endif
|
||||
|
||||
static const mozilla::Module::CIDEntry kBrowserCIDs[] = {
|
||||
{ &kNS_BROWSERDIRECTORYPROVIDER_CID, false, NULL, DirectoryProviderConstructor },
|
||||
{ &kNS_BROWSERDIRECTORYPROVIDER_CID, false, nullptr, DirectoryProviderConstructor },
|
||||
#if defined(XP_WIN)
|
||||
{ &kNS_SHELLSERVICE_CID, false, NULL, nsWindowsShellServiceConstructor },
|
||||
{ &kNS_SHELLSERVICE_CID, false, nullptr, nsWindowsShellServiceConstructor },
|
||||
#elif defined(MOZ_WIDGET_GTK)
|
||||
{ &kNS_SHELLSERVICE_CID, false, NULL, nsGNOMEShellServiceConstructor },
|
||||
{ &kNS_SHELLSERVICE_CID, false, nullptr, nsGNOMEShellServiceConstructor },
|
||||
#endif
|
||||
{ &kNS_FEEDSNIFFER_CID, false, NULL, nsFeedSnifferConstructor },
|
||||
{ &kNS_BROWSER_ABOUT_REDIRECTOR_CID, false, NULL, AboutRedirector::Create },
|
||||
{ &kNS_FEEDSNIFFER_CID, false, nullptr, nsFeedSnifferConstructor },
|
||||
{ &kNS_BROWSER_ABOUT_REDIRECTOR_CID, false, nullptr, AboutRedirector::Create },
|
||||
#if defined(XP_WIN)
|
||||
{ &kNS_WINIEHISTORYENUMERATOR_CID, false, NULL, nsIEHistoryEnumeratorConstructor },
|
||||
{ &kNS_WINIEHISTORYENUMERATOR_CID, false, nullptr, nsIEHistoryEnumeratorConstructor },
|
||||
#elif defined(XP_MACOSX)
|
||||
{ &kNS_SHELLSERVICE_CID, false, NULL, nsMacShellServiceConstructor },
|
||||
{ &kNS_SHELLSERVICE_CID, false, nullptr, nsMacShellServiceConstructor },
|
||||
#endif
|
||||
{ NULL }
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
static const mozilla::Module::ContractIDEntry kBrowserContracts[] = {
|
||||
@ -113,13 +113,13 @@ static const mozilla::Module::ContractIDEntry kBrowserContracts[] = {
|
||||
#elif defined(XP_MACOSX)
|
||||
{ NS_SHELLSERVICE_CONTRACTID, &kNS_SHELLSERVICE_CID },
|
||||
#endif
|
||||
{ NULL }
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
static const mozilla::Module::CategoryEntry kBrowserCategories[] = {
|
||||
{ XPCOM_DIRECTORY_PROVIDER_CATEGORY, "browser-directory-provider", NS_BROWSERDIRECTORYPROVIDER_CONTRACTID },
|
||||
{ NS_CONTENT_SNIFFER_CATEGORY, "Feed Sniffer", NS_FEEDSNIFFER_CONTRACTID },
|
||||
{ NULL }
|
||||
{ nullptr }
|
||||
};
|
||||
|
||||
static const mozilla::Module kBrowserModule = {
|
||||
|
@ -7,39 +7,33 @@
|
||||
* Make sure the downloads panel can display items in the right order and
|
||||
* contains the expected data.
|
||||
*/
|
||||
function gen_test()
|
||||
function test_task()
|
||||
{
|
||||
// Display one of each download state.
|
||||
const DownloadData = [
|
||||
{ endTime: 1180493839859239, state: nsIDM.DOWNLOAD_NOTSTARTED },
|
||||
{ endTime: 1180493839859238, state: nsIDM.DOWNLOAD_DOWNLOADING },
|
||||
{ endTime: 1180493839859237, state: nsIDM.DOWNLOAD_PAUSED },
|
||||
{ endTime: 1180493839859236, state: nsIDM.DOWNLOAD_SCANNING },
|
||||
{ endTime: 1180493839859235, state: nsIDM.DOWNLOAD_QUEUED },
|
||||
{ endTime: 1180493839859234, state: nsIDM.DOWNLOAD_FINISHED },
|
||||
{ endTime: 1180493839859233, state: nsIDM.DOWNLOAD_FAILED },
|
||||
{ endTime: 1180493839859232, state: nsIDM.DOWNLOAD_CANCELED },
|
||||
{ endTime: 1180493839859231, state: nsIDM.DOWNLOAD_BLOCKED_PARENTAL },
|
||||
{ endTime: 1180493839859230, state: nsIDM.DOWNLOAD_DIRTY },
|
||||
{ endTime: 1180493839859229, state: nsIDM.DOWNLOAD_BLOCKED_POLICY },
|
||||
{ state: nsIDM.DOWNLOAD_NOTSTARTED },
|
||||
{ state: nsIDM.DOWNLOAD_PAUSED },
|
||||
{ state: nsIDM.DOWNLOAD_FINISHED },
|
||||
{ state: nsIDM.DOWNLOAD_FAILED },
|
||||
{ state: nsIDM.DOWNLOAD_CANCELED },
|
||||
];
|
||||
|
||||
// For testing purposes, show all the download items at once.
|
||||
var originalCountLimit = DownloadsView.kItemCountLimit;
|
||||
DownloadsView.kItemCountLimit = DownloadData.length;
|
||||
registerCleanupFunction(function () {
|
||||
DownloadsView.kItemCountLimit = originalCountLimit;
|
||||
});
|
||||
|
||||
try {
|
||||
// Ensure that state is reset in case previous tests didn't finish.
|
||||
for (let yy in gen_resetState(DownloadsCommon.getData(window))) yield undefined;
|
||||
yield task_resetState();
|
||||
|
||||
// For testing purposes, show all the download items at once.
|
||||
var originalCountLimit = DownloadsView.kItemCountLimit;
|
||||
DownloadsView.kItemCountLimit = DownloadData.length;
|
||||
registerCleanupFunction(function () {
|
||||
DownloadsView.kItemCountLimit = originalCountLimit;
|
||||
});
|
||||
|
||||
// Populate the downloads database with the data required by this test.
|
||||
for (let yy in gen_addDownloadRows(DownloadData)) yield undefined;
|
||||
yield task_addDownloads(DownloadData);
|
||||
|
||||
// Open the user interface and wait for data to be fully loaded.
|
||||
for (let yy in gen_openPanel(DownloadsCommon.getData(window))) yield undefined;
|
||||
yield task_openPanel();
|
||||
|
||||
// Test item data and count. This also tests the ordering of the display.
|
||||
let richlistbox = document.getElementById("downloadsListBox");
|
||||
@ -47,16 +41,14 @@ function gen_test()
|
||||
is(richlistbox.children.length, DownloadData.length,
|
||||
"There is the correct number of richlistitems");
|
||||
*/
|
||||
for (let i = 0; i < richlistbox.children.length; i++) {
|
||||
let element = richlistbox.children[i];
|
||||
let itemCount = richlistbox.children.length;
|
||||
for (let i = 0; i < itemCount; i++) {
|
||||
let element = richlistbox.children[itemCount - i - 1];
|
||||
let dataItem = new DownloadsViewItemController(element).dataItem;
|
||||
is(dataItem.target, DownloadData[i].name, "Download names match up");
|
||||
is(dataItem.state, DownloadData[i].state, "Download states match up");
|
||||
is(dataItem.file, DownloadData[i].target, "Download targets match up");
|
||||
is(dataItem.uri, DownloadData[i].source, "Download sources match up");
|
||||
}
|
||||
} finally {
|
||||
// Clean up when the test finishes.
|
||||
for (let yy in gen_resetState(DownloadsCommon.getData(window))) yield undefined;
|
||||
yield task_resetState();
|
||||
}
|
||||
}
|
||||
|
@ -8,19 +8,19 @@
|
||||
* download it notices. All subsequent downloads, even across sessions, should
|
||||
* not open the panel automatically.
|
||||
*/
|
||||
function gen_test()
|
||||
function test_task()
|
||||
{
|
||||
try {
|
||||
// Ensure that state is reset in case previous tests didn't finish.
|
||||
for (let yy in gen_resetState(DownloadsCommon.getData(window))) yield undefined;
|
||||
yield task_resetState();
|
||||
|
||||
// With this set to false, we should automatically open the panel
|
||||
// the first time a download is started.
|
||||
// With this set to false, we should automatically open the panel the first
|
||||
// time a download is started.
|
||||
DownloadsCommon.getData(window).panelHasShownBefore = false;
|
||||
|
||||
prepareForPanelOpen();
|
||||
let promise = promisePanelOpened();
|
||||
DownloadsCommon.getData(window)._notifyDownloadEvent("start");
|
||||
yield undefined;
|
||||
yield promise;
|
||||
|
||||
// If we got here, that means the panel opened.
|
||||
DownloadsPanel.hidePanel();
|
||||
@ -28,29 +28,26 @@ function gen_test()
|
||||
ok(DownloadsCommon.getData(window).panelHasShownBefore,
|
||||
"Should have recorded that the panel was opened on a download.")
|
||||
|
||||
// Next, make sure that if we start another download, we don't open
|
||||
// the panel automatically.
|
||||
panelShouldNotOpen();
|
||||
DownloadsCommon.getData(window)._notifyDownloadEvent("start");
|
||||
yield waitFor(2);
|
||||
} catch(e) {
|
||||
ok(false, e);
|
||||
// Next, make sure that if we start another download, we don't open the
|
||||
// panel automatically.
|
||||
let originalOnPopupShown = DownloadsPanel.onPopupShown;
|
||||
DownloadsPanel.onPopupShown = function () {
|
||||
originalOnPopupShown.apply(this, arguments);
|
||||
ok(false, "Should not have opened the downloads panel.");
|
||||
};
|
||||
|
||||
try {
|
||||
DownloadsCommon.getData(window)._notifyDownloadEvent("start");
|
||||
|
||||
// Wait 2 seconds to ensure that the panel does not open.
|
||||
let deferTimeout = Promise.defer();
|
||||
setTimeout(deferTimeout.resolve, 2000);
|
||||
yield deferTimeout.promise;
|
||||
} finally {
|
||||
DownloadsPanel.onPopupShown = originalOnPopupShown;
|
||||
}
|
||||
} finally {
|
||||
// Clean up when the test finishes.
|
||||
for (let yy in gen_resetState(DownloadsCommon.getData(window))) yield undefined;
|
||||
yield task_resetState();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this to record a test failure for the next time the downloads panel
|
||||
* opens.
|
||||
*/
|
||||
function panelShouldNotOpen()
|
||||
{
|
||||
// Hook to wait until the test data has been loaded.
|
||||
let originalOnViewLoadCompleted = DownloadsPanel.onViewLoadCompleted;
|
||||
DownloadsPanel.onViewLoadCompleted = function () {
|
||||
DownloadsPanel.onViewLoadCompleted = originalOnViewLoadCompleted;
|
||||
ok(false, "Should not have opened the downloads panel.");
|
||||
};
|
||||
}
|
||||
|
@ -10,10 +10,16 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Globals
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
|
||||
"resource://gre/modules/FileUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
|
||||
"resource://gre/modules/Downloads.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "DownloadsCommon",
|
||||
"resource:///modules/DownloadsCommon.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
|
||||
"resource://gre/modules/FileUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/Promise.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||
"resource://gre/modules/Task.jsm");
|
||||
const nsIDM = Ci.nsIDownloadManager;
|
||||
|
||||
let gTestTargetFile = FileUtils.getFile("TmpD", ["dm-ui-test.file"]);
|
||||
@ -22,253 +28,85 @@ registerCleanupFunction(function () {
|
||||
gTestTargetFile.remove(false);
|
||||
});
|
||||
|
||||
/**
|
||||
* This objects contains a property for each column in the downloads table.
|
||||
*/
|
||||
let gDownloadRowTemplate = {
|
||||
name: "test-download.txt",
|
||||
source: "http://www.example.com/test-download.txt",
|
||||
target: NetUtil.newURI(gTestTargetFile).spec,
|
||||
startTime: 1180493839859230,
|
||||
endTime: 1180493839859234,
|
||||
state: nsIDM.DOWNLOAD_FINISHED,
|
||||
currBytes: 0,
|
||||
maxBytes: -1,
|
||||
preferredAction: 0,
|
||||
autoResume: 0
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Infrastructure
|
||||
|
||||
// All test are run through the test runner.
|
||||
function test()
|
||||
{
|
||||
testRunner.runTest(this.gen_test);
|
||||
waitForExplicitFinish();
|
||||
Task.spawn(test_task).then(null, ex => ok(false, ex)).then(finish);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a browser-chrome test defined through a generator function.
|
||||
*
|
||||
* This object is a singleton, initialized automatically when this script is
|
||||
* included. Every browser-chrome test file includes a new copy of this object.
|
||||
*/
|
||||
var testRunner = {
|
||||
_testIterator: null,
|
||||
_lastEventResult: undefined,
|
||||
_testRunning: false,
|
||||
_eventRaised: false,
|
||||
|
||||
// --- Main test runner ---
|
||||
|
||||
/**
|
||||
* Runs the test described by the provided generator function asynchronously.
|
||||
*
|
||||
* Calling yield in the generator will cause it to wait until continueTest is
|
||||
* called. The parameter provided to continueTest will be the return value of
|
||||
* the yield operator.
|
||||
*
|
||||
* @param aGenerator
|
||||
* Test generator function. The function will be called with no
|
||||
* arguments to retrieve its iterator.
|
||||
*/
|
||||
runTest: function TR_runTest(aGenerator) {
|
||||
waitForExplicitFinish();
|
||||
testRunner._testIterator = aGenerator();
|
||||
testRunner.continueTest();
|
||||
},
|
||||
|
||||
/**
|
||||
* Continues the currently running test.
|
||||
*
|
||||
* @param aEventResult
|
||||
* This will be the return value of the yield operator in the test.
|
||||
*/
|
||||
continueTest: function TR_continueTest(aEventResult) {
|
||||
// Store the last event result, or set it to undefined.
|
||||
testRunner._lastEventResult = aEventResult;
|
||||
|
||||
// Never reenter the main loop, but notify that the event has been raised.
|
||||
if (testRunner._testRunning) {
|
||||
testRunner._eventRaised = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Enter the main iteration loop.
|
||||
testRunner._testRunning = true;
|
||||
try {
|
||||
do {
|
||||
// Call the iterator, but don't leave the loop if the expected event is
|
||||
// raised during the execution of the generator.
|
||||
testRunner._eventRaised = false;
|
||||
testRunner._testIterator.send(testRunner._lastEventResult);
|
||||
} while (testRunner._eventRaised);
|
||||
}
|
||||
catch (e) {
|
||||
// This block catches exceptions raised by the generator, including the
|
||||
// normal StopIteration exception. Unexpected exceptions are reported as
|
||||
// test failures.
|
||||
if (!(e instanceof StopIteration))
|
||||
ok(false, e);
|
||||
// In any case, stop the tests in this file.
|
||||
finish();
|
||||
}
|
||||
|
||||
// Wait for the next event or finish.
|
||||
testRunner._testRunning = false;
|
||||
}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Asynchronous generator-based support subroutines
|
||||
//// Asynchronous support subroutines
|
||||
|
||||
//
|
||||
// The following functions are all generators that can be used inside the main
|
||||
// test generator to perform specific tasks asynchronously. To invoke these
|
||||
// subroutines correctly, an iteration syntax should be used:
|
||||
//
|
||||
// for (let yy in gen_example("Parameter")) yield undefined;
|
||||
//
|
||||
|
||||
function gen_resetState(aData)
|
||||
function promiseFocus()
|
||||
{
|
||||
let statement = Services.downloads.DBConnection.createAsyncStatement(
|
||||
"DELETE FROM moz_downloads");
|
||||
try {
|
||||
statement.executeAsync({
|
||||
handleResult: function(aResultSet) { },
|
||||
handleError: function(aError)
|
||||
{
|
||||
Cu.reportError(aError);
|
||||
},
|
||||
handleCompletion: function(aReason)
|
||||
{
|
||||
testRunner.continueTest();
|
||||
}
|
||||
});
|
||||
yield undefined;
|
||||
} finally {
|
||||
statement.finalize();
|
||||
let deferred = Promise.defer();
|
||||
waitForFocus(deferred.resolve);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function promisePanelOpened()
|
||||
{
|
||||
let deferred = Promise.defer();
|
||||
|
||||
// Hook to wait until the panel is shown.
|
||||
let originalOnPopupShown = DownloadsPanel.onPopupShown;
|
||||
DownloadsPanel.onPopupShown = function () {
|
||||
DownloadsPanel.onPopupShown = originalOnPopupShown;
|
||||
originalOnPopupShown.apply(this, arguments);
|
||||
|
||||
// Defer to the next tick of the event loop so that we don't continue
|
||||
// processing during the DOM event handler itself.
|
||||
setTimeout(deferred.resolve, 0);
|
||||
};
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function task_resetState()
|
||||
{
|
||||
// Remove all downloads.
|
||||
let publicList = yield Downloads.getPublicDownloadList();
|
||||
let downloads = yield publicList.getAll();
|
||||
for (let download of downloads) {
|
||||
publicList.remove(download);
|
||||
yield download.finalize(true);
|
||||
}
|
||||
|
||||
// Reset any prefs that might have been changed.
|
||||
Services.prefs.clearUserPref("browser.download.panel.shown");
|
||||
|
||||
// Ensure that the panel is closed and data is unloaded.
|
||||
aData.clear();
|
||||
aData._loadState = aData.kLoadNone;
|
||||
DownloadsPanel.hidePanel();
|
||||
|
||||
// Wait for focus on the main window.
|
||||
waitForFocus(testRunner.continueTest);
|
||||
yield undefined;
|
||||
yield promiseFocus();
|
||||
}
|
||||
|
||||
function gen_addDownloadRows(aDataRows)
|
||||
function task_addDownloads(aItems)
|
||||
{
|
||||
let columnNames = Object.keys(gDownloadRowTemplate).join(", ");
|
||||
let parameterNames = Object.keys(gDownloadRowTemplate)
|
||||
.map(function(n) ":" + n)
|
||||
.join(", ");
|
||||
let statement = Services.downloads.DBConnection.createAsyncStatement(
|
||||
"INSERT INTO moz_downloads (" + columnNames +
|
||||
", guid) VALUES(" + parameterNames + ", GENERATE_GUID())");
|
||||
try {
|
||||
// Execute the statement for each of the provided downloads in reverse.
|
||||
for (let i = aDataRows.length - 1; i >= 0; i--) {
|
||||
let dataRow = aDataRows[i];
|
||||
let startTimeMs = Date.now();
|
||||
|
||||
// Populate insert parameters from the provided data.
|
||||
for (let columnName in gDownloadRowTemplate) {
|
||||
if (!(columnName in dataRow)) {
|
||||
// Update the provided row object with data from the global template,
|
||||
// for columns whose value is not provided explicitly.
|
||||
dataRow[columnName] = gDownloadRowTemplate[columnName];
|
||||
}
|
||||
statement.params[columnName] = dataRow[columnName];
|
||||
}
|
||||
|
||||
// Run the statement asynchronously and wait.
|
||||
statement.executeAsync({
|
||||
handleResult: function(aResultSet) { },
|
||||
handleError: function(aError)
|
||||
{
|
||||
Cu.reportError(aError.message + " (Result = " + aError.result + ")");
|
||||
},
|
||||
handleCompletion: function(aReason)
|
||||
{
|
||||
testRunner.continueTest();
|
||||
}
|
||||
});
|
||||
yield undefined;
|
||||
|
||||
// At each iteration, ensure that the start and end time in the global
|
||||
// template is distinct, as these column are used to sort each download
|
||||
// in its category.
|
||||
gDownloadRowTemplate.startTime++;
|
||||
gDownloadRowTemplate.endTime++;
|
||||
}
|
||||
} finally {
|
||||
statement.finalize();
|
||||
let publicList = yield Downloads.getPublicDownloadList();
|
||||
for (let item of aItems) {
|
||||
publicList.add(yield Downloads.createDownload({
|
||||
source: "http://www.example.com/test-download.txt",
|
||||
target: gTestTargetFile,
|
||||
succeeded: item.state == nsIDM.DOWNLOAD_FINISHED,
|
||||
canceled: item.state == nsIDM.DOWNLOAD_CANCELED ||
|
||||
item.state == nsIDM.DOWNLOAD_PAUSED,
|
||||
error: item.state == nsIDM.DOWNLOAD_FAILED ? new Error("Failed.") : null,
|
||||
hasPartialData: item.state == nsIDM.DOWNLOAD_PAUSED,
|
||||
startTime: new Date(startTimeMs++),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
function gen_openPanel(aData)
|
||||
function task_openPanel()
|
||||
{
|
||||
// Hook to wait until the test data has been loaded.
|
||||
let originalOnViewLoadCompleted = DownloadsPanel.onViewLoadCompleted;
|
||||
DownloadsPanel.onViewLoadCompleted = function () {
|
||||
DownloadsPanel.onViewLoadCompleted = originalOnViewLoadCompleted;
|
||||
originalOnViewLoadCompleted.apply(this);
|
||||
testRunner.continueTest();
|
||||
};
|
||||
yield promiseFocus();
|
||||
|
||||
// Start loading all the downloads from the database asynchronously.
|
||||
aData.ensurePersistentDataLoaded(false);
|
||||
|
||||
// Wait for focus on the main window.
|
||||
waitForFocus(testRunner.continueTest);
|
||||
yield undefined;
|
||||
|
||||
// Open the downloads panel, waiting until loading is completed.
|
||||
let promise = promisePanelOpened();
|
||||
DownloadsPanel.showPanel();
|
||||
yield undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Spin the event loop for aSeconds seconds, and then signal the test to
|
||||
* continue.
|
||||
*
|
||||
* @param aSeconds the number of seconds to wait.
|
||||
* @note This helper should _only_ be used when there's no valid event to
|
||||
* listen to and one can't be made.
|
||||
*/
|
||||
function waitFor(aSeconds)
|
||||
{
|
||||
setTimeout(function() {
|
||||
testRunner.continueTest();
|
||||
}, aSeconds * 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Make it so that the next time the downloads panel opens, we signal to
|
||||
* continue the test. This function needs to be called each time you want
|
||||
* to wait for the panel to open.
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* prepareForPanelOpen();
|
||||
* // Do something to open the panel
|
||||
* yield undefined;
|
||||
* // We can assume the panel is open now.
|
||||
*/
|
||||
function prepareForPanelOpen()
|
||||
{
|
||||
// Hook to wait until the test data has been loaded.
|
||||
let originalOnPopupShown = DownloadsPanel.onPopupShown;
|
||||
DownloadsPanel.onPopupShown = function (aEvent) {
|
||||
DownloadsPanel.onPopupShown = originalOnPopupShown;
|
||||
DownloadsPanel.onPopupShown.apply(this, [aEvent]);
|
||||
testRunner.continueTest();
|
||||
};
|
||||
yield promise;
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ NS_IMPL_ISUPPORTS1(nsIEHistoryEnumerator, nsISimpleEnumerator)
|
||||
|
||||
nsIEHistoryEnumerator::nsIEHistoryEnumerator()
|
||||
{
|
||||
::CoInitialize(NULL);
|
||||
::CoInitialize(nullptr);
|
||||
}
|
||||
|
||||
nsIEHistoryEnumerator::~nsIEHistoryEnumerator()
|
||||
@ -60,7 +60,7 @@ nsIEHistoryEnumerator::EnsureInitialized()
|
||||
return;
|
||||
|
||||
HRESULT hr = ::CoCreateInstance(CLSID_CUrlHistory,
|
||||
NULL,
|
||||
nullptr,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_IUrlHistoryStg2,
|
||||
getter_AddRefs(mIEHistory));
|
||||
|
@ -262,6 +262,12 @@ let SessionSaverInternal = {
|
||||
return;
|
||||
}
|
||||
|
||||
// We update the time stamp before writing so that we don't write again
|
||||
// too soon, if saving is requested before the write completes. Without
|
||||
// this update we may save repeatedly if actions cause a runDelayed
|
||||
// before writing has completed. See Bug 902280
|
||||
this.updateLastSaveTime();
|
||||
|
||||
// Write (atomically) to a session file, using a tmp file. Once the session
|
||||
// file is successfully updated, save the time stamp of the last save and
|
||||
// notify the observers.
|
||||
|
@ -75,7 +75,7 @@ this._SessionFile = {
|
||||
* state. This must only be called once, it will throw an error otherwise.
|
||||
*/
|
||||
writeLoadStateOnceAfterStartup: function (aLoadState) {
|
||||
return SessionFileInternal.writeLoadStateOnceAfterStartup(aLoadState);
|
||||
SessionFileInternal.writeLoadStateOnceAfterStartup(aLoadState);
|
||||
},
|
||||
/**
|
||||
* Create a backup copy, asynchronously.
|
||||
@ -95,7 +95,7 @@ this._SessionFile = {
|
||||
* Wipe the contents of the session file, asynchronously.
|
||||
*/
|
||||
wipe: function () {
|
||||
return SessionFileInternal.wipe();
|
||||
SessionFileInternal.wipe();
|
||||
}
|
||||
};
|
||||
|
||||
@ -231,7 +231,7 @@ let SessionFileInternal = {
|
||||
},
|
||||
|
||||
writeLoadStateOnceAfterStartup: function (aLoadState) {
|
||||
return SessionWorker.post("writeLoadStateOnceAfterStartup", [aLoadState]).then(msg => {
|
||||
SessionWorker.post("writeLoadStateOnceAfterStartup", [aLoadState]).then(msg => {
|
||||
this._recordTelemetry(msg.telemetry);
|
||||
return msg;
|
||||
});
|
||||
@ -246,7 +246,7 @@ let SessionFileInternal = {
|
||||
},
|
||||
|
||||
wipe: function () {
|
||||
return SessionWorker.post("wipe");
|
||||
SessionWorker.post("wipe");
|
||||
},
|
||||
|
||||
_recordTelemetry: function(telemetry) {
|
||||
|
@ -152,7 +152,8 @@ nsGNOMEShellService::KeyMatchesAppName(const char *aKeyValue) const
|
||||
|
||||
gchar *commandPath;
|
||||
if (mUseLocaleFilenames) {
|
||||
gchar *nativePath = g_filename_from_utf8(aKeyValue, -1, NULL, NULL, NULL);
|
||||
gchar *nativePath = g_filename_from_utf8(aKeyValue, -1,
|
||||
nullptr, nullptr, nullptr);
|
||||
if (!nativePath) {
|
||||
NS_ERROR("Error converting path to filesystem encoding");
|
||||
return false;
|
||||
@ -182,7 +183,7 @@ nsGNOMEShellService::CheckHandlerMatchesAppName(const nsACString &handler) const
|
||||
// The string will be something of the form: [/path/to/]browser "%s"
|
||||
// We want to remove all of the parameters and get just the binary name.
|
||||
|
||||
if (g_shell_parse_argv(command.get(), &argc, &argv, NULL) && argc > 0) {
|
||||
if (g_shell_parse_argv(command.get(), &argc, &argv, nullptr) && argc > 0) {
|
||||
command.Assign(argv[0]);
|
||||
g_strfreev(argv);
|
||||
}
|
||||
@ -380,7 +381,7 @@ WriteImage(const nsCString& aPath, imgIContainer* aImage)
|
||||
if (!pixbuf)
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
|
||||
gboolean res = gdk_pixbuf_save(pixbuf, aPath.get(), "png", NULL, NULL);
|
||||
gboolean res = gdk_pixbuf_save(pixbuf, aPath.get(), "png", nullptr, nullptr);
|
||||
|
||||
g_object_unref(pixbuf);
|
||||
return res ? NS_OK : NS_ERROR_FAILURE;
|
||||
@ -454,7 +455,7 @@ nsGNOMEShellService::SetDesktopBackground(nsIDOMElement* aElement,
|
||||
gsettings->GetCollectionForSchema(
|
||||
NS_LITERAL_CSTRING(kDesktopBGSchema), getter_AddRefs(background_settings));
|
||||
if (background_settings) {
|
||||
gchar *file_uri = g_filename_to_uri(filePath.get(), NULL, NULL);
|
||||
gchar *file_uri = g_filename_to_uri(filePath.get(), nullptr, nullptr);
|
||||
if (!file_uri)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
@ -615,7 +616,7 @@ nsGNOMEShellService::OpenApplication(int32_t aApplication)
|
||||
// Perform shell argument expansion
|
||||
int argc;
|
||||
char **argv;
|
||||
if (!g_shell_parse_argv(appCommand.get(), &argc, &argv, NULL))
|
||||
if (!g_shell_parse_argv(appCommand.get(), &argc, &argv, nullptr))
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
char **newArgv = new char*[argc + 1];
|
||||
@ -630,8 +631,8 @@ nsGNOMEShellService::OpenApplication(int32_t aApplication)
|
||||
|
||||
newArgv[newArgc] = nullptr;
|
||||
|
||||
gboolean err = g_spawn_async(NULL, newArgv, NULL, G_SPAWN_SEARCH_PATH,
|
||||
NULL, NULL, NULL, NULL);
|
||||
gboolean err = g_spawn_async(nullptr, newArgv, nullptr, G_SPAWN_SEARCH_PATH,
|
||||
nullptr, nullptr, nullptr, nullptr);
|
||||
|
||||
g_strfreev(argv);
|
||||
delete[] newArgv;
|
||||
|
@ -40,13 +40,14 @@ nsMacShellService::IsDefaultBrowser(bool aStartupCheck,
|
||||
|
||||
CFStringRef firefoxID = ::CFBundleGetIdentifier(::CFBundleGetMainBundle());
|
||||
if (!firefoxID) {
|
||||
// CFBundleGetIdentifier is expected to return NULL only if the specified
|
||||
// CFBundleGetIdentifier is expected to return nullptr only if the specified
|
||||
// bundle doesn't have a bundle identifier in its plist. In this case, that
|
||||
// means a failure, since our bundle does have an identifier.
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Get the default http handler's bundle ID (or NULL if it has not been explicitly set)
|
||||
// Get the default http handler's bundle ID (or nullptr if it has not been
|
||||
// explicitly set)
|
||||
CFStringRef defaultBrowserID = ::LSCopyDefaultHandlerForURLScheme(CFSTR("http"));
|
||||
if (defaultBrowserID) {
|
||||
*aIsDefaultBrowser = ::CFStringCompare(firefoxID, defaultBrowserID, 0) == kCFCompareEqualTo;
|
||||
@ -259,7 +260,8 @@ nsMacShellService::OnStateChange(nsIWebProgress* aWebProgress,
|
||||
OSStatus status;
|
||||
|
||||
// Convert the path into a FSRef
|
||||
status = ::FSPathMakeRef((const UInt8*)nativePath.get(), &pictureRef, NULL);
|
||||
status = ::FSPathMakeRef((const UInt8*)nativePath.get(), &pictureRef,
|
||||
nullptr);
|
||||
if (status == noErr) {
|
||||
err = ::FSNewAlias(nil, &pictureRef, &aliasHandle);
|
||||
if (err == noErr && aliasHandle == nil)
|
||||
@ -312,22 +314,22 @@ nsMacShellService::OpenApplication(int32_t aApplication)
|
||||
case nsIShellService::APPLICATION_MAIL:
|
||||
{
|
||||
CFURLRef tempURL = ::CFURLCreateWithString(kCFAllocatorDefault,
|
||||
CFSTR("mailto:"), NULL);
|
||||
err = ::LSGetApplicationForURL(tempURL, kLSRolesAll, NULL, &appURL);
|
||||
CFSTR("mailto:"), nullptr);
|
||||
err = ::LSGetApplicationForURL(tempURL, kLSRolesAll, nullptr, &appURL);
|
||||
::CFRelease(tempURL);
|
||||
}
|
||||
break;
|
||||
case nsIShellService::APPLICATION_NEWS:
|
||||
{
|
||||
CFURLRef tempURL = ::CFURLCreateWithString(kCFAllocatorDefault,
|
||||
CFSTR("news:"), NULL);
|
||||
err = ::LSGetApplicationForURL(tempURL, kLSRolesAll, NULL, &appURL);
|
||||
CFSTR("news:"), nullptr);
|
||||
err = ::LSGetApplicationForURL(tempURL, kLSRolesAll, nullptr, &appURL);
|
||||
::CFRelease(tempURL);
|
||||
}
|
||||
break;
|
||||
case nsIMacShellService::APPLICATION_KEYCHAIN_ACCESS:
|
||||
err = ::LSGetApplicationForInfo('APPL', 'kcmr', NULL, kLSRolesAll, NULL,
|
||||
&appURL);
|
||||
err = ::LSGetApplicationForInfo('APPL', 'kcmr', nullptr, kLSRolesAll,
|
||||
nullptr, &appURL);
|
||||
break;
|
||||
case nsIMacShellService::APPLICATION_NETWORK:
|
||||
{
|
||||
@ -356,7 +358,7 @@ nsMacShellService::OpenApplication(int32_t aApplication)
|
||||
}
|
||||
|
||||
if (appURL && err == noErr) {
|
||||
err = ::LSOpenCFURLRef(appURL, NULL);
|
||||
err = ::LSOpenCFURLRef(appURL, nullptr);
|
||||
rv = err != noErr ? NS_ERROR_FAILURE : NS_OK;
|
||||
|
||||
::CFRelease(appURL);
|
||||
@ -394,12 +396,12 @@ nsMacShellService::OpenApplicationWithURI(nsIFile* aApplication, const nsACStrin
|
||||
|
||||
const nsCString spec(aURI);
|
||||
const UInt8* uriString = (const UInt8*)spec.get();
|
||||
CFURLRef uri = ::CFURLCreateWithBytes(NULL, uriString, aURI.Length(),
|
||||
kCFStringEncodingUTF8, NULL);
|
||||
CFURLRef uri = ::CFURLCreateWithBytes(nullptr, uriString, aURI.Length(),
|
||||
kCFStringEncodingUTF8, nullptr);
|
||||
if (!uri)
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
|
||||
CFArrayRef uris = ::CFArrayCreate(NULL, (const void**)&uri, 1, NULL);
|
||||
CFArrayRef uris = ::CFArrayCreate(nullptr, (const void**)&uri, 1, nullptr);
|
||||
if (!uris) {
|
||||
::CFRelease(uri);
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
@ -408,11 +410,11 @@ nsMacShellService::OpenApplicationWithURI(nsIFile* aApplication, const nsACStrin
|
||||
LSLaunchURLSpec launchSpec;
|
||||
launchSpec.appURL = appURL;
|
||||
launchSpec.itemURLs = uris;
|
||||
launchSpec.passThruParams = NULL;
|
||||
launchSpec.passThruParams = nullptr;
|
||||
launchSpec.launchFlags = kLSLaunchDefaults;
|
||||
launchSpec.asyncRefCon = NULL;
|
||||
launchSpec.asyncRefCon = nullptr;
|
||||
|
||||
OSErr err = ::LSOpenFromURLSpec(&launchSpec, NULL);
|
||||
OSErr err = ::LSOpenFromURLSpec(&launchSpec, nullptr);
|
||||
|
||||
::CFRelease(uris);
|
||||
::CFRelease(uri);
|
||||
@ -433,11 +435,11 @@ nsMacShellService::GetDefaultFeedReader(nsIFile** _retval)
|
||||
kCFStringEncodingASCII);
|
||||
}
|
||||
|
||||
CFURLRef defaultHandlerURL = NULL;
|
||||
CFURLRef defaultHandlerURL = nullptr;
|
||||
OSStatus status = ::LSFindApplicationForInfo(kLSUnknownCreator,
|
||||
defaultHandlerID,
|
||||
NULL, // inName
|
||||
NULL, // outAppRef
|
||||
nullptr, // inName
|
||||
nullptr, // outAppRef
|
||||
&defaultHandlerURL);
|
||||
|
||||
if (status == noErr && defaultHandlerURL) {
|
||||
|
@ -223,8 +223,8 @@ LaunchHelper(nsAutoString& aPath)
|
||||
STARTUPINFOW si = {sizeof(si), 0};
|
||||
PROCESS_INFORMATION pi = {0};
|
||||
|
||||
if (!CreateProcessW(NULL, (LPWSTR)aPath.get(), NULL, NULL, FALSE, 0, NULL,
|
||||
NULL, &si, &pi)) {
|
||||
if (!CreateProcessW(nullptr, (LPWSTR)aPath.get(), nullptr, nullptr, FALSE,
|
||||
0, nullptr, nullptr, &si, &pi)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
@ -361,7 +361,7 @@ nsWindowsShellService::IsDefaultBrowserVista(bool aCheckAllTypes,
|
||||
{
|
||||
IApplicationAssociationRegistration* pAAR;
|
||||
HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration,
|
||||
NULL,
|
||||
nullptr,
|
||||
CLSCTX_INPROC,
|
||||
IID_IApplicationAssociationRegistration,
|
||||
(void**)&pAAR);
|
||||
@ -447,7 +447,8 @@ nsWindowsShellService::IsDefaultBrowser(bool aStartupCheck,
|
||||
|
||||
::ZeroMemory(currValue, sizeof(currValue));
|
||||
DWORD len = sizeof currValue;
|
||||
res = ::RegQueryValueExW(theKey, L"", NULL, NULL, (LPBYTE)currValue, &len);
|
||||
res = ::RegQueryValueExW(theKey, L"", nullptr, nullptr,
|
||||
(LPBYTE)currValue, &len);
|
||||
// Close the key that was opened.
|
||||
::RegCloseKey(theKey);
|
||||
if (REG_FAILED(res) ||
|
||||
@ -516,8 +517,8 @@ nsWindowsShellService::IsDefaultBrowser(bool aStartupCheck,
|
||||
|
||||
::ZeroMemory(currValue, sizeof(currValue));
|
||||
DWORD len = sizeof currValue;
|
||||
res = ::RegQueryValueExW(theKey, L"", NULL, NULL, (LPBYTE)currValue,
|
||||
&len);
|
||||
res = ::RegQueryValueExW(theKey, L"", nullptr, nullptr,
|
||||
(LPBYTE)currValue, &len);
|
||||
// Close the key that was opened.
|
||||
::RegCloseKey(theKey);
|
||||
if (REG_FAILED(res) || PRUnichar('\0') != *currValue) {
|
||||
@ -525,9 +526,9 @@ nsWindowsShellService::IsDefaultBrowser(bool aStartupCheck,
|
||||
// Delete the key along with all of its childrean and then recreate it.
|
||||
const nsString &flatName = PromiseFlatString(keyName);
|
||||
::SHDeleteKeyW(HKEY_CURRENT_USER, flatName.get());
|
||||
res = ::RegCreateKeyExW(HKEY_CURRENT_USER, flatName.get(), 0, NULL,
|
||||
REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL,
|
||||
&theKey, NULL);
|
||||
res = ::RegCreateKeyExW(HKEY_CURRENT_USER, flatName.get(), 0, nullptr,
|
||||
REG_OPTION_NON_VOLATILE, KEY_SET_VALUE,
|
||||
nullptr, &theKey, nullptr);
|
||||
if (REG_FAILED(res)) {
|
||||
// If disabling DDE fails try to disable it using the helper
|
||||
// application when setting Firefox as the default browser.
|
||||
@ -564,7 +565,7 @@ nsWindowsShellService::IsDefaultBrowser(bool aStartupCheck,
|
||||
|
||||
::ZeroMemory(currValue, sizeof(currValue));
|
||||
DWORD len = sizeof currValue;
|
||||
res = ::RegQueryValueExW(theKey, L"", NULL, NULL, (LPBYTE)currValue,
|
||||
res = ::RegQueryValueExW(theKey, L"", nullptr, nullptr, (LPBYTE)currValue,
|
||||
&len);
|
||||
|
||||
// Don't update the FTP protocol handler's shell open command when the
|
||||
@ -606,7 +607,7 @@ DynSHOpenWithDialog(HWND hwndParent, const OPENASINFO *poainfo)
|
||||
{
|
||||
typedef HRESULT (WINAPI * SHOpenWithDialogPtr)(HWND hwndParent,
|
||||
const OPENASINFO *poainfo);
|
||||
static SHOpenWithDialogPtr SHOpenWithDialogFn = NULL;
|
||||
static SHOpenWithDialogPtr SHOpenWithDialogFn = nullptr;
|
||||
if (!SHOpenWithDialogFn) {
|
||||
// shell32.dll is in the knownDLLs list so will always be loaded from the
|
||||
// system32 directory.
|
||||
@ -650,8 +651,8 @@ nsWindowsShellService::LaunchControlPanelDefaultPrograms()
|
||||
si.dwFlags = STARTF_USESHOWWINDOW;
|
||||
si.wShowWindow = SW_SHOWDEFAULT;
|
||||
PROCESS_INFORMATION pi = {0};
|
||||
if (!CreateProcessW(controlEXEPath, params, NULL, NULL, FALSE, 0, NULL,
|
||||
NULL, &si, &pi)) {
|
||||
if (!CreateProcessW(controlEXEPath, params, nullptr, nullptr, FALSE,
|
||||
0, nullptr, nullptr, &si, &pi)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
CloseHandle(pi.hProcess);
|
||||
@ -665,11 +666,11 @@ nsWindowsShellService::LaunchHTTPHandlerPane()
|
||||
{
|
||||
OPENASINFO info;
|
||||
info.pcszFile = L"http";
|
||||
info.pcszClass = NULL;
|
||||
info.pcszClass = nullptr;
|
||||
info.oaifInFlags = OAIF_FORCE_REGISTRATION |
|
||||
OAIF_URL_PROTOCOL |
|
||||
OAIF_REGISTER_EXT;
|
||||
return DynSHOpenWithDialog(NULL, &info);
|
||||
return DynSHOpenWithDialog(nullptr, &info);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -1028,8 +1029,8 @@ nsWindowsShellService::OpenApplication(int32_t aApplication)
|
||||
::ZeroMemory(&si, sizeof(STARTUPINFOW));
|
||||
::ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
|
||||
|
||||
BOOL success = ::CreateProcessW(NULL, (LPWSTR)path.get(), NULL,
|
||||
NULL, FALSE, 0, NULL, NULL,
|
||||
BOOL success = ::CreateProcessW(nullptr, (LPWSTR)path.get(), nullptr,
|
||||
nullptr, FALSE, 0, nullptr, nullptr,
|
||||
&si, &pi);
|
||||
if (!success)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
@ -46,11 +46,13 @@ var APZCObserver = {
|
||||
case 'TabOpen': {
|
||||
let browser = aEvent.originalTarget.linkedBrowser;
|
||||
browser.addEventListener("pageshow", this, true);
|
||||
browser.messageManager.addMessageListener("scroll", this);
|
||||
break;
|
||||
}
|
||||
case 'TabClose': {
|
||||
let browser = aEvent.originalTarget.linkedBrowser;
|
||||
browser.removeEventListener("pageshow", this);
|
||||
browser.removeEventListener("pageshow", this, true);
|
||||
browser.messageManager.removeMessageListener("scroll", this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -113,5 +115,16 @@ var APZCObserver = {
|
||||
} else if (aTopic == "apzc-handle-pan-end") {
|
||||
Util.dumpLn("APZC pan-end");
|
||||
}
|
||||
},
|
||||
|
||||
receiveMessage: function(aMessage) {
|
||||
let json = aMessage.json;
|
||||
switch (aMessage.name) {
|
||||
case "scroll": {
|
||||
let data = json.viewId + " " + json.presShellId + " (" + json.scrollOffset.x + ", " + json.scrollOffset.y + ")";
|
||||
Services.obs.notifyObservers(null, "scroll-offset-changed", data);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -652,11 +652,7 @@ let ContentScroll = {
|
||||
break;
|
||||
|
||||
case "scroll": {
|
||||
let doc = aEvent.target;
|
||||
if (doc != content.document)
|
||||
break;
|
||||
|
||||
this.sendScroll();
|
||||
this.sendScroll(aEvent.target);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -683,13 +679,35 @@ let ContentScroll = {
|
||||
}
|
||||
},
|
||||
|
||||
sendScroll: function sendScroll() {
|
||||
let scrollOffset = this.getScrollOffset(content);
|
||||
if (this._scrollOffset.x == scrollOffset.x && this._scrollOffset.y == scrollOffset.y)
|
||||
return;
|
||||
sendScroll: function sendScroll(target) {
|
||||
let isRoot = false;
|
||||
if (target instanceof Ci.nsIDOMDocument) {
|
||||
var window = target.defaultView;
|
||||
var scrollOffset = this.getScrollOffset(window);
|
||||
var element = target.documentElement;
|
||||
|
||||
this._scrollOffset = scrollOffset;
|
||||
sendAsyncMessage("scroll", scrollOffset);
|
||||
if (target == content.document) {
|
||||
if (this._scrollOffset.x == scrollOffset.x && this._scrollOffset.y == scrollOffset.y) {
|
||||
return;
|
||||
}
|
||||
this._scrollOffset = scrollOffset;
|
||||
isRoot = true;
|
||||
}
|
||||
} else {
|
||||
var window = target.currentDoc.defaultView;
|
||||
var scrollOffset = this.getScrollOffsetForElement(target);
|
||||
var element = target;
|
||||
}
|
||||
|
||||
let utils = window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
|
||||
let presShellId = {};
|
||||
utils.getPresShellId(presShellId);
|
||||
let viewId = utils.getViewId(element);
|
||||
|
||||
sendAsyncMessage("scroll", { presShellId: presShellId.value,
|
||||
viewId: viewId,
|
||||
scrollOffset: scrollOffset,
|
||||
isRoot: isRoot });
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -859,9 +859,11 @@
|
||||
|
||||
switch (aMessage.name) {
|
||||
case "scroll":
|
||||
if (!json.isRoot)
|
||||
return;
|
||||
if (!self.scrollSync)
|
||||
return;
|
||||
this.doScroll(json.x, json.y, 0);
|
||||
this.doScroll(json.scrollOffset.x, json.scrollOffset.y, 0);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
@ -23,7 +23,7 @@ Log(const wchar_t *fmt, ...)
|
||||
#if !defined(SHOW_CONSOLE)
|
||||
return;
|
||||
#endif
|
||||
va_list a = NULL;
|
||||
va_list a = nullptr;
|
||||
wchar_t szDebugString[1024];
|
||||
if(!lstrlenW(fmt))
|
||||
return;
|
||||
@ -34,8 +34,8 @@ Log(const wchar_t *fmt, ...)
|
||||
return;
|
||||
|
||||
DWORD len;
|
||||
WriteConsoleW(sCon, szDebugString, lstrlenW(szDebugString), &len, NULL);
|
||||
WriteConsoleW(sCon, L"\n", 1, &len, NULL);
|
||||
WriteConsoleW(sCon, szDebugString, lstrlenW(szDebugString), &len, nullptr);
|
||||
WriteConsoleW(sCon, L"\n", 1, &len, nullptr);
|
||||
|
||||
if (IsDebuggerPresent()) {
|
||||
OutputDebugStringW(szDebugString);
|
||||
@ -53,7 +53,7 @@ SetupConsole()
|
||||
int fd = _open_osfhandle(reinterpret_cast<intptr_t>(sCon), 0);
|
||||
fp = _fdopen(fd, "w");
|
||||
*stdout = *fp;
|
||||
setvbuf(stdout, NULL, _IONBF, 0);
|
||||
setvbuf(stdout, nullptr, _IONBF, 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -104,19 +104,19 @@ IsDX10Available()
|
||||
|
||||
CComPtr<ID3D10Device1> device;
|
||||
// Try for DX10.1
|
||||
if (FAILED(createD3DDevice(adapter1, D3D10_DRIVER_TYPE_HARDWARE, NULL,
|
||||
if (FAILED(createD3DDevice(adapter1, D3D10_DRIVER_TYPE_HARDWARE, nullptr,
|
||||
D3D10_CREATE_DEVICE_BGRA_SUPPORT |
|
||||
D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
|
||||
D3D10_FEATURE_LEVEL_10_1,
|
||||
D3D10_1_SDK_VERSION, &device))) {
|
||||
// Try for DX10
|
||||
if (FAILED(createD3DDevice(adapter1, D3D10_DRIVER_TYPE_HARDWARE, NULL,
|
||||
if (FAILED(createD3DDevice(adapter1, D3D10_DRIVER_TYPE_HARDWARE, nullptr,
|
||||
D3D10_CREATE_DEVICE_BGRA_SUPPORT |
|
||||
D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
|
||||
D3D10_FEATURE_LEVEL_10_0,
|
||||
D3D10_1_SDK_VERSION, &device))) {
|
||||
// Try for DX9.3 (we fall back to cairo and cairo has support for D3D 9.3)
|
||||
if (FAILED(createD3DDevice(adapter1, D3D10_DRIVER_TYPE_HARDWARE, NULL,
|
||||
if (FAILED(createD3DDevice(adapter1, D3D10_DRIVER_TYPE_HARDWARE, nullptr,
|
||||
D3D10_CREATE_DEVICE_BGRA_SUPPORT |
|
||||
D3D10_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS,
|
||||
D3D10_FEATURE_LEVEL_9_3,
|
||||
|
@ -49,7 +49,7 @@ static bool GetModulePath(CStringW& aPathBuffer)
|
||||
WCHAR buffer[MAX_PATH];
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
|
||||
if (!GetModuleFileName(NULL, buffer, MAX_PATH)) {
|
||||
if (!GetModuleFileName(nullptr, buffer, MAX_PATH)) {
|
||||
Log(L"GetModuleFileName failed.");
|
||||
return false;
|
||||
}
|
||||
@ -68,7 +68,7 @@ template <class T>void SafeRelease(T **ppT)
|
||||
{
|
||||
if (*ppT) {
|
||||
(*ppT)->Release();
|
||||
*ppT = NULL;
|
||||
*ppT = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,8 +89,8 @@ public:
|
||||
|
||||
CExecuteCommandVerb() :
|
||||
mRef(1),
|
||||
mShellItemArray(NULL),
|
||||
mUnkSite(NULL),
|
||||
mShellItemArray(nullptr),
|
||||
mUnkSite(nullptr),
|
||||
mTargetIsFileSystemLink(false),
|
||||
mTargetIsDefaultBrowser(false),
|
||||
mTargetIsBrowser(false),
|
||||
@ -176,9 +176,9 @@ public:
|
||||
#ifdef SHOW_CONSOLE
|
||||
Log(L"SetSelection param count: %d", count);
|
||||
for (DWORD idx = 0; idx < count; idx++) {
|
||||
IShellItem* item = NULL;
|
||||
IShellItem* item = nullptr;
|
||||
if (SUCCEEDED(aArray->GetItemAt(idx, &item))) {
|
||||
LPWSTR str = NULL;
|
||||
LPWSTR str = nullptr;
|
||||
if (FAILED(item->GetDisplayName(SIGDN_FILESYSPATH, &str))) {
|
||||
if (FAILED(item->GetDisplayName(SIGDN_URL, &str))) {
|
||||
Log(L"Failed to get a shell item array item.");
|
||||
@ -193,7 +193,7 @@ public:
|
||||
}
|
||||
#endif
|
||||
|
||||
IShellItem* item = NULL;
|
||||
IShellItem* item = nullptr;
|
||||
if (FAILED(aArray->GetItemAt(0, &item))) {
|
||||
return E_FAIL;
|
||||
}
|
||||
@ -211,7 +211,7 @@ public:
|
||||
|
||||
IFACEMETHODIMP GetSelection(REFIID aRefID, void **aInt)
|
||||
{
|
||||
*aInt = NULL;
|
||||
*aInt = nullptr;
|
||||
return mShellItemArray ? mShellItemArray->QueryInterface(aRefID, aInt) : E_FAIL;
|
||||
}
|
||||
|
||||
@ -235,7 +235,7 @@ public:
|
||||
|
||||
IFACEMETHODIMP GetSite(REFIID aRefID, void **aInt)
|
||||
{
|
||||
*aInt = NULL;
|
||||
*aInt = nullptr;
|
||||
return mUnkSite ? mUnkSite->QueryInterface(aRefID, aInt) : E_FAIL;
|
||||
}
|
||||
|
||||
@ -252,14 +252,14 @@ public:
|
||||
}
|
||||
|
||||
HRESULT hr;
|
||||
IServiceProvider* pSvcProvider = NULL;
|
||||
IServiceProvider* pSvcProvider = nullptr;
|
||||
hr = mUnkSite->QueryInterface(IID_IServiceProvider, (void**)&pSvcProvider);
|
||||
if (!pSvcProvider) {
|
||||
Log(L"Couldn't get IServiceProvider service from explorer. (%X)", hr);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IExecuteCommandHost* pHost = NULL;
|
||||
IExecuteCommandHost* pHost = nullptr;
|
||||
// If we can't get this it's a conventional desktop launch
|
||||
hr = pSvcProvider->QueryService(SID_ExecuteCommandHost,
|
||||
IID_IExecuteCommandHost, (void**)&pHost);
|
||||
@ -340,7 +340,7 @@ public:
|
||||
{
|
||||
IApplicationAssociationRegistration* pAAR;
|
||||
HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration,
|
||||
NULL,
|
||||
nullptr,
|
||||
CLSCTX_INPROC,
|
||||
IID_IApplicationAssociationRegistration,
|
||||
(void**)&pAAR);
|
||||
@ -416,7 +416,7 @@ static bool GetDefaultBrowserPath(CStringW& aPathBuffer)
|
||||
|
||||
if (FAILED(AssocQueryStringW(ASSOCF_NOTRUNCATE | ASSOCF_INIT_IGNOREUNKNOWN,
|
||||
ASSOCSTR_EXECUTABLE,
|
||||
kDefaultMetroBrowserIDPathKey, NULL,
|
||||
kDefaultMetroBrowserIDPathKey, nullptr,
|
||||
buffer, &length))) {
|
||||
Log(L"AssocQueryString failed.");
|
||||
return false;
|
||||
@ -451,7 +451,7 @@ static bool GetDefaultBrowserAppModelID(WCHAR* aIDBuffer,
|
||||
}
|
||||
DWORD len = aCharLength * sizeof(WCHAR);
|
||||
memset(aIDBuffer, 0, len);
|
||||
if (RegQueryValueExW(key, L"AppUserModelID", NULL, NULL,
|
||||
if (RegQueryValueExW(key, L"AppUserModelID", nullptr, nullptr,
|
||||
(LPBYTE)aIDBuffer, &len) != ERROR_SUCCESS || !len) {
|
||||
RegCloseKey(key);
|
||||
return false;
|
||||
@ -513,7 +513,7 @@ bool CExecuteCommandVerb::SetTargetPath(IShellItem* aItem)
|
||||
CComPtr<IDataObject> object;
|
||||
// Check the underlying data object first to insure we get
|
||||
// absolute uri. See chromium bug 157184.
|
||||
if (SUCCEEDED(aItem->BindToHandler(NULL, BHID_DataObject,
|
||||
if (SUCCEEDED(aItem->BindToHandler(nullptr, BHID_DataObject,
|
||||
IID_IDataObject,
|
||||
reinterpret_cast<void**>(&object))) &&
|
||||
GetPlainText(object, cstrText)) {
|
||||
@ -537,7 +537,7 @@ bool CExecuteCommandVerb::SetTargetPath(IShellItem* aItem)
|
||||
Log(L"No data object or data object has no text.");
|
||||
|
||||
// Use the shell item display name
|
||||
LPWSTR str = NULL;
|
||||
LPWSTR str = nullptr;
|
||||
mTargetIsFileSystemLink = true;
|
||||
if (FAILED(aItem->GetDisplayName(SIGDN_FILESYSPATH, &str))) {
|
||||
mTargetIsFileSystemLink = false;
|
||||
@ -596,12 +596,12 @@ void CExecuteCommandVerb::LaunchDesktopBrowser()
|
||||
SHELLEXECUTEINFOW seinfo;
|
||||
memset(&seinfo, 0, sizeof(seinfo));
|
||||
seinfo.cbSize = sizeof(SHELLEXECUTEINFOW);
|
||||
seinfo.fMask = NULL;
|
||||
seinfo.hwnd = NULL;
|
||||
seinfo.lpVerb = NULL;
|
||||
seinfo.fMask = 0;
|
||||
seinfo.hwnd = nullptr;
|
||||
seinfo.lpVerb = nullptr;
|
||||
seinfo.lpFile = browserPath;
|
||||
seinfo.lpParameters = params;
|
||||
seinfo.lpDirectory = NULL;
|
||||
seinfo.lpDirectory = nullptr;
|
||||
seinfo.nShow = SW_SHOWNORMAL;
|
||||
|
||||
ShellExecuteExW(&seinfo);
|
||||
@ -635,9 +635,9 @@ IFACEMETHODIMP CExecuteCommandVerb::Execute()
|
||||
}
|
||||
|
||||
// Launch into Metro
|
||||
IApplicationActivationManager* activateMgr = NULL;
|
||||
IApplicationActivationManager* activateMgr = nullptr;
|
||||
DWORD processID;
|
||||
if (FAILED(CoCreateInstance(CLSID_ApplicationActivationManager, NULL,
|
||||
if (FAILED(CoCreateInstance(CLSID_ApplicationActivationManager, nullptr,
|
||||
CLSCTX_LOCAL_SERVER,
|
||||
IID_IApplicationActivationManager,
|
||||
(void**)&activateMgr))) {
|
||||
@ -657,7 +657,7 @@ IFACEMETHODIMP CExecuteCommandVerb::Execute()
|
||||
|
||||
// Hand off focus rights to the out-of-process activation server. Without
|
||||
// this the metro interface won't launch.
|
||||
hr = CoAllowSetForegroundWindow(activateMgr, NULL);
|
||||
hr = CoAllowSetForegroundWindow(activateMgr, nullptr);
|
||||
if (FAILED(hr)) {
|
||||
Log(L"CoAllowSetForegroundWindow result %X", hr);
|
||||
activateMgr->Release();
|
||||
@ -727,7 +727,7 @@ ClassFactory::Register(CLSCTX aClass, REGCLS aUse)
|
||||
STDMETHODIMP
|
||||
ClassFactory::QueryInterface(REFIID riid, void **ppv)
|
||||
{
|
||||
IUnknown *punk = NULL;
|
||||
IUnknown *punk = nullptr;
|
||||
if (riid == IID_IUnknown || riid == IID_IClassFactory) {
|
||||
punk = static_cast<IClassFactory*>(this);
|
||||
}
|
||||
@ -743,7 +743,7 @@ ClassFactory::QueryInterface(REFIID riid, void **ppv)
|
||||
STDMETHODIMP
|
||||
ClassFactory::CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
|
||||
{
|
||||
*ppv = NULL;
|
||||
*ppv = nullptr;
|
||||
if (punkOuter)
|
||||
return CLASS_E_NOAGGREGATION;
|
||||
return mUnkObject->QueryInterface(riid, ppv);
|
||||
@ -771,7 +771,7 @@ int APIENTRY wWinMain(HINSTANCE, HINSTANCE, PWSTR pszCmdLine, int)
|
||||
|
||||
if (!wcslen(pszCmdLine) || StrStrI(pszCmdLine, L"-Embedding"))
|
||||
{
|
||||
CoInitialize(NULL);
|
||||
CoInitialize(nullptr);
|
||||
|
||||
CExecuteCommandVerb *pHandler = new CExecuteCommandVerb();
|
||||
if (!pHandler)
|
||||
@ -784,13 +784,13 @@ int APIENTRY wWinMain(HINSTANCE, HINSTANCE, PWSTR pszCmdLine, int)
|
||||
|
||||
ClassFactory classFactory(ppi);
|
||||
ppi->Release();
|
||||
ppi = NULL;
|
||||
ppi = nullptr;
|
||||
|
||||
// REGCLS_SINGLEUSE insures we only get used once and then discarded.
|
||||
if (FAILED(classFactory.Register(CLSCTX_LOCAL_SERVER, REGCLS_SINGLEUSE)))
|
||||
return -1;
|
||||
|
||||
if (!SetTimer(NULL, 1, HEARTBEAT_MSEC, NULL)) {
|
||||
if (!SetTimer(nullptr, 1, HEARTBEAT_MSEC, nullptr)) {
|
||||
Log(L"Failed to set timer, can't process request.");
|
||||
return -1;
|
||||
}
|
||||
|
@ -37,10 +37,13 @@ HRESULT
|
||||
SetShortcutProps(LPCWSTR aShortcutPath, LPCWSTR aAppModelID, bool aSetID, bool aSetMode)
|
||||
{
|
||||
HRESULT hres;
|
||||
::CoInitialize(NULL);
|
||||
::CoInitialize(nullptr);
|
||||
|
||||
IPropertyStore *m_pps = NULL;
|
||||
if (FAILED(hres = SHGetPropertyStoreFromParsingName(aShortcutPath, NULL, GPS_READWRITE, IID_PPV_ARGS(&m_pps)))) {
|
||||
IPropertyStore *m_pps = nullptr;
|
||||
if (FAILED(hres = SHGetPropertyStoreFromParsingName(aShortcutPath,
|
||||
nullptr,
|
||||
GPS_READWRITE,
|
||||
IID_PPV_ARGS(&m_pps)))) {
|
||||
printf("SHGetPropertyStoreFromParsingName failed\n");
|
||||
goto Exit;
|
||||
}
|
||||
@ -79,10 +82,13 @@ HRESULT
|
||||
PrintShortcutProps(LPCWSTR aTargetPath)
|
||||
{
|
||||
HRESULT hres;
|
||||
::CoInitialize(NULL);
|
||||
::CoInitialize(nullptr);
|
||||
|
||||
IPropertyStore *m_pps = NULL;
|
||||
if (FAILED(hres = SHGetPropertyStoreFromParsingName(aTargetPath, NULL, GPS_READWRITE, IID_PPV_ARGS(&m_pps)))) {
|
||||
IPropertyStore *m_pps = nullptr;
|
||||
if (FAILED(hres = SHGetPropertyStoreFromParsingName(aTargetPath,
|
||||
nullptr,
|
||||
GPS_READWRITE,
|
||||
IID_PPV_ARGS(&m_pps)))) {
|
||||
printf("SHGetPropertyStoreFromParsingName failed\n");
|
||||
goto Exit;
|
||||
}
|
||||
@ -125,9 +131,9 @@ CreateLink(LPCWSTR aTargetPath, LPCWSTR aShortcutPath, LPCWSTR aDescription)
|
||||
|
||||
wprintf(L"creating shortcut: '%s'\n", aShortcutPath);
|
||||
|
||||
CoInitialize(NULL);
|
||||
CoInitialize(nullptr);
|
||||
|
||||
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
|
||||
hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER,
|
||||
IID_IShellLink, (LPVOID*)&psl);
|
||||
if (FAILED(hres)) {
|
||||
CoUninitialize();
|
||||
@ -140,7 +146,7 @@ CreateLink(LPCWSTR aTargetPath, LPCWSTR aShortcutPath, LPCWSTR aDescription)
|
||||
psl->SetDescription(L"");
|
||||
}
|
||||
|
||||
IPersistFile* ppf = NULL;
|
||||
IPersistFile* ppf = nullptr;
|
||||
hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf);
|
||||
|
||||
if (SUCCEEDED(hres)) {
|
||||
@ -261,7 +267,9 @@ int wmain(int argc, WCHAR* argv[])
|
||||
}
|
||||
|
||||
if (createShortcutFound) {
|
||||
if (FAILED(hres = CreateLink(targetPathStr, shortcutPathStr, (descriptionFound ? descriptionStr : NULL)))) {
|
||||
if (FAILED(hres = CreateLink(targetPathStr,
|
||||
shortcutPathStr,
|
||||
(descriptionFound ? descriptionStr : nullptr)))) {
|
||||
printf("failed creating shortcut HRESULT=%X\n", hres);
|
||||
return -1;
|
||||
}
|
||||
@ -275,7 +283,9 @@ int wmain(int argc, WCHAR* argv[])
|
||||
}
|
||||
|
||||
if (appModelIDFound || modeFound) {
|
||||
if (FAILED(hres = SetShortcutProps(target, (appModelIDFound ? appModelIDStr : NULL), appModelIDFound, modeFound))) {
|
||||
if (FAILED(hres = SetShortcutProps(target,
|
||||
(appModelIDFound ? appModelIDStr : nullptr),
|
||||
appModelIDFound, modeFound))) {
|
||||
printf("failed adding property HRESULT=%X\n", hres);
|
||||
return -1;
|
||||
}
|
||||
|
@ -50,7 +50,7 @@ CString sFirefoxPath;
|
||||
|
||||
static void Log(const wchar_t *fmt, ...)
|
||||
{
|
||||
va_list a = NULL;
|
||||
va_list a = nullptr;
|
||||
wchar_t szDebugString[1024];
|
||||
if(!lstrlenW(fmt))
|
||||
return;
|
||||
@ -66,7 +66,7 @@ static void Log(const wchar_t *fmt, ...)
|
||||
|
||||
static void Fail(bool aRequestRetry, const wchar_t *fmt, ...)
|
||||
{
|
||||
va_list a = NULL;
|
||||
va_list a = nullptr;
|
||||
wchar_t szDebugString[1024];
|
||||
if(!lstrlenW(fmt))
|
||||
return;
|
||||
@ -93,7 +93,7 @@ static bool GetModulePath(CStringW& aPathBuffer)
|
||||
WCHAR buffer[MAX_PATH];
|
||||
memset(buffer, 0, sizeof(buffer));
|
||||
|
||||
if (!GetModuleFileName(NULL, buffer, MAX_PATH)) {
|
||||
if (!GetModuleFileName(nullptr, buffer, MAX_PATH)) {
|
||||
Fail(false, L"GetModuleFileName failed.");
|
||||
return false;
|
||||
}
|
||||
@ -145,7 +145,7 @@ static bool GetDefaultBrowserAppModelID(WCHAR* aIDBuffer,
|
||||
}
|
||||
DWORD len = aCharLength * sizeof(WCHAR);
|
||||
memset(aIDBuffer, 0, len);
|
||||
if (RegQueryValueExW(key, L"AppUserModelID", NULL, NULL,
|
||||
if (RegQueryValueExW(key, L"AppUserModelID", nullptr, nullptr,
|
||||
(LPBYTE)aIDBuffer, &len) != ERROR_SUCCESS || !len) {
|
||||
RegCloseKey(key);
|
||||
return false;
|
||||
@ -174,7 +174,7 @@ static bool SetupTestOutputPipe()
|
||||
SECURITY_ATTRIBUTES saAttr;
|
||||
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
||||
saAttr.bInheritHandle = TRUE;
|
||||
saAttr.lpSecurityDescriptor = NULL;
|
||||
saAttr.lpSecurityDescriptor = nullptr;
|
||||
|
||||
gTestOutputPipe =
|
||||
CreateNamedPipeW(L"\\\\.\\pipe\\metrotestharness",
|
||||
@ -182,7 +182,7 @@ static bool SetupTestOutputPipe()
|
||||
PIPE_TYPE_BYTE|PIPE_WAIT,
|
||||
1,
|
||||
PIPE_BUFFER_SIZE,
|
||||
PIPE_BUFFER_SIZE, 0, NULL);
|
||||
PIPE_BUFFER_SIZE, 0, nullptr);
|
||||
|
||||
if (gTestOutputPipe == INVALID_HANDLE_VALUE) {
|
||||
Log(L"Failed to create named logging pipe.");
|
||||
@ -194,7 +194,8 @@ static bool SetupTestOutputPipe()
|
||||
static void ReadPipe()
|
||||
{
|
||||
DWORD numBytesRead;
|
||||
while (ReadFile(gTestOutputPipe, buffer, PIPE_BUFFER_SIZE, &numBytesRead, NULL) &&
|
||||
while (ReadFile(gTestOutputPipe, buffer, PIPE_BUFFER_SIZE,
|
||||
&numBytesRead, nullptr) &&
|
||||
numBytesRead) {
|
||||
buffer[numBytesRead] = '\0';
|
||||
printf("%s", buffer);
|
||||
@ -209,7 +210,7 @@ static int Launch()
|
||||
|
||||
// The interface that allows us to activate the browser
|
||||
CComPtr<IApplicationActivationManager> activateMgr;
|
||||
if (FAILED(CoCreateInstance(CLSID_ApplicationActivationManager, NULL,
|
||||
if (FAILED(CoCreateInstance(CLSID_ApplicationActivationManager, nullptr,
|
||||
CLSCTX_LOCAL_SERVER,
|
||||
IID_IApplicationActivationManager,
|
||||
(void**)&activateMgr))) {
|
||||
@ -229,7 +230,7 @@ static int Launch()
|
||||
// Hand off focus rights if the terminal has focus to the out-of-process
|
||||
// activation server (explorer.exe). Without this the metro interface
|
||||
// won't launch.
|
||||
hr = CoAllowSetForegroundWindow(activateMgr, NULL);
|
||||
hr = CoAllowSetForegroundWindow(activateMgr, nullptr);
|
||||
if (FAILED(hr)) {
|
||||
// Log but don't fail. This has happened on vms with certain terminals run by
|
||||
// QA during mozmill testing.
|
||||
@ -264,7 +265,7 @@ static int Launch()
|
||||
} else {
|
||||
// Use the module path
|
||||
char path[MAX_PATH];
|
||||
if (!GetModuleFileNameA(NULL, path, MAX_PATH)) {
|
||||
if (!GetModuleFileNameA(nullptr, path, MAX_PATH)) {
|
||||
Fail(false, L"GetModuleFileNameA errorno=%d", GetLastError());
|
||||
return FAILURE;
|
||||
}
|
||||
@ -289,9 +290,9 @@ static int Launch()
|
||||
|
||||
Log(L"Writing out tests.ini to: '%s'", CStringW(testFilePath));
|
||||
HANDLE hTestFile = CreateFileA(testFilePath, GENERIC_WRITE,
|
||||
0, NULL, CREATE_ALWAYS,
|
||||
0, nullptr, CREATE_ALWAYS,
|
||||
FILE_ATTRIBUTE_NORMAL,
|
||||
NULL);
|
||||
nullptr);
|
||||
if (hTestFile == INVALID_HANDLE_VALUE) {
|
||||
Fail(false, L"CreateFileA errorno=%d", GetLastError());
|
||||
return FAILURE;
|
||||
@ -306,7 +307,8 @@ static int Launch()
|
||||
asciiParams += sAppParams;
|
||||
asciiParams.Trim();
|
||||
Log(L"Browser command line args: '%s'", CString(asciiParams));
|
||||
if (!WriteFile(hTestFile, asciiParams, asciiParams.GetLength(), NULL, 0)) {
|
||||
if (!WriteFile(hTestFile, asciiParams, asciiParams.GetLength(),
|
||||
nullptr, 0)) {
|
||||
CloseHandle(hTestFile);
|
||||
Fail(false, L"WriteFile errorno=%d", GetLastError());
|
||||
return FAILURE;
|
||||
@ -347,7 +349,7 @@ static int Launch()
|
||||
} else if (waitResult == WAIT_OBJECT_0 + 1) {
|
||||
ReadPipe();
|
||||
} else if (waitResult == WAIT_OBJECT_0 + 2 &&
|
||||
PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
|
||||
PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
@ -363,7 +365,7 @@ static int Launch()
|
||||
|
||||
int wmain(int argc, WCHAR* argv[])
|
||||
{
|
||||
CoInitialize(NULL);
|
||||
CoInitialize(nullptr);
|
||||
|
||||
int idx;
|
||||
bool firefoxParam = false;
|
||||
|
@ -91,6 +91,11 @@ this.Social = {
|
||||
providers: [],
|
||||
_disabledForSafeMode: false,
|
||||
|
||||
get allowMultipleWorkers() {
|
||||
return Services.prefs.prefHasUserValue("social.allowMultipleWorkers") &&
|
||||
Services.prefs.getBoolPref("social.allowMultipleWorkers");
|
||||
},
|
||||
|
||||
get _currentProviderPref() {
|
||||
try {
|
||||
return Services.prefs.getComplexValue("social.provider.current",
|
||||
@ -114,15 +119,14 @@ this.Social = {
|
||||
this._setProvider(val);
|
||||
},
|
||||
|
||||
// Sets the current provider and enables it. Also disables the
|
||||
// previously set provider, and notifies observers of the change.
|
||||
// Sets the current provider and notifies observers of the change.
|
||||
_setProvider: function (provider) {
|
||||
if (this._provider == provider)
|
||||
return;
|
||||
|
||||
// Disable the previous provider, if any, since we want only one provider to
|
||||
// be enabled at once.
|
||||
if (this._provider)
|
||||
// Disable the previous provider, if we are not allowing multiple workers,
|
||||
// since we want only one provider to be enabled at once.
|
||||
if (this._provider && !Social.allowMultipleWorkers)
|
||||
this._provider.enabled = false;
|
||||
|
||||
this._provider = provider;
|
||||
@ -134,7 +138,6 @@ this.Social = {
|
||||
let enabled = !!provider;
|
||||
if (enabled != SocialService.enabled) {
|
||||
SocialService.enabled = enabled;
|
||||
Services.prefs.setBoolPref("social.enabled", enabled);
|
||||
}
|
||||
|
||||
let origin = this._provider && this._provider.origin;
|
||||
@ -159,31 +162,40 @@ this.Social = {
|
||||
if (SocialService.enabled) {
|
||||
// Retrieve the current set of providers, and set the current provider.
|
||||
SocialService.getOrderedProviderList(function (providers) {
|
||||
this._updateProviderCache(providers);
|
||||
}.bind(this));
|
||||
Social._updateProviderCache(providers);
|
||||
Social._updateWorkerState(true);
|
||||
});
|
||||
}
|
||||
|
||||
// Register an observer for changes to the provider list
|
||||
SocialService.registerProviderListener(function providerListener(topic, data) {
|
||||
// An engine change caused by adding/removing a provider should notify
|
||||
// An engine change caused by adding/removing a provider should notify.
|
||||
// any providers we receive are enabled in the AddonsManager
|
||||
if (topic == "provider-added" || topic == "provider-removed") {
|
||||
this._updateProviderCache(data);
|
||||
Social._updateProviderCache(data);
|
||||
Social._updateWorkerState(true);
|
||||
Services.obs.notifyObservers(null, "social:providers-changed", null);
|
||||
return;
|
||||
}
|
||||
if (topic == "provider-update") {
|
||||
// a provider has self-updated its manifest, we need to update our
|
||||
// cache and possibly reload if it was the current provider.
|
||||
// a provider has self-updated its manifest, we need to update our cache
|
||||
// and reload the provider.
|
||||
let provider = data;
|
||||
// if we need a reload, do it now
|
||||
if (provider.enabled) {
|
||||
Social.enabled = false;
|
||||
Services.tm.mainThread.dispatch(function() {
|
||||
Social.enabled = true;
|
||||
}, Components.interfaces.nsIThread.DISPATCH_NORMAL);
|
||||
}
|
||||
SocialService.getOrderedProviderList(function(providers) {
|
||||
Social._updateProviderCache(providers);
|
||||
provider.reload();
|
||||
Services.obs.notifyObservers(null, "social:providers-changed", null);
|
||||
});
|
||||
}
|
||||
}.bind(this));
|
||||
});
|
||||
},
|
||||
|
||||
_updateWorkerState: function(enable) {
|
||||
// ensure that our providers are all disabled, and enabled if we allow
|
||||
// multiple workers
|
||||
if (enable && !Social.allowMultipleWorkers)
|
||||
return;
|
||||
[p.enabled = enable for (p of Social.providers) if (p.enabled != enable)];
|
||||
},
|
||||
|
||||
// Called to update our cache of providers and set the current provider
|
||||
@ -203,6 +215,9 @@ this.Social = {
|
||||
set enabled(val) {
|
||||
// Setting .enabled is just a shortcut for setting the provider to either
|
||||
// the default provider or null...
|
||||
|
||||
this._updateWorkerState(val);
|
||||
|
||||
if (val) {
|
||||
if (!this.provider)
|
||||
this.provider = this.defaultProvider;
|
||||
@ -210,6 +225,7 @@ this.Social = {
|
||||
this.provider = null;
|
||||
}
|
||||
},
|
||||
|
||||
get enabled() {
|
||||
return this.provider != null;
|
||||
},
|
||||
@ -229,10 +245,6 @@ this.Social = {
|
||||
Services.prefs.setBoolPref("social.toast-notifications.enabled", !prefValue);
|
||||
},
|
||||
|
||||
haveLoggedInUser: function () {
|
||||
return !!(this.provider && this.provider.profile && this.provider.profile.userName);
|
||||
},
|
||||
|
||||
setProviderByOrigin: function (origin) {
|
||||
this.provider = this._getProviderFromOrigin(origin);
|
||||
},
|
||||
|
@ -2076,7 +2076,10 @@ ia64*-hpux*)
|
||||
XPCOM_FROZEN_LDOPTS='$(LIBXUL_DIST)/lib/xul.lib $(LIBXUL_DIST)/lib/mozalloc.lib'
|
||||
LIBXUL_LIBS='$(LIBXUL_DIST)/lib/xul.lib $(LIBXUL_DIST)/lib/mozalloc.lib'
|
||||
MOZ_COMPONENT_NSPR_LIBS='$(NSPR_LIBS)'
|
||||
LDFLAGS="$LDFLAGS -LARGEADDRESSAWARE -NXCOMPAT -RELEASE"
|
||||
LDFLAGS="$LDFLAGS -LARGEADDRESSAWARE -NXCOMPAT"
|
||||
if test -z "$DEVELOPER_OPTIONS"; then
|
||||
LDFLAGS="$LDFLAGS -RELEASE"
|
||||
fi
|
||||
dnl For profile-guided optimization
|
||||
PROFILE_GEN_CFLAGS="-GL"
|
||||
PROFILE_GEN_LDFLAGS="-LTCG:PGINSTRUMENT"
|
||||
|
@ -1808,7 +1808,7 @@ nsINode::ReplaceOrInsertBefore(bool aReplace, nsINode* aNewChild,
|
||||
}
|
||||
|
||||
// Verify that newContent has no parent.
|
||||
if (newContent->GetParent()) {
|
||||
if (newContent->GetParentNode()) {
|
||||
aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
@ -1885,7 +1885,7 @@ nsINode::ReplaceOrInsertBefore(bool aReplace, nsINode* aNewChild,
|
||||
|
||||
// Verify that all the things in fragChildren have no parent.
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
if (fragChildren.ref().ElementAt(i)->GetParent()) {
|
||||
if (fragChildren.ref().ElementAt(i)->GetParentNode()) {
|
||||
aError.Throw(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -2643,12 +2643,6 @@ nsEventStateManager::ComputeScrollTarget(nsIFrame* aTargetFrame,
|
||||
nsIScrollableFrame* frameToScroll =
|
||||
lastScrollFrame->GetScrollTargetFrame();
|
||||
if (frameToScroll) {
|
||||
nsIFrame* activeRootFrame = nsLayoutUtils::GetActiveScrolledRootFor(
|
||||
lastScrollFrame, nullptr);
|
||||
if (!nsLayoutUtils::GetCrossDocParentFrame(activeRootFrame)) {
|
||||
// Record the fact that the scroll occurred on the top-level page.
|
||||
aEvent->viewPortIsScrollTargetParent = true;
|
||||
}
|
||||
return frameToScroll;
|
||||
}
|
||||
}
|
||||
@ -2714,14 +2708,7 @@ nsEventStateManager::ComputeScrollTarget(nsIFrame* aTargetFrame,
|
||||
aTargetFrame->PresContext()->FrameManager()->GetRootFrame());
|
||||
aOptions =
|
||||
static_cast<ComputeScrollTargetOptions>(aOptions & ~START_FROM_PARENT);
|
||||
if (newFrame) {
|
||||
return ComputeScrollTarget(newFrame, aEvent, aOptions);
|
||||
}
|
||||
|
||||
// Record the fact that the scroll occurred past the bounds of the top-level
|
||||
// page.
|
||||
aEvent->viewPortIsScrollTargetParent = true;
|
||||
return nullptr;
|
||||
return newFrame ? ComputeScrollTarget(newFrame, aEvent, aOptions) : nullptr;
|
||||
}
|
||||
|
||||
nsSize
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "nsMappedAttributes.h"
|
||||
#include "nsSize.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIDOMMutationEvent.h"
|
||||
#include "nsIScriptContext.h"
|
||||
#include "nsIURL.h"
|
||||
#include "nsIIOService.h"
|
||||
@ -251,6 +252,11 @@ HTMLImageElement::GetAttributeChangeHint(const nsIAtom* aAttribute,
|
||||
if (aAttribute == nsGkAtoms::usemap ||
|
||||
aAttribute == nsGkAtoms::ismap) {
|
||||
NS_UpdateHint(retval, NS_STYLE_HINT_FRAMECHANGE);
|
||||
} else if (aAttribute == nsGkAtoms::alt) {
|
||||
if (aModType == nsIDOMMutationEvent::ADDITION ||
|
||||
aModType == nsIDOMMutationEvent::REMOVAL) {
|
||||
NS_UpdateHint(retval, NS_STYLE_HINT_FRAMECHANGE);
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
@ -125,6 +125,7 @@ MOCHITEST_FILES = \
|
||||
test_bug448534.html \
|
||||
test_bug463162.xhtml \
|
||||
test_decoder_disable.html \
|
||||
test_mediarecorder_record_no_timeslice.html \
|
||||
test_mediarecorder_reload_crash.html \
|
||||
test_media_selection.html \
|
||||
test_playback.html \
|
||||
|
117
content/media/test/test_mediarecorder_record_no_timeslice.html
Normal file
117
content/media/test/test_mediarecorder_record_no_timeslice.html
Normal 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>
|
@ -1694,6 +1694,16 @@ nsDOMWindowUtils::FindElementWithViewId(nsViewID aID,
|
||||
return content ? CallQueryInterface(content, aResult) : NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::GetViewId(nsIDOMElement* aElement, nsViewID* aResult)
|
||||
{
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
|
||||
if (content && nsLayoutUtils::FindIDFor(content, aResult)) {
|
||||
return NS_OK;
|
||||
}
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::GetScreenPixelsPerCSSPixel(float* aScreenPixels)
|
||||
{
|
||||
|
@ -108,10 +108,6 @@ EXPORTS_GENERATED_DEST := $(DIST)/include/$(binding_include_path)
|
||||
EXPORTS_GENERATED_TARGET := export
|
||||
INSTALL_TARGETS += EXPORTS_GENERATED
|
||||
|
||||
ifdef GNU_CC
|
||||
CXXFLAGS += -Wno-uninitialized
|
||||
endif
|
||||
|
||||
# Install auto-generated GlobalGen files. The rules for the install must
|
||||
# be in the same target/subtier as GlobalGen.py, otherwise the files will not
|
||||
# get installed into the appropriate location as they are generated.
|
||||
@ -128,6 +124,10 @@ INSTALL_TARGETS += globalgen_headers
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
include $(topsrcdir)/ipc/chromium/chromium-config.mk
|
||||
|
||||
ifdef GNU_CC
|
||||
CXXFLAGS += -Wno-uninitialized
|
||||
endif
|
||||
|
||||
# If you change bindinggen_dependencies here, change it in
|
||||
# dom/bindings/test/Makefile.in too.
|
||||
bindinggen_dependencies := \
|
||||
|
@ -42,7 +42,7 @@ interface nsIURI;
|
||||
interface nsIDOMEventTarget;
|
||||
interface nsIRunnable;
|
||||
|
||||
[scriptable, uuid(ff1cec22-b183-40d3-8b42-b81a2f0ba4e6)]
|
||||
[scriptable, uuid(d6e733ef-492b-4e67-b723-28571c2959f0)]
|
||||
interface nsIDOMWindowUtils : nsISupports {
|
||||
|
||||
/**
|
||||
@ -1177,6 +1177,12 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||
*/
|
||||
nsIDOMElement findElementWithViewId(in nsViewID aId);
|
||||
|
||||
/**
|
||||
* Find the view ID for a given element. This is the reverse of
|
||||
* findElementWithViewId().
|
||||
*/
|
||||
nsViewID getViewId(in nsIDOMElement aElement);
|
||||
|
||||
/**
|
||||
* Checks the layer tree for this window and returns true
|
||||
* if all layers have transforms that are translations by integers,
|
||||
|
@ -129,6 +129,10 @@ expandAction = expanded
|
||||
activateAction = activated
|
||||
cycleAction = cycled
|
||||
|
||||
# Live regions
|
||||
# 'hidden' will be spoken when something disappears in a live region.
|
||||
hidden = hidden
|
||||
|
||||
# Tab states
|
||||
tabLoading = loading
|
||||
tabLoaded = loaded
|
||||
|
@ -1147,30 +1147,6 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetri
|
||||
bool isDefault = mFrameMetrics.IsDefault();
|
||||
mFrameMetrics.mMayHaveTouchListeners = aLayerMetrics.mMayHaveTouchListeners;
|
||||
|
||||
// TODO: Once a mechanism for calling UpdateScrollOffset() when content does
|
||||
// a scrollTo() is implemented for metro (bug 898580), this block can be removed.
|
||||
#ifdef MOZ_METRO
|
||||
if (!mPaintThrottler.IsOutstanding()) {
|
||||
// No paint was requested, but we got one anyways. One possible cause of this
|
||||
// is that content could have fired a scrollTo(). In this case, we should take
|
||||
// the new scroll offset. Document/viewport changes are handled elsewhere.
|
||||
// Also note that, since NotifyLayersUpdated() is called whenever there's a
|
||||
// layers update, we didn't necessarily get a new scroll offset, but we're
|
||||
// updating our local copy of it anyways just in case.
|
||||
switch (mState) {
|
||||
case NOTHING:
|
||||
case FLING:
|
||||
case TOUCHING:
|
||||
case WAITING_LISTENERS:
|
||||
mFrameMetrics.mScrollOffset = aLayerMetrics.mScrollOffset;
|
||||
break;
|
||||
// Don't clobber if we're in other states.
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
mPaintThrottler.TaskComplete(GetFrameTime());
|
||||
bool needContentRepaint = false;
|
||||
if (aLayerMetrics.mCompositionBounds.width == mFrameMetrics.mCompositionBounds.width &&
|
||||
|
@ -78,7 +78,7 @@ nsUTF16ToUnicodeBase::UTF16ConvertToUnicode(const char * aSrc,
|
||||
// previous run while the 2nd byte has to come from |*src|.
|
||||
mState = STATE_NORMAL;
|
||||
#ifdef IS_BIG_ENDIAN
|
||||
u = (mOddByte << 8) | *src++; // safe, we know we have at least one byte.
|
||||
u = (mOddByte << 8) | uint8_t(*src++); // safe, we know we have at least one byte.
|
||||
#else
|
||||
u = (*src++ << 8) | mOddByte; // safe, we know we have at least one byte.
|
||||
#endif
|
||||
|
@ -1629,7 +1629,10 @@ ia64*-hpux*)
|
||||
XPCOM_FROZEN_LDOPTS='$(LIBXUL_DIST)/lib/xpcom.lib $(LIBXUL_DIST)/lib/mozalloc.lib'
|
||||
LIBXUL_LIBS='$(LIBXUL_DIST)/lib/xpcom.lib $(LIBXUL_DIST)/lib/xul.lib $(LIBXUL_DIST)/lib/mozalloc.lib'
|
||||
MOZ_COMPONENT_NSPR_LIBS='$(NSPR_LIBS)'
|
||||
LDFLAGS="$LDFLAGS -LARGEADDRESSAWARE -NXCOMPAT -RELEASE"
|
||||
LDFLAGS="$LDFLAGS -LARGEADDRESSAWARE -NXCOMPAT"
|
||||
if test -z "$DEVELOPER_OPTIONS"; then
|
||||
LDFLAGS="$LDFLAGS -RELEASE"
|
||||
fi
|
||||
dnl For profile-guided optimization
|
||||
PROFILE_GEN_CFLAGS="-GL"
|
||||
PROFILE_GEN_LDFLAGS="-LTCG:PGINSTRUMENT"
|
||||
|
@ -69,7 +69,7 @@ CheckArgumentsWithinEval(JSContext *cx, Parser<FullParseHandler> &parser, Handle
|
||||
}
|
||||
|
||||
// It's an error to use |arguments| in a legacy generator expression.
|
||||
if (script->isGeneratorExp && script->isLegacyGenerator) {
|
||||
if (script->isGeneratorExp && script->isLegacyGenerator()) {
|
||||
parser.report(ParseError, false, NULL, JSMSG_BAD_GENEXP_BODY, js_arguments_str);
|
||||
return false;
|
||||
}
|
||||
@ -294,7 +294,8 @@ frontend::CompileScript(ExclusiveContext *cx, LifoAlloc *alloc, HandleObject sco
|
||||
*/
|
||||
JSFunction *fun = evalCaller->functionOrCallerFunction();
|
||||
Directives directives(/* strict = */ fun->strict());
|
||||
ObjectBox *funbox = parser.newFunctionBox(/* fn = */ NULL, fun, pc.addr(), directives);
|
||||
ObjectBox *funbox = parser.newFunctionBox(/* fn = */ NULL, fun, pc.addr(),
|
||||
directives, fun->generatorKind());
|
||||
if (!funbox)
|
||||
return NULL;
|
||||
bce.objectList.add(funbox);
|
||||
@ -414,7 +415,9 @@ frontend::CompileLazyFunction(JSContext *cx, LazyScript *lazy, const jschar *cha
|
||||
uint32_t staticLevel = lazy->staticLevel(cx);
|
||||
|
||||
Rooted<JSFunction*> fun(cx, lazy->function());
|
||||
ParseNode *pn = parser.standaloneLazyFunction(fun, staticLevel, lazy->strict());
|
||||
JS_ASSERT(!lazy->isLegacyGenerator());
|
||||
ParseNode *pn = parser.standaloneLazyFunction(fun, staticLevel, lazy->strict(),
|
||||
lazy->generatorKind());
|
||||
if (!pn)
|
||||
return false;
|
||||
|
||||
@ -518,7 +521,7 @@ frontend::CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileO
|
||||
ParseNode *fn;
|
||||
while (true) {
|
||||
Directives newDirectives = directives;
|
||||
fn = parser.standaloneFunctionBody(fun, formals, directives, &newDirectives);
|
||||
fn = parser.standaloneFunctionBody(fun, formals, NotGenerator, directives, &newDirectives);
|
||||
if (fn)
|
||||
break;
|
||||
|
||||
|
@ -384,9 +384,9 @@ Parser<FullParseHandler>::cloneParseTree(ParseNode *opn)
|
||||
if (pn->getKind() == PNK_MODULE) {
|
||||
MOZ_ASSUME_UNREACHABLE("module nodes cannot be cloned");
|
||||
}
|
||||
NULLCHECK(pn->pn_funbox =
|
||||
newFunctionBox(pn, opn->pn_funbox->function(), pc,
|
||||
Directives(/* strict = */ opn->pn_funbox->strict)));
|
||||
NULLCHECK(pn->pn_funbox = newFunctionBox(pn, opn->pn_funbox->function(), pc,
|
||||
Directives(/* strict = */ opn->pn_funbox->strict),
|
||||
opn->pn_funbox->generatorKind()));
|
||||
NULLCHECK(pn->pn_body = cloneParseTree(opn->pn_body));
|
||||
pn->pn_cookie = opn->pn_cookie;
|
||||
pn->pn_dflags = opn->pn_dflags;
|
||||
|
@ -56,7 +56,6 @@ namespace frontend {
|
||||
typedef Rooted<StaticBlockObject*> RootedStaticBlockObject;
|
||||
typedef Handle<StaticBlockObject*> HandleStaticBlockObject;
|
||||
|
||||
typedef MutableHandle<PropertyName*> MutableHandlePropertyName;
|
||||
|
||||
/*
|
||||
* Insist that the next token be of type tt, or report errno and return null.
|
||||
@ -466,13 +465,14 @@ Parser<ParseHandler>::newObjectBox(JSObject *obj)
|
||||
template <typename ParseHandler>
|
||||
FunctionBox::FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunction *fun,
|
||||
ParseContext<ParseHandler> *outerpc, Directives directives,
|
||||
bool extraWarnings)
|
||||
bool extraWarnings, GeneratorKind generatorKind)
|
||||
: ObjectBox(fun, traceListHead),
|
||||
SharedContext(cx, directives, extraWarnings),
|
||||
bindings(),
|
||||
bufStart(0),
|
||||
bufEnd(0),
|
||||
ndefaults(0),
|
||||
generatorKindBits_(GeneratorKindAsBits(generatorKind)),
|
||||
inWith(false), // initialized below
|
||||
inGenexpLambda(false),
|
||||
hasDestructuringArgs(false),
|
||||
@ -533,7 +533,7 @@ FunctionBox::FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunct
|
||||
template <typename ParseHandler>
|
||||
FunctionBox *
|
||||
Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction *fun, ParseContext<ParseHandler> *outerpc,
|
||||
Directives inheritedDirectives)
|
||||
Directives inheritedDirectives, GeneratorKind generatorKind)
|
||||
{
|
||||
JS_ASSERT(fun && !IsPoisonedPtr(fun));
|
||||
|
||||
@ -546,7 +546,8 @@ Parser<ParseHandler>::newFunctionBox(Node fn, JSFunction *fun, ParseContext<Pars
|
||||
*/
|
||||
FunctionBox *funbox =
|
||||
alloc.new_<FunctionBox>(context, traceListHead, fun, outerpc,
|
||||
inheritedDirectives, options().extraWarningsOption);
|
||||
inheritedDirectives, options().extraWarningsOption,
|
||||
generatorKind);
|
||||
if (!funbox) {
|
||||
js_ReportOutOfMemory(context);
|
||||
return NULL;
|
||||
@ -863,6 +864,7 @@ Parser<ParseHandler>::checkStrictBinding(PropertyName *name, Node pn)
|
||||
template <>
|
||||
ParseNode *
|
||||
Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun, const AutoNameVector &formals,
|
||||
GeneratorKind generatorKind,
|
||||
Directives inheritedDirectives,
|
||||
Directives *newDirectives)
|
||||
{
|
||||
@ -877,7 +879,8 @@ Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun, const AutoN
|
||||
argsbody->makeEmpty();
|
||||
fn->pn_body = argsbody;
|
||||
|
||||
FunctionBox *funbox = newFunctionBox(fn, fun, /* outerpc = */ NULL, inheritedDirectives);
|
||||
FunctionBox *funbox = newFunctionBox(fn, fun, /* outerpc = */ NULL, inheritedDirectives,
|
||||
generatorKind);
|
||||
if (!funbox)
|
||||
return null();
|
||||
handler.setFunctionBox(fn, funbox);
|
||||
@ -1062,7 +1065,9 @@ Parser<ParseHandler>::functionBody(FunctionSyntaxKind kind, FunctionBodyType typ
|
||||
JS_ASSERT(pc->sc->isFunctionBox());
|
||||
JS_ASSERT(!pc->funHasReturnExpr && !pc->funHasReturnVoid);
|
||||
|
||||
#ifdef DEBUG
|
||||
uint32_t startYieldOffset = pc->lastYieldOffset;
|
||||
#endif
|
||||
|
||||
Node pn;
|
||||
if (type == StatementListBody) {
|
||||
@ -1082,8 +1087,14 @@ Parser<ParseHandler>::functionBody(FunctionSyntaxKind kind, FunctionBodyType typ
|
||||
return null();
|
||||
}
|
||||
|
||||
if (pc->lastYieldOffset != startYieldOffset) {
|
||||
JS_ASSERT(pc->isLegacyGenerator());
|
||||
switch (pc->generatorKind()) {
|
||||
case NotGenerator:
|
||||
JS_ASSERT(pc->lastYieldOffset == startYieldOffset);
|
||||
break;
|
||||
|
||||
case LegacyGenerator:
|
||||
// FIXME: Catch these errors eagerly, in yieldExpression().
|
||||
JS_ASSERT(pc->lastYieldOffset != startYieldOffset);
|
||||
if (kind == Arrow) {
|
||||
reportWithOffset(ParseError, false, pc->lastYieldOffset,
|
||||
JSMSG_YIELD_IN_ARROW, js_yield_str);
|
||||
@ -1095,9 +1106,12 @@ Parser<ParseHandler>::functionBody(FunctionSyntaxKind kind, FunctionBodyType typ
|
||||
JSMSG_BAD_ANON_GENERATOR_RETURN);
|
||||
return null();
|
||||
}
|
||||
pc->sc->asFunctionBox()->setIsLegacyGenerator();
|
||||
} else {
|
||||
JS_ASSERT(!pc->isLegacyGenerator());
|
||||
break;
|
||||
|
||||
case StarGenerator:
|
||||
JS_ASSERT(kind != Arrow);
|
||||
JS_ASSERT(type == StatementListBody);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Check for falling off the end of a function that returns a value. */
|
||||
@ -1627,6 +1641,11 @@ Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node *listp, No
|
||||
}
|
||||
#endif /* JS_HAS_DESTRUCTURING */
|
||||
|
||||
case TOK_YIELD:
|
||||
if (!checkYieldNameValidity(JSMSG_MISSING_FORMAL))
|
||||
return false;
|
||||
goto TOK_NAME;
|
||||
|
||||
case TOK_TRIPLEDOT:
|
||||
{
|
||||
hasRest = true;
|
||||
@ -1636,15 +1655,16 @@ Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node *listp, No
|
||||
report(ParseError, false, null(), JSMSG_NO_REST_NAME);
|
||||
return false;
|
||||
}
|
||||
/* Fall through */
|
||||
goto TOK_NAME;
|
||||
}
|
||||
|
||||
TOK_NAME:
|
||||
case TOK_NAME:
|
||||
{
|
||||
if (parenFreeArrow)
|
||||
funbox->setStart(tokenStream);
|
||||
|
||||
RootedPropertyName name(context, tokenStream.currentToken().name());
|
||||
RootedPropertyName name(context, tokenStream.currentName());
|
||||
bool disallowDuplicateArgs = funbox->hasDestructuringArgs || hasDefaults;
|
||||
if (!defineArg(funcpn, name, disallowDuplicateArgs, &duplicatedArg))
|
||||
return false;
|
||||
@ -1695,6 +1715,20 @@ Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node *listp, No
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::checkFunctionName(HandlePropertyName funName)
|
||||
{
|
||||
if (pc->isStarGenerator() && funName == context->names().yield) {
|
||||
// The name of a named function expression is specified to be bound in
|
||||
// the outer context as if via "let". In an ES6 generator, "yield" is
|
||||
// not a valid name for a let-bound variable.
|
||||
report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <>
|
||||
bool
|
||||
Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
||||
@ -1707,6 +1741,9 @@ Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
||||
/* Function statements add a binding to the enclosing scope. */
|
||||
bool bodyLevel = pc->atBodyLevel();
|
||||
|
||||
if (!checkFunctionName(funName))
|
||||
return false;
|
||||
|
||||
if (kind == Statement) {
|
||||
/*
|
||||
* Handle redeclaration and optimize cases where we can statically bind the
|
||||
@ -1820,7 +1857,9 @@ Parser<FullParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
||||
// so we can skip over them after accounting for their free variables.
|
||||
if (LazyScript *lazyOuter = handler.lazyOuterFunction()) {
|
||||
JSFunction *fun = handler.nextLazyInnerFunction();
|
||||
FunctionBox *funbox = newFunctionBox(pn, fun, pc, Directives(/* strict = */ false));
|
||||
JS_ASSERT(!fun->isLegacyGenerator());
|
||||
FunctionBox *funbox = newFunctionBox(pn, fun, pc, Directives(/* strict = */ false),
|
||||
fun->generatorKind());
|
||||
if (!funbox)
|
||||
return false;
|
||||
|
||||
@ -1896,6 +1935,9 @@ Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
||||
/* Function statements add a binding to the enclosing scope. */
|
||||
bool bodyLevel = pc->atBodyLevel();
|
||||
|
||||
if (!checkFunctionName(funName))
|
||||
return false;
|
||||
|
||||
if (kind == Statement) {
|
||||
/*
|
||||
* Handle redeclaration and optimize cases where we can statically bind the
|
||||
@ -1934,7 +1976,8 @@ Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream::Position &start,
|
||||
FunctionType type, FunctionSyntaxKind kind)
|
||||
FunctionType type, FunctionSyntaxKind kind,
|
||||
GeneratorKind generatorKind)
|
||||
{
|
||||
JS_ASSERT_IF(kind == Statement, funName);
|
||||
|
||||
@ -1962,7 +2005,7 @@ Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream:
|
||||
Directives newDirectives = directives;
|
||||
|
||||
while (true) {
|
||||
if (functionArgsAndBody(pn, fun, type, kind, directives, &newDirectives))
|
||||
if (functionArgsAndBody(pn, fun, type, kind, generatorKind, directives, &newDirectives))
|
||||
break;
|
||||
if (tokenStream.hadError() || directives == newDirectives)
|
||||
return null();
|
||||
@ -2066,6 +2109,7 @@ Parser<SyntaxParseHandler>::finishFunctionDefinition(Node pn, FunctionBox *funbo
|
||||
|
||||
if (pc->sc->strict)
|
||||
lazy->setStrict();
|
||||
lazy->setGeneratorKind(funbox->generatorKind());
|
||||
if (funbox->usesArguments && funbox->usesApply)
|
||||
lazy->setUsesArgumentsAndApply();
|
||||
PropagateTransitiveParseFlags(funbox, lazy);
|
||||
@ -2078,13 +2122,14 @@ template <>
|
||||
bool
|
||||
Parser<FullParseHandler>::functionArgsAndBody(ParseNode *pn, HandleFunction fun,
|
||||
FunctionType type, FunctionSyntaxKind kind,
|
||||
GeneratorKind generatorKind,
|
||||
Directives inheritedDirectives,
|
||||
Directives *newDirectives)
|
||||
{
|
||||
ParseContext<FullParseHandler> *outerpc = pc;
|
||||
|
||||
// Create box for fun->object early to protect against last-ditch GC.
|
||||
FunctionBox *funbox = newFunctionBox(pn, fun, pc, inheritedDirectives);
|
||||
FunctionBox *funbox = newFunctionBox(pn, fun, pc, inheritedDirectives, generatorKind);
|
||||
if (!funbox)
|
||||
return false;
|
||||
|
||||
@ -2160,13 +2205,14 @@ template <>
|
||||
bool
|
||||
Parser<SyntaxParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun,
|
||||
FunctionType type, FunctionSyntaxKind kind,
|
||||
GeneratorKind generatorKind,
|
||||
Directives inheritedDirectives,
|
||||
Directives *newDirectives)
|
||||
{
|
||||
ParseContext<SyntaxParseHandler> *outerpc = pc;
|
||||
|
||||
// Create box for fun->object early to protect against last-ditch GC.
|
||||
FunctionBox *funbox = newFunctionBox(pn, fun, pc, inheritedDirectives);
|
||||
FunctionBox *funbox = newFunctionBox(pn, fun, pc, inheritedDirectives, generatorKind);
|
||||
if (!funbox)
|
||||
return false;
|
||||
|
||||
@ -2192,14 +2238,15 @@ Parser<SyntaxParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun,
|
||||
template <>
|
||||
ParseNode *
|
||||
Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, unsigned staticLevel,
|
||||
bool strict)
|
||||
bool strict, GeneratorKind generatorKind)
|
||||
{
|
||||
Node pn = handler.newFunctionDefinition();
|
||||
if (!pn)
|
||||
return null();
|
||||
|
||||
Directives directives(/* strict = */ strict);
|
||||
FunctionBox *funbox = newFunctionBox(pn, fun, /* outerpc = */ NULL, directives);
|
||||
FunctionBox *funbox = newFunctionBox(pn, fun, /* outerpc = */ NULL, directives,
|
||||
generatorKind);
|
||||
if (!funbox)
|
||||
return null();
|
||||
|
||||
@ -2273,6 +2320,10 @@ Parser<ParseHandler>::functionArgsAndBodyGeneric(Node pn, HandleFunction fun, Fu
|
||||
// Parse the function body.
|
||||
FunctionBodyType bodyType = StatementListBody;
|
||||
if (tokenStream.getToken(TokenStream::Operand) != TOK_LC) {
|
||||
if (funbox->isStarGenerator()) {
|
||||
report(ParseError, false, null(), JSMSG_CURLY_BEFORE_BODY);
|
||||
return false;
|
||||
}
|
||||
tokenStream.ungetToken();
|
||||
bodyType = ExpressionBody;
|
||||
fun->setIsExprClosure();
|
||||
@ -2310,7 +2361,7 @@ template <>
|
||||
ParseNode *
|
||||
Parser<FullParseHandler>::moduleDecl()
|
||||
{
|
||||
JS_ASSERT(tokenStream.currentToken().name() == context->names().module);
|
||||
JS_ASSERT(tokenStream.currentName() == context->names().module);
|
||||
if (!((pc->sc->isGlobalSharedContext() || pc->sc->isModuleBox()) && pc->atBodyLevel()))
|
||||
{
|
||||
report(ParseError, false, NULL, JSMSG_MODULE_STATEMENT);
|
||||
@ -2352,18 +2403,44 @@ Parser<SyntaxParseHandler>::moduleDecl()
|
||||
return SyntaxParseHandler::NodeFailure;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::checkYieldNameValidity(unsigned errorNumber)
|
||||
{
|
||||
// In star generators and in JS >= 1.7, yield is a keyword.
|
||||
if (pc->isStarGenerator() || versionNumber() >= JSVERSION_1_7) {
|
||||
report(ParseError, false, null(), errorNumber);
|
||||
return false;
|
||||
}
|
||||
// Otherwise in strict mode, yield is a future reserved word.
|
||||
if (pc->sc->strict) {
|
||||
report(ParseError, false, null(), JSMSG_RESERVED_ID, "yield");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::functionStmt()
|
||||
{
|
||||
JS_ASSERT(tokenStream.currentToken().type == TOK_FUNCTION);
|
||||
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
|
||||
|
||||
TokenStream::Position start(keepAtoms);
|
||||
tokenStream.tell(&start);
|
||||
|
||||
RootedPropertyName name(context);
|
||||
if (tokenStream.getToken(TokenStream::KeywordIsName) == TOK_NAME) {
|
||||
name = tokenStream.currentToken().name();
|
||||
GeneratorKind generatorKind = NotGenerator;
|
||||
TokenKind tt = tokenStream.getToken(TokenStream::KeywordIsName);
|
||||
|
||||
if (tt == TOK_MUL) {
|
||||
tokenStream.tell(&start);
|
||||
tt = tokenStream.getToken(TokenStream::KeywordIsName);
|
||||
generatorKind = StarGenerator;
|
||||
}
|
||||
|
||||
if (tt == TOK_NAME) {
|
||||
name = tokenStream.currentName();
|
||||
} else {
|
||||
/* Unnamed function expressions are forbidden in statement context. */
|
||||
report(ParseError, false, null(), JSMSG_UNNAMED_FUNCTION_STMT);
|
||||
@ -2375,22 +2452,34 @@ Parser<ParseHandler>::functionStmt()
|
||||
!report(ParseStrictError, pc->sc->strict, null(), JSMSG_STRICT_FUNCTION_STATEMENT))
|
||||
return null();
|
||||
|
||||
return functionDef(name, start, Normal, Statement);
|
||||
return functionDef(name, start, Normal, Statement, generatorKind);
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::functionExpr()
|
||||
{
|
||||
RootedPropertyName name(context);
|
||||
JS_ASSERT(tokenStream.currentToken().type == TOK_FUNCTION);
|
||||
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FUNCTION));
|
||||
|
||||
TokenStream::Position start(keepAtoms);
|
||||
tokenStream.tell(&start);
|
||||
if (tokenStream.getToken(TokenStream::KeywordIsName) == TOK_NAME)
|
||||
name = tokenStream.currentToken().name();
|
||||
|
||||
RootedPropertyName name(context);
|
||||
GeneratorKind generatorKind = NotGenerator;
|
||||
TokenKind tt = tokenStream.getToken(TokenStream::KeywordIsName);
|
||||
|
||||
if (tt == TOK_MUL) {
|
||||
tokenStream.tell(&start);
|
||||
tt = tokenStream.getToken(TokenStream::KeywordIsName);
|
||||
generatorKind = StarGenerator;
|
||||
}
|
||||
|
||||
if (tt == TOK_NAME)
|
||||
name = tokenStream.currentName();
|
||||
else
|
||||
tokenStream.ungetToken();
|
||||
return functionDef(name, start, Normal, Expression);
|
||||
|
||||
return functionDef(name, start, Normal, Expression, generatorKind);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -2524,7 +2613,7 @@ Parser<ParseHandler>::maybeParseDirective(Node list, Node pn, bool *cont)
|
||||
}
|
||||
}
|
||||
} else if (directive == context->names().useAsm) {
|
||||
if (pc->sc->isFunctionBox())
|
||||
if (pc->sc->isFunctionBox() && !pc->isGenerator())
|
||||
return asmJS(list);
|
||||
return report(ParseWarning, false, pn, JSMSG_USE_ASM_DIRECTIVE_FAIL);
|
||||
}
|
||||
@ -2606,15 +2695,21 @@ Parser<ParseHandler>::condition()
|
||||
return pn;
|
||||
}
|
||||
|
||||
static bool
|
||||
MatchLabel(TokenStream &ts, MutableHandlePropertyName label)
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
Parser<ParseHandler>::matchLabel(MutableHandle<PropertyName*> label)
|
||||
{
|
||||
TokenKind tt = ts.peekTokenSameLine(TokenStream::Operand);
|
||||
TokenKind tt = tokenStream.peekTokenSameLine(TokenStream::Operand);
|
||||
if (tt == TOK_ERROR)
|
||||
return false;
|
||||
if (tt == TOK_NAME) {
|
||||
(void) ts.getToken();
|
||||
label.set(ts.currentToken().name());
|
||||
tokenStream.consumeKnownToken(TOK_NAME);
|
||||
label.set(tokenStream.currentName());
|
||||
} else if (tt == TOK_YIELD) {
|
||||
tokenStream.consumeKnownToken(TOK_YIELD);
|
||||
if (!checkYieldNameValidity())
|
||||
return false;
|
||||
label.set(tokenStream.currentName());
|
||||
} else {
|
||||
label.set(NULL);
|
||||
}
|
||||
@ -3240,7 +3335,7 @@ template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::letBlock(LetContext letContext)
|
||||
{
|
||||
JS_ASSERT(tokenStream.currentToken().type == TOK_LET);
|
||||
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LET));
|
||||
|
||||
RootedStaticBlockObject blockObj(context, StaticBlockObject::create(context));
|
||||
if (!blockObj)
|
||||
@ -3330,7 +3425,7 @@ template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::blockStatement()
|
||||
{
|
||||
JS_ASSERT(tokenStream.currentToken().type == TOK_LC);
|
||||
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LC));
|
||||
|
||||
StmtInfoPC stmtInfo(context);
|
||||
if (!PushBlocklikeStatement(&stmtInfo, STMT_BLOCK, pc))
|
||||
@ -3372,7 +3467,6 @@ Parser<ParseHandler>::newBindingNode(PropertyName *name, bool functionScope, Var
|
||||
}
|
||||
|
||||
/* Make a new node for this declarator name (or destructuring pattern). */
|
||||
JS_ASSERT(tokenStream.currentToken().type == TOK_NAME);
|
||||
return newName(name);
|
||||
}
|
||||
|
||||
@ -3461,12 +3555,17 @@ Parser<ParseHandler>::variables(ParseNodeKind kind, bool *psimple,
|
||||
#endif /* JS_HAS_DESTRUCTURING */
|
||||
|
||||
if (tt != TOK_NAME) {
|
||||
if (tt != TOK_ERROR)
|
||||
report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME);
|
||||
return null();
|
||||
if (tt == TOK_YIELD) {
|
||||
if (!checkYieldNameValidity(JSMSG_NO_VARIABLE_NAME))
|
||||
return null();
|
||||
} else {
|
||||
if (tt != TOK_ERROR)
|
||||
report(ParseError, false, null(), JSMSG_NO_VARIABLE_NAME);
|
||||
return null();
|
||||
}
|
||||
}
|
||||
|
||||
RootedPropertyName name(context, tokenStream.currentToken().name());
|
||||
RootedPropertyName name(context, tokenStream.currentName());
|
||||
pn2 = newBindingNode(name, kind == PNK_VAR || kind == PNK_CONST, varContext);
|
||||
if (!pn2)
|
||||
return null();
|
||||
@ -4123,9 +4222,14 @@ Parser<SyntaxParseHandler>::forStatement()
|
||||
PushStatementPC(pc, &forStmt, STMT_FOR_LOOP);
|
||||
|
||||
/* Don't parse 'for each' loops. */
|
||||
if (allowsForEachIn() && tokenStream.peekToken() == TOK_NAME) {
|
||||
JS_ALWAYS_FALSE(abortIfSyntaxParser());
|
||||
return null();
|
||||
if (allowsForEachIn()) {
|
||||
TokenKind tt = tokenStream.peekToken();
|
||||
// Not all "yield" tokens are names, but the ones that aren't names are
|
||||
// invalid in this context anyway.
|
||||
if (tt == TOK_NAME || tt == TOK_YIELD) {
|
||||
JS_ALWAYS_FALSE(abortIfSyntaxParser());
|
||||
return null();
|
||||
}
|
||||
}
|
||||
|
||||
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR);
|
||||
@ -4327,7 +4431,7 @@ Parser<ParseHandler>::continueStatement()
|
||||
uint32_t begin = pos().begin;
|
||||
|
||||
RootedPropertyName label(context);
|
||||
if (!MatchLabel(tokenStream, &label))
|
||||
if (!matchLabel(&label))
|
||||
return null();
|
||||
|
||||
StmtInfoPC *stmt = pc->topStmt;
|
||||
@ -4374,7 +4478,7 @@ Parser<ParseHandler>::breakStatement()
|
||||
uint32_t begin = pos().begin;
|
||||
|
||||
RootedPropertyName label(context);
|
||||
if (!MatchLabel(tokenStream, &label))
|
||||
if (!matchLabel(&label))
|
||||
return null();
|
||||
StmtInfoPC *stmt = pc->topStmt;
|
||||
if (label) {
|
||||
@ -4405,98 +4509,45 @@ Parser<ParseHandler>::breakStatement()
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::returnStatementOrYieldExpression()
|
||||
Parser<ParseHandler>::returnStatement()
|
||||
{
|
||||
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_RETURN) ||
|
||||
tokenStream.isCurrentTokenType(TOK_YIELD));
|
||||
bool isYield = tokenStream.isCurrentTokenType(TOK_YIELD);
|
||||
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_RETURN));
|
||||
uint32_t begin = pos().begin;
|
||||
|
||||
if (!pc->sc->isFunctionBox()) {
|
||||
report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD,
|
||||
isYield ? js_yield_str : js_return_str);
|
||||
report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_return_str);
|
||||
return null();
|
||||
}
|
||||
|
||||
// Legacy generators are identified by the presence of "yield" in their
|
||||
// bodies. We only see "yield" as TOK_YIELD in JS 1.7+.
|
||||
if (isYield) {
|
||||
JS_ASSERT(tokenStream.versionNumber() >= JSVERSION_1_7);
|
||||
if (!abortIfSyntaxParser())
|
||||
return null();
|
||||
|
||||
if (pc->isLegacyGenerator()) {
|
||||
// We are in a legacy generator: a function that has already seen a
|
||||
// yield.
|
||||
JS_ASSERT(pc->sc->isFunctionBox());
|
||||
JS_ASSERT(pc->lastYieldOffset != ParseContext<ParseHandler>::NoYieldOffset);
|
||||
} else {
|
||||
// We are in a code that has not seen a yield, and in JS 1.8 so
|
||||
// "yield" parsed as TOK_YIELD. Try to transition to being a legacy
|
||||
// generator.
|
||||
JS_ASSERT(tokenStream.currentToken().type == TOK_YIELD);
|
||||
JS_ASSERT(pc->lastYieldOffset == ParseContext<ParseHandler>::NoYieldOffset);
|
||||
|
||||
if (!pc->sc->isFunctionBox()) {
|
||||
report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_yield_str);
|
||||
return null();
|
||||
}
|
||||
|
||||
pc->generatorParseMode = ParseContext<ParseHandler>::LegacyGenerator;
|
||||
}
|
||||
|
||||
pc->lastYieldOffset = begin;
|
||||
}
|
||||
|
||||
// Parse an optional operand.
|
||||
//
|
||||
// Checking whether yield has an operand is especially wonky since
|
||||
// there is not a mandatory semicolon.
|
||||
//
|
||||
// ES6 does not permit yield without an operand. We will have to sunset
|
||||
// this extension in order to conform to the ES6 syntax, which treats
|
||||
// "yield \n expr;" as a single ExpressionStatement.
|
||||
// This is ugly, but we don't want to require a semicolon.
|
||||
Node exprNode;
|
||||
TokenKind next = tokenStream.peekTokenSameLine(TokenStream::Operand);
|
||||
if (next == TOK_ERROR)
|
||||
switch (tokenStream.peekTokenSameLine(TokenStream::Operand)) {
|
||||
case TOK_ERROR:
|
||||
return null();
|
||||
if (next == TOK_EOF || next == TOK_EOL || next == TOK_SEMI || next == TOK_RC ||
|
||||
(isYield && (next == TOK_RB || next == TOK_RP || next == TOK_COLON || next == TOK_COMMA)))
|
||||
{
|
||||
if (isYield) {
|
||||
if (!reportWithOffset(ParseWarning, false, pos().begin, JSMSG_YIELD_WITHOUT_OPERAND))
|
||||
return null();
|
||||
}
|
||||
|
||||
case TOK_EOF:
|
||||
case TOK_EOL:
|
||||
case TOK_SEMI:
|
||||
case TOK_RC:
|
||||
exprNode = null();
|
||||
if (!isYield)
|
||||
pc->funHasReturnVoid = true;
|
||||
} else {
|
||||
exprNode = isYield ? assignExpr() : expr();
|
||||
pc->funHasReturnVoid = true;
|
||||
break;
|
||||
default: {
|
||||
exprNode = expr();
|
||||
if (!exprNode)
|
||||
return null();
|
||||
if (!isYield)
|
||||
pc->funHasReturnExpr = true;
|
||||
pc->funHasReturnExpr = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isYield) {
|
||||
if (!MatchOrInsertSemicolon(tokenStream))
|
||||
return null();
|
||||
}
|
||||
if (!MatchOrInsertSemicolon(tokenStream))
|
||||
return null();
|
||||
|
||||
Node pn = isYield
|
||||
? handler.newUnary(PNK_YIELD, JSOP_YIELD, begin, exprNode)
|
||||
: handler.newReturnStatement(exprNode, TokenPos(begin, pos().end));
|
||||
Node pn = handler.newReturnStatement(exprNode, TokenPos(begin, pos().end));
|
||||
if (!pn)
|
||||
return null();
|
||||
|
||||
if (pc->funHasReturnExpr && pc->isLegacyGenerator()) {
|
||||
/* As in Python (see PEP-255), disallow return v; in generators. */
|
||||
reportBadReturn(pn, ParseError, JSMSG_BAD_GENERATOR_RETURN,
|
||||
JSMSG_BAD_ANON_GENERATOR_RETURN);
|
||||
return null();
|
||||
}
|
||||
|
||||
if (options().extraWarningsOption && pc->funHasReturnExpr && pc->funHasReturnVoid &&
|
||||
!reportBadReturn(pn, ParseExtraWarning,
|
||||
JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE))
|
||||
@ -4504,9 +4555,108 @@ Parser<ParseHandler>::returnStatementOrYieldExpression()
|
||||
return null();
|
||||
}
|
||||
|
||||
if (pc->isLegacyGenerator() && exprNode) {
|
||||
/* Disallow "return v;" in legacy generators. */
|
||||
reportBadReturn(pn, ParseError, JSMSG_BAD_GENERATOR_RETURN,
|
||||
JSMSG_BAD_ANON_GENERATOR_RETURN);
|
||||
return null();
|
||||
}
|
||||
|
||||
return pn;
|
||||
}
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::yieldExpression()
|
||||
{
|
||||
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_YIELD));
|
||||
uint32_t begin = pos().begin;
|
||||
|
||||
switch (pc->generatorKind()) {
|
||||
case StarGenerator:
|
||||
{
|
||||
JS_ASSERT(pc->sc->isFunctionBox());
|
||||
|
||||
pc->lastYieldOffset = begin;
|
||||
|
||||
bool isDelegatingYield = tokenStream.matchToken(TOK_MUL);
|
||||
|
||||
// ES6 generators require a value.
|
||||
Node exprNode = assignExpr();
|
||||
if (!exprNode)
|
||||
return null();
|
||||
|
||||
// FIXME: Plumb isDelegatingYield appropriately.
|
||||
(void) isDelegatingYield;
|
||||
return handler.newUnary(PNK_YIELD, JSOP_YIELD, begin, exprNode);
|
||||
}
|
||||
|
||||
case NotGenerator:
|
||||
// We are in code that has not seen a yield, but we are in JS 1.7 or
|
||||
// later. Try to transition to being a legacy generator.
|
||||
JS_ASSERT(tokenStream.versionNumber() >= JSVERSION_1_7);
|
||||
JS_ASSERT(pc->lastYieldOffset == ParseContext<ParseHandler>::NoYieldOffset);
|
||||
|
||||
if (!abortIfSyntaxParser())
|
||||
return null();
|
||||
|
||||
if (!pc->sc->isFunctionBox()) {
|
||||
report(ParseError, false, null(), JSMSG_BAD_RETURN_OR_YIELD, js_yield_str);
|
||||
return null();
|
||||
}
|
||||
|
||||
pc->sc->asFunctionBox()->setGeneratorKind(LegacyGenerator);
|
||||
|
||||
if (pc->funHasReturnExpr) {
|
||||
/* As in Python (see PEP-255), disallow return v; in generators. */
|
||||
reportBadReturn(null(), ParseError, JSMSG_BAD_GENERATOR_RETURN,
|
||||
JSMSG_BAD_ANON_GENERATOR_RETURN);
|
||||
return null();
|
||||
}
|
||||
// Fall through.
|
||||
|
||||
case LegacyGenerator:
|
||||
{
|
||||
// We are in a legacy generator: a function that has already seen a
|
||||
// yield, or in a legacy generator comprehension.
|
||||
JS_ASSERT(pc->sc->isFunctionBox());
|
||||
|
||||
pc->lastYieldOffset = begin;
|
||||
|
||||
// Legacy generators do not require a value.
|
||||
Node exprNode;
|
||||
switch (tokenStream.peekTokenSameLine(TokenStream::Operand)) {
|
||||
case TOK_ERROR:
|
||||
return null();
|
||||
case TOK_EOF:
|
||||
case TOK_EOL:
|
||||
case TOK_SEMI:
|
||||
case TOK_RC:
|
||||
case TOK_RB:
|
||||
case TOK_RP:
|
||||
case TOK_COLON:
|
||||
case TOK_COMMA:
|
||||
// No value.
|
||||
exprNode = null();
|
||||
// ES6 does not permit yield without an operand. We should
|
||||
// encourage users of yield expressions of this kind to pass an
|
||||
// operand, to bring users closer to standard syntax.
|
||||
if (!reportWithOffset(ParseWarning, false, pos().begin, JSMSG_YIELD_WITHOUT_OPERAND))
|
||||
return null();
|
||||
break;
|
||||
default:
|
||||
exprNode = assignExpr();
|
||||
if (!exprNode)
|
||||
return null();
|
||||
}
|
||||
|
||||
return handler.newUnary(PNK_YIELD, JSOP_YIELD, begin, exprNode);
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSUME_UNREACHABLE("yieldExpr");
|
||||
}
|
||||
|
||||
template <>
|
||||
ParseNode *
|
||||
Parser<FullParseHandler>::withStatement()
|
||||
@ -4576,7 +4726,7 @@ typename ParseHandler::Node
|
||||
Parser<ParseHandler>::labeledStatement()
|
||||
{
|
||||
uint32_t begin = pos().begin;
|
||||
RootedPropertyName label(context, tokenStream.currentToken().name());
|
||||
RootedPropertyName label(context, tokenStream.currentName());
|
||||
for (StmtInfoPC *stmt = pc->topStmt; stmt; stmt = stmt->down) {
|
||||
if (stmt->type == STMT_LABEL && stmt->label == label) {
|
||||
report(ParseError, false, null(), JSMSG_DUPLICATE_LABEL);
|
||||
@ -4717,9 +4867,13 @@ Parser<ParseHandler>::tryStatement()
|
||||
break;
|
||||
#endif
|
||||
|
||||
case TOK_YIELD:
|
||||
if (!checkYieldNameValidity(JSMSG_CATCH_IDENTIFIER))
|
||||
return null();
|
||||
// Fall through.
|
||||
case TOK_NAME:
|
||||
{
|
||||
RootedPropertyName label(context, tokenStream.currentToken().name());
|
||||
RootedPropertyName label(context, tokenStream.currentName());
|
||||
catchName = newBindingNode(label, false);
|
||||
if (!catchName)
|
||||
return null();
|
||||
@ -4855,7 +5009,7 @@ Parser<ParseHandler>::statement(bool canHaveDirectives)
|
||||
case TOK_BREAK:
|
||||
return breakStatement();
|
||||
case TOK_RETURN:
|
||||
return returnStatementOrYieldExpression();
|
||||
return returnStatement();
|
||||
case TOK_WITH:
|
||||
return withStatement();
|
||||
case TOK_THROW:
|
||||
@ -4886,10 +5040,18 @@ Parser<ParseHandler>::statement(bool canHaveDirectives)
|
||||
}
|
||||
return expressionStatement();
|
||||
|
||||
case TOK_YIELD:
|
||||
if (tokenStream.peekToken() == TOK_COLON) {
|
||||
if (!checkYieldNameValidity())
|
||||
return null();
|
||||
return labeledStatement();
|
||||
}
|
||||
return expressionStatement();
|
||||
|
||||
case TOK_NAME:
|
||||
if (tokenStream.peekToken() == TOK_COLON)
|
||||
return labeledStatement();
|
||||
if (tokenStream.currentToken().name() == context->names().module
|
||||
if (tokenStream.currentName() == context->names().module
|
||||
&& tokenStream.peekTokenSameLine() == TOK_STRING)
|
||||
{
|
||||
return moduleDecl();
|
||||
@ -5202,8 +5364,8 @@ Parser<ParseHandler>::assignExpr()
|
||||
if (tt == TOK_STRING && tokenStream.nextTokenEndsExpr())
|
||||
return stringLiteral();
|
||||
|
||||
if (tt == TOK_YIELD)
|
||||
return returnStatementOrYieldExpression();
|
||||
if (tt == TOK_YIELD && (versionNumber() >= JSVERSION_1_7 || pc->isGenerator()))
|
||||
return yieldExpression();
|
||||
|
||||
tokenStream.ungetToken();
|
||||
|
||||
@ -5241,7 +5403,7 @@ Parser<ParseHandler>::assignExpr()
|
||||
return null();
|
||||
tokenStream.ungetToken();
|
||||
|
||||
return functionDef(NullPtr(), start, Normal, Arrow);
|
||||
return functionDef(NullPtr(), start, Normal, Arrow, NotGenerator);
|
||||
}
|
||||
|
||||
default:
|
||||
@ -5662,7 +5824,7 @@ Parser<FullParseHandler>::comprehensionTail(ParseNode *kid, unsigned blockid, bo
|
||||
BindData<FullParseHandler> data(context);
|
||||
TokenKind tt;
|
||||
|
||||
JS_ASSERT(tokenStream.currentToken().type == TOK_FOR);
|
||||
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_FOR));
|
||||
|
||||
if (kind == PNK_SEMI) {
|
||||
/*
|
||||
@ -5747,7 +5909,7 @@ Parser<FullParseHandler>::comprehensionTail(ParseNode *kid, unsigned blockid, bo
|
||||
#endif
|
||||
|
||||
case TOK_NAME:
|
||||
name = tokenStream.currentToken().name();
|
||||
name = tokenStream.currentName();
|
||||
|
||||
/*
|
||||
* Create a name node with pn_op JSOP_NAME. We can't set pn_op to
|
||||
@ -5955,7 +6117,8 @@ Parser<FullParseHandler>::generatorExpr(ParseNode *kid)
|
||||
|
||||
/* Create box for fun->object early to protect against last-ditch GC. */
|
||||
Directives directives(/* strict = */ outerpc->sc->strict);
|
||||
FunctionBox *genFunbox = newFunctionBox(genfn, fun, outerpc, directives);
|
||||
FunctionBox *genFunbox = newFunctionBox(genfn, fun, outerpc, directives,
|
||||
LegacyGenerator);
|
||||
if (!genFunbox)
|
||||
return null();
|
||||
|
||||
@ -5975,7 +6138,7 @@ Parser<FullParseHandler>::generatorExpr(ParseNode *kid)
|
||||
if (outerpc->sc->isFunctionBox())
|
||||
genFunbox->funCxFlags = outerpc->sc->asFunctionBox()->funCxFlags;
|
||||
|
||||
genFunbox->setIsLegacyGenerator();
|
||||
JS_ASSERT(genFunbox->isLegacyGenerator());
|
||||
genFunbox->inGenexpLambda = true;
|
||||
genfn->pn_blockid = genpc.bodyid;
|
||||
|
||||
@ -6116,7 +6279,7 @@ Parser<ParseHandler>::memberExpr(TokenKind tt, bool allowCallSyntax)
|
||||
if (tt == TOK_ERROR)
|
||||
return null();
|
||||
if (tt == TOK_NAME) {
|
||||
PropertyName *field = tokenStream.currentToken().name();
|
||||
PropertyName *field = tokenStream.currentName();
|
||||
nextMember = handler.newPropertyAccess(lhs, field, pos().end);
|
||||
if (!nextMember)
|
||||
return null();
|
||||
@ -6191,9 +6354,7 @@ template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::identifierName()
|
||||
{
|
||||
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_NAME));
|
||||
|
||||
RootedPropertyName name(context, tokenStream.currentToken().name());
|
||||
RootedPropertyName name(context, tokenStream.currentName());
|
||||
Node pn = newName(name);
|
||||
if (!pn)
|
||||
return null();
|
||||
@ -6395,7 +6556,7 @@ Parser<ParseHandler>::objectLiteral()
|
||||
break;
|
||||
|
||||
case TOK_NAME: {
|
||||
atom = tokenStream.currentToken().name();
|
||||
atom = tokenStream.currentName();
|
||||
if (atom == context->names().get) {
|
||||
op = JSOP_INITPROP_GETTER;
|
||||
} else if (atom == context->names().set) {
|
||||
@ -6411,7 +6572,7 @@ Parser<ParseHandler>::objectLiteral()
|
||||
// name next.
|
||||
TokenKind tt = tokenStream.getToken(TokenStream::KeywordIsName);
|
||||
if (tt == TOK_NAME) {
|
||||
atom = tokenStream.currentToken().name();
|
||||
atom = tokenStream.currentName();
|
||||
propname = newName(atom->asPropertyName());
|
||||
if (!propname)
|
||||
return null();
|
||||
@ -6527,7 +6688,7 @@ Parser<ParseHandler>::objectLiteral()
|
||||
TokenStream::Position start(keepAtoms);
|
||||
tokenStream.tell(&start);
|
||||
Node accessor = functionDef(funName, start, op == JSOP_INITPROP_GETTER ? Getter : Setter,
|
||||
Expression);
|
||||
Expression, NotGenerator);
|
||||
if (!accessor)
|
||||
return null();
|
||||
if (!handler.addAccessorPropertyDefinition(literal, propname, accessor, op))
|
||||
@ -6628,6 +6789,10 @@ Parser<ParseHandler>::primaryExpr(TokenKind tt)
|
||||
case TOK_STRING:
|
||||
return stringLiteral();
|
||||
|
||||
case TOK_YIELD:
|
||||
if (!checkYieldNameValidity())
|
||||
return null();
|
||||
// Fall through.
|
||||
case TOK_NAME:
|
||||
return identifierName();
|
||||
|
||||
@ -6689,7 +6854,7 @@ template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::parenExpr(bool *genexp)
|
||||
{
|
||||
JS_ASSERT(tokenStream.currentToken().type == TOK_LP);
|
||||
JS_ASSERT(tokenStream.isCurrentTokenType(TOK_LP));
|
||||
uint32_t begin = pos().begin;
|
||||
|
||||
if (genexp)
|
||||
|
@ -104,18 +104,20 @@ struct ParseContext : public GenericParseContext
|
||||
|
||||
const unsigned staticLevel; /* static compilation unit nesting level */
|
||||
|
||||
// Functions start off being parsed as NotGenerator.
|
||||
// NotGenerator transitions to LegacyGenerator on parsing "yield" in JS 1.7.
|
||||
enum GeneratorParseMode { NotGenerator, LegacyGenerator };
|
||||
GeneratorParseMode generatorParseMode;
|
||||
|
||||
// lastYieldOffset stores the offset of the last yield that was parsed.
|
||||
// NoYieldOffset is its initial value.
|
||||
static const uint32_t NoYieldOffset = UINT32_MAX;
|
||||
uint32_t lastYieldOffset;
|
||||
|
||||
bool isGenerator() const { return generatorParseMode != NotGenerator; }
|
||||
bool isLegacyGenerator() const { return generatorParseMode == LegacyGenerator; }
|
||||
// Most functions start off being parsed as non-generators.
|
||||
// Non-generators transition to LegacyGenerator on parsing "yield" in JS 1.7.
|
||||
// An ES6 generator is marked as a "star generator" before its body is parsed.
|
||||
GeneratorKind generatorKind() const {
|
||||
return sc->isFunctionBox() ? sc->asFunctionBox()->generatorKind() : NotGenerator;
|
||||
}
|
||||
bool isGenerator() const { return generatorKind() != NotGenerator; }
|
||||
bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; }
|
||||
bool isStarGenerator() const { return generatorKind() == StarGenerator; }
|
||||
|
||||
Node blockNode; /* parse node for a block with let declarations
|
||||
(block with its own lexical scope) */
|
||||
@ -249,7 +251,6 @@ struct ParseContext : public GenericParseContext
|
||||
blockChain(prs->context),
|
||||
maybeFunction(maybeFunction),
|
||||
staticLevel(staticLevel),
|
||||
generatorParseMode(NotGenerator),
|
||||
lastYieldOffset(NoYieldOffset),
|
||||
blockNode(ParseHandler::null()),
|
||||
decls_(prs->context, prs->alloc),
|
||||
@ -400,7 +401,7 @@ class Parser : private AutoGCRooter, public StrictModeGetter
|
||||
ObjectBox *newObjectBox(JSObject *obj);
|
||||
ModuleBox *newModuleBox(Module *module, ParseContext<ParseHandler> *pc);
|
||||
FunctionBox *newFunctionBox(Node fn, JSFunction *fun, ParseContext<ParseHandler> *pc,
|
||||
Directives directives);
|
||||
Directives directives, GeneratorKind generatorKind);
|
||||
|
||||
/*
|
||||
* Create a new function object given parse context (pc) and a name (which
|
||||
@ -433,13 +434,16 @@ class Parser : private AutoGCRooter, public StrictModeGetter
|
||||
Node statement(bool canHaveDirectives = false);
|
||||
bool maybeParseDirective(Node list, Node pn, bool *cont);
|
||||
|
||||
// Parse a function, given only its body. Used for the Function constructor.
|
||||
// Parse a function, given only its body. Used for the Function and
|
||||
// Generator constructors.
|
||||
Node standaloneFunctionBody(HandleFunction fun, const AutoNameVector &formals,
|
||||
GeneratorKind generatorKind,
|
||||
Directives inheritedDirectives, Directives *newDirectives);
|
||||
|
||||
// Parse a function, given only its arguments and body. Used for lazily
|
||||
// parsed functions.
|
||||
Node standaloneLazyFunction(HandleFunction fun, unsigned staticLevel, bool strict);
|
||||
Node standaloneLazyFunction(HandleFunction fun, unsigned staticLevel, bool strict,
|
||||
GeneratorKind generatorKind);
|
||||
|
||||
/*
|
||||
* Parse a function body. Pass StatementListBody if the body is a list of
|
||||
@ -487,7 +491,7 @@ class Parser : private AutoGCRooter, public StrictModeGetter
|
||||
Node switchStatement();
|
||||
Node continueStatement();
|
||||
Node breakStatement();
|
||||
Node returnStatementOrYieldExpression();
|
||||
Node returnStatement();
|
||||
Node withStatement();
|
||||
Node labeledStatement();
|
||||
Node throwStatement();
|
||||
@ -504,6 +508,7 @@ class Parser : private AutoGCRooter, public StrictModeGetter
|
||||
Node expr();
|
||||
Node assignExpr();
|
||||
Node assignExprWithoutYield(unsigned err);
|
||||
Node yieldExpression();
|
||||
Node condExpr1();
|
||||
Node orExpr1();
|
||||
Node unaryExpr();
|
||||
@ -517,9 +522,10 @@ class Parser : private AutoGCRooter, public StrictModeGetter
|
||||
bool functionArguments(FunctionSyntaxKind kind, Node *list, Node funcpn, bool &hasRest);
|
||||
|
||||
Node functionDef(HandlePropertyName name, const TokenStream::Position &start,
|
||||
FunctionType type, FunctionSyntaxKind kind);
|
||||
FunctionType type, FunctionSyntaxKind kind, GeneratorKind generatorKind);
|
||||
bool functionArgsAndBody(Node pn, HandleFunction fun,
|
||||
FunctionType type, FunctionSyntaxKind kind,
|
||||
GeneratorKind generatorKind,
|
||||
Directives inheritedDirectives, Directives *newDirectives);
|
||||
|
||||
Node unaryOpExpr(ParseNodeKind kind, JSOp op, uint32_t begin);
|
||||
@ -536,6 +542,9 @@ class Parser : private AutoGCRooter, public StrictModeGetter
|
||||
|
||||
Node identifierName();
|
||||
|
||||
bool matchLabel(MutableHandle<PropertyName*> label);
|
||||
bool checkYieldNameValidity(unsigned errorNumber = JSMSG_SYNTAX_ERROR);
|
||||
|
||||
bool allowsForEachIn() {
|
||||
#if !JS_HAS_FOR_EACH_IN
|
||||
return false;
|
||||
@ -556,6 +565,7 @@ class Parser : private AutoGCRooter, public StrictModeGetter
|
||||
|
||||
bool checkFunctionArguments();
|
||||
bool makeDefIntoUse(Definition *dn, Node pn, JSAtom *atom);
|
||||
bool checkFunctionName(HandlePropertyName funName);
|
||||
bool checkFunctionDefinition(HandlePropertyName funName, Node *pn, FunctionSyntaxKind kind,
|
||||
bool *pbodyProcessed);
|
||||
bool finishFunctionDefinition(Node pn, FunctionBox *funbox, Node prelude, Node body);
|
||||
|
@ -72,10 +72,6 @@ class FunctionContextFlags
|
||||
// This class's data is all private and so only visible to these friends.
|
||||
friend class FunctionBox;
|
||||
|
||||
// We parsed a yield statement in the function, which can happen in JS1.7+
|
||||
// mode.
|
||||
bool isLegacyGenerator:1;
|
||||
|
||||
// The function or a function that encloses it may define new local names
|
||||
// at runtime through means other than calling eval.
|
||||
bool mightAliasLocals:1;
|
||||
@ -129,8 +125,7 @@ class FunctionContextFlags
|
||||
|
||||
public:
|
||||
FunctionContextFlags()
|
||||
: isLegacyGenerator(false),
|
||||
mightAliasLocals(false),
|
||||
: mightAliasLocals(false),
|
||||
hasExtensibleScope(false),
|
||||
needsDeclEnvObject(false),
|
||||
argumentsHasLocalBinding(false),
|
||||
@ -264,6 +259,8 @@ class FunctionBox : public ObjectBox, public SharedContext
|
||||
uint32_t startLine;
|
||||
uint32_t startColumn;
|
||||
uint16_t ndefaults;
|
||||
|
||||
uint8_t generatorKindBits_; /* The GeneratorKind of this function. */
|
||||
bool inWith:1; /* some enclosing scope is a with-statement */
|
||||
bool inGenexpLambda:1; /* lambda from generator expression */
|
||||
bool hasDestructuringArgs:1; /* arguments list contains destructuring expression */
|
||||
@ -279,21 +276,30 @@ class FunctionBox : public ObjectBox, public SharedContext
|
||||
template <typename ParseHandler>
|
||||
FunctionBox(ExclusiveContext *cx, ObjectBox* traceListHead, JSFunction *fun,
|
||||
ParseContext<ParseHandler> *pc, Directives directives,
|
||||
bool extraWarnings);
|
||||
bool extraWarnings, GeneratorKind generatorKind);
|
||||
|
||||
ObjectBox *toObjectBox() { return this; }
|
||||
JSFunction *function() const { return &object->as<JSFunction>(); }
|
||||
|
||||
// In the future, isGenerator will also return true for ES6 generators.
|
||||
bool isGenerator() const { return isLegacyGenerator(); }
|
||||
bool isLegacyGenerator() const { return funCxFlags.isLegacyGenerator; }
|
||||
GeneratorKind generatorKind() const { return GeneratorKindFromBits(generatorKindBits_); }
|
||||
bool isGenerator() const { return generatorKind() != NotGenerator; }
|
||||
bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; }
|
||||
bool isStarGenerator() const { return generatorKind() == StarGenerator; }
|
||||
|
||||
void setGeneratorKind(GeneratorKind kind) {
|
||||
// A generator kind can be set at initialization, or when "yield" is
|
||||
// first seen. In both cases the transition can only happen from
|
||||
// NotGenerator.
|
||||
JS_ASSERT(!isGenerator());
|
||||
generatorKindBits_ = GeneratorKindAsBits(kind);
|
||||
}
|
||||
|
||||
bool mightAliasLocals() const { return funCxFlags.mightAliasLocals; }
|
||||
bool hasExtensibleScope() const { return funCxFlags.hasExtensibleScope; }
|
||||
bool needsDeclEnvObject() const { return funCxFlags.needsDeclEnvObject; }
|
||||
bool argumentsHasLocalBinding() const { return funCxFlags.argumentsHasLocalBinding; }
|
||||
bool definitelyNeedsArgsObj() const { return funCxFlags.definitelyNeedsArgsObj; }
|
||||
|
||||
void setIsLegacyGenerator() { funCxFlags.isLegacyGenerator = true; }
|
||||
void setMightAliasLocals() { funCxFlags.mightAliasLocals = true; }
|
||||
void setHasExtensibleScope() { funCxFlags.hasExtensibleScope = true; }
|
||||
void setNeedsDeclEnvObject() { funCxFlags.needsDeclEnvObject = true; }
|
||||
|
@ -937,10 +937,10 @@ TokenStream::checkForKeyword(const jschar *s, size_t length, TokenKind *ttp)
|
||||
return reportError(JSMSG_RESERVED_ID, kw->chars);
|
||||
}
|
||||
|
||||
// The keyword is not in this version. Treat it as an identifier,
|
||||
// unless it is let or yield which we treat as TOK_STRICT_RESERVED by
|
||||
// falling through to the code below (ES5 forbids them in strict mode).
|
||||
if (kw->tokentype != TOK_LET && kw->tokentype != TOK_YIELD)
|
||||
// The keyword is not in this version. Treat it as an identifier, unless
|
||||
// it is let which we treat as TOK_STRICT_RESERVED by falling through to
|
||||
// the code below (ES5 forbids it in strict mode).
|
||||
if (kw->tokentype != TOK_LET)
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -407,6 +407,13 @@ class MOZ_STACK_CLASS TokenStream
|
||||
JSVersion versionNumber() const { return VersionNumber(options().version); }
|
||||
JSVersion versionWithFlags() const { return options().version; }
|
||||
|
||||
PropertyName *currentName() const {
|
||||
if (isCurrentTokenType(TOK_YIELD))
|
||||
return cx->names().yield;
|
||||
JS_ASSERT(isCurrentTokenType(TOK_NAME));
|
||||
return currentToken().name();
|
||||
}
|
||||
|
||||
bool isCurrentTokenAssignment() const {
|
||||
return TokenKindIsAssignment(currentToken().type);
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import os, shlex, subprocess, sys, traceback
|
||||
import os, posixpath, shlex, subprocess, sys, traceback
|
||||
|
||||
def add_libdir_to_path():
|
||||
from os.path import dirname, exists, join, realpath
|
||||
@ -74,6 +74,23 @@ def main(argv):
|
||||
help='Run tests with all IonMonkey option combinations (ignores --jitflags)')
|
||||
op.add_option('-j', '--worker-count', dest='max_jobs', type=int, default=max_jobs_default,
|
||||
help='Number of tests to run in parallel (default %default)')
|
||||
op.add_option('--remote', action='store_true',
|
||||
help='Run tests on a remote device')
|
||||
op.add_option('--deviceIP', action='store',
|
||||
type='string', dest='device_ip',
|
||||
help='IP address of remote device to test')
|
||||
op.add_option('--devicePort', action='store',
|
||||
type=int, dest='device_port', default=20701,
|
||||
help='port of remote device to test')
|
||||
op.add_option('--deviceTransport', action='store',
|
||||
type='string', dest='device_transport', default='sut',
|
||||
help='The transport to use to communicate with device: [adb|sut]; default=sut')
|
||||
op.add_option('--remoteTestRoot', dest='remote_test_root', action='store',
|
||||
type='string', default='/data/local/tests',
|
||||
help='The remote directory to use as test root (eg. /data/local/tests)')
|
||||
op.add_option('--localLib', dest='local_lib', action='store',
|
||||
type='string',
|
||||
help='The location of libraries to push -- preferably stripped')
|
||||
|
||||
options, args = op.parse_args(argv)
|
||||
if len(args) < 1:
|
||||
@ -171,7 +188,11 @@ def main(argv):
|
||||
job_list.append(new_test)
|
||||
|
||||
prefix = [os.path.abspath(args[0])] + shlex.split(options.shell_args)
|
||||
prefix += ['-f', os.path.join(jittests.LIB_DIR, 'prolog.js')]
|
||||
prolog = os.path.join(jittests.LIB_DIR, 'prolog.js')
|
||||
if options.remote:
|
||||
prolog = posixpath.join(options.remote_test_root, 'jit-tests/lib/prolog.js')
|
||||
|
||||
prefix += ['-f', prolog]
|
||||
if options.debug:
|
||||
if len(job_list) > 1:
|
||||
print 'Multiple tests match command line arguments, debugger can only run one'
|
||||
@ -186,7 +207,9 @@ def main(argv):
|
||||
|
||||
try:
|
||||
ok = None
|
||||
if options.max_jobs > 1 and jittests.HAVE_MULTIPROCESSING:
|
||||
if options.remote:
|
||||
ok = jittests.run_tests_remote(job_list, prefix, options)
|
||||
elif options.max_jobs > 1 and jittests.HAVE_MULTIPROCESSING:
|
||||
ok = jittests.run_tests_parallel(job_list, prefix, options)
|
||||
else:
|
||||
ok = jittests.run_tests(job_list, prefix, options)
|
||||
|
38
js/src/jit-test/tests/generators/es6-syntax.js
Normal file
38
js/src/jit-test/tests/generators/es6-syntax.js
Normal 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; })()")
|
@ -4607,7 +4607,7 @@ ParseFunction(ModuleCompiler &m, ParseNode **fnOut)
|
||||
AsmJSParseContext *outerpc = m.parser().pc;
|
||||
|
||||
Directives directives(outerpc);
|
||||
FunctionBox *funbox = m.parser().newFunctionBox(fn, fun, outerpc, directives);
|
||||
FunctionBox *funbox = m.parser().newFunctionBox(fn, fun, outerpc, directives, NotGenerator);
|
||||
if (!funbox)
|
||||
return false;
|
||||
|
||||
|
@ -409,3 +409,4 @@ MSG_DEF(JSMSG_BINARYDATA_NOT_BINARYSTRUCT, 355, 1, JSEXN_TYPEERR, "{0} is not
|
||||
MSG_DEF(JSMSG_BINARYDATA_SUBARRAY_INTEGER_ARG, 356, 1, JSEXN_ERR, "argument {0} must be an integer")
|
||||
MSG_DEF(JSMSG_BINARYDATA_STRUCTTYPE_EMPTY_DESCRIPTOR, 357, 0, JSEXN_ERR, "field descriptor cannot be empty")
|
||||
MSG_DEF(JSMSG_BINARYDATA_STRUCTTYPE_BAD_FIELD, 358, 1, JSEXN_ERR, "field {0} is not a valid BinaryData Type descriptor")
|
||||
MSG_DEF(JSMSG_ES6_UNIMPLEMENTED, 359, 0, JSEXN_ERR, "ES6 functionality not yet implemented")
|
||||
|
@ -1749,7 +1749,7 @@ ScriptAnalysis::needsArgsObj(JSContext *cx)
|
||||
*
|
||||
* FIXME: Don't build arguments for ES6 generator expressions.
|
||||
*/
|
||||
if (cx->compartment()->debugMode() || script_->isGenerator)
|
||||
if (cx->compartment()->debugMode() || script_->isGenerator())
|
||||
return true;
|
||||
|
||||
/*
|
||||
|
@ -104,7 +104,6 @@ const char js_typeof_str[] = "typeof";
|
||||
const char js_void_str[] = "void";
|
||||
const char js_while_str[] = "while";
|
||||
const char js_with_str[] = "with";
|
||||
const char js_yield_str[] = "yield";
|
||||
|
||||
/*
|
||||
* For a browser build from 2007-08-09 after the browser starts up there are
|
||||
|
@ -153,7 +153,6 @@ extern const char js_typeof_str[];
|
||||
extern const char js_void_str[];
|
||||
extern const char js_while_str[];
|
||||
extern const char js_with_str[];
|
||||
extern const char js_yield_str[];
|
||||
|
||||
namespace js {
|
||||
|
||||
|
@ -526,6 +526,7 @@ FindBody(JSContext *cx, HandleFunction fun, StableCharPtr chars, size_t length,
|
||||
do {
|
||||
switch (ts.getToken()) {
|
||||
case TOK_NAME:
|
||||
case TOK_YIELD:
|
||||
if (nest == 0)
|
||||
onward = false;
|
||||
break;
|
||||
@ -1228,14 +1229,7 @@ fun_isGenerator(JSContext *cx, unsigned argc, Value *vp)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool result = false;
|
||||
if (fun->hasScript()) {
|
||||
JSScript *script = fun->nonLazyScript();
|
||||
JS_ASSERT(script->length != 0);
|
||||
result = script->isGenerator;
|
||||
}
|
||||
|
||||
JS_SET_RVAL(cx, vp, BooleanValue(result));
|
||||
JS_SET_RVAL(cx, vp, BooleanValue(fun->isGenerator()));
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1456,10 +1450,15 @@ js::Function(JSContext *cx, unsigned argc, Value *vp)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (tt == TOK_YIELD && ts.versionNumber() < JSVERSION_1_7)
|
||||
tt = TOK_NAME;
|
||||
|
||||
if (tt != TOK_NAME) {
|
||||
if (tt == TOK_TRIPLEDOT) {
|
||||
hasRest = true;
|
||||
tt = ts.getToken();
|
||||
if (tt == TOK_YIELD && ts.versionNumber() < JSVERSION_1_7)
|
||||
tt = TOK_NAME;
|
||||
if (tt != TOK_NAME) {
|
||||
if (tt != TOK_ERROR)
|
||||
ts.reportError(JSMSG_NO_REST_NAME);
|
||||
@ -1470,7 +1469,7 @@ js::Function(JSContext *cx, unsigned argc, Value *vp)
|
||||
}
|
||||
}
|
||||
|
||||
if (!formals.append(ts.currentToken().name()))
|
||||
if (!formals.append(ts.currentName()))
|
||||
return false;
|
||||
|
||||
/*
|
||||
|
@ -284,6 +284,18 @@ class JSFunction : public JSObject
|
||||
return u.i.s.lazy_;
|
||||
}
|
||||
|
||||
js::GeneratorKind generatorKind() const {
|
||||
if (!isInterpreted())
|
||||
return js::NotGenerator;
|
||||
return hasScript() ? nonLazyScript()->generatorKind() : lazyScript()->generatorKind();
|
||||
}
|
||||
|
||||
bool isGenerator() const { return generatorKind() != js::NotGenerator; }
|
||||
|
||||
bool isLegacyGenerator() const { return generatorKind() == js::LegacyGenerator; }
|
||||
|
||||
bool isStarGenerator() const { return generatorKind() == js::StarGenerator; }
|
||||
|
||||
inline void setScript(JSScript *script_);
|
||||
inline void initScript(JSScript *script_);
|
||||
void initLazyScript(js::LazyScript *lazy) {
|
||||
|
@ -1475,6 +1475,13 @@ js_NewGenerator(JSContext *cx, const FrameRegs &stackRegs)
|
||||
JS_ASSERT(stackRegs.stackDepth() == 0);
|
||||
StackFrame *stackfp = stackRegs.fp();
|
||||
|
||||
JS_ASSERT(stackfp->script()->isGenerator());
|
||||
|
||||
if (stackfp->script()->isStarGenerator()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_ES6_UNIMPLEMENTED);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Rooted<GlobalObject*> global(cx, &stackfp->global());
|
||||
RootedObject obj(cx);
|
||||
{
|
||||
|
@ -2787,6 +2787,7 @@ ASTSerializer::function(ParseNode *pn, ASTType type, MutableHandleValue dst)
|
||||
{
|
||||
RootedFunction func(cx, pn->pn_funbox->function());
|
||||
|
||||
// FIXME: Provide more information (legacy generator vs star generator).
|
||||
bool isGenerator = pn->pn_funbox->isGenerator();
|
||||
|
||||
bool isExpression =
|
||||
|
@ -408,9 +408,9 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
|
||||
FunHasAnyAliasedFormal,
|
||||
ArgumentsHasVarBinding,
|
||||
NeedsArgsObj,
|
||||
IsGenerator,
|
||||
IsGeneratorExp,
|
||||
IsLegacyGenerator,
|
||||
IsStarGenerator,
|
||||
OwnSource,
|
||||
ExplicitUseStrict,
|
||||
SelfHosted
|
||||
@ -497,12 +497,12 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
|
||||
scriptBits |= (1 << NeedsArgsObj);
|
||||
if (!enclosingScript || enclosingScript->scriptSource() != script->scriptSource())
|
||||
scriptBits |= (1 << OwnSource);
|
||||
if (script->isGenerator)
|
||||
scriptBits |= (1 << IsGenerator);
|
||||
if (script->isGeneratorExp)
|
||||
scriptBits |= (1 << IsGeneratorExp);
|
||||
if (script->isLegacyGenerator)
|
||||
if (script->isLegacyGenerator())
|
||||
scriptBits |= (1 << IsLegacyGenerator);
|
||||
if (script->isStarGenerator())
|
||||
scriptBits |= (1 << IsStarGenerator);
|
||||
|
||||
JS_ASSERT(!script->compileAndGo);
|
||||
JS_ASSERT(!script->hasSingletons);
|
||||
@ -597,12 +597,14 @@ js::XDRScript(XDRState<mode> *xdr, HandleObject enclosingScope, HandleScript enc
|
||||
script->setArgumentsHasVarBinding();
|
||||
if (scriptBits & (1 << NeedsArgsObj))
|
||||
script->setNeedsArgsObj(true);
|
||||
if (scriptBits & (1 << IsGenerator))
|
||||
script->isGenerator = true;
|
||||
if (scriptBits & (1 << IsGeneratorExp))
|
||||
script->isGeneratorExp = true;
|
||||
if (scriptBits & (1 << IsLegacyGenerator))
|
||||
script->isLegacyGenerator = true;
|
||||
|
||||
if (scriptBits & (1 << IsLegacyGenerator)) {
|
||||
JS_ASSERT(!(scriptBits & (1 << IsStarGenerator)));
|
||||
script->setGeneratorKind(LegacyGenerator);
|
||||
} else if (scriptBits & (1 << IsStarGenerator))
|
||||
script->setGeneratorKind(StarGenerator);
|
||||
}
|
||||
|
||||
JS_STATIC_ASSERT(sizeof(jsbytecode) == 1);
|
||||
@ -1974,9 +1976,8 @@ JSScript::fullyInitFromEmitter(ExclusiveContext *cx, HandleScript script, Byteco
|
||||
RootedFunction fun(cx, NULL);
|
||||
if (funbox) {
|
||||
JS_ASSERT(!bce->script->noScriptRval);
|
||||
script->isGenerator = funbox->isGenerator();
|
||||
script->isGeneratorExp = funbox->inGenexpLambda;
|
||||
script->isLegacyGenerator = funbox->isLegacyGenerator();
|
||||
script->setGeneratorKind(funbox->generatorKind());
|
||||
script->setFunction(funbox->function());
|
||||
}
|
||||
|
||||
@ -2477,8 +2478,8 @@ js::CloneScript(JSContext *cx, HandleObject enclosingScope, HandleFunction fun,
|
||||
dst->funHasAnyAliasedFormal = src->funHasAnyAliasedFormal;
|
||||
dst->hasSingletons = src->hasSingletons;
|
||||
dst->treatAsRunOnce = src->treatAsRunOnce;
|
||||
dst->isGenerator = src->isGenerator;
|
||||
dst->isGeneratorExp = src->isGeneratorExp;
|
||||
dst->setGeneratorKind(src->generatorKind());
|
||||
|
||||
/* Copy over hints. */
|
||||
dst->shouldInline = src->shouldInline;
|
||||
@ -2919,7 +2920,7 @@ JSScript::argumentsOptimizationFailed(JSContext *cx, HandleScript script)
|
||||
if (script->needsArgsObj())
|
||||
return true;
|
||||
|
||||
JS_ASSERT(!script->isGenerator);
|
||||
JS_ASSERT(!script->isGenerator());
|
||||
|
||||
script->needsArgsObj_ = true;
|
||||
|
||||
@ -3011,6 +3012,7 @@ LazyScript::LazyScript(JSFunction *fun, void *table, uint32_t numFreeVariables,
|
||||
version_(version),
|
||||
numFreeVariables_(numFreeVariables),
|
||||
numInnerFunctions_(numInnerFunctions),
|
||||
generatorKindBits_(GeneratorKindAsBits(NotGenerator)),
|
||||
strict_(false),
|
||||
bindingsAccessedDynamically_(false),
|
||||
hasDebuggerStatement_(false),
|
||||
|
@ -414,6 +414,19 @@ class ScriptSourceObject : public JSObject
|
||||
static const uint32_t SOURCE_SLOT = 0;
|
||||
};
|
||||
|
||||
enum GeneratorKind { NotGenerator, LegacyGenerator, StarGenerator };
|
||||
|
||||
static inline unsigned
|
||||
GeneratorKindAsBits(GeneratorKind generatorKind) {
|
||||
return static_cast<unsigned>(generatorKind);
|
||||
}
|
||||
|
||||
static inline GeneratorKind
|
||||
GeneratorKindFromBits(unsigned val) {
|
||||
JS_ASSERT(val <= StarGenerator);
|
||||
return static_cast<GeneratorKind>(val);
|
||||
}
|
||||
|
||||
} /* namespace js */
|
||||
|
||||
class JSScript : public js::gc::Cell
|
||||
@ -519,7 +532,7 @@ class JSScript : public js::gc::Cell
|
||||
uint16_t nslots; /* vars plus maximum stack depth */
|
||||
uint16_t staticLevel;/* static level for display maintenance */
|
||||
|
||||
// 8-bit fields.
|
||||
// 4-bit fields.
|
||||
|
||||
public:
|
||||
// The kinds of the optional arrays.
|
||||
@ -528,15 +541,16 @@ class JSScript : public js::gc::Cell
|
||||
OBJECTS,
|
||||
REGEXPS,
|
||||
TRYNOTES,
|
||||
LIMIT
|
||||
ARRAY_KIND_BITS
|
||||
};
|
||||
|
||||
typedef uint8_t ArrayBitsT;
|
||||
|
||||
private:
|
||||
// The bits in this field indicate the presence/non-presence of several
|
||||
// optional arrays in |data|. See the comments above Create() for details.
|
||||
ArrayBitsT hasArrayBits;
|
||||
uint8_t hasArrayBits:4;
|
||||
|
||||
// The GeneratorKind of the script.
|
||||
uint8_t generatorKindBits_:4;
|
||||
|
||||
// 1-bit fields.
|
||||
|
||||
@ -589,14 +603,9 @@ class JSScript : public js::gc::Cell
|
||||
#endif
|
||||
bool invalidatedIdempotentCache:1; /* idempotent cache has triggered invalidation */
|
||||
|
||||
// All generators have isGenerator set to true.
|
||||
bool isGenerator:1;
|
||||
// If the generator was created implicitly via a generator expression,
|
||||
// isGeneratorExp will be true.
|
||||
bool isGeneratorExp:1;
|
||||
// Generators are either legacy-style (JS 1.7+ starless generators with
|
||||
// StopIteration), or ES6-style (function* with boxed return values).
|
||||
bool isLegacyGenerator:1;
|
||||
|
||||
bool hasScriptCounts:1;/* script has an entry in
|
||||
JSCompartment::scriptCountsMap */
|
||||
@ -647,6 +656,19 @@ class JSScript : public js::gc::Cell
|
||||
jsbytecode *argumentsBytecode() const { JS_ASSERT(code[0] == JSOP_ARGUMENTS); return code; }
|
||||
void setArgumentsHasVarBinding();
|
||||
|
||||
js::GeneratorKind generatorKind() const {
|
||||
return js::GeneratorKindFromBits(generatorKindBits_);
|
||||
}
|
||||
bool isGenerator() const { return generatorKind() != js::NotGenerator; }
|
||||
bool isLegacyGenerator() const { return generatorKind() == js::LegacyGenerator; }
|
||||
bool isStarGenerator() const { return generatorKind() == js::StarGenerator; }
|
||||
void setGeneratorKind(js::GeneratorKind kind) {
|
||||
// A script only gets its generator kind set as part of initialization,
|
||||
// so it can only transition from not being a generator.
|
||||
JS_ASSERT(!isGenerator());
|
||||
generatorKindBits_ = GeneratorKindAsBits(kind);
|
||||
}
|
||||
|
||||
/*
|
||||
* As an optimization, even when argsHasLocalBinding, the function prologue
|
||||
* may not need to create an arguments object. This is determined by
|
||||
@ -1038,7 +1060,8 @@ class JSScript : public js::gc::Cell
|
||||
void markChildren(JSTracer *trc);
|
||||
};
|
||||
|
||||
JS_STATIC_ASSERT(sizeof(JSScript::ArrayBitsT) * 8 >= JSScript::LIMIT);
|
||||
/* The array kind flags are stored in a 4-bit field; make sure they fit. */
|
||||
JS_STATIC_ASSERT(JSScript::ARRAY_KIND_BITS <= 4);
|
||||
|
||||
/* If this fails, add/remove padding within JSScript. */
|
||||
JS_STATIC_ASSERT(sizeof(JSScript) % js::gc::CellSize == 0);
|
||||
@ -1145,7 +1168,9 @@ class LazyScript : public js::gc::Cell
|
||||
uint32_t version_ : 8;
|
||||
|
||||
uint32_t numFreeVariables_ : 24;
|
||||
uint32_t numInnerFunctions_ : 26;
|
||||
uint32_t numInnerFunctions_ : 24;
|
||||
|
||||
uint32_t generatorKindBits_:2;
|
||||
|
||||
// N.B. These are booleans but need to be uint32_t to pack correctly on MSVC.
|
||||
uint32_t strict_ : 1;
|
||||
@ -1211,6 +1236,23 @@ class LazyScript : public js::gc::Cell
|
||||
return (HeapPtrFunction *)&freeVariables()[numFreeVariables()];
|
||||
}
|
||||
|
||||
GeneratorKind generatorKind() const { return GeneratorKindFromBits(generatorKindBits_); }
|
||||
|
||||
bool isGenerator() const { return generatorKind() != NotGenerator; }
|
||||
|
||||
bool isLegacyGenerator() const { return generatorKind() == LegacyGenerator; }
|
||||
|
||||
bool isStarGenerator() const { return generatorKind() == StarGenerator; }
|
||||
|
||||
void setGeneratorKind(GeneratorKind kind) {
|
||||
// A script only gets its generator kind set as part of initialization,
|
||||
// so it can only transition from NotGenerator.
|
||||
JS_ASSERT(!isGenerator());
|
||||
// Legacy generators cannot currently be lazy.
|
||||
JS_ASSERT(kind != LegacyGenerator);
|
||||
generatorKindBits_ = GeneratorKindAsBits(kind);
|
||||
}
|
||||
|
||||
bool strict() const {
|
||||
return strict_;
|
||||
}
|
||||
|
0
js/src/tests/ecma_6/Generators/shell.js
Normal file
0
js/src/tests/ecma_6/Generators/shell.js
Normal file
97
js/src/tests/ecma_6/Generators/syntax.js
Normal file
97
js/src/tests/ecma_6/Generators/syntax.js
Normal 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);
|
@ -7,11 +7,12 @@
|
||||
# jit_test.py -- Python harness for JavaScript trace tests.
|
||||
|
||||
from __future__ import print_function
|
||||
import os, sys, tempfile, traceback, time
|
||||
import os, posixpath, sys, tempfile, traceback, time
|
||||
import subprocess
|
||||
from subprocess import Popen, PIPE
|
||||
from threading import Thread
|
||||
import signal
|
||||
import StringIO
|
||||
|
||||
try:
|
||||
from multiprocessing import Process, Manager, cpu_count
|
||||
@ -152,15 +153,29 @@ class Test:
|
||||
|
||||
return test
|
||||
|
||||
def command(self, prefix):
|
||||
scriptdir_var = os.path.dirname(self.path);
|
||||
def command(self, prefix, libdir, remote_prefix=None):
|
||||
path = self.path
|
||||
if remote_prefix:
|
||||
path = self.path.replace(TEST_DIR, remote_prefix)
|
||||
|
||||
scriptdir_var = os.path.dirname(path);
|
||||
if not scriptdir_var.endswith('/'):
|
||||
scriptdir_var += '/'
|
||||
expr = ("const platform=%r; const libdir=%r; const scriptdir=%r"
|
||||
% (sys.platform, LIB_DIR, scriptdir_var))
|
||||
|
||||
# Platforms where subprocess immediately invokes exec do not care
|
||||
# whether we use double or single quotes. On windows and when using
|
||||
# a remote device, however, we have to be careful to use the quote
|
||||
# style that is the opposite of what the exec wrapper uses.
|
||||
# This uses %r to get single quotes on windows and special cases
|
||||
# the remote device.
|
||||
fmt = 'const platform=%r; const libdir=%r; const scriptdir=%r'
|
||||
if remote_prefix:
|
||||
fmt = 'const platform="%s"; const libdir="%s"; const scriptdir="%s"'
|
||||
expr = fmt % (sys.platform, libdir, scriptdir_var)
|
||||
|
||||
# We may have specified '-a' or '-d' twice: once via --jitflags, once
|
||||
# via the "|jit-test|" line. Remove dups because they are toggles.
|
||||
cmd = prefix + list(set(self.jitflags)) + ['-e', expr, '-f', self.path]
|
||||
cmd = prefix + list(set(self.jitflags)) + ['-e', expr, '-f', path]
|
||||
if self.valgrind:
|
||||
cmd = self.VALGRIND_CMD + cmd
|
||||
return cmd
|
||||
@ -268,7 +283,7 @@ def run_cmd_avoid_stdio(cmdline, env, timeout):
|
||||
return read_and_unlink(stdoutPath), read_and_unlink(stderrPath), code
|
||||
|
||||
def run_test(test, prefix, options):
|
||||
cmd = test.command(prefix)
|
||||
cmd = test.command(prefix, LIB_DIR)
|
||||
if options.show_cmd:
|
||||
print(subprocess.list2cmdline(cmd))
|
||||
|
||||
@ -284,6 +299,26 @@ def run_test(test, prefix, options):
|
||||
out, err, code, timed_out = run(cmd, env, options.timeout)
|
||||
return TestOutput(test, cmd, out, err, code, None, timed_out)
|
||||
|
||||
def run_test_remote(test, device, prefix, options):
|
||||
cmd = test.command(prefix, posixpath.join(options.remote_test_root, 'lib/'), posixpath.join(options.remote_test_root, 'tests'))
|
||||
if options.show_cmd:
|
||||
print(subprocess.list2cmdline(cmd))
|
||||
|
||||
env = {}
|
||||
if test.tz_pacific:
|
||||
env['TZ'] = 'PST8PDT'
|
||||
|
||||
env['LD_LIBRARY_PATH'] = options.remote_test_root
|
||||
|
||||
buf = StringIO.StringIO()
|
||||
returncode = device.shell(cmd, buf, env=env, cwd=options.remote_test_root,
|
||||
timeout=int(options.timeout))
|
||||
|
||||
out = buf.getvalue()
|
||||
# We can't distinguish between stdout and stderr so we pass
|
||||
# the same buffer to both.
|
||||
return TestOutput(test, cmd, out, out, returncode, None, False)
|
||||
|
||||
def check_output(out, err, rc, test):
|
||||
if test.expect_error:
|
||||
# The shell exits with code 3 on uncaught exceptions.
|
||||
@ -541,6 +576,58 @@ def run_tests(tests, prefix, options):
|
||||
ok = process_test_results(gen, len(tests), options)
|
||||
return ok
|
||||
|
||||
def get_remote_results(tests, device, prefix, options):
|
||||
for test in tests:
|
||||
yield run_test_remote(test, device, prefix, options)
|
||||
|
||||
def push_libs(options, device):
|
||||
# This saves considerable time in pushing unnecessary libraries
|
||||
# to the device but needs to be updated if the dependencies change.
|
||||
required_libs = ['libnss3.so', 'libmozglue.so']
|
||||
|
||||
for file in os.listdir(options.local_lib):
|
||||
if file in required_libs:
|
||||
remote_file = posixpath.join(options.remote_test_root, file)
|
||||
device.pushFile(os.path.join(options.local_lib, file), remote_file)
|
||||
|
||||
def push_progs(options, device, progs):
|
||||
for local_file in progs:
|
||||
remote_file = posixpath.join(options.remote_test_root, os.path.basename(local_file))
|
||||
device.pushFile(local_file, remote_file)
|
||||
|
||||
def run_tests_remote(tests, prefix, options):
|
||||
# Setup device with everything needed to run our tests.
|
||||
from mozdevice import devicemanager, devicemanagerADB, devicemanagerSUT
|
||||
|
||||
if options.device_transport == 'adb':
|
||||
if options.device_ip:
|
||||
dm = devicemanagerADB.DeviceManagerADB(options.device_ip, options.devicePort, packageName=None, deviceRoot=options.remoteTestRoot)
|
||||
else:
|
||||
dm = devicemanagerADB.DeviceManagerADB(packageName=None, deviceRoot=options.remote_test_root)
|
||||
else:
|
||||
dm = devicemanagerSUT.DeviceManagerSUT(options.device_ip, options.device_port, deviceRoot=options.remote_test_root)
|
||||
if options.device_ip == None:
|
||||
print('Error: you must provide a device IP to connect to via the --device option')
|
||||
sys.exit(1)
|
||||
|
||||
# Update the test root to point to our test directory.
|
||||
options.remote_test_root = posixpath.join(options.remote_test_root, 'jit-tests')
|
||||
|
||||
# Push js shell and libraries.
|
||||
if dm.dirExists(options.remote_test_root):
|
||||
dm.removeDir(options.remote_test_root)
|
||||
dm.mkDir(options.remote_test_root)
|
||||
push_libs(options, dm)
|
||||
push_progs(options, dm, [prefix[0]])
|
||||
dm.chmodDir(options.remote_test_root)
|
||||
dm.pushDir(os.path.dirname(TEST_DIR), options.remote_test_root)
|
||||
prefix[0] = os.path.join(options.remote_test_root, 'js')
|
||||
|
||||
# Run all tests.
|
||||
gen = get_remote_results(tests, dm, prefix, options)
|
||||
ok = process_test_results(gen, len(tests), options)
|
||||
return ok
|
||||
|
||||
def parse_jitflags(options):
|
||||
jitflags = [ [ '-' + flag for flag in flags ]
|
||||
for flags in options.jitflags.split(',') ]
|
||||
|
@ -152,6 +152,7 @@
|
||||
macro(void0, void0, "(void 0)") \
|
||||
macro(watch, watch, "watch") \
|
||||
macro(writable, writable, "writable") \
|
||||
macro(yield, yield, "yield") \
|
||||
/* Type names must be contiguous and ordered; see js::TypeName. */ \
|
||||
macro(undefined, undefined, "undefined") \
|
||||
macro(object, object, "object") \
|
||||
|
@ -72,8 +72,12 @@
|
||||
macro(protected, protected_, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \
|
||||
macro(public, public_, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \
|
||||
macro(static, static_, TOK_STRICT_RESERVED, JSVERSION_DEFAULT) \
|
||||
/* ES5 future reserved keyword in strict mode, keyword in JS1.7 even when not strict. */ \
|
||||
macro(yield, yield, TOK_YIELD, JSVERSION_1_7) \
|
||||
/* \
|
||||
* ES5 future reserved keyword in strict mode, keyword in JS1.7 even when \
|
||||
* not strict, keyword inside function* in all versions. Punt logic to \
|
||||
* parser. \
|
||||
*/ \
|
||||
macro(yield, yield, TOK_YIELD, JSVERSION_DEFAULT) \
|
||||
/* Various conditional keywords. */ \
|
||||
FOR_CONST_KEYWORD(macro) \
|
||||
FOR_LET_KEYWORD(macro)
|
||||
|
@ -118,7 +118,6 @@ if (!isWindows) {
|
||||
tests.push([ 'bug240933-2.html' , 'bug240933-1-ref.html' ]); // bug 681162
|
||||
tests.push([ 'bug389321-1.html' , 'bug389321-1-ref.html' ]); // bug 683163
|
||||
tests.push([ 'bug482484.html' , 'bug482484-ref.html' ]); // bug 688575
|
||||
tests.push([ 'bug512295-1.html' , 'bug512295-1-ref.html' ]); // bug 681152
|
||||
tests.push([ 'bug512295-2.html' , 'bug512295-2-ref.html' ]); // bug 681331
|
||||
tests.push([ 'bug597519-1.html' , 'bug597519-1-ref.html' ]); // bug 680579
|
||||
tests.push([ 'bug602141-1.html' , 'bug602141-1-ref.html' ]); // bug 681334
|
||||
@ -142,6 +141,7 @@ if (!isWindows) {
|
||||
if (!isWindows && !isOSXMtnLion) {
|
||||
tests.push([ 'bug106855-1.html' , 'bug106855-1-ref.html' ]); // bug 682837
|
||||
tests.push([ 'bug106855-2.html' , 'bug106855-1-ref.html?' ]); // bug 681138
|
||||
tests.push([ 'bug512295-1.html' , 'bug512295-1-ref.html' ]); // bug 681152
|
||||
}
|
||||
*/
|
||||
|
||||
|
@ -129,8 +129,6 @@ struct CachedOffsetForFrame {
|
||||
bool mCanCacheFrameOffset; // cached frame offset is valid?
|
||||
};
|
||||
|
||||
static RangeData sEmptyData(nullptr);
|
||||
|
||||
// Stack-class to turn on/off selection batching for table selection
|
||||
class MOZ_STACK_CLASS nsSelectionBatcher MOZ_FINAL
|
||||
{
|
||||
@ -4525,7 +4523,8 @@ Selection::GetRangeCount(int32_t* aRangeCount)
|
||||
NS_IMETHODIMP
|
||||
Selection::GetRangeAt(int32_t aIndex, nsIDOMRange** aReturn)
|
||||
{
|
||||
*aReturn = mRanges.SafeElementAt(aIndex, sEmptyData).mRange;
|
||||
RangeData empty(nullptr);
|
||||
*aReturn = mRanges.SafeElementAt(aIndex, empty).mRange;
|
||||
if (!*aReturn) {
|
||||
return NS_ERROR_DOM_INDEX_SIZE_ERR;
|
||||
}
|
||||
@ -4538,7 +4537,8 @@ Selection::GetRangeAt(int32_t aIndex, nsIDOMRange** aReturn)
|
||||
nsRange*
|
||||
Selection::GetRangeAt(int32_t aIndex)
|
||||
{
|
||||
return mRanges.SafeElementAt(aIndex, sEmptyData).mRange;
|
||||
RangeData empty(nullptr);
|
||||
return mRanges.SafeElementAt(aIndex, empty).mRange;
|
||||
}
|
||||
|
||||
/*
|
||||
|
7
layout/reftests/bugs/897491-1-ref.html
Normal file
7
layout/reftests/bugs/897491-1-ref.html
Normal 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>
|
15
layout/reftests/bugs/897491-1.html
Normal file
15
layout/reftests/bugs/897491-1.html
Normal 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>
|
7
layout/reftests/bugs/897491-2-ref.html
Normal file
7
layout/reftests/bugs/897491-2-ref.html
Normal 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>
|
15
layout/reftests/bugs/897491-2.html
Normal file
15
layout/reftests/bugs/897491-2.html
Normal 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>
|
@ -1772,3 +1772,5 @@ test-pref(layout.css.flexbox.enabled,true) == 849407-1.html 849407-1-ref.html
|
||||
== 883987-1f.html 883987-1-ref.html
|
||||
== 890495-1.html 890495-1-ref.html
|
||||
== 894931-1.html 894931-1-ref.html
|
||||
== 897491-1.html 897491-1-ref.html
|
||||
== 897491-2.html 897491-2-ref.html
|
||||
|
@ -25,8 +25,8 @@ skip-if(B2G) asserts-if(cocoaWidget,0-2) == size-change-1.html size-change-1-ref
|
||||
== text-ltr-alignment-test.html text-ltr-alignment-ref.html
|
||||
== text-rtl-alignment-test.html text-rtl-alignment-ref.html
|
||||
|
||||
== text-horzline-with-bottom.html text-horzline.html
|
||||
== text-horzline-with-top.html text-horzline.html
|
||||
fuzzy-if(B2G&&azureSkiaGL,1,256) == text-horzline-with-bottom.html text-horzline.html
|
||||
fuzzy-if(B2G&&azureSkiaGL,1,256) == text-horzline-with-top.html text-horzline.html
|
||||
|
||||
!= text-big-stroke.html text-blank.html
|
||||
!= text-big-stroke.html text-big-fill.html
|
||||
|
@ -200,7 +200,7 @@ pref(layout.css.masking.enabled,true) fuzzy-if(d2d,1,6400) == mask-type-04.svg m
|
||||
== nested-viewBox-01.svg pass.svg
|
||||
== nesting-invalid-01.svg nesting-invalid-01-ref.svg
|
||||
== non-scaling-stroke-01.svg non-scaling-stroke-01-ref.svg
|
||||
fuzzy-if(Android,1,99) fuzzy-if(!contentSameGfxBackendAsCanvas,9,99) == non-scaling-stroke-02.svg non-scaling-stroke-02-ref.svg
|
||||
fuzzy-if(Android||B2G,1,99) fuzzy-if(!contentSameGfxBackendAsCanvas,9,99) == non-scaling-stroke-02.svg non-scaling-stroke-02-ref.svg
|
||||
== non-scaling-stroke-03.svg non-scaling-stroke-03-ref.svg
|
||||
== objectBoundingBox-and-clipPath.svg pass.svg
|
||||
# Bug 588684
|
||||
|
@ -538,14 +538,6 @@ function BuildConditionSandbox(aURL) {
|
||||
sandbox.smallScreen = true;
|
||||
}
|
||||
|
||||
#if REFTEST_B2G
|
||||
// XXX nsIGfxInfo isn't available in B2G
|
||||
sandbox.d2d = false;
|
||||
sandbox.azureQuartz = false;
|
||||
sandbox.azureSkia = false;
|
||||
sandbox.azureSkiaGL = false;
|
||||
sandbox.contentSameGfxBackendAsCanvas = false;
|
||||
#else
|
||||
var gfxInfo = (NS_GFXINFO_CONTRACTID in CC) && CC[NS_GFXINFO_CONTRACTID].getService(CI.nsIGfxInfo);
|
||||
try {
|
||||
sandbox.d2d = gfxInfo.D2DEnabled;
|
||||
@ -559,7 +551,6 @@ function BuildConditionSandbox(aURL) {
|
||||
// true if we are using the same Azure backend for rendering canvas and content
|
||||
sandbox.contentSameGfxBackendAsCanvas = info.AzureContentBackend == info.AzureCanvasBackend
|
||||
|| (info.AzureContentBackend == "none" && info.AzureCanvasBackend == "cairo");
|
||||
#endif
|
||||
|
||||
sandbox.layersGPUAccelerated =
|
||||
gWindowUtils.layerManagerType != "Basic";
|
||||
|
@ -38,9 +38,9 @@ public class BrowserDB {
|
||||
|
||||
public Cursor filter(ContentResolver cr, CharSequence constraint, int limit);
|
||||
|
||||
// This should onlyl return frecent sites, BrowserDB.getTopSites will do the
|
||||
// This should only return frecent bookmarks, BrowserDB.getTopBookmarks will do the
|
||||
// work to combine that list with the pinned sites list
|
||||
public Cursor getTopSites(ContentResolver cr, int limit);
|
||||
public Cursor getTopBookmarks(ContentResolver cr, int limit);
|
||||
|
||||
public void updateVisitedHistory(ContentResolver cr, String uri);
|
||||
|
||||
@ -137,12 +137,12 @@ public class BrowserDB {
|
||||
return sDb.filter(cr, constraint, limit);
|
||||
}
|
||||
|
||||
public static Cursor getTopSites(ContentResolver cr, int limit) {
|
||||
// Note this is not a single query anymore, but actually returns a mixture of two queries, one for topSites
|
||||
// and one for pinned sites
|
||||
Cursor topSites = sDb.getTopSites(cr, limit);
|
||||
public static Cursor getTopBookmarks(ContentResolver cr, int limit) {
|
||||
// Note this is not a single query anymore, but actually returns a mixture of two queries,
|
||||
// one for top bookmarks, and one for pinned sites (which are actually bookmarks as well).
|
||||
Cursor topBookmarks = sDb.getTopBookmarks(cr, limit);
|
||||
Cursor pinnedSites = sDb.getPinnedSites(cr, limit);
|
||||
return new TopSitesCursorWrapper(pinnedSites, topSites, limit);
|
||||
return new TopSitesCursorWrapper(pinnedSites, topBookmarks, limit);
|
||||
}
|
||||
|
||||
public static void updateVisitedHistory(ContentResolver cr, String uri) {
|
||||
|
@ -231,9 +231,13 @@ public class LocalBrowserDB implements BrowserDB.BrowserDBIface {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor getTopSites(ContentResolver cr, int limit) {
|
||||
// Filter out sites that are pinned
|
||||
String selection = DBUtils.concatenateWhere("", Combined.URL + " NOT IN (SELECT " +
|
||||
public Cursor getTopBookmarks(ContentResolver cr, int limit) {
|
||||
// Only select bookmarks. Unfortunately, we need to query the combined view,
|
||||
// instead of just the bookmarks table, in order to do the frecency calculation.
|
||||
String selection = Combined.BOOKMARK_ID + " IS NOT NULL";
|
||||
|
||||
// Filter out sites that are pinned.
|
||||
selection = DBUtils.concatenateWhere(selection, Combined.URL + " NOT IN (SELECT " +
|
||||
Bookmarks.URL + " FROM bookmarks WHERE " +
|
||||
DBUtils.qualifyColumn("bookmarks", Bookmarks.PARENT) + " == ? AND " +
|
||||
DBUtils.qualifyColumn("bookmarks", Bookmarks.IS_DELETED) + " == 0)");
|
||||
|
@ -60,13 +60,18 @@ class BookmarksListAdapter extends MultiTypeCursorAdapter {
|
||||
|
||||
/**
|
||||
* Moves to parent folder, if one exists.
|
||||
*
|
||||
* @return Whether the adapter successfully moved to a parent folder.
|
||||
*/
|
||||
public void moveToParentFolder() {
|
||||
public boolean moveToParentFolder() {
|
||||
// If we're already at the root, we can't move to a parent folder
|
||||
if (mParentStack.size() != 1) {
|
||||
mParentStack.removeFirst();
|
||||
refreshCurrentFolder();
|
||||
if (mParentStack.size() == 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mParentStack.removeFirst();
|
||||
refreshCurrentFolder();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -13,6 +13,7 @@ import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
@ -56,6 +57,17 @@ public class BookmarksListView extends HomeListView
|
||||
super.onAttachedToWindow();
|
||||
|
||||
setOnItemClickListener(this);
|
||||
|
||||
setOnKeyListener(new View.OnKeyListener() {
|
||||
@Override
|
||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
||||
// If the user hit the BACK key, try to move to the parent folder.
|
||||
if (keyCode == KeyEvent.KEYCODE_BACK) {
|
||||
return getBookmarksListAdapter().moveToParentFolder();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -101,14 +113,7 @@ public class BookmarksListView extends HomeListView
|
||||
// Absolute position for the adapter.
|
||||
position -= headerCount;
|
||||
|
||||
BookmarksListAdapter adapter;
|
||||
ListAdapter listAdapter = getAdapter();
|
||||
if (listAdapter instanceof HeaderViewListAdapter) {
|
||||
adapter = (BookmarksListAdapter) ((HeaderViewListAdapter) listAdapter).getWrappedAdapter();
|
||||
} else {
|
||||
adapter = (BookmarksListAdapter) listAdapter;
|
||||
}
|
||||
|
||||
final BookmarksListAdapter adapter = getBookmarksListAdapter();
|
||||
if (adapter.isShowingChildFolder()) {
|
||||
if (position == 0) {
|
||||
// If we tap on an opened folder, move back to parent folder.
|
||||
@ -141,4 +146,15 @@ public class BookmarksListView extends HomeListView
|
||||
getOnUrlOpenListener().onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.ALLOW_SWITCH_TO_TAB));
|
||||
}
|
||||
}
|
||||
|
||||
private BookmarksListAdapter getBookmarksListAdapter() {
|
||||
BookmarksListAdapter adapter;
|
||||
ListAdapter listAdapter = getAdapter();
|
||||
if (listAdapter instanceof HeaderViewListAdapter) {
|
||||
adapter = (BookmarksListAdapter) ((HeaderViewListAdapter) listAdapter).getWrappedAdapter();
|
||||
} else {
|
||||
adapter = (BookmarksListAdapter) listAdapter;
|
||||
}
|
||||
return adapter;
|
||||
}
|
||||
}
|
||||
|
@ -149,6 +149,10 @@ public class BookmarksPage extends HomeFragment {
|
||||
});
|
||||
mList.setAdapter(mListAdapter);
|
||||
|
||||
// Invalidate the cached value that keeps track of whether or
|
||||
// not desktop bookmarks (or reading list items) exist.
|
||||
BrowserDB.invalidateCachedState();
|
||||
|
||||
// Create callbacks before the initial loader is started.
|
||||
mLoaderCallbacks = new CursorLoaderCallbacks(activity, getLoaderManager());
|
||||
mThumbnailsLoaderCallbacks = new ThumbnailsLoaderCallbacks();
|
||||
@ -362,7 +366,7 @@ public class BookmarksPage extends HomeFragment {
|
||||
@Override
|
||||
public Cursor loadCursor() {
|
||||
final int max = getContext().getResources().getInteger(R.integer.number_of_top_sites);
|
||||
return BrowserDB.getTopSites(getContext().getContentResolver(), max);
|
||||
return BrowserDB.getTopBookmarks(getContext().getContentResolver(), max);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@
|
||||
package org.mozilla.gecko.home;
|
||||
|
||||
import org.mozilla.gecko.EditBookmarkDialog;
|
||||
import org.mozilla.gecko.Favicons;
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.GeckoEvent;
|
||||
import org.mozilla.gecko.R;
|
||||
@ -132,14 +133,8 @@ abstract class HomeFragment extends Fragment {
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME: bug 897772
|
||||
Bitmap bitmap = null;
|
||||
if (info.favicon != null) {
|
||||
bitmap = BitmapUtils.decodeByteArray(info.favicon);
|
||||
}
|
||||
|
||||
String shortcutTitle = TextUtils.isEmpty(info.title) ? info.url.replaceAll(REGEX_URL_TO_TITLE, "") : info.title;
|
||||
GeckoAppShell.createShortcut(shortcutTitle, info.url, bitmap, "");
|
||||
new AddToLauncherTask(info.url, shortcutTitle).execute();
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -225,6 +220,35 @@ abstract class HomeFragment extends Fragment {
|
||||
}
|
||||
}
|
||||
|
||||
private static class AddToLauncherTask extends UiAsyncTask<Void, Void, String> {
|
||||
private final String mUrl;
|
||||
private final String mTitle;
|
||||
|
||||
public AddToLauncherTask(String url, String title) {
|
||||
super(ThreadUtils.getBackgroundHandler());
|
||||
|
||||
mUrl = url;
|
||||
mTitle = title;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String doInBackground(Void... params) {
|
||||
return Favicons.getInstance().getFaviconUrlForPageUrl(mUrl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPostExecute(String faviconUrl) {
|
||||
Favicons.OnFaviconLoadedListener listener = new Favicons.OnFaviconLoadedListener() {
|
||||
@Override
|
||||
public void onFaviconLoaded(String url, Bitmap favicon) {
|
||||
GeckoAppShell.createShortcut(mTitle, mUrl, favicon, "");
|
||||
}
|
||||
};
|
||||
|
||||
Favicons.getInstance().loadFavicon(mUrl, faviconUrl, 0, listener);
|
||||
}
|
||||
}
|
||||
|
||||
private static class RemoveBookmarkTask extends UiAsyncTask<Void, Void, Void> {
|
||||
private final Context mContext;
|
||||
private final int mId;
|
||||
|
@ -113,7 +113,6 @@ public class HomeListView extends ListView
|
||||
public int bookmarkId;
|
||||
public int historyId;
|
||||
public String url;
|
||||
public byte[] favicon;
|
||||
public String title;
|
||||
public int display;
|
||||
public boolean isFolder;
|
||||
@ -165,13 +164,6 @@ public class HomeListView extends ListView
|
||||
historyId = -1;
|
||||
}
|
||||
|
||||
final int faviconCol = cursor.getColumnIndex(Combined.FAVICON);
|
||||
if (faviconCol != -1) {
|
||||
favicon = cursor.getBlob(faviconCol);
|
||||
} else {
|
||||
favicon = null;
|
||||
}
|
||||
|
||||
// We only have the parent column in cursors from getBookmarksInFolder.
|
||||
final int parentCol = cursor.getColumnIndex(Bookmarks.PARENT);
|
||||
if (parentCol != -1) {
|
||||
|
@ -587,11 +587,11 @@ AboutReader.prototype = {
|
||||
|
||||
let start = 0;
|
||||
|
||||
if (host.startsWith("www"))
|
||||
if (host.startsWith("www."))
|
||||
start = 4;
|
||||
else if (host.startsWith("m"))
|
||||
else if (host.startsWith("m."))
|
||||
start = 2;
|
||||
else if (host.startsWith("mobile"))
|
||||
else if (host.startsWith("mobile."))
|
||||
start = 7;
|
||||
|
||||
return host.substring(start);
|
||||
|
@ -38,6 +38,7 @@
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#ifdef MOZ_PEERCONNECTION
|
||||
#include "mtransport/runnable_utils.h"
|
||||
#endif
|
||||
@ -78,7 +79,7 @@ static bool sctp_initialized;
|
||||
namespace mozilla {
|
||||
|
||||
class DataChannelShutdown;
|
||||
nsRefPtr<DataChannelShutdown> gDataChannelShutdown;
|
||||
StaticRefPtr<DataChannelShutdown> gDataChannelShutdown;
|
||||
|
||||
class DataChannelShutdown : public nsIObserver
|
||||
{
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user