зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to fx-team
This commit is contained in:
Коммит
cc7e315adc
|
@ -699,12 +699,24 @@ var Output = {
|
|||
}
|
||||
},
|
||||
|
||||
get androidBridge() {
|
||||
delete this.androidBridge;
|
||||
if (Utils.MozBuildApp === 'mobile/android') {
|
||||
this.androidBridge = Cc['@mozilla.org/android/bridge;1'].getService(
|
||||
Ci.nsIAndroidBridge);
|
||||
} else {
|
||||
this.androidBridge = null;
|
||||
}
|
||||
return this.androidBridge;
|
||||
},
|
||||
|
||||
Android: function Android(aDetails, aBrowser) {
|
||||
const ANDROID_VIEW_TEXT_CHANGED = 0x10;
|
||||
const ANDROID_VIEW_TEXT_SELECTION_CHANGED = 0x2000;
|
||||
|
||||
if (!this._bridge)
|
||||
this._bridge = Cc['@mozilla.org/android/bridge;1'].getService(Ci.nsIAndroidBridge);
|
||||
if (!this.androidBridge) {
|
||||
return;
|
||||
}
|
||||
|
||||
for each (let androidEvent in aDetails) {
|
||||
androidEvent.type = 'Accessibility:Event';
|
||||
|
@ -722,7 +734,7 @@ var Output = {
|
|||
androidEvent.brailleOutput = this.brailleState.init(androidEvent.brailleOutput);
|
||||
break;
|
||||
}
|
||||
this._bridge.handleGeckoMessage(JSON.stringify(androidEvent));
|
||||
this.androidBridge.handleGeckoMessage(JSON.stringify(androidEvent));
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -21,6 +21,10 @@ XPCOMUtils.defineLazyModuleGetter(this, 'Presentation',
|
|||
|
||||
this.EXPORTED_SYMBOLS = ['ContentControl'];
|
||||
|
||||
const MOVEMENT_GRANULARITY_CHARACTER = 1;
|
||||
const MOVEMENT_GRANULARITY_WORD = 2;
|
||||
const MOVEMENT_GRANULARITY_PARAGRAPH = 8;
|
||||
|
||||
this.ContentControl = function ContentControl(aContentScope) {
|
||||
this._contentScope = Cu.getWeakReference(aContentScope);
|
||||
this._vcCache = new WeakMap();
|
||||
|
@ -31,16 +35,19 @@ this.ContentControl.prototype = {
|
|||
messagesOfInterest: ['AccessFu:MoveCursor',
|
||||
'AccessFu:ClearCursor',
|
||||
'AccessFu:MoveToPoint',
|
||||
'AccessFu:AutoMove'],
|
||||
'AccessFu:AutoMove',
|
||||
'AccessFu:Activate',
|
||||
'AccessFu:MoveCaret',
|
||||
'AccessFu:MoveByGranularity'],
|
||||
|
||||
start: function ContentControl_start() {
|
||||
start: function cc_start() {
|
||||
let cs = this._contentScope.get();
|
||||
for (let message of this.messagesOfInterest) {
|
||||
cs.addMessageListener(message, this);
|
||||
}
|
||||
},
|
||||
|
||||
stop: function ContentControl_stop() {
|
||||
stop: function cc_stop() {
|
||||
let cs = this._contentScope.get();
|
||||
for (let message of this.messagesOfInterest) {
|
||||
cs.removeMessageListener(message, this);
|
||||
|
@ -59,29 +66,19 @@ this.ContentControl.prototype = {
|
|||
return Utils.getVirtualCursor(this.document);
|
||||
},
|
||||
|
||||
receiveMessage: function ContentControl_receiveMessage(aMessage) {
|
||||
receiveMessage: function cc_receiveMessage(aMessage) {
|
||||
Logger.debug(() => {
|
||||
return ['ContentControl.receiveMessage',
|
||||
this.document.location.toString(),
|
||||
aMessage.name,
|
||||
JSON.stringify(aMessage.json)];
|
||||
});
|
||||
|
||||
try {
|
||||
switch (aMessage.name) {
|
||||
case 'AccessFu:MoveCursor':
|
||||
this.handleMove(aMessage);
|
||||
break;
|
||||
case 'AccessFu:ClearCursor':
|
||||
this.handleClear(aMessage);
|
||||
break;
|
||||
case 'AccessFu:MoveToPoint':
|
||||
this.handleMoveToPoint(aMessage);
|
||||
break;
|
||||
case 'AccessFu:AutoMove':
|
||||
this.handleAutoMove(aMessage);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
let func = this['handle' + aMessage.name.slice(9)]; // 'AccessFu:'.length
|
||||
if (func) {
|
||||
func.bind(this)(aMessage);
|
||||
} else {
|
||||
Logger.warning('ContentControl: Unhandled message:', aMessage.name);
|
||||
}
|
||||
} catch (x) {
|
||||
Logger.logException(
|
||||
|
@ -89,7 +86,7 @@ this.ContentControl.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
handleMove: function ContentControl_handleMove(aMessage) {
|
||||
handleMoveCursor: function cc_handleMoveCursor(aMessage) {
|
||||
let origin = aMessage.json.origin;
|
||||
let action = aMessage.json.action;
|
||||
let vc = this.vc;
|
||||
|
@ -127,7 +124,7 @@ this.ContentControl.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
handleMoveToPoint: function ContentControl_handleMoveToPoint(aMessage) {
|
||||
handleMoveToPoint: function cc_handleMoveToPoint(aMessage) {
|
||||
let [x, y] = [aMessage.json.x, aMessage.json.y];
|
||||
let rule = TraversalRules[aMessage.json.rule];
|
||||
let vc = this.vc;
|
||||
|
@ -141,16 +138,169 @@ this.ContentControl.prototype = {
|
|||
this.sendToChild(vc, aMessage, delta);
|
||||
},
|
||||
|
||||
handleClear: function ContentControl_handleClear(aMessage) {
|
||||
handleClearCursor: function cc_handleClearCursor(aMessage) {
|
||||
this.sendToChild(this.vc, aMessage);
|
||||
this.vc.position = null;
|
||||
},
|
||||
|
||||
handleAutoMove: function ContentControl_handleAutoMove(aMessage) {
|
||||
handleAutoMove: function cc_handleAutoMove(aMessage) {
|
||||
this.autoMove(null, aMessage.json);
|
||||
},
|
||||
|
||||
getChildCursor: function ContentControl_getChildCursor(aAccessible) {
|
||||
handleActivate: function cc_handleActivate(aMessage) {
|
||||
let activateAccessible = (aAccessible) => {
|
||||
Logger.debug(() => {
|
||||
return ['activateAccessible', Logger.accessibleToString(aAccessible)];
|
||||
});
|
||||
try {
|
||||
if (aMessage.json.activateIfKey &&
|
||||
aAccessible.role != Roles.KEY) {
|
||||
// Only activate keys, don't do anything on other objects.
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
// accessible is invalid. Silently fail.
|
||||
return;
|
||||
}
|
||||
|
||||
if (aAccessible.actionCount > 0) {
|
||||
aAccessible.doAction(0);
|
||||
} else {
|
||||
let control = Utils.getEmbeddedControl(aAccessible);
|
||||
if (control && control.actionCount > 0) {
|
||||
control.doAction(0);
|
||||
}
|
||||
|
||||
// XXX Some mobile widget sets do not expose actions properly
|
||||
// (via ARIA roles, etc.), so we need to generate a click.
|
||||
// Could possibly be made simpler in the future. Maybe core
|
||||
// engine could expose nsCoreUtiles::DispatchMouseEvent()?
|
||||
let docAcc = Utils.AccRetrieval.getAccessibleFor(content.document);
|
||||
let docX = {}, docY = {}, docW = {}, docH = {};
|
||||
docAcc.getBounds(docX, docY, docW, docH);
|
||||
|
||||
let objX = {}, objY = {}, objW = {}, objH = {};
|
||||
aAccessible.getBounds(objX, objY, objW, objH);
|
||||
|
||||
let x = Math.round((objX.value - docX.value) + objW.value / 2);
|
||||
let y = Math.round((objY.value - docY.value) + objH.value / 2);
|
||||
|
||||
let node = aAccessible.DOMNode || aAccessible.parent.DOMNode;
|
||||
|
||||
for (let eventType of ['mousedown', 'mouseup']) {
|
||||
let evt = content.document.createEvent('MouseEvents');
|
||||
evt.initMouseEvent(eventType, true, true, content,
|
||||
x, y, 0, 0, 0, false, false, false, false, 0, null);
|
||||
node.dispatchEvent(evt);
|
||||
}
|
||||
}
|
||||
|
||||
if (aAccessible.role !== Roles.KEY) {
|
||||
// Keys will typically have a sound of their own.
|
||||
this._contentScope.get().sendAsyncMessage('AccessFu:Present',
|
||||
Presentation.actionInvoked(aAccessible, 'click'));
|
||||
}
|
||||
};
|
||||
|
||||
let focusedAcc = Utils.AccRetrieval.getAccessibleFor(
|
||||
this.document.activeElement);
|
||||
if (focusedAcc && focusedAcc.role === Roles.ENTRY) {
|
||||
let accText = focusedAcc.QueryInterface(Ci.nsIAccessibleText);
|
||||
let oldOffset = accText.caretOffset;
|
||||
let newOffset = aMessage.json.offset;
|
||||
let text = accText.getText(0, accText.characterCount);
|
||||
|
||||
if (newOffset >= 0 && newOffset <= accText.characterCount) {
|
||||
accText.caretOffset = newOffset;
|
||||
}
|
||||
|
||||
this.presentCaretChange(text, oldOffset, accText.caretOffset);
|
||||
return;
|
||||
}
|
||||
|
||||
let vc = this.vc;
|
||||
if (!this.sendToChild(vc, aMessage)) {
|
||||
activateAccessible(vc.position);
|
||||
}
|
||||
},
|
||||
|
||||
handleMoveByGranularity: function cc_handleMoveByGranularity(aMessage) {
|
||||
// XXX: Add sendToChild. Right now this is only used in Android, so no need.
|
||||
let direction = aMessage.json.direction;
|
||||
let granularity;
|
||||
|
||||
switch(aMessage.json.granularity) {
|
||||
case MOVEMENT_GRANULARITY_CHARACTER:
|
||||
granularity = Ci.nsIAccessiblePivot.CHAR_BOUNDARY;
|
||||
break;
|
||||
case MOVEMENT_GRANULARITY_WORD:
|
||||
granularity = Ci.nsIAccessiblePivot.WORD_BOUNDARY;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (direction === 'Previous') {
|
||||
this.vc.movePreviousByText(granularity);
|
||||
} else if (direction === 'Next') {
|
||||
this.vc.moveNextByText(granularity);
|
||||
}
|
||||
},
|
||||
|
||||
presentCaretChange: function cc_presentCaretChange(
|
||||
aText, aOldOffset, aNewOffset) {
|
||||
if (aOldOffset !== aNewOffset) {
|
||||
let msg = Presentation.textSelectionChanged(aText, aNewOffset, aNewOffset,
|
||||
aOldOffset, aOldOffset, true);
|
||||
this._contentScope.get().sendAsyncMessage('AccessFu:Present', msg);
|
||||
}
|
||||
},
|
||||
|
||||
handleMoveCaret: function cc_handleMoveCaret(aMessage) {
|
||||
let direction = aMessage.json.direction;
|
||||
let granularity = aMessage.json.granularity;
|
||||
let accessible = this.vc.position;
|
||||
let accText = accessible.QueryInterface(Ci.nsIAccessibleText);
|
||||
let oldOffset = accText.caretOffset;
|
||||
let text = accText.getText(0, accText.characterCount);
|
||||
|
||||
let start = {}, end = {};
|
||||
if (direction === 'Previous' && !aMessage.json.atStart) {
|
||||
switch (granularity) {
|
||||
case MOVEMENT_GRANULARITY_CHARACTER:
|
||||
accText.caretOffset--;
|
||||
break;
|
||||
case MOVEMENT_GRANULARITY_WORD:
|
||||
accText.getTextBeforeOffset(accText.caretOffset,
|
||||
Ci.nsIAccessibleText.BOUNDARY_WORD_START, start, end);
|
||||
accText.caretOffset = end.value === accText.caretOffset ?
|
||||
start.value : end.value;
|
||||
break;
|
||||
case MOVEMENT_GRANULARITY_PARAGRAPH:
|
||||
let startOfParagraph = text.lastIndexOf('\n', accText.caretOffset - 1);
|
||||
accText.caretOffset = startOfParagraph !== -1 ? startOfParagraph : 0;
|
||||
break;
|
||||
}
|
||||
} else if (direction === 'Next' && !aMessage.json.atEnd) {
|
||||
switch (granularity) {
|
||||
case MOVEMENT_GRANULARITY_CHARACTER:
|
||||
accText.caretOffset++;
|
||||
break;
|
||||
case MOVEMENT_GRANULARITY_WORD:
|
||||
accText.getTextAtOffset(accText.caretOffset,
|
||||
Ci.nsIAccessibleText.BOUNDARY_WORD_END, start, end);
|
||||
accText.caretOffset = end.value;
|
||||
break;
|
||||
case MOVEMENT_GRANULARITY_PARAGRAPH:
|
||||
accText.caretOffset = text.indexOf('\n', accText.caretOffset + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
this.presentCaretChange(text, oldOffset, accText.caretOffset);
|
||||
},
|
||||
|
||||
getChildCursor: function cc_getChildCursor(aAccessible) {
|
||||
let acc = aAccessible || this.vc.position;
|
||||
if (Utils.isAliveAndVisible(acc) && acc.role === Roles.INTERNAL_FRAME) {
|
||||
let domNode = acc.DOMNode;
|
||||
|
@ -167,9 +317,7 @@ this.ContentControl.prototype = {
|
|||
return null;
|
||||
},
|
||||
|
||||
sendToChild: function ContentControl_sendToChild(aVirtualCursor,
|
||||
aMessage,
|
||||
aReplacer) {
|
||||
sendToChild: function cc_sendToChild(aVirtualCursor, aMessage, aReplacer) {
|
||||
let mm = this.getChildCursor(aVirtualCursor.position);
|
||||
if (!mm) {
|
||||
return false;
|
||||
|
@ -186,7 +334,7 @@ this.ContentControl.prototype = {
|
|||
return true;
|
||||
},
|
||||
|
||||
sendToParent: function ContentControl_sendToParent(aMessage) {
|
||||
sendToParent: function cc_sendToParent(aMessage) {
|
||||
// XXX: This is a silly way to make a deep copy
|
||||
let newJSON = JSON.parse(JSON.stringify(aMessage.json));
|
||||
newJSON.origin = 'child';
|
||||
|
@ -205,7 +353,7 @@ this.ContentControl.prototype = {
|
|||
* precedence over given anchor.
|
||||
* - moveMethod: pivot move method to use, default is 'moveNext',
|
||||
*/
|
||||
autoMove: function ContentControl_autoMove(aAnchor, aOptions = {}) {
|
||||
autoMove: function cc_autoMove(aAnchor, aOptions = {}) {
|
||||
let win = this.window;
|
||||
win.clearTimeout(this._autoMove);
|
||||
|
||||
|
@ -266,4 +414,4 @@ this.ContentControl.prototype = {
|
|||
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupportsWeakReference,
|
||||
Ci.nsIMessageListener
|
||||
])
|
||||
};
|
||||
};
|
||||
|
|
|
@ -625,7 +625,8 @@ this.UtteranceGenerator = {
|
|||
// This is because we expose the checked information on the node itself.
|
||||
// XXX: this means the checked state is always appended to the end, regardless
|
||||
// of the utterance ordering preference.
|
||||
if (Utils.AndroidSdkVersion < 16 && aState.contains(States.CHECKABLE)) {
|
||||
if ((Utils.AndroidSdkVersion < 16 || Utils.MozBuildApp === 'browser') &&
|
||||
aState.contains(States.CHECKABLE)) {
|
||||
let statetr = aState.contains(States.CHECKED) ?
|
||||
'stateChecked' : 'stateNotChecked';
|
||||
stateUtterances.push(Utils.stringBundle.GetStringFromName(statetr));
|
||||
|
|
|
@ -569,15 +569,13 @@ BraillePresenter.prototype = {
|
|||
this.Presentation = {
|
||||
get presenters() {
|
||||
delete this.presenters;
|
||||
this.presenters = [new VisualPresenter()];
|
||||
|
||||
if (Utils.MozBuildApp == 'mobile/android') {
|
||||
this.presenters.push(new AndroidPresenter());
|
||||
} else {
|
||||
this.presenters.push(new SpeechPresenter());
|
||||
this.presenters.push(new HapticPresenter());
|
||||
}
|
||||
|
||||
let presenterMap = {
|
||||
'mobile/android': [VisualPresenter, AndroidPresenter],
|
||||
'b2g': [VisualPresenter, SpeechPresenter, HapticPresenter],
|
||||
'browser': [VisualPresenter, SpeechPresenter, HapticPresenter,
|
||||
AndroidPresenter]
|
||||
};
|
||||
this.presenters = [new P() for (P of presenterMap[Utils.MozBuildApp])];
|
||||
return this.presenters;
|
||||
},
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@ this.Utils = {
|
|||
this._AndroidSdkVersion = Services.sysinfo.getPropertyAsInt32('version');
|
||||
} else {
|
||||
// Most useful in desktop debugging.
|
||||
this._AndroidSdkVersion = 15;
|
||||
this._AndroidSdkVersion = 16;
|
||||
}
|
||||
}
|
||||
return this._AndroidSdkVersion;
|
||||
|
|
|
@ -5,10 +5,6 @@
|
|||
let Ci = Components.interfaces;
|
||||
let Cu = Components.utils;
|
||||
|
||||
const MOVEMENT_GRANULARITY_CHARACTER = 1;
|
||||
const MOVEMENT_GRANULARITY_WORD = 2;
|
||||
const MOVEMENT_GRANULARITY_PARAGRAPH = 8;
|
||||
|
||||
Cu.import('resource://gre/modules/XPCOMUtils.jsm');
|
||||
XPCOMUtils.defineLazyModuleGetter(this, 'Logger',
|
||||
'resource://gre/modules/accessibility/Utils.jsm');
|
||||
|
@ -68,86 +64,6 @@ function forwardToChild(aMessage, aListener, aVCPosition) {
|
|||
return true;
|
||||
}
|
||||
|
||||
function activateCurrent(aMessage) {
|
||||
Logger.debug('activateCurrent');
|
||||
function activateAccessible(aAccessible) {
|
||||
try {
|
||||
if (aMessage.json.activateIfKey &&
|
||||
aAccessible.role != Roles.KEY) {
|
||||
// Only activate keys, don't do anything on other objects.
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
// accessible is invalid. Silently fail.
|
||||
return;
|
||||
}
|
||||
|
||||
if (aAccessible.actionCount > 0) {
|
||||
aAccessible.doAction(0);
|
||||
} else {
|
||||
let control = Utils.getEmbeddedControl(aAccessible);
|
||||
if (control && control.actionCount > 0) {
|
||||
control.doAction(0);
|
||||
}
|
||||
|
||||
// XXX Some mobile widget sets do not expose actions properly
|
||||
// (via ARIA roles, etc.), so we need to generate a click.
|
||||
// Could possibly be made simpler in the future. Maybe core
|
||||
// engine could expose nsCoreUtiles::DispatchMouseEvent()?
|
||||
let docAcc = Utils.AccRetrieval.getAccessibleFor(content.document);
|
||||
let docX = {}, docY = {}, docW = {}, docH = {};
|
||||
docAcc.getBounds(docX, docY, docW, docH);
|
||||
|
||||
let objX = {}, objY = {}, objW = {}, objH = {};
|
||||
aAccessible.getBounds(objX, objY, objW, objH);
|
||||
|
||||
let x = Math.round((objX.value - docX.value) + objW.value / 2);
|
||||
let y = Math.round((objY.value - docY.value) + objH.value / 2);
|
||||
|
||||
let node = aAccessible.DOMNode || aAccessible.parent.DOMNode;
|
||||
|
||||
function dispatchMouseEvent(aEventType) {
|
||||
let evt = content.document.createEvent('MouseEvents');
|
||||
evt.initMouseEvent(aEventType, true, true, content,
|
||||
x, y, 0, 0, 0, false, false, false, false, 0, null);
|
||||
node.dispatchEvent(evt);
|
||||
}
|
||||
|
||||
dispatchMouseEvent('mousedown');
|
||||
dispatchMouseEvent('mouseup');
|
||||
}
|
||||
|
||||
if (aAccessible.role !== Roles.KEY) {
|
||||
// Keys will typically have a sound of their own.
|
||||
sendAsyncMessage('AccessFu:Present',
|
||||
Presentation.actionInvoked(aAccessible, 'click'));
|
||||
}
|
||||
}
|
||||
|
||||
function moveCaretTo(aAccessible, aOffset) {
|
||||
let accText = aAccessible.QueryInterface(Ci.nsIAccessibleText);
|
||||
let oldOffset = accText.caretOffset;
|
||||
let text = accText.getText(0, accText.characterCount);
|
||||
|
||||
if (aOffset >= 0 && aOffset <= accText.characterCount) {
|
||||
accText.caretOffset = aOffset;
|
||||
}
|
||||
|
||||
presentCaretChange(text, oldOffset, accText.caretOffset);
|
||||
}
|
||||
|
||||
let focusedAcc = Utils.AccRetrieval.getAccessibleFor(content.document.activeElement);
|
||||
if (focusedAcc && focusedAcc.role === Roles.ENTRY) {
|
||||
moveCaretTo(focusedAcc, aMessage.json.offset);
|
||||
return;
|
||||
}
|
||||
|
||||
let position = Utils.getVirtualCursor(content.document).position;
|
||||
if (!forwardToChild(aMessage, activateCurrent, position)) {
|
||||
activateAccessible(position);
|
||||
}
|
||||
}
|
||||
|
||||
function activateContextMenu(aMessage) {
|
||||
function sendContextMenuCoordinates(aAccessible) {
|
||||
let bounds = Utils.getBounds(aAccessible);
|
||||
|
@ -160,72 +76,6 @@ function activateContextMenu(aMessage) {
|
|||
}
|
||||
}
|
||||
|
||||
function moveByGranularity(aMessage) {
|
||||
let direction = aMessage.json.direction;
|
||||
let vc = Utils.getVirtualCursor(content.document);
|
||||
let granularity;
|
||||
|
||||
switch(aMessage.json.granularity) {
|
||||
case MOVEMENT_GRANULARITY_CHARACTER:
|
||||
granularity = Ci.nsIAccessiblePivot.CHAR_BOUNDARY;
|
||||
break;
|
||||
case MOVEMENT_GRANULARITY_WORD:
|
||||
granularity = Ci.nsIAccessiblePivot.WORD_BOUNDARY;
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (direction === 'Previous') {
|
||||
vc.movePreviousByText(granularity);
|
||||
} else if (direction === 'Next') {
|
||||
vc.moveNextByText(granularity);
|
||||
}
|
||||
}
|
||||
|
||||
function moveCaret(aMessage) {
|
||||
let direction = aMessage.json.direction;
|
||||
let granularity = aMessage.json.granularity;
|
||||
let accessible = Utils.getVirtualCursor(content.document).position;
|
||||
let accText = accessible.QueryInterface(Ci.nsIAccessibleText);
|
||||
let oldOffset = accText.caretOffset;
|
||||
let text = accText.getText(0, accText.characterCount);
|
||||
|
||||
let start = {}, end = {};
|
||||
if (direction === 'Previous' && !aMessage.json.atStart) {
|
||||
switch (granularity) {
|
||||
case MOVEMENT_GRANULARITY_CHARACTER:
|
||||
accText.caretOffset--;
|
||||
break;
|
||||
case MOVEMENT_GRANULARITY_WORD:
|
||||
accText.getTextBeforeOffset(accText.caretOffset,
|
||||
Ci.nsIAccessibleText.BOUNDARY_WORD_START, start, end);
|
||||
accText.caretOffset = end.value === accText.caretOffset ? start.value : end.value;
|
||||
break;
|
||||
case MOVEMENT_GRANULARITY_PARAGRAPH:
|
||||
let startOfParagraph = text.lastIndexOf('\n', accText.caretOffset - 1);
|
||||
accText.caretOffset = startOfParagraph !== -1 ? startOfParagraph : 0;
|
||||
break;
|
||||
}
|
||||
} else if (direction === 'Next' && !aMessage.json.atEnd) {
|
||||
switch (granularity) {
|
||||
case MOVEMENT_GRANULARITY_CHARACTER:
|
||||
accText.caretOffset++;
|
||||
break;
|
||||
case MOVEMENT_GRANULARITY_WORD:
|
||||
accText.getTextAtOffset(accText.caretOffset,
|
||||
Ci.nsIAccessibleText.BOUNDARY_WORD_END, start, end);
|
||||
accText.caretOffset = end.value;
|
||||
break;
|
||||
case MOVEMENT_GRANULARITY_PARAGRAPH:
|
||||
accText.caretOffset = text.indexOf('\n', accText.caretOffset + 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
presentCaretChange(text, oldOffset, accText.caretOffset);
|
||||
}
|
||||
|
||||
function presentCaretChange(aText, aOldOffset, aNewOffset) {
|
||||
if (aOldOffset !== aNewOffset) {
|
||||
let msg = Presentation.textSelectionChanged(aText, aNewOffset, aNewOffset,
|
||||
|
@ -282,12 +132,9 @@ addMessageListener(
|
|||
if (m.json.buildApp)
|
||||
Utils.MozBuildApp = m.json.buildApp;
|
||||
|
||||
addMessageListener('AccessFu:Activate', activateCurrent);
|
||||
addMessageListener('AccessFu:ContextMenu', activateContextMenu);
|
||||
addMessageListener('AccessFu:Scroll', scroll);
|
||||
addMessageListener('AccessFu:AdjustRange', adjustRange);
|
||||
addMessageListener('AccessFu:MoveCaret', moveCaret);
|
||||
addMessageListener('AccessFu:MoveByGranularity', moveByGranularity);
|
||||
|
||||
if (!eventManager) {
|
||||
eventManager = new EventManager(this);
|
||||
|
@ -307,11 +154,8 @@ addMessageListener(
|
|||
function(m) {
|
||||
Logger.debug('AccessFu:Stop');
|
||||
|
||||
removeMessageListener('AccessFu:Activate', activateCurrent);
|
||||
removeMessageListener('AccessFu:ContextMenu', activateContextMenu);
|
||||
removeMessageListener('AccessFu:Scroll', scroll);
|
||||
removeMessageListener('AccessFu:MoveCaret', moveCaret);
|
||||
removeMessageListener('AccessFu:MoveByGranularity', moveByGranularity);
|
||||
|
||||
eventManager.stop();
|
||||
contentControl.stop();
|
||||
|
|
|
@ -6,9 +6,11 @@ support-files =
|
|||
output.js
|
||||
doc_traversal.html
|
||||
doc_content_integration.html
|
||||
doc_content_text.html
|
||||
|
||||
[test_alive.html]
|
||||
[test_content_integration.html]
|
||||
[test_content_text.html]
|
||||
[test_explicit_names.html]
|
||||
[test_landmarks.html]
|
||||
[test_live_regions.html]
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Text content test document</title>
|
||||
<meta charset="utf-8" />
|
||||
</head>
|
||||
<body>
|
||||
<p>These are my awards, Mother. From Army.
|
||||
The seal is for marksmanship, and the gorilla is for sand racing.</p>
|
||||
<p>You're a good guy, mon frere. That means brother in French.
|
||||
I don't know how I know that. I took four years of Spanish.</p>
|
||||
<textarea>Please refrain from Mayoneggs during this salmonella scare.</textarea>
|
||||
</body>
|
||||
</html>
|
|
@ -172,6 +172,11 @@ AccessFuContentTest.prototype = {
|
|||
this.setupMessageManager(this.mms[0], function () {
|
||||
// Get child message managers and set them up
|
||||
var frames = currentTabDocument().querySelectorAll('iframe');
|
||||
if (frames.length === 0) {
|
||||
self.pump();
|
||||
return;
|
||||
}
|
||||
|
||||
var toSetup = 0;
|
||||
for (var i = 0; i < frames.length; i++ ) {
|
||||
var mm = Utils.getMessageManager(frames[i]);
|
||||
|
@ -206,7 +211,8 @@ AccessFuContentTest.prototype = {
|
|||
aMessageManager.addMessageListener('AccessFu:Present', this);
|
||||
aMessageManager.addMessageListener('AccessFu:Ready', function () {
|
||||
aMessageManager.addMessageListener('AccessFu:ContentStarted', aCallback);
|
||||
aMessageManager.sendAsyncMessage('AccessFu:Start', { buildApp: 'browser' });
|
||||
aMessageManager.sendAsyncMessage('AccessFu:Start',
|
||||
{ buildApp: 'browser', androidSdkVersion: Utils.AndroidSdkVersion});
|
||||
});
|
||||
|
||||
aMessageManager.loadFrameScript(
|
||||
|
@ -242,21 +248,46 @@ AccessFuContentTest.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
var expected = this.currentPair[1];
|
||||
|
||||
if (expected) {
|
||||
if (expected.speak !== undefined) {
|
||||
var speech = this.extractUtterance(aMessage.json);
|
||||
if (!speech) {
|
||||
// Probably a visual highlight adjustment after a scroll.
|
||||
return;
|
||||
}
|
||||
var checkFunc = SimpleTest[expected.speak_checkFunc] || is;
|
||||
checkFunc(speech, expected.speak);
|
||||
var expected = this.currentPair[1] || {};
|
||||
var speech = this.extractUtterance(aMessage.json);
|
||||
var android = this.extractAndroid(aMessage.json, expected.android);
|
||||
if ((speech && expected.speak) || (android && expected.android)) {
|
||||
if (expected.speak) {
|
||||
(SimpleTest[expected.speak_checkFunc] || is)(speech, expected.speak);
|
||||
}
|
||||
|
||||
if (expected.android) {
|
||||
var checkFunc = SimpleTest[expected.android_checkFunc] || ok;
|
||||
checkFunc.apply(SimpleTest,
|
||||
this.lazyCompare(android, expected.android));
|
||||
}
|
||||
|
||||
this.pump();
|
||||
}
|
||||
|
||||
this.pump();
|
||||
},
|
||||
|
||||
lazyCompare: function lazyCompare(aReceived, aExpected) {
|
||||
var matches = true;
|
||||
var delta = [];
|
||||
for (var attr in aExpected) {
|
||||
var expected = aExpected[attr];
|
||||
var received = aReceived !== undefined ? aReceived[attr] : null;
|
||||
if (typeof expected === 'object') {
|
||||
var [childMatches, childDelta] = this.lazyCompare(received, expected);
|
||||
if (!childMatches) {
|
||||
delta.push(attr + ' [ ' + childDelta + ' ]');
|
||||
matches = false;
|
||||
}
|
||||
} else {
|
||||
if (received !== expected) {
|
||||
delta.push(
|
||||
attr + ' [ expected ' + expected + ' got ' + received + ' ]');
|
||||
matches = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return [matches, delta.join(' ')];
|
||||
},
|
||||
|
||||
extractUtterance: function(aData) {
|
||||
|
@ -270,6 +301,156 @@ AccessFuContentTest.prototype = {
|
|||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
|
||||
extractAndroid: function(aData, aExpectedEvents) {
|
||||
for (var output of aData) {
|
||||
if (output && output.type === 'Android') {
|
||||
for (var i in output.details) {
|
||||
// Only extract if event types match expected event types.
|
||||
var exp = aExpectedEvents ? aExpectedEvents[i] : null;
|
||||
if (!exp || (output.details[i].eventType !== exp.eventType)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return output.details;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
// Common content messages
|
||||
|
||||
var ContentMessages = {
|
||||
simpleMoveFirst: {
|
||||
name: 'AccessFu:MoveCursor',
|
||||
json: {
|
||||
action: 'moveFirst',
|
||||
rule: 'Simple',
|
||||
inputType: 'gesture',
|
||||
origin: 'top'
|
||||
}
|
||||
},
|
||||
|
||||
simpleMoveLast: {
|
||||
name: 'AccessFu:MoveCursor',
|
||||
json: {
|
||||
action: 'moveLast',
|
||||
rule: 'Simple',
|
||||
inputType: 'gesture',
|
||||
origin: 'top'
|
||||
}
|
||||
},
|
||||
|
||||
simpleMoveNext: {
|
||||
name: 'AccessFu:MoveCursor',
|
||||
json: {
|
||||
action: 'moveNext',
|
||||
rule: 'Simple',
|
||||
inputType: 'gesture',
|
||||
origin: 'top'
|
||||
}
|
||||
},
|
||||
|
||||
simpleMovePrevious: {
|
||||
name: 'AccessFu:MoveCursor',
|
||||
json: {
|
||||
action: 'movePrevious',
|
||||
rule: 'Simple',
|
||||
inputType: 'gesture',
|
||||
origin: 'top'
|
||||
}
|
||||
},
|
||||
|
||||
clearCursor: {
|
||||
name: 'AccessFu:ClearCursor',
|
||||
json: {
|
||||
origin: 'top'
|
||||
}
|
||||
},
|
||||
|
||||
focusSelector: function focusSelector(aSelector, aBlur) {
|
||||
return {
|
||||
name: 'AccessFuTest:Focus',
|
||||
json: {
|
||||
selector: aSelector,
|
||||
blur: aBlur
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
activateCurrent: function activateCurrent(aOffset) {
|
||||
return {
|
||||
name: 'AccessFu:Activate',
|
||||
json: {
|
||||
origin: 'top',
|
||||
offset: aOffset
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
moveNextBy: function moveNextBy(aGranularity) {
|
||||
return {
|
||||
name: 'AccessFu:MoveByGranularity',
|
||||
json: {
|
||||
direction: 'Next',
|
||||
granularity: this._granularityMap[aGranularity]
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
movePreviousBy: function movePreviousBy(aGranularity) {
|
||||
return {
|
||||
name: 'AccessFu:MoveByGranularity',
|
||||
json: {
|
||||
direction: 'Previous',
|
||||
granularity: this._granularityMap[aGranularity]
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
moveCaretNextBy: function moveCaretNextBy(aGranularity) {
|
||||
return {
|
||||
name: 'AccessFu:MoveCaret',
|
||||
json: {
|
||||
direction: 'Next',
|
||||
granularity: this._granularityMap[aGranularity]
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
moveCaretPreviousBy: function moveCaretPreviousBy(aGranularity) {
|
||||
return {
|
||||
name: 'AccessFu:MoveCaret',
|
||||
json: {
|
||||
direction: 'Previous',
|
||||
granularity: this._granularityMap[aGranularity]
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
_granularityMap: {
|
||||
'character': 1, // MOVEMENT_GRANULARITY_CHARACTER
|
||||
'word': 2, // MOVEMENT_GRANULARITY_WORD
|
||||
'paragraph': 8 // MOVEMENT_GRANULARITY_PARAGRAPH
|
||||
}
|
||||
};
|
||||
|
||||
var AndroidEvent = {
|
||||
VIEW_CLICKED: 0x01,
|
||||
VIEW_LONG_CLICKED: 0x02,
|
||||
VIEW_SELECTED: 0x04,
|
||||
VIEW_FOCUSED: 0x08,
|
||||
VIEW_TEXT_CHANGED: 0x10,
|
||||
WINDOW_STATE_CHANGED: 0x20,
|
||||
VIEW_HOVER_ENTER: 0x80,
|
||||
VIEW_HOVER_EXIT: 0x100,
|
||||
VIEW_SCROLLED: 0x1000,
|
||||
VIEW_TEXT_SELECTION_CHANGED: 0x2000,
|
||||
ANNOUNCEMENT: 0x4000,
|
||||
VIEW_ACCESSIBILITY_FOCUSED: 0x8000,
|
||||
VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY: 0x20000
|
||||
};
|
||||
|
|
|
@ -26,163 +26,162 @@
|
|||
var iframe = doc.createElement("iframe");
|
||||
iframe.mozbrowser = true;
|
||||
iframe.addEventListener("mozbrowserloadend", function () {
|
||||
var simpleMoveNext = { name: 'AccessFu:MoveCursor',
|
||||
json: { action: 'moveNext',
|
||||
rule: 'Simple',
|
||||
inputType: 'gesture',
|
||||
origin: 'top' }
|
||||
};
|
||||
var contentTest = new AccessFuContentTest(
|
||||
[
|
||||
// Simple traversal forward
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: 'Phone status bar Traversal Rule test document'
|
||||
}],
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: 'wow heading level 1 such app'
|
||||
}],
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: 'many option not checked check button'
|
||||
}],
|
||||
// check checkbox
|
||||
[ContentMessages.activateCurrent(), {
|
||||
speak: 'checked'
|
||||
}],
|
||||
[null, {
|
||||
speak: 'checked',
|
||||
speak_checkFunc: 'todo_isnot'
|
||||
}],
|
||||
// uncheck checkbox
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: 'Home button'
|
||||
}],
|
||||
|
||||
var simpleMovePrevious = { name: 'AccessFu:MoveCursor',
|
||||
json: { action: 'movePrevious',
|
||||
rule: 'Simple',
|
||||
inputType: 'gesture',
|
||||
origin: 'top' }
|
||||
};
|
||||
// Simple traversal backward
|
||||
[ContentMessages.simpleMovePrevious, {
|
||||
speak: 'many option checked check button such app'
|
||||
}],
|
||||
[ContentMessages.activateCurrent(), {
|
||||
speak: 'unchecked'
|
||||
}],
|
||||
[null, {
|
||||
speak: 'unchecked',
|
||||
speak_checkFunc: 'todo_isnot'
|
||||
}],
|
||||
[ContentMessages.simpleMovePrevious, {
|
||||
speak: 'wow heading level 1'
|
||||
}],
|
||||
[ContentMessages.simpleMovePrevious, {
|
||||
speak: 'Phone status bar'
|
||||
}],
|
||||
|
||||
var activateCurrent = { name: 'AccessFu:Activate',
|
||||
json: { origin: 'top' } };
|
||||
// Moving to the absolute last item from an embedded document
|
||||
// fails. Bug 972035.
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: 'wow heading level 1 such app'
|
||||
}],
|
||||
// Move from an inner frame to the last element in the parent doc
|
||||
[ContentMessages.simpleMoveLast, {
|
||||
speak: 'Home button',
|
||||
speak_checkFunc: 'todo_is'
|
||||
}],
|
||||
|
||||
var simpleMoveLast = { name: 'AccessFu:MoveCursor',
|
||||
json: { action: 'moveLast',
|
||||
rule: 'Simple',
|
||||
inputType: 'gesture',
|
||||
origin: 'top' }
|
||||
};
|
||||
[ContentMessages.clearCursor, null], // Reset cursors
|
||||
|
||||
var simpleMoveFirst = { name: 'AccessFu:MoveCursor',
|
||||
json: { action: 'moveFirst',
|
||||
rule: 'Simple',
|
||||
inputType: 'gesture',
|
||||
origin: 'top' }
|
||||
};
|
||||
// Moving to the absolute first item from an embedded document
|
||||
// fails. Bug 972035.
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: 'Phone status bar Traversal Rule test document'
|
||||
}],
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: 'wow heading level 1 such app'
|
||||
}],
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: 'many option not checked check button'
|
||||
}],
|
||||
[ContentMessages.simpleMoveFirst, {
|
||||
speak: 'Phone status bar',
|
||||
speak_checkFunc: 'todo_is'
|
||||
}],
|
||||
|
||||
var clearCursor = { name: 'AccessFu:ClearCursor',
|
||||
json: { origin: 'top' }
|
||||
};
|
||||
// Reset cursors
|
||||
[ContentMessages.clearCursor, null],
|
||||
|
||||
function focusFunc(aSelector, aBlur) {
|
||||
return function (mm) {
|
||||
mm.sendAsyncMessage(
|
||||
'AccessFuTest:Focus', { selector: aSelector, blur: aBlur });
|
||||
};
|
||||
}
|
||||
// Move cursor with focus in outside document
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: 'Phone status bar Traversal Rule test document'
|
||||
}],
|
||||
[ContentMessages.focusSelector('button#home', false), {
|
||||
speak: 'Home button'
|
||||
}],
|
||||
|
||||
var contentTest = new AccessFuContentTest(
|
||||
[
|
||||
// Simple traversal forward
|
||||
[simpleMoveNext,
|
||||
{ speak: 'Phone status bar Traversal Rule test document' }],
|
||||
[simpleMoveNext,
|
||||
{ speak: 'wow heading level 1 such app' }],
|
||||
[simpleMoveNext,
|
||||
{ speak: 'many option not checked check button' }],
|
||||
// check checkbox
|
||||
[activateCurrent,
|
||||
{ speak: 'checked' }],
|
||||
[null,
|
||||
{ speak: 'checked', speak_checkFunc: 'todo_isnot' }],
|
||||
// uncheck checkbox
|
||||
[simpleMoveNext,
|
||||
{ speak: 'Home button' }],
|
||||
// Blur button and reset cursor
|
||||
[ContentMessages.focusSelector('button#home', true), null],
|
||||
[ContentMessages.clearCursor, null],
|
||||
|
||||
// Simple traversal backward
|
||||
[simpleMovePrevious,
|
||||
{ speak: 'many option checked check button such app' }],
|
||||
[activateCurrent,
|
||||
{ speak: 'unchecked' }],
|
||||
[null,
|
||||
{ speak: 'unchecked', speak_checkFunc: 'todo_isnot' }],
|
||||
[simpleMovePrevious,
|
||||
{ speak: 'wow heading level 1' }],
|
||||
[simpleMovePrevious,
|
||||
{ speak: 'Phone status bar' }],
|
||||
// Set focus on element outside of embedded frame while
|
||||
// cursor is in frame
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: 'Phone status bar Traversal Rule test document'
|
||||
}],
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: 'wow heading level 1 such app'
|
||||
}],
|
||||
[ContentMessages.focusSelector('button#home', false), {
|
||||
speak: 'Home button'
|
||||
}]
|
||||
|
||||
// Moving to the absolute last item from an embedded document fails.
|
||||
// Bug 972035.
|
||||
[simpleMoveNext,
|
||||
{ speak: 'wow heading level 1 such app' }],
|
||||
// Move from an inner frame to the last element in the parent doc
|
||||
[simpleMoveLast,
|
||||
{ speak: 'Home button', speak_checkFunc: 'todo_is' }],
|
||||
// Blur button and reset cursor
|
||||
[ContentMessages.focusSelector('button#home', true), null],
|
||||
[ContentMessages.clearCursor, null],
|
||||
|
||||
[clearCursor, null], // Reset cursors
|
||||
// XXX: Set focus on iframe itself.
|
||||
// XXX: Set focus on element in iframe when cursor is outside of it.
|
||||
// XXX: Set focus on element in iframe when cursor is in iframe.
|
||||
|
||||
// Moving to the absolute first item from an embedded document fails.
|
||||
// Bug 972035.
|
||||
[simpleMoveNext,
|
||||
{ speak: 'Phone status bar Traversal Rule test document' }],
|
||||
[simpleMoveNext,
|
||||
{ speak: 'wow heading level 1 such app' }],
|
||||
[simpleMoveNext,
|
||||
{ speak: 'many option not checked check button' }],
|
||||
[simpleMoveFirst,
|
||||
{ speak: 'Phone status bar', speak_checkFunc: 'todo_is' }],
|
||||
// Open dialog in outer doc, while cursor is also in outer doc
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: 'Phone status bar Traversal Rule test document'
|
||||
}],
|
||||
[doc.defaultView.showAlert, {
|
||||
speak: 'This is an alert! heading level 1 dialog'
|
||||
}],
|
||||
|
||||
// Reset cursors
|
||||
[clearCursor, null],
|
||||
[function() {
|
||||
doc.defaultView.hideAlert()
|
||||
}, {
|
||||
speak: 'wow heading level 1 such app'
|
||||
}],
|
||||
|
||||
// Move cursor with focus in outside document
|
||||
[simpleMoveNext,
|
||||
{ speak: 'Phone status bar Traversal Rule test document' }],
|
||||
[ focusFunc('button#home', false), { speak: 'Home button' }],
|
||||
[ContentMessages.clearCursor, null],
|
||||
|
||||
// Blur button and reset cursor
|
||||
[focusFunc('button#home', true), null],
|
||||
[clearCursor, null],
|
||||
// Open dialog in outer doc, while cursor is in inner frame
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: 'Phone status bar Traversal Rule test document'
|
||||
}],
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: 'wow heading level 1 such app'
|
||||
}],
|
||||
[doc.defaultView.showAlert, {
|
||||
speak: 'This is an alert! heading level 1 dialog'
|
||||
}],
|
||||
|
||||
// Set focus on element outside of embedded frame while cursor is in frame
|
||||
[simpleMoveNext,
|
||||
{ speak: 'Phone status bar Traversal Rule test document' }],
|
||||
[simpleMoveNext,
|
||||
{ speak: 'wow heading level 1 such app' }],
|
||||
[focusFunc('button#home', false), { speak: 'Home button' }]
|
||||
// XXX: Place cursor back where it was.
|
||||
[doc.defaultView.hideAlert, {
|
||||
speak: 'many option not checked check button such app'
|
||||
}],
|
||||
|
||||
// Blur button and reset cursor
|
||||
[focusFunc('button#home', true), null],
|
||||
[clearCursor, null],
|
||||
[ContentMessages.clearCursor, null],
|
||||
|
||||
// XXX: Set focus on iframe itself.
|
||||
// XXX: Set focus on element in iframe when cursor is outside of it.
|
||||
// XXX: Set focus on element in iframe when cursor is in iframe.
|
||||
// Open dialog, then focus on something when closing
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: 'Phone status bar Traversal Rule test document'
|
||||
}],
|
||||
[doc.defaultView.showAlert, {
|
||||
speak: 'This is an alert! heading level 1 dialog'
|
||||
}],
|
||||
|
||||
// Open dialog in outer doc, while cursor is also in outer doc
|
||||
[simpleMoveNext,
|
||||
{ speak: 'Phone status bar Traversal Rule test document' }],
|
||||
[doc.defaultView.showAlert,
|
||||
{ speak: 'This is an alert! heading level 1 dialog' }],
|
||||
|
||||
[function () { doc.defaultView.hideAlert() },
|
||||
{ speak: 'wow heading level 1 such app' }],
|
||||
|
||||
[clearCursor, null],
|
||||
|
||||
// Open dialog in outer doc, while cursor is in inner frame
|
||||
[simpleMoveNext,
|
||||
{ speak: 'Phone status bar Traversal Rule test document' }],
|
||||
[simpleMoveNext,
|
||||
{ speak: 'wow heading level 1 such app' }],
|
||||
[doc.defaultView.showAlert,
|
||||
{ speak: 'This is an alert! heading level 1 dialog' }],
|
||||
|
||||
// XXX: Place cursor back where it was.
|
||||
[doc.defaultView.hideAlert,
|
||||
{ speak: 'many option not checked check button such app' }],
|
||||
|
||||
[clearCursor, null],
|
||||
|
||||
// Open dialog, then focus on something when closing
|
||||
[simpleMoveNext,
|
||||
{ speak: 'Phone status bar Traversal Rule test document' }],
|
||||
[doc.defaultView.showAlert,
|
||||
{ speak: 'This is an alert! heading level 1 dialog' }],
|
||||
|
||||
[function () {
|
||||
doc.defaultView.hideAlert();
|
||||
doc.querySelector('button#home').focus();
|
||||
},
|
||||
{ speak: 'Home button Traversal Rule test document' }]
|
||||
]);
|
||||
[function() {
|
||||
doc.defaultView.hideAlert();
|
||||
doc.querySelector('button#home').focus();
|
||||
}, {
|
||||
speak: 'Home button Traversal Rule test document'
|
||||
}]
|
||||
]);
|
||||
|
||||
contentTest.start(function () {
|
||||
closeBrowserWindow();
|
||||
|
|
|
@ -0,0 +1,259 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Tests AccessFu content integration</title>
|
||||
<meta charset="utf-8" />
|
||||
<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="chrome://mochikit/content/chrome-harness.js">
|
||||
</script>
|
||||
|
||||
<script type="application/javascript" src="../common.js"></script>
|
||||
<script type="application/javascript" src="../browser.js"></script>
|
||||
<script type="application/javascript" src="../events.js"></script>
|
||||
<script type="application/javascript" src="../role.js"></script>
|
||||
<script type="application/javascript" src="../states.js"></script>
|
||||
<script type="application/javascript" src="../layout.js"></script>
|
||||
<script type="application/javascript" src="jsatcommon.js"></script>
|
||||
|
||||
<script type="application/javascript">
|
||||
function doTest() {
|
||||
var doc = currentTabDocument();
|
||||
var textTest = new AccessFuContentTest(
|
||||
[
|
||||
// Read-only text tests
|
||||
[ContentMessages.simpleMoveFirst, {
|
||||
speak: 'These are my awards, Mother. From Army. ' +
|
||||
'The seal is for marksmanship, and the gorilla is ' +
|
||||
'for sand racing. Text content test document'
|
||||
}],
|
||||
[ContentMessages.moveNextBy('word'), {
|
||||
speak: 'These',
|
||||
speak_checkFunc: 'todo_is', // Bug 980509
|
||||
android: [{
|
||||
eventType: AndroidEvent.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
|
||||
fromIndex: 0,
|
||||
toIndex: 5
|
||||
}]
|
||||
}],
|
||||
[ContentMessages.moveNextBy('word'), {
|
||||
speak: 'are',
|
||||
speak_checkFunc: 'todo_is', // Bug 980509
|
||||
android: [{
|
||||
eventType: AndroidEvent.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
|
||||
fromIndex: 6,
|
||||
toIndex: 9
|
||||
}]
|
||||
}],
|
||||
[ContentMessages.moveNextBy('word'), {
|
||||
speak: 'my',
|
||||
speak_checkFunc: 'todo_is', // Bug 980509
|
||||
android: [{
|
||||
eventType: AndroidEvent.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
|
||||
fromIndex: 10,
|
||||
toIndex: 12
|
||||
}]
|
||||
}],
|
||||
[ContentMessages.moveNextBy('word'), {
|
||||
speak: 'awards,',
|
||||
speak_checkFunc: 'todo_is', // Bug 980509
|
||||
android: [{
|
||||
eventType: AndroidEvent.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
|
||||
fromIndex: 13,
|
||||
toIndex: 20
|
||||
}]
|
||||
}],
|
||||
[ContentMessages.moveNextBy('word'), {
|
||||
speak: 'Mother.',
|
||||
speak_checkFunc: 'todo_is', // Bug 980509
|
||||
android: [{
|
||||
eventType: AndroidEvent.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
|
||||
fromIndex: 21,
|
||||
toIndex: 28
|
||||
}]
|
||||
}],
|
||||
[ContentMessages.movePreviousBy('word'), {
|
||||
speak: 'awards,',
|
||||
speak_checkFunc: 'todo_is', // Bug 980509
|
||||
android: [{
|
||||
eventType: AndroidEvent.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
|
||||
fromIndex: 13,
|
||||
toIndex: 20
|
||||
}]
|
||||
}],
|
||||
[ContentMessages.movePreviousBy('word'), {
|
||||
speak: 'my',
|
||||
speak_checkFunc: 'todo_is', // Bug 980509
|
||||
android: [{
|
||||
eventType: AndroidEvent.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
|
||||
fromIndex: 10,
|
||||
toIndex: 12
|
||||
}]
|
||||
}],
|
||||
[ContentMessages.movePreviousBy('word'), {
|
||||
speak: 'are',
|
||||
speak_checkFunc: 'todo_is', // Bug 980509
|
||||
android: [{
|
||||
eventType: AndroidEvent.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
|
||||
fromIndex: 6,
|
||||
toIndex: 9
|
||||
}]
|
||||
}],
|
||||
[ContentMessages.movePreviousBy('word'), {
|
||||
speak: 'These',
|
||||
speak_checkFunc: 'todo_is', // Bug 980509
|
||||
android: [{
|
||||
eventType: AndroidEvent.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
|
||||
fromIndex: 0,
|
||||
toIndex: 5
|
||||
}]
|
||||
}],
|
||||
// XXX: Bug 980510: doing next after text traversal should
|
||||
// bring us to the next paragraph.
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: 'You\'re a good guy, mon frere. ' +
|
||||
'That means brother in French. ' +
|
||||
'I don\'t know how I know that. ' +
|
||||
'I took four years of Spanish.',
|
||||
speak_checkFunc: 'todo_is' // Bug 980510
|
||||
}],
|
||||
// XXX: extra move op here because of bug 980510.
|
||||
[ContentMessages.simpleMoveNext, {
|
||||
speak: 'You\'re a good guy, mon frere. ' +
|
||||
'That means brother in French. ' +
|
||||
'I don\'t know how I know that. ' +
|
||||
'I took four years of Spanish.',
|
||||
}],
|
||||
// XXX: Word boundary should be past the apostraphe.
|
||||
[ContentMessages.moveNextBy('word'), {
|
||||
speak: 'You\'re',
|
||||
speak_checkFunc: 'todo_is', // Bug 980509
|
||||
android: [{
|
||||
eventType: AndroidEvent.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
|
||||
fromIndex: 0,
|
||||
toIndex: 6
|
||||
}],
|
||||
android_checkFunc: 'todo' // Bug 980512
|
||||
}],
|
||||
|
||||
// Editable text tests.
|
||||
[ContentMessages.focusSelector('textarea'), {
|
||||
speak: 'Please refrain from Mayoneggs during this ' +
|
||||
'salmonella scare. text area'
|
||||
}],
|
||||
[null, { // When we first focus, caret is at 0.
|
||||
android: [{
|
||||
eventType: AndroidEvent.VIEW_TEXT_SELECTION_CHANGED,
|
||||
brailleOutput: {
|
||||
selectionStart: 0,
|
||||
selectionEnd: 0
|
||||
}
|
||||
}]
|
||||
}
|
||||
],
|
||||
[ContentMessages.activateCurrent(10), {
|
||||
android: [{
|
||||
eventType: AndroidEvent.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
|
||||
fromIndex: 0,
|
||||
toIndex: 10
|
||||
}]
|
||||
}],
|
||||
[null, {
|
||||
android: [{
|
||||
eventType: AndroidEvent.VIEW_TEXT_SELECTION_CHANGED,
|
||||
brailleOutput: {
|
||||
selectionStart: 10,
|
||||
selectionEnd: 10
|
||||
}
|
||||
}]
|
||||
}],
|
||||
[ContentMessages.activateCurrent(20), {
|
||||
android: [{
|
||||
eventType: AndroidEvent.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
|
||||
fromIndex: 10,
|
||||
toIndex: 20
|
||||
}]
|
||||
}],
|
||||
[null, {
|
||||
android: [{
|
||||
eventType: AndroidEvent.VIEW_TEXT_SELECTION_CHANGED,
|
||||
brailleOutput: {
|
||||
selectionStart: 20,
|
||||
selectionEnd: 20
|
||||
}
|
||||
}]
|
||||
}],
|
||||
[ContentMessages.moveCaretNextBy('word'), {
|
||||
android: [{
|
||||
eventType: AndroidEvent.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
|
||||
fromIndex: 20,
|
||||
toIndex: 29
|
||||
}]
|
||||
}],
|
||||
[ContentMessages.moveCaretNextBy('word'), {
|
||||
android: [{
|
||||
eventType: AndroidEvent.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
|
||||
fromIndex: 29,
|
||||
toIndex: 36
|
||||
}]
|
||||
}],
|
||||
[ContentMessages.moveCaretNextBy('character'), {
|
||||
android: [{
|
||||
eventType: AndroidEvent.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
|
||||
fromIndex: 36,
|
||||
toIndex: 37
|
||||
}]
|
||||
}],
|
||||
[ContentMessages.moveCaretNextBy('character'), {
|
||||
android: [{
|
||||
eventType: AndroidEvent.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
|
||||
fromIndex: 37,
|
||||
toIndex: 38
|
||||
}]
|
||||
}],
|
||||
[ContentMessages.moveCaretNextBy('paragraph'), {
|
||||
android: [{
|
||||
eventType: AndroidEvent.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
|
||||
fromIndex: 38,
|
||||
toIndex: 59
|
||||
}]
|
||||
}],
|
||||
[ContentMessages.moveCaretPreviousBy('word'), {
|
||||
android: [{
|
||||
eventType: AndroidEvent.VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
|
||||
fromIndex: 53,
|
||||
toIndex: 59
|
||||
}]
|
||||
}]
|
||||
]);
|
||||
|
||||
textTest.start(function () {
|
||||
closeBrowserWindow();
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addLoadEvent(
|
||||
function () {
|
||||
openBrowserWindow(
|
||||
doTest,
|
||||
getRootDirectory(window.location.href) + "doc_content_text.html");
|
||||
});
|
||||
</script>
|
||||
</head>
|
||||
<body id="body">
|
||||
|
||||
<a target="_blank"
|
||||
title="Add tests for text editing and navigating"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=972047">Mozilla Bug 933808</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -19,7 +19,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="2346ad9002062d70b6b27978c6b942f04192bf1b"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="db2ef2b61c70889533a0837fa3e053d24e95fdea"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="2346ad9002062d70b6b27978c6b942f04192bf1b"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="db2ef2b61c70889533a0837fa3e053d24e95fdea"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="a9e08b91e9cd1f0930f16cfc49ec72f63575d5fe">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="2346ad9002062d70b6b27978c6b942f04192bf1b"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="db2ef2b61c70889533a0837fa3e053d24e95fdea"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="2346ad9002062d70b6b27978c6b942f04192bf1b"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="db2ef2b61c70889533a0837fa3e053d24e95fdea"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d11f524d00cacf5ba0dfbf25e4aa2158b1c3a036"/>
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
"branch": "",
|
||||
"revision": ""
|
||||
},
|
||||
"revision": "ca4f67df676ef3ab0072b9e3fb97baee4d4bde36",
|
||||
"revision": "91e5760ff948d3bef3959866c6a1744991560ef6",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="2346ad9002062d70b6b27978c6b942f04192bf1b"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="db2ef2b61c70889533a0837fa3e053d24e95fdea"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="2346ad9002062d70b6b27978c6b942f04192bf1b"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="db2ef2b61c70889533a0837fa3e053d24e95fdea"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="2346ad9002062d70b6b27978c6b942f04192bf1b"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="db2ef2b61c70889533a0837fa3e053d24e95fdea"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="2346ad9002062d70b6b27978c6b942f04192bf1b"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="db2ef2b61c70889533a0837fa3e053d24e95fdea"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="2346ad9002062d70b6b27978c6b942f04192bf1b"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="db2ef2b61c70889533a0837fa3e053d24e95fdea"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="cb16958e41105d7c551d9941f522db97b8312538"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="485846b2a40d8ac7d6c1c5f8af6d15b0c10af19d"/>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="2346ad9002062d70b6b27978c6b942f04192bf1b"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="db2ef2b61c70889533a0837fa3e053d24e95fdea"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="266bca6e60dad43e395f38b66edabe8bdc882334"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="1f6a1fe07f81c5bc5e1d079c9b60f7f78ca2bf4f"/>
|
||||
|
|
|
@ -1403,3 +1403,10 @@ pref("ui.key.menuAccessKeyFocuses", true);
|
|||
|
||||
// Delete HTTP cache v2 data of users that didn't opt-in manually
|
||||
pref("browser.cache.auto_delete_cache_version", 1);
|
||||
|
||||
// Telemetry experiments settings.
|
||||
pref("experiments.enabled", false);
|
||||
pref("experiments.manifest.fetchIntervalSeconds", 86400);
|
||||
pref("experiments.manifest.uri", "https://telemetry-experiment.cdn.mozilla.net/manifest/v1/firefox/%VERSION%/%CHANNEL%");
|
||||
pref("experiments.manifest.certs.1.commonName", "*.cdn.mozilla.net");
|
||||
pref("experiments.manifest.certs.1.issuerName", "CN=Cybertrust Public SureServer SV CA,O=Cybertrust Inc");
|
||||
|
|
|
@ -1186,13 +1186,6 @@ var gBrowserInit = {
|
|||
WindowsPrefSync.init();
|
||||
}
|
||||
|
||||
if (gMultiProcessBrowser) {
|
||||
// Bug 862519 - Backspace doesn't work in electrolysis builds.
|
||||
// We bypass the problem by disabling the backspace-to-go-back command.
|
||||
document.getElementById("cmd_handleBackspace").setAttribute("disabled", true);
|
||||
document.getElementById("key_delete").setAttribute("disabled", true);
|
||||
}
|
||||
|
||||
SessionStore.promiseInitialized.then(() => {
|
||||
// Bail out if the window has been closed in the meantime.
|
||||
if (window.closed) {
|
||||
|
|
|
@ -61,4 +61,5 @@ run-if = crashreporter
|
|||
[browser_pluginplaypreview2.js]
|
||||
[browser_pluginCrashCommentAndURL.js]
|
||||
run-if = crashreporter
|
||||
skip-if = os == "linux" && debug # bug 933680
|
||||
[browser_plugins_added_dynamically.js]
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,4 @@
|
|||
component {f7800463-3b97-47f9-9341-b7617e6d8d49} ExperimentsService.js
|
||||
contract @mozilla.org/browser/experiments-service;1 {f7800463-3b97-47f9-9341-b7617e6d8d49}
|
||||
category update-timer ExperimentsService @mozilla.org/browser/experiments-service;1,getService,experiments-update-timer,experiments.manifest.fetchIntervalSeconds,86400
|
||||
category profile-after-change ExperimentsService @mozilla.org/browser/experiments-service;1
|
|
@ -0,0 +1,37 @@
|
|||
/* 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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource:///modules/experiments/Experiments.jsm");
|
||||
Cu.import("resource://gre/modules/osfile.jsm")
|
||||
|
||||
function ExperimentsService() {
|
||||
}
|
||||
|
||||
ExperimentsService.prototype = {
|
||||
classID: Components.ID("{f7800463-3b97-47f9-9341-b7617e6d8d49}"),
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsITimerCallback, Ci.nsIObserver]),
|
||||
|
||||
notify: function (timer) {
|
||||
if (OS.Constants.Path.profileDir === undefined) {
|
||||
throw Error("Update timer fired before profile was initialized?");
|
||||
}
|
||||
Experiments.instance().updateManifest();
|
||||
},
|
||||
|
||||
observe: function (subject, topic, data) {
|
||||
switch (topic) {
|
||||
case "profile-after-change":
|
||||
Experiments.instance(); // for side effects
|
||||
break;
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([ExperimentsService]);
|
|
@ -0,0 +1,16 @@
|
|||
# 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/.
|
||||
|
||||
EXTRA_COMPONENTS += [
|
||||
'Experiments.manifest',
|
||||
'ExperimentsService.js',
|
||||
]
|
||||
|
||||
JS_MODULES_PATH = 'modules/experiments'
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'Experiments.jsm',
|
||||
]
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell/xpcshell.ini']
|
Двоичный файл не отображается.
Двоичный файл не отображается.
Двоичный файл не отображается.
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"version": 1,
|
||||
"experiments": [
|
||||
{
|
||||
"id": "test-experiment-1@tests.mozilla.org",
|
||||
"xpiURL": "https://experiments.mozilla.org/foo.xpi",
|
||||
"xpiHash": "sha1:cb1eb32b89d86d78b7326f416cf404548c5e0099",
|
||||
"startTime": 1393000000,
|
||||
"endTime": 1394000000,
|
||||
"appName": ["Firefox", "Fennec"],
|
||||
"minVersion": "28",
|
||||
"maxVersion": "30",
|
||||
"maxActiveSeconds": 60,
|
||||
"os": ["windows", "linux", "osx"],
|
||||
"channel": ["daily", "weekly", "nightly"],
|
||||
"jsfilter": "function filter(context) { return true; }"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,131 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
Cu.import("resource://services-sync/healthreport.jsm", this);
|
||||
Cu.import("resource://testing-common/services/healthreport/utils.jsm", this);
|
||||
Cu.import("resource://gre/modules/services/healthreport/providers.jsm");
|
||||
|
||||
const EXPERIMENT1_ID = "test-experiment-1@tests.mozilla.org";
|
||||
const EXPERIMENT1_XPI_SHA1 = "sha1:08c4d3ef1d0fc74faa455e85106ef0bc8cf8ca90";
|
||||
const EXPERIMENT1_XPI_NAME = "experiment-1.xpi";
|
||||
const EXPERIMENT1_NAME = "Test experiment 1";
|
||||
|
||||
const EXPERIMENT1A_XPI_SHA1 = "sha1:2b8d14e3e06a54d5ce628fe3598cbb364cff9e6b";
|
||||
const EXPERIMENT1A_XPI_NAME = "experiment-1a.xpi";
|
||||
const EXPERIMENT1A_NAME = "Test experiment 1.1";
|
||||
|
||||
const EXPERIMENT2_ID = "test-experiment-2@tests.mozilla.org"
|
||||
const EXPERIMENT2_XPI_SHA1 = "sha1:81877991ec70360fb48db84c34a9b2da7aa41d6a";
|
||||
const EXPERIMENT2_XPI_NAME = "experiment-2.xpi";
|
||||
|
||||
let gAppInfo = null;
|
||||
|
||||
function getReporter(name, uri, inspected) {
|
||||
return Task.spawn(function init() {
|
||||
let reporter = getHealthReporter(name, uri, inspected);
|
||||
yield reporter.init();
|
||||
|
||||
yield reporter._providerManager.registerProviderFromType(
|
||||
HealthReportProvider);
|
||||
|
||||
throw new Task.Result(reporter);
|
||||
});
|
||||
}
|
||||
|
||||
function removeCacheFile() {
|
||||
let path = OS.Path.join(OS.Constants.Path.profileDir, "experiments.json");
|
||||
return OS.File.remove(path);
|
||||
}
|
||||
|
||||
function disableCertificateChecks() {
|
||||
let pref = "experiments.manifest.cert.checkAttributes";
|
||||
Services.prefs.setBoolPref(pref, false);
|
||||
do_register_cleanup(() => Services.prefs.clearUserPref(pref));
|
||||
}
|
||||
|
||||
function patchPolicy(policy, data) {
|
||||
for (let key of Object.keys(data)) {
|
||||
Object.defineProperty(policy, key, {
|
||||
value: data[key],
|
||||
writable: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function defineNow(policy, time) {
|
||||
patchPolicy(policy, { now: () => new Date(time) });
|
||||
}
|
||||
|
||||
function futureDate(date, offset) {
|
||||
return new Date(date.getTime() + offset);
|
||||
}
|
||||
|
||||
function dateToSeconds(date) {
|
||||
return date.getTime() / 1000;
|
||||
}
|
||||
|
||||
function createAppInfo(options) {
|
||||
const XULAPPINFO_CONTRACTID = "@mozilla.org/xre/app-info;1";
|
||||
const XULAPPINFO_CID = Components.ID("{c763b610-9d49-455a-bbd2-ede71682a1ac}");
|
||||
|
||||
let options = options || {};
|
||||
let id = options.id || "xpcshell@tests.mozilla.org";
|
||||
let name = options.name || "XPCShell";
|
||||
let version = options.version || "1.0";
|
||||
let platformVersion = options.platformVersion || "1.0";
|
||||
let date = options.date || new Date();
|
||||
|
||||
let buildID = "" + date.getYear() + date.getMonth() + date.getDate() + "01";
|
||||
|
||||
gAppInfo = {
|
||||
// nsIXULAppInfo
|
||||
vendor: "Mozilla",
|
||||
name: name,
|
||||
ID: id,
|
||||
version: version,
|
||||
appBuildID: buildID,
|
||||
platformVersion: platformVersion ? platformVersion : "1.0",
|
||||
platformBuildID: buildID,
|
||||
|
||||
// nsIXULRuntime
|
||||
inSafeMode: false,
|
||||
logConsoleErrors: true,
|
||||
OS: "XPCShell",
|
||||
XPCOMABI: "noarch-spidermonkey",
|
||||
invalidateCachesOnRestart: function invalidateCachesOnRestart() {
|
||||
// Do nothing
|
||||
},
|
||||
|
||||
// nsICrashReporter
|
||||
annotations: {},
|
||||
|
||||
annotateCrashReport: function(key, data) {
|
||||
this.annotations[key] = data;
|
||||
},
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIXULAppInfo,
|
||||
Ci.nsIXULRuntime,
|
||||
Ci.nsICrashReporter,
|
||||
Ci.nsISupports])
|
||||
};
|
||||
|
||||
let XULAppInfoFactory = {
|
||||
createInstance: function (outer, iid) {
|
||||
if (outer != null) {
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
}
|
||||
return gAppInfo.QueryInterface(iid);
|
||||
}
|
||||
};
|
||||
|
||||
let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
|
||||
registrar.registerFactory(XULAPPINFO_CID, "XULAppInfo",
|
||||
XULAPPINFO_CONTRACTID, XULAppInfoFactory);
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
Cu.import("resource:///modules/experiments/Experiments.jsm");
|
||||
|
||||
const FILE_MANIFEST = "experiments.manifest";
|
||||
const PREF_EXPERIMENTS_ENABLED = "experiments.enabled";
|
||||
const PREF_LOGGING_LEVEL = "experiments.logging.level";
|
||||
const PREF_LOGGING_DUMP = "experiments.logging.dump";
|
||||
const PREF_MANIFEST_URI = "experiments.manifest.uri";
|
||||
|
||||
const SEC_IN_ONE_DAY = 24 * 60 * 60;
|
||||
const MS_IN_ONE_DAY = SEC_IN_ONE_DAY * 1000;
|
||||
|
||||
let gProfileDir = null;
|
||||
let gHttpServer = null;
|
||||
let gHttpRoot = null;
|
||||
let gReporter = null;
|
||||
let gPolicy = null;
|
||||
|
||||
let gGlobalScope = this;
|
||||
function loadAddonManager() {
|
||||
let ns = {};
|
||||
Cu.import("resource://gre/modules/Services.jsm", ns);
|
||||
let head = "../../../../toolkit/mozapps/extensions/test/xpcshell/head_addons.js";
|
||||
let file = do_get_file(head);
|
||||
let uri = ns.Services.io.newFileURI(file);
|
||||
ns.Services.scriptloader.loadSubScript(uri.spec, gGlobalScope);
|
||||
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
|
||||
startupManager();
|
||||
}
|
||||
|
||||
function ManifestEntry(data) {
|
||||
this.id = data.id || EXPERIMENT1_ID;
|
||||
this.xpiURL = data.xpiURL || gHttpRoot + EXPERIMENT1_XPI_NAME;
|
||||
this.xpiHash = data.xpiHash || EXPERIMENT1_XPI_SHA1;
|
||||
this.appName = data.appName || ["XPCShell"];
|
||||
this.channel = data.appName || ["nightly"];
|
||||
this.startTime = data.startTime || new Date(2010, 0, 1, 12).getTime() / 1000;
|
||||
this.endTime = data.endTime || new Date(9001, 0, 1, 12).getTime() / 1000;
|
||||
this.maxActiveSeconds = data.maxActiveSeconds || 5 * SEC_IN_ONE_DAY;
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_setup() {
|
||||
loadAddonManager();
|
||||
gProfileDir = do_get_profile();
|
||||
gPolicy = new Experiments.Policy();
|
||||
|
||||
gHttpServer = new HttpServer();
|
||||
gHttpServer.start(-1);
|
||||
let port = gHttpServer.identity.primaryPort;
|
||||
gHttpRoot = "http://localhost:" + port + "/";
|
||||
gHttpServer.registerDirectory("/", do_get_cwd());
|
||||
do_register_cleanup(() => gHttpServer.stop(() => {}));
|
||||
|
||||
gReporter = yield getReporter("json_payload_simple");
|
||||
yield gReporter.collectMeasurements();
|
||||
let payload = yield gReporter.getJSONPayload(true);
|
||||
|
||||
patchPolicy(gPolicy, {
|
||||
updatechannel: () => "nightly",
|
||||
healthReportPayload: () => Promise.resolve(payload),
|
||||
});
|
||||
|
||||
Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
|
||||
Services.prefs.setIntPref(PREF_LOGGING_LEVEL, 0);
|
||||
Services.prefs.setBoolPref(PREF_LOGGING_DUMP, true);
|
||||
|
||||
let experiments = new Experiments.Experiments();
|
||||
});
|
||||
|
||||
function isApplicable(experiment) {
|
||||
let deferred = Promise.defer();
|
||||
experiment.isApplicable().then(
|
||||
result => deferred.resolve({ applicable: true, reason: null }),
|
||||
reason => deferred.resolve({ applicable: false, reason: reason })
|
||||
);
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
add_task(function* test_startStop() {
|
||||
let baseDate = new Date(2014, 5, 1, 12);
|
||||
let startDate = futureDate(baseDate, 30 * MS_IN_ONE_DAY);
|
||||
let endDate = futureDate(baseDate, 60 * MS_IN_ONE_DAY);
|
||||
let manifestData = new ManifestEntry({
|
||||
startTime: dateToSeconds(startDate),
|
||||
endTime: dateToSeconds(endDate),
|
||||
maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
|
||||
});
|
||||
let experiment = new Experiments.ExperimentEntry(gPolicy);
|
||||
experiment.initFromManifestData(manifestData);
|
||||
let result;
|
||||
|
||||
defineNow(gPolicy, baseDate);
|
||||
result = yield isApplicable(experiment);
|
||||
Assert.equal(result.applicable, false, "Experiment should not be applicable.");
|
||||
Assert.equal(experiment.enabled, false, "Experiment should not be enabled.");
|
||||
|
||||
defineNow(gPolicy, futureDate(startDate, 5 * MS_IN_ONE_DAY));
|
||||
result = yield isApplicable(experiment);
|
||||
Assert.equal(result.applicable, true, "Experiment should now be applicable.");
|
||||
Assert.equal(experiment.enabled, false, "Experiment should not be enabled.");
|
||||
|
||||
yield experiment.start();
|
||||
Assert.equal(experiment.enabled, true, "Experiment should now be enabled.");
|
||||
|
||||
yield experiment.stop();
|
||||
Assert.equal(experiment.enabled, false, "Experiment should not be enabled.");
|
||||
|
||||
yield experiment.start();
|
||||
Assert.equal(experiment.enabled, true, "Experiment should now be enabled.");
|
||||
|
||||
let shouldStop = yield experiment._shouldStop();
|
||||
Assert.equal(shouldStop, false, "shouldStop should be false.");
|
||||
let maybeStop = yield experiment.maybeStop();
|
||||
Assert.equal(maybeStop, false, "Experiment should not have been stopped.");
|
||||
Assert.equal(experiment.enabled, true, "Experiment should be enabled.");
|
||||
|
||||
defineNow(gPolicy, futureDate(endDate, MS_IN_ONE_DAY));
|
||||
shouldStop = yield experiment._shouldStop();
|
||||
Assert.equal(shouldStop, true, "shouldStop should now be true.");
|
||||
maybeStop = yield experiment.maybeStop();
|
||||
Assert.equal(maybeStop, true, "Experiment should have been stopped.");
|
||||
Assert.equal(experiment.enabled, false, "Experiment should be disabled.");
|
||||
});
|
||||
|
||||
add_task(function* shutdown() {
|
||||
yield gReporter._shutdown();
|
||||
yield removeCacheFile();
|
||||
});
|
|
@ -0,0 +1,762 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Experiments",
|
||||
"resource:///modules/experiments/Experiments.jsm");
|
||||
|
||||
const FILE_MANIFEST = "experiments.manifest";
|
||||
const PREF_EXPERIMENTS_ENABLED = "experiments.enabled";
|
||||
const PREF_LOGGING_LEVEL = "experiments.logging.level";
|
||||
const PREF_LOGGING_DUMP = "experiments.logging.dump";
|
||||
const PREF_MANIFEST_URI = "experiments.manifest.uri";
|
||||
const PREF_FETCHINTERVAL = "experiments.manifest.fetchIntervalSeconds";
|
||||
|
||||
const MANIFEST_HANDLER = "manifests/handler";
|
||||
|
||||
const SEC_IN_ONE_DAY = 24 * 60 * 60;
|
||||
const MS_IN_ONE_DAY = SEC_IN_ONE_DAY * 1000;
|
||||
|
||||
let gProfileDir = null;
|
||||
let gHttpServer = null;
|
||||
let gHttpRoot = null;
|
||||
let gDataRoot = null;
|
||||
let gReporter = null;
|
||||
let gPolicy = null;
|
||||
let gManifestObject = null;
|
||||
let gManifestHandlerURI = null;
|
||||
let gTimerScheduleOffset = -1;
|
||||
|
||||
let gGlobalScope = this;
|
||||
function loadAddonManager() {
|
||||
let ns = {};
|
||||
Cu.import("resource://gre/modules/Services.jsm", ns);
|
||||
let head = "../../../../toolkit/mozapps/extensions/test/xpcshell/head_addons.js";
|
||||
let file = do_get_file(head);
|
||||
let uri = ns.Services.io.newFileURI(file);
|
||||
ns.Services.scriptloader.loadSubScript(uri.spec, gGlobalScope);
|
||||
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
|
||||
startupManager();
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_setup() {
|
||||
loadAddonManager();
|
||||
gProfileDir = do_get_profile();
|
||||
|
||||
gHttpServer = new HttpServer();
|
||||
gHttpServer.start(-1);
|
||||
let port = gHttpServer.identity.primaryPort;
|
||||
gHttpRoot = "http://localhost:" + port + "/";
|
||||
gDataRoot = gHttpRoot + "data/";
|
||||
gManifestHandlerURI = gHttpRoot + MANIFEST_HANDLER;
|
||||
gHttpServer.registerDirectory("/data/", do_get_cwd());
|
||||
gHttpServer.registerPathHandler("/" + MANIFEST_HANDLER, (request, response) => {
|
||||
response.setStatusLine(null, 200, "OK");
|
||||
response.write(JSON.stringify(gManifestObject));
|
||||
response.processAsync();
|
||||
response.finish();
|
||||
});
|
||||
do_register_cleanup(() => gHttpServer.stop(() => {}));
|
||||
|
||||
disableCertificateChecks();
|
||||
|
||||
Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
|
||||
Services.prefs.setIntPref(PREF_LOGGING_LEVEL, 0);
|
||||
Services.prefs.setBoolPref(PREF_LOGGING_DUMP, true);
|
||||
Services.prefs.setCharPref(PREF_MANIFEST_URI, gManifestHandlerURI);
|
||||
Services.prefs.setIntPref(PREF_FETCHINTERVAL, 0);
|
||||
|
||||
gReporter = yield getReporter("json_payload_simple");
|
||||
yield gReporter.collectMeasurements();
|
||||
let payload = yield gReporter.getJSONPayload(true);
|
||||
|
||||
gPolicy = new Experiments.Policy();
|
||||
patchPolicy(gPolicy, {
|
||||
updatechannel: () => "nightly",
|
||||
healthReportPayload: () => Promise.resolve(payload),
|
||||
oneshotTimer: (callback, timeout, thisObj, name) => gTimerScheduleOffset = timeout,
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// Test basic starting and stopping of experiments.
|
||||
|
||||
add_task(function* test_getExperiments() {
|
||||
const OBSERVER_TOPIC = "experiments-changed";
|
||||
let observerFireCount = 0;
|
||||
let expectedObserverFireCount = 0;
|
||||
let observer = () => ++observerFireCount;
|
||||
Services.obs.addObserver(observer, OBSERVER_TOPIC, false);
|
||||
|
||||
// Dates the following tests are based on.
|
||||
|
||||
let baseDate = new Date(2014, 5, 1, 12);
|
||||
let startDate1 = futureDate(baseDate, 50 * MS_IN_ONE_DAY);
|
||||
let endDate1 = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
|
||||
let startDate2 = futureDate(baseDate, 150 * MS_IN_ONE_DAY);
|
||||
let endDate2 = futureDate(baseDate, 200 * MS_IN_ONE_DAY);
|
||||
|
||||
// The manifest data we test with.
|
||||
|
||||
gManifestObject = {
|
||||
"version": 1,
|
||||
experiments: [
|
||||
{
|
||||
id: EXPERIMENT2_ID,
|
||||
xpiURL: gDataRoot + EXPERIMENT2_XPI_NAME,
|
||||
xpiHash: EXPERIMENT2_XPI_SHA1,
|
||||
startTime: dateToSeconds(startDate2),
|
||||
endTime: dateToSeconds(endDate2),
|
||||
maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
|
||||
appName: ["XPCShell"],
|
||||
channel: ["nightly"],
|
||||
},
|
||||
{
|
||||
id: EXPERIMENT1_ID,
|
||||
xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
|
||||
xpiHash: EXPERIMENT1_XPI_SHA1,
|
||||
startTime: dateToSeconds(startDate1),
|
||||
endTime: dateToSeconds(endDate1),
|
||||
maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
|
||||
appName: ["XPCShell"],
|
||||
channel: ["nightly"],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Data to compare the result of Experiments.getExperiments() against.
|
||||
|
||||
let experimentListData = [
|
||||
{
|
||||
id: EXPERIMENT2_ID,
|
||||
name: "Test experiment 2",
|
||||
description: "And yet another experiment that experiments experimentally.",
|
||||
},
|
||||
{
|
||||
id: EXPERIMENT1_ID,
|
||||
name: EXPERIMENT1_NAME,
|
||||
description: "Yet another experiment that experiments experimentally.",
|
||||
},
|
||||
];
|
||||
|
||||
let experiments = new Experiments.Experiments(gPolicy);
|
||||
|
||||
// Trigger update, clock set to before any activation.
|
||||
// Use updateManifest() to provide for coverage of that path.
|
||||
|
||||
let now = baseDate;
|
||||
gTimerScheduleOffset = -1;
|
||||
defineNow(gPolicy, now);
|
||||
|
||||
yield experiments.updateManifest();
|
||||
Assert.equal(observerFireCount, 0,
|
||||
"Experiments observer should not have been called yet.");
|
||||
let list = yield experiments.getExperiments();
|
||||
Assert.equal(list.length, 0, "Experiment list should be empty.");
|
||||
|
||||
// Trigger update, clock set for experiment 1 to start.
|
||||
|
||||
now = futureDate(startDate1, 5 * MS_IN_ONE_DAY);
|
||||
gTimerScheduleOffset = -1;
|
||||
defineNow(gPolicy, now);
|
||||
|
||||
yield experiments.updateManifest();
|
||||
Assert.equal(observerFireCount, ++expectedObserverFireCount,
|
||||
"Experiments observer should have been called.");
|
||||
|
||||
list = yield experiments.getExperiments();
|
||||
Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
|
||||
|
||||
experimentListData[1].active = true;
|
||||
experimentListData[1].endDate = now.getTime() + 10 * MS_IN_ONE_DAY;
|
||||
for (let k of Object.keys(experimentListData[1])) {
|
||||
Assert.equal(experimentListData[1][k], list[0][k],
|
||||
"Property " + k + " should match reference data.");
|
||||
}
|
||||
|
||||
Assert.equal(gTimerScheduleOffset, 10 * MS_IN_ONE_DAY,
|
||||
"Experiment re-evaluation should have been scheduled correctly.");
|
||||
|
||||
// Trigger update, clock set for experiment 1 to stop.
|
||||
|
||||
now = futureDate(endDate1, 1000);
|
||||
gTimerScheduleOffset = -1;
|
||||
defineNow(gPolicy, now);
|
||||
|
||||
yield experiments.updateManifest();
|
||||
Assert.equal(observerFireCount, ++expectedObserverFireCount,
|
||||
"Experiments observer should have been called.");
|
||||
|
||||
list = yield experiments.getExperiments();
|
||||
Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
|
||||
|
||||
experimentListData[1].active = false;
|
||||
experimentListData[1].endDate = now.getTime();
|
||||
for (let k of Object.keys(experimentListData[1])) {
|
||||
Assert.equal(experimentListData[1][k], list[0][k],
|
||||
"Property " + k + " should match reference data.");
|
||||
}
|
||||
|
||||
Assert.equal(gTimerScheduleOffset, startDate2 - now,
|
||||
"Experiment re-evaluation should have been scheduled correctly.");
|
||||
|
||||
// Trigger update, clock set for experiment 2 to start.
|
||||
// Use notify() to provide for coverage of that path.
|
||||
|
||||
now = startDate2;
|
||||
gTimerScheduleOffset = -1;
|
||||
defineNow(gPolicy, now);
|
||||
|
||||
experiments.notify();
|
||||
yield experiments._pendingTasksDone();
|
||||
Assert.equal(observerFireCount, ++expectedObserverFireCount,
|
||||
"Experiments observer should have been called.");
|
||||
|
||||
list = yield experiments.getExperiments();
|
||||
Assert.equal(list.length, 2, "Experiment list should have 2 entries now.");
|
||||
|
||||
experimentListData[0].active = true;
|
||||
experimentListData[0].endDate = now.getTime() + 10 * MS_IN_ONE_DAY;
|
||||
for (let i=0; i<experimentListData.length; ++i) {
|
||||
let entry = experimentListData[i];
|
||||
for (let k of Object.keys(entry)) {
|
||||
Assert.equal(entry[k], list[i][k],
|
||||
"Entry " + i + " - Property '" + k + "' should match reference data.");
|
||||
}
|
||||
}
|
||||
|
||||
Assert.equal(gTimerScheduleOffset, 10 * MS_IN_ONE_DAY,
|
||||
"Experiment re-evaluation should have been scheduled correctly.");
|
||||
|
||||
// Trigger update, clock set for experiment 2 to stop.
|
||||
|
||||
now = futureDate(startDate2, 10 * MS_IN_ONE_DAY + 1000);
|
||||
gTimerScheduleOffset = -1;
|
||||
defineNow(gPolicy, now);
|
||||
experiments.notify();
|
||||
yield experiments._pendingTasksDone();
|
||||
Assert.equal(observerFireCount, ++expectedObserverFireCount,
|
||||
"Experiments observer should have been called.");
|
||||
|
||||
list = yield experiments.getExperiments();
|
||||
Assert.equal(list.length, 2, "Experiment list should have 2 entries now.");
|
||||
|
||||
experimentListData[0].active = false;
|
||||
experimentListData[0].endDate = now.getTime();
|
||||
for (let i=0; i<experimentListData.length; ++i) {
|
||||
let entry = experimentListData[i];
|
||||
for (let k of Object.keys(entry)) {
|
||||
Assert.equal(entry[k], list[i][k],
|
||||
"Entry " + i + " - Property '" + k + "' should match reference data.");
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup.
|
||||
|
||||
Services.obs.removeObserver(observer, OBSERVER_TOPIC);
|
||||
yield experiments.uninit();
|
||||
yield removeCacheFile();
|
||||
});
|
||||
|
||||
// Test explicitly disabling experiments.
|
||||
|
||||
add_task(function* test_disableExperiment() {
|
||||
// Dates this test is based on.
|
||||
|
||||
let startDate = new Date(2004, 10, 9, 12);
|
||||
let endDate = futureDate(startDate, 100 * MS_IN_ONE_DAY);
|
||||
|
||||
// The manifest data we test with.
|
||||
|
||||
gManifestObject = {
|
||||
"version": 1,
|
||||
experiments: [
|
||||
{
|
||||
id: EXPERIMENT1_ID,
|
||||
xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
|
||||
xpiHash: EXPERIMENT1_XPI_SHA1,
|
||||
startTime: dateToSeconds(startDate),
|
||||
endTime: dateToSeconds(endDate),
|
||||
maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
|
||||
appName: ["XPCShell"],
|
||||
channel: ["nightly"],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Data to compare the result of Experiments.getExperiments() against.
|
||||
|
||||
let experimentInfo = {
|
||||
id: EXPERIMENT1_ID,
|
||||
name: EXPERIMENT1_NAME,
|
||||
description: "Yet another experiment that experiments experimentally.",
|
||||
};
|
||||
|
||||
let experiments = new Experiments.Experiments(gPolicy);
|
||||
|
||||
// Trigger update, clock set for the experiment to start.
|
||||
|
||||
let now = futureDate(startDate, 5 * MS_IN_ONE_DAY);
|
||||
defineNow(gPolicy, now);
|
||||
yield experiments.updateManifest();
|
||||
|
||||
let list = yield experiments.getExperiments();
|
||||
Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
|
||||
|
||||
experimentInfo.active = true;
|
||||
experimentInfo.endDate = now.getTime() + 10 * MS_IN_ONE_DAY;
|
||||
for (let k of Object.keys(experimentInfo)) {
|
||||
Assert.equal(experimentInfo[k], list[0][k],
|
||||
"Property " + k + " should match reference data.");
|
||||
}
|
||||
|
||||
// Test disabling the experiment.
|
||||
|
||||
now = futureDate(now, 1 * MS_IN_ONE_DAY);
|
||||
defineNow(gPolicy, now);
|
||||
yield experiments.disableExperiment(EXPERIMENT1_ID);
|
||||
|
||||
list = yield experiments.getExperiments();
|
||||
Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
|
||||
|
||||
experimentInfo.active = false;
|
||||
experimentInfo.endDate = now.getTime();
|
||||
for (let k of Object.keys(experimentInfo)) {
|
||||
Assert.equal(experimentInfo[k], list[0][k],
|
||||
"Property " + k + " should match reference data.");
|
||||
}
|
||||
|
||||
// Test that updating the list doesn't re-enable it.
|
||||
|
||||
now = futureDate(now, 1 * MS_IN_ONE_DAY);
|
||||
defineNow(gPolicy, now);
|
||||
yield experiments.updateManifest();
|
||||
|
||||
list = yield experiments.getExperiments();
|
||||
Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
|
||||
|
||||
for (let k of Object.keys(experimentInfo)) {
|
||||
Assert.equal(experimentInfo[k], list[0][k],
|
||||
"Property " + k + " should match reference data.");
|
||||
}
|
||||
|
||||
// Cleanup.
|
||||
|
||||
yield experiments.uninit();
|
||||
yield removeCacheFile();
|
||||
});
|
||||
|
||||
add_task(function* test_disableExperimentsFeature() {
|
||||
// Dates this test is based on.
|
||||
|
||||
let startDate = new Date(2004, 10, 9, 12);
|
||||
let endDate = futureDate(startDate, 100 * MS_IN_ONE_DAY);
|
||||
|
||||
// The manifest data we test with.
|
||||
|
||||
gManifestObject = {
|
||||
"version": 1,
|
||||
experiments: [
|
||||
{
|
||||
id: EXPERIMENT1_ID,
|
||||
xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
|
||||
xpiHash: EXPERIMENT1_XPI_SHA1,
|
||||
startTime: dateToSeconds(startDate),
|
||||
endTime: dateToSeconds(endDate),
|
||||
maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
|
||||
appName: ["XPCShell"],
|
||||
channel: ["nightly"],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Data to compare the result of Experiments.getExperiments() against.
|
||||
|
||||
let experimentInfo = {
|
||||
id: EXPERIMENT1_ID,
|
||||
name: EXPERIMENT1_NAME,
|
||||
description: "Yet another experiment that experiments experimentally.",
|
||||
};
|
||||
|
||||
let experiments = new Experiments.Experiments(gPolicy);
|
||||
Assert.equal(experiments.enabled, true, "Experiments feature should be enabled.");
|
||||
|
||||
// Trigger update, clock set for the experiment to start.
|
||||
|
||||
let now = futureDate(startDate, 5 * MS_IN_ONE_DAY);
|
||||
defineNow(gPolicy, now);
|
||||
yield experiments.updateManifest();
|
||||
|
||||
let list = yield experiments.getExperiments();
|
||||
Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
|
||||
|
||||
experimentInfo.active = true;
|
||||
experimentInfo.endDate = now.getTime() + 10 * MS_IN_ONE_DAY;
|
||||
for (let k of Object.keys(experimentInfo)) {
|
||||
Assert.equal(experimentInfo[k], list[0][k],
|
||||
"Property " + k + " should match reference data.");
|
||||
}
|
||||
|
||||
// Test disabling experiments.
|
||||
|
||||
yield experiments._toggleExperimentsEnabled(false);
|
||||
Assert.equal(experiments.enabled, false, "Experiments feature should be disabled now.");
|
||||
|
||||
list = yield experiments.getExperiments();
|
||||
Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
|
||||
|
||||
experimentInfo.active = false;
|
||||
experimentInfo.endDate = now.getTime();
|
||||
for (let k of Object.keys(experimentInfo)) {
|
||||
Assert.equal(experimentInfo[k], list[0][k],
|
||||
"Property " + k + " should match reference data.");
|
||||
}
|
||||
|
||||
// Test that updating the list doesn't re-enable it.
|
||||
|
||||
now = futureDate(now, 1 * MS_IN_ONE_DAY);
|
||||
defineNow(gPolicy, now);
|
||||
try {
|
||||
yield experiments.updateManifest();
|
||||
} catch (e) {
|
||||
// Exception expected, the feature is disabled.
|
||||
}
|
||||
|
||||
list = yield experiments.getExperiments();
|
||||
Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
|
||||
|
||||
for (let k of Object.keys(experimentInfo)) {
|
||||
Assert.equal(experimentInfo[k], list[0][k],
|
||||
"Property " + k + " should match reference data.");
|
||||
}
|
||||
|
||||
// Cleanup.
|
||||
|
||||
yield experiments.uninit();
|
||||
yield removeCacheFile();
|
||||
});
|
||||
|
||||
// Test that after a failed experiment install:
|
||||
// * the next applicable experiment gets installed
|
||||
// * changing the experiments data later triggers re-evaluation
|
||||
|
||||
add_task(function* test_installFailure() {
|
||||
const OBSERVER_TOPIC = "experiments-changed";
|
||||
let observerFireCount = 0;
|
||||
let expectedObserverFireCount = 0;
|
||||
let observer = () => ++observerFireCount;
|
||||
Services.obs.addObserver(observer, OBSERVER_TOPIC, false);
|
||||
|
||||
// Dates the following tests are based on.
|
||||
|
||||
let baseDate = new Date(2014, 5, 1, 12);
|
||||
let startDate = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
|
||||
let endDate = futureDate(baseDate, 10000 * MS_IN_ONE_DAY);
|
||||
|
||||
// The manifest data we test with.
|
||||
|
||||
gManifestObject = {
|
||||
"version": 1,
|
||||
experiments: [
|
||||
{
|
||||
id: EXPERIMENT1_ID,
|
||||
xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
|
||||
xpiHash: EXPERIMENT1_XPI_SHA1,
|
||||
startTime: dateToSeconds(startDate),
|
||||
endTime: dateToSeconds(endDate),
|
||||
maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
|
||||
appName: ["XPCShell"],
|
||||
channel: ["nightly"],
|
||||
},
|
||||
{
|
||||
id: EXPERIMENT2_ID,
|
||||
xpiURL: gDataRoot + EXPERIMENT2_XPI_NAME,
|
||||
xpiHash: EXPERIMENT2_XPI_SHA1,
|
||||
startTime: dateToSeconds(startDate),
|
||||
endTime: dateToSeconds(endDate),
|
||||
maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
|
||||
appName: ["XPCShell"],
|
||||
channel: ["nightly"],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
// Data to compare the result of Experiments.getExperiments() against.
|
||||
|
||||
let experimentListData = [
|
||||
{
|
||||
id: EXPERIMENT1_ID,
|
||||
name: EXPERIMENT1_NAME,
|
||||
description: "Yet another experiment that experiments experimentally.",
|
||||
},
|
||||
{
|
||||
id: EXPERIMENT2_ID,
|
||||
name: "Test experiment 2",
|
||||
description: "And yet another experiment that experiments experimentally.",
|
||||
},
|
||||
];
|
||||
|
||||
let experiments = new Experiments.Experiments(gPolicy);
|
||||
|
||||
// Trigger update, clock set to before any activation.
|
||||
|
||||
let now = baseDate;
|
||||
defineNow(gPolicy, now);
|
||||
yield experiments.updateManifest();
|
||||
Assert.equal(observerFireCount, 0,
|
||||
"Experiments observer should not have been called yet.");
|
||||
let list = yield experiments.getExperiments();
|
||||
Assert.equal(list.length, 0, "Experiment list should be empty.");
|
||||
|
||||
// Trigger update, clock set for experiment 1 & 2 to start,
|
||||
// invalid hash for experiment 1.
|
||||
// Order in the manifest matters, so we should start experiment 1,
|
||||
// fail to install it & start experiment 2 instead.
|
||||
|
||||
now = futureDate(startDate, 10 * MS_IN_ONE_DAY);
|
||||
defineNow(gPolicy, now);
|
||||
gManifestObject.experiments[0].xpiHash = "sha1:0000000000000000000000000000000000000000";
|
||||
yield experiments.updateManifest();
|
||||
Assert.equal(observerFireCount, ++expectedObserverFireCount,
|
||||
"Experiments observer should have been called.");
|
||||
|
||||
list = yield experiments.getExperiments();
|
||||
Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
|
||||
Assert.equal(list[0].id, EXPERIMENT2_ID, "Experiment 2 should be the sole entry.");
|
||||
Assert.equal(list[0].active, true, "Experiment 2 should be active.");
|
||||
|
||||
// Trigger update, clock set for experiment 2 to stop.
|
||||
|
||||
now = futureDate(now, 20 * MS_IN_ONE_DAY);
|
||||
defineNow(gPolicy, now);
|
||||
yield experiments.updateManifest();
|
||||
Assert.equal(observerFireCount, ++expectedObserverFireCount,
|
||||
"Experiments observer should have been called.");
|
||||
|
||||
experimentListData[0].active = false;
|
||||
experimentListData[0].endDate = now;
|
||||
|
||||
list = yield experiments.getExperiments();
|
||||
Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
|
||||
Assert.equal(list[0].id, EXPERIMENT2_ID, "Experiment 2 should be the sole entry.");
|
||||
Assert.equal(list[0].active, false, "Experiment should not be active.");
|
||||
|
||||
// Trigger update with a fixed entry for experiment 1,
|
||||
// which should get re-evaluated & started now.
|
||||
|
||||
now = futureDate(now, 20 * MS_IN_ONE_DAY);
|
||||
defineNow(gPolicy, now);
|
||||
gManifestObject.experiments[0].xpiHash = EXPERIMENT1_XPI_SHA1;
|
||||
yield experiments.updateManifest();
|
||||
Assert.equal(observerFireCount, ++expectedObserverFireCount,
|
||||
"Experiments observer should have been called.");
|
||||
|
||||
experimentListData[0].active = true;
|
||||
experimentListData[0].endDate = now.getTime() + 10 * MS_IN_ONE_DAY;
|
||||
|
||||
list = yield experiments.getExperiments();
|
||||
Assert.equal(list.length, 2, "Experiment list should have 2 entries now.");
|
||||
|
||||
for (let i=0; i<experimentListData.length; ++i) {
|
||||
let entry = experimentListData[i];
|
||||
for (let k of Object.keys(entry)) {
|
||||
Assert.equal(entry[k], list[i][k],
|
||||
"Entry " + i + " - Property '" + k + "' should match reference data.");
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup.
|
||||
|
||||
Services.obs.removeObserver(observer, OBSERVER_TOPIC);
|
||||
yield experiments.uninit();
|
||||
yield removeCacheFile();
|
||||
});
|
||||
|
||||
// Test that after an experiment was disabled by user action,
|
||||
// the experiment is not activated again if manifest data changes.
|
||||
|
||||
add_task(function* test_userDisabledAndUpdated() {
|
||||
const OBSERVER_TOPIC = "experiments-changed";
|
||||
let observerFireCount = 0;
|
||||
let expectedObserverFireCount = 0;
|
||||
let observer = () => ++observerFireCount;
|
||||
Services.obs.addObserver(observer, OBSERVER_TOPIC, false);
|
||||
|
||||
// Dates the following tests are based on.
|
||||
|
||||
let baseDate = new Date(2014, 5, 1, 12);
|
||||
let startDate = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
|
||||
let endDate = futureDate(baseDate, 10000 * MS_IN_ONE_DAY);
|
||||
|
||||
// The manifest data we test with.
|
||||
|
||||
gManifestObject = {
|
||||
"version": 1,
|
||||
experiments: [
|
||||
{
|
||||
id: EXPERIMENT1_ID,
|
||||
xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
|
||||
xpiHash: EXPERIMENT1_XPI_SHA1,
|
||||
startTime: dateToSeconds(startDate),
|
||||
endTime: dateToSeconds(endDate),
|
||||
maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
|
||||
appName: ["XPCShell"],
|
||||
channel: ["nightly"],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
let experiments = new Experiments.Experiments(gPolicy);
|
||||
|
||||
// Trigger update, clock set to before any activation.
|
||||
|
||||
let now = baseDate;
|
||||
defineNow(gPolicy, now);
|
||||
yield experiments.updateManifest();
|
||||
Assert.equal(observerFireCount, 0,
|
||||
"Experiments observer should not have been called yet.");
|
||||
let list = yield experiments.getExperiments();
|
||||
Assert.equal(list.length, 0, "Experiment list should be empty.");
|
||||
|
||||
// Trigger update, clock set for experiment 1 to start.
|
||||
|
||||
now = futureDate(startDate, 10 * MS_IN_ONE_DAY);
|
||||
defineNow(gPolicy, now);
|
||||
yield experiments.updateManifest();
|
||||
Assert.equal(observerFireCount, ++expectedObserverFireCount,
|
||||
"Experiments observer should have been called.");
|
||||
|
||||
list = yield experiments.getExperiments();
|
||||
Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
|
||||
Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
|
||||
Assert.equal(list[0].active, true, "Experiment 1 should be active.");
|
||||
|
||||
// Explicitly disable an experiment.
|
||||
|
||||
now = futureDate(now, 20 * MS_IN_ONE_DAY);
|
||||
defineNow(gPolicy, now);
|
||||
yield experiments.disableExperiment(EXPERIMENT1_ID);
|
||||
Assert.equal(observerFireCount, ++expectedObserverFireCount,
|
||||
"Experiments observer should have been called.");
|
||||
|
||||
list = yield experiments.getExperiments();
|
||||
Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
|
||||
Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
|
||||
Assert.equal(list[0].active, false, "Experiment should not be active anymore.");
|
||||
|
||||
// Trigger an update with a faked change for experiment 1.
|
||||
|
||||
now = futureDate(now, 20 * MS_IN_ONE_DAY);
|
||||
defineNow(gPolicy, now);
|
||||
experiments._experiments.get(EXPERIMENT1_ID)._manifestData.xpiHash =
|
||||
"sha1:0000000000000000000000000000000000000000";
|
||||
yield experiments.updateManifest();
|
||||
Assert.equal(observerFireCount, expectedObserverFireCount,
|
||||
"Experiments observer should not have been called.");
|
||||
|
||||
list = yield experiments.getExperiments();
|
||||
Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
|
||||
Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
|
||||
Assert.equal(list[0].active, false, "Experiment should still be inactive.");
|
||||
|
||||
// Cleanup.
|
||||
|
||||
Services.obs.removeObserver(observer, OBSERVER_TOPIC);
|
||||
yield experiments.uninit();
|
||||
yield removeCacheFile();
|
||||
});
|
||||
|
||||
// Test that changing the hash for an active experiments triggers an
|
||||
// update for it.
|
||||
|
||||
add_task(function* test_updateActiveExperiment() {
|
||||
const OBSERVER_TOPIC = "experiments-changed";
|
||||
let observerFireCount = 0;
|
||||
let expectedObserverFireCount = 0;
|
||||
let observer = () => ++observerFireCount;
|
||||
Services.obs.addObserver(observer, OBSERVER_TOPIC, false);
|
||||
|
||||
// Dates the following tests are based on.
|
||||
|
||||
let baseDate = new Date(2014, 5, 1, 12);
|
||||
let startDate = futureDate(baseDate, 100 * MS_IN_ONE_DAY);
|
||||
let endDate = futureDate(baseDate, 10000 * MS_IN_ONE_DAY);
|
||||
|
||||
// The manifest data we test with.
|
||||
|
||||
gManifestObject = {
|
||||
"version": 1,
|
||||
experiments: [
|
||||
{
|
||||
id: EXPERIMENT1_ID,
|
||||
xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
|
||||
xpiHash: EXPERIMENT1_XPI_SHA1,
|
||||
startTime: dateToSeconds(startDate),
|
||||
endTime: dateToSeconds(endDate),
|
||||
maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
|
||||
appName: ["XPCShell"],
|
||||
channel: ["nightly"],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
let experiments = new Experiments.Experiments(gPolicy);
|
||||
|
||||
// Trigger update, clock set to before any activation.
|
||||
|
||||
let now = baseDate;
|
||||
defineNow(gPolicy, now);
|
||||
yield experiments.updateManifest();
|
||||
Assert.equal(observerFireCount, 0,
|
||||
"Experiments observer should not have been called yet.");
|
||||
let list = yield experiments.getExperiments();
|
||||
Assert.equal(list.length, 0, "Experiment list should be empty.");
|
||||
|
||||
// Trigger update, clock set for the experiment to start.
|
||||
|
||||
now = futureDate(startDate, 10 * MS_IN_ONE_DAY);
|
||||
defineNow(gPolicy, now);
|
||||
yield experiments.updateManifest();
|
||||
Assert.equal(observerFireCount, ++expectedObserverFireCount,
|
||||
"Experiments observer should have been called.");
|
||||
|
||||
list = yield experiments.getExperiments();
|
||||
Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
|
||||
Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
|
||||
Assert.equal(list[0].active, true, "Experiment 1 should be active.");
|
||||
Assert.equal(list[0].name, EXPERIMENT1_NAME, "Experiments name should match.");
|
||||
|
||||
// Trigger an update for the active experiment by changing it's hash (and xpi)
|
||||
// in the manifest.
|
||||
|
||||
now = futureDate(now, 1 * MS_IN_ONE_DAY);
|
||||
defineNow(gPolicy, now);
|
||||
gManifestObject.experiments[0].xpiHash = EXPERIMENT1A_XPI_SHA1;
|
||||
gManifestObject.experiments[0].xpiURL = gDataRoot + EXPERIMENT1A_XPI_NAME;
|
||||
yield experiments.updateManifest();
|
||||
Assert.equal(observerFireCount, ++expectedObserverFireCount,
|
||||
"Experiments observer should have been called.");
|
||||
|
||||
list = yield experiments.getExperiments();
|
||||
Assert.equal(list.length, 1, "Experiment list should have 1 entry now.");
|
||||
Assert.equal(list[0].id, EXPERIMENT1_ID, "Experiment 1 should be the sole entry.");
|
||||
Assert.equal(list[0].active, true, "Experiment 1 should still be active.");
|
||||
Assert.equal(list[0].name, EXPERIMENT1A_NAME, "Experiments name should have been updated.");
|
||||
|
||||
// Cleanup.
|
||||
|
||||
Services.obs.removeObserver(observer, OBSERVER_TOPIC);
|
||||
yield experiments.uninit();
|
||||
yield removeCacheFile();
|
||||
});
|
||||
|
||||
add_task(function* shutdown() {
|
||||
yield gReporter._shutdown();
|
||||
yield removeCacheFile();
|
||||
});
|
|
@ -0,0 +1,174 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
|
||||
Cu.import("resource:///modules/experiments/Experiments.jsm");
|
||||
|
||||
const FILE_MANIFEST = "experiments.manifest";
|
||||
const PREF_EXPERIMENTS_ENABLED = "experiments.enabled";
|
||||
const PREF_LOGGING_LEVEL = "experiments.logging.level";
|
||||
const PREF_LOGGING_DUMP = "experiments.logging.dump";
|
||||
const PREF_MANIFEST_URI = "experiments.manifest.uri";
|
||||
|
||||
const SEC_IN_ONE_DAY = 24 * 60 * 60;
|
||||
const MS_IN_ONE_DAY = SEC_IN_ONE_DAY * 1000;
|
||||
|
||||
let gProfileDir = null;
|
||||
let gHttpServer = null;
|
||||
let gHttpRoot = null;
|
||||
let gReporter = null;
|
||||
let gPolicy = null;
|
||||
|
||||
|
||||
function ManifestEntry(data) {
|
||||
this.id = EXPERIMENT1_ID;
|
||||
this.xpiURL = "http://localhost:1/dummy.xpi";
|
||||
this.xpiHash = EXPERIMENT1_XPI_SHA1;
|
||||
this.startTime = new Date(2010, 0, 1, 12).getTime() / 1000;
|
||||
this.endTime = new Date(9001, 0, 1, 12).getTime() / 1000;
|
||||
this.maxActiveSeconds = SEC_IN_ONE_DAY;
|
||||
this.appName = ["XPCShell"];
|
||||
this.channel = ["nightly"];
|
||||
|
||||
data = data || {};
|
||||
for (let k of Object.keys(data)) {
|
||||
this[k] = data[k];
|
||||
}
|
||||
|
||||
if (!this.endTime) {
|
||||
this.endTime = this.startTime + 5 * SEC_IN_ONE_DAY;
|
||||
}
|
||||
}
|
||||
|
||||
function applicableFromManifestData(data, policy) {
|
||||
let manifestData = new ManifestEntry(data);
|
||||
let entry = new Experiments.ExperimentEntry(policy);
|
||||
entry.initFromManifestData(manifestData);
|
||||
return entry.isApplicable();
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_setup() {
|
||||
createAppInfo();
|
||||
gProfileDir = do_get_profile();
|
||||
gPolicy = new Experiments.Policy();
|
||||
|
||||
gReporter = yield getReporter("json_payload_simple");
|
||||
yield gReporter.collectMeasurements();
|
||||
let payload = yield gReporter.getJSONPayload(true);
|
||||
|
||||
patchPolicy(gPolicy, {
|
||||
updatechannel: () => "nightly",
|
||||
healthReportPayload: () => Promise.resolve(payload),
|
||||
});
|
||||
|
||||
Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
|
||||
Services.prefs.setIntPref(PREF_LOGGING_LEVEL, 0);
|
||||
Services.prefs.setBoolPref(PREF_LOGGING_DUMP, true);
|
||||
|
||||
let experiments = new Experiments.Experiments();
|
||||
});
|
||||
|
||||
// This function exists solely to be .toSource()d
|
||||
const sanityFilter = function filter(c) {
|
||||
if (c.telemetryPayload === undefined) {
|
||||
throw Error("No .telemetryPayload");
|
||||
}
|
||||
if (c.telemetryPayload.simpleMeasurements === undefined) {
|
||||
throw Error("No .simpleMeasurements");
|
||||
}
|
||||
if (c.healthReportPayload === undefined) {
|
||||
throw Error("No .healthReportPayload");
|
||||
}
|
||||
if (c.healthReportPayload.geckoAppInfo == undefined) {
|
||||
throw Error("No .geckoAppInfo");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
add_task(function* test_simpleFields() {
|
||||
let testData = [
|
||||
// "expected applicable?", failure reason or null, manifest data
|
||||
[false, "os", {os: ["42", "abcdef"]}],
|
||||
[true, null, {os: [gAppInfo.OS, "plan9"]}],
|
||||
[false, "buildIDs", {buildIDs: ["not-a-build-id", gAppInfo.platformBuildID + "-invalid"]}],
|
||||
[true, null, {buildIDs: ["not-a-build-id", gAppInfo.platformBuildID]}],
|
||||
[true, null, {jsfilter: "function filter(c) { return true; }"}],
|
||||
[false, null, {jsfilter: "function filter(c) { return false; }"}],
|
||||
[true, null, {jsfilter: "function filter(c) { return 123; }"}], // truthy
|
||||
[false, null, {jsfilter: "function filter(c) { return ''; }"}], // falsy
|
||||
[false, null, {jsfilter: "function filter(c) { var a = []; }"}], // undefined
|
||||
[false, "jsfilter:rejected some error", {jsfilter: "function filter(c) { throw new Error('some error'); }"}],
|
||||
[false, "jsfilter:evalFailure", {jsfilter: "123, this won't work"}],
|
||||
[true, {jsfilter: "var filter = " + sanityFilter.toSource()}],
|
||||
];
|
||||
|
||||
for (let i=0; i<testData.length; ++i) {
|
||||
let entry = testData[i];
|
||||
let applicable;
|
||||
let reason = null;
|
||||
|
||||
yield applicableFromManifestData(entry[2], gPolicy).then(
|
||||
value => applicable = value,
|
||||
value => {
|
||||
applicable = false;
|
||||
reason = value;
|
||||
}
|
||||
);
|
||||
|
||||
Assert.equal(applicable, entry[0],
|
||||
"Experiment entry applicability should match for test "
|
||||
+ i + ": " + JSON.stringify(entry[2]));
|
||||
if (!applicable) {
|
||||
Assert.equal(reason, entry[1], "Experiment rejection reason should match for test " + i);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* test_times() {
|
||||
let baseDate = new Date(2014, 5, 6, 12);
|
||||
let baseTimeSec = baseDate.getTime() / 1000;
|
||||
let testData = [
|
||||
// "expected applicable?", rejection reason or null, fake now date, manifest data
|
||||
[false, "maxStartTime", baseDate,
|
||||
{maxStartTime: baseTimeSec - 10 * SEC_IN_ONE_DAY}],
|
||||
[false, "endTime", baseDate,
|
||||
{startTime: baseTimeSec - 10 * SEC_IN_ONE_DAY,
|
||||
endTime: baseTimeSec - 5 * SEC_IN_ONE_DAY}],
|
||||
[true, null, baseDate,
|
||||
{startTime: baseTimeSec - 5 * SEC_IN_ONE_DAY,
|
||||
endTime: baseTimeSec + 10 * SEC_IN_ONE_DAY}],
|
||||
];
|
||||
|
||||
for (let i=0; i<testData.length; ++i) {
|
||||
let entry = testData[i];
|
||||
let applicable;
|
||||
let reason = null;
|
||||
defineNow(gPolicy, entry[2]);
|
||||
|
||||
yield applicableFromManifestData(entry[3], gPolicy).then(
|
||||
value => applicable = value,
|
||||
value => {
|
||||
applicable = false;
|
||||
reason = value;
|
||||
}
|
||||
);
|
||||
|
||||
Assert.equal(applicable, entry[0],
|
||||
"Experiment entry applicability should match for test "
|
||||
+ i + ": " + JSON.stringify([entry[2], entry[3]]));
|
||||
if (!applicable && entry[1]) {
|
||||
Assert.equal(reason, entry[1], "Experiment rejection reason should match for test " + i);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function* shutdown() {
|
||||
yield gReporter._shutdown();
|
||||
yield removeCacheFile();
|
||||
});
|
|
@ -0,0 +1,84 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
Cu.import("resource:///modules/experiments/Experiments.jsm");
|
||||
|
||||
const PREF_EXPERIMENTS_ENABLED = "experiments.enabled";
|
||||
const PREF_LOGGING_LEVEL = "experiments.logging.level";
|
||||
const PREF_LOGGING_DUMP = "experiments.logging.dump";
|
||||
const PREF_MANIFEST_URI = "experiments.manifest.uri";
|
||||
|
||||
|
||||
let gProfileDir = null;
|
||||
let gHttpServer = null;
|
||||
let gHttpRoot = null;
|
||||
let gPolicy = new Experiments.Policy();
|
||||
|
||||
function run_test() {
|
||||
createAppInfo();
|
||||
gProfileDir = do_get_profile();
|
||||
|
||||
gHttpServer = new HttpServer();
|
||||
gHttpServer.start(-1);
|
||||
let port = gHttpServer.identity.primaryPort;
|
||||
gHttpRoot = "http://localhost:" + port + "/";
|
||||
gHttpServer.registerDirectory("/", do_get_cwd());
|
||||
do_register_cleanup(() => gHttpServer.stop(() => {}));
|
||||
|
||||
disableCertificateChecks();
|
||||
|
||||
Services.prefs.setBoolPref(PREF_EXPERIMENTS_ENABLED, true);
|
||||
Services.prefs.setIntPref(PREF_LOGGING_LEVEL, 0);
|
||||
Services.prefs.setBoolPref(PREF_LOGGING_DUMP, true);
|
||||
|
||||
patchPolicy(gPolicy, {
|
||||
updatechannel: () => "nightly",
|
||||
});
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* test_fetchAndCache() {
|
||||
Services.prefs.setCharPref(PREF_MANIFEST_URI, gHttpRoot + "experiments_1.manifest");
|
||||
let ex = new Experiments.Experiments(gPolicy);
|
||||
|
||||
Assert.equal(ex._experiments.size, 0, "There should be no cached experiments yet.");
|
||||
yield ex.updateManifest();
|
||||
Assert.notEqual(ex._experiments.size, 0, "There should be cached experiments now.");
|
||||
|
||||
yield ex.uninit();
|
||||
});
|
||||
|
||||
add_task(function* test_checkCache() {
|
||||
let ex = new Experiments.Experiments(gPolicy);
|
||||
Assert.equal(ex._experiments.size, 0, "There should be no cached experiments yet.");
|
||||
|
||||
yield ex._loadFromCache();
|
||||
Assert.notEqual(ex._experiments.size, 0, "There should be cached experiments now.");
|
||||
|
||||
yield ex.uninit();
|
||||
});
|
||||
|
||||
add_task(function* test_fetchInvalid() {
|
||||
Services.prefs.setCharPref(PREF_MANIFEST_URI, gHttpRoot + "invalid.manifest");
|
||||
yield removeCacheFile();
|
||||
|
||||
let ex = new Experiments.Experiments(gPolicy);
|
||||
Assert.equal(ex._experiments.size, 0, "There should be no cached experiments yet.");
|
||||
|
||||
let error;
|
||||
yield ex.updateManifest().then(() => error = false, () => error = true);
|
||||
Assert.ok(error, "Updating the manifest should not have succeeded.");
|
||||
Assert.equal(ex._experiments.size, 0, "There should still be no cached experiments.");
|
||||
|
||||
yield ex.uninit();
|
||||
});
|
||||
|
||||
add_task(function* shutdown() {
|
||||
yield removeCacheFile();
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
[DEFAULT]
|
||||
head = head.js
|
||||
tail =
|
||||
firefox-appdir = browser
|
||||
support-files =
|
||||
experiments_1.manifest
|
||||
../experiment-1.xpi
|
||||
../experiment-1a.xpi
|
||||
../experiment-2.xpi
|
||||
|
||||
[test_activate.js]
|
||||
[test_api.js]
|
||||
[test_conditions.js]
|
||||
[test_fetch.js]
|
|
@ -9,6 +9,7 @@ CONFIGURE_SUBST_FILES += ['installer/Makefile']
|
|||
PARALLEL_DIRS += [
|
||||
'base',
|
||||
'components',
|
||||
'experiments',
|
||||
'fuel',
|
||||
'locales',
|
||||
'modules',
|
||||
|
|
|
@ -28,9 +28,11 @@ AC_DEFUN([MOZ_SET_FRAMEPTR_FLAGS], [
|
|||
esac
|
||||
fi
|
||||
|
||||
# if we are debugging or profiling, we want a frame pointer.
|
||||
# if we are debugging, profiling or using ASAN, we want a frame pointer.
|
||||
if test -z "$MOZ_OPTIMIZE" -o \
|
||||
-n "$MOZ_PROFILING" -o -n "$MOZ_DEBUG"; then
|
||||
-n "$MOZ_PROFILING" -o \
|
||||
-n "$MOZ_DEBUG" -o \
|
||||
-n "$MOZ_ASAN"; then
|
||||
MOZ_FRAMEPTR_FLAGS="$MOZ_ENABLE_FRAME_PTR"
|
||||
else
|
||||
MOZ_FRAMEPTR_FLAGS="$MOZ_DISABLE_FRAME_PTR"
|
||||
|
|
|
@ -8933,9 +8933,9 @@ if test "$MOZ_TREE_FREETYPE"; then
|
|||
export CXXFLAGS="$CXXFLAGS $MOZ_DEBUG_FLAGS"
|
||||
export LDFLAGS="$LDFLAGS $MOZ_DEBUG_LDFLAGS"
|
||||
export LIBPNG_CFLAGS="$MOZ_PNG_CFLAGS"
|
||||
export LIBPNG_LDFLAGS="$MOZ_PNG_LIBS"
|
||||
export LIBPNG_LIBS="$MOZ_PNG_LIBS"
|
||||
export CONFIG_FILES="unix-cc.mk:unix-cc.in unix-def.mk:unix-def.in freetype-config freetype2.pc:freetype2.in"
|
||||
ac_configure_args="$ac_configure_args --host=$target --disable-shared --with-pic=yes"
|
||||
ac_configure_args="$ac_configure_args --host=$target --disable-shared --with-pic=yes --with-png=yes"
|
||||
|
||||
if ! test -e modules; then
|
||||
mkdir modules
|
||||
|
|
|
@ -5473,6 +5473,14 @@ nsDocument::CustomElementConstructor(JSContext* aCx, unsigned aArgc, JS::Value*
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
nsDocument::IsRegisterElementEnabled(JSContext* aCx, JSObject* aObject)
|
||||
{
|
||||
JS::Rooted<JSObject*> obj(aCx, aObject);
|
||||
return Preferences::GetBool("dom.webcomponents.enabled") ||
|
||||
IsInCertifiedApp(aCx, obj);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDocument::RegisterUnresolvedElement(Element* aElement, nsIAtom* aTypeName)
|
||||
{
|
||||
|
|
|
@ -1421,6 +1421,8 @@ public:
|
|||
uint32_t aNamespaceID,
|
||||
mozilla::ErrorResult& rv);
|
||||
|
||||
static bool IsRegisterElementEnabled(JSContext* aCx, JSObject* aObject);
|
||||
|
||||
// The "registry" from the web components spec.
|
||||
nsRefPtr<Registry> mRegistry;
|
||||
|
||||
|
|
|
@ -3577,7 +3577,7 @@ CanvasRenderingContext2D::DrawWindow(nsGlobalWindow& window, double x,
|
|||
thebes = new gfxContext(mTarget);
|
||||
thebes->SetMatrix(gfxMatrix(matrix._11, matrix._12, matrix._21,
|
||||
matrix._22, matrix._31, matrix._32));
|
||||
} else if (gfxPlatform::GetPlatform()->SupportsAzureContent()) {
|
||||
} else {
|
||||
drawDT =
|
||||
gfxPlatform::GetPlatform()->CreateOffscreenContentDrawTarget(IntSize(ceil(sw), ceil(sh)),
|
||||
SurfaceFormat::B8G8R8A8);
|
||||
|
@ -3588,17 +3588,6 @@ CanvasRenderingContext2D::DrawWindow(nsGlobalWindow& window, double x,
|
|||
|
||||
thebes = new gfxContext(drawDT);
|
||||
thebes->Scale(matrix._11, matrix._22);
|
||||
} else {
|
||||
drawSurf =
|
||||
gfxPlatform::GetPlatform()->CreateOffscreenSurface(IntSize(ceil(sw), ceil(sh)),
|
||||
gfxContentType::COLOR_ALPHA);
|
||||
if (!drawSurf) {
|
||||
error.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
thebes = new gfxContext(drawSurf);
|
||||
thebes->Scale(matrix._11, matrix._22);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPresShell> shell = presContext->PresShell();
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "DOMSVGPoint.h"
|
||||
#include "DOMSVGPointList.h"
|
||||
#include "SVGPoint.h"
|
||||
#include "gfx2DGlue.h"
|
||||
#include "nsSVGElement.h"
|
||||
#include "nsError.h"
|
||||
#include "mozilla/dom/SVGMatrix.h"
|
||||
|
@ -13,6 +14,7 @@
|
|||
// See the architecture comment in DOMSVGPointList.h.
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::gfx;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
|
@ -112,7 +114,7 @@ DOMSVGPoint::MatrixTransform(dom::SVGMatrix& matrix)
|
|||
float x = HasOwner() ? InternalItem().mX : mPt.mX;
|
||||
float y = HasOwner() ? InternalItem().mY : mPt.mY;
|
||||
|
||||
gfxPoint pt = matrix.GetMatrix().Transform(gfxPoint(x, y));
|
||||
Point pt = ToMatrix(matrix.GetMatrix()) * Point(x, y);
|
||||
nsCOMPtr<nsISVGPoint> newPoint = new DOMSVGPoint(pt);
|
||||
return newPoint.forget();
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
#define MOZILLA_DOMSVGPOINT_H__
|
||||
|
||||
#include "DOMSVGPointList.h"
|
||||
#include "gfxPoint.h"
|
||||
#include "mozilla/gfx/2D.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsDebug.h"
|
||||
|
@ -87,15 +86,6 @@ public:
|
|||
"DOMSVGPoint coords are not finite");
|
||||
}
|
||||
|
||||
explicit DOMSVGPoint(const gfxPoint &aPt)
|
||||
: nsISVGPoint()
|
||||
{
|
||||
mPt.mX = float(aPt.x);
|
||||
mPt.mY = float(aPt.y);
|
||||
NS_ASSERTION(NS_finite(mPt.mX) && NS_finite(mPt.mX),
|
||||
"DOMSVGPoint coords are not finite");
|
||||
}
|
||||
|
||||
|
||||
// WebIDL
|
||||
virtual float X();
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "SVGMotionSMILType.h"
|
||||
|
||||
#include "gfx2DGlue.h"
|
||||
#include "mozilla/gfx/Point.h"
|
||||
#include "nsSMILValue.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsMathUtils.h"
|
||||
|
@ -233,16 +234,16 @@ SVGMotionSMILType::IsEqual(const nsSMILValue& aLeft,
|
|||
inline static void
|
||||
GetAngleAndPointAtDistance(Path* aPath, float aDistance,
|
||||
RotateType aRotateType,
|
||||
gfxFloat& aRotateAngle, // in & out-param.
|
||||
gfxPoint& aPoint) // out-param.
|
||||
float& aRotateAngle, // in & out-param.
|
||||
Point& aPoint) // out-param.
|
||||
{
|
||||
if (aRotateType == eRotateType_Explicit) {
|
||||
// Leave aRotateAngle as-is.
|
||||
aPoint = ThebesPoint(aPath->ComputePointAtLength(aDistance));
|
||||
aPoint = aPath->ComputePointAtLength(aDistance);
|
||||
} else {
|
||||
Point tangent; // Unit vector tangent to the point we find.
|
||||
aPoint = ThebesPoint(aPath->ComputePointAtLength(aDistance, &tangent));
|
||||
gfxFloat tangentAngle = atan2(tangent.y, tangent.x);
|
||||
aPoint = aPath->ComputePointAtLength(aDistance, &tangent);
|
||||
float tangentAngle = atan2(tangent.y, tangent.x);
|
||||
if (aRotateType == eRotateType_Auto) {
|
||||
aRotateAngle = tangentAngle;
|
||||
} else {
|
||||
|
@ -291,8 +292,8 @@ SVGMotionSMILType::Add(nsSMILValue& aDest, const nsSMILValue& aValueToAdd,
|
|||
Path* path = srcParams.mPath;
|
||||
|
||||
// Use destination to get our rotate angle.
|
||||
gfxFloat rotateAngle = dstSeg.mRotateAngle;
|
||||
gfxPoint dstPt;
|
||||
float rotateAngle = dstSeg.mRotateAngle;
|
||||
Point dstPt;
|
||||
GetAngleAndPointAtDistance(path, dstParams.mDistToPoint, dstSeg.mRotateType,
|
||||
rotateAngle, dstPt);
|
||||
|
||||
|
@ -450,8 +451,8 @@ SVGMotionSMILType::CreateMatrix(const nsSMILValue& aSMILVal)
|
|||
gfx::Matrix matrix;
|
||||
uint32_t length = arr.Length();
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
gfxPoint point; // initialized below
|
||||
gfxFloat rotateAngle = arr[i].mRotateAngle; // might get updated below
|
||||
Point point; // initialized below
|
||||
float rotateAngle = arr[i].mRotateAngle; // might get updated below
|
||||
if (arr[i].mSegmentType == eSegmentType_Translation) {
|
||||
point.x = arr[i].mU.mTranslationParams.mX;
|
||||
point.y = arr[i].mU.mTranslationParams.mY;
|
||||
|
|
|
@ -821,7 +821,7 @@ SVGPathData::ToPathForLengthOrPositionMeasuring() const
|
|||
}
|
||||
|
||||
static double
|
||||
AngleOfVector(const gfxPoint& aVector)
|
||||
AngleOfVector(const Point& aVector)
|
||||
{
|
||||
// C99 says about atan2 "A domain error may occur if both arguments are
|
||||
// zero" and "On a domain error, the function returns an implementation-
|
||||
|
@ -829,11 +829,11 @@ AngleOfVector(const gfxPoint& aVector)
|
|||
// seems to commonly be zero, but it could just as easily be a NaN value.
|
||||
// We specifically want zero in this case, hence the check:
|
||||
|
||||
return (aVector != gfxPoint(0.0, 0.0)) ? atan2(aVector.y, aVector.x) : 0.0;
|
||||
return (aVector != Point(0.0, 0.0)) ? atan2(aVector.y, aVector.x) : 0.0;
|
||||
}
|
||||
|
||||
static float
|
||||
AngleOfVectorF(const gfxPoint& aVector)
|
||||
AngleOfVectorF(const Point& aVector)
|
||||
{
|
||||
return static_cast<float>(AngleOfVector(aVector));
|
||||
}
|
||||
|
@ -846,14 +846,14 @@ SVGPathData::GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const
|
|||
// places, and repeat multiple times consecutively.
|
||||
|
||||
// info on current [sub]path (reset every M command):
|
||||
gfxPoint pathStart(0.0, 0.0);
|
||||
Point pathStart(0.0, 0.0);
|
||||
float pathStartAngle = 0.0f;
|
||||
|
||||
// info on previous segment:
|
||||
uint16_t prevSegType = PATHSEG_UNKNOWN;
|
||||
gfxPoint prevSegEnd(0.0, 0.0);
|
||||
Point prevSegEnd(0.0, 0.0);
|
||||
float prevSegEndAngle = 0.0f;
|
||||
gfxPoint prevCP; // if prev seg was a bezier, this was its last control point
|
||||
Point prevCP; // if prev seg was a bezier, this was its last control point
|
||||
|
||||
uint32_t i = 0;
|
||||
while (i < mData.Length()) {
|
||||
|
@ -861,8 +861,8 @@ SVGPathData::GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const
|
|||
// info on current segment:
|
||||
uint16_t segType =
|
||||
SVGPathSegUtils::DecodeType(mData[i++]); // advances i to args
|
||||
gfxPoint &segStart = prevSegEnd;
|
||||
gfxPoint segEnd;
|
||||
Point& segStart = prevSegEnd;
|
||||
Point segEnd;
|
||||
float segStartAngle, segEndAngle;
|
||||
|
||||
switch (segType) // to find segStartAngle, segEnd and segEndAngle
|
||||
|
@ -875,9 +875,9 @@ SVGPathData::GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const
|
|||
case PATHSEG_MOVETO_ABS:
|
||||
case PATHSEG_MOVETO_REL:
|
||||
if (segType == PATHSEG_MOVETO_ABS) {
|
||||
segEnd = gfxPoint(mData[i], mData[i+1]);
|
||||
segEnd = Point(mData[i], mData[i+1]);
|
||||
} else {
|
||||
segEnd = segStart + gfxPoint(mData[i], mData[i+1]);
|
||||
segEnd = segStart + Point(mData[i], mData[i+1]);
|
||||
}
|
||||
pathStart = segEnd;
|
||||
// If authors are going to specify multiple consecutive moveto commands
|
||||
|
@ -889,9 +889,9 @@ SVGPathData::GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const
|
|||
case PATHSEG_LINETO_ABS:
|
||||
case PATHSEG_LINETO_REL:
|
||||
if (segType == PATHSEG_LINETO_ABS) {
|
||||
segEnd = gfxPoint(mData[i], mData[i+1]);
|
||||
segEnd = Point(mData[i], mData[i+1]);
|
||||
} else {
|
||||
segEnd = segStart + gfxPoint(mData[i], mData[i+1]);
|
||||
segEnd = segStart + Point(mData[i], mData[i+1]);
|
||||
}
|
||||
segStartAngle = segEndAngle = AngleOfVectorF(segEnd - segStart);
|
||||
i += 2;
|
||||
|
@ -900,15 +900,15 @@ SVGPathData::GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const
|
|||
case PATHSEG_CURVETO_CUBIC_ABS:
|
||||
case PATHSEG_CURVETO_CUBIC_REL:
|
||||
{
|
||||
gfxPoint cp1, cp2; // control points
|
||||
Point cp1, cp2; // control points
|
||||
if (segType == PATHSEG_CURVETO_CUBIC_ABS) {
|
||||
cp1 = gfxPoint(mData[i], mData[i+1]);
|
||||
cp2 = gfxPoint(mData[i+2], mData[i+3]);
|
||||
segEnd = gfxPoint(mData[i+4], mData[i+5]);
|
||||
cp1 = Point(mData[i], mData[i+1]);
|
||||
cp2 = Point(mData[i+2], mData[i+3]);
|
||||
segEnd = Point(mData[i+4], mData[i+5]);
|
||||
} else {
|
||||
cp1 = segStart + gfxPoint(mData[i], mData[i+1]);
|
||||
cp2 = segStart + gfxPoint(mData[i+2], mData[i+3]);
|
||||
segEnd = segStart + gfxPoint(mData[i+4], mData[i+5]);
|
||||
cp1 = segStart + Point(mData[i], mData[i+1]);
|
||||
cp2 = segStart + Point(mData[i+2], mData[i+3]);
|
||||
segEnd = segStart + Point(mData[i+4], mData[i+5]);
|
||||
}
|
||||
prevCP = cp2;
|
||||
if (cp1 == segStart) {
|
||||
|
@ -926,13 +926,13 @@ SVGPathData::GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const
|
|||
case PATHSEG_CURVETO_QUADRATIC_ABS:
|
||||
case PATHSEG_CURVETO_QUADRATIC_REL:
|
||||
{
|
||||
gfxPoint cp1, cp2; // control points
|
||||
Point cp1, cp2; // control points
|
||||
if (segType == PATHSEG_CURVETO_QUADRATIC_ABS) {
|
||||
cp1 = gfxPoint(mData[i], mData[i+1]);
|
||||
segEnd = gfxPoint(mData[i+2], mData[i+3]);
|
||||
cp1 = Point(mData[i], mData[i+1]);
|
||||
segEnd = Point(mData[i+2], mData[i+3]);
|
||||
} else {
|
||||
cp1 = segStart + gfxPoint(mData[i], mData[i+1]);
|
||||
segEnd = segStart + gfxPoint(mData[i+2], mData[i+3]);
|
||||
cp1 = segStart + Point(mData[i], mData[i+1]);
|
||||
segEnd = segStart + Point(mData[i+2], mData[i+3]);
|
||||
}
|
||||
prevCP = cp1;
|
||||
segStartAngle = AngleOfVectorF(cp1 - segStart);
|
||||
|
@ -950,9 +950,9 @@ SVGPathData::GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const
|
|||
bool largeArcFlag = mData[i+3] != 0.0f;
|
||||
bool sweepFlag = mData[i+4] != 0.0f;
|
||||
if (segType == PATHSEG_ARC_ABS) {
|
||||
segEnd = gfxPoint(mData[i+5], mData[i+6]);
|
||||
segEnd = Point(mData[i+5], mData[i+6]);
|
||||
} else {
|
||||
segEnd = segStart + gfxPoint(mData[i+5], mData[i+6]);
|
||||
segEnd = segStart + Point(mData[i+5], mData[i+6]);
|
||||
}
|
||||
|
||||
// See section F.6 of SVG 1.1 for details on what we're doing here:
|
||||
|
@ -1017,8 +1017,8 @@ SVGPathData::GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const
|
|||
double cyp = -root * ry * x1p / rx;
|
||||
|
||||
double theta, delta;
|
||||
theta = AngleOfVector(gfxPoint((x1p-cxp)/rx, (y1p-cyp)/ry)); // F.6.5.5
|
||||
delta = AngleOfVector(gfxPoint((-x1p-cxp)/rx, (-y1p-cyp)/ry)) - // F.6.5.6
|
||||
theta = AngleOfVector(Point((x1p-cxp)/rx, (y1p-cyp)/ry)); // F.6.5.5
|
||||
delta = AngleOfVector(Point((-x1p-cxp)/rx, (-y1p-cyp)/ry)) - // F.6.5.6
|
||||
theta;
|
||||
if (!sweepFlag && delta > 0)
|
||||
delta -= 2.0 * M_PI;
|
||||
|
@ -1047,9 +1047,9 @@ SVGPathData::GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const
|
|||
case PATHSEG_LINETO_HORIZONTAL_ABS:
|
||||
case PATHSEG_LINETO_HORIZONTAL_REL:
|
||||
if (segType == PATHSEG_LINETO_HORIZONTAL_ABS) {
|
||||
segEnd = gfxPoint(mData[i++], segStart.y);
|
||||
segEnd = Point(mData[i++], segStart.y);
|
||||
} else {
|
||||
segEnd = segStart + gfxPoint(mData[i++], 0.0f);
|
||||
segEnd = segStart + Point(mData[i++], 0.0f);
|
||||
}
|
||||
segStartAngle = segEndAngle = AngleOfVectorF(segEnd - segStart);
|
||||
break;
|
||||
|
@ -1057,9 +1057,9 @@ SVGPathData::GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const
|
|||
case PATHSEG_LINETO_VERTICAL_ABS:
|
||||
case PATHSEG_LINETO_VERTICAL_REL:
|
||||
if (segType == PATHSEG_LINETO_VERTICAL_ABS) {
|
||||
segEnd = gfxPoint(segStart.x, mData[i++]);
|
||||
segEnd = Point(segStart.x, mData[i++]);
|
||||
} else {
|
||||
segEnd = segStart + gfxPoint(0.0f, mData[i++]);
|
||||
segEnd = segStart + Point(0.0f, mData[i++]);
|
||||
}
|
||||
segStartAngle = segEndAngle = AngleOfVectorF(segEnd - segStart);
|
||||
break;
|
||||
|
@ -1067,15 +1067,15 @@ SVGPathData::GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const
|
|||
case PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
|
||||
case PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
|
||||
{
|
||||
gfxPoint cp1 = SVGPathSegUtils::IsCubicType(prevSegType) ?
|
||||
Point cp1 = SVGPathSegUtils::IsCubicType(prevSegType) ?
|
||||
segStart * 2 - prevCP : segStart;
|
||||
gfxPoint cp2;
|
||||
Point cp2;
|
||||
if (segType == PATHSEG_CURVETO_CUBIC_SMOOTH_ABS) {
|
||||
cp2 = gfxPoint(mData[i], mData[i+1]);
|
||||
segEnd = gfxPoint(mData[i+2], mData[i+3]);
|
||||
cp2 = Point(mData[i], mData[i+1]);
|
||||
segEnd = Point(mData[i+2], mData[i+3]);
|
||||
} else {
|
||||
cp2 = segStart + gfxPoint(mData[i], mData[i+1]);
|
||||
segEnd = segStart + gfxPoint(mData[i+2], mData[i+3]);
|
||||
cp2 = segStart + Point(mData[i], mData[i+1]);
|
||||
segEnd = segStart + Point(mData[i+2], mData[i+3]);
|
||||
}
|
||||
prevCP = cp2;
|
||||
if (cp1 == segStart) {
|
||||
|
@ -1093,13 +1093,13 @@ SVGPathData::GetMarkerPositioningData(nsTArray<nsSVGMark> *aMarks) const
|
|||
case PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
|
||||
case PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
|
||||
{
|
||||
gfxPoint cp1 = SVGPathSegUtils::IsQuadraticType(prevSegType) ?
|
||||
Point cp1 = SVGPathSegUtils::IsQuadraticType(prevSegType) ?
|
||||
segStart * 2 - prevCP : segStart;
|
||||
gfxPoint cp2;
|
||||
Point cp2;
|
||||
if (segType == PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS) {
|
||||
segEnd = gfxPoint(mData[i], mData[i+1]);
|
||||
segEnd = Point(mData[i], mData[i+1]);
|
||||
} else {
|
||||
segEnd = segStart + gfxPoint(mData[i], mData[i+1]);
|
||||
segEnd = segStart + Point(mData[i], mData[i+1]);
|
||||
}
|
||||
prevCP = cp1;
|
||||
segStartAngle = AngleOfVectorF(cp1 - segStart);
|
||||
|
|
|
@ -334,7 +334,7 @@ SVGPathElement::ConstructPath(gfxContext *aCtx)
|
|||
mD.GetAnimValue().ConstructPath(aCtx);
|
||||
}
|
||||
|
||||
gfxFloat
|
||||
float
|
||||
SVGPathElement::GetPathLengthScale(PathLengthScaleForType aFor)
|
||||
{
|
||||
NS_ABORT_IF_FALSE(aFor == eForTextPath || aFor == eForStroking,
|
||||
|
|
|
@ -82,7 +82,7 @@ public:
|
|||
* length (as provided by the <path> element's 'pathLength' attribute). This
|
||||
* is used to scale stroke dashing, and to scale offsets along a textPath.
|
||||
*/
|
||||
gfxFloat GetPathLengthScale(PathLengthScaleForType aFor);
|
||||
float GetPathLengthScale(PathLengthScaleForType aFor);
|
||||
|
||||
// WebIDL
|
||||
already_AddRefed<SVGAnimatedNumber> PathLength();
|
||||
|
|
|
@ -131,14 +131,14 @@ SplitCubicBezier(const Point* aCurve, Point* aLeft, Point* aRight)
|
|||
aLeft[3].y = aRight[0].y = (aLeft[2].y + aRight[1].y) / 2;
|
||||
}
|
||||
|
||||
static gfxFloat
|
||||
static float
|
||||
CalcBezLengthHelper(const Point* aCurve, uint32_t aNumPts,
|
||||
uint32_t aRecursionCount,
|
||||
void (*aSplit)(const Point*, Point*, Point*))
|
||||
{
|
||||
Point left[4];
|
||||
Point right[4];
|
||||
gfxFloat length = 0, dist;
|
||||
float length = 0, dist;
|
||||
for (uint32_t i = 0; i < aNumPts - 1; i++) {
|
||||
length += CalcDistanceBetweenPoints(aCurve[i], aCurve[i+1]);
|
||||
}
|
||||
|
@ -153,7 +153,7 @@ CalcBezLengthHelper(const Point* aCurve, uint32_t aNumPts,
|
|||
return length;
|
||||
}
|
||||
|
||||
static inline gfxFloat
|
||||
static inline float
|
||||
CalcLengthOfCubicBezier(const Point& aPos, const Point &aCP1,
|
||||
const Point& aCP2, const Point &aTo)
|
||||
{
|
||||
|
@ -161,7 +161,7 @@ CalcLengthOfCubicBezier(const Point& aPos, const Point &aCP1,
|
|||
return CalcBezLengthHelper(curve, 4, 0, SplitCubicBezier);
|
||||
}
|
||||
|
||||
static inline gfxFloat
|
||||
static inline float
|
||||
CalcLengthOfQuadraticBezier(const Point& aPos, const Point& aCP,
|
||||
const Point& aTo)
|
||||
{
|
||||
|
|
|
@ -73,3 +73,10 @@ if CONFIG['SOLARIS_SUNPRO_CC']:
|
|||
if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
RCFILE = 'sqlite.rc'
|
||||
RESFILE = 'sqlite.res'
|
||||
|
||||
# Suppress warnings in third-party code.
|
||||
if CONFIG['GNU_CC']:
|
||||
CFLAGS += [
|
||||
'-Wno-sign-compare',
|
||||
'-Wno-type-limits',
|
||||
]
|
||||
|
|
|
@ -334,6 +334,8 @@ parent:
|
|||
|
||||
__delete__();
|
||||
|
||||
ReplyKeyEvent(WidgetKeyboardEvent event);
|
||||
|
||||
child:
|
||||
/**
|
||||
* Notify the remote browser that it has been Show()n on this
|
||||
|
|
|
@ -1932,6 +1932,10 @@ TabChild::RecvRealKeyEvent(const WidgetKeyboardEvent& event)
|
|||
mIgnoreKeyPressEvent = status == nsEventStatus_eConsumeNoDefault;
|
||||
}
|
||||
|
||||
if (localEvent.mFlags.mWantReplyFromContentProcess) {
|
||||
SendReplyKeyEvent(localEvent);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1192,6 +1192,28 @@ TabParent::GetChildProcessOffset()
|
|||
pt, targetFrame->PresContext()->AppUnitsPerDevPixel()));
|
||||
}
|
||||
|
||||
bool
|
||||
TabParent::RecvReplyKeyEvent(const WidgetKeyboardEvent& event)
|
||||
{
|
||||
NS_ENSURE_TRUE(mFrameElement, true);
|
||||
|
||||
WidgetKeyboardEvent localEvent(event);
|
||||
// Set mNoCrossProcessBoundaryForwarding to avoid this event from
|
||||
// being infinitely redispatched and forwarded to the child again.
|
||||
localEvent.mFlags.mNoCrossProcessBoundaryForwarding = true;
|
||||
|
||||
// Here we convert the WidgetEvent that we received to an nsIDOMEvent
|
||||
// to be able to dispatch it to the <browser> element as the target element.
|
||||
nsIDocument* doc = mFrameElement->OwnerDoc();
|
||||
nsIPresShell* presShell = doc->GetShell();
|
||||
NS_ENSURE_TRUE(presShell, true);
|
||||
nsPresContext* presContext = presShell->GetPresContext();
|
||||
NS_ENSURE_TRUE(presContext, true);
|
||||
|
||||
EventDispatcher::Dispatch(mFrameElement, presContext, &localEvent);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to answer query event using cached text.
|
||||
*
|
||||
|
|
|
@ -114,6 +114,7 @@ public:
|
|||
|
||||
virtual bool RecvMoveFocus(const bool& aForward) MOZ_OVERRIDE;
|
||||
virtual bool RecvEvent(const RemoteDOMEvent& aEvent) MOZ_OVERRIDE;
|
||||
virtual bool RecvReplyKeyEvent(const WidgetKeyboardEvent& event);
|
||||
virtual bool RecvPRenderFrameConstructor(PRenderFrameParent* actor) MOZ_OVERRIDE;
|
||||
virtual bool RecvInitRenderFrame(PRenderFrameParent* aFrame,
|
||||
ScrollingBehavior* scrolling,
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "nsPluginNativeWindow.h"
|
||||
#include "GeckoProfiler.h"
|
||||
#include "nsPluginInstanceOwner.h"
|
||||
#include "nsDataHashtable.h"
|
||||
|
||||
#define MAGIC_REQUEST_CONTEXT 0x01020304
|
||||
|
||||
|
@ -39,12 +40,12 @@ class nsPluginByteRangeStreamListener
|
|||
public:
|
||||
nsPluginByteRangeStreamListener(nsIWeakReference* aWeakPtr);
|
||||
virtual ~nsPluginByteRangeStreamListener();
|
||||
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
NS_DECL_NSISTREAMLISTENER
|
||||
NS_DECL_NSIINTERFACEREQUESTOR
|
||||
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIStreamListener> mStreamConverter;
|
||||
nsWeakPtr mWeakPtrPluginStreamListenerPeer;
|
||||
|
@ -88,17 +89,17 @@ NS_IMETHODIMP
|
|||
nsPluginByteRangeStreamListener::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
|
||||
nsCOMPtr<nsIStreamListener> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer);
|
||||
if (!finalStreamListener)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
|
||||
nsPluginStreamListenerPeer *pslp =
|
||||
static_cast<nsPluginStreamListenerPeer*>(finalStreamListener.get());
|
||||
|
||||
NS_ASSERTION(pslp->mRequests.IndexOfObject(GetBaseRequest(request)) != -1,
|
||||
"Untracked byte-range request?");
|
||||
|
||||
|
||||
nsCOMPtr<nsIStreamConverterService> serv = do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID, &rv);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
rv = serv->AsyncConvertData(MULTIPART_BYTERANGES,
|
||||
|
@ -113,18 +114,18 @@ nsPluginByteRangeStreamListener::OnStartRequest(nsIRequest *request, nsISupports
|
|||
}
|
||||
}
|
||||
mStreamConverter = 0;
|
||||
|
||||
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request));
|
||||
if (!httpChannel) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
uint32_t responseCode = 0;
|
||||
rv = httpChannel->GetResponseStatus(&responseCode);
|
||||
if (NS_FAILED(rv)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
||||
if (responseCode != 200) {
|
||||
uint32_t wantsAllNetworkStreams = 0;
|
||||
rv = pslp->GetPluginInstance()->GetValueFromPlugin(NPPVpluginWantsAllNetworkStreams,
|
||||
|
@ -138,12 +139,12 @@ nsPluginByteRangeStreamListener::OnStartRequest(nsIRequest *request, nsISupports
|
|||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// if server cannot continue with byte range (206 status) and sending us whole object (200 status)
|
||||
// reset this seekable stream & try serve it to plugin instance as a file
|
||||
mStreamConverter = finalStreamListener;
|
||||
mRemoveMagicNumber = true;
|
||||
|
||||
|
||||
rv = pslp->ServeStreamAsFile(request, ctxt);
|
||||
return rv;
|
||||
}
|
||||
|
@ -154,11 +155,11 @@ nsPluginByteRangeStreamListener::OnStopRequest(nsIRequest *request, nsISupports
|
|||
{
|
||||
if (!mStreamConverter)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
|
||||
nsCOMPtr<nsIStreamListener> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer);
|
||||
if (!finalStreamListener)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
|
||||
nsPluginStreamListenerPeer *pslp =
|
||||
static_cast<nsPluginStreamListenerPeer*>(finalStreamListener.get());
|
||||
bool found = pslp->mRequests.RemoveObject(request);
|
||||
|
@ -181,7 +182,7 @@ nsPluginByteRangeStreamListener::OnStopRequest(nsIRequest *request, nsISupports
|
|||
NS_WARNING("Bad state of nsPluginByteRangeStreamListener");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return mStreamConverter->OnStopRequest(request, ctxt, status);
|
||||
}
|
||||
|
||||
|
@ -223,11 +224,11 @@ nsPluginByteRangeStreamListener::OnDataAvailable(nsIRequest *request, nsISupport
|
|||
{
|
||||
if (!mStreamConverter)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
|
||||
nsCOMPtr<nsIStreamListener> finalStreamListener = do_QueryReferent(mWeakPtrPluginStreamListenerPeer);
|
||||
if (!finalStreamListener)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
|
||||
return mStreamConverter->OnDataAvailable(request, ctxt, inStr, sourceOffset, count);
|
||||
}
|
||||
|
||||
|
@ -241,28 +242,6 @@ nsPluginByteRangeStreamListener::GetInterface(const nsIID& aIID, void** result)
|
|||
|
||||
return finalStreamListener->GetInterface(aIID, result);
|
||||
}
|
||||
|
||||
|
||||
// nsPRUintKey
|
||||
|
||||
class nsPRUintKey : public nsHashKey {
|
||||
protected:
|
||||
uint32_t mKey;
|
||||
public:
|
||||
nsPRUintKey(uint32_t key) : mKey(key) {}
|
||||
|
||||
uint32_t HashCode() const {
|
||||
return mKey;
|
||||
}
|
||||
|
||||
bool Equals(const nsHashKey *aKey) const {
|
||||
return mKey == ((const nsPRUintKey*)aKey)->mKey;
|
||||
}
|
||||
nsHashKey *Clone() const {
|
||||
return new nsPRUintKey(mKey);
|
||||
}
|
||||
uint32_t GetValue() { return mKey; }
|
||||
};
|
||||
|
||||
// nsPluginStreamListenerPeer
|
||||
|
||||
|
@ -280,11 +259,11 @@ nsPluginStreamListenerPeer::nsPluginStreamListenerPeer()
|
|||
mStartBinding = false;
|
||||
mAbort = false;
|
||||
mRequestFailed = false;
|
||||
|
||||
|
||||
mPendingRequests = 0;
|
||||
mHaveFiredOnStartRequest = false;
|
||||
mDataForwardToRequest = nullptr;
|
||||
|
||||
|
||||
mSeekable = false;
|
||||
mModified = 0;
|
||||
mStreamOffset = 0;
|
||||
|
@ -306,7 +285,7 @@ nsPluginStreamListenerPeer::~nsPluginStreamListenerPeer()
|
|||
// or we won't be able to remove the cache file
|
||||
if (mFileCacheOutputStream)
|
||||
mFileCacheOutputStream = nullptr;
|
||||
|
||||
|
||||
delete mDataForwardToRequest;
|
||||
|
||||
if (mPluginInstance)
|
||||
|
@ -350,7 +329,7 @@ nsresult nsPluginStreamListenerPeer::Initialize(nsIURI *aURL,
|
|||
|
||||
mPendingRequests = 1;
|
||||
|
||||
mDataForwardToRequest = new nsHashtable();
|
||||
mDataForwardToRequest = new nsDataHashtable<nsUint32HashKey, uint32_t>();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -394,38 +373,38 @@ nsPluginStreamListenerPeer::SetupPluginCacheFile(nsIChannel* channel)
|
|||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
// Get the filename from the channel
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
rv = channel->GetURI(getter_AddRefs(uri));
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
|
||||
nsCOMPtr<nsIURL> url(do_QueryInterface(uri));
|
||||
if (!url)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
|
||||
nsAutoCString filename;
|
||||
url->GetFileName(filename);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
|
||||
// Create a file to save our stream into. Should we scramble the name?
|
||||
filename.Insert(NS_LITERAL_CSTRING("plugin-"), 0);
|
||||
rv = pluginTmp->AppendNative(filename);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
|
||||
// Yes, make it unique.
|
||||
rv = pluginTmp->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
|
||||
// create a file output stream to write to...
|
||||
nsCOMPtr<nsIOutputStream> outstream;
|
||||
rv = NS_NewLocalFileOutputStream(getter_AddRefs(mFileCacheOutputStream), pluginTmp, -1, 00600);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
|
||||
// save the file.
|
||||
mLocalCachedFileHolder = new CachedFileHolder(pluginTmp);
|
||||
}
|
||||
|
@ -618,28 +597,28 @@ nsPluginStreamListenerPeer::MakeByteRangeString(NPByteRange* aRangeList, nsACStr
|
|||
//the string should look like this: bytes=500-700,601-999
|
||||
if (!aRangeList)
|
||||
return;
|
||||
|
||||
|
||||
int32_t requestCnt = 0;
|
||||
nsAutoCString string("bytes=");
|
||||
|
||||
|
||||
for (NPByteRange * range = aRangeList; range != nullptr; range = range->next) {
|
||||
// XXX zero length?
|
||||
if (!range->length)
|
||||
continue;
|
||||
|
||||
|
||||
// XXX needs to be fixed for negative offsets
|
||||
string.AppendInt(range->offset);
|
||||
string.Append("-");
|
||||
string.AppendInt(range->offset + range->length - 1);
|
||||
if (range->next)
|
||||
string += ",";
|
||||
|
||||
|
||||
requestCnt++;
|
||||
}
|
||||
|
||||
|
||||
// get rid of possible trailing comma
|
||||
string.Trim(",", false);
|
||||
|
||||
|
||||
rangeRequest = string;
|
||||
*numRequests = requestCnt;
|
||||
return;
|
||||
|
@ -650,32 +629,32 @@ nsPluginStreamListenerPeer::RequestRead(NPByteRange* rangeList)
|
|||
{
|
||||
nsAutoCString rangeString;
|
||||
int32_t numRequests;
|
||||
|
||||
|
||||
MakeByteRangeString(rangeList, rangeString, &numRequests);
|
||||
|
||||
|
||||
if (numRequests == 0)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
|
||||
nsCOMPtr<nsIInterfaceRequestor> callbacks = do_QueryReferent(mWeakPtrChannelCallbacks);
|
||||
nsCOMPtr<nsILoadGroup> loadGroup = do_QueryReferent(mWeakPtrChannelLoadGroup);
|
||||
nsCOMPtr<nsIChannel> channel;
|
||||
rv = NS_NewChannel(getter_AddRefs(channel), mURL, nullptr, loadGroup, callbacks);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(channel));
|
||||
if (!httpChannel)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
|
||||
httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Range"), rangeString, false);
|
||||
|
||||
|
||||
mAbort = true; // instruct old stream listener to cancel
|
||||
// the request on the next ODA.
|
||||
|
||||
|
||||
nsCOMPtr<nsIStreamListener> converter;
|
||||
|
||||
|
||||
if (numRequests == 1) {
|
||||
converter = this;
|
||||
// set current stream offset equal to the first offset in the range list
|
||||
|
@ -692,16 +671,16 @@ nsPluginStreamListenerPeer::RequestRead(NPByteRange* rangeList)
|
|||
else
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
|
||||
mPendingRequests += numRequests;
|
||||
|
||||
|
||||
nsCOMPtr<nsISupportsPRUint32> container = do_CreateInstance(NS_SUPPORTS_PRUINT32_CONTRACTID, &rv);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
rv = container->SetData(MAGIC_REQUEST_CONTEXT);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
|
||||
rv = channel->AsyncOpen(converter, container);
|
||||
if (NS_SUCCEEDED(rv))
|
||||
TrackRequest(channel);
|
||||
|
@ -727,7 +706,7 @@ nsresult nsPluginStreamListenerPeer::ServeStreamAsFile(nsIRequest *request,
|
|||
{
|
||||
if (!mPluginInstance)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
|
||||
// mPluginInstance->Stop calls mPStreamListener->CleanUpStream(), so stream will be properly clean up
|
||||
mPluginInstance->Stop();
|
||||
mPluginInstance->Start();
|
||||
|
@ -737,7 +716,7 @@ nsresult nsPluginStreamListenerPeer::ServeStreamAsFile(nsIRequest *request,
|
|||
owner->GetWindow(window);
|
||||
#if (MOZ_WIDGET_GTK == 2) || defined(MOZ_WIDGET_QT)
|
||||
// Should call GetPluginPort() here.
|
||||
// This part is copied from nsPluginInstanceOwner::GetPluginPort().
|
||||
// This part is copied from nsPluginInstanceOwner::GetPluginPort().
|
||||
nsCOMPtr<nsIWidget> widget;
|
||||
((nsPluginNativeWindow*)window)->GetPluginWidget(getter_AddRefs(widget));
|
||||
if (widget) {
|
||||
|
@ -746,22 +725,22 @@ nsresult nsPluginStreamListenerPeer::ServeStreamAsFile(nsIRequest *request,
|
|||
#endif
|
||||
owner->CallSetWindow();
|
||||
}
|
||||
|
||||
|
||||
mSeekable = false;
|
||||
mPStreamListener->OnStartBinding(this);
|
||||
mStreamOffset = 0;
|
||||
|
||||
|
||||
// force the plugin to use stream as file
|
||||
mStreamType = NP_ASFILE;
|
||||
|
||||
|
||||
nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
|
||||
if (channel) {
|
||||
SetupPluginCacheFile(channel);
|
||||
}
|
||||
|
||||
|
||||
// unset mPendingRequests
|
||||
mPendingRequests = 0;
|
||||
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -769,7 +748,7 @@ bool
|
|||
nsPluginStreamListenerPeer::UseExistingPluginCacheFile(nsPluginStreamListenerPeer* psi)
|
||||
{
|
||||
NS_ENSURE_TRUE(psi, false);
|
||||
|
||||
|
||||
if (psi->mLength == mLength &&
|
||||
psi->mModified == mModified &&
|
||||
mStreamComplete &&
|
||||
|
@ -793,32 +772,32 @@ NS_IMETHODIMP nsPluginStreamListenerPeer::OnDataAvailable(nsIRequest *request,
|
|||
|
||||
if (mRequestFailed)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
|
||||
if (mAbort) {
|
||||
uint32_t magicNumber = 0; // set it to something that is not the magic number.
|
||||
nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(aContext);
|
||||
if (container)
|
||||
container->GetData(&magicNumber);
|
||||
|
||||
|
||||
if (magicNumber != MAGIC_REQUEST_CONTEXT) {
|
||||
// this is not one of our range requests
|
||||
mAbort = false;
|
||||
return NS_BINDING_ABORTED;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
|
||||
if (!mPStreamListener)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
|
||||
const char * url = nullptr;
|
||||
GetURL(&url);
|
||||
|
||||
|
||||
PLUGIN_LOG(PLUGIN_LOG_NOISY,
|
||||
("nsPluginStreamListenerPeer::OnDataAvailable this=%p request=%p, offset=%llu, length=%u, url=%s\n",
|
||||
this, request, sourceOffset, aLength, url ? url : "no url set"));
|
||||
|
||||
|
||||
// if the plugin has requested an AsFileOnly stream, then don't
|
||||
// call OnDataAvailable
|
||||
if (mStreamType != NP_ASFILEONLY) {
|
||||
|
@ -827,44 +806,42 @@ NS_IMETHODIMP nsPluginStreamListenerPeer::OnDataAvailable(nsIRequest *request,
|
|||
if (brr) {
|
||||
if (!mDataForwardToRequest)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
|
||||
int64_t absoluteOffset64 = 0;
|
||||
brr->GetStartRange(&absoluteOffset64);
|
||||
|
||||
|
||||
// XXX handle 64-bit for real
|
||||
int32_t absoluteOffset = (int32_t)int64_t(absoluteOffset64);
|
||||
|
||||
|
||||
// we need to track how much data we have forwarded to the
|
||||
// plugin.
|
||||
|
||||
|
||||
// FIXME: http://bugzilla.mozilla.org/show_bug.cgi?id=240130
|
||||
//
|
||||
// Why couldn't this be tracked on the plugin info, and not in a
|
||||
// *hash table*?
|
||||
nsPRUintKey key(absoluteOffset);
|
||||
int32_t amtForwardToPlugin =
|
||||
NS_PTR_TO_INT32(mDataForwardToRequest->Get(&key));
|
||||
mDataForwardToRequest->Put(&key, NS_INT32_TO_PTR(amtForwardToPlugin + aLength));
|
||||
|
||||
int32_t amtForwardToPlugin = mDataForwardToRequest->Get(absoluteOffset);
|
||||
mDataForwardToRequest->Put(absoluteOffset, (amtForwardToPlugin + aLength));
|
||||
|
||||
SetStreamOffset(absoluteOffset + amtForwardToPlugin);
|
||||
}
|
||||
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream = aIStream;
|
||||
|
||||
|
||||
// if we are caching the file ourselves to disk, we want to 'tee' off
|
||||
// the data as the plugin read from the stream. We do this by the magic
|
||||
// of an input stream tee.
|
||||
|
||||
|
||||
if (mFileCacheOutputStream) {
|
||||
rv = NS_NewInputStreamTee(getter_AddRefs(stream), aIStream, mFileCacheOutputStream);
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
rv = mPStreamListener->OnDataAvailable(this,
|
||||
stream,
|
||||
aLength);
|
||||
|
||||
|
||||
// if a plugin returns an error, the peer must kill the stream
|
||||
// else the stream and PluginStreamListener leak
|
||||
if (NS_FAILED(rv))
|
||||
|
@ -876,7 +853,7 @@ NS_IMETHODIMP nsPluginStreamListenerPeer::OnDataAvailable(nsIRequest *request,
|
|||
char* buffer = new char[aLength];
|
||||
uint32_t amountRead, amountWrote = 0;
|
||||
rv = aIStream->Read(buffer, aLength, &amountRead);
|
||||
|
||||
|
||||
// if we are caching this to disk ourselves, lets write the bytes out.
|
||||
if (mFileCacheOutputStream) {
|
||||
while (amountWrote < amountRead && NS_SUCCEEDED(rv)) {
|
||||
|
@ -901,11 +878,11 @@ NS_IMETHODIMP nsPluginStreamListenerPeer::OnStopRequest(nsIRequest *request,
|
|||
NS_ERROR("Received OnStopRequest for untracked request.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PLUGIN_LOG(PLUGIN_LOG_NOISY,
|
||||
("nsPluginStreamListenerPeer::OnStopRequest this=%p aStatus=%d request=%p\n",
|
||||
this, aStatus, request));
|
||||
|
||||
|
||||
// for ByteRangeRequest we're just updating the mDataForwardToRequest hash and return.
|
||||
nsCOMPtr<nsIByteRangeRequest> brr = do_QueryInterface(request);
|
||||
if (brr) {
|
||||
|
@ -913,13 +890,11 @@ NS_IMETHODIMP nsPluginStreamListenerPeer::OnStopRequest(nsIRequest *request,
|
|||
brr->GetStartRange(&absoluteOffset64);
|
||||
// XXX support 64-bit offsets
|
||||
int32_t absoluteOffset = (int32_t)int64_t(absoluteOffset64);
|
||||
|
||||
nsPRUintKey key(absoluteOffset);
|
||||
|
||||
|
||||
// remove the request from our data forwarding count hash.
|
||||
mDataForwardToRequest->Remove(&key);
|
||||
|
||||
|
||||
mDataForwardToRequest->Remove(absoluteOffset);
|
||||
|
||||
|
||||
PLUGIN_LOG(PLUGIN_LOG_NOISY,
|
||||
(" ::OnStopRequest for ByteRangeRequest Started=%d\n",
|
||||
absoluteOffset));
|
||||
|
@ -929,11 +904,11 @@ NS_IMETHODIMP nsPluginStreamListenerPeer::OnStopRequest(nsIRequest *request,
|
|||
// close & tear it down here
|
||||
mFileCacheOutputStream = nullptr;
|
||||
}
|
||||
|
||||
|
||||
// if we still have pending stuff to do, lets not close the plugin socket.
|
||||
if (--mPendingRequests > 0)
|
||||
return NS_OK;
|
||||
|
||||
|
||||
// we keep our connections around...
|
||||
nsCOMPtr<nsISupportsPRUint32> container = do_QueryInterface(aContext);
|
||||
if (container) {
|
||||
|
@ -944,10 +919,10 @@ NS_IMETHODIMP nsPluginStreamListenerPeer::OnStopRequest(nsIRequest *request,
|
|||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!mPStreamListener)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
|
||||
nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
|
||||
if (!channel)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
@ -956,21 +931,21 @@ NS_IMETHODIMP nsPluginStreamListenerPeer::OnStopRequest(nsIRequest *request,
|
|||
rv = channel->GetContentType(aContentType);
|
||||
if (NS_FAILED(rv) && !mRequestFailed)
|
||||
return rv;
|
||||
|
||||
|
||||
if (!aContentType.IsEmpty())
|
||||
mContentType = aContentType;
|
||||
|
||||
|
||||
// set error status if stream failed so we notify the plugin
|
||||
if (mRequestFailed)
|
||||
aStatus = NS_ERROR_FAILURE;
|
||||
|
||||
|
||||
if (NS_FAILED(aStatus)) {
|
||||
// on error status cleanup the stream
|
||||
// and return w/o OnFileAvailable()
|
||||
mPStreamListener->OnStopBinding(this, aStatus);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
// call OnFileAvailable if plugin requests stream type StreamType_AsFile or StreamType_AsFileOnly
|
||||
if (mStreamType >= NP_ASFILE) {
|
||||
nsCOMPtr<nsIFile> localFile;
|
||||
|
@ -983,12 +958,12 @@ NS_IMETHODIMP nsPluginStreamListenerPeer::OnStopRequest(nsIRequest *request,
|
|||
fileChannel->GetFile(getter_AddRefs(localFile));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (localFile) {
|
||||
OnFileAvailable(localFile);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (mStartBinding) {
|
||||
// On start binding has been called
|
||||
mPStreamListener->OnStopBinding(this, aStatus);
|
||||
|
@ -997,11 +972,11 @@ NS_IMETHODIMP nsPluginStreamListenerPeer::OnStopRequest(nsIRequest *request,
|
|||
mPStreamListener->OnStartBinding(this);
|
||||
mPStreamListener->OnStopBinding(this, aStatus);
|
||||
}
|
||||
|
||||
|
||||
if (NS_SUCCEEDED(aStatus)) {
|
||||
mStreamComplete = true;
|
||||
}
|
||||
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1009,7 +984,7 @@ nsresult nsPluginStreamListenerPeer::SetUpStreamListener(nsIRequest *request,
|
|||
nsIURI* aURL)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
|
||||
// If we don't yet have a stream listener, we need to get
|
||||
// one from the plugin.
|
||||
// NOTE: this should only happen when a stream was NOT created
|
||||
|
@ -1033,11 +1008,11 @@ nsresult nsPluginStreamListenerPeer::SetUpStreamListener(nsIRequest *request,
|
|||
mPStreamListener->SetStreamListenerPeer(this);
|
||||
|
||||
bool useLocalCache = false;
|
||||
|
||||
|
||||
// get httpChannel to retrieve some info we need for nsIPluginStreamInfo setup
|
||||
nsCOMPtr<nsIChannel> channel = do_QueryInterface(request);
|
||||
nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(channel);
|
||||
|
||||
|
||||
/*
|
||||
* Assumption
|
||||
* By the time nsPluginStreamListenerPeer::OnDataAvailable() gets
|
||||
|
@ -1077,7 +1052,7 @@ nsresult nsPluginStreamListenerPeer::SetUpStreamListener(nsIRequest *request,
|
|||
|
||||
// Also provide all HTTP response headers to our listener.
|
||||
httpChannel->VisitResponseHeaders(this);
|
||||
|
||||
|
||||
mSeekable = false;
|
||||
// first we look for a content-encoding header. If we find one, we tell the
|
||||
// plugin that stream is not seekable, because the plugin always sees
|
||||
|
@ -1103,7 +1078,7 @@ nsresult nsPluginStreamListenerPeer::SetUpStreamListener(nsIRequest *request,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// we require a content len
|
||||
// get Last-Modified header for plugin info
|
||||
nsAutoCString lastModified;
|
||||
|
@ -1111,22 +1086,22 @@ nsresult nsPluginStreamListenerPeer::SetUpStreamListener(nsIRequest *request,
|
|||
!lastModified.IsEmpty()) {
|
||||
PRTime time64;
|
||||
PR_ParseTimeString(lastModified.get(), true, &time64); //convert string time to integer time
|
||||
|
||||
|
||||
// Convert PRTime to unix-style time_t, i.e. seconds since the epoch
|
||||
double fpTime = double(time64);
|
||||
mModified = (uint32_t)(fpTime * 1e-6 + 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
rv = mPStreamListener->OnStartBinding(this);
|
||||
|
||||
|
||||
mStartBinding = true;
|
||||
|
||||
|
||||
if (NS_FAILED(rv))
|
||||
return rv;
|
||||
|
||||
|
||||
mPStreamListener->GetStreamType(&mStreamType);
|
||||
|
||||
|
||||
if (!useLocalCache && mStreamType >= NP_ASFILE) {
|
||||
// check it out if this is not a file channel.
|
||||
nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(request);
|
||||
|
@ -1134,11 +1109,11 @@ nsresult nsPluginStreamListenerPeer::SetUpStreamListener(nsIRequest *request,
|
|||
useLocalCache = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (useLocalCache) {
|
||||
SetupPluginCacheFile(channel);
|
||||
}
|
||||
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1148,16 +1123,16 @@ nsPluginStreamListenerPeer::OnFileAvailable(nsIFile* aFile)
|
|||
nsresult rv;
|
||||
if (!mPStreamListener)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
|
||||
nsAutoCString path;
|
||||
rv = aFile->GetNativePath(path);
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
|
||||
if (path.IsEmpty()) {
|
||||
NS_WARNING("empty path");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
||||
rv = mPStreamListener->OnFileAvailable(this, path.get());
|
||||
return rv;
|
||||
}
|
||||
|
@ -1247,7 +1222,7 @@ private:
|
|||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS1(ChannelRedirectProxyCallback, nsIAsyncVerifyRedirectCallback)
|
||||
|
||||
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsPluginStreamListenerPeer::AsyncOnChannelRedirect(nsIChannel *oldChannel, nsIChannel *newChannel,
|
||||
|
|
|
@ -13,7 +13,8 @@
|
|||
#include "nsIHttpHeaderVisitor.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "nsNPAPIPluginStreamListener.h"
|
||||
#include "nsHashtable.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsHashKeys.h"
|
||||
#include "nsNPAPIPluginInstance.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "nsIChannelEventSink.h"
|
||||
|
@ -31,12 +32,12 @@ class CachedFileHolder
|
|||
public:
|
||||
CachedFileHolder(nsIFile* cacheFile);
|
||||
~CachedFileHolder();
|
||||
|
||||
|
||||
void AddRef();
|
||||
void Release();
|
||||
|
||||
|
||||
nsIFile* file() const { return mFile; }
|
||||
|
||||
|
||||
private:
|
||||
nsAutoRefCnt mRefCnt;
|
||||
nsCOMPtr<nsIFile> mFile;
|
||||
|
@ -52,7 +53,7 @@ public nsIChannelEventSink
|
|||
public:
|
||||
nsPluginStreamListenerPeer();
|
||||
virtual ~nsPluginStreamListenerPeer();
|
||||
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIPROGRESSEVENTSINK
|
||||
NS_DECL_NSIREQUESTOBSERVER
|
||||
|
@ -105,7 +106,7 @@ public:
|
|||
mRequests.ReplaceObjectAt(newRequest, i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CancelRequests(nsresult status)
|
||||
{
|
||||
// Copy the array to avoid modification during the loop.
|
||||
|
@ -137,7 +138,7 @@ private:
|
|||
|
||||
// Set to true if we request failed (like with a HTTP response of 404)
|
||||
bool mRequestFailed;
|
||||
|
||||
|
||||
/*
|
||||
* Set to true after nsNPAPIPluginStreamListener::OnStartBinding() has
|
||||
* been called. Checked in ::OnStopRequest so we can call the
|
||||
|
@ -149,20 +150,20 @@ private:
|
|||
// these get passed to the plugin stream listener
|
||||
uint32_t mLength;
|
||||
int32_t mStreamType;
|
||||
|
||||
|
||||
// local cached file, we save the content into local cache if browser cache is not available,
|
||||
// or plugin asks stream as file and it expects file extension until bug 90558 got fixed
|
||||
nsRefPtr<CachedFileHolder> mLocalCachedFileHolder;
|
||||
nsCOMPtr<nsIOutputStream> mFileCacheOutputStream;
|
||||
nsHashtable *mDataForwardToRequest;
|
||||
|
||||
nsDataHashtable<nsUint32HashKey, uint32_t>* mDataForwardToRequest;
|
||||
|
||||
nsCString mContentType;
|
||||
bool mSeekable;
|
||||
uint32_t mModified;
|
||||
nsRefPtr<nsNPAPIPluginInstance> mPluginInstance;
|
||||
int32_t mStreamOffset;
|
||||
bool mStreamComplete;
|
||||
|
||||
|
||||
public:
|
||||
bool mAbort;
|
||||
int32_t mPendingRequests;
|
||||
|
|
|
@ -529,42 +529,7 @@ TelephonyProvider.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
let parentId = this._currentCalls[aClientId][aCallIndex].parentId;
|
||||
if (parentId) {
|
||||
this.resumeCall(aClientId, parentId);
|
||||
return;
|
||||
}
|
||||
|
||||
function onCdmaHoldCallSuccess() {
|
||||
let call = this._currentCalls[aClientId][aCallIndex];
|
||||
if (!call) {
|
||||
return;
|
||||
}
|
||||
|
||||
call.state = RIL.CALL_STATE_HOLDING;
|
||||
this.notifyCallStateChanged(aClientId, call);
|
||||
|
||||
if (!call.childId) {
|
||||
return;
|
||||
}
|
||||
|
||||
let childCall = this._currentCalls[aClientId][call.childId];
|
||||
childCall.state = RIL.CALL_STATE_ACTIVE;
|
||||
this.notifyCallStateChanged(aClientId, childCall);
|
||||
};
|
||||
|
||||
this._getClient(aClientId).sendWorkerMessage("holdCall", {
|
||||
callIndex: aCallIndex
|
||||
},(function(response) {
|
||||
if (!response.success) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (response.isCdma) {
|
||||
onCdmaHoldCallSuccess.call(this);
|
||||
}
|
||||
return false;
|
||||
}).bind(this));
|
||||
this._getClient(aClientId).sendWorkerMessage("holdCall", { callIndex: aCallIndex });
|
||||
},
|
||||
|
||||
resumeCall: function(aClientId, aCallIndex) {
|
||||
|
@ -575,43 +540,7 @@ TelephonyProvider.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
let parentId = this._currentCalls[aClientId][aCallIndex].parentId;
|
||||
if (parentId) {
|
||||
this.holdCall(aClientId, parentId);
|
||||
return;
|
||||
}
|
||||
|
||||
function onCdmaResumeCallSuccess() {
|
||||
let call = this._currentCalls[aClientId][aCallIndex];
|
||||
if (!call) {
|
||||
return;
|
||||
}
|
||||
|
||||
call.state = RIL.CALL_STATE_ACTIVE;
|
||||
this.notifyCallStateChanged(aClientId, call);
|
||||
|
||||
let childId = call.childId;
|
||||
if (!childId) {
|
||||
return;
|
||||
}
|
||||
|
||||
let childCall = this._currentCalls[aClientId][childId];
|
||||
childCall.state = RIL.CALL_STATE_HOLDING;
|
||||
this.notifyCallStateChanged(aClientId, childCall);
|
||||
};
|
||||
|
||||
this._getClient(aClientId).sendWorkerMessage("resumeCall", {
|
||||
callIndex: aCallIndex
|
||||
},(function(response) {
|
||||
if (!response.success) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (response.isCdma) {
|
||||
onCdmaResumeCallSuccess.call(this);
|
||||
}
|
||||
return false;
|
||||
}).bind(this));
|
||||
this._getClient(aClientId).sendWorkerMessage("resumeCall", { callIndex: aCallIndex });
|
||||
},
|
||||
|
||||
conferenceCall: function(aClientId) {
|
||||
|
|
|
@ -20,8 +20,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=850517
|
|||
sb.win = window;
|
||||
sb.childA = ifrA.contentWindow;
|
||||
sb.childB = ifrB.contentWindow;
|
||||
sb.is = is;
|
||||
sb.ok = ok;
|
||||
sb.is = SpecialPowers.wrap(is);
|
||||
sb.ok = SpecialPowers.wrap(ok);
|
||||
is(window.theoneandonly.frameElement, ifrA, "Named child resolution works");
|
||||
SpecialPowers.Cu.evalInSandbox('is(win.theoneandonly, childA, "Named child resolution works via Xray");', sb);
|
||||
ifrA.removeAttribute('name');
|
||||
|
|
|
@ -19,8 +19,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=862918
|
|||
is(evt.data, 'dosetup', "message from modal window is correct");
|
||||
var win = SpecialPowers.wrap(evt.source);
|
||||
win.wrappedJSObject.SpecialPowers = SpecialPowers;
|
||||
win.wrappedJSObject.is = is;
|
||||
win.wrappedJSObject.ok = ok;
|
||||
win.wrappedJSObject.is = SpecialPowers.wrap(is);
|
||||
win.wrappedJSObject.ok = SpecialPowers.wrap(ok);
|
||||
win.wrappedJSObject.go();
|
||||
};
|
||||
|
||||
|
|
|
@ -239,7 +239,7 @@ partial interface Document {
|
|||
|
||||
//http://dvcs.w3.org/hg/webcomponents/raw-file/tip/spec/custom/index.html#dfn-document-register
|
||||
partial interface Document {
|
||||
[Throws, Pref="dom.webcomponents.enabled"]
|
||||
[Throws, Func="nsDocument::IsRegisterElementEnabled"]
|
||||
object registerElement(DOMString name, optional ElementRegistrationOptions options);
|
||||
};
|
||||
|
||||
|
|
|
@ -75,7 +75,7 @@ XBLFinalize(JSFreeOp *fop, JSObject *obj)
|
|||
nsXBLDocumentInfo* docInfo =
|
||||
static_cast<nsXBLDocumentInfo*>(::JS_GetPrivate(obj));
|
||||
nsContentUtils::DeferredFinalize(docInfo);
|
||||
|
||||
|
||||
nsXBLJSClass* c = nsXBLJSClass::fromJSClass(::JS_GetClass(obj));
|
||||
c->Drop();
|
||||
}
|
||||
|
@ -121,8 +121,7 @@ nsXBLJSClass::Destroy()
|
|||
"referenced nsXBLJSClass is on LRU list already!?");
|
||||
|
||||
if (nsXBLService::gClassTable) {
|
||||
nsCStringKey key(mKey);
|
||||
(nsXBLService::gClassTable)->Remove(&key);
|
||||
nsXBLService::gClassTable->Remove(mKey);
|
||||
mKey.Truncate();
|
||||
}
|
||||
|
||||
|
@ -141,14 +140,7 @@ nsXBLJSClass::Destroy()
|
|||
nsXBLJSClass*
|
||||
nsXBLService::getClass(const nsCString& k)
|
||||
{
|
||||
nsCStringKey key(k);
|
||||
return getClass(&key);
|
||||
}
|
||||
|
||||
nsXBLJSClass*
|
||||
nsXBLService::getClass(nsCStringKey *k)
|
||||
{
|
||||
return static_cast<nsXBLJSClass*>(nsXBLService::gClassTable->Get(k));
|
||||
return nsXBLService::gClassTable->Get(k);
|
||||
}
|
||||
|
||||
// Implementation /////////////////////////////////////////////////////////////////
|
||||
|
@ -265,7 +257,7 @@ nsXBLBinding::InstallAnonymousContent(nsIContent* aAnonParent, nsIContent* aElem
|
|||
// XXXbz This really shouldn't be a void method!
|
||||
child->UnbindFromTree();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
child->SetFlags(NODE_IS_ANONYMOUS_ROOT);
|
||||
|
||||
|
@ -360,7 +352,7 @@ nsXBLBinding::GenerateAnonymousContent()
|
|||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Find out if we're really building kids or if we're just
|
||||
// using the attribute-setting shorthand hack.
|
||||
uint32_t contentCount = content->GetChildCount();
|
||||
|
@ -478,7 +470,7 @@ nsXBLBinding::FindInsertionPointFor(nsIContent* aChild)
|
|||
if (mContent) {
|
||||
return FindInsertionPointForInternal(aChild);
|
||||
}
|
||||
|
||||
|
||||
return mNextBinding ? mNextBinding->FindInsertionPointFor(aChild)
|
||||
: nullptr;
|
||||
}
|
||||
|
@ -492,7 +484,7 @@ nsXBLBinding::FindInsertionPointForInternal(nsIContent* aChild)
|
|||
return point;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return mDefaultInsertionPoint;
|
||||
}
|
||||
|
||||
|
@ -584,7 +576,7 @@ nsXBLBinding::InstallEventHandlers()
|
|||
nsAutoString type;
|
||||
handler->GetEventName(type);
|
||||
|
||||
// If this is a command, add it in the system event group, otherwise
|
||||
// If this is a command, add it in the system event group, otherwise
|
||||
// add it to the standard event group.
|
||||
|
||||
// Figure out if we're using capturing or not.
|
||||
|
@ -684,7 +676,7 @@ nsXBLBinding::UnhookEventHandlers()
|
|||
if (!manager) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
bool isChromeBinding = mPrototypeBinding->IsChrome();
|
||||
nsXBLPrototypeHandler* curr;
|
||||
for (curr = handlerChain; curr; curr = curr->GetNextHandler()) {
|
||||
|
@ -692,7 +684,7 @@ nsXBLBinding::UnhookEventHandlers()
|
|||
if (!handler) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
nsCOMPtr<nsIAtom> eventAtom = curr->GetEventName();
|
||||
if (!eventAtom ||
|
||||
eventAtom == nsGkAtoms::keyup ||
|
||||
|
@ -731,7 +723,7 @@ nsXBLBinding::UnhookEventHandlers()
|
|||
EventListenerFlags flags;
|
||||
flags.mCapture = (handler->GetPhase() == NS_PHASE_CAPTURING);
|
||||
|
||||
// If this is a command, remove it from the system event group, otherwise
|
||||
// If this is a command, remove it from the system event group, otherwise
|
||||
// remove it from the standard event group.
|
||||
|
||||
if ((handler->GetType() & (NS_HANDLER_TYPE_XBL_COMMAND | NS_HANDLER_TYPE_SYSTEM)) &&
|
||||
|
@ -905,7 +897,7 @@ nsXBLBinding::InheritsStyle() const
|
|||
// XXX What about bindings with <content> but no kids, e.g., my treecell-text binding?
|
||||
if (mContent)
|
||||
return mPrototypeBinding->InheritsStyle();
|
||||
|
||||
|
||||
if (mNextBinding)
|
||||
return mNextBinding->InheritsStyle();
|
||||
|
||||
|
@ -995,9 +987,8 @@ nsXBLBinding::DoInitJSClass(JSContext *cx, JS::Handle<JSObject*> global,
|
|||
// We need to initialize the class.
|
||||
*aNew = true;
|
||||
|
||||
nsCStringKey key(xblKey);
|
||||
if (!c) {
|
||||
c = nsXBLService::getClass(&key);
|
||||
c = nsXBLService::getClass(xblKey);
|
||||
}
|
||||
if (c) {
|
||||
// If c is on the LRU list, remove it now!
|
||||
|
@ -1015,8 +1006,7 @@ nsXBLBinding::DoInitJSClass(JSContext *cx, JS::Handle<JSObject*> global,
|
|||
nsXBLService::gClassLRUListLength--;
|
||||
|
||||
// Remove any mapping from the old name to the class struct.
|
||||
nsCStringKey oldKey(c->Key());
|
||||
(nsXBLService::gClassTable)->Remove(&oldKey);
|
||||
nsXBLService::gClassTable->Remove(c->Key());
|
||||
|
||||
// Change the class name and we're done.
|
||||
nsMemory::Free((void*) c->name);
|
||||
|
@ -1025,7 +1015,7 @@ nsXBLBinding::DoInitJSClass(JSContext *cx, JS::Handle<JSObject*> global,
|
|||
}
|
||||
|
||||
// Add c to our table.
|
||||
(nsXBLService::gClassTable)->Put(&key, (void*)c);
|
||||
nsXBLService::gClassTable->Put(xblKey, c);
|
||||
}
|
||||
|
||||
// The prototype holds a strong reference to its class struct.
|
||||
|
@ -1034,7 +1024,7 @@ nsXBLBinding::DoInitJSClass(JSContext *cx, JS::Handle<JSObject*> global,
|
|||
// Make a new object prototyped by parent_proto and parented by global.
|
||||
proto = ::JS_InitClass(cx, // context
|
||||
global, // global object
|
||||
parent_proto, // parent proto
|
||||
parent_proto, // parent proto
|
||||
c, // JSClass
|
||||
nullptr, // JSNative ctor
|
||||
0, // ctor args
|
||||
|
@ -1046,7 +1036,7 @@ nsXBLBinding::DoInitJSClass(JSContext *cx, JS::Handle<JSObject*> global,
|
|||
// This will happen if we're OOM or if the security manager
|
||||
// denies defining the new class...
|
||||
|
||||
(nsXBLService::gClassTable)->Remove(&key);
|
||||
nsXBLService::gClassTable->Remove(xblKey);
|
||||
|
||||
c->Drop();
|
||||
|
||||
|
|
|
@ -154,7 +154,7 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
// nsXBLStreamListener, a helper class used for
|
||||
// nsXBLStreamListener, a helper class used for
|
||||
// asynchronous parsing of URLs
|
||||
/* Header file */
|
||||
class nsXBLStreamListener MOZ_FINAL : public nsIStreamListener,
|
||||
|
@ -177,7 +177,7 @@ public:
|
|||
private:
|
||||
nsCOMPtr<nsIStreamListener> mInner;
|
||||
nsAutoTArray<nsXBLBindingRequest*, 8> mBindingRequests;
|
||||
|
||||
|
||||
nsCOMPtr<nsIWeakReference> mBoundDocument;
|
||||
nsCOMPtr<nsIXMLContentSink> mSink; // Only set until OnStartRequest
|
||||
nsCOMPtr<nsIDocument> mBindingDocument; // Only set until OnStartRequest
|
||||
|
@ -247,7 +247,7 @@ nsXBLStreamListener::OnStartRequest(nsIRequest* request, nsISupports* aCtxt)
|
|||
return mInner->OnStartRequest(request, aCtxt);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
NS_IMETHODIMP
|
||||
nsXBLStreamListener::OnStopRequest(nsIRequest* request, nsISupports* aCtxt, nsresult aStatus)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
@ -301,7 +301,7 @@ nsXBLStreamListener::HandleEvent(nsIDOMEvent* aEvent)
|
|||
// We have to do a flush prior to notification of the document load.
|
||||
// This has to happen since the HTML content sink can be holding on
|
||||
// to notifications related to our children (e.g., if you bind to the
|
||||
// <body> tag) that result in duplication of content.
|
||||
// <body> tag) that result in duplication of content.
|
||||
// We need to get the sink's notifications flushed and then make the binding
|
||||
// ready.
|
||||
if (count > 0) {
|
||||
|
@ -347,7 +347,7 @@ nsXBLStreamListener::HandleEvent(nsIDOMEvent* aEvent)
|
|||
cache->PutXBLDocumentInfo(info);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
bindingManager->PutXBLDocumentInfo(info);
|
||||
|
||||
// Notify all pending requests that their bindings are
|
||||
|
@ -368,7 +368,7 @@ nsXBLStreamListener::HandleEvent(nsIDOMEvent* aEvent)
|
|||
// Static member variable initialization
|
||||
bool nsXBLService::gAllowDataURIs = false;
|
||||
|
||||
nsHashtable* nsXBLService::gClassTable = nullptr;
|
||||
nsXBLService::ClassTable* nsXBLService::gClassTable = nullptr;
|
||||
|
||||
LinkedList<nsXBLJSClass>* nsXBLService::gClassLRUList = nullptr;
|
||||
uint32_t nsXBLService::gClassLRUListLength = 0;
|
||||
|
@ -393,7 +393,7 @@ nsXBLService::Init()
|
|||
// Constructors/Destructors
|
||||
nsXBLService::nsXBLService(void)
|
||||
{
|
||||
gClassTable = new nsHashtable();
|
||||
gClassTable = new ClassTable();
|
||||
gClassLRUList = new LinkedList<nsXBLJSClass>();
|
||||
|
||||
Preferences::AddBoolVarCache(&gAllowDataURIs, "layout.debug.enable_data_xbl");
|
||||
|
@ -423,7 +423,7 @@ nsXBLService::IsChromeOrResourceURI(nsIURI* aURI)
|
|||
{
|
||||
bool isChrome = false;
|
||||
bool isResource = false;
|
||||
if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) &&
|
||||
if (NS_SUCCEEDED(aURI->SchemeIs("chrome", &isChrome)) &&
|
||||
NS_SUCCEEDED(aURI->SchemeIs("resource", &isResource)))
|
||||
return (isChrome || isResource);
|
||||
return false;
|
||||
|
@ -435,10 +435,10 @@ nsXBLService::IsChromeOrResourceURI(nsIURI* aURI)
|
|||
nsresult
|
||||
nsXBLService::LoadBindings(nsIContent* aContent, nsIURI* aURL,
|
||||
nsIPrincipal* aOriginPrincipal,
|
||||
nsXBLBinding** aBinding, bool* aResolveStyle)
|
||||
nsXBLBinding** aBinding, bool* aResolveStyle)
|
||||
{
|
||||
NS_PRECONDITION(aOriginPrincipal, "Must have an origin principal");
|
||||
|
||||
|
||||
*aBinding = nullptr;
|
||||
*aResolveStyle = false;
|
||||
|
||||
|
@ -518,11 +518,11 @@ nsXBLService::LoadBindings(nsIContent* aContent, nsIURI* aURL,
|
|||
|
||||
// Figure out if we have any scoped sheets. If so, we do a second resolve.
|
||||
*aResolveStyle = newBinding->HasStyleSheets();
|
||||
|
||||
|
||||
newBinding.swap(*aBinding);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
|
@ -537,7 +537,7 @@ nsXBLService::FlushStyleBindings(nsIContent* aContent)
|
|||
|
||||
aContent->SetXBLBinding(nullptr); // Flush old style bindings
|
||||
}
|
||||
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -585,6 +585,15 @@ nsXBLService::AttachGlobalKeyHandler(EventTarget* aTarget)
|
|||
manager->AddEventListenerByType(handler, NS_LITERAL_STRING("keypress"),
|
||||
TrustedEventsAtSystemGroupBubble());
|
||||
|
||||
// The capturing listener is only used for XUL keysets to properly handle
|
||||
// shortcut keys in a multi-process environment.
|
||||
manager->AddEventListenerByType(handler, NS_LITERAL_STRING("keydown"),
|
||||
TrustedEventsAtSystemGroupCapture());
|
||||
manager->AddEventListenerByType(handler, NS_LITERAL_STRING("keyup"),
|
||||
TrustedEventsAtSystemGroupCapture());
|
||||
manager->AddEventListenerByType(handler, NS_LITERAL_STRING("keypress"),
|
||||
TrustedEventsAtSystemGroupCapture());
|
||||
|
||||
if (contentNode)
|
||||
return contentNode->SetProperty(nsGkAtoms::listener,
|
||||
handler.forget().take(),
|
||||
|
@ -630,6 +639,13 @@ nsXBLService::DetachGlobalKeyHandler(EventTarget* aTarget)
|
|||
manager->RemoveEventListenerByType(handler, NS_LITERAL_STRING("keypress"),
|
||||
TrustedEventsAtSystemGroupBubble());
|
||||
|
||||
manager->RemoveEventListenerByType(handler, NS_LITERAL_STRING("keydown"),
|
||||
TrustedEventsAtSystemGroupCapture());
|
||||
manager->RemoveEventListenerByType(handler, NS_LITERAL_STRING("keyup"),
|
||||
TrustedEventsAtSystemGroupCapture());
|
||||
manager->RemoveEventListenerByType(handler, NS_LITERAL_STRING("keypress"),
|
||||
TrustedEventsAtSystemGroupCapture());
|
||||
|
||||
contentNode->DeleteProperty(nsGkAtoms::listener);
|
||||
|
||||
return NS_OK;
|
||||
|
@ -659,7 +675,7 @@ nsXBLService::FlushMemory()
|
|||
|
||||
nsresult
|
||||
nsXBLService::BindingReady(nsIContent* aBoundElement,
|
||||
nsIURI* aURI,
|
||||
nsIURI* aURI,
|
||||
bool* aIsReady)
|
||||
{
|
||||
// Don't do a security check here; we know this binding is set to go.
|
||||
|
@ -667,7 +683,7 @@ nsXBLService::BindingReady(nsIContent* aBoundElement,
|
|||
}
|
||||
|
||||
nsresult
|
||||
nsXBLService::GetBinding(nsIContent* aBoundElement, nsIURI* aURI,
|
||||
nsXBLService::GetBinding(nsIContent* aBoundElement, nsIURI* aURI,
|
||||
bool aPeekOnly, nsIPrincipal* aOriginPrincipal,
|
||||
bool* aIsReady, nsXBLBinding** aResult)
|
||||
{
|
||||
|
@ -678,7 +694,7 @@ nsXBLService::GetBinding(nsIContent* aBoundElement, nsIURI* aURI,
|
|||
}
|
||||
|
||||
nsresult
|
||||
nsXBLService::GetBinding(nsIContent* aBoundElement, nsIURI* aURI,
|
||||
nsXBLService::GetBinding(nsIContent* aBoundElement, nsIURI* aURI,
|
||||
bool aPeekOnly, nsIPrincipal* aOriginPrincipal,
|
||||
bool* aIsReady, nsXBLBinding** aResult,
|
||||
nsTArray<nsIURI*>& aDontExtendURIs)
|
||||
|
@ -686,7 +702,7 @@ nsXBLService::GetBinding(nsIContent* aBoundElement, nsIURI* aURI,
|
|||
NS_ASSERTION(aPeekOnly || aResult,
|
||||
"Must have non-null out param if not just peeking to see "
|
||||
"whether the binding is ready");
|
||||
|
||||
|
||||
if (aResult)
|
||||
*aResult = nullptr;
|
||||
|
||||
|
@ -703,7 +719,7 @@ nsXBLService::GetBinding(nsIContent* aBoundElement, nsIURI* aURI,
|
|||
aOriginPrincipal,
|
||||
false, getter_AddRefs(docInfo));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
||||
if (!docInfo)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
|
@ -821,11 +837,11 @@ IsSystemOrChromeURLPrincipal(nsIPrincipal* aPrincipal)
|
|||
if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
aPrincipal->GetURI(getter_AddRefs(uri));
|
||||
NS_ENSURE_TRUE(uri, false);
|
||||
|
||||
|
||||
bool isChrome = false;
|
||||
return NS_SUCCEEDED(uri->SchemeIs("chrome", &isChrome)) && isChrome;
|
||||
}
|
||||
|
@ -841,11 +857,11 @@ nsXBLService::LoadBindingDocumentInfo(nsIContent* aBoundElement,
|
|||
NS_PRECONDITION(aBindingURI, "Must have a binding URI");
|
||||
NS_PRECONDITION(!aOriginPrincipal || aBoundDocument,
|
||||
"If we're doing a security check, we better have a document!");
|
||||
|
||||
|
||||
nsresult rv;
|
||||
if (aOriginPrincipal) {
|
||||
// Security check - Enforce same-origin policy, except to chrome.
|
||||
// We have to be careful to not pass aContent as the context here.
|
||||
// We have to be careful to not pass aContent as the context here.
|
||||
// Otherwise, if there is a JS-implemented content policy, we will attempt
|
||||
// to wrap the content node, which will try to load XBL bindings for it, if
|
||||
// any. Since we're not done loading this binding yet, that will reenter
|
||||
|
@ -886,13 +902,13 @@ nsXBLService::LoadBindingDocumentInfo(nsIContent* aBoundElement,
|
|||
#ifdef MOZ_XUL
|
||||
// We've got a file. Check our XBL document cache.
|
||||
nsXULPrototypeCache* cache = nsXULPrototypeCache::GetInstance();
|
||||
bool useXULCache = cache && cache->IsEnabled();
|
||||
bool useXULCache = cache && cache->IsEnabled();
|
||||
|
||||
if (useXULCache) {
|
||||
// The first line of defense is the chrome cache.
|
||||
// The first line of defense is the chrome cache.
|
||||
// This cache crosses the entire product, so that any XBL bindings that are
|
||||
// part of chrome will be reused across all XUL documents.
|
||||
info = cache->GetXBLDocumentInfo(documentURI);
|
||||
info = cache->GetXBLDocumentInfo(documentURI);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -957,7 +973,7 @@ nsXBLService::LoadBindingDocumentInfo(nsIContent* aBoundElement,
|
|||
if (!info) {
|
||||
// Finally, if all lines of defense fail, we go and fetch the binding
|
||||
// document.
|
||||
|
||||
|
||||
// Always load chrome synchronously
|
||||
bool chrome;
|
||||
if (NS_SUCCEEDED(documentURI->SchemeIs("chrome", &chrome)) && chrome)
|
||||
|
@ -985,7 +1001,7 @@ nsXBLService::LoadBindingDocumentInfo(nsIContent* aBoundElement,
|
|||
info->WritePrototypeBindings();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
if (bindingManager) {
|
||||
// Also put it in our binding manager's document table.
|
||||
bindingManager->PutXBLDocumentInfo(info);
|
||||
|
@ -1001,7 +1017,7 @@ nsXBLService::LoadBindingDocumentInfo(nsIContent* aBoundElement,
|
|||
|
||||
nsresult
|
||||
nsXBLService::FetchBindingDocument(nsIContent* aBoundElement, nsIDocument* aBoundDocument,
|
||||
nsIURI* aDocumentURI, nsIURI* aBindingURI,
|
||||
nsIURI* aDocumentURI, nsIURI* aBindingURI,
|
||||
bool aForceSyncLoad, nsIDocument** aResult)
|
||||
{
|
||||
nsresult rv = NS_OK;
|
||||
|
@ -1009,7 +1025,7 @@ nsXBLService::FetchBindingDocument(nsIContent* aBoundElement, nsIDocument* aBoun
|
|||
*aResult = nullptr;
|
||||
|
||||
// Now we have to synchronously load the binding file.
|
||||
// Create an XML content sink and a parser.
|
||||
// Create an XML content sink and a parser.
|
||||
nsCOMPtr<nsILoadGroup> loadGroup;
|
||||
if (aBoundDocument)
|
||||
loadGroup = aBoundDocument->GetDocumentLoadGroup();
|
||||
|
@ -1092,4 +1108,3 @@ nsXBLService::FetchBindingDocument(nsIContent* aBoundElement, nsIDocument* aBoun
|
|||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,8 +14,9 @@
|
|||
#include "nsWeakReference.h"
|
||||
#include "js/Class.h" // nsXBLJSClass derives from JSClass
|
||||
#include "nsTArray.h"
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsHashKeys.h"
|
||||
|
||||
class nsCStringKey;
|
||||
class nsXBLBinding;
|
||||
class nsXBLDocumentInfo;
|
||||
class nsXBLJSClass;
|
||||
|
@ -24,7 +25,6 @@ class nsIDocument;
|
|||
class nsString;
|
||||
class nsIURI;
|
||||
class nsIPrincipal;
|
||||
class nsHashtable;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
@ -84,10 +84,10 @@ protected:
|
|||
|
||||
// Release any memory that we can
|
||||
nsresult FlushMemory();
|
||||
|
||||
|
||||
// This method synchronously loads and parses an XBL file.
|
||||
nsresult FetchBindingDocument(nsIContent* aBoundElement, nsIDocument* aBoundDocument,
|
||||
nsIURI* aDocumentURI, nsIURI* aBindingURI,
|
||||
nsIURI* aDocumentURI, nsIURI* aBindingURI,
|
||||
bool aForceSyncLoad, nsIDocument** aResult);
|
||||
|
||||
/**
|
||||
|
@ -123,7 +123,8 @@ protected:
|
|||
public:
|
||||
static bool gDisableChromeCache;
|
||||
|
||||
static nsHashtable* gClassTable; // A table of nsXBLJSClass objects.
|
||||
typedef nsDataHashtable<nsCStringHashKey, nsXBLJSClass*> ClassTable;
|
||||
static ClassTable* gClassTable; // A table of nsXBLJSClass objects.
|
||||
|
||||
static mozilla::LinkedList<nsXBLJSClass>* gClassLRUList;
|
||||
// LRU list of cached classes.
|
||||
|
@ -135,7 +136,6 @@ public:
|
|||
|
||||
// Look up the class by key in gClassTable.
|
||||
static nsXBLJSClass *getClass(const nsCString &key);
|
||||
static nsXBLJSClass *getClass(nsCStringKey *key);
|
||||
};
|
||||
|
||||
class nsXBLJSClass : public mozilla::LinkedListElement<nsXBLJSClass>
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/TextEvents.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/Event.h"
|
||||
#include "nsEventStateManager.h"
|
||||
#include "nsIEditor.h"
|
||||
#include "nsIHTMLEditor.h"
|
||||
|
@ -276,24 +277,23 @@ nsXBLWindowKeyHandler::WalkHandlers(nsIDOMKeyEvent* aKeyEvent, nsIAtom* aEventTy
|
|||
nsresult rv = EnsureHandlers();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<Element> el = GetElement();
|
||||
bool isDisabled;
|
||||
nsCOMPtr<Element> el = GetElement(&isDisabled);
|
||||
if (!el) {
|
||||
if (mUserHandler) {
|
||||
WalkHandlersInternal(aKeyEvent, aEventType, mUserHandler);
|
||||
WalkHandlersInternal(aKeyEvent, aEventType, mUserHandler, true);
|
||||
aKeyEvent->GetDefaultPrevented(&prevent);
|
||||
if (prevent)
|
||||
return NS_OK; // Handled by the user bindings. Our work here is done.
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIContent> content = do_QueryInterface(el);
|
||||
// skip keysets that are disabled
|
||||
if (content && content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
|
||||
nsGkAtoms::_true, eCaseMatters)) {
|
||||
if (el && isDisabled) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
WalkHandlersInternal(aKeyEvent, aEventType, mHandler);
|
||||
WalkHandlersInternal(aKeyEvent, aEventType, mHandler, true);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -304,20 +304,50 @@ nsXBLWindowKeyHandler::HandleEvent(nsIDOMEvent* aEvent)
|
|||
nsCOMPtr<nsIDOMKeyEvent> keyEvent(do_QueryInterface(aEvent));
|
||||
NS_ENSURE_TRUE(keyEvent, NS_ERROR_INVALID_ARG);
|
||||
|
||||
uint16_t eventPhase;
|
||||
aEvent->GetEventPhase(&eventPhase);
|
||||
if (eventPhase == nsIDOMEvent::CAPTURING_PHASE) {
|
||||
HandleEventOnCapture(keyEvent);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsAutoString eventType;
|
||||
aEvent->GetType(eventType);
|
||||
nsCOMPtr<nsIAtom> eventTypeAtom = do_GetAtom(eventType);
|
||||
NS_ENSURE_TRUE(eventTypeAtom, NS_ERROR_OUT_OF_MEMORY);
|
||||
|
||||
if (!mWeakPtrForElement) {
|
||||
nsCOMPtr<mozilla::dom::Element> originalTarget =
|
||||
do_QueryInterface(aEvent->GetInternalNSEvent()->originalTarget);
|
||||
if (nsEventStateManager::IsRemoteTarget(originalTarget)) {
|
||||
return NS_OK;
|
||||
}
|
||||
return WalkHandlers(keyEvent, eventTypeAtom);
|
||||
}
|
||||
|
||||
void
|
||||
nsXBLWindowKeyHandler::HandleEventOnCapture(nsIDOMKeyEvent* aEvent)
|
||||
{
|
||||
WidgetKeyboardEvent* widgetEvent =
|
||||
aEvent->GetInternalNSEvent()->AsKeyboardEvent();
|
||||
|
||||
if (widgetEvent->mFlags.mNoCrossProcessBoundaryForwarding) {
|
||||
return;
|
||||
}
|
||||
|
||||
return WalkHandlers(keyEvent, eventTypeAtom);
|
||||
nsCOMPtr<mozilla::dom::Element> originalTarget =
|
||||
do_QueryInterface(aEvent->GetInternalNSEvent()->originalTarget);
|
||||
if (!nsEventStateManager::IsRemoteTarget(originalTarget)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!HasHandlerForEvent(aEvent)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If this event hasn't been marked as mNoCrossProcessBoundaryForwarding
|
||||
// yet, it means it wasn't processed by content. We'll not call any
|
||||
// of the handlers at this moment, and will wait for the event to be
|
||||
// redispatched with mNoCrossProcessBoundaryForwarding = 1 to process it.
|
||||
|
||||
// Inform the child process that this is a event that we want a reply
|
||||
// from.
|
||||
widgetEvent->mFlags.mWantReplyFromContentProcess = 1;
|
||||
aEvent->StopPropagation();
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -391,29 +421,31 @@ nsXBLWindowKeyHandler::IsHTMLEditableFieldFocused()
|
|||
// WalkHandlersInternal and WalkHandlersAndExecute
|
||||
//
|
||||
// Given a particular DOM event and a pointer to the first handler in the list,
|
||||
// scan through the list to find something to handle the event and then make it
|
||||
// so.
|
||||
// scan through the list to find something to handle the event. If aExecute = true,
|
||||
// the handler will be executed; otherwise just return an answer telling if a handler
|
||||
// for that event was found.
|
||||
//
|
||||
nsresult
|
||||
bool
|
||||
nsXBLWindowKeyHandler::WalkHandlersInternal(nsIDOMKeyEvent* aKeyEvent,
|
||||
nsIAtom* aEventType,
|
||||
nsXBLPrototypeHandler* aHandler)
|
||||
nsXBLPrototypeHandler* aHandler,
|
||||
bool aExecute)
|
||||
{
|
||||
nsAutoTArray<nsShortcutCandidate, 10> accessKeys;
|
||||
nsContentUtils::GetAccelKeyCandidates(aKeyEvent, accessKeys);
|
||||
|
||||
if (accessKeys.IsEmpty()) {
|
||||
WalkHandlersAndExecute(aKeyEvent, aEventType, aHandler, 0, false);
|
||||
return NS_OK;
|
||||
return WalkHandlersAndExecute(aKeyEvent, aEventType, aHandler,
|
||||
0, false, aExecute);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < accessKeys.Length(); ++i) {
|
||||
nsShortcutCandidate &key = accessKeys[i];
|
||||
if (WalkHandlersAndExecute(aKeyEvent, aEventType, aHandler,
|
||||
key.mCharCode, key.mIgnoreShift))
|
||||
return NS_OK;
|
||||
key.mCharCode, key.mIgnoreShift, aExecute))
|
||||
return true;
|
||||
}
|
||||
return NS_OK;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -421,7 +453,8 @@ nsXBLWindowKeyHandler::WalkHandlersAndExecute(nsIDOMKeyEvent* aKeyEvent,
|
|||
nsIAtom* aEventType,
|
||||
nsXBLPrototypeHandler* aHandler,
|
||||
uint32_t aCharCode,
|
||||
bool aIgnoreShiftKey)
|
||||
bool aIgnoreShiftKey,
|
||||
bool aExecute)
|
||||
{
|
||||
nsresult rv;
|
||||
|
||||
|
@ -491,6 +524,10 @@ nsXBLWindowKeyHandler::WalkHandlersAndExecute(nsIDOMKeyEvent* aKeyEvent,
|
|||
piTarget = mTarget;
|
||||
}
|
||||
|
||||
if (!aExecute) {
|
||||
return true;
|
||||
}
|
||||
|
||||
rv = currHandler->ExecuteHandler(piTarget, aKeyEvent);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
return true;
|
||||
|
@ -500,10 +537,38 @@ nsXBLWindowKeyHandler::WalkHandlersAndExecute(nsIDOMKeyEvent* aKeyEvent,
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
nsXBLWindowKeyHandler::HasHandlerForEvent(nsIDOMKeyEvent* aEvent)
|
||||
{
|
||||
if (!aEvent->InternalDOMEvent()->IsTrusted()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsresult rv = EnsureHandlers();
|
||||
NS_ENSURE_SUCCESS(rv, false);
|
||||
|
||||
bool isDisabled;
|
||||
nsCOMPtr<Element> el = GetElement(&isDisabled);
|
||||
if (el && isDisabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsAutoString eventType;
|
||||
aEvent->GetType(eventType);
|
||||
nsCOMPtr<nsIAtom> eventTypeAtom = do_GetAtom(eventType);
|
||||
NS_ENSURE_TRUE(eventTypeAtom, false);
|
||||
|
||||
return WalkHandlersInternal(aEvent, eventTypeAtom, mHandler, false);
|
||||
}
|
||||
|
||||
already_AddRefed<Element>
|
||||
nsXBLWindowKeyHandler::GetElement()
|
||||
nsXBLWindowKeyHandler::GetElement(bool* aIsDisabled)
|
||||
{
|
||||
nsCOMPtr<Element> element = do_QueryReferent(mWeakPtrForElement);
|
||||
if (element && aIsDisabled) {
|
||||
*aIsDisabled = element->AttrValueIs(kNameSpaceID_None, nsGkAtoms::disabled,
|
||||
nsGkAtoms::_true, eCaseMatters);
|
||||
}
|
||||
return element.forget();
|
||||
}
|
||||
|
||||
|
|
|
@ -35,14 +35,23 @@ protected:
|
|||
nsresult WalkHandlers(nsIDOMKeyEvent* aKeyEvent, nsIAtom* aEventType);
|
||||
|
||||
// walk the handlers, looking for one to handle the event
|
||||
nsresult WalkHandlersInternal(nsIDOMKeyEvent* aKeyEvent,
|
||||
nsIAtom* aEventType,
|
||||
nsXBLPrototypeHandler* aHandler);
|
||||
bool WalkHandlersInternal(nsIDOMKeyEvent* aKeyEvent,
|
||||
nsIAtom* aEventType,
|
||||
nsXBLPrototypeHandler* aHandler,
|
||||
bool aExecute);
|
||||
|
||||
// walk the handlers for aEvent, aCharCode and aIgnoreShiftKey
|
||||
// walk the handlers for aEvent, aCharCode and aIgnoreShiftKey. Execute it
|
||||
// if aExecute = true.
|
||||
bool WalkHandlersAndExecute(nsIDOMKeyEvent* aKeyEvent, nsIAtom* aEventType,
|
||||
nsXBLPrototypeHandler* aHandler,
|
||||
uint32_t aCharCode, bool aIgnoreShiftKey);
|
||||
nsXBLPrototypeHandler* aHandler,
|
||||
uint32_t aCharCode, bool aIgnoreShiftKey,
|
||||
bool aExecute);
|
||||
|
||||
// HandleEvent function for the capturing phase.
|
||||
void HandleEventOnCapture(nsIDOMKeyEvent* aEvent);
|
||||
|
||||
// Check if any handler would handle the given event.
|
||||
bool HasHandlerForEvent(nsIDOMKeyEvent* aEvent);
|
||||
|
||||
// lazily load the handlers. Overridden to handle being attached
|
||||
// to a particular element rather than the document
|
||||
|
@ -57,8 +66,10 @@ protected:
|
|||
bool IsHTMLEditableFieldFocused();
|
||||
|
||||
// Returns the element which was passed as a parameter to the constructor,
|
||||
// unless the element has been removed from the document.
|
||||
already_AddRefed<mozilla::dom::Element> GetElement();
|
||||
// unless the element has been removed from the document. Optionally returns
|
||||
// whether the disabled attribute is set on the element (assuming the element
|
||||
// is non-null).
|
||||
already_AddRefed<mozilla::dom::Element> GetElement(bool* aIsDisabled = nullptr);
|
||||
// Using weak pointer to the DOM Element.
|
||||
nsWeakPtr mWeakPtrForElement;
|
||||
mozilla::dom::EventTarget* mTarget; // weak ref
|
||||
|
|
|
@ -68,5 +68,39 @@ Matrix::NudgeToIntegers()
|
|||
NudgeToInteger(&_32);
|
||||
}
|
||||
|
||||
Rect
|
||||
Matrix4x4::TransformBounds(const Rect& aRect) const
|
||||
{
|
||||
Point quad[4];
|
||||
Float min_x, max_x;
|
||||
Float min_y, max_y;
|
||||
|
||||
quad[0] = *this * aRect.TopLeft();
|
||||
quad[1] = *this * aRect.TopRight();
|
||||
quad[2] = *this * aRect.BottomLeft();
|
||||
quad[3] = *this * aRect.BottomRight();
|
||||
|
||||
min_x = max_x = quad[0].x;
|
||||
min_y = max_y = quad[0].y;
|
||||
|
||||
for (int i = 1; i < 4; i++) {
|
||||
if (quad[i].x < min_x) {
|
||||
min_x = quad[i].x;
|
||||
}
|
||||
if (quad[i].x > max_x) {
|
||||
max_x = quad[i].x;
|
||||
}
|
||||
|
||||
if (quad[i].y < min_y) {
|
||||
min_y = quad[i].y;
|
||||
}
|
||||
if (quad[i].y > max_y) {
|
||||
max_y = quad[i].y;
|
||||
}
|
||||
}
|
||||
|
||||
return Rect(min_x, min_y, max_x - min_x, max_y - min_y);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -353,6 +353,40 @@ public:
|
|||
return Is2D() && As2D().IsIntegerTranslation();
|
||||
}
|
||||
|
||||
Point4D operator *(const Point4D& aPoint) const
|
||||
{
|
||||
Point4D retPoint;
|
||||
|
||||
retPoint.x = aPoint.x * _11 + aPoint.y * _21 + aPoint.z * _31 + _41;
|
||||
retPoint.y = aPoint.x * _12 + aPoint.y * _22 + aPoint.z * _32 + _42;
|
||||
retPoint.z = aPoint.x * _13 + aPoint.y * _23 + aPoint.z * _33 + _43;
|
||||
retPoint.w = aPoint.x * _14 + aPoint.y * _24 + aPoint.z * _34 + _44;
|
||||
|
||||
return retPoint;
|
||||
}
|
||||
|
||||
Point3D operator *(const Point3D& aPoint) const
|
||||
{
|
||||
Point4D temp(aPoint.x, aPoint.y, aPoint.z, 1);
|
||||
|
||||
temp = *this * temp;
|
||||
temp /= temp.w;
|
||||
|
||||
return Point3D(temp.x, temp.y, temp.z);
|
||||
}
|
||||
|
||||
Point operator *(const Point &aPoint) const
|
||||
{
|
||||
Point4D temp(aPoint.x, aPoint.y, 0, 1);
|
||||
|
||||
temp = *this * temp;
|
||||
temp /= temp.w;
|
||||
|
||||
return Point(temp.x, temp.y);
|
||||
}
|
||||
|
||||
GFX2D_API Rect TransformBounds(const Rect& rect) const;
|
||||
|
||||
// Apply a scale to this matrix. This scale will be applied -before- the
|
||||
// existing transformation of the matrix.
|
||||
Matrix4x4 &Scale(Float aX, Float aY, Float aZ)
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include "Types.h"
|
||||
#include "BasePoint.h"
|
||||
#include "BasePoint3D.h"
|
||||
#include "BasePoint4D.h"
|
||||
#include "BaseSize.h"
|
||||
|
||||
#include <cmath>
|
||||
|
@ -92,6 +93,27 @@ struct Point3DTyped :
|
|||
};
|
||||
typedef Point3DTyped<UnknownUnits> Point3D;
|
||||
|
||||
template<class units>
|
||||
struct Point4DTyped :
|
||||
public BasePoint4D< Float, Point4DTyped<units> > {
|
||||
typedef BasePoint4D< Float, Point4DTyped<units> > Super;
|
||||
|
||||
Point4DTyped() : Super() {}
|
||||
Point4DTyped(Float aX, Float aY, Float aZ, Float aW) : Super(aX, aY, aZ, aW) {}
|
||||
|
||||
// XXX When all of the code is ported, the following functions to convert to and from
|
||||
// unknown types should be removed.
|
||||
|
||||
static Point4DTyped<units> FromUnknownPoint(const Point4DTyped<UnknownUnits>& aPoint) {
|
||||
return Point4DTyped<units>(aPoint.x, aPoint.y, aPoint.z, aPoint.w);
|
||||
}
|
||||
|
||||
Point4DTyped<UnknownUnits> ToUnknownPoint() const {
|
||||
return Point4DTyped<UnknownUnits>(this->x, this->y, this->z, this->w);
|
||||
}
|
||||
};
|
||||
typedef Point4DTyped<UnknownUnits> Point4D;
|
||||
|
||||
template<class units>
|
||||
struct IntSizeTyped :
|
||||
public BaseSize< int32_t, IntSizeTyped<units> >,
|
||||
|
|
|
@ -82,24 +82,14 @@ TextureClientX11::UpdateSurface(gfxASurface* aSurface)
|
|||
{
|
||||
MOZ_ASSERT(IsValid());
|
||||
|
||||
if (gfxPlatform::GetPlatform()->SupportsAzureContent()) {
|
||||
RefPtr<DrawTarget> dt = GetAsDrawTarget();
|
||||
if (!dt) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RefPtr<SourceSurface> source = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, aSurface);
|
||||
dt->CopySurface(source, IntRect(IntPoint(), GetSize()), IntPoint());
|
||||
} else {
|
||||
if (!mSurface) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsRefPtr<gfxContext> ctx = new gfxContext(mSurface.get());
|
||||
ctx->SetOperator(gfxContext::OPERATOR_SOURCE);
|
||||
ctx->DrawSurface(aSurface, mSurface->GetSize());
|
||||
RefPtr<DrawTarget> dt = GetAsDrawTarget();
|
||||
if (!dt) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RefPtr<SourceSurface> source = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, aSurface);
|
||||
dt->CopySurface(source, IntRect(IntPoint(), GetSize()), IntPoint());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -37,11 +37,6 @@ using namespace mozilla::gfx;
|
|||
namespace mozilla {
|
||||
namespace layers {
|
||||
|
||||
TextureClientPoolMember::TextureClientPoolMember(SurfaceFormat aFormat, TextureClientPool* aTexturePool)
|
||||
: mFormat(aFormat)
|
||||
, mTexturePool(aTexturePool)
|
||||
{}
|
||||
|
||||
ClientLayerManager::ClientLayerManager(nsIWidget* aWidget)
|
||||
: mPhase(PHASE_NONE)
|
||||
, mWidget(aWidget)
|
||||
|
@ -61,8 +56,6 @@ ClientLayerManager::~ClientLayerManager()
|
|||
mRoot = nullptr;
|
||||
|
||||
MOZ_COUNT_DTOR(ClientLayerManager);
|
||||
|
||||
mTexturePools.clear();
|
||||
}
|
||||
|
||||
int32_t
|
||||
|
@ -233,9 +226,8 @@ ClientLayerManager::EndTransaction(DrawThebesLayerCallback aCallback,
|
|||
MakeSnapshotIfRequired();
|
||||
}
|
||||
|
||||
for (const TextureClientPoolMember* item = mTexturePools.getFirst();
|
||||
item; item = item->getNext()) {
|
||||
item->mTexturePool->ReturnDeferredClients();
|
||||
for (size_t i = 0; i < mTexturePools.Length(); i++) {
|
||||
mTexturePools[i]->ReturnDeferredClients();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -458,21 +450,18 @@ ClientLayerManager::SetIsFirstPaint()
|
|||
TextureClientPool*
|
||||
ClientLayerManager::GetTexturePool(SurfaceFormat aFormat)
|
||||
{
|
||||
for (const TextureClientPoolMember* item = mTexturePools.getFirst();
|
||||
item; item = item->getNext()) {
|
||||
if (item->mFormat == aFormat) {
|
||||
return item->mTexturePool;
|
||||
for (size_t i = 0; i < mTexturePools.Length(); i++) {
|
||||
if (mTexturePools[i]->GetFormat() == aFormat) {
|
||||
return mTexturePools[i];
|
||||
}
|
||||
}
|
||||
|
||||
TextureClientPoolMember* texturePoolMember =
|
||||
new TextureClientPoolMember(aFormat,
|
||||
mTexturePools.AppendElement(
|
||||
new TextureClientPool(aFormat, IntSize(TILEDLAYERBUFFER_TILE_SIZE,
|
||||
TILEDLAYERBUFFER_TILE_SIZE),
|
||||
mForwarder));
|
||||
mTexturePools.insertBack(texturePoolMember);
|
||||
|
||||
return texturePoolMember->mTexturePool;
|
||||
return mTexturePools.LastElement();
|
||||
}
|
||||
|
||||
SimpleTextureClientPool*
|
||||
|
@ -502,9 +491,8 @@ ClientLayerManager::ClearCachedResources(Layer* aSubtree)
|
|||
} else if (mRoot) {
|
||||
ClearLayer(mRoot);
|
||||
}
|
||||
for (const TextureClientPoolMember* item = mTexturePools.getFirst();
|
||||
item; item = item->getNext()) {
|
||||
item->mTexturePool->Clear();
|
||||
for (size_t i = 0; i < mTexturePools.Length(); i++) {
|
||||
mTexturePools[i]->Clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,15 +36,6 @@ class PLayerChild;
|
|||
class TextureClientPool;
|
||||
class SimpleTextureClientPool;
|
||||
|
||||
class TextureClientPoolMember
|
||||
: public LinkedListElement<TextureClientPoolMember> {
|
||||
public:
|
||||
TextureClientPoolMember(gfx::SurfaceFormat aFormat, TextureClientPool* aTexturePool);
|
||||
|
||||
gfx::SurfaceFormat mFormat;
|
||||
RefPtr<TextureClientPool> mTexturePool;
|
||||
};
|
||||
|
||||
class ClientLayerManager : public LayerManager
|
||||
{
|
||||
typedef nsTArray<nsRefPtr<Layer> > LayerRefArray;
|
||||
|
@ -229,7 +220,7 @@ private:
|
|||
bool mNeedsComposite;
|
||||
|
||||
RefPtr<ShadowLayerForwarder> mForwarder;
|
||||
LinkedList<TextureClientPoolMember> mTexturePools;
|
||||
nsAutoTArray<RefPtr<TextureClientPool>,2> mTexturePools;
|
||||
|
||||
// indexed by gfx::SurfaceFormat
|
||||
nsTArray<RefPtr<SimpleTextureClientPool> > mSimpleTilePools;
|
||||
|
|
|
@ -103,7 +103,6 @@ ContentClientBasic::CreateBuffer(ContentType aType,
|
|||
RefPtr<gfx::DrawTarget>* aWhiteDT)
|
||||
{
|
||||
MOZ_ASSERT(!(aFlags & BUFFER_COMPONENT_ALPHA));
|
||||
MOZ_ASSERT(gfxPlatform::GetPlatform()->SupportsAzureContent());
|
||||
gfxImageFormat format =
|
||||
gfxPlatform::GetPlatform()->OptimalFormatForContent(aType);
|
||||
|
||||
|
@ -1285,7 +1284,6 @@ ContentClientIncremental::BorrowDrawTargetForPainting(ThebesLayer* aLayer,
|
|||
"BeginUpdate should always modify the draw region in the same way!");
|
||||
FillSurface(onBlack, aPaintState.mRegionToDraw, nsIntPoint(drawBounds.x, drawBounds.y), gfxRGBA(0.0, 0.0, 0.0, 1.0));
|
||||
FillSurface(onWhite, aPaintState.mRegionToDraw, nsIntPoint(drawBounds.x, drawBounds.y), gfxRGBA(1.0, 1.0, 1.0, 1.0));
|
||||
MOZ_ASSERT(gfxPlatform::GetPlatform()->SupportsAzureContent());
|
||||
RefPtr<DrawTarget> onBlackDT = gfxPlatform::GetPlatform()->CreateDrawTargetForUpdateSurface(onBlack, onBlack->GetSize().ToIntSize());
|
||||
RefPtr<DrawTarget> onWhiteDT = gfxPlatform::GetPlatform()->CreateDrawTargetForUpdateSurface(onWhite, onWhite->GetSize().ToIntSize());
|
||||
mLoanedDrawTarget = Factory::CreateDualDrawTarget(onBlackDT, onWhiteDT);
|
||||
|
@ -1294,7 +1292,6 @@ ContentClientIncremental::BorrowDrawTargetForPainting(ThebesLayer* aLayer,
|
|||
}
|
||||
} else {
|
||||
nsRefPtr<gfxASurface> surf = GetUpdateSurface(BUFFER_BLACK, aPaintState.mRegionToDraw);
|
||||
MOZ_ASSERT(gfxPlatform::GetPlatform()->SupportsAzureContent());
|
||||
mLoanedDrawTarget = gfxPlatform::GetPlatform()->CreateDrawTargetForUpdateSurface(surf, surf->GetSize().ToIntSize());
|
||||
}
|
||||
if (!mLoanedDrawTarget) {
|
||||
|
|
|
@ -248,29 +248,8 @@ DisableGralloc(SurfaceFormat aFormat)
|
|||
if (aFormat == gfx::SurfaceFormat::A8) {
|
||||
return true;
|
||||
}
|
||||
#if ANDROID_VERSION <= 15
|
||||
static bool checkedDevice = false;
|
||||
static bool disableGralloc = false;
|
||||
|
||||
if (!checkedDevice) {
|
||||
char propValue[PROPERTY_VALUE_MAX];
|
||||
property_get("ro.product.device", propValue, "None");
|
||||
|
||||
if (strcmp("crespo",propValue) == 0) {
|
||||
NS_WARNING("Nexus S has issues with gralloc, falling back to shmem");
|
||||
disableGralloc = true;
|
||||
}
|
||||
|
||||
checkedDevice = true;
|
||||
}
|
||||
|
||||
if (disableGralloc) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -678,25 +657,13 @@ BufferTextureClient::UpdateSurface(gfxASurface* aSurface)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (gfxPlatform::GetPlatform()->SupportsAzureContent()) {
|
||||
RefPtr<DrawTarget> dt = GetAsDrawTarget();
|
||||
RefPtr<SourceSurface> source = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, aSurface);
|
||||
RefPtr<DrawTarget> dt = GetAsDrawTarget();
|
||||
RefPtr<SourceSurface> source = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, aSurface);
|
||||
|
||||
dt->CopySurface(source, IntRect(IntPoint(), serializer.GetSize()), IntPoint());
|
||||
// XXX - if the Moz2D backend is D2D, we would be much better off memcpying
|
||||
// the content of the surface directly because with D2D, GetAsDrawTarget is
|
||||
// very expensive.
|
||||
} else {
|
||||
RefPtr<gfxImageSurface> surf = serializer.GetAsThebesSurface();
|
||||
if (!surf) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsRefPtr<gfxContext> tmpCtx = new gfxContext(surf.get());
|
||||
tmpCtx->SetOperator(gfxContext::OPERATOR_SOURCE);
|
||||
tmpCtx->DrawSurface(aSurface, gfxSize(serializer.GetSize().width,
|
||||
serializer.GetSize().height));
|
||||
}
|
||||
dt->CopySurface(source, IntRect(IntPoint(), serializer.GetSize()), IntPoint());
|
||||
// XXX - if the Moz2D backend is D2D, we would be much better off memcpying
|
||||
// the content of the surface directly because with D2D, GetAsDrawTarget is
|
||||
// very expensive.
|
||||
|
||||
if (TextureRequiresLocking(mFlags) && !ImplementsLocking()) {
|
||||
// We don't have support for proper locking yet, so we'll
|
||||
|
|
|
@ -30,6 +30,11 @@ TextureClientPool::TextureClientPool(gfx::SurfaceFormat aFormat, gfx::IntSize aS
|
|||
mTimer = do_CreateInstance("@mozilla.org/timer;1");
|
||||
}
|
||||
|
||||
TextureClientPool::~TextureClientPool()
|
||||
{
|
||||
mTimer->Cancel();
|
||||
}
|
||||
|
||||
TemporaryRef<TextureClient>
|
||||
TextureClientPool::GetTextureClient()
|
||||
{
|
||||
|
|
|
@ -24,6 +24,7 @@ public:
|
|||
MOZ_DECLARE_REFCOUNTED_TYPENAME(TextureClientPool)
|
||||
TextureClientPool(gfx::SurfaceFormat aFormat, gfx::IntSize aSize,
|
||||
ISurfaceAllocator *aAllocator);
|
||||
~TextureClientPool();
|
||||
|
||||
/**
|
||||
* Gets an allocated TextureClient of size and format that are determined
|
||||
|
@ -78,6 +79,8 @@ public:
|
|||
*/
|
||||
void Clear();
|
||||
|
||||
gfx::SurfaceFormat GetFormat() { return mFormat; }
|
||||
|
||||
private:
|
||||
// The time in milliseconds before the pool will be shrunk to the minimum
|
||||
// size after returning a client.
|
||||
|
|
|
@ -318,9 +318,8 @@ TiledContentHost::RenderTile(const TileHost& aTile,
|
|||
}
|
||||
|
||||
nsIntRect screenBounds = aScreenRegion.GetBounds();
|
||||
Matrix mat = aTransform.As2D();
|
||||
Rect quad(screenBounds.x, screenBounds.y, screenBounds.width, screenBounds.height);
|
||||
quad = mat.TransformBounds(quad);
|
||||
quad = aTransform.TransformBounds(quad);
|
||||
|
||||
if (!quad.Intersects(mCompositor->ClipRectInLayersCoordinates(aClipRect))) {
|
||||
return;
|
||||
|
|
|
@ -408,14 +408,6 @@ ThebesLayerD3D10::DrawRegion(nsIntRegion &aRegion, SurfaceMode aMode)
|
|||
|
||||
if (aMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
|
||||
FillTexturesBlackWhite(aRegion, visibleRect.TopLeft());
|
||||
if (!gfxPlatform::GetPlatform()->SupportsAzureContent()) {
|
||||
gfxASurface* surfaces[2] = { mD2DSurface.get(), mD2DSurfaceOnWhite.get() };
|
||||
destinationSurface = new gfxTeeSurface(surfaces, ArrayLength(surfaces));
|
||||
// Using this surface as a source will likely go horribly wrong, since
|
||||
// only the onBlack surface will really be used, so alpha information will
|
||||
// be incorrect.
|
||||
destinationSurface->SetAllowUseAsSource(false);
|
||||
}
|
||||
} else {
|
||||
destinationSurface = mD2DSurface;
|
||||
}
|
||||
|
@ -465,18 +457,7 @@ ThebesLayerD3D10::CreateNewTextures(const gfx::IntSize &aSize, SurfaceMode aMode
|
|||
NS_WARNING("Failed to create shader resource view for ThebesLayerD3D10.");
|
||||
}
|
||||
|
||||
if (!gfxPlatform::GetPlatform()->SupportsAzureContent()) {
|
||||
mD2DSurface = new gfxD2DSurface(mTexture, aMode != SurfaceMode::SURFACE_SINGLE_CHANNEL_ALPHA ?
|
||||
gfxContentType::COLOR : gfxContentType::COLOR_ALPHA);
|
||||
|
||||
if (!mD2DSurface || mD2DSurface->CairoStatus()) {
|
||||
NS_WARNING("Failed to create surface for ThebesLayerD3D10.");
|
||||
mD2DSurface = nullptr;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
mDrawTarget = nullptr;
|
||||
}
|
||||
mDrawTarget = nullptr;
|
||||
}
|
||||
|
||||
if (aMode == SurfaceMode::SURFACE_COMPONENT_ALPHA && !mTextureOnWhite) {
|
||||
|
@ -493,20 +474,10 @@ ThebesLayerD3D10::CreateNewTextures(const gfx::IntSize &aSize, SurfaceMode aMode
|
|||
NS_WARNING("Failed to create shader resource view for ThebesLayerD3D10.");
|
||||
}
|
||||
|
||||
if (!gfxPlatform::GetPlatform()->SupportsAzureContent()) {
|
||||
mD2DSurfaceOnWhite = new gfxD2DSurface(mTextureOnWhite, gfxContentType::COLOR);
|
||||
|
||||
if (!mD2DSurfaceOnWhite || mD2DSurfaceOnWhite->CairoStatus()) {
|
||||
NS_WARNING("Failed to create surface for ThebesLayerD3D10.");
|
||||
mD2DSurfaceOnWhite = nullptr;
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
mDrawTarget = nullptr;
|
||||
}
|
||||
mDrawTarget = nullptr;
|
||||
}
|
||||
|
||||
if (gfxPlatform::GetPlatform()->SupportsAzureContent() && !mDrawTarget) {
|
||||
if (!mDrawTarget) {
|
||||
if (aMode == SurfaceMode::SURFACE_COMPONENT_ALPHA) {
|
||||
mDrawTarget = Factory::CreateDualDrawTargetForD3D10Textures(mTexture, mTextureOnWhite, SurfaceFormat::B8G8R8X8);
|
||||
} else {
|
||||
|
|
|
@ -47,12 +47,16 @@ IsSurfaceDescriptorValid(const SurfaceDescriptor& aSurface)
|
|||
|
||||
ISurfaceAllocator::~ISurfaceAllocator()
|
||||
{
|
||||
ShrinkShmemSectionHeap();
|
||||
|
||||
// Check if we're not leaking..
|
||||
MOZ_ASSERT(mUsedShmems.empty());
|
||||
}
|
||||
|
||||
void
|
||||
ISurfaceAllocator::Finalize()
|
||||
{
|
||||
ShrinkShmemSectionHeap();
|
||||
}
|
||||
|
||||
bool
|
||||
ISurfaceAllocator::AllocSharedImageSurface(const gfx::IntSize& aSize,
|
||||
gfxContentType aContent,
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include "mozilla/layers/LayersMessages.h" // for ShmemSection
|
||||
#include "LayersTypes.h"
|
||||
#include <vector>
|
||||
#include "mozilla/layers/AtomicRefCountedWithFinalize.h"
|
||||
|
||||
/*
|
||||
* FIXME [bjacob] *** PURE CRAZYNESS WARNING ***
|
||||
|
@ -76,12 +77,14 @@ bool ReleaseOwnedSurfaceDescriptor(const SurfaceDescriptor& aDescriptor);
|
|||
* These methods should be only called in the ipdl implementor's thread, unless
|
||||
* specified otherwise in the implementing class.
|
||||
*/
|
||||
class ISurfaceAllocator : public AtomicRefCounted<ISurfaceAllocator>
|
||||
class ISurfaceAllocator : public AtomicRefCountedWithFinalize<ISurfaceAllocator>
|
||||
{
|
||||
public:
|
||||
MOZ_DECLARE_REFCOUNTED_TYPENAME(ISurfaceAllocator)
|
||||
ISurfaceAllocator() {}
|
||||
|
||||
void Finalize();
|
||||
|
||||
/**
|
||||
* Returns the type of backend that is used off the main thread.
|
||||
* We only don't allow changing the backend type at runtime so this value can
|
||||
|
@ -181,7 +184,7 @@ protected:
|
|||
// This is used to implement an extremely simple & naive heap allocator.
|
||||
std::vector<mozilla::ipc::Shmem> mUsedShmems;
|
||||
|
||||
friend class detail::RefCounted<ISurfaceAllocator, detail::AtomicRefCount>;
|
||||
friend class AtomicRefCountedWithFinalize<ISurfaceAllocator>;
|
||||
};
|
||||
|
||||
class GfxMemoryImageReporter MOZ_FINAL : public nsIMemoryReporter
|
||||
|
|
|
@ -423,29 +423,6 @@ ISurfaceAllocator::PlatformAllocSurfaceDescriptor(const gfx::IntSize& aSize,
|
|||
SurfaceDescriptor* aBuffer)
|
||||
{
|
||||
|
||||
// Check for devices that have problems with gralloc. We only check for
|
||||
// this on ICS or earlier, in hopes that JB will work.
|
||||
#if ANDROID_VERSION <= 15
|
||||
static bool checkedDevice = false;
|
||||
static bool disableGralloc = false;
|
||||
|
||||
if (!checkedDevice) {
|
||||
char propValue[PROPERTY_VALUE_MAX];
|
||||
property_get("ro.product.device", propValue, "None");
|
||||
|
||||
if (strcmp("crespo",propValue) == 0) {
|
||||
NS_WARNING("Nexus S has issues with gralloc, falling back to shmem");
|
||||
disableGralloc = true;
|
||||
}
|
||||
|
||||
checkedDevice = true;
|
||||
}
|
||||
|
||||
if (disableGralloc) {
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Some GL implementations fail to render gralloc textures with
|
||||
// width < 64. There's not much point in gralloc'ing buffers that
|
||||
// small anyway, so fall back on shared memory plus a texture
|
||||
|
|
|
@ -169,18 +169,10 @@ GrallocTextureClientOGL::UpdateSurface(gfxASurface* aSurface)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (gfxPlatform::GetPlatform()->SupportsAzureContent()) {
|
||||
RefPtr<DrawTarget> dt = GetAsDrawTarget();
|
||||
RefPtr<SourceSurface> source = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, aSurface);
|
||||
RefPtr<DrawTarget> dt = GetAsDrawTarget();
|
||||
RefPtr<SourceSurface> source = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, aSurface);
|
||||
|
||||
dt->CopySurface(source, IntRect(IntPoint(), GetSize()), IntPoint());
|
||||
} else {
|
||||
nsRefPtr<gfxASurface> surf = GetAsSurface();
|
||||
nsRefPtr<gfxContext> tmpCtx = new gfxContext(surf.get());
|
||||
tmpCtx->SetOperator(gfxContext::OPERATOR_SOURCE);
|
||||
tmpCtx->DrawSurface(aSurface, gfxSize(GetSize().width,
|
||||
GetSize().height));
|
||||
}
|
||||
dt->CopySurface(source, IntRect(IntPoint(), GetSize()), IntPoint());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -362,17 +362,10 @@ gfxASurface::CopyToARGB32ImageSurface()
|
|||
nsRefPtr<gfxImageSurface> imgSurface =
|
||||
new gfxImageSurface(size, gfxImageFormat::ARGB32);
|
||||
|
||||
if (gfxPlatform::GetPlatform()->SupportsAzureContent()) {
|
||||
RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(imgSurface, IntSize(size.width, size.height));
|
||||
RefPtr<SourceSurface> source = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, this);
|
||||
RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(imgSurface, IntSize(size.width, size.height));
|
||||
RefPtr<SourceSurface> source = gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, this);
|
||||
|
||||
dt->CopySurface(source, IntRect(0, 0, size.width, size.height), IntPoint());
|
||||
} else {
|
||||
gfxContext ctx(imgSurface);
|
||||
ctx.SetOperator(gfxContext::OPERATOR_SOURCE);
|
||||
ctx.SetSource(this);
|
||||
ctx.Paint();
|
||||
}
|
||||
dt->CopySurface(source, IntRect(0, 0, size.width, size.height), IntPoint());
|
||||
|
||||
return imgSurface.forget();
|
||||
}
|
||||
|
|
|
@ -392,13 +392,11 @@ gfxPlatform::Init()
|
|||
NS_RUNTIMEABORT("Could not initialize mScreenReferenceSurface");
|
||||
}
|
||||
|
||||
if (gPlatform->SupportsAzureContent()) {
|
||||
gPlatform->mScreenReferenceDrawTarget =
|
||||
gPlatform->CreateOffscreenContentDrawTarget(IntSize(1, 1),
|
||||
SurfaceFormat::B8G8R8A8);
|
||||
if (!gPlatform->mScreenReferenceDrawTarget) {
|
||||
NS_RUNTIMEABORT("Could not initialize mScreenReferenceDrawTarget");
|
||||
}
|
||||
gPlatform->mScreenReferenceDrawTarget =
|
||||
gPlatform->CreateOffscreenContentDrawTarget(IntSize(1, 1),
|
||||
SurfaceFormat::B8G8R8A8);
|
||||
if (!gPlatform->mScreenReferenceDrawTarget) {
|
||||
NS_RUNTIMEABORT("Could not initialize mScreenReferenceDrawTarget");
|
||||
}
|
||||
|
||||
rv = gfxFontCache::Init();
|
||||
|
|
|
@ -256,20 +256,6 @@ public:
|
|||
CreateDrawTargetForData(unsigned char* aData, const mozilla::gfx::IntSize& aSize,
|
||||
int32_t aStride, mozilla::gfx::SurfaceFormat aFormat);
|
||||
|
||||
/**
|
||||
* Returns true if we will render content using Azure using a gfxPlatform
|
||||
* provided DrawTarget.
|
||||
* Prefer using SupportsAzureContentForDrawTarget or
|
||||
* SupportsAzureContentForType.
|
||||
* This function is potentially misleading and dangerous because we might
|
||||
* support a certain Azure backend on the current platform, but when you
|
||||
* ask for a DrawTarget you get one for a different backend which is not
|
||||
* supported for content drawing.
|
||||
*/
|
||||
bool SupportsAzureContent() {
|
||||
return GetContentBackend() != mozilla::gfx::BackendType::NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if we should use Azure to render content with aTarget. For
|
||||
* example, it is possible that we are using Direct2D for rendering and thus
|
||||
|
|
|
@ -235,19 +235,10 @@ ClippedImage::GetFrameInternal(const nsIntSize& aViewportSize,
|
|||
mozilla::RefPtr<mozilla::gfx::DrawTarget> target;
|
||||
nsRefPtr<gfxContext> ctx;
|
||||
|
||||
if (gfxPlatform::GetPlatform()->SupportsAzureContent()) {
|
||||
target = gfxPlatform::GetPlatform()->
|
||||
CreateOffscreenContentDrawTarget(gfx::IntSize(mClip.width, mClip.height),
|
||||
gfx::SurfaceFormat::B8G8R8A8);
|
||||
ctx = new gfxContext(target);
|
||||
} else {
|
||||
target = gfxPlatform::GetPlatform()->
|
||||
CreateOffscreenCanvasDrawTarget(gfx::IntSize(mClip.width, mClip.height),
|
||||
gfx::SurfaceFormat::B8G8R8A8);
|
||||
nsRefPtr<gfxASurface> surface = gfxPlatform::GetPlatform()->
|
||||
GetThebesSurfaceForDrawTarget(target);
|
||||
ctx = new gfxContext(surface);
|
||||
}
|
||||
target = gfxPlatform::GetPlatform()->
|
||||
CreateOffscreenContentDrawTarget(gfx::IntSize(mClip.width, mClip.height),
|
||||
gfx::SurfaceFormat::B8G8R8A8);
|
||||
ctx = new gfxContext(target);
|
||||
|
||||
// Create our callback.
|
||||
nsRefPtr<gfxDrawingCallback> drawTileCallback =
|
||||
|
|
|
@ -17,12 +17,6 @@
|
|||
|
||||
namespace base {
|
||||
|
||||
namespace {
|
||||
// Paranoia. Semaphores and shared memory segments should live in different
|
||||
// namespaces, but who knows what's out there.
|
||||
const char kSemaphoreSuffix[] = "-sem";
|
||||
}
|
||||
|
||||
SharedMemory::SharedMemory()
|
||||
: mapped_file_(-1),
|
||||
inode_(0),
|
||||
|
|
|
@ -302,20 +302,6 @@ TrimPositions TrimStringT(const STR& input,
|
|||
((last_good_char == last_char) ? TRIM_NONE : TRIM_TRAILING));
|
||||
}
|
||||
|
||||
// Removes characters in trim_chars from the beginning and end of input.
|
||||
// NOTE: Safe to use the same variable for both input and output.
|
||||
static bool TrimString(const std::wstring& input,
|
||||
const wchar_t trim_chars[],
|
||||
std::wstring* output) {
|
||||
return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
|
||||
}
|
||||
|
||||
static bool TrimString(const std::string& input,
|
||||
const char trim_chars[],
|
||||
std::string* output) {
|
||||
return TrimStringT(input, trim_chars, TRIM_ALL, output) != TRIM_NONE;
|
||||
}
|
||||
|
||||
TrimPositions TrimWhitespace(const std::wstring& input,
|
||||
TrimPositions positions,
|
||||
std::wstring* output) {
|
||||
|
@ -501,6 +487,24 @@ struct IntToStringT {
|
|||
}
|
||||
};
|
||||
|
||||
// This set of templates is very similar to the above templates, but
|
||||
// for testing whether an integer is negative.
|
||||
template <typename INT2, bool NEG2>
|
||||
struct TestNegT {};
|
||||
template <typename INT2>
|
||||
struct TestNegT<INT2, false> {
|
||||
static bool TestNeg(INT2 value) {
|
||||
// value is unsigned, and can never be negative.
|
||||
return false;
|
||||
}
|
||||
};
|
||||
template <typename INT2>
|
||||
struct TestNegT<INT2, true> {
|
||||
static bool TestNeg(INT2 value) {
|
||||
return value < 0;
|
||||
}
|
||||
};
|
||||
|
||||
static STR IntToString(INT value) {
|
||||
// log10(2) ~= 0.3 bytes needed per bit or per byte log10(2**8) ~= 2.4.
|
||||
// So round up to allocate 3 output characters per byte, plus 1 for '-'.
|
||||
|
@ -510,7 +514,7 @@ struct IntToStringT {
|
|||
// then return the substr of what we ended up using.
|
||||
STR outbuf(kOutputBufSize, 0);
|
||||
|
||||
bool is_neg = value < 0;
|
||||
bool is_neg = TestNegT<INT, NEG>::TestNeg(value);
|
||||
// Even though is_neg will never be true when INT is parameterized as
|
||||
// unsigned, even the presence of the unary operation causes a warning.
|
||||
UINT res = ToUnsignedT<INT, UINT, NEG>::ToUnsigned(value);
|
||||
|
|
|
@ -113,11 +113,10 @@ static CFStringRef STLStringToCFStringWithEncodingsT(
|
|||
// Specify the byte ordering explicitly, otherwise CFString will be confused
|
||||
// when strings don't carry BOMs, as they typically won't.
|
||||
static const CFStringEncoding kNarrowStringEncoding = kCFStringEncodingUTF8;
|
||||
|
||||
#ifdef __BIG_ENDIAN__
|
||||
static const CFStringEncoding kMediumStringEncoding = kCFStringEncodingUTF16BE;
|
||||
static const CFStringEncoding kWideStringEncoding = kCFStringEncodingUTF32BE;
|
||||
#elif defined(__LITTLE_ENDIAN__)
|
||||
static const CFStringEncoding kMediumStringEncoding = kCFStringEncodingUTF16LE;
|
||||
static const CFStringEncoding kWideStringEncoding = kCFStringEncodingUTF32LE;
|
||||
#endif // __LITTLE_ENDIAN__
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ class JSAtom;
|
|||
class JSFreeOp;
|
||||
|
||||
namespace js {
|
||||
class StackFrame;
|
||||
class InterpreterFrame;
|
||||
class ScriptFrameIter;
|
||||
}
|
||||
|
||||
|
@ -94,7 +94,7 @@ FormatStackDump(JSContext *cx, char *buf, bool showArgs, bool showLocals, bool s
|
|||
# ifdef JS_DEBUG
|
||||
JS_FRIEND_API(void) js_DumpValue(const JS::Value &val);
|
||||
JS_FRIEND_API(void) js_DumpId(jsid id);
|
||||
JS_FRIEND_API(void) js_DumpStackFrame(JSContext *cx, js::StackFrame *start = nullptr);
|
||||
JS_FRIEND_API(void) js_DumpInterpreterFrame(JSContext *cx, js::InterpreterFrame *start = nullptr);
|
||||
# endif
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
|
|
|
@ -260,7 +260,7 @@ EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, AbstractFrame
|
|||
unsigned staticLevel;
|
||||
RootedValue thisv(cx);
|
||||
if (evalType == DIRECT_EVAL) {
|
||||
JS_ASSERT_IF(caller.isStackFrame(), !caller.asStackFrame()->runningInJit());
|
||||
JS_ASSERT_IF(caller.isInterpreterFrame(), !caller.asInterpreterFrame()->runningInJit());
|
||||
staticLevel = caller.script()->staticLevel() + 1;
|
||||
|
||||
// Direct calls to eval are supposed to see the caller's |this|. If we
|
||||
|
|
|
@ -211,6 +211,7 @@ CreateX4Class(JSContext *cx, Handle<GlobalObject*> global)
|
|||
return nullptr;
|
||||
x4->initReservedSlot(JS_DESCR_SLOT_TYPE_REPR, ObjectValue(*typeReprObj));
|
||||
x4->initReservedSlot(JS_DESCR_SLOT_TYPE, Int32Value(T::type));
|
||||
x4->initReservedSlot(JS_DESCR_SLOT_PROTO, ObjectValue(*proto));
|
||||
|
||||
// Link constructor to prototype and install properties.
|
||||
|
||||
|
|
|
@ -608,6 +608,8 @@ ArrayMetaTypeDescr::create(JSContext *cx,
|
|||
if (!prototypeObj)
|
||||
return nullptr;
|
||||
|
||||
obj->initReservedSlot(JS_DESCR_SLOT_PROTO, ObjectValue(*prototypeObj));
|
||||
|
||||
if (!LinkConstructorAndPrototype(cx, obj, prototypeObj))
|
||||
return nullptr;
|
||||
|
||||
|
@ -979,6 +981,8 @@ StructMetaTypeDescr::create(JSContext *cx,
|
|||
if (!prototypeObj)
|
||||
return nullptr;
|
||||
|
||||
descr->initReservedSlot(JS_DESCR_SLOT_PROTO, ObjectValue(*prototypeObj));
|
||||
|
||||
if (!LinkConstructorAndPrototype(cx, descr, prototypeObj))
|
||||
return nullptr;
|
||||
|
||||
|
|
|
@ -135,6 +135,14 @@ static T ConvertScalar(double d)
|
|||
class TypeDescr : public JSObject
|
||||
{
|
||||
public:
|
||||
// This is *intentionally* not defined so as to produce link
|
||||
// errors if a is<FooTypeDescr>() etc goes wrong. Otherwise, the
|
||||
// default implementation resolves this to a reference to
|
||||
// FooTypeDescr::class_ which resolves to
|
||||
// JSObject::class_. Debugging the resulting errors leads to much
|
||||
// fun and rejoicing.
|
||||
static const Class class_;
|
||||
|
||||
enum Kind {
|
||||
Scalar = JS_TYPEREPR_SCALAR_KIND,
|
||||
Reference = JS_TYPEREPR_REFERENCE_KIND,
|
||||
|
@ -269,10 +277,22 @@ class ReferenceTypeDescr : public SimpleTypeDescr
|
|||
macro_(ReferenceTypeDescr::TYPE_OBJECT, HeapPtrObject, Object) \
|
||||
macro_(ReferenceTypeDescr::TYPE_STRING, HeapPtrString, string)
|
||||
|
||||
// Type descriptors whose instances are objects and hence which have
|
||||
// an associated `prototype` property.
|
||||
class ComplexTypeDescr : public SizedTypeDescr
|
||||
{
|
||||
public:
|
||||
// Returns the prototype that instances of this type descriptor
|
||||
// will have.
|
||||
JSObject &instancePrototype() const {
|
||||
return getReservedSlot(JS_DESCR_SLOT_PROTO).toObject();
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Type descriptors `float32x4` and `int32x4`
|
||||
*/
|
||||
class X4TypeDescr : public SizedTypeDescr
|
||||
class X4TypeDescr : public ComplexTypeDescr
|
||||
{
|
||||
public:
|
||||
enum Type {
|
||||
|
@ -346,6 +366,11 @@ class ArrayMetaTypeDescr : public JSObject
|
|||
|
||||
/*
|
||||
* Type descriptor created by `new ArrayType(typeObj)`
|
||||
*
|
||||
* These have a prototype, and hence *could* be a subclass of
|
||||
* `ComplexTypeDescr`, but it would require some reshuffling of the
|
||||
* hierarchy, and it's not worth the trouble since they will be going
|
||||
* away as part of bug 973238.
|
||||
*/
|
||||
class UnsizedArrayTypeDescr : public TypeDescr
|
||||
{
|
||||
|
@ -364,7 +389,7 @@ class UnsizedArrayTypeDescr : public TypeDescr
|
|||
/*
|
||||
* Type descriptor created by `unsizedArrayTypeObj.dimension()`
|
||||
*/
|
||||
class SizedArrayTypeDescr : public SizedTypeDescr
|
||||
class SizedArrayTypeDescr : public ComplexTypeDescr
|
||||
{
|
||||
public:
|
||||
static const Class class_;
|
||||
|
@ -413,7 +438,8 @@ class StructMetaTypeDescr : public JSObject
|
|||
static bool construct(JSContext *cx, unsigned argc, Value *vp);
|
||||
};
|
||||
|
||||
class StructTypeDescr : public SizedTypeDescr {
|
||||
class StructTypeDescr : public ComplexTypeDescr
|
||||
{
|
||||
public:
|
||||
static const Class class_;
|
||||
|
||||
|
@ -873,13 +899,19 @@ IsSimpleTypeDescrClass(const Class* clasp)
|
|||
clasp == &ReferenceTypeDescr::class_;
|
||||
}
|
||||
|
||||
inline bool
|
||||
IsComplexTypeDescrClass(const Class* clasp)
|
||||
{
|
||||
return clasp == &StructTypeDescr::class_ ||
|
||||
clasp == &SizedArrayTypeDescr::class_ ||
|
||||
clasp == &X4TypeDescr::class_;
|
||||
}
|
||||
|
||||
inline bool
|
||||
IsSizedTypeDescrClass(const Class* clasp)
|
||||
{
|
||||
return IsSimpleTypeDescrClass(clasp) ||
|
||||
clasp == &StructTypeDescr::class_ ||
|
||||
clasp == &SizedArrayTypeDescr::class_ ||
|
||||
clasp == &X4TypeDescr::class_;
|
||||
IsComplexTypeDescrClass(clasp);
|
||||
}
|
||||
|
||||
inline bool
|
||||
|
@ -908,6 +940,13 @@ JSObject::is<js::SizedTypeDescr>() const
|
|||
return IsSizedTypeDescrClass(getClass());
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool
|
||||
JSObject::is<js::ComplexTypeDescr>() const
|
||||
{
|
||||
return IsComplexTypeDescrClass(getClass());
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool
|
||||
JSObject::is<js::TypeDescr>() const
|
||||
|
|
|
@ -945,18 +945,21 @@ function TypedArrayMap(a, b) {
|
|||
|
||||
// Warning: user exposed!
|
||||
function TypedArrayMapPar(a, b) {
|
||||
// Arguments: [depth], func
|
||||
|
||||
// Defer to the sequential variant for error cases or
|
||||
// when not working with typed objects.
|
||||
if (!IsObject(this) || !ObjectIsTypedObject(this))
|
||||
return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
|
||||
return callFunction(TypedArrayMap, this, a, b);
|
||||
var thisType = TYPEDOBJ_TYPE_DESCR(this);
|
||||
if (!TypeDescrIsArrayType(thisType))
|
||||
return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
|
||||
return callFunction(TypedArrayMap, this, a, b);
|
||||
|
||||
// Arguments: [depth], func
|
||||
if (typeof a === "number" && typeof b === "function")
|
||||
if (typeof a === "number" && IsCallable(b))
|
||||
return MapTypedParImpl(this, a, thisType, b);
|
||||
else if (typeof a === "function")
|
||||
else if (IsCallable(a))
|
||||
return MapTypedParImpl(this, 1, thisType, a);
|
||||
return ThrowError(JSMSG_TYPEDOBJECT_BAD_ARGS);
|
||||
return callFunction(TypedArrayMap, this, a, b);
|
||||
}
|
||||
|
||||
// Warning: user exposed!
|
||||
|
@ -1017,6 +1020,20 @@ function TypedObjectArrayTypeBuildPar(a,b,c) {
|
|||
|
||||
// Warning: user exposed!
|
||||
function TypedObjectArrayTypeFromPar(a,b,c) {
|
||||
// Arguments: arrayLike, [depth], func
|
||||
|
||||
// Use the sequential version for error cases or when arrayLike is
|
||||
// not a typed object.
|
||||
if (!IsObject(this) || !ObjectIsTypeDescr(this) || !TypeDescrIsArrayType(this))
|
||||
return callFunction(TypedObjectArrayTypeFrom, this, a, b, c);
|
||||
if (!IsObject(a) || !ObjectIsTypedObject(a))
|
||||
return callFunction(TypedObjectArrayTypeFrom, this, a, b, c);
|
||||
|
||||
// Detect whether an explicit depth is supplied.
|
||||
if (typeof b === "number" && IsCallable(c))
|
||||
return MapTypedParImpl(a, b, this, c);
|
||||
if (IsCallable(b))
|
||||
return MapTypedParImpl(a, 1, this, b);
|
||||
return callFunction(TypedObjectArrayTypeFrom, this, a, b, c);
|
||||
}
|
||||
|
||||
|
@ -1355,6 +1372,10 @@ function MapTypedParImpl(inArray, depth, outputType, func) {
|
|||
"Map/From called on non-object or untyped input array.");
|
||||
assert(TypeDescrIsArrayType(outputType),
|
||||
"Map/From called on non array-type outputType");
|
||||
assert(typeof depth === "number",
|
||||
"Map/From called with non-numeric depth");
|
||||
assert(IsCallable(func),
|
||||
"Map/From called on something not callable");
|
||||
|
||||
var inArrayType = TypeOfTypedObject(inArray);
|
||||
|
||||
|
@ -1516,6 +1537,12 @@ function MapTypedParImplDepth1(inArray, inArrayType, outArrayType, func) {
|
|||
outOffset += outGrainTypeSize;
|
||||
}
|
||||
|
||||
// A transparent result type cannot contain references, and
|
||||
// hence there is no way for a pointer to a thread-local object
|
||||
// to escape.
|
||||
if (outGrainTypeIsTransparent)
|
||||
ClearThreadLocalArenas();
|
||||
|
||||
MARK_SLICE_DONE(slicesInfo, sliceId);
|
||||
if (warmup)
|
||||
return;
|
||||
|
|
|
@ -21,23 +21,24 @@
|
|||
#define JS_DESCR_SLOT_TYPE_REPR 0 // Associated Type Representation
|
||||
#define JS_DESCR_SLOT_ALIGNMENT 1 // Alignment in bytes
|
||||
#define JS_DESCR_SLOT_SIZE 2 // Size in bytes, if sized, else 0
|
||||
#define JS_DESCR_SLOT_PROTO 3 // Prototype for instances, if any
|
||||
|
||||
// Slots on scalars, references, and x4s
|
||||
#define JS_DESCR_SLOT_TYPE 3 // Type code
|
||||
#define JS_DESCR_SLOT_TYPE 4 // Type code
|
||||
|
||||
// Slots on all array descriptors
|
||||
#define JS_DESCR_SLOT_ARRAY_ELEM_TYPE 3
|
||||
#define JS_DESCR_SLOT_ARRAY_ELEM_TYPE 4
|
||||
|
||||
// Slots on sized array descriptors
|
||||
#define JS_DESCR_SLOT_SIZED_ARRAY_LENGTH 4
|
||||
#define JS_DESCR_SLOT_SIZED_ARRAY_LENGTH 5
|
||||
|
||||
// Slots on struct type objects
|
||||
#define JS_DESCR_SLOT_STRUCT_FIELD_NAMES 3
|
||||
#define JS_DESCR_SLOT_STRUCT_FIELD_TYPES 4
|
||||
#define JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS 5
|
||||
#define JS_DESCR_SLOT_STRUCT_FIELD_NAMES 4
|
||||
#define JS_DESCR_SLOT_STRUCT_FIELD_TYPES 5
|
||||
#define JS_DESCR_SLOT_STRUCT_FIELD_OFFSETS 6
|
||||
|
||||
// Maximum number of slots for any descriptor
|
||||
#define JS_DESCR_SLOTS 6
|
||||
#define JS_DESCR_SLOTS 7
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// Slots for type representation objects
|
||||
|
|
|
@ -1136,7 +1136,8 @@ if test "$GNU_CC"; then
|
|||
AC_MSG_CHECKING([for --build-id option to ld])
|
||||
_SAVE_LDFLAGS=$LDFLAGS
|
||||
LDFLAGS="$LDFLAGS -Wl,--build-id"
|
||||
AC_TRY_LINK(,,AC_MSG_RESULT([yes]),
|
||||
AC_TRY_LINK(,,AC_MSG_RESULT([yes])
|
||||
[NSPR_LDFLAGS="$NSPR_LDFLAGS -Wl,--build-id"],
|
||||
AC_MSG_RESULT([no])
|
||||
LDFLAGS=$_SAVE_LDFLAGS)
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ class FreeOp;
|
|||
namespace gc {
|
||||
|
||||
struct Arena;
|
||||
struct ArenaList;
|
||||
struct ArenaHeader;
|
||||
struct Chunk;
|
||||
|
||||
|
@ -579,6 +580,8 @@ struct Arena
|
|||
return address() + ArenaSize;
|
||||
}
|
||||
|
||||
void setAsFullyUnused(AllocKind thingKind);
|
||||
|
||||
template <typename T>
|
||||
bool finalize(FreeOp *fop, AllocKind thingKind, size_t thingSize);
|
||||
};
|
||||
|
@ -801,6 +804,7 @@ struct Chunk
|
|||
ArenaHeader *allocateArena(JS::Zone *zone, AllocKind kind);
|
||||
|
||||
void releaseArena(ArenaHeader *aheader);
|
||||
void recycleArena(ArenaHeader *aheader, ArenaList &dest, AllocKind thingKind);
|
||||
|
||||
static Chunk *allocate(JSRuntime *rt);
|
||||
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче